Add Chroma VectorStore support

- Implement ChromaApi client, based on Chroma REST API.
 - Implement ChromaVectorStore, including support for filter expression conversion.
 - Common VectorStoreUtil class to share to/from Float/Double list/array convertion as well as Json/Map convertions.
 - Add ITs including for Basic Auth and Token autheticatios.
 - Add ChromaApi security support for BasicAuth and Token.
 - Fix an issue with Text filter expression parser, related to double-quoted identifiers.
 - Add Chroma README.md.
 - Add Chroma boot autoconfiguration

Resolves# #86
This commit is contained in:
Christian Tzolov
2023-11-08 18:16:14 +01:00
committed by Mark Pollack
parent 837c4080aa
commit 98ca3e2a8f
36 changed files with 2735 additions and 67 deletions

View File

@@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.vectorstore.filter.converter.FilterExpressionConverter;
import org.springframework.ai.vectorstore.filter.converter.PgVectorFilterExpressionConverter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -56,7 +57,7 @@ public class PgVectorStore implements VectorStore, InitializingBean {
public static final String VECTOR_TABLE_NAME = "vector_store";
public final PgVectorFilterExpressionConverter filterExpressionConverter = new PgVectorFilterExpressionConverter();
public final FilterExpressionConverter filterExpressionConverter = new PgVectorFilterExpressionConverter();
private final JdbcTemplate jdbcTemplate;
@@ -273,7 +274,7 @@ public class PgVectorStore implements VectorStore, InitializingBean {
public List<Document> similaritySearch(SearchRequest request) {
String nativeFilterExpression = (request.getFilterExpression() != null)
? this.filterExpressionConverter.convert(request.getFilterExpression()) : "";
? this.filterExpressionConverter.convertExpression(request.getFilterExpression()) : "";
String jsonPathFilter = "";

View File

@@ -34,7 +34,6 @@ import org.junit.Assert;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
@@ -42,7 +41,6 @@ import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import org.springframework.ai.ResourceUtils;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.openai.embedding.OpenAiEmbeddingClient;
@@ -58,6 +56,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.CollectionUtils;
@@ -145,7 +144,7 @@ public class PgVectorStoreIT {
VectorStore vectorStore = context.getBean(VectorStore.class);
var bgDocument = new Document("The World is Big and Salvation Lurks Around the Corner",
Map.of("country", "BG", "year", 2020));
Map.of("country", "BG", "year", 2020, "foo bar 1", "bar.foo"));
var nlDocument = new Document("The World is Big and Salvation Lurks Around the Corner",
Map.of("country", "NL"));
var bgDocument2 = new Document("The World is Big and Salvation Lurks Around the Corner",
@@ -183,6 +182,13 @@ public class PgVectorStoreIT {
assertThat(results.get(0).getId()).isIn(bgDocument.getId(), nlDocument.getId());
assertThat(results.get(1).getId()).isIn(bgDocument.getId(), nlDocument.getId());
results = vectorStore.similaritySearch(SearchRequest.query("The World")
.withTopK(5)
.withSimilarityThresholdAll()
.withFilterExpression("\"foo bar 1\" == 'bar.foo'"));
assertThat(results).hasSize(1);
assertThat(results.get(0).getId()).isEqualTo(bgDocument.getId());
try {
vectorStore.similaritySearch(searchRequest.withFilterExpression("country == NL"));
Assert.fail("Invalid filter expression should have been cached!");