Move documentation that was in README.md into Antora.

* Add minimal ETL pipeline docs

Fixes #137
This commit is contained in:
Mark Pollack
2023-12-04 18:16:51 -05:00
parent 3423688908
commit f6dc8f6bd8
23 changed files with 1249 additions and 1071 deletions

View File

@@ -1,53 +0,0 @@
# How to configure PDF Reader
## PagePdfDocumentReader
``` java
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(
"file:document-readers/pdf-reader/src/test/resources/sample.pdf",
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageBottomMargin(0)
.withPageExtractedTextFormatter(PageExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.withNumberOfBottomTextLinesToDelete(3)
.withNumberOfTopPagesToSkipBeforeDelete(0)
.build())
.withPagesPerDocument(1)
.build());
var documents = pdfReader.get();
PdfTestUtils.writeToFile("document-readers/pdf-reader/target/sample.txt", documents, false);
```
```java
public static void main(String[] args) throws IOException {
ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(
"file:document-readers/pdf-reader/src/test/resources/sample2.pdf",
PdfDocumentReaderConfig.builder()
// .withPageBottomMargin(15)
// .withReversedParagraphPosition(true)
// .withTextLeftAlignment(true)
.build());
// ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(
// "file:document-readers/pdf-reader/src/test/resources/spring-framework.pdf",
// PdfDocumentReaderConfig.builder()
// .withPageBottomMargin(15)
// .withReversedParagraphPosition(true)
// // .withTextLeftAlignment(true)
// .build());
// PdfDocumentReader pdfReader = new
// PdfDocumentReader("file:document-readers/pdf-reader/src/test/resources/uber-k-10.pdf",
// PdfDocumentReaderConfig.builder().withPageTopMargin(80).withPageBottomMargin(60).build());
var documents = pdfReader.get();
writeToFile("document-readers/pdf-reader/target/sample2.txt", documents, true);
System.out.println(documents.size());
}
```

View File

@@ -3,10 +3,19 @@
* xref:getting-started.adoc[Getting Started]
* xref:api/index.adoc[]
** xref:api/aiclient.adoc[]
*** xref:api/clients/huggingface.adoc[]
** xref:api/prompt.adoc[]
** xref:api/output-parser.adoc[]
** xref:api/etl-pipeline.adoc[]
** xref:api/embeddings.adoc[]
*** xref:api/embeddings/onnx.adoc[]
** xref:api/vectordbs.adoc[]
*** xref:api/vectordbs/azure.adoc[]
*** xref:api/vectordbs/chroma.adoc[]
*** xref:api/vectordbs/milvus.adoc[]
*** xref:api/vectordbs/neo4j.adoc[]
*** xref:api/vectordbs/pgvector.adoc[]
*** xref:api/vectordbs/weaviate.adoc[]
** xref:api/testing.adoc[]
* Appendices
** xref:glossary.adoc[]

View File

@@ -1,45 +1,48 @@
# HuggingFace Inference Endpoints with Spring AI
= HuggingFace
HuggingFace Inference Endpoints allow you to deploy and serve machine learning models in the cloud, making them accessible via an API. Further details on HuggingFace Inference Endpoints can be found [here](https://huggingface.co/docs/inference-endpoints/index).
HuggingFace Inference Endpoints allow you to deploy and serve machine learning models in the cloud, making them accessible via an API. Further details on HuggingFace Inference Endpoints can be found link:https://huggingface.co/docs/inference-endpoints/index[here].
## Prerequisites
== Prerequisites
Add the `spring-ai-huggingface` dependency:
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-huggingface</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
You should get your HuggingFace API key and set it as an environment variable
```shell
[source,shell]
----
export HUGGINGFACE_API_KEY=your_api_key_here
```
----
Note, there is not yet a Spring Boot Starter for this client implementation.
Obtain the endpoint URL of the Inference Endpoint.
You can find this on the Inference Endpoint's UI [here](https://ui.endpoints.huggingface.co/).
You can find this on the Inference Endpoint's UI link:https://ui.endpoints.huggingface.co/[here].
== Making a call to the model
## Making a call to the model
```java
[source,java]
----
HuggingfaceAiClient client = new HuggingfaceAiClient(apiKey, basePath);
Prompt prompt = new Prompt("Your text here...");
AiResponse response = client.generate(prompt);
System.out.println(response.getGeneration().getText());
```
----
## Example
== Example
Using the example found [here](https://www.promptingguide.ai/models/mistral-7b)
Using the example found link:https://www.promptingguide.ai/models/mistral-7b[here]
```java
[source,java]
----
String mistral7bInstruct = """
[INST] You are a helpful code assistant. Your task is to generate a valid JSON object based on the given information:
name: John
@@ -50,17 +53,15 @@ String mistral7bInstruct = """
Prompt prompt = new Prompt(mistral7bInstruct);
AiResponse aiResponse = huggingfaceAiClient.generate(prompt);
System.out.println(response.getGeneration().getText());
```
----
Will produce the output
````
```json
[source,json]
----
{
"name": "John",
"lastname": "Smith",
"address": "#1 Samuel St."
}
```
````
Note the response itself is in Markdown format.
----

View File

@@ -1,28 +1,30 @@
# Ollama
= Ollama
Ollama lets you get up and running with large language models locally.
Refer to the official [README](https://github.com/jmorganca/ollama) to get started.
Refer to the official link:https://github.com/jmorganca/ollama[README] to get started.
Note, installing `ollama run llama2` will download a 4GB docker image.
You can run the disabled test in `OllamaClientTests.java` to kick the tires.
## How to use
== How to use
Add the `spring-ai-ollama` dependency to your project's pom:
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-ollama</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
then create an client and use generate response:
```java
[source,java]
----
var ollamaClient = new OllamaClient("http://127.0.0.1:11434", "llama2",
ollamaResult -> {
if (ollamaResult.getDone()) {
@@ -31,26 +33,29 @@ var ollamaClient = new OllamaClient("http://127.0.0.1:11434", "llama2",
});
AiResponse aiResponse = ollamaClient.generate(new Prompt("Hello"));
```
----
### Spring Boot Starter
=== Spring Boot Starter
For convenience you can opt for the Ollama Boot starter.
For this add the following dependency:
For convenience, you can opt for the Ollama Boot starter.
For this, add the following dependency:
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
and use the `spring.ai.ollama.*` properties to configure it if you want to use something other than the default values.
The complete list of supported properties are:
| Property | Description | Default |
| -------- | ----- | ----- |
| spring.ai.ollama.base-url | Base URL where Ollama API server is running. | `http://localhost:11434` |
| spring.ai.ollama.model | Language model to use. | `llama2` |
[cols="3,5,3"]
|====
| Property | Description | Default
| spring.ai.ollama.base-url | Base URL where Ollama API server is running. | `http://localhost:11434`
| spring.ai.ollama.model | Language model to use. | `llama2`
|====

View File

@@ -1,3 +1,57 @@
= Embeddings
[[EmbeddingClient]]
= EmbeddingClient
The `EmbeddingClient` interface is designed for straightforward integration with embedding models in AI and machine learning.
Its primary function is to convert text into numerical vectors, commonly referred to as embeddings.
These embeddings are crucial for various tasks such as semantic analysis and text classification.
The design of the EmbeddingClient interface centers around two primary goals:
* *Portability*: This interface ensures easy adaptability across various embedding models.
It allows developers to switch between different embedding techniques or models with minimal code changes.
This design aligns with Spring's philophy of modularity and interchangeability.
* *Simplicity*: EmbeddingClient simplifies the process of converting text to embeddings.
By providing straightforward methods like `embed(String text)` and `embed(Document document)`, it takes the complexity out of dealing with raw text data and embedding algorithms. This design choice makes it easier for developers, especially those new to AI, to utilize embeddings in their applications without delving deep into the underlying mechanics.
== API Overview
This section provides a guide to the `EmbeddingClient` interface and associated classes.
=== EmbeddingClient
Here is the `EmbeddingClient` interface definition:
java
Copy code
public interface EmbeddingClient {
List<Double> embed(String text);
List<Double> embed(Document document);
List<List<Double>> embed(List<String> texts);
EmbeddingResponse embedForResponse(List<String> texts);
default int dimensions() {
return embed("Test String").size();
}
}
The embed methods offer various options for converting text into embeddings, accommodating single strings, structured Document objects, or batches of text.
The returned values are lists of doubles, representing the embeddings in a numerical vector format.
The `embedForResponse` method provides a more comprehensive output, potentially including additional information about the embeddings.
The dimensions method is a handy tool for developers to quickly ascertain the size of the embedding vectors, which is important for understanding the embedding space and for subsequent processing steps.
== Available Implementations
The `EmbeddingClient` interface has the following available implementations:
* OpenAI: Using the https://github.com/TheoKanning/openai-java[Theo Kanning client library].
* Azure OpenAI: Using https://learn.microsoft.com/en-us/java/api/overview/azure/ai-openai-readme?view=azure-java-preview[Microsoft's OpenAI client library].
* PostgresML: https://postgresml.org/docs/[PostgresML is a complete MLOps platform built on PostgreSQL]
* Sentence embedding with local ONNX models: The https://djl.ai/[Deep Java Library] and the Microsoft https://onnxruntime.ai/docs/get-started/with-java.html[ONNX Java Runtime] libraries are applied to run the ONNX models and compute the embeddings in Java.
TBD

View File

@@ -1,59 +1,63 @@
# Local Transformers Embedding Client
= ONNX
The `TransformersEmbeddingClient` is a `EmbeddingClient` implementation that computes, locally, [sentence embeddings](https://www.sbert.net/examples/applications/computing-embeddings/README.html#sentence-embeddings-with-transformers) using a selected [sentence transformer](https://www.sbert.net/).
The `TransformersEmbeddingClient` is an `EmbeddingClient` implementation that computes, locally, https://www.sbert.net/examples/applications/computing-embeddings/README.html#sentence-embeddings-with-transformers[sentence embeddings] using a selected https://www.sbert.net/[sentence transformer].
It uses [pre-trained](https://www.sbert.net/docs/pretrained_models.html) transformer models, serialized into the [Open Neural Network Exchange (ONNX)](https://onnx.ai/) format.
It uses https://www.sbert.net/docs/pretrained_models.html[pre-trained] transformer models, serialized into the https://onnx.ai/[Open Neural Network Exchange (ONNX)] format.
The [Deep Java Library](https://djl.ai/) and the Microsoft [ONNX Java Runtime](https://onnxruntime.ai/docs/get-started/with-java.html) libraries are applied to run the ONNX models and compute the embeddings in Java.
The https://djl.ai/[Deep Java Library] and the Microsoft https://onnxruntime.ai/docs/get-started/with-java.html[ONNX Java Runtime] libraries are applied to run the ONNX models and compute the embeddings in Java.
## Serialize the Tokenizer and the Transformer Model
== Serialize the Tokenizer and the Transformer Model
To run things in Java, we need to serialize the Tokenizer and the Transformer Model into ONNX format.
### Serialize with optimum-cli
=== Serialize with optimum-cli
One, quick, way to achieve this, is to use the [optimum-cli](https://huggingface.co/docs/optimum/exporters/onnx/usage_guides/export_a_model#exporting-a-model-to-onnx-using-the-cli) command line tool.
One, quick, way to achieve this, is to use the https://huggingface.co/docs/optimum/exporters/onnx/usage_guides/export_a_model#exporting-a-model-to-onnx-using-the-cli[optimum-cli] command line tool.
Following snippet prepares a python virtual environment, installs the required packages and serializes (e.g. exports) specified model using `optimum-cli` :
```bash
[source,bash]
----
python3 -m venv venv
source ./venv/bin/activate
(venv) pip install --upgrade pip
(venv) pip install optimum onnx onnxruntime
(venv) optimum-cli export onnx --model sentence-transformers/all-MiniLM-L6-v2 onnx-output-folder
```
----
The snippet exports the [sentence-transformers/all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) transformer into the `onnx-output-folder` folder. Later includes the `tokenizer.json` and `model.onnx` files used by the embedding client.
The snippet exports the https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2[sentence-transformers/all-MiniLM-L6-v2] transformer into the `onnx-output-folder` folder. Later includes the `tokenizer.json` and `model.onnx` files used by the embedding client.
In place of the all-MiniLM-L6-v2 you can pick any huggingface transformer identifier or provide direct file path.
## Using the ONNX models
== Using the ONNX models
Add the `transformers-embedding` project to your maven dependencies:
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>transformers-embedding</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
then create a new `TransformersEmbeddingClient` instance and use the `setTokenizerResource(tokenizerJsonUri)` and `setModelResource(modelOnnxUri)` methods to set the URIs of the exported `tokenizer.json` and `model.onnx` files. (`classpath:`, `file:` or `https:` URI schemas are supported).
If the model is not explicitly set, `TransformersEmbeddingClient` defaults to [sentence-transformers/all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2):
If the model is not explicitly set, `TransformersEmbeddingClient` defaults to https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2[sentence-transformers/all-MiniLM-L6-v2]:
| | |
| -------- | ------- |
| Dimensions |384 |
| Avg. performance | 58.80 |
| Speed | 14200 sentences/sec |
| Size | 80MB |
[cols="2*"]
|===
| Dimensions | 384
| Avg. performance | 58.80
| Speed | 14200 sentences/sec
| Size | 80MB
|===
Following snippet illustrates how to use the `TransformersEmbeddingClient` manually:
```java
[source,java]
----
TransformersEmbeddingClient embeddingClient = new TransformersEmbeddingClient();
// (optional) defaults to classpath:/onnx/all-MiniLM-L6-v2/tokenizer.json
@@ -74,7 +78,7 @@ embeddingClient.afterPropertiesSet();
List<List<Double>> embeddings = embeddingClient.embed(List.of("Hello world", "World is big"));
```
----
Note that when created manually you have to call the `afterPropertiesSet()` after setting the properties and before using the client.
@@ -86,49 +90,52 @@ The default cache folder is `${java.io.tmpdir}/spring-ai-onnx-model`.
It is more convenient (and preferred) to create the TransformersEmbeddingClient as a `Bean`.
Then you don't have to call the `afterPropertiesSet()` manually.
```java
[source,java]
----
@Bean
public EmbeddingClient embeddingClient() {
return new TransformersEmbeddingClient();
}
```
----
## Transformers Embedding Spring Boot Starter.
== Transformers Embedding Spring Boot Starter.
You can bootstrap and auto-wire the `TransformersEmbeddingClient` with following boot starer:
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
and use the `spring.ai.embedding.transformer.*` properties to configure it.
For example add this to your application.properties to configure with the [intfloat/e5-small-v2](https://huggingface.co/intfloat/e5-small-v2) text embedding model:
For example add this to your application.properties to configure with the https://huggingface.co/intfloat/e5-small-v2[intfloat/e5-small-v2] text embedding model:
```
----
spring.ai.embedding.transformer.onnx.modelUri=https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx
spring.ai.embedding.transformer.tokenizer.uri=https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json
```
----
The complete list of supported properties are:
| Property | Description | Default |
| -------- | ------- | ------- |
| spring.ai.embedding.transformer.tokenizer.uri | URI of a pre-trained HuggingFaceTokenizer created by the ONNX engine (e.g. tokenizer.json). | onnx/all-MiniLM-L6-v2/tokenizer.json |
| spring.ai.embedding.transformer.tokenizer.options | HuggingFaceTokenizer options such as '`addSpecialTokens`', '`modelMaxLength`', '`truncation`', '`padding`', '`maxLength`', '`stride`' and '`padToMultipleOf`'. Leave empty to fallback to the defaults. | empty |
| spring.ai.embedding.transformer.cache.enabled | Enable remote Resource caching. | true |
| spring.ai.embedding.transformer.cache.directory | Directory path to cache remote resources, such as the ONNX models | ${java.io.tmpdir}/spring-ai-onnx-model |
| spring.ai.embedding.transformer.onnx.modelUri | Existing, pre-trained ONNX model. | onnx/all-MiniLM-L6-v2/model.onnx |
| spring.ai.embedding.transformer.onnx.gpuDeviceId | The GPU device ID to execute on. Only applicable if >= 0. Ignored otherwise. | -1 |
| spring.ai.embedding.transformer.metadataMode | Specifies what parts of the Documents content and metadata will be used for computing the embeddings. | NONE |
[cols="3*"]
|===
| Property | Description | Default
| spring.ai.embedding.transformer.tokenizer.uri | URI of a pre-trained HuggingFaceTokenizer created by the ONNX engine (e.g. tokenizer.json). | onnx/all-MiniLM-L6-v2/tokenizer.json
| spring.ai.embedding.transformer.tokenizer.options | HuggingFaceTokenizer options such as '`addSpecialTokens`', '`modelMaxLength`', '`truncation`', '`padding`', '`maxLength`', '`stride`', '`padToMultipleOf`'. Leave empty to fallback to the defaults. | empty
| spring.ai.embedding.transformer.cache.enabled | Enable remote Resource caching. | true
| spring.ai.embedding.transformer.cache.directory | Directory path to cache remote resources, such as the ONNX models | ${java.io.tmpdir}/spring-ai-onnx-model
| spring.ai.embedding.transformer.onnx.modelUri | Existing, pre-trained ONNX model. | onnx/all-MiniLM-L6-v2/model.onnx
| spring.ai.embedding.transformer.onnx.gpuDeviceId | The GPU device ID to execute on. Only applicable if >= 0. Ignored otherwise. | -1
| spring.ai.embedding.transformer.metadataMode | Specifies what parts of the Documents content and metadata will be used for computing the embeddings. | NONE
|===
Note: if you see error like: `Caused by: ai.onnxruntime.OrtException: Supplied array is ragged,..` then you need to enable the tokenizer padding in boot starter's `application.properties`:
```
----
spring.ai.embedding.transformer.tokenizer.options.padding=true
```
----

View File

@@ -1,3 +1,150 @@
= Document Readers
= ETL Pipeline
TBD
The Extraction, Transformation, and Loading (ETL) framework serves as the backbone of data processing within the Retrieval-Augmented Generation (RAG) use case.
The ETL pipeline orchestrates the flow from raw data sources to a structured vector store, ensuring data is in the optimal format for retrieval by the AI model.
The RAG use-case is designed to augment the capabilities of generative models by retrieving relevant information from a body of data to enhancing the quality and relevance of the generated output.
== API Overview
=== DocumentReader
```java
public interface DocumentReader extends Supplier<List<Document>> {
}
```
=== DocumentTransformer
```java
public interface DocumentTransformer extends Function<List<Document>, List<Document>> {
}
```
=== DocumentWriter
```java
public interface DocumentWriter extends Consumer<List<Document>> {
}
```
=== Available Implementations
==== DocumentReader Interface
*Supplier<List<Document>>*::
+ Provides a source of documents from diverse origins.
*JsonReader*::
+ Parses documents in JSON format.
*TextReader*::
+ Processes plain text documents.
*Document*::
+ Represents the core data structure manipulated throughout the pipeline.
=== DocumentTransformer Interface
*Function<List<Document>, List<Document>>*::
+ Transforms a batch of documents as part of the processing workflow.
*TextSplitter*::
+ Divides documents to fit the AI model's context window.
*TokenTextSplitter*::
+ Splits documents while preserving token-level integrity.
*ContentFormatTransformer*::
+ Ensures uniform content formats across all documents.
*KeywordMetadataEnricher*::
+ Augments documents with essential keyword metadata.
*SummaryMetadataEnricher*::
+ Enriches documents with summarization metadata for enhanced retrieval.
=== DocumentWriter Interface
*Consumer<List<Document>>*::
+ Manages the final stage of the ETL process, preparing documents for storage.
*VectorStore*::
+ The abstracted interface for vector database interactions.
*MilvusVectorStore*::
+ An implementation for the Milvus vector database.
*PgVectorStore*::
+ Provides vector storage capabilities using PostgreSQL.
*SimplePersistentVectorStore*::
+ A straightforward approach to persistent vector storage.
*InMemoryVectorStore*::
+ Enables rapid access with in-memory storage solutions.
*Neo4jVectorStore*::
+ Leverages the Neo4j graph database for vector storage.
== Using PDF Reader
== Using PagePdfDocumentReader
[source,java]
----
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(
"file:document-readers/pdf-reader/src/test/resources/sample.pdf",
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageBottomMargin(0)
.withPageExtractedTextFormatter(PageExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.withNumberOfBottomTextLinesToDelete(3)
.withNumberOfTopPagesToSkipBeforeDelete(0)
.build())
.withPagesPerDocument(1)
.build());
var documents = pdfReader.get();
PdfTestUtils.writeToFile("document-readers/pdf-reader/target/sample.txt", documents, false);
----
[source,java]
----
public static void main(String[] args) throws IOException {
ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(
"file:document-readers/pdf-reader/src/test/resources/sample2.pdf",
PdfDocumentReaderConfig.builder()
// .withPageBottomMargin(15)
// .withReversedParagraphPosition(true)
// .withTextLeftAlignment(true)
.build());
// ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(
// "file:document-readers/pdf-reader/src/test/resources/spring-framework.pdf",
// PdfDocumentReaderConfig.builder()
// .withPageBottomMargin(15)
// .withReversedParagraphPosition(true)
// // .withTextLeftAlignment(true)
// .build());
// PdfDocumentReader pdfReader = new
// PdfDocumentReader("file:document-readers/pdf-reader/src/test/resources/uber-k-10.pdf",
// PdfDocumentReaderConfig.builder().withPageTopMargin(80).withPageBottomMargin(60).build());
var documents = pdfReader.get();
writeToFile("document-readers/pdf-reader/target/sample2.txt", documents, true);
System.out.println(documents.size());
}
----

View File

@@ -1,3 +1,123 @@
[[OutputParser]]
= Output Parsers
TBD
The `OutputParser` interface allows you to obtain structured output, for example ampping the output to a Java class or an array of values from the String based ouput of AI Models.
You can think of it in terms similar to Spring JDBC's concept of a `RowMapper` or `ResultSetExtractor`.
Developers want to quickly turn results from an AI model into data types that can be passed to other functions and methods in their application.
The `OutputParser` helps achieve that goal.
== API Overview
This section provides a guide to the `OutputParser` interface.
=== OutputParser
Here is the `OutputParser` interface definition
```java
public interface OutputParser<T> extends Parser<T>, FormatProvider {
}
```
It combines the `Parser<T>` interface
```java
@FunctionalInterface
public interface Parser<T> {
T parse(String text);
}
```
and the `FormatProvider` interface
```java
public interface FormatProvider {
String getFormat();
}
```
The `Parser` interface parses text strings to produce instances of the type T.
The `FormatProvider` provides text instructions for the AI Model to format the output so that it an be parsed into the type T by the `Parser`.
These text instructions are most often appended to the end of the user input to the AI Model.
== Available Implementations
The `OutputParser` interface has the following available implementations.
* `BeanOutputParser`: Specifies the JSON schema for Java class and uses `DRAFT_2020_12` of the JSON schema specification as OpenAI has indicated this would give the best results.
The JSON output of the AI Model is then deserialized to a Java object, aka `JavaBean`.
* MapOutputParser: Similar to BeanOutputParser but the JSON payload is deserialized into a `java.util.Map<String, Object> instance.
* `ListOutputParser`: Specifies the output to be a comma delimited list.
There has been considerable effort in recent OpenAI models to improve the model's ability to return JSON by simply specifying 'return in JSON', but not all models support such direct support for returning structured data.
== Example Usage
You can run a fully working example that demonstrates the use of `BeanOutputParser` as part of the https://github.com/Azure-Samples/spring-ai-azure-workshop[Spring AI Azure Workshop].
Part of this workshop code is reproduced below.
The use case for the example is to as the AI Model to generate the filography for an actor.
The User prompt used is
```
String userMessage = """
Generate the filmography for the actor {actor}.
{format}
""";
```
The class `ActorsFilms` shown below
```java
public class ActorsFilms {
private String actor;
private List<String> movies;
// getters and toString omitted
}
```
Here is a controller class that shows these classes in use
```java
@GetMapping("/ai/output")
public ActorsFilms generate(@RequestParam(value = "actor", defaultValue = "Jeff Bridges") String actor) {
var outputParser = new BeanOutputParser<>(ActorsFilms.class);
String userMessage =
"""
Generate the filmography for the actor {actor}.
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(userMessage, Map.of("actor", actor, "format", outputParser.getFormat() ));
Prompt prompt = promptTemplate.create();
Generation generation = aiClient.generate(prompt).getGeneration();
ActorsFilms actorsFilms = outputParser.parse(generation.getText());
return actorsFilms;
}
```

View File

@@ -0,0 +1,3 @@
= Evaluation Testing
TBD

View File

@@ -99,6 +99,8 @@ More implementations may be supported in future releases.
If you have a vector database that needs to be supported by Spring AI, open an issue on GitHub or, even better, submit a pull request with an implementation.
Information on each of the Vector Store implementations can be found in the subsections of this chapter.
== Example Usage
To compute the embeddings for a vector database, you need to pick an embedding model that matches the higher-level AI model being used.
@@ -202,6 +204,7 @@ Consider the following example:
Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();
----
== Understanding Vectors
Vectors have dimensionality and a direction.
@@ -299,3 +302,4 @@ stem:[similarity(vec{A},vec{B}) = \cos(\theta) = \frac{ \sum_{i=1}^{n} {A_i B_i
****
This is the key formula used in the simple implementation of a vector store and can be found in the `InMemoryVectorStore` implementation.

View File

@@ -0,0 +1,197 @@
= Azure AI Service
This section will walk you through setting up the AzureVectorStore to store document embeddings and perform similarity searches using the Azure AI Search Service.
link:https://azure.microsoft.com/en-us/products/ai-services/cognitive-search[Azure AI Search] is a versatile cloud-hosted cloud information retrieval system that is part of Microsoft's larger AI platform. Among other features, it allows users to query information using vector-based storage and retrieval.
== Prerequisites
1. Azure Subscription: You will need an link:https://azure.microsoft.com/en-us/free/[Azure subscription] to use any Azure service.
2. Azure AI Search Service: Create an link:https://portal.azure.com/#create/Microsoft.Search[AI Search service]. Once the service is created, obtain the admin apiKey from the `Keys` section under `Settings` and retrieve the endpoint from the `Url` field under the `Overview` section.
3. (Optional) Azure OpenAI Service: Create an Azure link:https://portal.azure.com/#create/Microsoft.AIServicesOpenAI[OpenAI service]. **NOTE:** You may have to fill out a separate form to gain access to Azure Open AI services. Once the service is created, obtain the endpoint and apiKey from the `Keys and Endpoint` section under `Resource Management`.
== Configuration
On startup, the AzureVectorStore will attempt to create a new index within your AI Search service instance. Alternatively, you can create the index manually as explained in <<appendix-a, Appendix A>>.
To set up an AzureVectorStore, you will need the settings retrieved from the prerequisites above along with your index name:
* Azure AI Search Endpoint
* Azure AI Search Key
* (optional) Azure OpenAI API Endpoint
* (optional) Azure OpenAI API Key
You can provide these values as OS environment variables.
[source,bash]
----
export AZURE_AI_SEARCH_API_KEY=<My AI Search API Key>
export AZURE_AI_SEARCH_ENDPOINT=<My AI Search Index>
export OPENAI_API_KEY=<My Azure AI API Key> (Optional)
----
[NOTE]
====
You can replace Azure Open AI implementation with any valid OpenAI implementation that supports the Embeddings interface. For example, you could use Spring AI's Open AI or TransformersEmbedding implementations for embeddings instead of the Azure implementation.
====
== Dependencies
Add these dependencies to your project:
1. Select an Embeddings interface implementation. You can choose between:
* or OpenAI Embedding:
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
* Or Azure AI Embedding:
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
* Or Local Sentence Transformers Embedding:
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
2. Azure (AI Search) Vector Store
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-azure-vector-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
== Sample Code
To configure an Azure `SearchIndexClient` in your application, you can use the following code:
[source,java]
----
@Bean
public SearchIndexClient searchIndexClient() {
return new SearchIndexClientBuilder().endpoint(System.getenv("AZURE_AI_SEARCH_ENDPOINT"))
.credential(new AzureKeyCredential(System.getenv("AZURE_AI_SEARCH_API_KEY")))
.buildClient();
}
----
To create a vector store, you can use the following code by injecting the `SearchIndexClient` bean created in the above sample along with an `EmbeddingClient` provided by the Spring AI library that implements the desired Embeddings interface.
[source,java]
----
@Bean
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingClient embeddingClient) {
return new AzureVectorStore(searchIndexClient, embeddingClient,
// Define the metadata fields to be used
// in the similarity search filters.
List.of(MetadataField.text("country"),
MetadataField.int64("year"),
MetadataField.bool("active")));
}
----
[NOTE]
====
You must list explicitly all metadata field names and types for any metadata key used in the filter expression. The list above registers filterable metadata fields: `country` of type `TEXT`, `year` of type `INT64`, and `active` of type `BOOLEAN`.
If the filterable metadata fields are expanded with new entries, you have to (re)upload/update the documents with this metadata.
====
In your main code, create some documents:
[source,java]
----
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("country", "BG", "year", 2020)),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("country", "NL", "year", 2023)));
----
Add the documents to your vector store:
[source,java]
----
vectorStore.add(List.of(document));
----
And finally, retrieve documents similar to a query:
[source,java]
----
List<Document> results = vectorStore.similaritySearch(
SearchRequest
.query("Spring")
.withTopK(5));
----
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
=== Metadata filtering
You can leverage the generic, portable link:https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters[metadata filters] with AzureVectorStore as well.
For example, you can use either the text expression language:
[source,java]
----
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));
----
or programmatically using the expression DSL:
[source,java]
----
FilterExpressionBuilder b = Filter.builder();
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()));
----
The portable filter expressions get automatically converted into the proprietary Azure Search link:https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter[OData filters]. For example, the following portable filter expression:
[source,sql]
----
country in ['UK', 'NL'] && year >= 2020
----
is converted into Azure OData link:https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter[filter expression]:
[source,graphql]
----
$filter search.in(meta_country, 'UK,NL', ',') and meta_year ge 2020
----

View File

@@ -0,0 +1,164 @@
= Chroma
This section will walk you through setting up the Chroma VectorStore to store document embeddings and perform similarity searches.
link:https://github.com/chroma-core/chroma/pkgs/container/chroma[Chroma Container]
== What is Chroma?
link:https://docs.trychroma.com/[Chroma] is the open-source embedding database. It gives you the tools to store document embeddings, content, and metadata and to search through those embeddings, including metadata filtering.
=== Prerequisites
1. OpenAI Account: Create an account at link:https://platform.openai.com/signup[OpenAI Signup] and generate the token at link:https://platform.openai.com/account/api-keys[API Keys].
2. Access to ChromeDB. The <<appendix-a, setup local ChromaDB>> appendix shows how to set up a DB locally with a Docker container.
On startup, the `ChromaVectorStore` creates the required collection if one is not provisioned already.
== Configuration
To set up ChromaVectorStore, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
[source,bash]
----
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
----
== Dependencies
Add these dependencies to your project:
* OpenAI: Required for calculating embeddings.
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
----
* Chroma VectorStore.
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
----
== Sample Code
Create a `RestTemplate` instance with proper ChromaDB authorization configurations and Use it to create a `ChromaApi` instance:
[source,java]
----
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ChromaApi chromaApi(RestTemplate restTemplate) {
String chromaUrl = "http://localhost:8000";
ChromaApi chromaApi = ChromaApi(chromaUrl, restTemplate);
return chromaApi;
}
----
[NOTE]
====
For ChromaDB secured with link:https://docs.trychroma.com/usage-guide#static-api-token-authentication[Static API Token Authentication] use the `ChromaApi#withKeyToken(<Your Token Credentials>)` method to set your credentials. Check the `ChromaWhereIT` for an example.
For ChromaDB secured with link:https://docs.trychroma.com/usage-guide#basic-authentication[Basic Authentication] use the `ChromaApi#withBasicAuth(<your user>, <your password>)` method to set your credentials. Check the `BasicAuthChromaWhereIT` for an example.
====
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI starter to your project. This provides you with an implementation of the Embeddings client:
[source,java]
----
@Bean
public VectorStore chromaVectorStore(EmbeddingClient embeddingClient, ChromaApi chromaApi) {
return new ChromaVectorStore(embeddingClient, chromaApi, "TestCollection");
}
----
In your main code, create some documents:
[source,java]
----
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
----
Add the documents to your vector store:
[source,java]
----
vectorStore.add(List.of(document));
----
And finally, retrieve documents similar to a query:
[source,java]
----
List<Document> results = vectorStore.similaritySearch("Spring", 5);
----
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
=== Metadata filtering
You can leverage the generic, portable link:https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters[metadata filters] with ChromaVector store as well.
For example, you can use either the text expression language:
[source,java]
----
vectorStore.similaritySearch("The World", TOP_K, SIMILARITY_THRESHOLD,
"author in ['john', 'jill'] && article_type == 'blog'");
----
or programmatically using the `Filter.Expression` DSL:
[source,java]
----
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch("The World", TOP_K, SIMILARITY_THRESHOLD,
b.and(
b.in(List.of("john", "jill")),
b.eq("article_type", "blog")).build());
----
NOTE: Those (portable) filter expressions get automatically converted into the proprietary Chroma `where` link:https://docs.trychroma.com/usage-guide#using-where-filters[filter expressions].
For example, this portable filter expression:
```sql
author in ['john', 'jill'] && article_type == 'blog'
```
is converted inot the proprietyar Chroma format
```json
{"$and":[
{"author": {"$in": ["john", "jill"]}},
{"article_type":{"$eq":"blog"}}]
}
```
=== Run Chroma Locally
```shell
docker run -it --rm --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:0.4.15
```
Starts a chroma store at <http://localhost:8000/api/v1>

View File

@@ -0,0 +1,34 @@
= Milvus
link:https://milvus.io/[Milvus] is an open-source vector database that has garnered significant attention in the fields of data science and machine learning. One of its standout features lies in its robust support for vector indexing and querying. Milvus employs state-of-the-art, cutting-edge algorithms to accelerate the search process, making it exceptionally efficient at retrieving similar vectors, even when handling extensive datasets.
Milvus's popularity also comes from its ease of integration with popular Python-based frameworks such as PyTorch and TensorFlow, allowing for seamless inclusion in existing machine learning workflows.
In the e-commerce industry, Milvus is used in recommendation systems, which suggest products based on user preferences. In image and video analysis, it excels in tasks like object recognition, image similarity search, and content-based image retrieval. Additionally, it is commonly used in natural language processing for document clustering, semantic search, and question-answering systems.
== Starting Milvus Store
From within the `src/test/resources/` folder run:
[source,bash]
----
docker-compose up
----
To clean the environment:
[source,bash]
----
docker-compose down; rm -Rf ./volumes
----
Then connect to the vector store on link:http://localhost:19530[http://localhost:19530] or for management link:http://localhost:9001[http://localhost:9001] (user: `minioadmin`, pass: `minioadmin`)
== Troubleshooting
If Docker complains about resources, then execute:
[source,bash]
----
docker system prune --all --force --volumes
----

View File

@@ -0,0 +1,81 @@
= Neo4j
This section walks you through setting up `Neo4jVectorStore` to store document embeddings and perform similarity searches.
== What is Neo4j?
link:https://neo4j.com[Neo4j] is an open-source NoSQL graph database. It is a fully transactional database (ACID) that stores data structured as graphs consisting of nodes, connected by relationships. Inspired by the structure of the real world, it allows for high query performance on complex data while remaining intuitive and simple for the developer.
== What is Neo4j Vector Search?
link:https://neo4j.com/docs/cypher-manual/current/indexes-for-vector-search/[Neo4j's Vector Search] got introduced in Neo4j 5.11 and was considered GA with the release of version 5.13. Embeddings can be stored on _Node_ properties and can be queried with the `db.index.vector.queryNodes()` function. Those indexes are powered by Lucene using a Hierarchical Navigable Small World Graph (HNSW) to perform a k approximate nearest neighbors (k-ANN) query over the vector fields.
=== Prerequisites
1. OpenAI Account: Create an account at link:https://platform.openai.com/signup[OpenAI Signup] and generate the token at link:https://platform.openai.com/account/api-keys[API Keys].
2. A running Neo4j (5.13+) instance
a. link:https://hub.docker.com/_/neo4j[Docker] image _neo4j:5.13_
b. link:https://neo4j.com/download/[Neo4j Desktop]
c. link:https://neo4j.com/cloud/aura-free/[Neo4j Aura]
d. link:https://neo4j.com/deployment-center/[Neo4j Server] instance
== Configuration
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide (e.g. via `application.properties`) configurations for your instance.
Additionally, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
[source,bash]
----
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
----
== Repository
To acquire Spring AI artifacts, declare the Spring Snapshot repository:
[source,xml]
----
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
----
== Dependencies
Add these dependencies to your project:
* OpenAI: Required for calculating embeddings.
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
* Neo4j Vector Store
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-neo4j-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
== Sample Code
To configure `Neo4jVectorStore` in your application, you can use the following setup:
Add to `application.properties` (using your Neo4j credentials):

View File

@@ -1,53 +1,54 @@
# PGvector Vector Store
= PGvector
This readme walks you through setting up the PGvector `VectorStore` to store document embeddings and perform similarity searches.
This section walks you through setting up the PGvector `VectorStore` to store document embeddings and perform similarity searches.
## What is PGvector?
== What is PGvector?
[PGvector](https://github.com/pgvector/pgvector) is an open-source extension for PostgreSQL that enables storing and searching over machine learning-generated embeddings. It provides different capabilities that let users identify both exact and approximate nearest neighbors. It is designed to work seamlessly with other PostgreSQL features, including indexing and querying.
link:https://github.com/pgvector/pgvector[PGvector] is an open-source extension for PostgreSQL that enables storing and searching over machine learning-generated embeddings. It provides different capabilities that let users identify both exact and approximate nearest neighbors. It is designed to work seamlessly with other PostgreSQL features, including indexing and querying.
## Prerequisites
=== Prerequisites
1. OpenAI Account: Create an account at [OpenAI Signup](https://platform.openai.com/signup) and generate the token at [API Keys](https://platform.openai.com/account/api-keys).
1. OpenAI Account: Create an account at link:https://platform.openai.com/signup[OpenAI Signup] and generate the token at link:https://platform.openai.com/account/api-keys[API Keys].
2. Access to PostgresSQL instance with following configurations
2. Access to PostgreSQL instance with the following configurations
The [setup local Postgres/PGVector](#appendix_a) appendix shows how to setup a DB locally with a Docker container.
The <<appendix_a,setup local Postgres/PGVector>> appendix shows how to set up a DB locally with a Docker container.
On startup the `PgVectorStore` will attempt to install the required database extensions and create the required `vector_store` table with index.
Optionally, you can do this manually like so:
On startup, the `PgVectorStore` will attempt to install the required database extensions and create the required `vector_store` table with an index. Optionally, you can do this manually like so:
(Optional)
```sql
CREATE EXTENSION IF NOT EXISTS vector
CREATE EXTENSION IF NOT EXISTS hstore
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"
[sql]
----
CREATE EXTENSION IF NOT EXISTS vector
CREATE EXTENSION IF NOT EXISTS hstore
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"
CREATE TABLE IF NOT EXISTS vector_store (
id uuid DEFAULT uuid_generate_v4 () PRIMARY KEY,
content text,
metadata json,
embedding vector(1536)
)
CREATE TABLE IF NOT EXISTS vector_store (
id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(1536)
)
CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops)
```
CREATE INDEX ON vector_store USING HNSW (embedding vector_cosine_ops)
----
## Configuration
== Configuration
To set up `PgVectorStore`, you need to provide (via `application.yaml`) configurations to your PostgresSQL database.
To set up `PgVectorStore`, you need to provide (via `application.yaml`) configurations to your PostgreSQL database.
Additionally, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
```bash
[source,bash]
----
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
```
----
## Repository
== Repository
To acquire Spring AI artifacts, declare the Spring Snapshot repository:
```xml
[source,xml]
----
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
@@ -56,15 +57,16 @@ To acquire Spring AI artifacts, declare the Spring Snapshot repository:
<enabled>false</enabled>
</releases>
</repository>
```
----
## Dependencies
== Dependencies
Add these dependencies to your project:
1. PostgresSQL connection and `JdbcTemplate` auto-configuration.
* PostgreSQL connection and `JdbcTemplate` auto-configuration.
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
@@ -75,83 +77,91 @@ Add these dependencies to your project:
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
```
----
2. OpenAI: Required for calculating embeddings.
* OpenAI: Required for calculating embeddings.
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
3. PGvector
* PGvector
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-pgvector-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
## Sample Code
== Sample Code
To configure `PgVectorStore` in your application, you can use the following setup:
Add to `application.yml` (using your DB credentials):
```yml
[yml]
----
spring:
datasource:
url: jdbc:postgresql://localhost:5432/vector_store
username: postgres
password: postgres
```
datasource:
url: jdbc:postgresql://localhost:5432/vector_store
username: postgres
password: postgres
----
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI Starter to your project.
This provides you with an implementation of the Embeddings client:
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI Starter to your project. This provides you with an implementation of the Embeddings client:
```java
[source,java]
----
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingClient embeddingClient) {
return new PgVectorStore(jdbcTemplate, embeddingClient);
}
```
----
In your main code, create some documents:
```java
[source,java]
----
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
```
----
Add the documents to your vector store:
```java
[source,java]
----
vectorStore.add(List.of(document));
```
----
And finally, retrieve documents similar to a query:
```java
[source,java]
----
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
```
----
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
## <a name="appendix_a" /> Appendix A: Run Postgres & PGVector DB locally
== Run Postgres & PGVector DB locally
```
----
docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres ankane/pgvector
```
----
You can connect to this server like this:
```
----
psql -U postgres -h localhost -p 5432
```
----

View File

@@ -1,18 +1,18 @@
# Pinecone Vector Store
= Pinecone
This readme walks you through setting up the Pinecone `VectorStore` to store document embeddings and perform similarity searches.
This section walks you through setting up the Pinecone `VectorStore` to store document embeddings and perform similarity searches.
## What is Pinecone?
== What is Pinecone?
[Pinecone](https://www.pinecone.io/) is a popular cloud-based vector database, which allows you to store and search vectors efficiently.
link:https://www.pinecone.io/[Pinecone] is a popular cloud-based vector database, which allows you to store and search vectors efficiently.
## Prerequisites
== Prerequisites
1. Pinecone Account: Before you start, sign up for a [Pinecone account](https://app.pinecone.io/).
1. Pinecone Account: Before you start, sign up for a link:https://app.pinecone.io/[Pinecone account].
2. Pinecone Project: Once registered, create a new project, an index, and generate an API key. You'll need these details for configuration.
3. OpenAI Account: Create an account at [OpenAI Signup](https://platform.openai.com/signup) and generate the token at [API Keys](https://platform.openai.com/account/api-keys)
3. OpenAI Account: Create an account at link:https://platform.openai.com/signup[OpenAI Signup] and generate the token at link:https://platform.openai.com/account/api-keys[API Keys].
## Configuration
== Configuration
To set up `PineconeVectorStore`, gather the following details from your Pinecone account:
@@ -22,23 +22,26 @@ To set up `PineconeVectorStore`, gather the following details from your Pinecone
* Pinecone Index Name
* Pinecone Namespace
> **Note**
> This information is available to you in the Pinecone UI portal.
[NOTE]
====
This information is available to you in the Pinecone UI portal.
====
When setting up embeddings, select a vector dimension of `1536`. This matches the dimensionality of OpenAI's model `text-embedding-ada-002`, which we'll be using for this guide.
Additionally, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
```bash
[source,bash]
----
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
```
----
## Repository
== Repository
To acquire Spring AI artifacts, declare the Spring Snapshot repository:
```xml
[source,xml]
----
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
@@ -47,37 +50,40 @@ To acquire Spring AI artifacts, declare the Spring Snapshot repository:
<enabled>false</enabled>
</releases>
</repository>
```
----
## Dependencies
== Dependencies
Add these dependencies to your project:
1. OpenAI: Required for calculating embeddings.
* OpenAI: Required for calculating embeddings.
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
2. Pinecone
* Pinecone
```xml
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-pinecone</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
----
## Sample Code
== Sample Code
To configure Pinecone in your application, you can use the following setup:
```java
[source,java]
----
@Bean
public PineconeVectorStoreConfig pineconeVectorStoreConfig() {
@@ -89,37 +95,41 @@ public PineconeVectorStoreConfig pineconeVectorStoreConfig() {
.withNamespace("") // the free tier doesn't support namespaces.
.build();
}
```
----
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI starter to your project.
This provides you with an implementation of the Embeddings client:
```java
[source,java]
----
@Bean
public VectorStore vectorStore(PineconeVectorStoreConfig config, EmbeddingClient embeddingClient) {
return new PineconeVectorStore(config, embeddingClient);
}
```
----
In your main code, create some documents:
```java
[source,java]
----
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
```
----
Add the documents to Pinecone:
```java
[source,java]
----
vectorStore.add(List.of(document));
```
----
And finally, retrieve documents similar to a query:
```java
[source,java]
----
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
```
----
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".

View File

@@ -0,0 +1,207 @@
= Weaviate
This section will walk you through setting up the Weaviate VectorStore to store document embeddings and perform similarity searches.
== What is Weaviate?
link:https://weaviate.io/[Weaviate] is an open-source vector database.
It allows you to store data objects and vector embeddings from your favorite ML-models and scale seamlessly into billions of data objects.
It provides tools to store document embeddings, content, and metadata and to search through those embeddings, including metadata filtering.
== Prerequisites
1. `EmbeddingClient` instance to compute the document embeddings. Several options are available:
- `Transformers Embedding` - computes the embedding in your local environment. Follow the ONNX Transformers Embedding instructions.
- `OpenAI Embedding` - uses the OpenAI embedding endpoint. You need to create an account at link:https://platform.openai.com/signup[OpenAI Signup] and generate the api-key token at link:https://platform.openai.com/account/api-keys[API Keys].
- You can also use the `Azure OpenAI Embedding` or the `PostgresML Embedding Client`.
2. `Weaviate cluster`. You can set up a cluster locally in a Docker container or create a link:https://console.weaviate.cloud/[Weaviate Cloud Service]. For the latter, you need to create a Weaviate account, set up a cluster, and get your access API key from the link:https://console.weaviate.cloud/dashboard[dashboard details].
On startup, the `WeaviateVectorStore` creates the required `SpringAiWeaviate` object schema if it's not already provisioned.
== Dependencies
Add these dependencies to your project:
* Embedding Client boot starter, required for calculating embeddings.
* Transformers Embedding (Local) and follow the ONNX Transformers Embedding instructions.
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
or use OpenAI (Cloud)
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
You'll need to provide your OpenAI API Key. Set it as an environment variable like so:
[source,bash]
----
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
----
* Add the Weaviate VectorStore dependency
[source,xml]
----
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-weaviate-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
----
== Usage
Create a WeaviateVectorStore instance connected to the local Weaviate cluster:
[source,java]
----
@Bean
public VectorStore vectorStore(EmbeddingClient embeddingClient) {
WeaviateVectorStoreConfig config = WeaviateVectorStoreConfig.builder()
.withScheme("http")
.withHost("localhost:8080")
// Define the metadata fields to be used
// in the similarity search filters.
.withFilterableMetadataFields(List.of(
MetadataField.text("country"),
MetadataField.number("year"),
MetadataField.bool("active")))
// Consistency level can be: ONE, QUORUM, or ALL.
.withConsistencyLevel(ConsistentLevel.ONE)
.build();
return new WeaviateVectorStore(config, embeddingClient);
}
----
> [NOTE]
> You must list explicitly all metadata field names and types (`BOOLEAN`, `TEXT`, or `NUMBER`) for any metadata key used in filter expression.
> The `withFilterableMetadataKeys` above registers filterable metadata fields: `country` of type `TEXT`, `year` of type `NUMBER`, and `active` of type `BOOLEAN`.
>
> If the filterable metadata fields are expanded with new entries, you have to (re)upload/update the documents with this metadata.
>
> You can use the following Weaviate link:https://weaviate.io/developers/weaviate/api/graphql/filters#special-cases[system metadata] fields without explicit definition: `id`, `_creationTimeUnix`, and `_lastUpdateTimeUnix`.
Then in your main code, create some documents:
[source,java]
----
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("country", "UK", "active", true, "year", 2020)),
new Document("The World is Big and Salvation Lurks Around the Corner", Map.of()),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("country", "NL", "active", false, "year", 2023)));
----
Now add the documents to your vector store:
[source,java]
----
vectorStore.add(List.of(document));
----
And finally, retrieve documents similar to a query:
[source,java]
----
List<Document> results = vectorStore.similaritySearch(
SearchRequest
.query("Spring")
.withTopK(5));
----
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
=== Metadata filtering
You can leverage the generic, portable link:https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters[metadata filters] with WeaviateVectorStore as well.
For example, you can use either the text expression language:
[source,java]
----
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));
----
or programmatically using the expression DSL:
[source,java]
----
FilterExpressionBuilder b = Filter.builder();
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()));
----
The portable filter expressions get automatically converted into the proprietary Weaviate link:https://weaviate.io/developers/weaviate/api/graphql/filters[where filters].
For example, the following portable filter expression:
[source,sql]
----
country in ['UK', 'NL'] && year >= 2020
----
is converted into Weaviate GraphQL link:https://weaviate.io/developers/weaviate/api/graphql/filters[where filter expression]:
[source,graphql]
----
operator:And
operands:
[{
operator:Or
operands:
[{
path:["meta_country"]
operator:Equal
valueText:"UK"
},
{
path:["meta_country"]
operator:Equal
valueText:"NL"
}]
},
{
path:["meta_year"]
operator:GreaterThanEqual
valueNumber:2020
}]
----
== Run Weaviate cluster in docker container
Start Weaviate in a docker container:
[source,bash]
----
docker run -it --rm --name weaviate -e AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true -e PERSISTENCE_DATA_PATH=/var/lib/weaviate -e QUERY_DEFAULTS_LIMIT=25 -e DEFAULT_VECTORIZER_MODULE=none -e CLUSTER_HOSTNAME=node1 -p 8080:8080 semitechnologies/weaviate:1.22.4
----
Starts a Weaviate cluster at http://localhost:8080/v1 with scheme=http, host=localhost:8080, and apiKey="". Then follow the usage instructions.

View File

@@ -93,6 +93,21 @@ The "'rendered'" string becomes the content of the prompt supplied to the AI mod
There is considerable variability in the specific data format of the prompt sent to the model.
Initially starting as simple strings, prompts have evolved to include multiple messages, where each string in each message represents a distinct role for the model.
== Embeddings
Embeddings transform text into numerical arrays or vectors, enabling AI models to process and interpret language data.
This transformation from text to numbers and back is a key element in how AI interacts with and understands human language.
As a Java developer exploring AI, it's not necessary to comprehend the intricate mathematical theories or the specific implementations behind these vector representations.
A basic understanding of their role and function within AI systems suffices, particularly when you're integrating AI functionalities into your applications.
Embeddings are particularly relevant in practical applications like the retrieval-augmented generation pattern.
They enable the representation of data as points in a semantic space, which is akin to the 2-D space of Euclidean geometry, but in higher dimensions.
This means just like how points on a plane in Euclidean geometry can be close or far based on their coordinates, in a semantic space, the proximity of points reflects the similarity in meaning.
So, sentences about similar topics are positioned closer in this multi-dimensional space, much like points lying close to each other on a graph.
This proximity aids in tasks like text classification, semantic search, and even product recommendations, as it allows the AI to discern and group related concepts based on their 'location' in this expanded semantic landscape.
You can think of this semantic space as a vector,
== Tokens
Tokens serve as the building blocks of how an AI model works.
@@ -125,13 +140,6 @@ Output parsing employs meticulously crafted prompts, often necessitating multipl
This challenge has prompted OpenAI to introduce 'OpenAI Functions' as a means to specify the desired output format from the model precisely.
== Chaining Calls
A chain is a concept that represents a series of calls to an AI model.
It uses the output from one call as the input to another.
By chaining calls together, you can support complex use cases by composing pipelines of multiple chains.
== Bringing Your Data to the AI model
How can you equip the AI model with information on which it has not been trained?

View File

@@ -1,304 +0,0 @@
# Azure AI Search VectorStore
This README will walk you through setting up the `AzureVectorStore`` to store document embeddings and perform similarity searches using the Azure AI Search Service.
[Azure AI Search](https://azure.microsoft.com/en-us/products/ai-services/cognitive-search) is a versatile cloud hosted cloud information retrieval system that is part of Microsoft's larger AI platform.
Among other features, it allows users to query information using vector based storage and retrieval.
## Prerequisites
1. Azure Subscription: You will need an [Azure subscription](https://azure.microsoft.com/en-us/free/) to use any Azure service.
2. Azure AI Search Service: Create an [AI Search service](https://portal.azure.com/#create/Microsoft.Search). Once the service is created,
obtain the admin apiKey from the `Keys` section under `Settings` and retrieve the endpoint from the `Url` field under the `Overview` section.
3. (Optional) Azure OpenAI Service: Create an an Azure [OpenAI service](https://portal.azure.com/#create/Microsoft.AIServicesOpenAI).
**NOTE:** You may have to fill out a separate form to gain access to Azure Open AI services.
Once the service is created, obtain the endpoint and apiKey from the `Keys and Endpoint` section under `Resource Management`
## Configuration
On startup the `AzureVectorStore` will attempt to create a new index within your AI Search service instance.
Alternatively you create the index, manually as explained in [Appendix A](appendix_a).
To set up an AzureVectorStore, you will need the settings retrieved from the prerequisites above along with your index name:
* Azure AI Search Endpoint
* Azure AI Search Key
* (optional) Azure OpenAI API Endpoint
* (optional) Azure OpenAI API Key
You can provide these values as OS environment variables.
```bash
export 'AZURE_AI_SEARCH_API_KEY=<My AI Search API Key>'
export 'AZURE_AI_SEARCH_ENDPOINT=<My AI Search Index>'
export 'OPENAI_API_KEY=<My Azure AI API Key>' (Optional)
```
**NOTE** You can replace Azure Open AI implementation with any valid OpenAI implementation that supports the Embeddings interface. For example, you could use Spring AIs Open AI or TransformersEmbedding implementations for embeddings instead of the Azure implementation.
## Dependencies
Add these dependencies to your project:
1. Select an Embeddings interface implementation.
You can choose between:
* or OpenAI Embedding:
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
* Or Azure AI Embedding:
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
* Or Local Sentence Transformers Embedding:
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
2. Azure (AI Search) Vector Store
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-azure-vector-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
## Sample Code
To configure an Azure `SearchIndexClient` in your application, you can use the following code:
```java
@Bean
public SearchIndexClient searchIndexClient() {
return new SearchIndexClientBuilder().endpoint(System.getenv("AZURE_AI_SEARCH_ENDPOINT"))
.credential(new AzureKeyCredential(System.getenv("AZURE_AI_SEARCH_API_KEY")))
.buildClient();
}
```
To create a vector store, you can use the following code by injecting the `SearchIndexClient` bean created in the above sample along with and `EmbeddingClient` provided by Spring AI library that's implements the desired Embeddings interface.
```java
@Bean
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingClient embeddingClient) {
return new AzureVectorStore(searchIndexClient, embeddingClient,
// Define the metadata fields to be used
// in the similarity search filters.
List.of(MetadataField.text("country"),
MetadataField.int64("year"),
MetadataField.bool("active")));
}
```
> [!NOTE]
> You must list explicitly all metadata field names and types for any metadata key used in filter expression.
>The list above registers filterable metadata fields: `country` of type `TEXT`, `year` of type `INT64` and `active` of type `BOOLEAN`.
>
> If the filterable metadata fields is expanded with new entires, you have to (re)upload/update the documents with this metadata.
In your main code, create some documents
```java
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("country", "BG", "year", 2020)),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("country", "NL", "year", 2023)));
```
Add the documents to your vector store:
```java
vectorStore.add(List.of(document));
```
And finally, retrieve documents similar to a query:
```java
List<Document> results = vectorStore.similaritySearch(
SearchRequest
.query("Spring")
.withTopK(5));
```
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
### Metadata filtering
You can leverage the generic, portable [metadata filters](https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters) with AzureVectorStore as well.
For example you can use either the text expression language:
```java
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));
```
or programmatically using the expression DSL:
```java
FilterExpressionBuilder b = Filter.builder();
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()));
```
The, portable, filter expressions get automatically converted into the proprietary Azure Search [OData filters](https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter).
For example the following, portable, filter expression
```sql
country in ['UK', 'NL'] && year >= 2020
```
is converted into Azure, OData, [filter expression](https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter):
```graphQL
$filter search.in(meta_country, 'UK,NL', ',') and meta_year ge 2020
```
## Integration With Azure OpenAI Studio Data Ingestion
Azure Open AI services provides a convenient method to upload documents into an Index as described in this Microsoft
[learning document](https://learn.microsoft.com/en-us/azure/ai-services/openai/use-your-data-quickstart?tabs=command-line&pivots=programming-language-csharp).
The `AzureVectorStore` implementation is compatible with indexes that use this methodology facilitating an *easier* way to integrate with your existing documents for the purpose of searching and integrating with the AI system.
## <a name="appendix_a" /> Appendix A: Create Vector Store Search Index </a>
The easiest way to crate a search index manually, is to create one from a JSON document.
This can be done by clicking on the `Indexes` link under the `Search management` section.
From the Indexes page, click `+ Add index` and select `Add index (JSON)`. In the
`Add index (JSON)` window of the right side of your screen, enter the following JSON replacing `<INDEX NAME>` with the name you would like to give your index and click
`save`.
```json
{
"name": "<INDEX NAME>",
"defaultScoringProfile": null,
"fields": [
{
"name": "id",
"type": "Edm.String",
"searchable": false,
"filterable": false,
"retrievable": true,
"sortable": false,
"facetable": false,
"key": true,
"indexAnalyzer": null,
"searchAnalyzer": null,
"analyzer": null,
"normalizer": null,
"dimensions": null,
"vectorSearchConfiguration": null,
"synonymMaps": []
},
{
"name": "embedding",
"type": "Collection(Edm.Single)",
"searchable": true,
"filterable": false,
"retrievable": true,
"sortable": false,
"facetable": false,
"key": false,
"indexAnalyzer": null,
"searchAnalyzer": null,
"analyzer": null,
"normalizer": null,
"dimensions": 1536, // set the dimensions for the configured Embedding Client. It defaults to to OpenAI's 1536 size.
"vectorSearchConfiguration": "default",
"synonymMaps": []
},
{
"name": "content",
"type": "Edm.String",
"searchable": true,
"filterable": false,
"retrievable": true,
"sortable": false,
"facetable": false,
"key": false,
"indexAnalyzer": null,
"searchAnalyzer": null,
"analyzer": null,
"normalizer": null,
"dimensions": null,
"vectorSearchConfiguration": null,
"synonymMaps": []
},
{
"name": "metadata",
"type": "Edm.String",
"searchable": true,
"filterable": true,
"retrievable": true,
"sortable": true,
"facetable": true,
"key": false,
"indexAnalyzer": null,
"searchAnalyzer": null,
"analyzer": null,
"normalizer": null,
"dimensions": null,
"vectorSearchConfiguration": null,
"synonymMaps": []
}
],
"scoringProfiles": [],
"corsOptions": null,
"suggesters": [],
"analyzers": [],
"normalizers": [],
"tokenizers": [],
"tokenFilters": [],
"charFilters": [],
"encryptionKey": null,
"semantic": null,
"vectorSearch": {
"algorithmConfigurations": [
{
"name": "default",
"kind": "hnsw",
"hnswParameters": {
"metric": "cosine",
"m": 4,
"efConstruction": 400,
"efSearch": 1000
},
"exhaustiveKnnParameters": null
}
]
}
}
```

View File

@@ -1,154 +0,0 @@
# Chroma VectorStore
This readme will walk you through setting up the Chroma VectorStore to store document embeddings and perform similarity searches.
<https://github.com/chroma-core/chroma/pkgs/container/chroma>
## What is Chroma?
[Chroma](https://docs.trychroma.com/) is the open-source embedding database. It gives you the tools to store document embeddings, content and metadata and to search through those embeddings including metadata filtering.
## Prerequisites
1. OpenAI Account: Create an account at [OpenAI Signup](https://platform.openai.com/signup) and generate the token at [API Keys](https://platform.openai.com/account/api-keys).
2. Access to ChromeDB. The [setup local ChromaDB](#appendix_a) appendix show how to setup a DB locally with a Docker container.
On startup the `ChromaVectorStore` creates the required collection if one is not provisioned already.
## Configuration
To set up ChromaVectorStore, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
```bash
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
```
## Dependencies
Add these dependencies to your project:
1. OpenAI: Required for calculating embeddings.
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
```
2. Chroma VectorStore.
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
```
## Sample Code
Create an `RestTemplate` instance with proper ChromaDB authorization configurations and Use it to create `ChromaApi` instance:
```java
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ChromaApi chromaApi(RestTemplate restTemplate) {
String chromaUrl = "http://localhost:8000";
ChromaApi chromaApi = ChromaApi(chromaUrl, restTemplate);
return chromaApi;
}
```
> [!NOTE]
> For ChromaDB secured with [Static API Token Authentication](https://docs.trychroma.com/usage-guide#static-api-token-authentication) use the `ChromaApi#withKeyToken(<Your Token Credentials>)` method to set your credentials. Check the `ChromaWhereIT` for an example.
> [!NOTE]
> For ChromaDB secured with [Basic Authentication](https://docs.trychroma.com/usage-guide#basic-authentication) use the `ChromaApi#withBasicAuth(<your user>, <your password>)` method to set your credentials. Check the `BasicAuthChromaWhereIT` for an example.
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI starter to your project.
This provides you with an implementation of the Embeddings client:
```java
@Bean
public VectorStore chromaVectorStore(EmbeddingClient embeddingClient, ChromaApi chromaApi) {
return new ChromaVectorStore(embeddingClient, chromaApi, "TestCollection");
}
```
In your main code, create some documents
```java
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
```
Add the documents to your vector store:
```java
vectorStore.add(List.of(document));
```
And finally, retrieve documents similar to a query:
```java
List<Document> results = vectorStore.similaritySearch("Spring", 5);
```
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
### Metadata filtering
You can leverage the generic, portable [metadata filters](https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters) with ChromaVector store as well.
For example you can use either the text expression language:
```java
vectorStore.similaritySearch("The World", TOP_K, SIMILARITY_THRESHOLD,
"author in ['john', 'jill'] && article_type == 'blog'");
```
or programmatically using the `Filter.Expression` DSL:
```java
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch("The World", TOP_K, SIMILARITY_THRESHOLD,
b.and(
b.in(List.of("john", "jill")),
b.eq("article_type", "blog")).build());
```
NOTE: Those (portable) filter expressions get automatically converted into the proprietary Chroma `where` [filter expressions](https://docs.trychroma.com/usage-guide#using-where-filters).
For example this portable filter expression:
```sql
author in ['john', 'jill'] && article_type == 'blog'
```
is converted into the proprietary Chroma format:
```json
{"$and":[
{"author": {"$in": ["john", "jill"]}},
{"article_type":{"$eq":"blog"}}]
}"
```
## <a name="appendix_a" /> Appendix A: Run Chroma Locally
```
docker run -it --rm --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:0.4.15
```
starts a chroma store at <http://localhost:8000/api/v1>

View File

@@ -1,35 +0,0 @@
# Introduction to Milvus
[Milvus](https://milvus.io/) is an open-source vector database that has garnered significant attention in the fields of data science and machine learning.
One of its standout features lies in its robust support for vector indexing and querying.
Milvus employs state-of-the-art, cutting-edge algorithms to accelerate the search process, making it exceptionally efficient at retrieving similar vectors, even when handling extensive datasets.
Milvus's popularity also comes from its ease of integration with popular Python based frameworks such as PyTorch and TensorFlow, allowing for seamless inclusion in existing machine learning workflows.
In the e-commerce industry, Milvus is used in recommendation systems, which suggest products based on user preferences.
In image and video analysis, it excels in tasks like object recognition, image similarity search, and content-based image retrieval.
Additionally, it is commonly used in natural language processing for document clustering, semantic search, and question-answering systems.
## Starting Milvus Store
From within the `src/test/resources/` folder run:
```
docker-compose up
```
To clean the environment:
```
docker-compose down; rm -Rf ./volumes
```
Then connect to the vector store on http://localhost:19530 or for management http://localhost:9001 (user: `minioadmin`, pass: `minioadmin`)
## Throubleshooting
If Docker complains about resources, then execute:
```
docker system prune --all --force --volumes
```

View File

@@ -1,141 +0,0 @@
# Neo4j Store
This readme walks you through setting up `Neo4jVectorStore` to store document embeddings and perform similarity searches.
## What is Neo4j?
[Neo4j](https://neo4j.com) is an open source NoSQL graph database.
It is a fully transactional database (ACID) that stores data structured as graphs consisting of nodes, connected by relationships.
Inspired by the structure of the real world, it allows for high query performance on complex data, while remaining intuitive and simple for the developer.
## What is Neo4j Vector Search?
[Neo4j's Vector Search](https://neo4j.com/docs/cypher-manual/current/indexes-for-vector-search/) got introduced in Neo4j 5.11 and was considered GA with the release of version 5.13.
Embeddings can be stored on _Node_ properties and can be queried with the [`db.index.vector.queryNodes()`](https://neo4j.com/docs/operations-manual/5/reference/procedures/#procedure_db_index_vector_queryNodes) function.
Those indexes are powered by Lucene using a Hierarchical Navigable Small World Graph (HNSW) to perform a k approximate nearest neighbors (k-ANN) query over the vector fields.
## Prerequisites
1. OpenAI Account: Create an account at [OpenAI Signup](https://platform.openai.com/signup) and generate the token at [API Keys](https://platform.openai.com/account/api-keys).
2. A running Neo4j (5.13+) instance
1. [Docker](https://hub.docker.com/_/neo4j) image _neo4j:5.13_
2. [Neo4j Desktop](https://neo4j.com/download/)
3. [Neo4j Aura](https://neo4j.com/cloud/aura-free/)
4. [Neo4j Server](https://neo4j.com/deployment-center/) instance
## Configuration
To connect to Neo4j and use the `Neo4jVectorStore`, you need to provide (e.g. via `application.properties`) configurations for your instance.
Additionally, you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
```bash
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
```
## Repository
To acquire Spring AI artifacts, declare the Spring Snapshot repository:
```xml
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
```
## Dependencies
Add these dependencies to your project:
1. OpenAI: Required for calculating embeddings.
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
2. Neo4j Vector Store
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-neo4j-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
## Sample Code
To configure `Neo4jVectorStore` in your application, you can use the following setup:
Add to `application.properties` (using your Neo4j credentials):
```
spring.neo4j.uri=neo4j://localhost:7687
spring.neo4j.authentication.username=neo4j
spring.neo4j.authentication.password=password
```
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI Starter to your project.
This provides you with an implementation of the Embeddings client:
```java
public VectorStore vectorStore(Driver driver, EmbeddingClient embeddingClient) {
return new Neo4jVectorStore(driver, embeddingClient,
Neo4jVectorStore.Neo4jVectorStoreConfig.defaultConfig());
}
```
In your main code, create some documents:
```java
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
```
Add the documents to your vector store:
```java
vectorStore.add(List.of(document));
```
And finally, retrieve documents similar to a query:
```java
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
```
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!" as the first result.
## Neo4jVectorStore config
As you have already noticed, the `Neo4jVectorStore` accepts a configuration parameter.
The default configuration should fit for most of the basic use-cases, but if you want to tweak it a little bit for you needs, you can edit those defaults.
The default params
* embedding dimension = 1536
* distance type = cosine
* document node label = "Document"
* node property for embedding = "embedding"
* database name = "neo4j"
can be configured with
```java
Neo4jVectorStore.Neo4jVectorStoreConfig.builder()
.withDatabaseName("databaseName")
.withDistanceType(Neo4jVectorStore.Neo4jDistanceType.COSINE / Neo4jVectorStore.Neo4jDistanceType.EUCLIDEAN)
.withLabel("CustomLabel")
.withEmbeddingProperty("vectorEmbedding")
.withEmbeddingDimension(1024)
```

View File

@@ -1,196 +0,0 @@
# Weaviate VectorStore
This readme will walk you through setting up the Weaviate VectorStore to store document embeddings and perform similarity searches.
## What is Weaviate?
[Weaviate](https://weaviate.io/) is an open-source vector database.
It allows you to store data objects and vector embeddings from your favorite ML-models, and scale seamlessly into billions of data objects.
It gives you the tools to store document embeddings, content and metadata and to search through those embeddings including metadata filtering.
## Prerequisites
1. `EmbeddingClient` instance to compute the document embeddings. Several options are available:
- `Transformers Embedding` - computes the embedding in your, local environment. Follow the [Transformers Embedding](../../embedding-clients/transformers-embedding/) instructions.
- `OpenAI Embedding` - uses the OpenAI embedding endpoint. You need to create an account at [OpenAI Signup](https://platform.openai.com/signup) and generate the api-key token at [API Keys](https://platform.openai.com/account/api-keys).
- You can also use the `Azure OpenAI Embedding` or the `PostgresML Embedding Client`.
2. `Weaviate cluster`. You can a cluster, locally, in a Docker container ([Local Weaviate](#appendix_a)) or create a [Weaviate Cloud Service](https://console.weaviate.cloud/). For later you need to create an weaviate account spin a cluster and get your access api-key from the [dashboard details](https://console.weaviate.cloud/dashboard).
On startup the `WeaviateVectorStore` creates the required `SpringAiWeaviate` object schema (if such is not already provisioned).
## Dependencies
Add these dependencies to your project:
1. Embedding Client boot starter, required for calculating embeddings.
- Transformers Embedding (Local)
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
follow the [transformers-embedding](../../embedding-clients/transformers-embedding/README.md) instructions.
- or OpenAI (Cloud)
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
you'll need to provide your OpenAI API Key. Set it as an environment variable like so:
```bash
export SPRING_AI_OPENAI_API_KEY='Your_OpenAI_API_Key'
```
2. Weaviate VectorStore.
```xml
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-weaviate-store</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
```
## <a name="usage"/> Usage </a>
Create a WeaviateVectorStore instance connected to local Weaviate cluster:
```java
@Bean
public VectorStore vectorStore(EmbeddingClient embeddingClient) {
WeaviateVectorStoreConfig config = WeaviateVectorStoreConfig.builder()
.withScheme("http")
.withHost("localhost:8080")
// Define the metadata fields to be used
// in the similarity search filters.
.withFilterableMetadataFields(List.of(
MetadataField.text("country"),
MetadataField.number("year"),
MetadataField.bool("active")))
// Consistency level can be: ONE, QUORUM or ALL.
.withConsistencyLevel(ConsistentLevel.ONE)
.build();
return new WeaviateVectorStore(config, embeddingClient);
}
```
> [!NOTE]
> You must list explicitly all metadata field names and types (`BOOLEAN`, `TEXT` or `NUMBER`) for any metadata key used in filter expression.
>The `withFilterableMetadataKeys` above registers filterable metadata fields: `country` of type `TEXT`, `year` of type `NUMBER` and `active` of type `BOOLEAN`.
>
> If the filterable metadata fields is expanded with new entires, you have to (re)upload/update the documents with this metadata.
>
> You can use the following, Weaviate [system metadata](https://weaviate.io/developers/weaviate/api/graphql/filters#special-cases) fields without explicit definition: `id`, `_creationTimeUnix` and `_lastUpdateTimeUnix`.
Then yn your main code, create some documents
```java
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("country", "UK", "active", true, "year", 2020)),
new Document("The World is Big and Salvation Lurks Around the Corner", Map.of()),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("country", "NL", "active", false, "year", 2023)));
```
Add the documents to your vector store:
```java
vectorStore.add(List.of(document));
```
And finally, retrieve documents similar to a query:
```java
List<Document> results = vectorStore.similaritySearch(
SearchRequest
.query("Spring")
.withTopK(5));
```
If all goes well, you should retrieve the document containing the text "Spring AI rocks!!".
### Metadata filtering
You can leverage the generic, portable [metadata filters](https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters) with WeaviateVectorStore as well.
For example you can use either the text expression language:
```java
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));
```
or programmatically using the expression DSL:
```java
FilterExpressionBuilder b = Filter.builder();
vectorStore.similaritySearch(
SearchRequest
.query("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()));
```
The, portable, filter expressions get automatically converted into the proprietary Weaviate [where filters](https://weaviate.io/developers/weaviate/api/graphql/filters).
For example the following, portable, filter expression
```sql
country in ['UK', 'NL'] && year >= 2020
```
is converted into Weaviate, GraphQL, [where filter expression](https://weaviate.io/developers/weaviate/api/graphql/filters):
```graphQL
operator:And
operands:
[{
operator:Or
operands:
[{
path:["meta_country"]
operator:Equal
valueText:"UK"
},
{
path:["meta_country"]
operator:Equal
valueText:"NL"
}]
},
{
path:["meta_year"]
operator:GreaterThanEqual
valueNumber:2020
}]
```
## <a name="appendix_a"/> Appendix A: Run Weaviate cluster in docker container </a>
Start Weaviate in a docker container:
```bash
docker run -it --rm --name weaviate -e AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true -e PERSISTENCE_DATA_PATH=/var/lib/weaviate -e QUERY_DEFAULTS_LIMIT=25 -e DEFAULT_VECTORIZER_MODULE=none -e CLUSTER_HOSTNAME=node1 -p 8080:8080 semitechnologies/weaviate:1.22.4
```
Starts a Weaviate cluster at http://localhost:8080/v1 with scheme=`http`, host=`localhost:8080` and apiKey=`""`. Then follow the [usage instructions](#usage).