Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
a4a9d37
fix(models): Drop stale thought signatures
its-amann Jun 23, 2026
086b57e
Merge branch 'main' into fix-thought-signature-pruning
rohityan Jun 26, 2026
b832dbd
Sync branch with upstream main for PR #6202
its-amann Jun 27, 2026
c3de34f
Merge branch 'main' into fix-thought-signature-pruning
its-amann Jun 28, 2026
32a1160
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 29, 2026
cb0cdb9
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 29, 2026
a9007fb
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 29, 2026
2dc70ee
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 30, 2026
7988a76
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 30, 2026
58d6953
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jun 30, 2026
67bacc2
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 1, 2026
13552ab
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 1, 2026
2ca4589
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 2, 2026
3fc1217
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 2, 2026
962891d
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 2, 2026
bd803c9
Merge remote-tracking branch 'origin/main' into fix-thought-signature…
its-amann Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/google/adk/models/google_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@
"""


def _remove_old_thought_signatures(contents: list[types.Content]) -> None:
"""Keeps only the latest thought signature in Gemini request contents."""
latest_signature_seen = False
for content in reversed(contents):
if not content.parts:
continue
for part in reversed(content.parts):
if part.thought_signature is None:
continue
if latest_signature_seen:
part.thought_signature = None
else:
latest_signature_seen = True


class _ResourceExhaustedError(ClientError):
"""Represents a resources exhausted error received from the Model."""

Expand Down Expand Up @@ -195,6 +210,7 @@ async def generate_content_async(
"""
await self._preprocess_request(llm_request)
self._maybe_append_user_content(llm_request)
_remove_old_thought_signatures(llm_request.contents)

# Handle context caching if configured
cache_metadata = None
Expand Down
47 changes: 47 additions & 0 deletions tests/unittests/models/test_google_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,53 @@ async def mock_coro():
mock_client.aio.models.generate_content.assert_called_once()


@pytest.mark.asyncio
async def test_generate_content_async_keeps_only_latest_thought_signature(
gemini_llm, generate_content_response
):
"""Gemini requests keep only the newest thought signature."""

def _function_call_part(name, signature):
return Part(
function_call=types.FunctionCall(name=name, args={}),
thought_signature=signature,
)

old_part = _function_call_part("first_tool", b"old")
newer_part = _function_call_part("second_tool", b"newer")
latest_part = _function_call_part("third_tool", b"latest")
llm_request = LlmRequest(
model="gemini-2.5-flash",
contents=[
Content(role="model", parts=[old_part]),
Content(role="user", parts=[Part.from_text(text="tool result")]),
Content(role="model", parts=[newer_part, latest_part]),
],
)

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()

responses = [
resp
async for resp in gemini_llm.generate_content_async(
llm_request, stream=False
)
]

assert len(responses) == 1
request_contents = mock_client.aio.models.generate_content.call_args.kwargs[
"contents"
]
assert request_contents[0].parts[0].thought_signature is None
assert request_contents[2].parts[0].thought_signature is None
assert request_contents[2].parts[1].thought_signature == b"latest"


@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:
Expand Down