Skip to content

Commit

Permalink
✨ RAG API 연동 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
tinajeong committed Oct 28, 2024
1 parent b14468f commit ec3280f
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.moneygement.lyf.jarvis.recommendation.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import lombok.Getter;

@Getter
@Configuration
public class RAGConfig {
@Value("${url.rag}")
private String ragUrl;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,37 @@

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.moneygement.lyf.jarvis.recommendation.domain.Recommendation;
import com.moneygement.lyf.jarvis.recommendation.domain.RecommendationCreateRequest;
import com.moneygement.lyf.jarvis.recommendation.service.RecommendationService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/recommend")
@RequiredArgsConstructor
@Tag(name = "Recommend", description = "추천 관련 정보를 호출하고, 저장하고, 조회할 수 있습니다.")
public class GroupRecommendationController {
public class RecommendationController {
private final RecommendationService recommendationService;

@GetMapping("group/{groupId}")
@Operation(summary = "History 조회", description = "GroupId 기준의 추천 장소 목록 정보를 제공 합니다.")
@GetMapping("/group/{groupId}")
@Operation(summary = "추천 목록 조회", description = "GroupId 기준의 추천 장소 목록 정보를 제공 합니다.")
public List<Recommendation> getRecommendationsByGroupId(@PathVariable Long groupId) {
return recommendationService.getRecommendationsByGroupId(groupId);
}

@PostMapping("/rag")
@Operation(summary = "추천 정보 요청", description = "추천 정보를 요청하고 저장합니다.")
public String getRecommendation(@Valid @RequestBody RecommendationCreateRequest request) {
return recommendationService.recommend(request);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.moneygement.lyf.jarvis.recommendation.domain;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

import java.util.List;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class RagResponse {

@JsonProperty("startPoint")
private Point startPoint; // 출발지 정보

@JsonProperty("endPoint")
private Point endPoint; // 도착지 정보

@JsonProperty("pointList")
private List<Point> pointList; // 추천 명소 리스트

@JsonProperty("message")
private String message; // 전체 메시지

/**
* Point 클래스: 출발지, 도착지, 명소 정보를 담는 내부 클래스
*/
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Point {

@JsonProperty("name")
private String name; // 장소 이름

@JsonProperty("latitude")
private double latitude; // 위도

@JsonProperty("longitude")
private double longitude; // 경도

@JsonProperty("message")
private String message; // 장소 설명 (명소일 경우)

@JsonProperty("status")
private String status; // 장소 상태 (예: 맛집, 관광 등)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,15 @@ public class Recommendation {
private Double rating = 0.0;

public Recommendation(Long groupId, String name,
String description,
String category,
Double latitude, Double longitude, Double rating) {
Double latitude, Double longitude) {
this.groupId = groupId;
this.name = name;
this.description = description;
this.category = category;
this.latitude = latitude;
this.longitude = longitude;
this.rating = rating;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moneygement.lyf.jarvis.recommendation.domain;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record RecommendationCreateRAGRequest(@NotBlank(message = "user id can't be empty") String userId,
@NotBlank(message = "command can't be empty") String command,
@NotNull(message = "latitude can't be empty") Double latitude,
@NotNull(message = "latitude can't be empty") Double longitude,
String weather
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.moneygement.lyf.jarvis.recommendation.domain;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record RecommendationCreateRequest(@NotBlank(message = "user id can't be empty") String userId,
@NotBlank(message = "request can't be empty") String request,
@NotNull(message = "latitude can't be empty") Double latitude,
@NotNull(message = "latitude can't be empty") Double longitude) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.moneygement.lyf.jarvis.recommendation.service;

import static com.moneygement.lyf.jarvis.common.util.JsonUtil.*;

import java.io.IOException;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import com.moneygement.lyf.jarvis.common.util.HttpClientUtil;
import com.moneygement.lyf.jarvis.recommendation.config.RAGConfig;
import com.moneygement.lyf.jarvis.recommendation.domain.RagResponse;
import com.moneygement.lyf.jarvis.recommendation.domain.RecommendationCreateRAGRequest;
import com.moneygement.lyf.jarvis.route.domain.OptimalRouteRequest;
import com.moneygement.lyf.jarvis.route.domain.OptimalRouteResponse;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class RAGService {
private final RAGConfig ragConfig;

public RagResponse getRecommendations(RecommendationCreateRAGRequest request) throws IOException {
ResponseEntity<Object> response = HttpClientUtil.post(null, ragConfig.getRagUrl(), request);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
return convertObject(response.getBody(), RagResponse.class);
} else {
log.error("Failed to call RAG API: " + response.getStatusCode());
throw new IOException("Failed to call RAG API: " + response.getStatusCode());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import java.util.List;

import com.moneygement.lyf.jarvis.recommendation.domain.Recommendation;
import com.moneygement.lyf.jarvis.recommendation.domain.RecommendationCreateRequest;

public interface RecommendationService {
List<Recommendation> getRecommendationsByGroupId(Long groupId);

String recommend(RecommendationCreateRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,93 @@
package com.moneygement.lyf.jarvis.recommendation.service;

import java.io.IOException;
import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.moneygement.lyf.jarvis.common.error.ExternalApiFailedException;
import com.moneygement.lyf.jarvis.history.domain.HistoryCreateRequest;
import com.moneygement.lyf.jarvis.history.service.HistoryService;
import com.moneygement.lyf.jarvis.recommendation.domain.GroupRecommendation;
import com.moneygement.lyf.jarvis.recommendation.domain.RagResponse;
import com.moneygement.lyf.jarvis.recommendation.domain.Recommendation;
import com.moneygement.lyf.jarvis.recommendation.domain.RecommendationCreateRAGRequest;
import com.moneygement.lyf.jarvis.recommendation.domain.RecommendationCreateRequest;
import com.moneygement.lyf.jarvis.recommendation.persistence.GroupRecommendationRepository;
import com.moneygement.lyf.jarvis.recommendation.persistence.RecommendationRepository;
import com.moneygement.lyf.jarvis.weather.service.WeatherReportService;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class RecommendationServiceImpl implements RecommendationService{
public class RecommendationServiceImpl implements RecommendationService {
private final GroupRecommendationRepository groupRecommendationRepository;
private final RecommendationRepository recommendationRepository;
private final HistoryService historyService;
private final WeatherReportService weatherReportService;
private final RAGService ragService;

@Override
public List<Recommendation> getRecommendationsByGroupId(Long groupId) {
return recommendationRepository.findByGroupId(groupId);
}

@Override
@Transactional
public String recommend(RecommendationCreateRequest request) {
// 1. weather service summary 서비스 호출 -> weather 값에 포함
String weather =
weatherReportService.getWeatherSummary(request.latitude(), request.longitude());
// 2. RAG 호출
RagResponse ragResponse;
try {
ragResponse = ragService.getRecommendations(new RecommendationCreateRAGRequest(
request.userId(),
request.request(),
request.latitude(),
request.longitude(),
weather
));
} catch (IOException e) {
throw new ExternalApiFailedException(e.getMessage());
}
// 3. group_recommendation 저장
if (ragResponse == null) {
throw new ExternalApiFailedException("Can't retrieve data from RAG API");
}

GroupRecommendation group = new GroupRecommendation(ragResponse.getMessage(), request.userId());
group.setSessionName("자동 생성된 여행 코스");
GroupRecommendation savedGroup = groupRecommendationRepository.save(group);

// 4. recommendation 저장
recommendationRepository.save(new Recommendation(
savedGroup.getGroupId(),
ragResponse.getStartPoint().getName(),
ragResponse.getMessage(),
ragResponse.getStartPoint().getStatus(),
ragResponse.getStartPoint().getLatitude(),
ragResponse.getStartPoint().getLongitude()));

for (RagResponse.Point point : ragResponse.getPointList()) {
Recommendation recommendation = new Recommendation(
savedGroup.getGroupId(),
point.getName(),
point.getMessage(),
point.getStatus(),
point.getLatitude(),
point.getLongitude());
recommendationRepository.save(recommendation);
}

// 5. history에 저장
historyService.save(new HistoryCreateRequest(
request.userId(),
savedGroup.getGroupId(),
request.request(),
ragResponse.getMessage()));
return ragResponse.getMessage();
}
}
3 changes: 2 additions & 1 deletion src/main/resources/external-url.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
url:
open-ai: https://api.openai.com/v1/completions
graph-hopper: https://graphhopper.com/api/1/route
open-weather: http://api.openweathermap.org/data/2.5/weather
open-weather: http://api.openweathermap.org/data/2.5/weather
rag: https://moneygement-rag.o-r.kr/recommend

0 comments on commit ec3280f

Please sign in to comment.