Testing is a very important aspect of Software Development
, all the major frameworks
provide tools
or mechanisms
for testing
the response. Generally, Java unit testing
can be done via Junit
, or integration testing with the help of Mockito
. The Spring AI
also supports testing to ensure the Model
is not producing a Hallucinated Response
, but Model response
testing cannot be done via the traditional way. We are storing the data in Redis Vector Store, also known as RAG
and with new data or information, the AI Model
response evolves with time and may result differently with each iteration hence we need to perform Spring AI Evaluation Testing of Redis Vector Store
.
What is Spring AI Evaluation Testing of Redis Vector Store
The Spring AI Evaluation Testing of Redis Vector Store
is the verification
and validation
of the response generated by the AI Model
after reading embedding
from Redis Vector Store
. General practice is to perform Evaluation Testing
is to use another AI Model
to evaluate the response or the same AI Model
to validate its response.
package com.example.springai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SpringAiController {
private final ChatClient chatClient;
@Autowired
private VectorStore redisVectorStore;
public SpringAiController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping ("/redisVectorStore")
public ChatResponse redisVectorStore(@RequestParam (value = "question", defaultValue = "What is Spring Framework?") String question) {
return chatClient.prompt()
.advisors(new QuestionAnswerAdvisor(redisVectorStore))
.user(question)
.call()
.chatResponse();
}
}
package com.example.springai;
import org.springframework.ai.reader.ExtractedTextFormatter;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
@SpringBootApplication
public class SpringAiApplication {
@Value("vectorStore.json")
private String vectorStoreFileName;
@Value("classpath:/docs/spring-faq.pdf")
private Resource pdfResource;
@Autowired
private VectorStore vectorStore;
public static void main(String[] args) {
SpringApplication.run(SpringAiApplication.class, args);
}
@Bean
VectorStore redisVectorStore() {
System.out.println("SpringAiApplication.redisVectorStore");
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(pdfResource, PdfDocumentReaderConfig.builder()
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfBottomTextLinesToDelete(3)
.withNumberOfTopPagesToSkipBeforeDelete(1)
.build())
.withPagesPerDocument(1)
.build());
var tokenTextSplitter = new TokenTextSplitter();
this.vectorStore.accept(tokenTextSplitter.apply(pdfReader.get()));
return vectorStore;
}
}
spring.application.name=SpringAi
spring.docker.compose.lifecycle-management=start-only
spring.threads.virtual.enabled=true
spring.ai.vectorstore.redis.uri=redis://localhost:6379
spring.ai.vectorstore.redis.index=redis-index
spring.ai.vectorstore.redis.prefix=redis-prefix
spring.ai.vectorstore.redis.initialize-schema=true
# The default Ollama Model in Spring Ai is mistral, but it can be changed by setting the below property. make sure to download the same model in entrypoint.sh file
#spring.ai.ollama.chat.options.model=mistral
spring.ai.ollama.chat.model=mistral
spring.ai.ollama.embedding.model=mistral
# If running the Ollama Docker Instance separately, then set this property
spring.docker.compose.enabled=false
package com.example.springai;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringAiApplicationTests {
@Test
void contextLoads() {
}
}
package com.example.springai.controller;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.evaluation.EvaluationRequest;
import org.springframework.ai.evaluation.RelevancyEvaluator;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertTrue;
@SpringBootTest
class SpringAiControllerTest {
@Autowired
VectorStore vectorStore;
@Autowired
SpringAiController springAiController;
@Autowired
private ChatModel chatModel;
private ChatClient chatClient;
@BeforeEach
public void setup() {
chatClient = ChatClient.builder(chatModel).build();
}
@Test
void simpleVectorStore() {
var userText = "What is Spring Framework?";
var response = springAiController.redisVectorStore(userText);
var content = response.getResult().getOutput().getText();
var relevancyEvaluator = new RelevancyEvaluator(ChatClient.builder(chatModel));
var evaluationRequest = new EvaluationRequest(userText, response.getMetadata()
.get(QuestionAnswerAdvisor.RETRIEVED_DOCUMENTS), content);
var evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest);
assertTrue(evaluationResponse.isPass(), "Response is not relevant to the question");
}
}
services:
ollama-model:
image: ollama/ollama:latest
container_name: ollama_container
ports:
- 11434:11434/tcp
healthcheck:
test: ollama --version || exit 1
command: serve
volumes:
- ./ollama/ollama:/root/.ollama
- ./entrypoint.sh:/entrypoint.sh
pull_policy: missing
tty: true
restart: no
entrypoint: [ "/usr/bin/bash", "/entrypoint.sh" ]
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open_webui_container
environment:
WEBUI_AUTH: false
ports:
- "8081:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- open-webui:/app/backend/data
restart: no
redis:
image: redislabs/redismod
container_name: redislabs_container
restart: always
ports:
- "6379:6379"
# in redis-insight ui, add host name as redis
redis-insight:
image: redis/redisinsight:latest
container_name: redis_insight_container
restart: always
ports:
- '5540:5540'
volumes:
- redisinsight_db:/db
volumes:
open-webui:
redisinsight_db:
#!/bin/bash
# Start Ollama in the background.
/bin/ollama serve &
# Record Process ID.
pid=$!
# Pause for Ollama to start.
# the default Ollama Model in Spring Ai is mistral, but it can be changed in the applications property file. Make sure to download the same Model here
echo "🔴 Retrieve LLAMA3 model..."
ollama pull mistral
echo "🟢 Done!"
# Wait for the Ollama process to finish.
wait $pid
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
<relativePath/>
</parent>
<groupId>com.example.springai</groupId>
<artifactId>evaluation_testing_redis_vector_store</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Evaluation Testing Redis Vector Store</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0-SNAPSHOT</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.springai.SpringAiApplication</mainClass>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
Run the curl to see the Spring AI Evaluation Testing of Redis Vector Store
http://localhost:8080/redisVectorStore
run the test case to see the Spring AI Evaluation Testing of Redis Vector Store