From 4fe3523e9b0b086df5b5b419197adcf10c7b6af4 Mon Sep 17 00:00:00 2001 From: Adit Sheth Date: Wed, 28 May 2025 11:36:45 -0700 Subject: [PATCH 1/2] Fixed bug 12257. --- .../Core/Gemini/GeminiRequestTests.cs | 38 +++++++++++++++++++ ...oogleAIGeminiChatCompletionServiceTests.cs | 29 ++++++++++++++ ...ertexAIGeminiChatCompletionServiceTests.cs | 29 ++++++++++++++ .../Core/Gemini/Models/GeminiRequest.cs | 5 +++ .../GeminiPromptExecutionSettings.cs | 17 +++++++++ 5 files changed, 118 insertions(+) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs index 58185a691ab5..913878b8c59d 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/GeminiRequestTests.cs @@ -521,6 +521,24 @@ public void CachedContentFromPromptReturnsAsExpected() Assert.Equal(executionSettings.CachedContent, request.CachedContent); } + [Fact] + public void LabelsFromPromptReturnsAsExpected() + { + // Arrange + var prompt = "prompt-example"; + var executionSettings = new GeminiPromptExecutionSettings + { + Labels = "Key1:Value1" + }; + + // Act + var request = GeminiRequest.FromPromptAndExecutionSettings(prompt, executionSettings); + + // Assert + Assert.NotNull(request.Configuration); + Assert.Equal(executionSettings.Labels, request.Labels); + } + [Fact] public void CachedContentFromChatHistoryReturnsAsExpected() { @@ -541,6 +559,26 @@ public void CachedContentFromChatHistoryReturnsAsExpected() Assert.Equal(executionSettings.CachedContent, request.CachedContent); } + [Fact] + public void LabelsFromChatHistoryReturnsAsExpected() + { + // Arrange + ChatHistory chatHistory = []; + chatHistory.AddUserMessage("user-message"); + chatHistory.AddAssistantMessage("assist-message"); + chatHistory.AddUserMessage("user-message2"); + var executionSettings = new GeminiPromptExecutionSettings + { + Labels = "Key1:Value1" + }; + + // Act + var request = GeminiRequest.FromChatHistoryAndExecutionSettings(chatHistory, executionSettings); + + // Assert + Assert.Equal(executionSettings.Labels, request.Labels); + } + [Fact] public void ResponseSchemaConvertsNullableTypesToOpenApiFormat() { diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs index 46cfc91f8946..580c65bb07d1 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs @@ -69,6 +69,35 @@ public async Task RequestCachedContentWorksCorrectlyAsync(string? cachedContent) } } + [Theory] + [InlineData(null)] + [InlineData("key:value")] + [InlineData("")] + public async Task RequestLabelsWorksCorrectlyAsync(string? labels) + { + // Arrange + string model = "fake-model"; + var sut = new GoogleAIGeminiChatCompletionService(model, "key", httpClient: this._httpClient); + + // Act + var result = await sut.GetChatMessageContentAsync("my prompt", new GeminiPromptExecutionSettings { Labels = labels}); + + // Assert + Assert.NotNull(result); + Assert.NotNull(this._messageHandlerStub.RequestContent); + + var requestBody = UTF8Encoding.UTF8.GetString(this._messageHandlerStub.RequestContent); + if (labels is not null) + { + Assert.Contains($"\"labels\":\"{labels}\"", requestBody); + } + else + { + // Then no quality is provided, it should not be included in the request body + Assert.DoesNotContain("labels", requestBody); + } + } + [Theory] [InlineData(null, false)] [InlineData(0, true)] diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/VertexAIGeminiChatCompletionServiceTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/VertexAIGeminiChatCompletionServiceTests.cs index e4cba485563e..9140924bc011 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/VertexAIGeminiChatCompletionServiceTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/VertexAIGeminiChatCompletionServiceTests.cs @@ -80,6 +80,35 @@ public async Task RequestCachedContentWorksCorrectlyAsync(string? cachedContent) } } + [Theory] + [InlineData(null)] + [InlineData("key:value")] + [InlineData("")] + public async Task RequestLabelsWorksCorrectlyAsync(string? labels) + { + // Arrange + string model = "fake-model"; + var sut = new GoogleAIGeminiChatCompletionService(model, "key", httpClient: this._httpClient); + + // Act + var result = await sut.GetChatMessageContentAsync("my prompt", new GeminiPromptExecutionSettings { Labels = labels }); + + // Assert + Assert.NotNull(result); + Assert.NotNull(this._messageHandlerStub.RequestContent); + + var requestBody = UTF8Encoding.UTF8.GetString(this._messageHandlerStub.RequestContent); + if (labels is not null) + { + Assert.Contains($"\"labels\":\"{labels}\"", requestBody); + } + else + { + // Then no quality is provided, it should not be included in the request body + Assert.DoesNotContain("labels", requestBody); + } + } + [Theory] [InlineData(null, false)] [InlineData(0, true)] diff --git a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs index c5152ae979e5..46405c991ada 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiRequest.cs @@ -46,6 +46,10 @@ internal sealed class GeminiRequest [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? CachedContent { get; set; } + [JsonPropertyName("labels")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Labels { get; set; } + public void AddFunction(GeminiFunction function) { // NOTE: Currently Gemini only supports one tool i.e. function calling. @@ -430,6 +434,7 @@ private static void AddSafetySettings(GeminiPromptExecutionSettings executionSet private static void AddAdditionalBodyFields(GeminiPromptExecutionSettings executionSettings, GeminiRequest request) { request.CachedContent = executionSettings.CachedContent; + request.Labels = executionSettings.Labels; if (executionSettings.ThinkingConfig is not null) { request.Configuration ??= new ConfigurationElement(); diff --git a/dotnet/src/Connectors/Connectors.Google/GeminiPromptExecutionSettings.cs b/dotnet/src/Connectors/Connectors.Google/GeminiPromptExecutionSettings.cs index b3473db1fe63..c4d4514feb5f 100644 --- a/dotnet/src/Connectors/Connectors.Google/GeminiPromptExecutionSettings.cs +++ b/dotnet/src/Connectors/Connectors.Google/GeminiPromptExecutionSettings.cs @@ -28,6 +28,7 @@ public sealed class GeminiPromptExecutionSettings : PromptExecutionSettings private string? _responseMimeType; private object? _responseSchema; private string? _cachedContent; + private string? _labels; private IList? _safetySettings; private GeminiToolCallBehavior? _toolCallBehavior; private GeminiThinkingConfig? _thinkingConfig; @@ -147,6 +148,22 @@ public IList? SafetySettings } } + /// + /// Gets or sets the labels. + /// + /// + /// Metadata that can be added to the API call in the format of key-value pairs. + /// + public string? Labels + { + get => this._labels; + set + { + this.ThrowIfFrozen(); + this._labels = value; + } + } + /// /// Gets or sets the behavior for how tool calls are handled. /// From 6584bd1c978edf8cb678e612a158e70089f50b92 Mon Sep 17 00:00:00 2001 From: Adit Sheth Date: Wed, 28 May 2025 14:31:06 -0700 Subject: [PATCH 2/2] Fixed space. --- .../Services/GoogleAIGeminiChatCompletionServiceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs index 580c65bb07d1..6291fa898b99 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Services/GoogleAIGeminiChatCompletionServiceTests.cs @@ -80,7 +80,7 @@ public async Task RequestLabelsWorksCorrectlyAsync(string? labels) var sut = new GoogleAIGeminiChatCompletionService(model, "key", httpClient: this._httpClient); // Act - var result = await sut.GetChatMessageContentAsync("my prompt", new GeminiPromptExecutionSettings { Labels = labels}); + var result = await sut.GetChatMessageContentAsync("my prompt", new GeminiPromptExecutionSettings { Labels = labels }); // Assert Assert.NotNull(result);