From 4aafeedc6f44504b5dfd43b7d42a62ffb6e0d88d Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Fri, 31 Jul 2026 14:07:29 -0700 Subject: [PATCH 1/2] Test: pin the Gemini-API label stripping in preprocessRequest preprocessRequest clears GenerateContentConfig.labels for the Gemini API backend and leaves it alone for Vertex AI, but neither branch had any test coverage. Add both cases so the asymmetry is pinned before the surrounding code is touched. --- core/test/models/google_llm_test.ts | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/core/test/models/google_llm_test.ts b/core/test/models/google_llm_test.ts index 1d745c403..bb05a4b81 100644 --- a/core/test/models/google_llm_test.ts +++ b/core/test/models/google_llm_test.ts @@ -403,6 +403,52 @@ describe('GoogleLlm', () => { await expect(generator.next()).rejects.toThrow('Aborted'); }); + + it('should clear labels on the request config for the Gemini API backend', async () => { + const llm = new TestGemini({apiKey: 'test-key'}); + const generateContentMock = + vi.fn(); + generateContentMock.mockResolvedValue(new GenerateContentResponse()); + llm.apiClient.models.generateContent = generateContentMock; + + const llmRequest: LlmRequest = { + contents: [{role: 'user', parts: [{text: 'hello'}]}], + config: {labels: {'adk-agent-name': 'agent'}}, + liveConnectConfig: {}, + toolsDict: {}, + }; + + await llm.generateContentAsync(llmRequest).next(); + + const [callArg] = generateContentMock.mock.calls[0]; + expect(callArg.config?.labels).toBeUndefined(); + expect(llmRequest.config?.labels).toBeUndefined(); + }); + + it('should preserve labels on the request config for the Vertex AI backend', async () => { + const llm = new TestGemini({ + vertexai: true, + project: 'test-project', + location: 'us-central1', + }); + const generateContentMock = + vi.fn(); + generateContentMock.mockResolvedValue(new GenerateContentResponse()); + llm.apiClient.models.generateContent = generateContentMock; + + const llmRequest: LlmRequest = { + contents: [{role: 'user', parts: [{text: 'hello'}]}], + config: {labels: {'adk-agent-name': 'agent'}}, + liveConnectConfig: {}, + toolsDict: {}, + }; + + await llm.generateContentAsync(llmRequest).next(); + + const [callArg] = generateContentMock.mock.calls[0]; + expect(callArg.config?.labels).toEqual({'adk-agent-name': 'agent'}); + expect(llmRequest.config?.labels).toEqual({'adk-agent-name': 'agent'}); + }); }); describe('generateContentAsync streaming', () => { From 87e36e06e1b1439978bfe1b77b27b049e0dd4c79 Mon Sep 17 00:00:00 2001 From: Amaad Martin Date: Fri, 31 Jul 2026 14:07:38 -0700 Subject: [PATCH 2/2] Fix: drop the unnecessary structural cast when clearing labels in Gemini.preprocessRequest @google/genai already declares GenerateContentConfig.labels?: Record, so `(llmRequest.config as {labels?: unknown}).labels = undefined` asserted a weaker type than the one the compiler had. Write the declared property directly, matching the uncast sibling write in llm_agent.ts. No runtime change: labels is still assigned undefined rather than deleted, preserving parity with adk-python's `llm_request.config.labels = None`. --- core/src/models/google_llm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/models/google_llm.ts b/core/src/models/google_llm.ts index 0bc30049e..80958818c 100644 --- a/core/src/models/google_llm.ts +++ b/core/src/models/google_llm.ts @@ -338,7 +338,7 @@ export class Gemini extends BaseLlm { if (llmRequest.config) { // Using API key from Google AI Studio to call model doesn't support // labels. - (llmRequest.config as {labels?: unknown}).labels = undefined; + llmRequest.config.labels = undefined; } if (llmRequest.contents) { for (const content of llmRequest.contents) {