Skip to content

Commit 04efe88

Browse files
committed
Improve ChromaVectorStore with builder pattern and package organization
- Introduce builder pattern for ChromaVectorStore configuration - Move Chroma-related classes to org.springframework.ai.chroma package - Add proper null-safety annotations and validation - Deprecate direct constructors in favor of builder - Improve JavaDoc documentation - Clean up code organization and imports
1 parent be0f9fb commit 04efe88

File tree

14 files changed

+214
-69
lines changed

14 files changed

+214
-69
lines changed

spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/AbstractObservationVectorStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public abstract class AbstractObservationVectorStore implements VectorStore {
4848
* @param customObservationConvention the custom observation convention to use
4949
*/
5050
public AbstractObservationVectorStore(ObservationRegistry observationRegistry,
51-
VectorStoreObservationConvention customObservationConvention) {
51+
@Nullable VectorStoreObservationConvention customObservationConvention) {
5252
this.observationRegistry = observationRegistry;
5353
this.customObservationConvention = customObservationConvention;
5454
}

spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ public Builder withDimensions(Integer dimensions) {
194194
return this;
195195
}
196196

197-
public Builder withFieldName(String fieldName) {
197+
public Builder withFieldName(@Nullable String fieldName) {
198198
this.context.setFieldName(fieldName);
199199
return this;
200200
}

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreAutoConfiguration.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import com.fasterxml.jackson.databind.ObjectMapper;
2020
import io.micrometer.observation.ObservationRegistry;
2121

22-
import org.springframework.ai.chroma.ChromaApi;
22+
import org.springframework.ai.chroma.api.ChromaApi;
2323
import org.springframework.ai.embedding.BatchingStrategy;
2424
import org.springframework.ai.embedding.EmbeddingModel;
2525
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
26-
import org.springframework.ai.vectorstore.ChromaVectorStore;
26+
import org.springframework.ai.chroma.vectorstore.ChromaVectorStore;
2727
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
2828
import org.springframework.beans.factory.ObjectProvider;
2929
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -86,9 +86,13 @@ public ChromaVectorStore vectorStore(EmbeddingModel embeddingModel, ChromaApi ch
8686
ChromaVectorStoreProperties storeProperties, ObjectProvider<ObservationRegistry> observationRegistry,
8787
ObjectProvider<VectorStoreObservationConvention> customObservationConvention,
8888
BatchingStrategy chromaBatchingStrategy) {
89-
return new ChromaVectorStore(embeddingModel, chromaApi, storeProperties.getCollectionName(),
90-
storeProperties.isInitializeSchema(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
91-
customObservationConvention.getIfAvailable(() -> null), chromaBatchingStrategy);
89+
return ChromaVectorStore.builder(embeddingModel, chromaApi)
90+
.collectionName(storeProperties.getCollectionName())
91+
.initializeSchema(storeProperties.isInitializeSchema())
92+
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
93+
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
94+
.batchingStrategy(chromaBatchingStrategy)
95+
.build();
9296
}
9397

9498
static class PropertiesChromaConnectionDetails implements ChromaConnectionDetails {

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/chroma/ChromaVectorStoreProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.ai.autoconfigure.vectorstore.chroma;
1818

1919
import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
20-
import org.springframework.ai.vectorstore.ChromaVectorStore;
20+
import org.springframework.ai.chroma.vectorstore.ChromaVectorStore;
2121
import org.springframework.boot.context.properties.ConfigurationProperties;
2222

2323
/**
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.chroma;
17+
package org.springframework.ai.chroma.api;
1818

1919
import java.util.ArrayList;
2020
import java.util.HashMap;
@@ -28,7 +28,7 @@
2828
import com.fasterxml.jackson.core.JsonProcessingException;
2929
import com.fasterxml.jackson.databind.ObjectMapper;
3030

31-
import org.springframework.ai.chroma.ChromaApi.QueryRequest.Include;
31+
import org.springframework.ai.chroma.api.ChromaApi.QueryRequest.Include;
3232
import org.springframework.http.HttpHeaders;
3333
import org.springframework.http.MediaType;
3434
import org.springframework.http.client.SimpleClientHttpRequestFactory;
@@ -49,10 +49,10 @@
4949
public class ChromaApi {
5050

5151
// Regular expression pattern that looks for a message inside the ValueError(...).
52-
private static Pattern VALUE_ERROR_PATTERN = Pattern.compile("ValueError\\('([^']*)'\\)");
52+
private static final Pattern VALUE_ERROR_PATTERN = Pattern.compile("ValueError\\('([^']*)'\\)");
5353

5454
// Regular expression pattern that looks for a message.
55-
private static Pattern MESSAGE_ERROR_PATTERN = Pattern.compile("\"message\":\"(.*?)\"");
55+
private static final Pattern MESSAGE_ERROR_PATTERN = Pattern.compile("\"message\":\"(.*?)\"");
5656

5757
private final ObjectMapper objectMapper;
5858

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.chroma.vectorstore;
1818

1919
import org.springframework.ai.vectorstore.filter.Filter;
2020
import org.springframework.ai.vectorstore.filter.converter.PineconeFilterExpressionConverter;
Lines changed: 122 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.chroma.vectorstore;
1818

1919
import java.util.ArrayList;
2020
import java.util.HashMap;
@@ -27,23 +27,26 @@
2727
import com.fasterxml.jackson.databind.json.JsonMapper;
2828
import io.micrometer.observation.ObservationRegistry;
2929

30-
import org.springframework.ai.chroma.ChromaApi;
31-
import org.springframework.ai.chroma.ChromaApi.AddEmbeddingsRequest;
32-
import org.springframework.ai.chroma.ChromaApi.DeleteEmbeddingsRequest;
33-
import org.springframework.ai.chroma.ChromaApi.Embedding;
30+
import org.springframework.ai.chroma.api.ChromaApi;
31+
import org.springframework.ai.chroma.api.ChromaApi.AddEmbeddingsRequest;
32+
import org.springframework.ai.chroma.api.ChromaApi.DeleteEmbeddingsRequest;
33+
import org.springframework.ai.chroma.api.ChromaApi.Embedding;
3434
import org.springframework.ai.document.Document;
3535
import org.springframework.ai.embedding.BatchingStrategy;
3636
import org.springframework.ai.embedding.EmbeddingModel;
3737
import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
3838
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
3939
import org.springframework.ai.observation.conventions.VectorStoreProvider;
4040
import org.springframework.ai.util.JacksonUtils;
41+
import org.springframework.ai.vectorstore.SearchRequest;
42+
import org.springframework.ai.vectorstore.VectorStore;
4143
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
4244
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
4345
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
4446
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
4547
import org.springframework.beans.factory.InitializingBean;
4648
import org.springframework.lang.NonNull;
49+
import org.springframework.lang.Nullable;
4750
import org.springframework.util.Assert;
4851
import org.springframework.util.CollectionUtils;
4952

@@ -73,6 +76,7 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
7376

7477
private FilterExpressionConverter filterExpressionConverter;
7578

79+
@Nullable
7680
private String collectionId;
7781

7882
private final boolean initializeSchema;
@@ -83,19 +87,31 @@ public class ChromaVectorStore extends AbstractObservationVectorStore implements
8387

8488
private boolean initialized = false;
8589

90+
/**
91+
* @deprecated in favor of the builder method
92+
*/
93+
@Deprecated(forRemoval = true)
8694
public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, boolean initializeSchema) {
8795
this(embeddingModel, chromaApi, DEFAULT_COLLECTION_NAME, initializeSchema);
8896
}
8997

98+
/**
99+
* @deprecated in favor of the builder method
100+
*/
101+
@Deprecated(forRemoval = true)
90102
public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, String collectionName,
91103
boolean initializeSchema) {
92104
this(embeddingModel, chromaApi, collectionName, initializeSchema, ObservationRegistry.NOOP, null,
93105
new TokenCountBatchingStrategy());
94106
}
95107

108+
/**
109+
* @deprecated in favor of the builder method
110+
*/
111+
@Deprecated(forRemoval = true)
96112
public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, String collectionName,
97113
boolean initializeSchema, ObservationRegistry observationRegistry,
98-
VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
114+
@Nullable VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
99115

100116
super(observationRegistry, customObservationConvention);
101117

@@ -108,6 +124,9 @@ public ChromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi, Str
108124
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
109125
}
110126

127+
/**
128+
* @param builder {@link Builder} for chroma vector store
129+
*/
111130
private ChromaVectorStore(Builder builder) {
112131
super(builder.observationRegistry, builder.customObservationConvention);
113132
this.embeddingModel = builder.embeddingModel;
@@ -128,9 +147,27 @@ private ChromaVectorStore(Builder builder) {
128147
}
129148
}
130149

131-
public void setFilterExpressionConverter(FilterExpressionConverter filterExpressionConverter) {
132-
Assert.notNull(filterExpressionConverter, "FilterExpressionConverter should not be null.");
133-
this.filterExpressionConverter = filterExpressionConverter;
150+
@Override
151+
public void afterPropertiesSet() throws Exception {
152+
if (!this.initialized) {
153+
var collection = this.chromaApi.getCollection(this.collectionName);
154+
if (collection == null) {
155+
if (this.initializeSchema) {
156+
collection = this.chromaApi
157+
.createCollection(new ChromaApi.CreateCollectionRequest(this.collectionName));
158+
}
159+
else {
160+
throw new RuntimeException("Collection " + this.collectionName
161+
+ " doesn't exist and won't be created as the initializeSchema is set to false.");
162+
}
163+
}
164+
this.collectionId = collection.id();
165+
this.initialized = true;
166+
}
167+
}
168+
169+
public static Builder builder(EmbeddingModel embeddingModel, ChromaApi chromaApi) {
170+
return new Builder(embeddingModel, chromaApi);
134171
}
135172

136173
@Override
@@ -212,36 +249,34 @@ private Map<String, Object> jsonToMap(String jsonText) {
212249
}
213250
}
214251

252+
/**
253+
* @deprecated not used currently anywhere
254+
*/
255+
@Deprecated(forRemoval = true)
215256
public String getCollectionName() {
216257
return this.collectionName;
217258
}
218259

260+
/**
261+
* @deprecated only used in tests
262+
*/
263+
@Deprecated(forRemoval = true)
264+
@Nullable
219265
public String getCollectionId() {
220266
return this.collectionId;
221267
}
222268

223-
@Override
224-
public void afterPropertiesSet() throws Exception {
225-
if (!this.initialized) {
226-
var collection = this.chromaApi.getCollection(this.collectionName);
227-
if (collection == null) {
228-
if (this.initializeSchema) {
229-
collection = this.chromaApi
230-
.createCollection(new ChromaApi.CreateCollectionRequest(this.collectionName));
231-
}
232-
else {
233-
throw new RuntimeException("Collection " + this.collectionName
234-
+ " doesn't exist and won't be created as the initializeSchema is set to false.");
235-
}
236-
}
237-
this.collectionId = collection.id();
238-
this.initialized = true;
239-
}
269+
/**
270+
* @deprecated in favor the builder method
271+
*/
272+
@Deprecated(forRemoval = true)
273+
public void setFilterExpressionConverter(FilterExpressionConverter filterExpressionConverter) {
274+
Assert.notNull(filterExpressionConverter, "FilterExpressionConverter should not be null.");
275+
this.filterExpressionConverter = filterExpressionConverter;
240276
}
241277

242278
@Override
243-
public @NonNull VectorStoreObservationContext.Builder createObservationContextBuilder(
244-
@NonNull String operationName) {
279+
public VectorStoreObservationContext.Builder createObservationContextBuilder(String operationName) {
245280
return VectorStoreObservationContext.builder(VectorStoreProvider.CHROMA.value(), operationName)
246281
.withDimensions(this.embeddingModel.dimensions())
247282
.withCollectionName(this.collectionName + ":" + this.collectionId)
@@ -260,6 +295,7 @@ public static class Builder {
260295

261296
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
262297

298+
@Nullable
263299
private VectorStoreObservationConvention customObservationConvention = null;
264300

265301
private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy();
@@ -268,46 +304,103 @@ public static class Builder {
268304

269305
private boolean initializeImmediately = false;
270306

307+
/**
308+
* Constructor with required parameters.
309+
* @param embeddingModel The {@link EmbeddingModel} to use
310+
* @param chromaApi The {@link ChromaApi} instance
311+
* @throws IllegalArgumentException if any required parameter is null
312+
*/
271313
public Builder(EmbeddingModel embeddingModel, ChromaApi chromaApi) {
314+
Assert.notNull(embeddingModel, "embeddingModel must not be null");
315+
Assert.notNull(chromaApi, "chromaApi must not be null");
272316
this.embeddingModel = embeddingModel;
273317
this.chromaApi = chromaApi;
274318
}
275319

320+
/**
321+
* Sets the collection name.
322+
* @param collectionName the name of the collection
323+
* @return the builder instance
324+
* @throws IllegalArgumentException if collectionName is null or empty
325+
*/
276326
public Builder collectionName(String collectionName) {
327+
Assert.hasText(collectionName, "collectionName must not be null or empty");
277328
this.collectionName = collectionName;
278329
return this;
279330
}
280331

332+
/**
333+
* Sets whether to initialize the schema.
334+
* @param initializeSchema true to initialize schema, false otherwise
335+
* @return the builder instance
336+
*/
281337
public Builder initializeSchema(boolean initializeSchema) {
282338
this.initializeSchema = initializeSchema;
283339
return this;
284340
}
285341

342+
/**
343+
* Sets the {@link ObservationRegistry}
344+
* @param observationRegistry the observation registry to use
345+
* @return the builder instance
346+
* @throws IllegalArgumentException if observationRegistry is null
347+
*/
286348
public Builder observationRegistry(ObservationRegistry observationRegistry) {
349+
Assert.notNull(observationRegistry, "observationRegistry must not be null");
287350
this.observationRegistry = observationRegistry;
288351
return this;
289352
}
290353

291-
public Builder customObservationConvention(VectorStoreObservationConvention convention) {
354+
/**
355+
* Sets the custom observation convention
356+
* {@link VectorStoreObservationConvention}.
357+
* @param convention the custom observation convention to use
358+
* @return the builder instance
359+
*/
360+
public Builder customObservationConvention(@Nullable VectorStoreObservationConvention convention) {
292361
this.customObservationConvention = convention;
293362
return this;
294363
}
295364

365+
/**
366+
* Sets the batching strategy.
367+
* @param batchingStrategy the batching strategy to use
368+
* @return the builder instance
369+
* @throws IllegalArgumentException if batchingStrategy is null
370+
*/
296371
public Builder batchingStrategy(BatchingStrategy batchingStrategy) {
372+
Assert.notNull(batchingStrategy, "batchingStrategy must not be null");
297373
this.batchingStrategy = batchingStrategy;
298374
return this;
299375
}
300376

377+
/**
378+
* Sets the filter expression converter.
379+
* @param converter the filter expression converter to use
380+
* @return the builder instance
381+
* @throws IllegalArgumentException if converter is null
382+
*/
301383
public Builder filterExpressionConverter(FilterExpressionConverter converter) {
384+
Assert.notNull(converter, "filterExpressionConverter must not be null");
302385
this.filterExpressionConverter = converter;
303386
return this;
304387
}
305388

389+
/**
390+
* Sets whether to initialize immediately.
391+
* @param initialize true to initialize immediately, false otherwise
392+
* @return the builder instance
393+
*/
306394
public Builder initializeImmediately(boolean initialize) {
307395
this.initializeImmediately = initialize;
308396
return this;
309397
}
310398

399+
/**
400+
* Builds the {@link ChromaVectorStore} instance.
401+
* @return a new ChromaVectorStore instance
402+
* @throws IllegalStateException if the builder is in an invalid state
403+
*/
311404
public ChromaVectorStore build() {
312405
return new ChromaVectorStore(this);
313406
}

0 commit comments

Comments
 (0)