From 4c1b0c56ba5cf7cd030173374a8354e3e7bc7c42 Mon Sep 17 00:00:00 2001 From: gioboa Date: Tue, 1 Sep 2026 20:43:17 +0200 Subject: [PATCH] fix: strip function IDs from generateContent --- src/google/adk/models/google_llm.py | 13 ++++++ tests/unittests/models/test_google_llm.py | 55 +++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/google/adk/models/google_llm.py b/src/google/adk/models/google_llm.py index 1ce781fcf0..1a818d1b00 100644 --- a/src/google/adk/models/google_llm.py +++ b/src/google/adk/models/google_llm.py @@ -71,6 +71,16 @@ """ +def _remove_function_call_ids(contents: list[types.Content]) -> None: + """Removes unsupported function call and response ids in place.""" + for content in contents: + for part in content.parts or []: + if part.function_call and part.function_call.id: + part.function_call.id = None + if part.function_response and part.function_response.id: + part.function_response.id = None + + class _ResourceExhaustedError(ClientError): """Represents a resources exhausted error received from the Model.""" @@ -235,6 +245,9 @@ async def generate_content_async( if model is None: raise ValueError('Gemini requests require a model name.') + if not self.use_interactions_api: + _remove_function_call_ids(llm_request.contents) + # Handle context caching if configured cache_metadata = None cache_manager = None diff --git a/tests/unittests/models/test_google_llm.py b/tests/unittests/models/test_google_llm.py index 91a3445701..2066d51657 100644 --- a/tests/unittests/models/test_google_llm.py +++ b/tests/unittests/models/test_google_llm.py @@ -453,6 +453,61 @@ async def mock_coro(): mock_client.aio.models.generate_content.assert_called_once() +@pytest.mark.asyncio +async def test_generate_content_async_strips_model_function_call_ids( + gemini_llm, generate_content_response +): + """generateContent omits model-supplied function ids.""" + function_call_id = "call_240342" + llm_request = LlmRequest( + model="gemini-2.5-flash", + contents=[ + Content( + role="model", + parts=[ + Part( + function_call=types.FunctionCall( + id=function_call_id, + name="test_tool", + args={"x": 1}, + ) + ) + ], + ), + Content( + role="user", + parts=[ + Part( + function_response=types.FunctionResponse( + id=function_call_id, + name="test_tool", + response={"result": 2}, + ) + ) + ], + ), + ], + ) + + with mock.patch.object(gemini_llm, "api_client") as mock_client: + + async def mock_coro(): + return generate_content_response + + mock_client.aio.models.generate_content.return_value = mock_coro() + + _ = [ + response + async for response in gemini_llm.generate_content_async(llm_request) + ] + + sent_contents = mock_client.aio.models.generate_content.call_args.kwargs[ + "contents" + ] + assert sent_contents[0].parts[0].function_call.id is None + assert sent_contents[1].parts[0].function_response.id is None + + @pytest.mark.asyncio async def test_generate_content_async_stream(gemini_llm, llm_request): with mock.patch.object(gemini_llm, "api_client") as mock_client: