import asyncio from typing import Any, Dict, cast from collections.abc import Callable from anthropic.types.beta import ( BetaContentBlock, BetaContentBlockParam, BetaImageBlockParam, BetaMessage, BetaMessageParam, BetaTextBlockParam, BetaToolResultBlockParam, ) from anthropic.types import TextBlock from anthropic.types.beta import BetaMessage, BetaTextBlock, BetaToolUseBlock from tools import ComputerTool, ToolCollection, ToolResult class AnthropicExecutor: def __init__( self, output_callback: Callable[[BetaContentBlockParam], None], tool_output_callback: Callable[[Any, str], None], ): self.tool_collection = ToolCollection( ComputerTool() ) self.output_callback = output_callback self.tool_output_callback = tool_output_callback def __call__(self, response: BetaMessage, messages: list[BetaMessageParam]): new_message = { "role": "assistant", "content": cast(list[BetaContentBlockParam], response.content), } if new_message not in messages: messages.append(new_message) else: print("new_message already in messages, there are duplicates.") tool_result_content: list[BetaToolResultBlockParam] = [] for content_block in cast(list[BetaContentBlock], response.content): self.output_callback(content_block, sender="bot") # Execute the tool if content_block.type == "tool_use": # Run the asynchronous tool execution in a synchronous context result = asyncio.run(self.tool_collection.run( name=content_block.name, tool_input=cast(dict[str, Any], content_block.input), )) self.output_callback(result, sender="bot") tool_result_content.append( _make_api_tool_result(result, content_block.id) ) # self.tool_output_callback(result, content_block.id) # Craft messages based on the content_block # Note: to display the messages in the gradio, you should organize the messages in the following way (user message, bot message) display_messages = _message_display_callback(messages) # display_messages = [] # Send the messages to the gradio for user_msg, bot_msg in display_messages: # yield [user_msg, bot_msg], tool_result_content yield [None, None], tool_result_content if not tool_result_content: return messages return tool_result_content def _message_display_callback(messages): display_messages = [] for msg in messages: try: if isinstance(msg["content"][0], TextBlock): display_messages.append((msg["content"][0].text, None)) # User message elif isinstance(msg["content"][0], BetaTextBlock): display_messages.append((None, msg["content"][0].text)) # Bot message elif isinstance(msg["content"][0], BetaToolUseBlock): display_messages.append((None, f"Tool Use: {msg['content'][0].name}\nInput: {msg['content'][0].input}")) # Bot message elif isinstance(msg["content"][0], Dict) and msg["content"][0]["content"][-1]["type"] == "image": display_messages.append((None, f'')) # Bot message else: print(msg["content"][0]) except Exception as e: print("error", e) pass return display_messages def _make_api_tool_result( result: ToolResult, tool_use_id: str ) -> BetaToolResultBlockParam: """Convert an agent ToolResult to an API ToolResultBlockParam.""" tool_result_content: list[BetaTextBlockParam | BetaImageBlockParam] | str = [] is_error = False if result.error: is_error = True tool_result_content = _maybe_prepend_system_tool_result(result, result.error) else: if result.output: tool_result_content.append( { "type": "text", "text": _maybe_prepend_system_tool_result(result, result.output), } ) if result.base64_image: tool_result_content.append( { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": result.base64_image, }, } ) return { "type": "tool_result", "content": tool_result_content, "tool_use_id": tool_use_id, "is_error": is_error, } def _maybe_prepend_system_tool_result(result: ToolResult, result_text: str): if result.system: result_text = f"{result.system}\n{result_text}" return result_text