Skip to content

Commit

Permalink
Enhance use cases with Ollama and PGVector
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasVitale committed Jul 28, 2024
1 parent 5d774da commit 2ca40cd
Show file tree
Hide file tree
Showing 17 changed files with 87 additions and 49 deletions.
14 changes: 8 additions & 6 deletions 00-use-cases/question-answering/README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
# Question Answering (RAG)

Ask questions about documents with LLMs via Ollama.
Ask questions about documents with LLMs via Ollama and PGVector.

## Running the application

The application relies on Ollama for providing LLMs. You can either run Ollama locally on your laptop, or rely on the Testcontainers support in Spring Boot to spin up an Ollama service automatically.
Furthermore, the application relies on the native Testcontainers support in Spring Boot to spin up a PostgreSQL database with the pgvector extension for embeddings.

### Ollama as a native application

First, make sure you have [Ollama](https://ollama.ai) installed on your laptop.
Then, use Ollama to run the _mistral_ large language model.
Then, use Ollama to run the _mistral_ and _nomic-embed-text_ large language models.

```shell
ollama run mistral
ollama run nomic-embed-text
```

Finally, run the Spring Boot application.

```shell
./gradlew bootRun
./gradlew bootTestRun
```

### Ollama as a dev service with Testcontainers

The application relies on the native Testcontainers support in Spring Boot to spin up an Ollama service with a _mistral_ model at startup time.
The application relies on the native Testcontainers support in Spring Boot to spin up an Ollama service with _mistral_ and _nomic-embed-text_ models at startup time.

```shell
./gradlew bootTestRun
./gradlew bootTestRun --args='--spring.profiles.active=ollama-image'
```

## Calling the application

You can now call the application that will use Ollama and _mistral_ to load text documents as embeddings and generate an answer to your questions based on those documents (RAG pattern).
You can now call the application that will use Ollama with _nomic-embed-text_ and _mistral_ to load text documents as embeddings and generate an answer to your questions based on those documents (RAG pattern).
This example uses [httpie](https://httpie.io) to send HTTP requests.

```shell
Expand Down
2 changes: 2 additions & 0 deletions 00-use-cases/question-answering/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.ai:spring-ai-ollama-spring-boot-starter'
implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter'

testAndDevelopmentOnly 'org.springframework.boot:spring-boot-devtools'

Expand All @@ -33,6 +34,7 @@ dependencies {
testImplementation 'org.springframework.ai:spring-ai-spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:ollama'
testImplementation 'org.testcontainers:postgresql'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

Expand All @@ -19,7 +18,7 @@ class ChatService {

String chatWithDocument(String message) {
return chatClient.prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults().withTopK(5)))
.advisors(new QuestionAnswerAdvisor(vectorStore))
.user(message)
.call()
.content();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public void run() {
documents.addAll(textReader2.get());

logger.info("Creating and storing Embeddings from Documents");
vectorStore.add(new TokenTextSplitter(300, 300, 5, 1000, true).split(documents));
vectorStore.add(new TokenTextSplitter().split(documents));
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.thomasvitale.ai.spring;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class QuestionAnswering {
Expand All @@ -14,9 +10,4 @@ public static void main(String[] args) {
SpringApplication.run(QuestionAnswering.class, args);
}

@Bean
VectorStore vectorStore(EmbeddingModel embeddingModel) {
return new SimpleVectorStore(embeddingModel);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ spring:
chat:
options:
model: mistral
num-ctx: 4096
embedding:
options:
model: mistral
model: nomic-embed-text
vectorstore:
pgvector:
initialize-schema: true
dimensions: 768
index-type: hnsw
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

Expand All @@ -14,6 +16,14 @@ public class TestQuestionAnswering {
@Bean
@RestartScope
@ServiceConnection
PostgreSQLContainer<?> pgvectorContainer() {
return new PostgreSQLContainer<>(DockerImageName.parse("pgvector/pgvector:pg16"));
}

@Bean
@Profile("ollama-image")
@RestartScope
@ServiceConnection
OllamaContainer ollama() {
return new OllamaContainer(DockerImageName.parse("ghcr.io/thomasvitale/ollama-mistral")
.asCompatibleSubstituteFor("ollama/ollama"));
Expand Down
15 changes: 8 additions & 7 deletions 00-use-cases/semantic-search/README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
# Semantic Search

Semantic search with LLMs via Ollama.
Semantic search with LLMs via Ollama and PGVector.

## Running the application

The application relies on Ollama for providing LLMs. You can either run Ollama locally on your laptop, or rely on the Testcontainers support in Spring Boot to spin up an Ollama service automatically.
Furthermore, the application relies on the native Testcontainers support in Spring Boot to spin up a PostgreSQL database with the pgvector extension for embeddings.

### Ollama as a native application

First, make sure you have [Ollama](https://ollama.ai) installed on your laptop.
Then, use Ollama to run the _mistral_ large language model.
Then, use Ollama to run the _nomic-embed-text_ large language model.

```shell
ollama run mistral
ollama run nomic-embed-text
```

Finally, run the Spring Boot application.

```shell
./gradlew bootRun
./gradlew bootTestRun
```

### Ollama as a dev service with Testcontainers

The application relies on the native Testcontainers support in Spring Boot to spin up an Ollama service with a _mistral_ model at startup time.
The application relies on the native Testcontainers support in Spring Boot to spin up an Ollama service with a _nomic-embed-text_ model at startup time.

```shell
./gradlew bootTestRun
./gradlew bootTestRun --args='--spring.profiles.active=ollama-image'
```

## Calling the application

You can now call the application that will use Ollama and _mistral_ to perform a semantic search.
You can now call the application that will use Ollama and _nomic-embed-text_ to perform a semantic search.
This example uses [httpie](https://httpie.io) to send HTTP requests.

```shell
Expand Down
2 changes: 2 additions & 0 deletions 00-use-cases/semantic-search/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.ai:spring-ai-ollama-spring-boot-starter'
implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter'

testAndDevelopmentOnly 'org.springframework.boot:spring-boot-devtools'

Expand All @@ -33,6 +34,7 @@ dependencies {
testImplementation 'org.springframework.ai:spring-ai-spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:ollama'
testImplementation 'org.testcontainers:postgresql'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.thomasvitale.ai.spring;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SemanticSearch {
Expand All @@ -14,9 +10,4 @@ public static void main(String[] args) {
SpringApplication.run(SemanticSearch.class, args);
}

@Bean
VectorStore vectorStore(EmbeddingModel embeddingModel) {
return new SimpleVectorStore(embeddingModel);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ spring:
ollama:
embedding:
options:
model: mistral
model: nomic-embed-text
vectorstore:
pgvector:
initialize-schema: true
dimensions: 768
index-type: hnsw
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,32 @@
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

@TestConfiguration(proxyBeanMethods = false)
public class TestClassification {
public class TestSemanticSearch {

@Bean
@RestartScope
@ServiceConnection
PostgreSQLContainer<?> pgvectorContainer() {
return new PostgreSQLContainer<>(DockerImageName.parse("pgvector/pgvector:pg16"));
}

@Bean
@Profile("ollama-image")
@RestartScope
@ServiceConnection
OllamaContainer ollama() {
return new OllamaContainer(DockerImageName.parse("ghcr.io/thomasvitale/ollama-mistral")
return new OllamaContainer(DockerImageName.parse("ghcr.io/thomasvitale/ollama-nomic-embed-text")
.asCompatibleSubstituteFor("ollama/ollama"));
}

public static void main(String[] args) {
SpringApplication.from(SemanticSearch::main).with(TestClassification.class).run(args);
SpringApplication.from(SemanticSearch::main).with(TestSemanticSearch.class).run(args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
import java.util.List;

public record PatientJournal(String fullName, List<Observation> observations, Diagnosis diagnosis) {
public record Observation(String type, String content) {}
public record Observation(Type type, String content) {}
public record Diagnosis(String content) {}

enum Type {
BODY_WEIGHT,
TEMPERATURE,
VITAL_SIGNS,
OTHER
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ PatientJournal extract(String message) {
Extract structured data from the provided text.
If you do not know the value of a field asked to extract,
do not include any value for the field in the result.
Finally, save the object in the database.
---------------------
TEXT:
Expand Down
2 changes: 1 addition & 1 deletion 00-use-cases/text-classification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ First, make sure you have [Ollama](https://ollama.ai) installed on your laptop.
Then, use Ollama to pull the _mistral_ large language model.

```shell
ollama pull mistral
ollama pull mistral-nemo
```

Finally, run the Spring Boot application.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package com.thomasvitale.ai.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.ollama.OllamaContainer;
import org.testcontainers.utility.DockerImageName;

public class TestTextClassificationApplication {

@Bean
@RestartScope
@ServiceConnection
OllamaContainer ollama() {
return new OllamaContainer(DockerImageName.parse("ghcr.io/thomasvitale/ollama-mistral-nemo")
.asCompatibleSubstituteFor("ollama/ollama"));
}

public static void main(String[] args) {
SpringApplication.from(TextClassificationApplication::main)
.with(TestcontainersConfiguration.class).run(args);
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ Samples showing how to build Java applications powered by Generative AI and LLMs

### 0. Use Cases

| Project | Description |
|-----------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|
| [chatbot](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/chatbot) | Chatbot using LLMs via Ollama. |
| [question-answering](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/question-answering) | Question answering with documents (RAG) using LLMs via Ollama. |
| [semantic-search](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/semantic-search) | Semantic search using LLMs via Ollama. |
| [structured-data-extraction](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/structured-data-extraction) | Structured data extraction using LLMs via Ollama. |
| [text-classification](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/text-classification) | Text classification using LLMs via Ollama. |
| Project | Description |
|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [chatbot](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/chatbot) | Chatbot using LLMs via Ollama. |
| [question-answering](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/question-answering) | Question answering with documents (RAG) using LLMs via Ollama and PGVector. |
| [semantic-search](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/semantic-search) | Semantic search using LLMs via Ollama and PGVector. |
| [structured-data-extraction](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/structured-data-extraction) | Structured data extraction using LLMs via Ollama. |
| [text-classification](https://github.com/ThomasVitale/llm-apps-java-spring-ai/tree/main/00-use-cases/text-classification) | Text classification using LLMs via Ollama. |

### 1. Chat Completion Models

Expand Down

0 comments on commit 2ca40cd

Please sign in to comment.