From 2f655f0c67940a924b5818cebcb83fb4a3565dcb Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Thu, 10 Jul 2025 16:16:56 -0700 Subject: [PATCH] fix: Add support for code execution result and skip inline data in anthropic llm ``` blaze run assistant/lamda/bard/scrape/vertex/tools:use_vertex_agent -- --prompt_collection_id=cab_v0p7@3 --agent_name=vertex_1p_agent --kernel_id=vertex_agent:claude-sonnet-4@20250514 --dynamically_inject_tools=True --add_final_answer_tag=True --example_ids=plot-line-004_0.5_new_1 ``` TODO: https://evalhub.corp.google.com/runs/Uf5en14gqmK-R/links PiperOrigin-RevId: 781724544 --- src/google/adk/models/anthropic_llm.py | 49 +++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py index a3a0e0962..15d78d724 100644 --- a/src/google/adk/models/anthropic_llm.py +++ b/src/google/adk/models/anthropic_llm.py @@ -16,6 +16,7 @@ from __future__ import annotations +import base64 from functools import cached_property import logging import os @@ -45,7 +46,7 @@ logger = logging.getLogger("google_adk." + __name__) -MAX_TOKEN = 1024 +MAX_TOKEN = 8192 class ClaudeRequest(BaseModel): @@ -70,6 +71,14 @@ def to_google_genai_finish_reason( return "FINISH_REASON_UNSPECIFIED" +def _is_image_part(part: types.Part) -> bool: + return ( + part.inline_data + and part.inline_data.mime_type + and part.inline_data.mime_type.startswith("image") + ) + + def part_to_message_block( part: types.Part, ) -> Union[ @@ -80,7 +89,7 @@ def part_to_message_block( ]: if part.text: return anthropic_types.TextBlockParam(text=part.text, type="text") - if part.function_call: + elif part.function_call: assert part.function_call.name return anthropic_types.ToolUseBlockParam( @@ -89,7 +98,7 @@ def part_to_message_block( input=part.function_call.args, type="tool_use", ) - if part.function_response: + elif part.function_response: content = "" if ( "result" in part.function_response.response @@ -105,15 +114,45 @@ def part_to_message_block( content=content, is_error=False, ) - raise NotImplementedError("Not supported yet.") + elif _is_image_part(part): + data = base64.b64encode(part.inline_data.data).decode() + return anthropic_types.ImageBlockParam( + type="image", + source=dict( + type="base64", media_type=part.inline_data.mime_type, data=data + ), + ) + elif part.executable_code: + return anthropic_types.TextBlockParam( + type="text", + text="Code:```python\n" + part.executable_code.code + "\n```", + ) + elif part.code_execution_result: + return anthropic_types.TextBlockParam( + text="Execution Result:```code_output\n" + + part.code_execution_result.output + + "\n```", + type="text", + ) + + raise NotImplementedError(f"Not supported yet: {part}") def content_to_message_param( content: types.Content, ) -> anthropic_types.MessageParam: + message_block = [] + for part in content.parts or []: + # Image data is not supported in Claude for model turns. + if _is_image_part(part): + logger.warning("Image data is not supported in Claude for model turns.") + continue + + message_block.append(part_to_message_block(part)) + return { "role": to_claude_role(content.role), - "content": [part_to_message_block(part) for part in content.parts or []], + "content": message_block, }