From c2977078d9a0ad3dccb353fd9cd52d33ce103c62 Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Fri, 16 Oct 2026 21:08:16 -0400 Subject: [PATCH] fix(models): map Anthropic model_context_window_exceeded to MAX_TOKENS Claude Sonnet 4.5 and newer end a turn with stop_reason "model_context_window_exceeded" when generation fills the context window. It was missing from the stop reason mapping and fell through to FINISH_REASON_UNSPECIFIED, so a truncated response looked like one without a reported finish reason, and callers that check for MAX_TOKENS (e.g. the LLM classifier) accepted the truncated output. --- src/google/adk/models/anthropic_llm.py | 7 ++++++- tests/unittests/models/test_anthropic_llm.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py index 3edbaeb3be0..54d2066ce05 100644 --- a/src/google/adk/models/anthropic_llm.py +++ b/src/google/adk/models/anthropic_llm.py @@ -330,12 +330,17 @@ def to_claude_role(role: Optional[str]) -> Literal["user", "assistant"]: # Mapping of Anthropic stop_reason strings to FinishReason enum values -_STOP_REASON_MAPPING: dict[anthropic_types.StopReason, types.FinishReason] = { +# Keyed by str: "model_context_window_exceeded" is newer than the minimum +# supported anthropic SDK's StopReason literal. +_STOP_REASON_MAPPING: dict[str, types.FinishReason] = { "end_turn": types.FinishReason.STOP, "stop_sequence": types.FinishReason.STOP, "tool_use": types.FinishReason.STOP, "pause_turn": types.FinishReason.STOP, "max_tokens": types.FinishReason.MAX_TOKENS, + # Generation stopped because the context window filled up, so the output + # is truncated just as with max_tokens. + "model_context_window_exceeded": types.FinishReason.MAX_TOKENS, "refusal": types.FinishReason.SAFETY, } diff --git a/tests/unittests/models/test_anthropic_llm.py b/tests/unittests/models/test_anthropic_llm.py index b7a54248f33..f57e7bcd689 100644 --- a/tests/unittests/models/test_anthropic_llm.py +++ b/tests/unittests/models/test_anthropic_llm.py @@ -4243,6 +4243,7 @@ async def mock_coro(): ("stop_sequence", types.FinishReason.STOP), ("tool_use", types.FinishReason.STOP), ("max_tokens", types.FinishReason.MAX_TOKENS), + ("model_context_window_exceeded", types.FinishReason.MAX_TOKENS), ("pause_turn", types.FinishReason.STOP), ("refusal", types.FinishReason.SAFETY), (None, None),