diff --git a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreObservationAttributes.java b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreObservationAttributes.java index 9bfada67c9c..bd869f0cbef 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreObservationAttributes.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreObservationAttributes.java @@ -56,6 +56,13 @@ public enum VectorStoreObservationAttributes { */ DB_SYSTEM("db.system"), + // DB Search + + /** + * The metric used in similarity search. + */ + DB_SEARCH_SIMILARITY_METRIC("db.search.similarity_metric"), + // DB Vector /** @@ -68,11 +75,6 @@ public enum VectorStoreObservationAttributes { */ DB_VECTOR_FIELD_NAME("db.vector.field_name"), - /** - * The model used for the embedding. - */ - DB_VECTOR_MODEL("db.vector.model"), - /** * The content of the search query being executed. */ @@ -98,12 +100,7 @@ public enum VectorStoreObservationAttributes { /** * The top-k most similar vectors returned by a query. */ - DB_VECTOR_QUERY_TOP_K("db.vector.query.top_k"), - - /** - * The metric used in similarity search. - */ - DB_VECTOR_SIMILARITY_METRIC("db.vector.similarity_metric"); + DB_VECTOR_QUERY_TOP_K("db.vector.query.top_k"); private final String value; diff --git a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java index 1ae621c6921..3d3d76c151e 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java @@ -16,32 +16,42 @@ package org.springframework.ai.observation.conventions; /** + * Collection of systems providing vector store functionality. Based on the OpenTelemetry + * Semantic Conventions for Vector Databases. + * * @author Christian Tzolov + * @author Thomas Vitale * @since 1.0.0 + * @see DB + * Semantic Conventions. */ public enum VectorStoreProvider { // @formatter:off - PG_VECTOR("pg_vector"), - AZURE("azure"), - CASSANDRA("cassandra"), - CHROMA("chroma"), - ELASTICSEARCH("elasticsearch"), - MILVUS("milvus"), - NEO4J("neo4j"), - OPENSEARCH("opensearch"), - QDRANT("qdrant"), - REDIS("redis"), - TYPESENSE("typesense"), - WEAVIATE("weaviate"), - PINECONE("pinecone"), - ORACLE("oracle"), - MONGODB("mongodb"), - GEMFIRE("gemfire"), - HANA("hana"), - SIMPLE("simple"); - - // @formatter:on + + // Please, keep the alphabetical sorting. + AZURE("azure"), + CASSANDRA("cassandra"), + CHROMA("chroma"), + ELASTICSEARCH("elasticsearch"), + GEMFIRE("gemfire"), + HANA("hana"), + MILVUS("milvus"), + MONGODB("mongodb"), + NEO4J("neo4j"), + OPENSEARCH("opensearch"), + ORACLE("oracle"), + PG_VECTOR("pg_vector"), + PINECONE("pinecone"), + QDRANT("qdrant"), + REDIS("redis"), + SIMPLE("simple"), + TYPESENSE("typesense"), + WEAVIATE("weaviate"); + + // @formatter:on + private final String value; VectorStoreProvider(String value) { diff --git a/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConvention.java b/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConvention.java index 246f1c2f42b..cfddd211c62 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConvention.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConvention.java @@ -35,33 +35,6 @@ public class DefaultVectorStoreObservationConvention implements VectorStoreObser public static final String DEFAULT_NAME = "db.vector.client.operation"; - private static final KeyValue COLLECTION_NAME_NONE = KeyValue.of(HighCardinalityKeyNames.DB_COLLECTION_NAME, - KeyValue.NONE_VALUE); - - private static final KeyValue DIMENSIONS_NONE = KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT, - KeyValue.NONE_VALUE); - - private static final KeyValue METADATA_FILTER_NONE = KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER, - KeyValue.NONE_VALUE); - - private static final KeyValue FIELD_NAME_NONE = KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME, - KeyValue.NONE_VALUE); - - private static final KeyValue NAMESPACE_NONE = KeyValue.of(HighCardinalityKeyNames.DB_NAMESPACE, - KeyValue.NONE_VALUE); - - private static final KeyValue QUERY_CONTENT_NONE = KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT, - KeyValue.NONE_VALUE); - - private static final KeyValue SIMILARITY_METRIC_NONE = KeyValue - .of(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC, KeyValue.NONE_VALUE); - - private static final KeyValue SIMILARITY_THRESHOLD_NONE = KeyValue - .of(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD, KeyValue.NONE_VALUE); - - private static final KeyValue TOP_K_NONE = KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K, - KeyValue.NONE_VALUE); - private final String name; public DefaultVectorStoreObservationConvention() { @@ -102,74 +75,86 @@ protected KeyValue dbOperationName(VectorStoreObservationContext context) { @Override public KeyValues getHighCardinalityKeyValues(VectorStoreObservationContext context) { - return KeyValues.of(collectionName(context), dimensions(context), fieldName(context), metadataFilter(context), - namespace(context), queryContent(context), similarityMetric(context), similarityThreshold(context), - topK(context)); - } - - protected KeyValue collectionName(VectorStoreObservationContext context) { + var keyValues = KeyValues.empty(); + keyValues = collectionName(keyValues, context); + keyValues = dimensions(keyValues, context); + keyValues = fieldName(keyValues, context); + keyValues = metadataFilter(keyValues, context); + keyValues = namespace(keyValues, context); + keyValues = queryContent(keyValues, context); + keyValues = similarityMetric(keyValues, context); + keyValues = similarityThreshold(keyValues, context); + keyValues = topK(keyValues, context); + return keyValues; + } + + protected KeyValues collectionName(KeyValues keyValues, VectorStoreObservationContext context) { if (StringUtils.hasText(context.getCollectionName())) { - return KeyValue.of(HighCardinalityKeyNames.DB_COLLECTION_NAME, context.getCollectionName()); + return keyValues.and(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), context.getCollectionName()); } - return COLLECTION_NAME_NONE; + return keyValues; } - protected KeyValue dimensions(VectorStoreObservationContext context) { + protected KeyValues dimensions(KeyValues keyValues, VectorStoreObservationContext context) { if (context.getDimensions() != null && context.getDimensions() > 0) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT, "" + context.getDimensions()); + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), + "" + context.getDimensions()); } - return DIMENSIONS_NONE; + return keyValues; } - protected KeyValue fieldName(VectorStoreObservationContext context) { + protected KeyValues fieldName(KeyValues keyValues, VectorStoreObservationContext context) { if (StringUtils.hasText(context.getFieldName())) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME, context.getFieldName()); + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), context.getFieldName()); } - return FIELD_NAME_NONE; + return keyValues; } - protected KeyValue metadataFilter(VectorStoreObservationContext context) { + protected KeyValues metadataFilter(KeyValues keyValues, VectorStoreObservationContext context) { if (context.getQueryRequest() != null && context.getQueryRequest().getFilterExpression() != null) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER, + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER.asString(), context.getQueryRequest().getFilterExpression().toString()); } - return METADATA_FILTER_NONE; + return keyValues; } - protected KeyValue namespace(VectorStoreObservationContext context) { + protected KeyValues namespace(KeyValues keyValues, VectorStoreObservationContext context) { if (StringUtils.hasText(context.getNamespace())) { - return KeyValue.of(HighCardinalityKeyNames.DB_NAMESPACE, context.getNamespace()); + return keyValues.and(HighCardinalityKeyNames.DB_NAMESPACE.asString(), context.getNamespace()); } - return NAMESPACE_NONE; + return keyValues; } - protected KeyValue queryContent(VectorStoreObservationContext context) { + protected KeyValues queryContent(KeyValues keyValues, VectorStoreObservationContext context) { if (context.getQueryRequest() != null && StringUtils.hasText(context.getQueryRequest().getQuery())) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT, context.getQueryRequest().getQuery()); + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), + context.getQueryRequest().getQuery()); } - return QUERY_CONTENT_NONE; + return keyValues; } - protected KeyValue similarityMetric(VectorStoreObservationContext context) { + protected KeyValues similarityMetric(KeyValues keyValues, VectorStoreObservationContext context) { if (StringUtils.hasText(context.getSimilarityMetric())) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC, context.getSimilarityMetric()); + return keyValues.and(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + context.getSimilarityMetric()); } - return SIMILARITY_METRIC_NONE; + return keyValues; } - protected KeyValue similarityThreshold(VectorStoreObservationContext context) { + protected KeyValues similarityThreshold(KeyValues keyValues, VectorStoreObservationContext context) { if (context.getQueryRequest() != null && context.getQueryRequest().getSimilarityThreshold() >= 0) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD, + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), String.valueOf(context.getQueryRequest().getSimilarityThreshold())); } - return SIMILARITY_THRESHOLD_NONE; + return keyValues; } - protected KeyValue topK(VectorStoreObservationContext context) { + protected KeyValues topK(KeyValues keyValues, VectorStoreObservationContext context) { if (context.getQueryRequest() != null && context.getQueryRequest().getTopK() > 0) { - return KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K, "" + context.getQueryRequest().getTopK()); + return keyValues.and(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), + "" + context.getQueryRequest().getTopK()); } - return TOP_K_NONE; + return keyValues; } } diff --git a/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationDocumentation.java b/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationDocumentation.java index 20996ec2bec..f56ead4def5 100644 --- a/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationDocumentation.java +++ b/spring-ai-core/src/main/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationDocumentation.java @@ -116,6 +116,18 @@ public String asString() { } }, + // DB Search + + /** + * The metric used in similarity search. + */ + DB_SEARCH_SIMILARITY_METRIC { + @Override + public String asString() { + return VectorStoreObservationAttributes.DB_SEARCH_SIMILARITY_METRIC.value(); + } + }, + // DB Vector /** @@ -188,16 +200,6 @@ public String asString() { public String asString() { return VectorStoreObservationAttributes.DB_VECTOR_QUERY_TOP_K.value(); } - }, - - /** - * The metric used in similarity search. - */ - DB_VECTOR_SIMILARITY_METRIC { - @Override - public String asString() { - return VectorStoreObservationAttributes.DB_VECTOR_SIMILARITY_METRIC.value(); - } }; } diff --git a/spring-ai-core/src/test/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConventionTests.java b/spring-ai-core/src/test/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConventionTests.java index 00e33e51791..5882f92858b 100644 --- a/spring-ai-core/src/test/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConventionTests.java +++ b/spring-ai-core/src/test/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConventionTests.java @@ -68,13 +68,13 @@ void shouldHaveRequiredKeyValues() { .builder("my_database", VectorStoreObservationContext.Operation.QUERY) .build(); assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext)).contains( + KeyValue.of(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()), KeyValue.of(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query"), KeyValue.of(LowCardinalityKeyNames.DB_SYSTEM.asString(), "my_database")); } @Test void shouldHaveOptionalKeyValues() { - VectorStoreObservationContext observationContext = VectorStoreObservationContext .builder("my-database", VectorStoreObservationContext.Operation.QUERY) .withCollectionName("COLLECTION_NAME") @@ -102,26 +102,28 @@ void shouldHaveOptionalKeyValues() { KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "696"), KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "FIELD_NAME"), KeyValue.of(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "NAMESPACE"), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "SIMILARITY_METRIC"), + KeyValue.of(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), "SIMILARITY_METRIC"), KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "VDB QUERY"), KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER.asString(), "Expression[type=AND, left=Expression[type=EQ, left=Key[key=country], right=Value[value=UK]], right=Expression[type=GTE, left=Key[key=year], right=Value[value=2020]]]")); } @Test - void shouldHaveMissingKeyValues() { + void shouldNotHaveKeyValuesWhenMissing() { VectorStoreObservationContext observationContext = VectorStoreObservationContext .builder("my-database", VectorStoreObservationContext.Operation.QUERY) .build(); - assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)).contains( - KeyValue.of(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_NAMESPACE.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), KeyValue.NONE_VALUE), - KeyValue.of(HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER.asString(), KeyValue.NONE_VALUE)); + assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext) + .stream() + .map(KeyValue::getKey) + .toList()).doesNotContain(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), + HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), + HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), + HighCardinalityKeyNames.DB_NAMESPACE.asString(), + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), + HighCardinalityKeyNames.DB_VECTOR_QUERY_FILTER.asString()); } } diff --git a/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreObservationIT.java b/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreObservationIT.java index 72f6c81294f..10c876db0d2 100644 --- a/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreObservationIT.java @@ -32,6 +32,8 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; +import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; @@ -104,21 +106,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("azure add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.AZURE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "azure") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.AZURE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), AzureVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -134,9 +138,10 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("azure query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.AZURE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "azure") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.AZURE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) @@ -145,9 +150,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), AzureVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreObservationIT.java b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreObservationIT.java index 1e3ac241e33..43f6500229d 100644 --- a/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-cassandra-store/src/test/java/org/springframework/ai/vectorstore/CassandraVectorStoreObservationIT.java @@ -27,6 +27,8 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; +import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.CassandraVectorStoreConfig.SchemaColumn; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -95,21 +97,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("cassandra add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.CASSANDRA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "cassandra") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.CASSANDRA.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), CassandraVectorStoreConfig.DEFAULT_TABLE_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "test_springframework") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -125,9 +129,10 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("cassandra query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.CASSANDRA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "cassandra") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.CASSANDRA.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) @@ -137,8 +142,9 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), CassandraVectorStoreConfig.DEFAULT_TABLE_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "test_springframework") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/ChromaImage.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/ChromaImage.java new file mode 100644 index 00000000000..9cbbaa4a49a --- /dev/null +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/ChromaImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class ChromaImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("ghcr.io/chroma-core/chroma:0.5.11"); + +} diff --git a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java index 120485514cf..89e9eab2e00 100644 --- a/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-chroma-store/src/test/java/org/springframework/ai/vectorstore/ChromaVectorStoreObservationIT.java @@ -23,6 +23,7 @@ import java.util.Map; import org.junit.jupiter.api.Test; +import org.springframework.ai.ChromaImage; import org.springframework.ai.chroma.ChromaApi; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; @@ -57,7 +58,7 @@ public class ChromaVectorStoreObservationIT { @Container - static ChromaDBContainer chromaContainer = new ChromaDBContainer("ghcr.io/chroma-core/chroma:0.5.0"); + static ChromaDBContainer chromaContainer = new ChromaDBContainer(ChromaImage.DEFAULT_IMAGE); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class); @@ -92,22 +93,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("chroma add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.CHROMA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.CHROMA.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "TestCollection:" + vectorStore.getCollectionId()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "distance") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -123,7 +125,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("chroma query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.CHROMA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.CHROMA.value()) @@ -135,9 +137,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "TestCollection:" + vectorStore.getCollectionId()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "distance") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchImage.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchImage.java new file mode 100644 index 00000000000..2697c19506c --- /dev/null +++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchImage.java @@ -0,0 +1,28 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class ElasticsearchImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName + .parse("docker.elastic.co/elasticsearch/elasticsearch:8.15.2"); + +} diff --git a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java index e6bb89b9960..4a0c9cc58e3 100644 --- a/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-elasticsearch-store/src/test/java/org/springframework/ai/vectorstore/ElasticsearchVectorStoreObservationIT.java @@ -36,6 +36,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -73,7 +74,7 @@ public class ElasticsearchVectorStoreObservationIT { @Container private static final ElasticsearchContainer elasticsearchContainer = new ElasticsearchContainer( - "docker.elastic.co/elasticsearch/elasticsearch:8.13.3") + ElasticsearchImage.DEFAULT_IMAGE) .withEnv("xpack.security.enabled", "false"); List documents = List.of( @@ -129,22 +130,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("elasticsearch add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.ELASTICSEARCH.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.ELASTICSEARCH.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "spring-ai-document-index") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -165,7 +167,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("elasticsearch query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.ELASTICSEARCH.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.ELASTICSEARCH.value()) @@ -177,9 +179,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "spring-ai-document-index") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireImage.java b/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireImage.java new file mode 100644 index 00000000000..806497e25a4 --- /dev/null +++ b/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class GemFireImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("gemfire/gemfire-all:10.1-jdk17"); + +} diff --git a/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreObservationIT.java b/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreObservationIT.java index 6fb44d38561..5cdd5059194 100644 --- a/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-gemfire-store/src/test/java/org/springframework/ai/vectorstore/GemFireVectorStoreObservationIT.java @@ -80,7 +80,7 @@ public static void startGemFireCluster() { Ports.Binding hostPort = Ports.Binding.bindPort(HTTP_SERVICE_PORT); ExposedPort exposedPort = new ExposedPort(HTTP_SERVICE_PORT); PortBinding mappedPort = new PortBinding(hostPort, exposedPort); - gemFireCluster = new GemFireCluster("gemfire/gemfire-all:10.1-jdk17", LOCATOR_COUNT, SERVER_COUNT); + gemFireCluster = new GemFireCluster(GemFireImage.DEFAULT_IMAGE, LOCATOR_COUNT, SERVER_COUNT); gemFireCluster.withConfiguration(GemFireCluster.SERVER_GLOB, container -> container.withExposedPorts(HTTP_SERVICE_PORT) .withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withPortBindings(mappedPort))); @@ -125,21 +125,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("gemfire add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.GEMFIRE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.GEMFIRE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "/embeddings") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -161,7 +162,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("gemfire query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.GEMFIRE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.GEMFIRE.value()) @@ -172,9 +173,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "/embeddings") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java index 6f654b591a1..78c324a4d3e 100644 --- a/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-hanadb-store/src/test/java/org/springframework/ai/vectorstore/HanaVectorStoreObservationIT.java @@ -30,6 +30,7 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -94,21 +95,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("hana add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.HANA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.HANA.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_TABLE_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -124,7 +126,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("hana query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.HANA.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.HANA.value()) @@ -135,9 +137,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_TABLE_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusImage.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusImage.java new file mode 100644 index 00000000000..ffdcd3c4b0f --- /dev/null +++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class MilvusImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("milvusdb/milvus:v2.4.9"); + +} diff --git a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java index a0c351451aa..4abca78758d 100644 --- a/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-milvus-store/src/test/java/org/springframework/ai/vectorstore/MilvusVectorStoreObservationIT.java @@ -28,6 +28,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.vectorstore.MilvusVectorStore.MilvusVectorStoreConfig; @@ -61,7 +62,7 @@ public class MilvusVectorStoreObservationIT { private static final String TEST_COLLECTION_NAME = "test_vector_store"; @Container - private static MilvusContainer milvusContainer = new MilvusContainer("milvusdb/milvus:v2.3.8"); + private static MilvusContainer milvusContainer = new MilvusContainer(MilvusImage.DEFAULT_IMAGE); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class); @@ -96,21 +97,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("milvus add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.MILVUS.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.MILVUS.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_COLLECTION_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "default") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -126,7 +128,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("milvus query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.MILVUS.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.MILVUS.value()) @@ -138,8 +140,9 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_COLLECTION_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "default") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbImage.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbImage.java new file mode 100644 index 00000000000..ed59c4993aa --- /dev/null +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class MongoDbImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("mongodb/mongodb-atlas-local:8.0.0"); + +} diff --git a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java index f69c55788d3..3c78d31ea35 100644 --- a/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-mongodb-atlas-store/src/test/java/org/springframework/ai/vectorstore/MongoDbVectorStoreObservationIT.java @@ -69,8 +69,7 @@ public class MongoDbVectorStoreObservationIT { @Container - private static MongoDBAtlasLocalContainer container = new MongoDBAtlasLocalContainer( - "mongodb/mongodb-atlas-local:7.0.9"); + private static MongoDBAtlasLocalContainer container = new MongoDBAtlasLocalContainer(MongoDbImage.DEFAULT_IMAGE); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class) @@ -117,22 +116,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("mongodb add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.MONGODB.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.MONGODB.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), MongoDBAtlasVectorStore.DEFAULT_VECTOR_COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -148,7 +148,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("mongodb query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.MONGODB.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.MONGODB.value()) @@ -160,9 +160,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), MongoDBAtlasVectorStore.DEFAULT_VECTOR_COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java new file mode 100644 index 00000000000..e64012b76a9 --- /dev/null +++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class Neo4jImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("neo4j:5.24"); + +} diff --git a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java index 4f0b7bcd748..ebf09454d27 100644 --- a/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-neo4j-store/src/test/java/org/springframework/ai/vectorstore/Neo4jVectorStoreObservationIT.java @@ -34,6 +34,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -48,7 +49,6 @@ import org.testcontainers.containers.Neo4jContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -63,8 +63,7 @@ public class Neo4jVectorStoreObservationIT { @Container - static Neo4jContainer neo4jContainer = new Neo4jContainer<>(DockerImageName.parse("neo4j:5.18")) - .withRandomPassword(); + static Neo4jContainer neo4jContainer = new Neo4jContainer<>(Neo4jImage.DEFAULT_IMAGE).withRandomPassword(); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class); @@ -105,22 +104,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("neo4j add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.NEO4J.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.NEO4J.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), SchemaNames.sanitize(Neo4jVectorStore.DEFAULT_INDEX_NAME).orElseThrow()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -136,7 +136,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("neo4j query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.NEO4J.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.NEO4J.value()) @@ -148,9 +148,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), SchemaNames.sanitize(Neo4jVectorStore.DEFAULT_INDEX_NAME).orElseThrow()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java new file mode 100644 index 00000000000..dea664624a5 --- /dev/null +++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class OpenSearchImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("opensearchproject/opensearch:2.17.1"); + +} diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java index 5e3faed1c66..45298605d50 100644 --- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java @@ -39,6 +39,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -52,7 +53,6 @@ import org.springframework.core.io.DefaultResourceLoader; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -70,7 +70,7 @@ public class OpenSearchVectorStoreObservationIT { @Container private static final OpensearchContainer opensearchContainer = new OpensearchContainer<>( - DockerImageName.parse("opensearchproject/opensearch:2.13.0")); + OpenSearchImage.DEFAULT_IMAGE); List documents = List.of( new Document(getText("classpath:/test/data/spring.ai.txt"), Map.of("meta1", "meta1")), @@ -121,22 +121,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("opensearch add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.OPENSEARCH.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.OPENSEARCH.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), OpenSearchVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -156,7 +157,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("opensearch query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.OPENSEARCH.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.OPENSEARCH.value()) @@ -168,9 +169,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), OpenSearchVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java new file mode 100644 index 00000000000..3f2250a3c68 --- /dev/null +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class OracleImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("gvenzl/oracle-free:23-slim"); + +} diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java index 534e026b72c..8d2636fbf02 100644 --- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java @@ -30,6 +30,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -64,9 +65,8 @@ public class OracleVectorStoreObservationIT { @Container - static OracleContainer oracle23aiContainer = new OracleContainer("gvenzl/oracle-free:23-slim") - .withCopyFileToContainer(MountableFile.forClasspathResource("/initialize.sql"), - "/container-entrypoint-initdb.d/initialize.sql"); + static OracleContainer oracle23aiContainer = new OracleContainer(OracleImage.DEFAULT_IMAGE).withCopyFileToContainer( + MountableFile.forClasspathResource("/initialize.sql"), "/container-entrypoint-initdb.d/initialize.sql"); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withUserConfiguration(Config.class) @@ -112,22 +112,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("oracle add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.ORACLE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.ORACLE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), OracleVectorStore.DEFAULT_TABLE_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -143,7 +144,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("oracle query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.ORACLE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.ORACLE.value()) @@ -155,9 +156,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), OracleVectorStore.DEFAULT_TABLE_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorImage.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorImage.java new file mode 100644 index 00000000000..5e4204cdbbc --- /dev/null +++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class PgVectorImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("pgvector/pgvector:pg17"); + +} diff --git a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java index 1f1563318ab..f60b66b4940 100644 --- a/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-pgvector-store/src/test/java/org/springframework/ai/vectorstore/PgVectorStoreObservationIT.java @@ -29,6 +29,8 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.observation.conventions.SpringAiKind; +import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiEmbeddingModel; import org.springframework.ai.openai.api.OpenAiApi; @@ -69,7 +71,7 @@ public class PgVectorStoreObservationIT { @Container @SuppressWarnings("resource") - static PostgreSQLContainer postgresContainer = new PostgreSQLContainer<>("pgvector/pgvector:pg16") + static PostgreSQLContainer postgresContainer = new PostgreSQLContainer<>(PgVectorImage.DEFAULT_IMAGE) .withUsername("postgres") .withPassword("postgres"); @@ -113,21 +115,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("pg_vector add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.PG_VECTOR.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "pg_vector") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.PG_VECTOR.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1536") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), PgVectorStore.DEFAULT_TABLE_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "public") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -143,9 +147,10 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("pg_vector query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.PG_VECTOR.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") - .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), "pg_vector") + .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), + VectorStoreProvider.PG_VECTOR.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) @@ -155,8 +160,9 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), PgVectorStore.DEFAULT_TABLE_NAME) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "public") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreObservationIT.java b/vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreObservationIT.java index ee64e809e8e..8fc6b41f774 100644 --- a/vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-pinecone-store/src/test/java/org/springframework/ai/vectorstore/PineconeVectorStoreObservationIT.java @@ -107,21 +107,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("pinecone add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.PINECONE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.PINECONE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), PINECONE_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "article") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -141,7 +142,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("pinecone query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.PINECONE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.PINECONE.value()) @@ -152,9 +153,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), PINECONE_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "article") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantImage.java b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantImage.java new file mode 100644 index 00000000000..4a6592ffad0 --- /dev/null +++ b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore.qdrant; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class QdrantImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("qdrant/qdrant:v1.9.7"); + +} diff --git a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java index 0dbf6765708..5d181cf03d4 100644 --- a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java @@ -68,7 +68,7 @@ public class QdrantVectorStoreObservationIT { private static final int EMBEDDING_DIMENSION = 1024; @Container - static QdrantContainer qdrantContainer = new QdrantContainer("qdrant/qdrant:v1.9.2"); + static QdrantContainer qdrantContainer = new QdrantContainer(QdrantImage.DEFAULT_IMAGE); List documents = List.of( new Document(getText("classpath:/test/data/spring.ai.txt"), Map.of("meta1", "meta1")), @@ -118,21 +118,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("qdrant add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.QDRANT.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.QDRANT.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1024") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -148,7 +149,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("qdrant query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.QDRANT.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.QDRANT.value()) @@ -159,9 +160,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "1024") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java index 943b31e1a4c..d6c7c6808d8 100644 --- a/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ b/vector-stores/spring-ai-redis-store/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java @@ -35,6 +35,7 @@ import org.springframework.ai.embedding.EmbeddingOptionsBuilder; import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore; import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext; @@ -491,7 +492,8 @@ public VectorStoreObservationContext.Builder createObservationContextBuilder(Str .withCollectionName(this.config.indexName) .withDimensions(this.embeddingModel.dimensions()) .withFieldName(this.config.embeddingFieldName) - .withSimilarityMetric(vectorAlgorithm().name()); + .withSimilarityMetric( + "COSINE".equals(DEFAULT_DISTANCE_METRIC) ? VectorStoreSimilarityMetric.COSINE.value() : ""); } diff --git a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreObservationIT.java b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreObservationIT.java index 2a7b717bfd6..64b30ce05be 100644 --- a/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-redis-store/src/test/java/org/springframework/ai/vectorstore/RedisVectorStoreObservationIT.java @@ -29,6 +29,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.RedisVectorStore.MetadataField; import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; @@ -104,22 +105,23 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("redis add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.REDIS.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.REDIS.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), RedisVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "HNSW") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -135,7 +137,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("redis query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.REDIS.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.REDIS.value()) @@ -147,9 +149,10 @@ void observationVectorStoreAddAndQueryOperations() { .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), RedisVectorStore.DEFAULT_INDEX_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "HNSW") + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseImage.java b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseImage.java new file mode 100644 index 00000000000..ac27de2a8db --- /dev/null +++ b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class TypesenseImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("typesense/typesense:27.1"); + +} diff --git a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseVectorStoreObservationIT.java b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseVectorStoreObservationIT.java index 47d35b40d8a..c5fdc881029 100644 --- a/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-typesense-store/src/test/java/org/springframework/ai/vectorstore/TypesenseVectorStoreObservationIT.java @@ -30,6 +30,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.observation.conventions.SpringAiKind; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.TypesenseVectorStore.TypesenseVectorStoreConfig; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; @@ -61,7 +62,7 @@ public class TypesenseVectorStoreObservationIT { private static final String TEST_COLLECTION_NAME = "test_vector_store"; @Container - private static GenericContainer typesenseContainer = new GenericContainer<>("typesense/typesense:26.0") + private static GenericContainer typesenseContainer = new GenericContainer<>(TypesenseImage.DEFAULT_IMAGE) .withExposedPorts(8108) .withCommand("--data-dir", "/tmp", "--api-key=xyz", "--enable-cors"); @@ -98,21 +99,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("typesense add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.TYPESENSE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.TYPESENSE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -128,7 +130,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("typesense query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.TYPESENSE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.TYPESENSE.value()) @@ -139,9 +141,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), TEST_COLLECTION_NAME) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "embedding") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "cosine") + .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString(), + VectorStoreSimilarityMetric.COSINE.value()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0") diff --git a/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateImage.java b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateImage.java new file mode 100644 index 00000000000..81e78bc5c70 --- /dev/null +++ b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateImage.java @@ -0,0 +1,27 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.ai.vectorstore; + +import org.testcontainers.utility.DockerImageName; + +/** + * @author Thomas Vitale + */ +public class WeaviateImage { + + public static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("semitechnologies/weaviate:1.25.9"); + +} diff --git a/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreObservationIT.java b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreObservationIT.java index 20d04a97d9a..35f9c4d599f 100644 --- a/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-weaviate-store/src/test/java/org/springframework/ai/vectorstore/WeaviateVectorStoreObservationIT.java @@ -56,7 +56,7 @@ public class WeaviateVectorStoreObservationIT { @Container - static WeaviateContainer weaviateContainer = new WeaviateContainer("semitechnologies/weaviate:1.25.4") + static WeaviateContainer weaviateContainer = new WeaviateContainer(WeaviateImage.DEFAULT_IMAGE) .waitingFor(Wait.forHttp("/v1/.well-known/ready").forPort(8080)); List documents = List.of( @@ -92,21 +92,22 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("weaviate add") + .hasContextualNameEqualTo("%s add".formatted(VectorStoreProvider.WEAVIATE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "add") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.WEAVIATE.value()) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.SPRING_AI_KIND.asString(), SpringAiKind.VECTOR_STORE.value()) - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_CONTENT.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "SpringAiWeaviate") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), - "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString()) .hasBeenStarted() .hasBeenStopped(); @@ -122,7 +123,7 @@ void observationVectorStoreAddAndQueryOperations() { .doesNotHaveAnyRemainingCurrentObservation() .hasObservationWithNameEqualTo(DefaultVectorStoreObservationConvention.DEFAULT_NAME) .that() - .hasContextualNameEqualTo("weaviate query") + .hasContextualNameEqualTo("%s query".formatted(VectorStoreProvider.WEAVIATE.value())) .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_OPERATION_NAME.asString(), "query") .hasLowCardinalityKeyValue(LowCardinalityKeyNames.DB_SYSTEM.asString(), VectorStoreProvider.WEAVIATE.value()) @@ -133,9 +134,10 @@ void observationVectorStoreAddAndQueryOperations() { "What is Great Depression") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_DIMENSION_COUNT.asString(), "384") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_COLLECTION_NAME.asString(), "SpringAiWeaviate") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_NAMESPACE.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString(), "none") - .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_SIMILARITY_METRIC.asString(), "none") + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_NAMESPACE.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey(HighCardinalityKeyNames.DB_VECTOR_FIELD_NAME.asString()) + .doesNotHaveHighCardinalityKeyValueWithKey( + HighCardinalityKeyNames.DB_SEARCH_SIMILARITY_METRIC.asString()) .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_TOP_K.asString(), "1") .hasHighCardinalityKeyValue(HighCardinalityKeyNames.DB_VECTOR_QUERY_SIMILARITY_THRESHOLD.asString(), "0.0")