From af543e5861e83442ce939efe44c8810859a0f48d Mon Sep 17 00:00:00 2001 From: Kateryna Hrytsaienko Date: Sun, 26 Nov 2023 21:08:58 +0200 Subject: [PATCH 01/11] add spring ai controller and sey up project --- spring-ai/.gitignore | 12 ++++ spring-ai/pom.xml | 61 +++++++++++++++++++ .../spring/ai/SpringAIProjectApplication.java | 9 +++ .../com/baeldung/spring/ai/dto/PoetryDto.java | 3 + .../spring/ai/web/PoetryController.java | 54 ++++++++++++++++ spring-ai/src/main/resources/application.yml | 3 + 6 files changed, 142 insertions(+) create mode 100644 spring-ai/.gitignore create mode 100644 spring-ai/pom.xml create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/SpringAIProjectApplication.java create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java create mode 100644 spring-ai/src/main/resources/application.yml diff --git a/spring-ai/.gitignore b/spring-ai/.gitignore new file mode 100644 index 0000000000..dec013dfa4 --- /dev/null +++ b/spring-ai/.gitignore @@ -0,0 +1,12 @@ +#folders# +.idea +/target +/neoDb* +/data +/src/main/webapp/WEB-INF/classes +*/META-INF/* + +# Packaged files # +*.jar +*.war +*.ear \ No newline at end of file diff --git a/spring-ai/pom.xml b/spring-ai/pom.xml new file mode 100644 index 0000000000..52eba30508 --- /dev/null +++ b/spring-ai/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + spring-ai + spring-ai + war + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../parent-boot-2 + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + false + + + + + + + org.springframework.experimental.ai + spring-ai-openai-spring-boot-starter + 0.7.1-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.spring.ai.SpringAIProjectApplication + JAR + + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + + + \ No newline at end of file diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/SpringAIProjectApplication.java b/spring-ai/src/main/java/com/baeldung/spring/ai/SpringAIProjectApplication.java new file mode 100644 index 0000000000..3dfa800d26 --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/SpringAIProjectApplication.java @@ -0,0 +1,9 @@ +package com.baeldung.spring.ai; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringAIProjectApplication { + public static void main(String[] args) { SpringApplication.run(SpringAIProjectApplication.class, args);} +} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java new file mode 100644 index 0000000000..b51c511659 --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java @@ -0,0 +1,3 @@ +package com.baeldung.spring.ai.dto; + +public record PoetryDto (String title, String poetry){} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java new file mode 100644 index 0000000000..b6e510c65b --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java @@ -0,0 +1,54 @@ +package com.baeldung.spring.ai.web; + +import com.baeldung.spring.ai.dto.PoetryDto; +import org.springframework.ai.client.AiClient; +import org.springframework.ai.client.AiResponse; +import org.springframework.ai.parser.BeanOutputParser; +import org.springframework.ai.prompt.PromptTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("ai") +public class PoetryController { + + public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat"; + private final AiClient aiClient; + + @Autowired + public PoetryController(AiClient aiClient) { + this.aiClient = aiClient; + } + + @GetMapping("/cathaiku") + public ResponseEntity generateHaiku(){ + return ResponseEntity.ok(aiClient.generate(WRITE_ME_HAIKU_ABOUT_CAT)); + } + + @GetMapping("/poetry") + public ResponseEntity generatePoetry( + @RequestParam("genre") String genre, + @RequestParam("theme") String theme + ){ + BeanOutputParser poetryDtoBeanOutputParser = new BeanOutputParser<>(PoetryDto.class); + + String promtString = """ + Write me {genre} poetry about {theme} + {format} + """; + + PromptTemplate promptTemplate = new PromptTemplate(promtString); + promptTemplate.add("genre", genre); + promptTemplate.add("theme", theme); + promptTemplate.add("format", poetryDtoBeanOutputParser.getFormat()); + + promptTemplate.setOutputParser(poetryDtoBeanOutputParser); + + AiResponse response = aiClient.generate(promptTemplate.create()); + return ResponseEntity.ok(poetryDtoBeanOutputParser.parse(response.getGeneration().getText())); + } +} diff --git a/spring-ai/src/main/resources/application.yml b/spring-ai/src/main/resources/application.yml new file mode 100644 index 0000000000..b9d9ee1ce8 --- /dev/null +++ b/spring-ai/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + ai: + openai.api-key: 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 \ No newline at end of file From d0ca98b95fe2b9765a6a53b99e03f93e608f5eb0 Mon Sep 17 00:00:00 2001 From: Kateryna Hrytsaienko Date: Sun, 26 Nov 2023 21:17:03 +0200 Subject: [PATCH 02/11] BAEL-7142, modify application.ym; --- spring-ai/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-ai/src/main/resources/application.yml b/spring-ai/src/main/resources/application.yml index b9d9ee1ce8..dfc8294e45 100644 --- a/spring-ai/src/main/resources/application.yml +++ b/spring-ai/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: ai: - openai.api-key: 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 \ No newline at end of file + openai.api-key: ${OPEN_AI_KEY} \ No newline at end of file From 684c91239dfe06c9a6bcd002eca6142c877aaaec Mon Sep 17 00:00:00 2001 From: Kateryna Hrytsaienko Date: Sun, 26 Nov 2023 22:16:13 +0200 Subject: [PATCH 03/11] BAEL-7142, modify add exeption handler and service layer --- .../Spring_AI_Poetry.postman_environment.json | 25 +++++++++ .../postman/spring-ai.postman_collection.json | 56 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 spring-ai/postman/Spring_AI_Poetry.postman_environment.json create mode 100644 spring-ai/postman/spring-ai.postman_collection.json diff --git a/spring-ai/postman/Spring_AI_Poetry.postman_environment.json b/spring-ai/postman/Spring_AI_Poetry.postman_environment.json new file mode 100644 index 0000000000..b3d4a00bee --- /dev/null +++ b/spring-ai/postman/Spring_AI_Poetry.postman_environment.json @@ -0,0 +1,25 @@ +{ + "id": "df61838b-eb6f-4243-87ee-ca02c77e8646", + "name": "Spring_AI_Poetry", + "values": [ + { + "key": "baseUrl", + "value": "localhost:8080", + "type": "default", + "enabled": true + }, + { + "key": "genre", + "value": "liric", + "enabled": true + }, + { + "key": "theme", + "value": "flames", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2023-11-26T19:49:21.755Z", + "_postman_exported_using": "Postman/10.20.3" +} \ No newline at end of file diff --git a/spring-ai/postman/spring-ai.postman_collection.json b/spring-ai/postman/spring-ai.postman_collection.json new file mode 100644 index 0000000000..b26652bb4e --- /dev/null +++ b/spring-ai/postman/spring-ai.postman_collection.json @@ -0,0 +1,56 @@ +{ + "info": { + "_postman_id": "f4282fac-bfe5-45b9-aae6-5ea7c43528ee", + "name": "spring-ai", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "9576856" + }, + "item": [ + { + "name": "Generate poetry with genre and theme", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/ai/poetry?genre={{genre}}&theme={{theme}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "ai", + "poetry" + ], + "query": [ + { + "key": "genre", + "value": "{{genre}}" + }, + { + "key": "theme", + "value": "{{theme}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Generate haiku about cats", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/ai/cathaiku", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "ai", + "cathaiku" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file From 490612f9c7fe7db5bfb02165589848d3a6a68e0c Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Sun, 26 Nov 2023 22:48:58 +0200 Subject: [PATCH 04/11] BAEL-7142, add integration test for haikucat endpoint --- spring-ai/pom.xml | 4 +- .../spring/ai/web/ExceptionTranslator.java | 19 ++++++++++ .../spring/ai/web/PoetryController.java | 2 +- .../web/PoetryControllerIntegrationTest.java | 38 +++++++++++++++++++ spring-ai/src/test/resources/application.yml | 3 ++ 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java create mode 100644 spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java create mode 100644 spring-ai/src/test/resources/application.yml diff --git a/spring-ai/pom.xml b/spring-ai/pom.xml index 52eba30508..572941358b 100644 --- a/spring-ai/pom.xml +++ b/spring-ai/pom.xml @@ -9,9 +9,9 @@ com.baeldung - parent-boot-2 + parent-boot-3 0.0.1-SNAPSHOT - ../parent-boot-2 + ../parent-boot-3 diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java b/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java new file mode 100644 index 0000000000..98c6a35e94 --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java @@ -0,0 +1,19 @@ +package com.baeldung.spring.ai.web; + +import com.theokanning.openai.OpenAiHttpException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@RestControllerAdvice +public class ExceptionTranslator extends ResponseEntityExceptionHandler { + + @ExceptionHandler(OpenAiHttpException.class) + ProblemDetail handleOpenAiHttpException(OpenAiHttpException ex){ + ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage()); + problemDetail.setTitle("Open AI client raised exception"); + return problemDetail; + } +} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java index b6e510c65b..7c612e5fee 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("ai") public class PoetryController { - public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat"; + public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat, haiku should contain word cat"; private final AiClient aiClient; @Autowired diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java new file mode 100644 index 0000000000..782eb6aa86 --- /dev/null +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java @@ -0,0 +1,38 @@ +package com.baeldung.spring.ai.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.ai.client.AiClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@SpringBootTest +public class PoetryControllerIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private AiClient aiClient; + + @Test + public void getCatHaikuTest() throws Exception { + mockMvc.perform(get("/ai/cathaiku")) + .andExpect(status().isOk()) + .andExpect(content().string(containsStringIgnoringCase("cat"))); + } +} diff --git a/spring-ai/src/test/resources/application.yml b/spring-ai/src/test/resources/application.yml new file mode 100644 index 0000000000..dfc8294e45 --- /dev/null +++ b/spring-ai/src/test/resources/application.yml @@ -0,0 +1,3 @@ +spring: + ai: + openai.api-key: ${OPEN_AI_KEY} \ No newline at end of file From 07b7278310cacac939f12a1e0365cbf10e62b4d8 Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Mon, 27 Nov 2023 11:01:39 +0200 Subject: [PATCH 05/11] BAEL-7142, move the logic to Service layer --- .../spring/ai/service/PoetryService.java | 10 ++++ .../ai/service/impl/PoetryServiceImpl.java | 48 +++++++++++++++++++ .../spring/ai/web/PoetryController.java | 27 +++-------- 3 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/service/PoetryService.java create mode 100644 spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/service/PoetryService.java b/spring-ai/src/main/java/com/baeldung/spring/ai/service/PoetryService.java new file mode 100644 index 0000000000..e2760b8950 --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/service/PoetryService.java @@ -0,0 +1,10 @@ +package com.baeldung.spring.ai.service; + +import com.baeldung.spring.ai.dto.PoetryDto; + +public interface PoetryService { + + String getCatHaiku(); + + PoetryDto getPoetryByGenreAndTheme(String genre, String theme); +} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java new file mode 100644 index 0000000000..26ae407a6e --- /dev/null +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java @@ -0,0 +1,48 @@ +package com.baeldung.spring.ai.service.impl; + +import com.baeldung.spring.ai.dto.PoetryDto; +import com.baeldung.spring.ai.service.PoetryService; +import org.springframework.ai.client.AiClient; +import org.springframework.ai.client.AiResponse; +import org.springframework.ai.parser.BeanOutputParser; +import org.springframework.ai.prompt.PromptTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.bind.annotation.GetMapping; + +@Service +public class PoetryServiceImpl implements PoetryService { + + public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat, haiku should contain word cat"; + private final AiClient aiClient; + + @Autowired + public PoetryServiceImpl(AiClient aiClient) { + this.aiClient = aiClient; + } + @Override + public String getCatHaiku() { + return aiClient.generate(WRITE_ME_HAIKU_ABOUT_CAT); + } + + @Override + public PoetryDto getPoetryByGenreAndTheme(String genre, String theme) { + BeanOutputParser poetryDtoBeanOutputParser = new BeanOutputParser<>(PoetryDto.class); + + String promtString = """ + Write me {genre} poetry about {theme} + {format} + """; + + PromptTemplate promptTemplate = new PromptTemplate(promtString); + promptTemplate.add("genre", genre); + promptTemplate.add("theme", theme); + promptTemplate.add("format", poetryDtoBeanOutputParser.getFormat()); + + promptTemplate.setOutputParser(poetryDtoBeanOutputParser); + + AiResponse response = aiClient.generate(promptTemplate.create()); + return poetryDtoBeanOutputParser.parse(response.getGeneration().getText()); + } +} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java index 7c612e5fee..27c1bad6e2 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/web/PoetryController.java @@ -1,6 +1,7 @@ package com.baeldung.spring.ai.web; import com.baeldung.spring.ai.dto.PoetryDto; +import com.baeldung.spring.ai.service.PoetryService; import org.springframework.ai.client.AiClient; import org.springframework.ai.client.AiResponse; import org.springframework.ai.parser.BeanOutputParser; @@ -16,17 +17,16 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("ai") public class PoetryController { - public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat, haiku should contain word cat"; - private final AiClient aiClient; + private final PoetryService poetryService; @Autowired - public PoetryController(AiClient aiClient) { - this.aiClient = aiClient; + public PoetryController(PoetryService poetryService) { + this.poetryService = poetryService; } @GetMapping("/cathaiku") public ResponseEntity generateHaiku(){ - return ResponseEntity.ok(aiClient.generate(WRITE_ME_HAIKU_ABOUT_CAT)); + return ResponseEntity.ok(poetryService.getCatHaiku()); } @GetMapping("/poetry") @@ -34,21 +34,6 @@ public class PoetryController { @RequestParam("genre") String genre, @RequestParam("theme") String theme ){ - BeanOutputParser poetryDtoBeanOutputParser = new BeanOutputParser<>(PoetryDto.class); - - String promtString = """ - Write me {genre} poetry about {theme} - {format} - """; - - PromptTemplate promptTemplate = new PromptTemplate(promtString); - promptTemplate.add("genre", genre); - promptTemplate.add("theme", theme); - promptTemplate.add("format", poetryDtoBeanOutputParser.getFormat()); - - promptTemplate.setOutputParser(poetryDtoBeanOutputParser); - - AiResponse response = aiClient.generate(promptTemplate.create()); - return ResponseEntity.ok(poetryDtoBeanOutputParser.parse(response.getGeneration().getText())); + return ResponseEntity.ok(poetryService.getPoetryByGenreAndTheme(genre, theme)); } } From 8492c4dffacd287c9ab29c1baa6a745519c572ea Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Mon, 27 Nov 2023 15:45:11 +0200 Subject: [PATCH 06/11] BAEL-7142, finilize the pr, refresh test cases --- .../com/baeldung/spring/ai/dto/PoetryDto.java | 2 +- .../ai/service/impl/PoetryServiceImpl.java | 4 +- .../spring/ai/web/ExceptionTranslator.java | 14 ++++-- .../ExceptionTranslatorIntegrationTest.java | 45 +++++++++++++++++++ .../web/PoetryControllerIntegrationTest.java | 17 +++++-- 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java index b51c511659..a636189f2e 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java @@ -1,3 +1,3 @@ package com.baeldung.spring.ai.dto; -public record PoetryDto (String title, String poetry){} +public record PoetryDto (String title, String poetry, String genre, String theme){} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java index 26ae407a6e..bceddcd67e 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java @@ -14,7 +14,9 @@ import org.springframework.web.bind.annotation.GetMapping; @Service public class PoetryServiceImpl implements PoetryService { - public static final String WRITE_ME_HAIKU_ABOUT_CAT = "Write me Haiku about cat, haiku should contain word cat"; + public static final String WRITE_ME_HAIKU_ABOUT_CAT = """ + Write me Haiku about cat, + haiku should start with the word cat obligatory"""; private final AiClient aiClient; @Autowired diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java b/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java index 98c6a35e94..55ba383fa6 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/web/ExceptionTranslator.java @@ -7,13 +7,21 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import java.util.Optional; + @RestControllerAdvice public class ExceptionTranslator extends ResponseEntityExceptionHandler { + public static final String OPEN_AI_CLIENT_RAISED_EXCEPTION = "Open AI client raised exception"; + @ExceptionHandler(OpenAiHttpException.class) - ProblemDetail handleOpenAiHttpException(OpenAiHttpException ex){ - ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage()); - problemDetail.setTitle("Open AI client raised exception"); + ProblemDetail handleOpenAiHttpException(OpenAiHttpException ex) { + HttpStatus status = Optional + .ofNullable(HttpStatus.resolve(ex.statusCode)) + .orElse(HttpStatus.BAD_REQUEST); + + ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, ex.getMessage()); + problemDetail.setTitle(OPEN_AI_CLIENT_RAISED_EXCEPTION); return problemDetail; } } diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java new file mode 100644 index 0000000000..4795dce1f8 --- /dev/null +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java @@ -0,0 +1,45 @@ +package com.baeldung.spring.ai.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.ai.client.AiClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static java.lang.String.format; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@SpringBootTest +public class ExceptionTranslatorIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private AiClient aiClient; + + @DynamicPropertySource + static void setupTestcontainerProperties(DynamicPropertyRegistry registry) { + registry.add("spring.ai.openai.api-key", () -> "incorrect_token"); + } + + @Test + public void givenGetCatHaiku_whenCallingAiClientWithIncorrectToken_thenUnauthorized() throws Exception { + mockMvc.perform(get("/ai/cathaiku")) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.title").value(ExceptionTranslator.OPEN_AI_CLIENT_RAISED_EXCEPTION)); + } +} diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java index 782eb6aa86..7a454f09fa 100644 --- a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java @@ -11,9 +11,8 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import static org.hamcrest.Matchers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @AutoConfigureMockMvc @RunWith(SpringRunner.class) @@ -30,9 +29,21 @@ public class PoetryControllerIntegrationTest { private AiClient aiClient; @Test - public void getCatHaikuTest() throws Exception { + public void givenGetCatHaiku_whenCallingAiClient_thenCorrect() throws Exception { mockMvc.perform(get("/ai/cathaiku")) .andExpect(status().isOk()) .andExpect(content().string(containsStringIgnoringCase("cat"))); } + + @Test + public void givenGetPoetryWithGenreAndTheme_whenCallingAiClient_thenCorrect() throws Exception { + String genre = "lyric"; + String theme = "coffee"; + mockMvc.perform(get("/ai/poetry?genre={genre}&theme={theme}", genre, theme)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.genre").value(containsStringIgnoringCase(genre))) + .andExpect(jsonPath("$.theme").value(containsStringIgnoringCase(theme))) + .andExpect(jsonPath("$.poetry").isNotEmpty()) + .andExpect(jsonPath("$.title").exists()); + } } From c8dafdf0e40f71699bda49f3ac272c8c4d925efe Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Mon, 27 Nov 2023 16:42:34 +0200 Subject: [PATCH 07/11] BAEL-742, add spring ai module to pom file --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index 3452ae81ee..551cb66688 100644 --- a/pom.xml +++ b/pom.xml @@ -835,6 +835,7 @@ spring-5 spring-activiti spring-actuator + spring-ai spring-aop-2 spring-aop spring-batch-2 @@ -1080,6 +1081,7 @@ spring-5 spring-activiti spring-actuator + spring-ai spring-aop-2 spring-aop spring-batch-2 From e1116c86bdffca18fd4079ce7736b2b98d015a8d Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Mon, 27 Nov 2023 17:05:05 +0200 Subject: [PATCH 08/11] BAEL-7142, fix the tests to not call external client --- .../ExceptionTranslatorIntegrationTest.java | 45 ----------------- .../web/PoetryControllerIntegrationTest.java | 50 ++++++++++++++++--- spring-ai/src/test/resources/application.yml | 2 +- 3 files changed, 45 insertions(+), 52 deletions(-) delete mode 100644 spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java deleted file mode 100644 index 4795dce1f8..0000000000 --- a/spring-ai/src/test/java/com/baeldung/spring/ai/web/ExceptionTranslatorIntegrationTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.baeldung.spring.ai.web; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.ai.client.AiClient; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import static java.lang.String.format; -import static org.hamcrest.Matchers.containsStringIgnoringCase; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@AutoConfigureMockMvc -@RunWith(SpringRunner.class) -@SpringBootTest -public class ExceptionTranslatorIntegrationTest { - - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private AiClient aiClient; - - @DynamicPropertySource - static void setupTestcontainerProperties(DynamicPropertyRegistry registry) { - registry.add("spring.ai.openai.api-key", () -> "incorrect_token"); - } - - @Test - public void givenGetCatHaiku_whenCallingAiClientWithIncorrectToken_thenUnauthorized() throws Exception { - mockMvc.perform(get("/ai/cathaiku")) - .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.title").value(ExceptionTranslator.OPEN_AI_CLIENT_RAISED_EXCEPTION)); - } -} diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java index 7a454f09fa..b951c53f1e 100644 --- a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java @@ -1,16 +1,29 @@ package com.baeldung.spring.ai.web; +import com.baeldung.spring.ai.dto.PoetryDto; import com.fasterxml.jackson.databind.ObjectMapper; +import com.theokanning.openai.OpenAiError; +import com.theokanning.openai.OpenAiHttpException; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.ai.client.AiClient; +import org.springframework.ai.client.AiResponse; +import org.springframework.ai.client.Generation; +import org.springframework.ai.prompt.Prompt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; +import java.util.List; +import java.util.Optional; + import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -19,31 +32,56 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SpringBootTest public class PoetryControllerIntegrationTest { + public static final String GENERATED_HAIKU = "generated haiku"; + public static final String TEST_TITLE = "test_title"; + public static final String SOME_POETRY = "some poetry"; + public static final String TEST_GENRE = "test_genre"; + public static final String TEST_THEME = "test_theme"; @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; - @Autowired + @MockBean private AiClient aiClient; @Test public void givenGetCatHaiku_whenCallingAiClient_thenCorrect() throws Exception { + when(aiClient.generate(anyString())).thenReturn(GENERATED_HAIKU); mockMvc.perform(get("/ai/cathaiku")) .andExpect(status().isOk()) - .andExpect(content().string(containsStringIgnoringCase("cat"))); + .andExpect(content().string(GENERATED_HAIKU)); } @Test public void givenGetPoetryWithGenreAndTheme_whenCallingAiClient_thenCorrect() throws Exception { + PoetryDto poetryDto = new PoetryDto( + TEST_TITLE, + SOME_POETRY, + TEST_GENRE, + TEST_THEME + ); + when(aiClient.generate((Prompt) any(Prompt.class))).thenReturn( + new AiResponse(List.of(new Generation(objectMapper.writeValueAsString(poetryDto))))); String genre = "lyric"; String theme = "coffee"; mockMvc.perform(get("/ai/poetry?genre={genre}&theme={theme}", genre, theme)) .andExpect(status().isOk()) - .andExpect(jsonPath("$.genre").value(containsStringIgnoringCase(genre))) - .andExpect(jsonPath("$.theme").value(containsStringIgnoringCase(theme))) - .andExpect(jsonPath("$.poetry").isNotEmpty()) - .andExpect(jsonPath("$.title").exists()); + .andExpect(jsonPath("$.genre").value(poetryDto.genre())) + .andExpect(jsonPath("$.theme").value(poetryDto.theme())) + .andExpect(jsonPath("$.poetry").value(poetryDto.poetry())) + .andExpect(jsonPath("$.title").value(poetryDto.title())); + } + + @Test + public void givenGetCatHaiku_whenCallingAiClientWithIncorrectToken_thenUnauthorized() throws Exception { + when(aiClient.generate(anyString())).thenThrow(new OpenAiHttpException( + new OpenAiError(new OpenAiError.OpenAiErrorDetails()), + new Exception(), + 401)); + mockMvc.perform(get("/ai/cathaiku")) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.title").value(ExceptionTranslator.OPEN_AI_CLIENT_RAISED_EXCEPTION)); } } diff --git a/spring-ai/src/test/resources/application.yml b/spring-ai/src/test/resources/application.yml index dfc8294e45..f3b2cb8d7b 100644 --- a/spring-ai/src/test/resources/application.yml +++ b/spring-ai/src/test/resources/application.yml @@ -1,3 +1,3 @@ spring: ai: - openai.api-key: ${OPEN_AI_KEY} \ No newline at end of file + openai.api-key: "mock-key-token" \ No newline at end of file From 571d8e2aca1490d7ef6da858fc12263ab1238ce3 Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Thu, 30 Nov 2023 16:17:45 +0200 Subject: [PATCH 09/11] BAEL-7142, fix the tests to the manual after aligment --- .../web/PoetryControllerIntegrationTest.java | 87 ------------------- .../ai/web/PoetryControllerManualTest.java | 51 +++++++++++ 2 files changed, 51 insertions(+), 87 deletions(-) delete mode 100644 spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java create mode 100644 spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java deleted file mode 100644 index b951c53f1e..0000000000 --- a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerIntegrationTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.baeldung.spring.ai.web; - -import com.baeldung.spring.ai.dto.PoetryDto; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.theokanning.openai.OpenAiError; -import com.theokanning.openai.OpenAiHttpException; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.ai.client.AiClient; -import org.springframework.ai.client.AiResponse; -import org.springframework.ai.client.Generation; -import org.springframework.ai.prompt.Prompt; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; - -import java.util.List; -import java.util.Optional; - -import static org.hamcrest.Matchers.*; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@AutoConfigureMockMvc -@RunWith(SpringRunner.class) -@SpringBootTest -public class PoetryControllerIntegrationTest { - - public static final String GENERATED_HAIKU = "generated haiku"; - public static final String TEST_TITLE = "test_title"; - public static final String SOME_POETRY = "some poetry"; - public static final String TEST_GENRE = "test_genre"; - public static final String TEST_THEME = "test_theme"; - @Autowired - private MockMvc mockMvc; - - @Autowired - private ObjectMapper objectMapper; - - @MockBean - private AiClient aiClient; - - @Test - public void givenGetCatHaiku_whenCallingAiClient_thenCorrect() throws Exception { - when(aiClient.generate(anyString())).thenReturn(GENERATED_HAIKU); - mockMvc.perform(get("/ai/cathaiku")) - .andExpect(status().isOk()) - .andExpect(content().string(GENERATED_HAIKU)); - } - - @Test - public void givenGetPoetryWithGenreAndTheme_whenCallingAiClient_thenCorrect() throws Exception { - PoetryDto poetryDto = new PoetryDto( - TEST_TITLE, - SOME_POETRY, - TEST_GENRE, - TEST_THEME - ); - when(aiClient.generate((Prompt) any(Prompt.class))).thenReturn( - new AiResponse(List.of(new Generation(objectMapper.writeValueAsString(poetryDto))))); - String genre = "lyric"; - String theme = "coffee"; - mockMvc.perform(get("/ai/poetry?genre={genre}&theme={theme}", genre, theme)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.genre").value(poetryDto.genre())) - .andExpect(jsonPath("$.theme").value(poetryDto.theme())) - .andExpect(jsonPath("$.poetry").value(poetryDto.poetry())) - .andExpect(jsonPath("$.title").value(poetryDto.title())); - } - - @Test - public void givenGetCatHaiku_whenCallingAiClientWithIncorrectToken_thenUnauthorized() throws Exception { - when(aiClient.generate(anyString())).thenThrow(new OpenAiHttpException( - new OpenAiError(new OpenAiError.OpenAiErrorDetails()), - new Exception(), - 401)); - mockMvc.perform(get("/ai/cathaiku")) - .andExpect(status().isUnauthorized()) - .andExpect(jsonPath("$.title").value(ExceptionTranslator.OPEN_AI_CLIENT_RAISED_EXCEPTION)); - } -} diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java new file mode 100644 index 0000000000..d1750bdb4a --- /dev/null +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java @@ -0,0 +1,51 @@ +package com.baeldung.spring.ai.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.ai.client.AiClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@AutoConfigureMockMvc +@RunWith(SpringRunner.class) +@SpringBootTest +public class PoetryControllerManualTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private AiClient aiClient; + + @Test + public void givenGetCatHaiku_whenCallingAiClient_thenCorrect() throws Exception { + mockMvc.perform(get("/ai/cathaiku")) + .andExpect(status().isOk()) + .andExpect(content().string(containsStringIgnoringCase("cat"))); + } + + @Test + public void givenGetPoetryWithGenreAndTheme_whenCallingAiClient_thenCorrect() throws Exception { + String genre = "lyric"; + String theme = "coffee"; + mockMvc.perform(get("/ai/poetry?genre={genre}&theme={theme}", genre, theme)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.genre").value(containsStringIgnoringCase(genre))) + .andExpect(jsonPath("$.theme").value(containsStringIgnoringCase(theme))) + .andExpect(jsonPath("$.poetry").isNotEmpty()) + .andExpect(jsonPath("$.title").exists()); + } +} + +} From 5f3b4660f6b0c0f1189bde6036bb9d8fc3bc5713 Mon Sep 17 00:00:00 2001 From: Ekatereana Date: Thu, 30 Nov 2023 21:17:10 +0200 Subject: [PATCH 10/11] BAEL-7142, fix manual tests exclusion --- spring-ai/pom.xml | 3 +++ .../com/baeldung/spring/ai/web/PoetryControllerManualTest.java | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-ai/pom.xml b/spring-ai/pom.xml index 572941358b..b966ae9898 100644 --- a/spring-ai/pom.xml +++ b/spring-ai/pom.xml @@ -53,6 +53,9 @@ 16 16 + + **/*ManualTest.java + diff --git a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java index d1750bdb4a..23202e6bce 100644 --- a/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java +++ b/spring-ai/src/test/java/com/baeldung/spring/ai/web/PoetryControllerManualTest.java @@ -48,4 +48,3 @@ public class PoetryControllerManualTest { } } -} From bf45a052f4408bb96a0de9fd65ea26cc5d029f3e Mon Sep 17 00:00:00 2001 From: "kateryna.hrytsaenko" Date: Tue, 26 Dec 2023 21:35:19 +0200 Subject: [PATCH 11/11] BAEL-7142, fix minor comment --- spring-ai/.gitignore | 2 +- spring-ai/pom.xml | 3 +-- .../src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java | 2 +- .../baeldung/spring/ai/service/impl/PoetryServiceImpl.java | 4 ++-- spring-ai/src/main/resources/application.yml | 2 +- spring-ai/src/test/resources/application.yml | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/spring-ai/.gitignore b/spring-ai/.gitignore index dec013dfa4..f475e4f82b 100644 --- a/spring-ai/.gitignore +++ b/spring-ai/.gitignore @@ -9,4 +9,4 @@ # Packaged files # *.jar *.war -*.ear \ No newline at end of file +*.ear diff --git a/spring-ai/pom.xml b/spring-ai/pom.xml index b966ae9898..d0e8ab2d1b 100644 --- a/spring-ai/pom.xml +++ b/spring-ai/pom.xml @@ -60,5 +60,4 @@ - - \ No newline at end of file + diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java index a636189f2e..229d2b13f0 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/dto/PoetryDto.java @@ -1,3 +1,3 @@ package com.baeldung.spring.ai.dto; -public record PoetryDto (String title, String poetry, String genre, String theme){} +public record PoetryDto (String title, String poetry, String genre, String theme) {} diff --git a/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java index bceddcd67e..791dba372e 100644 --- a/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java +++ b/spring-ai/src/main/java/com/baeldung/spring/ai/service/impl/PoetryServiceImpl.java @@ -32,12 +32,12 @@ public class PoetryServiceImpl implements PoetryService { public PoetryDto getPoetryByGenreAndTheme(String genre, String theme) { BeanOutputParser poetryDtoBeanOutputParser = new BeanOutputParser<>(PoetryDto.class); - String promtString = """ + String promptString = """ Write me {genre} poetry about {theme} {format} """; - PromptTemplate promptTemplate = new PromptTemplate(promtString); + PromptTemplate promptTemplate = new PromptTemplate(promptString); promptTemplate.add("genre", genre); promptTemplate.add("theme", theme); promptTemplate.add("format", poetryDtoBeanOutputParser.getFormat()); diff --git a/spring-ai/src/main/resources/application.yml b/spring-ai/src/main/resources/application.yml index dfc8294e45..ec3b936adc 100644 --- a/spring-ai/src/main/resources/application.yml +++ b/spring-ai/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: ai: - openai.api-key: ${OPEN_AI_KEY} \ No newline at end of file + openai.api-key: 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 diff --git a/spring-ai/src/test/resources/application.yml b/spring-ai/src/test/resources/application.yml index f3b2cb8d7b..813348fec5 100644 --- a/spring-ai/src/test/resources/application.yml +++ b/spring-ai/src/test/resources/application.yml @@ -1,3 +1,3 @@ spring: ai: - openai.api-key: "mock-key-token" \ No newline at end of file + openai.api-key: "mock-key-token"