diff --git a/Microsoft.Azure.Cosmos/src/CosmosClient.cs b/Microsoft.Azure.Cosmos/src/CosmosClient.cs
index 3e19e603ca..3cb7353f8c 100644
--- a/Microsoft.Azure.Cosmos/src/CosmosClient.cs
+++ b/Microsoft.Azure.Cosmos/src/CosmosClient.cs
@@ -607,6 +607,15 @@ internal CosmosClient(
/// This property is read-only. Modifying any options after the client has been created has no effect on the existing client instance.
public virtual CosmosClientOptions ClientOptions => this.ClientContext.ClientOptions;
+#if PREVIEW
+ ///
+ /// Gets the client-wide , or null if none was set.
+ /// Set via or
+ /// .
+ ///
+ public virtual ICosmosEmbeddingGenerator EmbeddingGenerator => this.ClientContext.ClientOptions.EmbeddingGenerator;
+#endif
+
///
/// The response factory used to create CosmosClient response types.
///
diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
index 9e3b8dfc00..90920e82e1 100644
--- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
+++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
@@ -405,6 +405,18 @@ public ConnectionMode ConnectionMode
#endif
ReadConsistencyStrategy? ReadConsistencyStrategy { get; set; }
+ ///
+ /// Gets or sets the client-wide default used to generate
+ /// query-time vector embeddings for hybrid and vector-search queries.
+ ///
+ [JsonIgnore]
+#if PREVIEW
+ public
+#else
+ internal
+#endif
+ ICosmosEmbeddingGenerator EmbeddingGenerator { get; set; }
+
///
/// Sets the priority level for requests created using cosmos client.
///
diff --git a/Microsoft.Azure.Cosmos/src/CosmosEmbeddingResult.cs b/Microsoft.Azure.Cosmos/src/CosmosEmbeddingResult.cs
new file mode 100644
index 0000000000..8d61ba2bbe
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/src/CosmosEmbeddingResult.cs
@@ -0,0 +1,69 @@
+//------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------
+
+namespace Microsoft.Azure.Cosmos
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// The result of a call to .
+ /// Carries the generated float32 vectors plus optional diagnostic fields (token usage,
+ /// latency) the SDK surfaces through CosmosDiagnostics.
+ ///
+#if PREVIEW
+ public
+#else
+ internal
+#endif
+ sealed class CosmosEmbeddingResult
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ ///
+ /// The generated float32 embedding vectors, one per input string supplied to the
+ /// originating call,
+ /// in the same order as the inputs.
+ ///
+ ///
+ /// Optional total token count consumed by the embedding service to produce these vectors.
+ /// Pass null when the underlying service does not report token usage.
+ ///
+ ///
+ /// Optional duration the implementation observed for the embedding service call (for
+ /// example, the wall-clock time around the underlying HTTP request). Surfaced through
+ /// CosmosDiagnostics for query-time observability. Pass null when the
+ /// implementation does not measure latency.
+ ///
+ public CosmosEmbeddingResult(
+ IReadOnlyList> vectors,
+ int? totalTokens = null,
+ TimeSpan? latency = null)
+ {
+ this.Vectors = vectors ?? throw new ArgumentNullException(nameof(vectors));
+ this.TotalTokens = totalTokens;
+ this.Latency = latency;
+ }
+
+ ///
+ /// Gets the generated float32 embedding vectors, one per input string, in the same
+ /// order as the inputs supplied to .
+ ///
+ public IReadOnlyList> Vectors { get; }
+
+ ///
+ /// Gets the total number of tokens the embedding service consumed to generate
+ /// , or null when the underlying service does not report it.
+ ///
+ public int? TotalTokens { get; }
+
+ ///
+ /// Gets the duration the implementation observed for the underlying embedding service
+ /// call, or null when the implementation does not measure it. Surfaced through
+ /// CosmosDiagnostics for query-time observability.
+ ///
+ public TimeSpan? Latency { get; }
+ }
+}
diff --git a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
index e33de132e0..4775f3e690 100644
--- a/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
+++ b/Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
@@ -869,5 +869,23 @@ CosmosClientBuilder WithReadConsistencyStrategy(Cosmos.ReadConsistencyStrategy r
this.clientOptions.ReadConsistencyStrategy = readConsistencyStrategy;
return this;
}
+
+ ///
+ /// Sets the client-wide default used to generate
+ /// query-time vector embeddings for hybrid and vector-search queries.
+ ///
+ /// The embedding generator to use as the client-wide default.
+ /// The current .
+ /// Thrown if is null.
+#if PREVIEW
+ public
+#else
+ internal
+#endif
+ CosmosClientBuilder WithEmbeddingGenerator(ICosmosEmbeddingGenerator embeddingGenerator)
+ {
+ this.clientOptions.EmbeddingGenerator = embeddingGenerator ?? throw new ArgumentNullException(nameof(embeddingGenerator));
+ return this;
+ }
}
}
diff --git a/Microsoft.Azure.Cosmos/src/ICosmosEmbeddingGenerator.cs b/Microsoft.Azure.Cosmos/src/ICosmosEmbeddingGenerator.cs
new file mode 100644
index 0000000000..a1164dae0c
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/src/ICosmosEmbeddingGenerator.cs
@@ -0,0 +1,113 @@
+//------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------
+
+namespace Microsoft.Azure.Cosmos
+{
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ ///
+ /// Defines a contract for generating float32 vector embeddings from input text strings
+ /// supplied by the Azure Cosmos DB query pipeline.
+ /// The SDK invokes this when a query plan contains GenerateEmbeddings(...) literals
+ /// (for example VectorDistance(GenerateEmbeddings("big brown cat"), c.embedding)).
+ /// Set a client-wide default via CosmosClientOptions.EmbeddingGenerator or
+ /// CosmosClientBuilder.WithEmbeddingGenerator. Implementations MUST be thread-safe and are
+ /// responsible for any caching, retries, and authentication required to call the underlying
+ /// embedding service.
+ ///
+ ///
+ /// Preview surface. The SDK call site that invokes this method is delivered
+ /// in a follow-up release. Setting an instance via
+ /// or
+ /// has no runtime effect
+ /// today; the surface is shipped in this preview so customers can author and test
+ /// implementations against the contract ahead of the resolver landing.
+ /// Lifecycle and disposal. The customer owns the generator instance. The SDK
+ /// keeps a reference for the lifetime of the configured (or the
+ /// reference it was bound to) but never disposes it. If the
+ /// implementation holds disposable resources (for example an HttpClient or an
+ /// EmbeddingClient), the customer is responsible for disposing them when their
+ /// application tears down.
+ ///
+ /// Error semantics. Implementations are responsible for handling transient
+ /// failures from the underlying embedding service (network errors, rate limiting, etc.)
+ /// via their own retry policy. The SDK does not retry calls to this method. Any exception
+ /// thrown by the implementation is wrapped into a and
+ /// surfaced to the originating SDK caller.
+ ///
+ /// Cancellation. Implementations should honor the supplied
+ /// cooperatively wherever feasible (typically by forwarding
+ /// it to the underlying HTTP call). Best-effort cancellation is acceptable; ignoring the
+ /// token entirely is discouraged because it defeats caller-side timeouts.
+ ///
+ /// Idempotency and concurrency. The SDK may invoke this method multiple times
+ /// for the same inputs (for example during internal query retry) and may invoke it
+ /// concurrently from multiple threads. Implementations must be safe to call repeatedly
+ /// and from parallel callers, and must not assume per-call state. Note that each call
+ /// typically incurs cost at the underlying embedding service; implementations may cache
+ /// responses internally if they want to avoid duplicate billing for identical inputs.
+ ///
+#if PREVIEW
+ public
+#else
+ internal
+#endif
+ interface ICosmosEmbeddingGenerator
+ {
+ ///
+ /// Generates an embedding vector for each of the supplied input strings.
+ ///
+ ///
+ /// The input strings to embed, in the order the implementation MUST preserve in the
+ /// returned (one vector per input, same
+ /// index). Typed as so implementations can size their
+ /// outbound batch without re-enumeration and so the 1:1 ordered contract is encoded
+ /// in the signature.
+ ///
+ ///
+ /// The embedding service endpoint to call (for example the Azure OpenAI account endpoint).
+ /// Sourced from the container's EmbeddingSource.Endpoint when configured.
+ ///
+ ///
+ /// The model deployment name to invoke at . Sourced from the
+ /// container's EmbeddingSource.DeploymentName when configured.
+ ///
+ ///
+ /// The vector dimensionality the produced embeddings must match. For models that support
+ /// dimensionality reduction (for example text-embedding-3-small /
+ /// text-embedding-3-large), implementations MUST forward this value to the
+ /// underlying service so the returned vectors have the expected length; otherwise the
+ /// service returns its default size, which may not match the container's
+ /// .
+ ///
+ ///
+ /// A propagated from the originating SDK call
+ /// (for example FeedIterator.ReadNextAsync). Implementations should honor cancellation.
+ ///
+ ///
+ /// A task that resolves to a whose
+ /// contains one float32 vector per input,
+ /// each of length , in the same order as
+ /// .
+ ///
+ /// Query-time vectors are sent to the Azure Cosmos DB gateway as float32 regardless of
+ /// the container's stored . Implementations targeting
+ /// containers configured for ,
+ /// , or storage
+ /// should still produce float32 vectors here; the Azure Cosmos DB service applies the
+ /// configured quantization at write time. This contract
+ /// covers all four storage configurations supported by
+ /// the container's .
+ ///
+ ///
+ Task GenerateEmbeddingsAsync(
+ IReadOnlyList texts,
+ string endpoint,
+ string deploymentName,
+ int dimensions,
+ CancellationToken cancellationToken = default);
+ }
+}
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json
index 3ed1d7753e..dc82836891 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json
@@ -42,6 +42,22 @@
},
"NestedTypes": {}
},
+ "Microsoft.Azure.Cosmos.CosmosClient;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
+ "Subclasses": {},
+ "Members": {
+ "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator EmbeddingGenerator": {
+ "Type": "Property",
+ "Attributes": [],
+ "MethodInfo": "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator EmbeddingGenerator;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator()": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ }
+ },
+ "NestedTypes": {}
+ },
"Microsoft.Azure.Cosmos.CosmosClientOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
@@ -55,6 +71,20 @@
"Attributes": [],
"MethodInfo": "Boolean get_EnableRemoteRegionPreferredForSessionRetry();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
+ "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator EmbeddingGenerator[Newtonsoft.Json.JsonIgnoreAttribute()]": {
+ "Type": "Property",
+ "Attributes": [
+ "JsonIgnoreAttribute"
+ ],
+ "MethodInfo": "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator EmbeddingGenerator;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_EmbeddingGenerator(Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
+ "Type": "Method",
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
+ "MethodInfo": "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator get_EmbeddingGenerator();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"System.Nullable`1[Microsoft.Azure.Cosmos.ReadConsistencyStrategy] get_ReadConsistencyStrategy()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
@@ -91,6 +121,13 @@
"Attributes": [],
"MethodInfo": "System.TimeSpan InferenceRequestTimeout;CanRead:True;CanWrite:True;System.TimeSpan get_InferenceRequestTimeout();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_InferenceRequestTimeout(System.TimeSpan);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
+ "Void set_EmbeddingGenerator(Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
+ "Type": "Method",
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
+ "MethodInfo": "Void set_EmbeddingGenerator(Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"Void set_EnableRemoteRegionPreferredForSessionRetry(Boolean)": {
"Type": "Method",
"Attributes": [],
@@ -908,6 +945,53 @@
},
"NestedTypes": {}
},
+ "Microsoft.Azure.Cosmos.CosmosEmbeddingResult;System.Object;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
+ "Subclasses": {},
+ "Members": {
+ "System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]] get_Vectors()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
+ "Type": "Method",
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
+ "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]] get_Vectors();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]] Vectors": {
+ "Type": "Property",
+ "Attributes": [],
+ "MethodInfo": "System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]] Vectors;CanRead:True;CanWrite:False;System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]] get_Vectors();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Nullable`1[System.Int32] get_TotalTokens()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
+ "Type": "Method",
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
+ "MethodInfo": "System.Nullable`1[System.Int32] get_TotalTokens();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Nullable`1[System.Int32] TotalTokens": {
+ "Type": "Property",
+ "Attributes": [],
+ "MethodInfo": "System.Nullable`1[System.Int32] TotalTokens;CanRead:True;CanWrite:False;System.Nullable`1[System.Int32] get_TotalTokens();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Nullable`1[System.TimeSpan] get_Latency()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
+ "Type": "Method",
+ "Attributes": [
+ "CompilerGeneratedAttribute"
+ ],
+ "MethodInfo": "System.Nullable`1[System.TimeSpan] get_Latency();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "System.Nullable`1[System.TimeSpan] Latency": {
+ "Type": "Property",
+ "Attributes": [],
+ "MethodInfo": "System.Nullable`1[System.TimeSpan] Latency;CanRead:True;CanWrite:False;System.Nullable`1[System.TimeSpan] get_Latency();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
+ "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]], System.Nullable`1[System.Int32], System.Nullable`1[System.TimeSpan])": {
+ "Type": "Constructor",
+ "Attributes": [],
+ "MethodInfo": "Void .ctor(System.Collections.Generic.IReadOnlyList`1[System.ReadOnlyMemory`1[System.Single]], System.Nullable`1[System.Int32], System.Nullable`1[System.TimeSpan])"
+ }
+ },
+ "NestedTypes": {}
+ },
"Microsoft.Azure.Cosmos.Embedding;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
@@ -1102,6 +1186,11 @@
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
+ "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithEmbeddingGenerator(Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator)": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithEmbeddingGenerator(Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ },
"Microsoft.Azure.Cosmos.Fluent.CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(Boolean)": {
"Type": "Method",
"Attributes": [],
@@ -1151,6 +1240,17 @@
},
"NestedTypes": {}
},
+ "Microsoft.Azure.Cosmos.ICosmosEmbeddingGenerator;;IsAbstract:True;IsSealed:False;IsInterface:True;IsEnum:False;IsClass:False;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
+ "Subclasses": {},
+ "Members": {
+ "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosEmbeddingResult] GenerateEmbeddingsAsync(System.Collections.Generic.IReadOnlyList`1[System.String], System.String, System.String, Int32, System.Threading.CancellationToken)": {
+ "Type": "Method",
+ "Attributes": [],
+ "MethodInfo": "System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.CosmosEmbeddingResult] GenerateEmbeddingsAsync(System.Collections.Generic.IReadOnlyList`1[System.String], System.String, System.String, Int32, System.Threading.CancellationToken);IsAbstract:True;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
+ }
+ },
+ "NestedTypes": {}
+ },
"Microsoft.Azure.Cosmos.ItemRequestOptions;Microsoft.Azure.Cosmos.RequestOptions;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {
"Subclasses": {},
"Members": {
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
index 1fe9127de4..9b3797ba11 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
@@ -278,6 +278,100 @@ public void VerifyReadConsistencyStrategyBuilderProperties()
}
}
+ [TestMethod]
+ public void VerifyEmbeddingGeneratorBuilderProperties()
+ {
+ string endpoint = AccountEndpoint;
+ string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
+
+ // Verify default is null
+ CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder(
+ accountEndpoint: endpoint,
+ authKeyOrResourceToken: key);
+
+ CosmosClient cosmosClient = cosmosClientBuilder.Build(new MockDocumentClient());
+ CosmosClientOptions clientOptions = cosmosClient.ClientOptions;
+
+ Assert.IsNull(clientOptions.EmbeddingGenerator);
+
+ // Verify WithEmbeddingGenerator sets the property
+ ICosmosEmbeddingGenerator generator = new MockEmbeddingGenerator();
+ cosmosClientBuilder = new CosmosClientBuilder(
+ accountEndpoint: endpoint,
+ authKeyOrResourceToken: key);
+
+ cosmosClientBuilder.WithEmbeddingGenerator(generator);
+
+ cosmosClient = cosmosClientBuilder.Build(new MockDocumentClient());
+ clientOptions = cosmosClient.ClientOptions;
+
+ Assert.AreSame(generator, clientOptions.EmbeddingGenerator,
+ "EmbeddingGenerator instance did not round-trip through the builder");
+
+ // Verify null throws ArgumentNullException
+ Assert.ThrowsException(
+ () => new CosmosClientBuilder(accountEndpoint: endpoint, authKeyOrResourceToken: key)
+ .WithEmbeddingGenerator(null),
+ "WithEmbeddingGenerator should throw ArgumentNullException for null input");
+ }
+
+#if PREVIEW
+ [TestMethod]
+ public void CosmosClient_EmbeddingGenerator_ReturnsConfiguredInstance()
+ {
+ string endpoint = AccountEndpoint;
+ string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
+
+ // Default: CosmosClient.EmbeddingGenerator is null when nothing was configured.
+ CosmosClient defaultClient = new CosmosClientBuilder(endpoint, key)
+ .Build(new MockDocumentClient());
+ Assert.IsNull(defaultClient.EmbeddingGenerator,
+ "CosmosClient.EmbeddingGenerator must be null when no generator was configured");
+
+ // Configured via builder: CosmosClient.EmbeddingGenerator returns the same instance.
+ ICosmosEmbeddingGenerator builderGenerator = new MockEmbeddingGenerator();
+ CosmosClient builderClient = new CosmosClientBuilder(endpoint, key)
+ .WithEmbeddingGenerator(builderGenerator)
+ .Build(new MockDocumentClient());
+ Assert.AreSame(builderGenerator, builderClient.EmbeddingGenerator,
+ "CosmosClient.EmbeddingGenerator must return the instance set via CosmosClientBuilder.WithEmbeddingGenerator");
+
+ // Configured via CosmosClientOptions directly: same accessor surfaces it.
+ ICosmosEmbeddingGenerator optionsGenerator = new MockEmbeddingGenerator();
+ CosmosClient optionsClient = new CosmosClientBuilder(endpoint, key)
+ .WithCustomSerializer(new CosmosJsonDotNetSerializer()) // ensures non-default options path
+ .Build(new MockDocumentClient());
+ optionsClient.ClientOptions.EmbeddingGenerator = optionsGenerator;
+ Assert.AreSame(optionsGenerator, optionsClient.EmbeddingGenerator,
+ "CosmosClient.EmbeddingGenerator must return the instance set on CosmosClientOptions.EmbeddingGenerator");
+ }
+
+ [TestMethod]
+ public void CosmosClientOptions_Clone_PreservesEmbeddingGenerator()
+ {
+ ICosmosEmbeddingGenerator generator = new MockEmbeddingGenerator();
+
+ CosmosClientOptions options = new CosmosClientOptions
+ {
+ EmbeddingGenerator = generator,
+ };
+
+ CosmosClientOptions clone = options.Clone();
+
+ Assert.AreSame(generator, clone.EmbeddingGenerator,
+ "CosmosClientOptions.Clone() must preserve the EmbeddingGenerator reference on the clone");
+
+ // The clone must be a distinct instance so subsequent mutations are isolated.
+ Assert.AreNotSame(options, clone, "Clone() must return a distinct instance");
+
+ // Mutating the clone must not affect the source.
+ ICosmosEmbeddingGenerator otherGenerator = new MockEmbeddingGenerator();
+ clone.EmbeddingGenerator = otherGenerator;
+ Assert.AreSame(generator, options.EmbeddingGenerator,
+ "Mutating EmbeddingGenerator on a clone must not affect the original options instance");
+ }
+#endif
+
///
/// Test to validate that when the partition level failover is enabled with the preferred regions list is missing, then the client
/// initialization should succeed. This should hold true for both environment variable and CosmosClientOptions.
@@ -1335,5 +1429,18 @@ public int Compare(object x, object y)
return 1;
}
}
+
+ private sealed class MockEmbeddingGenerator : ICosmosEmbeddingGenerator
+ {
+ public System.Threading.Tasks.Task GenerateEmbeddingsAsync(
+ System.Collections.Generic.IReadOnlyList texts,
+ string endpoint,
+ string deploymentName,
+ int dimensions,
+ System.Threading.CancellationToken cancellationToken = default)
+ {
+ throw new NotImplementedException();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/changelog.md b/changelog.md
index 9761217a21..a5b7a303db 100644
--- a/changelog.md
+++ b/changelog.md
@@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Features Added
+- [5838](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/5838) EmbeddingGenerator: Adds ICosmosEmbeddingGenerator client-wide configuration (preview)
+
#### Breaking Changes
#### Bugs Fixed