Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand All @@ -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.
*/
Expand All @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href=
* "https://github.com/open-telemetry/semantic-conventions/tree/main/docs/database">DB
* Semantic Conventions</a>.
*/
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down Expand Up @@ -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();
}
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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());
}

}
Loading