From 1354d07e285aeb2ff52a3a1aaa64f1c0f0b934eb Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:43:52 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20GPTService=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/gpt/service/GptService.java | 85 ++++++++----------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/service/GptService.java b/backend/src/main/java/com/isp/backend/domain/gpt/service/GptService.java index 56104236..cbb9fbb2 100644 --- a/backend/src/main/java/com/isp/backend/domain/gpt/service/GptService.java +++ b/backend/src/main/java/com/isp/backend/domain/gpt/service/GptService.java @@ -13,7 +13,6 @@ import com.isp.backend.domain.gpt.entity.GptScheduleParser; import com.isp.backend.domain.schedule.service.ScheduleService; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -30,10 +29,10 @@ import java.util.concurrent.Executors; import java.util.stream.Collectors; -@Slf4j -@RequiredArgsConstructor @Service +@RequiredArgsConstructor public class GptService { + private final GptScheduleParser gptScheduleParser; private final ScheduleService scheduleService; private final WebClient webClient; @@ -42,7 +41,6 @@ public class GptService { private String apiKey; public GptScheduleResponse getResponse(HttpHeaders headers, GptRequest gptRequest) { - GptResponse response = webClient.post() .uri(GptConfig.CHAT_URL) .headers(h -> h.addAll(headers)) @@ -52,78 +50,67 @@ public GptScheduleResponse getResponse(HttpHeaders headers, GptRequest gptReques .bodyToMono(GptResponse.class) .block(); - List gptSchedules = gptScheduleParser.parseScheduleText(getScheduleText(response)); + List gptSchedules = gptScheduleParser.parseScheduleText(extractScheduleText(response)); return new GptScheduleResponse(gptSchedules); } - private String getScheduleText(GptResponse gptResponse) { - return getGptMessage(gptResponse).toString(); - } - - private GptMessage getGptMessage(GptResponse gptResponse) { - return gptResponse.getChoices().get(0).getMessage(); + private String extractScheduleText(GptResponse gptResponse) { + return gptResponse.getChoices().get(0).getMessage().getContent(); } @Async - public CompletableFuture askQuestion(GptScheduleRequest questionRequestDTO) { - String question = makeQuestion(questionRequestDTO); - List messages = Collections.singletonList( - GptMessage.builder() - .role(GptConfig.ROLE) - .content(question) - .build() - ); - - Country country = scheduleService.validateCountry(questionRequestDTO.getDestination()); + public CompletableFuture askQuestion(GptScheduleRequest questionRequest) { + String question = formatQuestion(questionRequest); + List messages = Collections.singletonList(createMessage(question)); + Country country = scheduleService.validateCountry(questionRequest.getDestination()); String countryImage = country.getImageUrl(); ExecutorService executorService = Executors.newFixedThreadPool(5); List> futures = Arrays.asList( - sendRequestAsync(apiKey, messages, executorService), - sendRequestAsync(apiKey, messages, executorService), - sendRequestAsync(apiKey, messages, executorService) + sendRequestAsync(messages, executorService), + sendRequestAsync(messages, executorService), + sendRequestAsync(messages, executorService) ); CompletableFuture> combinedFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .thenApply(v -> futures.stream() - .map(CompletableFuture::join) - .collect(Collectors.toList())); + .thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())); return combinedFuture.thenApply(schedules -> new GptSchedulesResponse(countryImage, schedules)); } - private CompletableFuture sendRequestAsync(String apiKey, List messages, ExecutorService executor) { + private CompletableFuture sendRequestAsync(List messages, ExecutorService executor) { HttpHeaders headers = buildHttpHeaders(apiKey); - GptRequest request = getGptRequest(messages); + GptRequest request = createGptRequest(messages); return CompletableFuture.supplyAsync(() -> getResponse(headers, request), executor); } - private GptRequest getGptRequest(List messages) { - return new GptRequest( - GptConfig.CHAT_MODEL, - GptConfig.MAX_TOKEN, - GptConfig.TEMPERATURE, - GptConfig.STREAM, - messages - ); + private GptRequest createGptRequest(List messages) { + return new GptRequest(GptConfig.CHAT_MODEL, GptConfig.MAX_TOKEN, GptConfig.TEMPERATURE, GptConfig.STREAM, messages); } private HttpHeaders buildHttpHeaders(String key) { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - httpHeaders.add(GptConfig.AUTHORIZATION, GptConfig.BEARER + key); - return httpHeaders; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add(HttpHeaders.AUTHORIZATION, GptConfig.BEARER + key); + return headers; } - private String makeQuestion(GptScheduleRequest questionRequestDTO) { + private String formatQuestion(GptScheduleRequest questionRequest) { return String.format(GptConfig.PROMPT, - questionRequestDTO.getDestination(), - questionRequestDTO.getPurpose(), - questionRequestDTO.getIncludedActivities(), - questionRequestDTO.getExcludedActivities(), - questionRequestDTO.getDepartureDate(), - questionRequestDTO.getReturnDate(), - String.join(ParsingConstants.COMMA, questionRequestDTO.getPurpose()) + questionRequest.getDestination(), + questionRequest.getPurpose(), + questionRequest.getIncludedActivities(), + questionRequest.getExcludedActivities(), + questionRequest.getDepartureDate(), + questionRequest.getReturnDate(), + String.join(ParsingConstants.COMMA, questionRequest.getPurpose()) ); } + + private GptMessage createMessage(String content) { + return GptMessage.builder() + .role(GptConfig.ROLE) + .content(content) + .build(); + } } \ No newline at end of file From b8d10b095349d85b079285244cb3f7edd8cbb780 Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:44:17 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20=EC=A2=8C=ED=91=9C=EA=B0=92(?= =?UTF-8?q?=EC=9C=84=EB=8F=84=20=EA=B2=BD=EB=8F=84)=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/gpt/entity/Coordinate.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/src/main/java/com/isp/backend/domain/gpt/entity/Coordinate.java diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/entity/Coordinate.java b/backend/src/main/java/com/isp/backend/domain/gpt/entity/Coordinate.java new file mode 100644 index 00000000..f3bb8bc7 --- /dev/null +++ b/backend/src/main/java/com/isp/backend/domain/gpt/entity/Coordinate.java @@ -0,0 +1,16 @@ +package com.isp.backend.domain.gpt.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class Coordinate { + private Double latitude; + private Double longitude; + + public Coordinate(Double latitude, Double longitude) { + this.latitude = latitude; + this.longitude = longitude; + } +} \ No newline at end of file From 6ce532de2d7a85f8fb9ac12b5564da0c13c546c9 Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:44:42 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20=EC=84=B8=EB=B6=80=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=EA=B3=BC=20=EC=A2=8C=ED=91=9C=EA=B0=92=EC=9D=84=20?= =?UTF-8?q?=EA=B0=80=EC=A7=84=20=EC=84=B8=EB=B6=80=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/gpt/entity/GptScheduleDetail.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleDetail.java diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleDetail.java b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleDetail.java new file mode 100644 index 00000000..99781771 --- /dev/null +++ b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleDetail.java @@ -0,0 +1,16 @@ +package com.isp.backend.domain.gpt.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class GptScheduleDetail { + private String detail; + private Coordinate coordinate; + + public GptScheduleDetail(String detail, Coordinate coordinate) { + this.detail = detail; + this.coordinate = coordinate; + } +} \ No newline at end of file From e37da2939fda6b5953a7d33b9f136791b2dbb93a Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:45:02 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=9D=91=EB=8B=B5=EC=9D=B4=20?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A7=81=EC=9D=B4=20=EC=95=84=EB=8B=88?= =?UTF-8?q?=EB=9D=BC=20GptSchduleDetail=EC=9D=B4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/isp/backend/domain/gpt/entity/GptSchedule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptSchedule.java b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptSchedule.java index 4a82d134..c78e099d 100644 --- a/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptSchedule.java +++ b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptSchedule.java @@ -9,10 +9,10 @@ @NoArgsConstructor public class GptSchedule { private String date; - private List scheduleDetail; + private List scheduleDetail; - public GptSchedule(String date, List scheduleDetail) { + public GptSchedule(String date, List scheduleDetail) { this.date = date; this.scheduleDetail = scheduleDetail; } -} +} \ No newline at end of file From 6abc3606f16603f5831b83b6199631032cb8e636 Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:45:28 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20WebClient=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/isp/backend/domain/gpt/config/WebClientConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/config/WebClientConfig.java b/backend/src/main/java/com/isp/backend/domain/gpt/config/WebClientConfig.java index f6500b65..b78c3b53 100644 --- a/backend/src/main/java/com/isp/backend/domain/gpt/config/WebClientConfig.java +++ b/backend/src/main/java/com/isp/backend/domain/gpt/config/WebClientConfig.java @@ -17,8 +17,8 @@ public WebClient webClient() { HttpClient httpClient = HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000) .doOnConnected(conn -> conn - .addHandlerLast(new ReadTimeoutHandler(10)) - .addHandlerLast(new WriteTimeoutHandler(10))); + .addHandlerLast(new ReadTimeoutHandler(30)) + .addHandlerLast(new WriteTimeoutHandler(30))); return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); From a55688cb585ac00a644fa5d5b346cf441d6706ac Mon Sep 17 00:00:00 2001 From: Kim Jiyoon Date: Wed, 19 Jun 2024 15:45:39 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20=EC=A2=8C=ED=91=9C=EA=B0=92=EB=8F=84?= =?UTF-8?q?=20=ED=95=A8=EA=BB=98=20=EB=84=98=EC=96=B4=EC=98=A4=EA=B8=B0=20?= =?UTF-8?q?=EB=95=8C=EB=AC=B8=EC=97=90=20=ED=8C=8C=EC=8B=B1=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/gpt/entity/GptScheduleParser.java | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleParser.java b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleParser.java index ffb4306e..64a06dc0 100644 --- a/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleParser.java +++ b/backend/src/main/java/com/isp/backend/domain/gpt/entity/GptScheduleParser.java @@ -15,30 +15,70 @@ public class GptScheduleParser { public List parseScheduleText(String scheduleText) { List schedules = new ArrayList<>(); - Pattern datePattern = Pattern.compile(ParsingConstants.DATE_REGEX); - List lines = List.of(scheduleText.split(ParsingConstants.NEW_LINE_REGEX)); - List currentScheduleDetail = new ArrayList<>(); + List currentScheduleDetail = new ArrayList<>(); String currentDate = ParsingConstants.CURRENT_DATE; for (String line : lines) { - Matcher dateMatcher = datePattern.matcher(line); - if (dateMatcher.find()) { - if (!currentDate.isEmpty()) { - schedules.add(new GptSchedule(currentDate, currentScheduleDetail)); - currentScheduleDetail = new ArrayList<>(); - } - currentDate = dateMatcher.group(ParsingConstants.GROUP_MATCH); - } else if (!line.trim().isEmpty() && ParsingConstants.FILTER_STRINGS.stream().noneMatch(line::contains)) { - currentScheduleDetail.add(line.trim().substring(ParsingConstants.BEGIN_INDEX)); // Remove leading index - } + currentDate = processLine(line, currentDate, currentScheduleDetail, schedules); } if (!currentDate.isEmpty()) { - schedules.add(new GptSchedule(currentDate, currentScheduleDetail)); + addSchedule(schedules, currentDate, currentScheduleDetail); } return schedules; } -} + private String processLine(String line, String currentDate, List currentScheduleDetail, List schedules) { + if (isDateLine(line)) { + if (!currentDate.isEmpty()) { + addSchedule(schedules, currentDate, currentScheduleDetail); + currentScheduleDetail.clear(); + } + return extractDate(line); + } else if (shouldProcessLine(line)) { + addDetail(line, currentScheduleDetail); + } + return currentDate; + } + + private boolean isDateLine(String line) { + Pattern datePattern = Pattern.compile(ParsingConstants.DATE_REGEX); + Matcher dateMatcher = datePattern.matcher(line); + return dateMatcher.find(); + } + + private String extractDate(String line) { + Pattern datePattern = Pattern.compile(ParsingConstants.DATE_REGEX); + Matcher dateMatcher = datePattern.matcher(line); + if (dateMatcher.find()) { + return dateMatcher.group(ParsingConstants.GROUP_MATCH); + } + return ParsingConstants.CURRENT_DATE; + } + + private boolean shouldProcessLine(String line) { + return !line.trim().isEmpty() && ParsingConstants.FILTER_STRINGS.stream().noneMatch(line::contains); + } + + private void addDetail(String line, List currentScheduleDetail) { + Pattern detailPattern = Pattern.compile("^(?:\\d+\\.\\s*)?(.*?)(\\d+\\.\\d{1,8}),\\s*(\\d+\\.\\d{1,8})$"); + Matcher detailMatcher = detailPattern.matcher(line.trim()); + if (detailMatcher.find()) { + String detail = detailMatcher.group(1).trim(); + Double latitude = Double.valueOf(detailMatcher.group(2)); + Double longitude = Double.valueOf(detailMatcher.group(3)); + + String formattedLatitude = String.format("%.8f", latitude); + String formattedLongitude = String.format("%.8f", longitude); + + Coordinate coordinate = new Coordinate(Double.parseDouble(formattedLatitude), Double.parseDouble(formattedLongitude)); + currentScheduleDetail.add(new GptScheduleDetail(detail, coordinate)); + } + } + + private void addSchedule(List schedules, String date, List details) { + schedules.add(new GptSchedule(date, details)); + } +} \ No newline at end of file