Gemfire vector store cleanup

- Based on the pattern established in other vector store support implementations,
  added a builder class as an inner class of the GemfireVectorStoreConfig class which
  is also moved as an inner class to GemfireVectorStore.

Based on the original PR: https://github.com/spring-projects/spring-ai/pull/1168
This commit is contained in:
Soby Chacko
2024-08-22 11:32:54 -04:00
parent a3c0927c9b
commit d752b3d8d7
7 changed files with 190 additions and 149 deletions

View File

@@ -18,7 +18,6 @@ package org.springframework.ai.autoconfigure.vectorstore.gemfire;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.GemFireVectorStore;
import org.springframework.ai.vectorstore.GemFireVectorStoreConfig;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -33,6 +32,7 @@ import io.micrometer.observation.ObservationRegistry;
/**
* @author Geet Rawat
* @author Christian Tzolov
* @author Soby Chacko
*/
@AutoConfiguration
@ConditionalOnClass({ GemFireVectorStore.class, EmbeddingModel.class })
@@ -52,9 +52,9 @@ public class GemFireVectorStoreAutoConfiguration {
public GemFireVectorStore gemfireVectorStore(EmbeddingModel embeddingModel, GemFireVectorStoreProperties properties,
GemFireConnectionDetails gemFireConnectionDetails, ObjectProvider<ObservationRegistry> observationRegistry,
ObjectProvider<VectorStoreObservationConvention> customObservationConvention) {
var config = new GemFireVectorStoreConfig();
var builder = new GemFireVectorStore.GemFireVectorStoreConfig.Builder();
config.setHost(gemFireConnectionDetails.getHost())
builder.setHost(gemFireConnectionDetails.getHost())
.setPort(gemFireConnectionDetails.getPort())
.setIndexName(properties.getIndexName())
.setBeamWidth(properties.getBeamWidth())
@@ -63,7 +63,7 @@ public class GemFireVectorStoreAutoConfiguration {
.setVectorSimilarityFunction(properties.getVectorSimilarityFunction())
.setFields(properties.getFields())
.setSslEnabled(properties.isSslEnabled());
return new GemFireVectorStore(config, embeddingModel, properties.isInitializeSchema(),
return new GemFireVectorStore(builder.build(), embeddingModel, properties.isInitializeSchema(),
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
customObservationConvention.getIfAvailable(() -> null));
}

View File

@@ -17,11 +17,12 @@
package org.springframework.ai.autoconfigure.vectorstore.gemfire;
import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
import org.springframework.ai.vectorstore.GemFireVectorStoreConfig;
import org.springframework.ai.vectorstore.GemFireVectorStore;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Geet Rawat
* @author Soby Chacko
*/
@ConfigurationProperties(GemFireVectorStoreProperties.CONFIG_PREFIX)
public class GemFireVectorStoreProperties extends CommonVectorStoreProperties {
@@ -36,31 +37,31 @@ public class GemFireVectorStoreProperties extends CommonVectorStoreProperties {
* "spring.ai.vectorstore.gemfire.host";
*
*/
private String host = GemFireVectorStoreConfig.DEFAULT_HOST;
private String host = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_HOST;
/**
* The port of the GemFire to connect to. To specify a custom port, use
* "spring.ai.vectorstore.gemfire.port";
*/
private int port = GemFireVectorStoreConfig.DEFAULT_PORT;
private int port = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_PORT;
/**
* The name of the index in the GemFire. To specify a custom index, use
* "spring.ai.vectorstore.gemfire.index-name";
*/
private String indexName = GemFireVectorStoreConfig.DEFAULT_INDEX_NAME;
private String indexName = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_INDEX_NAME;
/**
* The beam width for similarity queries. Default value is {@code 100}. To specify a
* custom beam width, use "spring.ai.vectorstore.gemfire.beam-width";
*/
private int beamWidth = GemFireVectorStoreConfig.DEFAULT_BEAM_WIDTH;
private int beamWidth = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_BEAM_WIDTH;
/**
* The maximum number of connections allowed. Default value is {@code 16}. To specify
* custom number of connections, use "spring.ai.vectorstore.gemfire.max-connections";
*/
private int maxConnections = GemFireVectorStoreConfig.DEFAULT_MAX_CONNECTIONS;
private int maxConnections = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_MAX_CONNECTIONS;
/**
* The similarity function to be used for vector comparisons. Default value is
@@ -68,14 +69,14 @@ public class GemFireVectorStoreProperties extends CommonVectorStoreProperties {
* "spring.ai.vectorstore.gemfire.vector-similarity-function";
*
*/
private String vectorSimilarityFunction = GemFireVectorStoreConfig.DEFAULT_SIMILARITY_FUNCTION;
private String vectorSimilarityFunction = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_SIMILARITY_FUNCTION;
/**
* The fields to be used for queries. Default value is an array containing
* {@code "vector"}. To specify custom fields, use
* "spring.ai.vectorstore.gemfire.fields"
*/
private String[] fields = GemFireVectorStoreConfig.DEFAULT_FIELDS;
private String[] fields = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_FIELDS;
/**
* The number of buckets to use for partitioning the data. Default value is {@code 0}.
@@ -83,14 +84,14 @@ public class GemFireVectorStoreProperties extends CommonVectorStoreProperties {
* To specify custom buckets, use "spring.ai.vectorstore.gemfire.buckets";
*
*/
private int buckets = GemFireVectorStoreConfig.DEFAULT_BUCKETS;
private int buckets = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_BUCKETS;
/**
* Set to true if GemFire cluster is ssl enabled
*
* To specify sslEnabled, use "spring.ai.vectorstore.gemfire.ssl-enabled";
*/
private boolean sslEnabled = GemFireVectorStoreConfig.DEFAULT_SSL_ENABLED;
private boolean sslEnabled = GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_SSL_ENABLED;
public int getBeamWidth() {
return beamWidth;

View File

@@ -19,23 +19,26 @@ package org.springframework.ai.autoconfigure.vectorstore.gemfire;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.ai.vectorstore.GemFireVectorStoreConfig;
import org.springframework.ai.vectorstore.GemFireVectorStore;
/**
* @author Geet Rawat
* @author Soby Chacko
*/
class GemFireVectorStorePropertiesTests {
@Test
void defaultValues() {
var props = new GemFireVectorStoreProperties();
assertThat(props.getIndexName()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_INDEX_NAME);
assertThat(props.getHost()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_HOST);
assertThat(props.getPort()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_PORT);
assertThat(props.getBeamWidth()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_BEAM_WIDTH);
assertThat(props.getMaxConnections()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_MAX_CONNECTIONS);
assertThat(props.getFields()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_FIELDS);
assertThat(props.getBuckets()).isEqualTo(GemFireVectorStoreConfig.DEFAULT_BUCKETS);
assertThat(props.getIndexName()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_INDEX_NAME);
assertThat(props.getHost()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_HOST);
assertThat(props.getPort()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_PORT);
assertThat(props.getBeamWidth()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_BEAM_WIDTH);
assertThat(props.getMaxConnections())
.isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_MAX_CONNECTIONS);
assertThat(props.getFields()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_FIELDS);
assertThat(props.getBuckets()).isEqualTo(GemFireVectorStore.GemFireVectorStoreConfig.DEFAULT_BUCKETS);
}
@Test

View File

@@ -30,7 +30,6 @@ import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext.Builder;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
@@ -58,6 +57,7 @@ import reactor.util.annotation.NonNull;
* @author Geet Rawat
* @author Christian Tzolov
* @author Thomas Vitale
* @author Soby Chacko
*/
public class GemFireVectorStore extends AbstractObservationVectorStore implements InitializingBean {
@@ -537,11 +537,160 @@ public class GemFireVectorStore extends AbstractObservationVectorStore implement
}
@Override
public Builder createObservationContextBuilder(String operationName) {
public VectorStoreObservationContext.Builder createObservationContextBuilder(String operationName) {
return VectorStoreObservationContext.builder(VectorStoreProvider.GEMFIRE.value(), operationName)
.withCollectionName(this.indexName)
.withDimensions(this.embeddingModel.dimensions())
.withFieldName(EMBEDDINGS);
}
public static class GemFireVectorStoreConfig {
// Create Index DEFAULT Values
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 8080;
public static final String DEFAULT_INDEX_NAME = "spring-ai-gemfire-index";
public static final int UPPER_BOUND_BEAM_WIDTH = 3200;
public static final int DEFAULT_BEAM_WIDTH = 100;
private static final int UPPER_BOUND_MAX_CONNECTIONS = 512;
public static final int DEFAULT_MAX_CONNECTIONS = 16;
public static final String DEFAULT_SIMILARITY_FUNCTION = "COSINE";
public static final String[] DEFAULT_FIELDS = new String[] {};
public static final int DEFAULT_BUCKETS = 0;
public static final boolean DEFAULT_SSL_ENABLED = false;
String host;
int port;
String indexName;
int beamWidth;
int maxConnections;
String vectorSimilarityFunction;
String[] fields;
int buckets;
boolean sslEnabled;
private GemFireVectorStoreConfig(Builder builder) {
this.host = builder.host;
this.port = builder.port;
this.sslEnabled = builder.sslEnabled;
this.indexName = builder.indexName;
this.beamWidth = builder.beamWidth;
this.maxConnections = builder.maxConnections;
this.buckets = builder.buckets;
this.vectorSimilarityFunction = builder.vectorSimilarityFunction;
this.fields = builder.fields;
}
/**
* Start building a new configuration.
* @return The entry point for creating a new configuration.
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
// Create Index DEFAULT Values
String host = GemFireVectorStoreConfig.DEFAULT_HOST;
int port = GemFireVectorStoreConfig.DEFAULT_PORT;
String indexName = GemFireVectorStoreConfig.DEFAULT_INDEX_NAME;
int beamWidth = GemFireVectorStoreConfig.DEFAULT_BEAM_WIDTH;
int maxConnections = GemFireVectorStoreConfig.DEFAULT_MAX_CONNECTIONS;
String vectorSimilarityFunction = GemFireVectorStoreConfig.DEFAULT_SIMILARITY_FUNCTION;
String[] fields = GemFireVectorStoreConfig.DEFAULT_FIELDS;
int buckets = GemFireVectorStoreConfig.DEFAULT_BUCKETS;
boolean sslEnabled = GemFireVectorStoreConfig.DEFAULT_SSL_ENABLED;
public Builder setHost(String host) {
Assert.hasText(host, "host must have a value");
this.host = host;
return this;
}
public Builder setPort(int port) {
Assert.isTrue(port > 0, "port must be positive");
this.port = port;
return this;
}
public Builder setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
return this;
}
public Builder setIndexName(String indexName) {
Assert.hasText(indexName, "indexName must have a value");
this.indexName = indexName;
return this;
}
public Builder setBeamWidth(int beamWidth) {
Assert.isTrue(beamWidth > 0, "beamWidth must be positive");
Assert.isTrue(beamWidth <= GemFireVectorStoreConfig.UPPER_BOUND_BEAM_WIDTH,
"beamWidth must be less than or equal to " + GemFireVectorStoreConfig.UPPER_BOUND_BEAM_WIDTH);
this.beamWidth = beamWidth;
return this;
}
public Builder setMaxConnections(int maxConnections) {
Assert.isTrue(maxConnections > 0, "maxConnections must be positive");
Assert.isTrue(maxConnections <= GemFireVectorStoreConfig.UPPER_BOUND_MAX_CONNECTIONS,
"maxConnections must be less than or equal to "
+ GemFireVectorStoreConfig.UPPER_BOUND_MAX_CONNECTIONS);
this.maxConnections = maxConnections;
return this;
}
public Builder setBuckets(int buckets) {
Assert.isTrue(buckets >= 0, "bucket must be 1 or more");
this.buckets = buckets;
return this;
}
public Builder setVectorSimilarityFunction(String vectorSimilarityFunction) {
Assert.hasText(vectorSimilarityFunction, "vectorSimilarityFunction must have a value");
this.vectorSimilarityFunction = vectorSimilarityFunction;
return this;
}
public Builder setFields(String[] fields) {
this.fields = fields;
return this;
}
public GemFireVectorStoreConfig build() {
return new GemFireVectorStoreConfig(this);
}
}
}
}

View File

@@ -1,107 +0,0 @@
package org.springframework.ai.vectorstore;
import org.springframework.util.Assert;
public final class GemFireVectorStoreConfig {
// Create Index DEFAULT Values
public static final String DEFAULT_HOST = "localhost";
public static final int DEFAULT_PORT = 8080;
public static final String DEFAULT_INDEX_NAME = "spring-ai-gemfire-index";
public static final int UPPER_BOUND_BEAM_WIDTH = 3200;
public static final int DEFAULT_BEAM_WIDTH = 100;
private static final int UPPER_BOUND_MAX_CONNECTIONS = 512;
public static final int DEFAULT_MAX_CONNECTIONS = 16;
public static final String DEFAULT_SIMILARITY_FUNCTION = "COSINE";
public static final String[] DEFAULT_FIELDS = new String[] {};
public static final int DEFAULT_BUCKETS = 0;
public static final boolean DEFAULT_SSL_ENABLED = false;
String host = GemFireVectorStoreConfig.DEFAULT_HOST;
int port = DEFAULT_PORT;
String indexName = DEFAULT_INDEX_NAME;
int beamWidth = DEFAULT_BEAM_WIDTH;
int maxConnections = DEFAULT_MAX_CONNECTIONS;
String vectorSimilarityFunction = DEFAULT_SIMILARITY_FUNCTION;
String[] fields = DEFAULT_FIELDS;
int buckets = DEFAULT_BUCKETS;
boolean sslEnabled = DEFAULT_SSL_ENABLED;
public GemFireVectorStoreConfig() {
}
public GemFireVectorStoreConfig setHost(String host) {
Assert.hasText(host, "host must have a value");
this.host = host;
return this;
}
public GemFireVectorStoreConfig setPort(int port) {
Assert.isTrue(port > 0, "port must be positive");
this.port = port;
return this;
}
public GemFireVectorStoreConfig setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled;
return this;
}
public GemFireVectorStoreConfig setIndexName(String indexName) {
Assert.hasText(indexName, "indexName must have a value");
this.indexName = indexName;
return this;
}
public GemFireVectorStoreConfig setBeamWidth(int beamWidth) {
Assert.isTrue(beamWidth > 0, "beamWidth must be positive");
Assert.isTrue(beamWidth <= GemFireVectorStoreConfig.UPPER_BOUND_BEAM_WIDTH,
"beamWidth must be less than or equal to " + GemFireVectorStoreConfig.UPPER_BOUND_BEAM_WIDTH);
this.beamWidth = beamWidth;
return this;
}
public GemFireVectorStoreConfig setMaxConnections(int maxConnections) {
Assert.isTrue(maxConnections > 0, "maxConnections must be positive");
Assert.isTrue(maxConnections <= GemFireVectorStoreConfig.UPPER_BOUND_MAX_CONNECTIONS,
"maxConnections must be less than or equal to " + GemFireVectorStoreConfig.UPPER_BOUND_MAX_CONNECTIONS);
this.maxConnections = maxConnections;
return this;
}
public GemFireVectorStoreConfig setBuckets(int buckets) {
Assert.isTrue(buckets >= 0, "bucket must be 1 or more");
this.buckets = buckets;
return this;
}
public GemFireVectorStoreConfig setVectorSimilarityFunction(String vectorSimilarityFunction) {
Assert.hasText(vectorSimilarityFunction, "vectorSimilarityFunction must have a value");
this.vectorSimilarityFunction = vectorSimilarityFunction;
return this;
}
public GemFireVectorStoreConfig setFields(String[] fields) {
this.fields = fields;
return this;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023 - 2024 the original author or authors.
* Copyright 2023-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,6 +45,7 @@ import org.springframework.core.io.DefaultResourceLoader;
/**
* @author Geet Rawat
* @author Soby Chacko
* @since 1.0.0
*/
public class GemFireVectorStoreIT {
@@ -208,15 +209,12 @@ public class GemFireVectorStoreIT {
public static class TestApplication {
@Bean
public GemFireVectorStoreConfig gemfireVectorStoreConfig() {
return new GemFireVectorStoreConfig().setHost("localhost")
public GemFireVectorStore vectorStore(EmbeddingModel embeddingModel) {
return new GemFireVectorStore(GemFireVectorStore.GemFireVectorStoreConfig.builder()
.setHost("localhost")
.setPort(HTTP_SERVICE_PORT)
.setIndexName(INDEX_NAME);
}
@Bean
public GemFireVectorStore vectorStore(GemFireVectorStoreConfig config, EmbeddingModel embeddingModel) {
return new GemFireVectorStore(config, embeddingModel, true);
.setIndexName(INDEX_NAME)
.build(), embeddingModel, true);
}
@Bean

View File

@@ -55,6 +55,7 @@ import static org.hamcrest.Matchers.hasSize;
/**
* @author Christian Tzolov
* @author Thomas Vitale
* @author Soby Chacko
*/
public class GemFireVectorStoreObservationIT {
@@ -193,16 +194,12 @@ public class GemFireVectorStoreObservationIT {
}
@Bean
public GemFireVectorStoreConfig gemfireVectorStoreConfig() {
return new GemFireVectorStoreConfig().setHost("localhost")
public GemFireVectorStore vectorStore(EmbeddingModel embeddingModel, ObservationRegistry observationRegistry) {
return new GemFireVectorStore(GemFireVectorStore.GemFireVectorStoreConfig.builder()
.setHost("localhost")
.setPort(HTTP_SERVICE_PORT)
.setIndexName(TEST_INDEX_NAME);
}
@Bean
public GemFireVectorStore vectorStore(GemFireVectorStoreConfig config, EmbeddingModel embeddingModel,
ObservationRegistry observationRegistry) {
return new GemFireVectorStore(config, embeddingModel, true, observationRegistry, null);
.setIndexName(TEST_INDEX_NAME)
.build(), embeddingModel, true, observationRegistry, null);
}
@Bean