From dcb670f395215c95a1960f5565fe421b37f8520f Mon Sep 17 00:00:00 2001 From: Mason Daugherty Date: Mon, 8 Dec 2025 15:25:36 -0500 Subject: [PATCH] feat(anthropic): auto append relevant beta headers for computer use (#34117) in addition to documenting it https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool --- libs/model-profiles/uv.lock | 8 +-- .../langchain_anthropic/chat_models.py | 56 +++++++++++++++++++ .../langchain_anthropic/data/_profiles.py | 17 ++++++ .../integration_tests/test_chat_models.py | 41 ++++++++++++++ 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/libs/model-profiles/uv.lock b/libs/model-profiles/uv.lock index 3c61487ad9a..af84abbc60f 100644 --- a/libs/model-profiles/uv.lock +++ b/libs/model-profiles/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10.0, <4.0.0" resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", @@ -459,7 +459,7 @@ wheels = [ [[package]] name = "langchain" -version = "1.0.5" +version = "1.1.0" source = { editable = "../langchain_v1" } dependencies = [ { name = "langchain-core" }, @@ -527,7 +527,7 @@ typing = [ [[package]] name = "langchain-core" -version = "1.0.7" +version = "1.1.0" source = { editable = "../core" } dependencies = [ { name = "jsonpatch" }, @@ -653,7 +653,7 @@ typing = [ [[package]] name = "langchain-openai" -version = "1.0.3" +version = "1.1.0" source = { editable = "../partners/openai" } dependencies = [ { name = "langchain-core" }, diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 13c8c6a54d4..512ffb81aa1 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -138,6 +138,8 @@ _TOOL_TYPE_TO_BETA: dict[str, str] = { "code_execution_20250522": "code-execution-2025-05-22", "code_execution_20250825": "code-execution-2025-08-25", "memory_20250818": "context-management-2025-06-27", + "computer_20250124": "computer-use-2025-01-24", + "computer_20251124": "computer-use-2025-11-24", "tool_search_tool_regex_20251119": "advanced-tool-use-2025-11-20", "tool_search_tool_bm25_20251119": "advanced-tool-use-2025-11-20", } @@ -2531,6 +2533,60 @@ class ChatAnthropic(BaseChatModel): ) ``` + ??? example "Computer use tool" + + Claude supports computer use capabilities, allowing it to interact with + desktop environments through screenshots, mouse control, and keyboard input. + + !!! warning "Execution environment required" + + LangChain handles the API integration, but **you must provide**: + + - A sandboxed computing environment (Docker, VM, etc.) + - A virtual display (e.g., Xvfb) + - Code to execute tool calls (screenshot, clicks, typing) + - An agent loop to pass results back to Claude + + Anthropic provides a [reference implementation](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo). + + !!! note + + Computer use requires: + + - Claude Opus 4.5, Claude 4, or Claude Sonnet 3.7 + - A sandboxed computing environment with virtual display + + See the [Claude docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool) + for setup instructions, model capability, and best practices. + + ```python + from langchain_anthropic import ChatAnthropic + + model = ChatAnthropic(model="claude-sonnet-4-5-20250929") + + # LangChain handles the API call and tool binding + computer_tool = { + "type": "computer_20250124", + "name": "computer", + "display_width_px": 1024, + "display_height_px": 768, + "display_number": 1, + } + + model_with_computer = model.bind_tools([computer_tool]) + response = model_with_computer.invoke("Take a screenshot to see what's on the screen") + + # response.tool_calls contains the action Claude wants to perform + # You must execute this action in your environment and pass the result back + ``` + + !!! note "Automatic beta header" + + The required beta header is automatically appended based on the tool + version. For `computer_20250124` and `computer_20251124`, the respective + `computer-use-2025-01-24` and `computer-use-2025-11-24` beta header is + added automatically. + ??? example "Strict tool use" Strict tool use guarantees that tool names and arguments are validated diff --git a/libs/partners/anthropic/langchain_anthropic/data/_profiles.py b/libs/partners/anthropic/langchain_anthropic/data/_profiles.py index 977464a9943..5d6f748c023 100644 --- a/libs/partners/anthropic/langchain_anthropic/data/_profiles.py +++ b/libs/partners/anthropic/langchain_anthropic/data/_profiles.py @@ -118,6 +118,23 @@ _PROFILES: dict[str, dict[str, Any]] = { "image_tool_message": True, "structured_output": False, }, + "claude-opus-4-5": { + "max_input_tokens": 200000, + "max_output_tokens": 64000, + "image_inputs": True, + "audio_inputs": False, + "video_inputs": False, + "image_outputs": False, + "audio_outputs": False, + "video_outputs": False, + "reasoning_output": True, + "tool_calling": True, + "image_url_inputs": True, + "pdf_inputs": True, + "pdf_tool_message": True, + "image_tool_message": True, + "structured_output": False, + }, "claude-3-opus-20240229": { "max_input_tokens": 200000, "max_output_tokens": 4096, diff --git a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py index 743e39016ec..c79d7edc327 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py @@ -582,6 +582,47 @@ def test_builtin_tools_text_editor() -> None: assert content_blocks[1]["name"] == "str_replace_based_edit_tool" +def test_builtin_tools_computer_use() -> None: + """Test computer use tool integration. + + Beta header should be automatically appended based on tool type. + + This test only verifies tool call generation. + """ + llm = ChatAnthropic( + model="claude-sonnet-4-5-20250929", # type: ignore[call-arg] + ) + tool = { + "type": "computer_20250124", + "name": "computer", + "display_width_px": 1024, + "display_height_px": 768, + "display_number": 1, + } + llm_with_tools = llm.bind_tools([tool]) + response = llm_with_tools.invoke( + "Can you take a screenshot to see what's on the screen?", + ) + assert isinstance(response, AIMessage) + assert response.tool_calls + + content_blocks = response.content_blocks + assert len(content_blocks) >= 2 + assert content_blocks[0]["type"] == "text" + assert content_blocks[0]["text"] + + # Check that we have a tool_call for computer use + tool_call_blocks = [b for b in content_blocks if b["type"] == "tool_call"] + assert len(tool_call_blocks) >= 1 + assert tool_call_blocks[0]["name"] == "computer" + + # Verify tool call has expected action (screenshot in this case) + tool_call = response.tool_calls[0] + assert tool_call["name"] == "computer" + assert "action" in tool_call["args"] + assert tool_call["args"]["action"] == "screenshot" + + class GenerateUsername(BaseModel): """Get a username based on someone's name and hair color."""