Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions spring-ai-modules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
</parent>

<modules>
<module>spring-ai-introduction</module>
<module>spring-ai-mcp</module>
<module>spring-ai-text-to-sql</module>
<module>spring-ai-vector-stores</module>
Expand Down
49 changes: 49 additions & 0 deletions spring-ai-modules/spring-ai-introduction/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-ai-modules</artifactId>
<version>0.0.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.baeldung</groupId>
<artifactId>spring-ai-introduction</artifactId>
<version>0.0.1</version>
<name>spring-ai-introduction</name>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.1</spring-ai.version>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.baeldung.springai;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.openai.api.common.OpenAiApiClientErrorException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
class APIExceptionHandler {

private static final Logger logger = LoggerFactory.getLogger(APIExceptionHandler.class);
private static final String LLM_COMMUNICATION_ERROR =
"Unable to communicate with the configured LLM. Please try again later.";

@ExceptionHandler(OpenAiApiClientErrorException.class)
ProblemDetail handle(OpenAiApiClientErrorException exception) {
logger.error("OpenAI returned an error.", exception);
return ProblemDetail.forStatusAndDetail(HttpStatus.SERVICE_UNAVAILABLE, LLM_COMMUNICATION_ERROR);
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.baeldung;
package com.baeldung.springai;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringAIProjectApplication {
class Application {

public static void main(String[] args) {
SpringApplication.run(SpringAIProjectApplication.class, args);
SpringApplication.run(Application.class, args);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.baeldung.springai;

record Poem(
String title,
String content,
String genre,
String theme) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.baeldung.springai;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
class PoetryController {

private final PoetryService poetryService;

PoetryController(PoetryService poetryService) {
this.poetryService = poetryService;
}

@PostMapping("/poems")
ResponseEntity<Poem> generate(@RequestBody PoemGenerationRequest request) {
Poem response = poetryService.generate(request.genre, request.theme);
return ResponseEntity.ok(response);
}

record PoemGenerationRequest(String genre, String theme) {}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.baeldung.springai;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
class PoetryService {

private final static PromptTemplate PROMPT_TEMPLATE
= new PromptTemplate("Write a {genre} haiku about {theme} following the traditional 5-7-5 syllable structure.");

private final ChatClient chatClient;

PoetryService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}

Poem generate(String genre, String theme) {
Prompt prompt = PROMPT_TEMPLATE
.create(Map.of(
"genre", genre,
"theme", theme));
return chatClient
.prompt(prompt)
.call()
.entity(Poem.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-5
temperature: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.baeldung.springai;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

@SpringBootTest
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".*")
class PoetryServiceLiveTest {

@Autowired
private PoetryService poetryService;

@Test
void whenPoemGenerationRequested_thenCorrectResponseReturned() {
String genre = "playful";
String theme = "morning coffee";

Poem poem = poetryService.generate(genre, theme);

assertThat(poem)
.hasNoNullFieldsOrProperties()
.satisfies(p -> {
String[] lines = p.content().trim().split("\\n");
assertThat(lines)
.hasSize(3);
});
}

}
25 changes: 0 additions & 25 deletions spring-ai/postman/Spring_AI_Poetry.postman_environment.json

This file was deleted.

56 changes: 0 additions & 56 deletions spring-ai/postman/spring-ai.postman_collection.json

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading