From 9a0a305b2ef8e18db46c40e363f453e15558f5b2 Mon Sep 17 00:00:00 2001 From: PrathamAditya Date: Mon, 20 Oct 2025 00:38:24 +0530 Subject: [PATCH 1/5] fix(google): include taskType in Google AI embedding request (fixes #13250) --- .../GoogleAI/GoogleAIEmbeddingRequestTests.cs | 22 +++++++++++++++++++ .../Core/GoogleAI/GoogleAIEmbeddingClient.cs | 17 +++++++++++--- .../Core/GoogleAI/GoogleAIEmbeddingRequest.cs | 5 +++-- .../GoogleAITextEmbeddingGenerationService.cs | 22 ++++++++++++++++++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs index 731e20dda585..3067d22fb3e2 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Text.Json; using Microsoft.SemanticKernel.Connectors.Google.Core; using Xunit; @@ -83,4 +84,25 @@ public void FromDataJsonIncludesDimensionsWhenProvided() // Assert Assert.Contains($"{DimensionalityJsonPropertyName}:{Dimensions}", json); } + + [Fact] + public void FromDataShouldIncludeTaskTypeWhenProvided() + { + // Arrange + var input = new[] { "This is a retrieval document." }; + var modelId = "embedding-001"; + var dimensions = 1024; + var taskType = "RETRIEVAL_DOCUMENT"; + + // Act + var request = GoogleAIEmbeddingRequest.FromData(input, modelId, dimensions, taskType); + + // Serialize to JSON (this is what would be sent in the HTTP request) + var json = System.Text.Json.JsonSerializer.Serialize(request); + + // Assert + Assert.Contains("\"taskType\":\"RETRIEVAL_DOCUMENT\"", json); + Assert.Contains("\"model\":\"models/embedding-001\"", json); + } + } diff --git a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs index 6a801acff76e..141ebfe78930 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs @@ -6,6 +6,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging; namespace Microsoft.SemanticKernel.Connectors.Google.Core; @@ -54,15 +55,25 @@ public GoogleAIEmbeddingClient( /// Generates embeddings for the given data asynchronously. /// /// The list of strings to generate embeddings for. + /// The embedding generation options. /// The cancellation token to cancel the operation. /// Result contains a list of read-only memories of floats representing the generated embeddings. public async Task>> GenerateEmbeddingsAsync( IList data, + EmbeddingGenerationOptions? options = null, CancellationToken cancellationToken = default) { Verify.NotNullOrEmpty(data); - var geminiRequest = this.GetEmbeddingRequest(data); + // var geminiRequest = this.GetEmbeddingRequest(data); + string? taskType = null; + if (options?.AdditionalProperties?.TryGetValue("task_type", out var taskTypeValue) == true) + { + taskType = taskTypeValue?.ToString(); + } + + var geminiRequest = this.GetEmbeddingRequest(data, taskType); + using var httpRequestMessage = await this.CreateHttpRequestAsync(geminiRequest, this._embeddingEndpoint).ConfigureAwait(false); string body = await this.SendRequestAndGetStringBodyAsync(httpRequestMessage, cancellationToken) @@ -71,8 +82,8 @@ public async Task>> GenerateEmbeddingsAsync( return DeserializeAndProcessEmbeddingsResponse(body); } - private GoogleAIEmbeddingRequest GetEmbeddingRequest(IEnumerable data) - => GoogleAIEmbeddingRequest.FromData(data, this._embeddingModelId, this._dimensions); + private GoogleAIEmbeddingRequest GetEmbeddingRequest(IEnumerable data, string? taskType = null) + => GoogleAIEmbeddingRequest.FromData(data, this._embeddingModelId, this._dimensions, taskType); private static List> DeserializeAndProcessEmbeddingsResponse(string body) => ProcessEmbeddingsResponse(DeserializeResponse(body)); diff --git a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs index d69953dc5423..4b019832d034 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs @@ -11,7 +11,7 @@ internal sealed class GoogleAIEmbeddingRequest [JsonPropertyName("requests")] public IList Requests { get; set; } = null!; - public static GoogleAIEmbeddingRequest FromData(IEnumerable data, string modelId, int? dimensions = null) => new() + public static GoogleAIEmbeddingRequest FromData(IEnumerable data, string modelId, int? dimensions = null, string? taskType = null) => new() { Requests = data.Select(text => new RequestEmbeddingContent { @@ -26,7 +26,8 @@ internal sealed class GoogleAIEmbeddingRequest } ] }, - Dimensions = dimensions + Dimensions = dimensions, + TaskType = taskType }).ToList() }; diff --git a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs index d526801c52c9..52818160b7b5 100644 --- a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs +++ b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel.Connectors.Google.Core; using Microsoft.SemanticKernel.Embeddings; @@ -68,6 +69,25 @@ public Task>> GenerateEmbeddingsAsync( Kernel? kernel = null, CancellationToken cancellationToken = default) { - return this._embeddingClient.GenerateEmbeddingsAsync(data, cancellationToken); + return this._embeddingClient.GenerateEmbeddingsAsync(data, null, cancellationToken); } + + /// + /// Generates embeddings for the specified input text, allowing additional configuration + /// via (e.g., specifying the Google task type). + /// + /// The input text collection to generate embeddings for. + /// Embedding generation options (e.g., task_type). + /// Optional Kernel instance. + /// Token for cancelling the request. + /// A list of generated embeddings. + public Task>> GenerateEmbeddingsAsync( + IList data, + EmbeddingGenerationOptions? options, + Kernel? kernel = null, + CancellationToken cancellationToken = default) + { + return this._embeddingClient.GenerateEmbeddingsAsync(data, options, cancellationToken); + } + } From 33e3728ceec6761f9eb1bb4f21d93198cfb7241c Mon Sep 17 00:00:00 2001 From: PrathamAditya Date: Tue, 21 Oct 2025 15:08:16 +0545 Subject: [PATCH 2/5] Removed System namespace --- .../Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs index 3067d22fb3e2..7fc31c2467c2 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -using System; using System.Text.Json; using Microsoft.SemanticKernel.Connectors.Google.Core; using Xunit; From 44bb4b4c102334460748300574643480b4e087dd Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:52:32 +0000 Subject: [PATCH 3/5] Address Format + Comment --- .../Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs | 1 - .../Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs | 1 - .../Services/GoogleAITextEmbeddingGenerationService.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs index 7fc31c2467c2..26643dd45d97 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs @@ -103,5 +103,4 @@ public void FromDataShouldIncludeTaskTypeWhenProvided() Assert.Contains("\"taskType\":\"RETRIEVAL_DOCUMENT\"", json); Assert.Contains("\"model\":\"models/embedding-001\"", json); } - } diff --git a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs index 141ebfe78930..5ad762b8608a 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs @@ -65,7 +65,6 @@ public async Task>> GenerateEmbeddingsAsync( { Verify.NotNullOrEmpty(data); - // var geminiRequest = this.GetEmbeddingRequest(data); string? taskType = null; if (options?.AdditionalProperties?.TryGetValue("task_type", out var taskTypeValue) == true) { diff --git a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs index 52818160b7b5..6958105f3ee3 100644 --- a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs +++ b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs @@ -89,5 +89,4 @@ public Task>> GenerateEmbeddingsAsync( { return this._embeddingClient.GenerateEmbeddingsAsync(data, options, cancellationToken); } - } From 73374de8a0b91d62ecebd918031caadc1db3766b Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:07:34 +0000 Subject: [PATCH 4/5] Improving PR to consider options settings as override + snake and contiguous tasktype additional property support --- ...GoogleAIClientEmbeddingsGenerationTests.cs | 29 ++++++++++ .../GoogleAI/GoogleAIEmbeddingRequestTests.cs | 15 ++++- .../Core/GoogleAI/GoogleAIEmbeddingClient.cs | 12 +--- .../Core/GoogleAI/GoogleAIEmbeddingRequest.cs | 56 +++++++++++++------ 4 files changed, 84 insertions(+), 28 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIClientEmbeddingsGenerationTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIClientEmbeddingsGenerationTests.cs index 24b095874cb0..8216e8b78c91 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIClientEmbeddingsGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIClientEmbeddingsGenerationTests.cs @@ -219,6 +219,35 @@ public async Task ShouldNotIncludeDimensionsInAllRequestsWhenNotProvidedAsync() Assert.All(request.Requests, item => Assert.Null(item.Dimensions)); } + [Fact] + public async Task GenerateEmbeddingsUsingEmbeddingGenerationOptionsShouldOverrideDimensionsAndModelAsync() + { + // Arrange + var client = this.CreateEmbeddingsClient(); + var dataToEmbed = new List() + { + "First text to embed", + "Second text to embed", + "Third text to embed" + }; + + var options = new Microsoft.Extensions.AI.EmbeddingGenerationOptions { Dimensions = 10, ModelId = "override-model" }; + + // Act + await client.GenerateEmbeddingsAsync(dataToEmbed, options); + + // Assert + var request = JsonSerializer.Deserialize(this._messageHandlerStub.RequestContent); + Assert.NotNull(request); + Assert.Equal(dataToEmbed.Count, request.Requests.Count); + Assert.All(request.Requests, + item => + { + Assert.Contains(options.ModelId, item.Model); + Assert.Equal(options.Dimensions, item.Dimensions); + }); + } + private GoogleAIEmbeddingClient CreateEmbeddingsClient( string modelId = "fake-model", int? dimensions = null) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs index 26643dd45d97..d3ce4ab7e28d 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/GoogleAI/GoogleAIEmbeddingRequestTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System.Text.Json; +using Microsoft.Extensions.AI; using Microsoft.SemanticKernel.Connectors.Google.Core; using Xunit; @@ -84,8 +85,14 @@ public void FromDataJsonIncludesDimensionsWhenProvided() Assert.Contains($"{DimensionalityJsonPropertyName}:{Dimensions}", json); } - [Fact] - public void FromDataShouldIncludeTaskTypeWhenProvided() + [Theory] + [InlineData("TaskType")] + [InlineData("Task_Type")] + [InlineData("taskType")] + [InlineData("task_Type")] + [InlineData("tasktype")] + [InlineData("task_type")] + public void FromDataShouldIncludeTaskTypeWhenProvided(string additionalPropertyKeyName) { // Arrange var input = new[] { "This is a retrieval document." }; @@ -93,8 +100,10 @@ public void FromDataShouldIncludeTaskTypeWhenProvided() var dimensions = 1024; var taskType = "RETRIEVAL_DOCUMENT"; + var options = new EmbeddingGenerationOptions { AdditionalProperties = new AdditionalPropertiesDictionary { [additionalPropertyKeyName] = taskType } }; + // Act - var request = GoogleAIEmbeddingRequest.FromData(input, modelId, dimensions, taskType); + var request = GoogleAIEmbeddingRequest.FromData(input, modelId, dimensions, options); // Serialize to JSON (this is what would be sent in the HTTP request) var json = System.Text.Json.JsonSerializer.Serialize(request); diff --git a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs index 5ad762b8608a..8f009366dc46 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingClient.cs @@ -65,13 +65,7 @@ public async Task>> GenerateEmbeddingsAsync( { Verify.NotNullOrEmpty(data); - string? taskType = null; - if (options?.AdditionalProperties?.TryGetValue("task_type", out var taskTypeValue) == true) - { - taskType = taskTypeValue?.ToString(); - } - - var geminiRequest = this.GetEmbeddingRequest(data, taskType); + var geminiRequest = this.GetEmbeddingRequest(data, options); using var httpRequestMessage = await this.CreateHttpRequestAsync(geminiRequest, this._embeddingEndpoint).ConfigureAwait(false); @@ -81,8 +75,8 @@ public async Task>> GenerateEmbeddingsAsync( return DeserializeAndProcessEmbeddingsResponse(body); } - private GoogleAIEmbeddingRequest GetEmbeddingRequest(IEnumerable data, string? taskType = null) - => GoogleAIEmbeddingRequest.FromData(data, this._embeddingModelId, this._dimensions, taskType); + private GoogleAIEmbeddingRequest GetEmbeddingRequest(IEnumerable data, EmbeddingGenerationOptions? options = null) + => GoogleAIEmbeddingRequest.FromData(data, options?.ModelId ?? this._embeddingModelId, options?.Dimensions ?? this._dimensions, options); private static List> DeserializeAndProcessEmbeddingsResponse(string body) => ProcessEmbeddingsResponse(DeserializeResponse(body)); diff --git a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs index 4b019832d034..d7fd3443e94c 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/GoogleAI/GoogleAIEmbeddingRequest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; +using Microsoft.Extensions.AI; namespace Microsoft.SemanticKernel.Connectors.Google.Core; @@ -11,25 +12,48 @@ internal sealed class GoogleAIEmbeddingRequest [JsonPropertyName("requests")] public IList Requests { get; set; } = null!; - public static GoogleAIEmbeddingRequest FromData(IEnumerable data, string modelId, int? dimensions = null, string? taskType = null) => new() + public static GoogleAIEmbeddingRequest FromData(IEnumerable data, string modelId, int? dimensions = null, EmbeddingGenerationOptions? options = null) { - Requests = data.Select(text => new RequestEmbeddingContent + static string? GetTaskType(EmbeddingGenerationOptions? options) { - Model = $"models/{modelId}", - Content = new() + if (options?.AdditionalProperties is not null) { - Parts = - [ - new() - { - Text = text - } - ] - }, - Dimensions = dimensions, - TaskType = taskType - }).ToList() - }; + object? taskType = null; + object? task_type = null; + + // AdditionalProperties is case-insensitive + if (options?.AdditionalProperties.TryGetValue("task_type", out task_type) == true || + options?.AdditionalProperties.TryGetValue("tasktype", out taskType) == true) + { + return (task_type ?? taskType)?.ToString(); + } + } + + return null; + } + + var request = new GoogleAIEmbeddingRequest + { + Requests = [.. data.Select(text => new RequestEmbeddingContent + { + Model = $"models/{modelId}", + Content = new() + { + Parts = + [ + new() + { + Text = text + } + ] + }, + Dimensions = dimensions, + TaskType = GetTaskType(options) + })] + }; + + return request; + } internal sealed class RequestEmbeddingContent { From d9c6125ba23469932f51b2e9a65927cd250bf1c3 Mon Sep 17 00:00:00 2001 From: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Date: Fri, 28 Nov 2025 13:16:26 +0000 Subject: [PATCH 5/5] Improve XmlDoc --- .../GoogleAITextEmbeddingGenerationService.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs index 6958105f3ee3..43470e9f9b82 100644 --- a/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs +++ b/dotnet/src/Connectors/Connectors.Google/Services/GoogleAITextEmbeddingGenerationService.cs @@ -73,14 +73,21 @@ public Task>> GenerateEmbeddingsAsync( } /// - /// Generates embeddings for the specified input text, allowing additional configuration - /// via (e.g., specifying the Google task type). + /// Generates an embedding from the given . /// - /// The input text collection to generate embeddings for. - /// Embedding generation options (e.g., task_type). - /// Optional Kernel instance. - /// Token for cancelling the request. - /// A list of generated embeddings. + /// List of strings to generate embeddings for + /// Additional options for embedding generation + /// The containing services, plugins, and other state for use throughout the operation. + /// The to monitor for cancellation requests. The default is . + /// List of embeddings + /// + /// + /// The parameter can be used to override default settings such as and + /// + /// + /// Additionally a key/value of "taskType" can be provided in the for specific embedding tasks. + /// + /// public Task>> GenerateEmbeddingsAsync( IList data, EmbeddingGenerationOptions? options,