diff --git a/.gitignore b/.gitignore
index 549e00a..a8c9502 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
+.env
### STS ###
.apt_generated
diff --git a/pom.xml b/pom.xml
index 4fc80f5..30586fb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.3.0
+ 3.5.9
dev.danvega
@@ -28,16 +28,27 @@
21
- 1.1.0
+ 1.1.2
-
+
+ me.paulschwarz
+ spring-dotenv
+ 4.0.0
+
+
org.springframework.boot
spring-boot-starter-web
org.springframework.ai
spring-ai-starter-model-openai
+ compile
+
+
+ org.springframework.ai
+ spring-ai-starter-model-ollama
+ test
org.springframework.ai
@@ -91,6 +102,14 @@
pom
import
+
+
+ org.testcontainers
+ testcontainers-bom
+ 1.21.1
+ pom
+ import
+
diff --git a/src/main/java/dev/danvega/workshop/Application.java b/src/main/java/dev/danvega/workshop/Application.java
index df07099..6d7c562 100644
--- a/src/main/java/dev/danvega/workshop/Application.java
+++ b/src/main/java/dev/danvega/workshop/Application.java
@@ -1,7 +1,13 @@
package dev.danvega.workshop;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.embedding.EmbeddingModel;
+import org.springframework.ai.openai.OpenAiChatModel;
+import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
import java.io.IOException;
@@ -12,4 +18,15 @@ public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
+ @Bean
+ @Primary
+ public ChatModel primaryChatModel(OpenAiChatModel openAiChatModel) {
+ return openAiChatModel;
+ }
+
+ @Bean
+ @Primary
+ public EmbeddingModel primaryEmbeddingModel(OpenAiEmbeddingModel openAiEmbeddingModel) {
+ return openAiEmbeddingModel;
+ }
}
diff --git a/src/main/java/dev/danvega/workshop/SimpleChatController.java b/src/main/java/dev/danvega/workshop/SimpleChatController.java
index b86e822..77304be 100644
--- a/src/main/java/dev/danvega/workshop/SimpleChatController.java
+++ b/src/main/java/dev/danvega/workshop/SimpleChatController.java
@@ -1,6 +1,7 @@
package dev.danvega.workshop;
import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -17,7 +18,7 @@ public SimpleChatController(ChatClient.Builder builder) {
@GetMapping("/java")
public String chat() {
return chatClient.prompt()
- .user("Tell me an intresting fact about Java")
+ .user("Tell me an interesting fact about Java")
.call()
.content();
}
diff --git a/src/main/java/dev/danvega/workshop/evals/ReviewService.java b/src/main/java/dev/danvega/workshop/evals/ReviewService.java
index 0712a63..725d33b 100644
--- a/src/main/java/dev/danvega/workshop/evals/ReviewService.java
+++ b/src/main/java/dev/danvega/workshop/evals/ReviewService.java
@@ -4,6 +4,8 @@
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.stereotype.Service;
+import java.util.Objects;
+
@Service
public class ReviewService {
@@ -20,11 +22,12 @@ public Sentiment classifySentiment(String review) {
Classify the sentiment of the following text as POSITIVE, NEGATIVE, or NEUTRAL. \
Your response must be only one of these three words.""";
- return chatClient.prompt()
- .system(systemPrompt)
- .user(review)
- .call()
- .entity(Sentiment.class);
+ return Objects.requireNonNull(chatClient.prompt()
+ .system(systemPrompt)
+ .user(review)
+ .call()
+ .entity(SentimentResponse.class))
+ .getSentiment();
}
}
diff --git a/src/main/java/dev/danvega/workshop/evals/SentimentResponse.java b/src/main/java/dev/danvega/workshop/evals/SentimentResponse.java
new file mode 100644
index 0000000..218bd2c
--- /dev/null
+++ b/src/main/java/dev/danvega/workshop/evals/SentimentResponse.java
@@ -0,0 +1,16 @@
+package dev.danvega.workshop.evals;
+
+
+public class SentimentResponse {
+ private Sentiment sentiment;
+
+ public SentimentResponse() {}
+
+ public Sentiment getSentiment() {
+ return sentiment;
+ }
+
+ public void setSentiment(Sentiment sentiment) {
+ this.sentiment = sentiment;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 16cbed4..e0ceb82 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,9 +1,9 @@
spring.application.name=ai-workshop
spring.ai.openai.api-key=${OPENAI_API_KEY}
-spring.ai.openai.chat.options.model=gpt-4o
+spring.ai.openai.chat.options.model=gpt-4o-mini
spring.threads.virtual.enabled=true
-
spring.ai.ollama.chat.model=bespokeai/minicheck
-spring.ai.ollama.chat.temperature=0.1
+spring.ai.ollama.chat.options.temperature=0.1
+spring.ai.ollama.base-url=http://localhost:11434
logging.level.dev.danvega.workshop=DEBUG
\ No newline at end of file
diff --git a/src/test/java/dev/danvega/workshop/evals/ChatClientTestConfig.java b/src/test/java/dev/danvega/workshop/evals/ChatClientTestConfig.java
index 8e93885..64f651a 100644
--- a/src/test/java/dev/danvega/workshop/evals/ChatClientTestConfig.java
+++ b/src/test/java/dev/danvega/workshop/evals/ChatClientTestConfig.java
@@ -1,8 +1,13 @@
package dev.danvega.workshop.evals;
import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.evaluation.FactCheckingEvaluator;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Primary;
@TestConfiguration
class ChatClientTestConfig {
@@ -11,4 +16,11 @@ class ChatClientTestConfig {
ChatClient chatClient(ChatClient.Builder builder) {
return builder.build();
}
+
+
+ @Bean
+ public FactCheckingEvaluator factCheckingEvaluator(ChatClient.Builder chatClientBuilder) {
+ return FactCheckingEvaluator.builder(chatClientBuilder)
+ .build();
+ }
}
diff --git a/src/test/java/dev/danvega/workshop/evals/FactCheckingEvaluatorTest.java b/src/test/java/dev/danvega/workshop/evals/FactCheckingEvaluatorTest.java
index c0fd362..25cc786 100644
--- a/src/test/java/dev/danvega/workshop/evals/FactCheckingEvaluatorTest.java
+++ b/src/test/java/dev/danvega/workshop/evals/FactCheckingEvaluatorTest.java
@@ -8,8 +8,11 @@
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.EvaluationResponse;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.ollama.OllamaContainer;
@@ -20,7 +23,9 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
+@ActiveProfiles("test")
@Testcontainers
+@Import(ChatClientTestConfig.class)
public class FactCheckingEvaluatorTest {
private static final String OLLAMA_IMAGE = "ollama/ollama:0.1.48";
@@ -30,12 +35,13 @@ public class FactCheckingEvaluatorTest {
@ServiceConnection(name = "spring.ai.ollama.base-url")
static final OllamaContainer ollama = new OllamaContainer(OLLAMA_IMAGE);
+ @Autowired
private FactCheckingEvaluator factCheckingEvaluator;
- @BeforeEach
- void setUp(@Autowired ChatClient.Builder builder) {
- factCheckingEvaluator = new FactCheckingEvaluator(builder);
- }
+// @BeforeEach
+// void setUp(@Autowired FactCheckingEvaluator evaluator) {
+// factCheckingEvaluator = evaluator;
+// }
@Test
void passes_when_claim_is_true() {