From 3fca1a37f9ea4595750f157d1ff04375aa1bf90d Mon Sep 17 00:00:00 2001 From: khyaejin Date: Sun, 25 May 2025 20:28:01 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20[perf]=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=83=9D=EC=84=B1=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?dall-e-3=EB=A1=9C=20=EB=B3=80=EA=B2=BD(dall-e-2=EB=B3=B4?= =?UTF-8?q?=EB=8B=A4=20=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=ED=95=B4?= =?UTF-8?q?=EC=84=9D=20=EB=8A=A5=EB=A0=A5=20=EC=9A=B0=EC=88=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend_Config | 2 +- .../chatbot/controller/ChatbotController.java | 1 - .../openai/dto/ImageCreateRequestDto.java | 15 ++++++ .../openai/dto/ImageCreateResponseDto.java | 22 +++++++++ .../openai/service/ImageCreateService.java | 46 +++++++++++++------ .../quiz/generate/PictureQuizGenerator.java | 4 +- .../server/global/config/OpenAIConfig.java | 21 ++++++++- 7 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java create mode 100644 src/main/java/com/going/server/domain/openai/dto/ImageCreateResponseDto.java diff --git a/Backend_Config b/Backend_Config index 960f98c..c9e90a3 160000 --- a/Backend_Config +++ b/Backend_Config @@ -1 +1 @@ -Subproject commit 960f98c2361ce04653eca80622402539235416f3 +Subproject commit c9e90a3d713c885ee203fa7a939541c42ff59362 diff --git a/src/main/java/com/going/server/domain/chatbot/controller/ChatbotController.java b/src/main/java/com/going/server/domain/chatbot/controller/ChatbotController.java index da36039..f48f0dc 100644 --- a/src/main/java/com/going/server/domain/chatbot/controller/ChatbotController.java +++ b/src/main/java/com/going/server/domain/chatbot/controller/ChatbotController.java @@ -20,7 +20,6 @@ public class ChatbotController { private final ChatbotService chatbotService; - // 지금 챗봇 생성 컨트롤러 만드는 중이었음 @PostMapping("/{graphId}") @Operation(summary = "[챗봇 화면] 챗봇 응답 생성", description = "챗봇 화면에서 사용자의 질문에 응답을 생성합니다.") @ApiResponses( diff --git a/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java b/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java new file mode 100644 index 0000000..ea23319 --- /dev/null +++ b/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java @@ -0,0 +1,15 @@ +package com.going.server.domain.openai.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class ImageCreateRequestDto { + private String prompt; + private String model = "dall-e-3"; + private String size = "1024x1024"; // 기본값으로 지정 + private int n = 1; // 기본값 넣어두기 +} \ No newline at end of file diff --git a/src/main/java/com/going/server/domain/openai/dto/ImageCreateResponseDto.java b/src/main/java/com/going/server/domain/openai/dto/ImageCreateResponseDto.java new file mode 100644 index 0000000..9d8dfcb --- /dev/null +++ b/src/main/java/com/going/server/domain/openai/dto/ImageCreateResponseDto.java @@ -0,0 +1,22 @@ +package com.going.server.domain.openai.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class ImageCreateResponseDto { + private List data; + + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class Data { + private String url; + private String revised_prompt; + } +} \ No newline at end of file diff --git a/src/main/java/com/going/server/domain/openai/service/ImageCreateService.java b/src/main/java/com/going/server/domain/openai/service/ImageCreateService.java index 7767091..d507b5e 100644 --- a/src/main/java/com/going/server/domain/openai/service/ImageCreateService.java +++ b/src/main/java/com/going/server/domain/openai/service/ImageCreateService.java @@ -1,26 +1,42 @@ package com.going.server.domain.openai.service; -import com.theokanning.openai.image.CreateImageRequest; -import com.theokanning.openai.OpenAiService; -import jakarta.annotation.Resource; +import com.going.server.domain.openai.dto.ImageCreateRequestDto; +import com.going.server.domain.openai.dto.ImageCreateResponseDto; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.*; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; @Service -@RequiredArgsConstructor public class ImageCreateService { - @Resource(name = "getOpenAIService") - private final OpenAiService openAiService; + private final RestTemplate restTemplate = new RestTemplate(); + private final String openAIImageUrl; + private final String apiKey; - public String generatePicture(String prompt) { - CreateImageRequest createImageRequest = CreateImageRequest.builder() - .prompt(prompt) - .size("512x512") //사이즈 - .n(1) - .build(); + public ImageCreateService( + @Qualifier("openAIImageUrl") String openAIImageUrl, + @Qualifier("openAIKey") String apiKey + ) { + this.openAIImageUrl = openAIImageUrl; + this.apiKey = apiKey; + } + + public String generatePicture(ImageCreateRequestDto requestDto) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(apiKey); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(requestDto, headers); + + ResponseEntity response = restTemplate.exchange( + openAIImageUrl, + HttpMethod.POST, + entity, + ImageCreateResponseDto.class + ); - //URL로 리턴 (1시간 후 만료) - return openAiService.createImage(createImageRequest).getData().get(0).getUrl(); + return response.getBody().getData().get(0).getUrl(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java b/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java index 77b5f24..9591a7a 100644 --- a/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java +++ b/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java @@ -2,6 +2,7 @@ import com.going.server.domain.graph.entity.Graph; import com.going.server.domain.graph.entity.GraphNode; +import com.going.server.domain.openai.dto.ImageCreateRequestDto; import com.going.server.domain.openai.service.ImageCreateService; import com.going.server.domain.quiz.dto.PictureQuizDto; import lombok.AllArgsConstructor; @@ -59,7 +60,8 @@ public PictureQuizDto generate(Graph graph) { String answer = new ArrayList<>(selectedSentences).get(answerIndex); String prompt = buildImagePrompt(answer); - String imageUrl = imageCreateService.generatePicture(prompt); + ImageCreateRequestDto requestDto = new ImageCreateRequestDto(prompt, "1024x1024", 1); + String imageUrl = imageCreateService.generatePicture(requestDto); return PictureQuizDto.builder() .imageUrl(imageUrl) diff --git a/src/main/java/com/going/server/global/config/OpenAIConfig.java b/src/main/java/com/going/server/global/config/OpenAIConfig.java index 7e31345..284bcc2 100644 --- a/src/main/java/com/going/server/global/config/OpenAIConfig.java +++ b/src/main/java/com/going/server/global/config/OpenAIConfig.java @@ -1,19 +1,36 @@ package com.going.server.global.config; -import org.springframework.context.annotation.Configuration; import com.theokanning.openai.OpenAiService; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.time.Duration; @Configuration public class OpenAIConfig { + @Value("${openai.key}") private String apiKey; + @Value("${openai.image-url}") + private String imageUrl; + + @Value("${openai.timeout:30}") + private int timeout; + @Bean public OpenAiService getOpenAIService() { - return new OpenAiService(apiKey, Duration.ofSeconds(30)); + return new OpenAiService(apiKey, Duration.ofSeconds(timeout)); + } + + @Bean(name = "openAIImageUrl") + public String openAIImageUrl() { + return imageUrl; + } + + @Bean(name = "openAIKey") + public String openAIKey() { + return apiKey; } } From e61a1667eb15ae4f926fc4cdd8cb387dc816dc32 Mon Sep 17 00:00:00 2001 From: khyaejin Date: Sun, 25 May 2025 20:52:43 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20[perf]=20picture=20?= =?UTF-8?q?=ED=80=B4=EC=A6=88=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EA=B2=B0=EA=B3=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openai/dto/ImageCreateRequestDto.java | 13 ++++- .../quiz/generate/PictureQuizGenerator.java | 55 +++++++++++++++++-- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java b/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java index ea23319..89d07c5 100644 --- a/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java +++ b/src/main/java/com/going/server/domain/openai/dto/ImageCreateRequestDto.java @@ -3,13 +3,20 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; @Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class ImageCreateRequestDto { private String prompt; private String model = "dall-e-3"; - private String size = "1024x1024"; // 기본값으로 지정 - private int n = 1; // 기본값 넣어두기 -} \ No newline at end of file + private String style = "vivid"; + private String size = "1024x1024"; + private int n = 1; + + public ImageCreateRequestDto(String prompt) { + this.prompt = prompt; + } +} diff --git a/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java b/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java index 9591a7a..38236be 100644 --- a/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java +++ b/src/main/java/com/going/server/domain/quiz/generate/PictureQuizGenerator.java @@ -59,8 +59,8 @@ public PictureQuizDto generate(Graph graph) { int answerIndex = random.nextInt(shuffledListSize); String answer = new ArrayList<>(selectedSentences).get(answerIndex); - String prompt = buildImagePrompt(answer); - ImageCreateRequestDto requestDto = new ImageCreateRequestDto(prompt, "1024x1024", 1); + String prompt = buildQuizImagePrompt(answer); + ImageCreateRequestDto requestDto = new ImageCreateRequestDto(prompt); String imageUrl = imageCreateService.generatePicture(requestDto); return PictureQuizDto.builder() @@ -71,9 +71,54 @@ public PictureQuizDto generate(Graph graph) { } // 이미지 생성 프롬프트 생성 메서드 - private String buildImagePrompt(String answer) { - return "아래 설명을 이미지로 표현해주세요.\n\n" - + "[설명]\n" + answer; + + // 버전1 +// private String buildQuizImagePrompt(String answer) { +// return "You are given an educational description in natural language.\n\n" + +// "1. First, analyze the sentence to determine what kind of relationship it contains, such as:\n" + +// "- Cause and effect\n" + +// "- Inclusion or category\n" + +// "- Example and concept\n" + +// "- Behavioral actions\n" + +// "- General explanation\n\n" + +// "2. Then, generate a **cute, warm, and educational diagram-style illustration** that reflects the structure and meaning of the sentence.\n\n" + +// "Use **flat vector illustrations inspired by iOS emojis**, with **bright and soft colors**.\n" + +// "If the sentence includes multiple ideas, arrange the illustration using diagrams, arrows, or symbolic layouts that match the logical structure.\n" + +// "Do **not include any text or labels** in the image. Use only visuals.\n\n" + +// "[Description]\n" + answer; +// } + + // 버전2 +// public String buildQuizImagePrompt(String answer) { +// return "You are given an educational description in natural language.\n\n" + +// "1. First, analyze the sentence to determine what kind of relationship it contains, such as:\n" + +// "- Cause and effect\n" + +// "- Inclusion or category\n" + +// "- Example and concept\n" + +// "- Behavioral actions\n" + +// "- General explanation\n\n" + +// "2. Then, generate a cute, warm, and educational diagram-style illustration that reflects the structure and meaning of the sentence.\n\n" + +// "Use flat vector illustrations inspired by iOS emojis, with bright and soft colors.\n" + +// "If the sentence includes multiple ideas, arrange the illustration using symbols or visual layouts like arrows, sets, or diagrams **only when necessary to express the logical relationship**.\n" + +// "Do not include any text or labels in the image. Use only visuals.\n\n" + +// "[Description]\n" + answer; +// } + + // 버전3 + public static String buildQuizImagePrompt(String answer) { + return "You are given an educational description in natural language.\n\n" + + "1. First, analyze the sentence to determine what kind of relationship it contains, such as:\n" + + "- Cause and effect\n" + + "- Inclusion or category\n" + + "- Example and concept\n" + + "- Behavioral actions\n" + + "- General explanation\n\n" + + "2. Then, generate a cute, warm, and educational diagram-style illustration that reflects the structure and meaning of the sentence.\n\n" + + "Use flat vector illustrations inspired by iOS emojis, with bright and soft colors.\n\n" + + "If the sentence includes multiple ideas or relationships, use simple visual symbols like arrows or grouping layouts to represent those relationships **only when necessary**.\n" + + "Do **not overuse symbols**—use them only when they help express meaning clearly.\n\n" + + "Do not include any text or labels in the image. Use only visuals.\n\n" + + "[Description]\n" + answer; } }