From 05055afcf7a641a15fbb6b6fdcc5daa742211683 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Thu, 2 Oct 2025 15:24:15 +0200 Subject: [PATCH 1/2] Add Gemini usage metadata CachedContentTokenCount and ThoughtsTokenCount --- .../Clients/GeminiChatGenerationTests.cs | 4 +++ .../Clients/GeminiChatStreamingTests.cs | 4 +++ .../Clients/GeminiChatCompletionClient.cs | 2 ++ .../Core/Gemini/Models/GeminiResponse.cs | 12 +++++++++ .../Models/Gemini/GeminiMetadata.cs | 26 ++++++++++++++++--- .../Gemini/GeminiChatCompletionTests.cs | 6 +++++ 6 files changed, 50 insertions(+), 4 deletions(-) diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatGenerationTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatGenerationTests.cs index 2c19b210b2c8..82d804d500b3 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatGenerationTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatGenerationTests.cs @@ -164,6 +164,8 @@ public async Task ShouldReturnValidGeminiMetadataAsync() } Assert.Equal(testDataResponse.UsageMetadata!.PromptTokenCount, metadata.PromptTokenCount); + Assert.Equal(testDataResponse.UsageMetadata!.CachedContentTokenCount, metadata.CachedContentTokenCount); + Assert.Equal(testDataResponse.UsageMetadata!.ThoughtsTokenCount, metadata.ThoughtsTokenCount); Assert.Equal(testDataCandidate.TokenCount, metadata.CurrentCandidateTokenCount); Assert.Equal(testDataResponse.UsageMetadata.CandidatesTokenCount, metadata.CandidatesTokenCount); Assert.Equal(testDataResponse.UsageMetadata.TotalTokenCount, metadata.TotalTokenCount); @@ -207,6 +209,8 @@ public async Task ShouldReturnValidDictionaryMetadataAsync() } Assert.Equal(testDataResponse.UsageMetadata!.PromptTokenCount, metadata[nameof(GeminiMetadata.PromptTokenCount)]); + Assert.Equal(testDataResponse.UsageMetadata!.CachedContentTokenCount, metadata[nameof(GeminiMetadata.CachedContentTokenCount)]); + Assert.Equal(testDataResponse.UsageMetadata!.ThoughtsTokenCount, metadata[nameof(GeminiMetadata.ThoughtsTokenCount)]); Assert.Equal(testDataCandidate.TokenCount, metadata[nameof(GeminiMetadata.CurrentCandidateTokenCount)]); Assert.Equal(testDataResponse.UsageMetadata.CandidatesTokenCount, metadata[nameof(GeminiMetadata.CandidatesTokenCount)]); Assert.Equal(testDataResponse.UsageMetadata.TotalTokenCount, metadata[nameof(GeminiMetadata.TotalTokenCount)]); diff --git a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatStreamingTests.cs b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatStreamingTests.cs index 692da9146b04..808ce2eb6fc4 100644 --- a/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatStreamingTests.cs +++ b/dotnet/src/Connectors/Connectors.Google.UnitTests/Core/Gemini/Clients/GeminiChatStreamingTests.cs @@ -174,6 +174,8 @@ public async Task ShouldReturnValidGeminiMetadataAsync() } Assert.Equal(testDataResponse.UsageMetadata!.PromptTokenCount, metadata.PromptTokenCount); + Assert.Equal(testDataResponse.UsageMetadata!.CachedContentTokenCount, metadata.CachedContentTokenCount); + Assert.Equal(testDataResponse.UsageMetadata!.ThoughtsTokenCount, metadata.ThoughtsTokenCount); Assert.Equal(testDataCandidate.TokenCount, metadata.CurrentCandidateTokenCount); Assert.Equal(testDataResponse.UsageMetadata.CandidatesTokenCount, metadata.CandidatesTokenCount); Assert.Equal(testDataResponse.UsageMetadata.TotalTokenCount, metadata.TotalTokenCount); @@ -218,6 +220,8 @@ public async Task ShouldReturnValidDictionaryMetadataAsync() } Assert.Equal(testDataResponse.UsageMetadata!.PromptTokenCount, metadata[nameof(GeminiMetadata.PromptTokenCount)]); + Assert.Equal(testDataResponse.UsageMetadata!.CachedContentTokenCount, metadata[nameof(GeminiMetadata.CachedContentTokenCount)]); + Assert.Equal(testDataResponse.UsageMetadata!.ThoughtsTokenCount, metadata[nameof(GeminiMetadata.ThoughtsTokenCount)]); Assert.Equal(testDataCandidate.TokenCount, metadata[nameof(GeminiMetadata.CurrentCandidateTokenCount)]); Assert.Equal(testDataResponse.UsageMetadata.CandidatesTokenCount, metadata[nameof(GeminiMetadata.CandidatesTokenCount)]); Assert.Equal(testDataResponse.UsageMetadata.TotalTokenCount, metadata[nameof(GeminiMetadata.TotalTokenCount)]); diff --git a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Clients/GeminiChatCompletionClient.cs b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Clients/GeminiChatCompletionClient.cs index 3d52a92f8825..4b81ba03cf2a 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Clients/GeminiChatCompletionClient.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Clients/GeminiChatCompletionClient.cs @@ -685,6 +685,8 @@ private static GeminiMetadata GetResponseMetadata( FinishReason = candidate.FinishReason, Index = candidate.Index, PromptTokenCount = geminiResponse.UsageMetadata?.PromptTokenCount ?? 0, + CachedContentTokenCount = geminiResponse.UsageMetadata?.CachedContentTokenCount ?? 0, + ThoughtsTokenCount = geminiResponse.UsageMetadata?.ThoughtsTokenCount ?? 0, CurrentCandidateTokenCount = candidate.TokenCount, CandidatesTokenCount = geminiResponse.UsageMetadata?.CandidatesTokenCount ?? 0, TotalTokenCount = geminiResponse.UsageMetadata?.TotalTokenCount ?? 0, diff --git a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiResponse.cs b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiResponse.cs index 5a028c459a14..04802e3dfd15 100644 --- a/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiResponse.cs +++ b/dotnet/src/Connectors/Connectors.Google/Core/Gemini/Models/GeminiResponse.cs @@ -39,6 +39,18 @@ internal sealed class UsageMetadataElement [JsonPropertyName("promptTokenCount")] public int PromptTokenCount { get; set; } + /// + /// Gets the number of cached content tokens used. + /// + [JsonPropertyName("cachedContentTokenCount")] + public int CachedContentTokenCount { get; set; } + + /// + /// Gets the number of thoughts tokens used. + /// + [JsonPropertyName("thoughtsTokenCount")] + public int ThoughtsTokenCount { get; set; } + /// /// Gets the count of used tokens for all candidates. /// diff --git a/dotnet/src/Connectors/Connectors.Google/Models/Gemini/GeminiMetadata.cs b/dotnet/src/Connectors/Connectors.Google/Models/Gemini/GeminiMetadata.cs index bd03d4cba9ea..1765cebb6ce5 100644 --- a/dotnet/src/Connectors/Connectors.Google/Models/Gemini/GeminiMetadata.cs +++ b/dotnet/src/Connectors/Connectors.Google/Models/Gemini/GeminiMetadata.cs @@ -44,12 +44,21 @@ public int PromptTokenCount } /// - /// The count of token in the current candidate. + /// The count of cached content tokens. /// - public int CurrentCandidateTokenCount + public int CachedContentTokenCount { - get => (this.GetValueFromDictionary(nameof(this.CurrentCandidateTokenCount)) as int?) ?? 0; - internal init => this.SetValueInDictionary(value, nameof(this.CurrentCandidateTokenCount)); + get => (this.GetValueFromDictionary(nameof(this.CachedContentTokenCount)) as int?) ?? 0; + internal init => this.SetValueInDictionary(value, nameof(this.CachedContentTokenCount)); + } + + /// + /// The count of thoughts tokens. + /// + public int ThoughtsTokenCount + { + get => (this.GetValueFromDictionary(nameof(this.ThoughtsTokenCount)) as int?) ?? 0; + internal init => this.SetValueInDictionary(value, nameof(this.ThoughtsTokenCount)); } /// @@ -61,6 +70,15 @@ public int CandidatesTokenCount internal init => this.SetValueInDictionary(value, nameof(this.CandidatesTokenCount)); } + /// + /// The count of token in the current candidate. + /// + public int CurrentCandidateTokenCount + { + get => (this.GetValueFromDictionary(nameof(this.CurrentCandidateTokenCount)) as int?) ?? 0; + internal init => this.SetValueInDictionary(value, nameof(this.CurrentCandidateTokenCount)); + } + /// /// The total count of tokens (prompt + total candidates token count). /// diff --git a/dotnet/src/IntegrationTests/Connectors/Google/Gemini/GeminiChatCompletionTests.cs b/dotnet/src/IntegrationTests/Connectors/Google/Gemini/GeminiChatCompletionTests.cs index 7645d9cf107e..20f7e7f6eee3 100644 --- a/dotnet/src/IntegrationTests/Connectors/Google/Gemini/GeminiChatCompletionTests.cs +++ b/dotnet/src/IntegrationTests/Connectors/Google/Gemini/GeminiChatCompletionTests.cs @@ -409,6 +409,8 @@ public async Task ChatGenerationReturnsUsedTokensAsync(ServiceType serviceType) Assert.True(geminiMetadata.CandidatesTokenCount > 0); Assert.True(geminiMetadata.PromptTokenCount > 0); Assert.True(geminiMetadata.CurrentCandidateTokenCount > 0); + Assert.True(geminiMetadata.CachedContentTokenCount > 0); + Assert.True(geminiMetadata.ThoughtsTokenCount > 0); } [RetryTheory] @@ -433,10 +435,14 @@ public async Task ChatStreamingReturnsUsedTokensAsync(ServiceType serviceType) this.Output.WriteLine($"TotalTokenCount: {geminiMetadata.TotalTokenCount}"); this.Output.WriteLine($"CandidatesTokenCount: {geminiMetadata.CandidatesTokenCount}"); this.Output.WriteLine($"PromptTokenCount: {geminiMetadata.PromptTokenCount}"); + this.Output.WriteLine($"CachedContentTokenCount: {geminiMetadata.CachedContentTokenCount}"); + this.Output.WriteLine($"ThoughtsTokenCount: {geminiMetadata.ThoughtsTokenCount}"); this.Output.WriteLine($"CurrentCandidateTokenCount: {geminiMetadata.CurrentCandidateTokenCount}"); Assert.True(geminiMetadata.TotalTokenCount > 0); Assert.True(geminiMetadata.CandidatesTokenCount > 0); Assert.True(geminiMetadata.PromptTokenCount > 0); + Assert.True(geminiMetadata.CachedContentTokenCount > 0); + Assert.True(geminiMetadata.ThoughtsTokenCount > 0); Assert.True(geminiMetadata.CurrentCandidateTokenCount > 0); } From f3f4bba63da4d37f6645c7d67b554dc2d3e8f975 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Wed, 8 Oct 2025 11:05:15 +0200 Subject: [PATCH 2/2] run dotnet format on SK-dotnet.slnx --- dotnet/src/InternalUtilities/src/Diagnostics/ModelDiagnostics.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/src/InternalUtilities/src/Diagnostics/ModelDiagnostics.cs b/dotnet/src/InternalUtilities/src/Diagnostics/ModelDiagnostics.cs index 21504eda40c2..b3045c3e9f5d 100644 --- a/dotnet/src/InternalUtilities/src/Diagnostics/ModelDiagnostics.cs +++ b/dotnet/src/InternalUtilities/src/Diagnostics/ModelDiagnostics.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using System.Text.Json; using Microsoft.SemanticKernel.ChatCompletion;