|
13 | 13 | from .. import ModelHTTPError, UnexpectedModelBehavior, _utils, usage |
14 | 14 | from .._run_context import RunContext |
15 | 15 | from .._utils import guard_tool_call_id as _guard_tool_call_id |
16 | | -from ..builtin_tools import CodeExecutionTool, MCPServerTool, MemoryTool, WebSearchTool |
| 16 | +from ..builtin_tools import CodeExecutionTool, MCPServerTool, MemoryTool, WebFetchTool, WebSearchTool |
17 | 17 | from ..exceptions import ModelAPIError, UserError |
18 | 18 | from ..messages import ( |
19 | 19 | BinaryContent, |
|
67 | 67 | BetaBase64PDFBlockParam, |
68 | 68 | BetaBase64PDFSourceParam, |
69 | 69 | BetaCacheControlEphemeralParam, |
| 70 | + BetaCitationsConfigParam, |
70 | 71 | BetaCitationsDelta, |
71 | 72 | BetaCodeExecutionTool20250522Param, |
72 | 73 | BetaCodeExecutionToolResultBlock, |
|
114 | 115 | BetaToolUnionParam, |
115 | 116 | BetaToolUseBlock, |
116 | 117 | BetaToolUseBlockParam, |
| 118 | + BetaWebFetchTool20250910Param, |
| 119 | + BetaWebFetchToolResultBlock, |
| 120 | + BetaWebFetchToolResultBlockParam, |
117 | 121 | BetaWebSearchTool20250305Param, |
118 | 122 | BetaWebSearchToolResultBlock, |
119 | 123 | BetaWebSearchToolResultBlockContent, |
120 | 124 | BetaWebSearchToolResultBlockParam, |
121 | 125 | BetaWebSearchToolResultBlockParamContentParam, |
122 | 126 | ) |
| 127 | + from anthropic.types.beta.beta_web_fetch_tool_result_block_param import ( |
| 128 | + Content as WebFetchToolResultBlockParamContent, |
| 129 | + ) |
123 | 130 | from anthropic.types.beta.beta_web_search_tool_20250305_param import UserLocation |
124 | 131 | from anthropic.types.model_param import ModelParam |
125 | 132 |
|
@@ -423,6 +430,8 @@ def _process_response(self, response: BetaMessage) -> ModelResponse: |
423 | 430 | items.append(_map_web_search_tool_result_block(item, self.system)) |
424 | 431 | elif isinstance(item, BetaCodeExecutionToolResultBlock): |
425 | 432 | items.append(_map_code_execution_tool_result_block(item, self.system)) |
| 433 | + elif isinstance(item, BetaWebFetchToolResultBlock): |
| 434 | + items.append(_map_web_fetch_tool_result_block(item, self.system)) |
426 | 435 | elif isinstance(item, BetaRedactedThinkingBlock): |
427 | 436 | items.append( |
428 | 437 | ThinkingPart(id='redacted_thinking', content='', signature=item.data, provider_name=self.system) |
@@ -518,6 +527,20 @@ def _add_builtin_tools( |
518 | 527 | elif isinstance(tool, CodeExecutionTool): # pragma: no branch |
519 | 528 | tools.append(BetaCodeExecutionTool20250522Param(name='code_execution', type='code_execution_20250522')) |
520 | 529 | beta_features.append('code-execution-2025-05-22') |
| 530 | + elif isinstance(tool, WebFetchTool): # pragma: no branch |
| 531 | + citations = BetaCitationsConfigParam(enabled=tool.enable_citations) if tool.enable_citations else None |
| 532 | + tools.append( |
| 533 | + BetaWebFetchTool20250910Param( |
| 534 | + name='web_fetch', |
| 535 | + type='web_fetch_20250910', |
| 536 | + max_uses=tool.max_uses, |
| 537 | + allowed_domains=tool.allowed_domains, |
| 538 | + blocked_domains=tool.blocked_domains, |
| 539 | + citations=citations, |
| 540 | + max_content_tokens=tool.max_content_tokens, |
| 541 | + ) |
| 542 | + ) |
| 543 | + beta_features.append('web-fetch-2025-09-10') |
521 | 544 | elif isinstance(tool, MemoryTool): # pragma: no branch |
522 | 545 | if 'memory' not in model_request_parameters.tool_defs: |
523 | 546 | raise UserError("Built-in `MemoryTool` requires a 'memory' tool to be defined.") |
@@ -627,6 +650,7 @@ async def _map_message( # noqa: C901 |
627 | 650 | | BetaServerToolUseBlockParam |
628 | 651 | | BetaWebSearchToolResultBlockParam |
629 | 652 | | BetaCodeExecutionToolResultBlockParam |
| 653 | + | BetaWebFetchToolResultBlockParam |
630 | 654 | | BetaThinkingBlockParam |
631 | 655 | | BetaRedactedThinkingBlockParam |
632 | 656 | | BetaMCPToolUseBlockParam |
@@ -689,6 +713,14 @@ async def _map_message( # noqa: C901 |
689 | 713 | input=response_part.args_as_dict(), |
690 | 714 | ) |
691 | 715 | assistant_content_params.append(server_tool_use_block_param) |
| 716 | + elif response_part.tool_name == WebFetchTool.kind: |
| 717 | + server_tool_use_block_param = BetaServerToolUseBlockParam( |
| 718 | + id=tool_use_id, |
| 719 | + type='server_tool_use', |
| 720 | + name='web_fetch', |
| 721 | + input=response_part.args_as_dict(), |
| 722 | + ) |
| 723 | + assistant_content_params.append(server_tool_use_block_param) |
692 | 724 | elif ( |
693 | 725 | response_part.tool_name.startswith(MCPServerTool.kind) |
694 | 726 | and (server_id := response_part.tool_name.split(':', 1)[1]) |
@@ -735,6 +767,19 @@ async def _map_message( # noqa: C901 |
735 | 767 | ), |
736 | 768 | ) |
737 | 769 | ) |
| 770 | + elif response_part.tool_name == WebFetchTool.kind and isinstance( |
| 771 | + response_part.content, dict |
| 772 | + ): |
| 773 | + assistant_content_params.append( |
| 774 | + BetaWebFetchToolResultBlockParam( |
| 775 | + tool_use_id=tool_use_id, |
| 776 | + type='web_fetch_tool_result', |
| 777 | + content=cast( |
| 778 | + WebFetchToolResultBlockParamContent, |
| 779 | + response_part.content, # pyright: ignore[reportUnknownMemberType] |
| 780 | + ), |
| 781 | + ) |
| 782 | + ) |
738 | 783 | elif response_part.tool_name.startswith(MCPServerTool.kind) and isinstance( |
739 | 784 | response_part.content, dict |
740 | 785 | ): # pragma: no branch |
@@ -955,6 +1000,11 @@ async def _get_event_iterator(self) -> AsyncIterator[ModelResponseStreamEvent]: |
955 | 1000 | vendor_part_id=event.index, |
956 | 1001 | part=_map_code_execution_tool_result_block(current_block, self.provider_name), |
957 | 1002 | ) |
| 1003 | + elif isinstance(current_block, BetaWebFetchToolResultBlock): # pragma: lax no cover |
| 1004 | + yield self._parts_manager.handle_part( |
| 1005 | + vendor_part_id=event.index, |
| 1006 | + part=_map_web_fetch_tool_result_block(current_block, self.provider_name), |
| 1007 | + ) |
958 | 1008 | elif isinstance(current_block, BetaMCPToolUseBlock): |
959 | 1009 | call_part = _map_mcp_server_use_block(current_block, self.provider_name) |
960 | 1010 | builtin_tool_calls[call_part.tool_call_id] = call_part |
@@ -1061,7 +1111,14 @@ def _map_server_tool_use_block(item: BetaServerToolUseBlock, provider_name: str) |
1061 | 1111 | args=cast(dict[str, Any], item.input) or None, |
1062 | 1112 | tool_call_id=item.id, |
1063 | 1113 | ) |
1064 | | - elif item.name in ('web_fetch', 'bash_code_execution', 'text_editor_code_execution'): # pragma: no cover |
| 1114 | + elif item.name == 'web_fetch': |
| 1115 | + return BuiltinToolCallPart( |
| 1116 | + provider_name=provider_name, |
| 1117 | + tool_name=WebFetchTool.kind, |
| 1118 | + args=cast(dict[str, Any], item.input) or None, |
| 1119 | + tool_call_id=item.id, |
| 1120 | + ) |
| 1121 | + elif item.name in ('bash_code_execution', 'text_editor_code_execution'): # pragma: no cover |
1065 | 1122 | raise NotImplementedError(f'Anthropic built-in tool {item.name!r} is not currently supported.') |
1066 | 1123 | else: |
1067 | 1124 | assert_never(item.name) |
@@ -1097,6 +1154,16 @@ def _map_code_execution_tool_result_block( |
1097 | 1154 | ) |
1098 | 1155 |
|
1099 | 1156 |
|
| 1157 | +def _map_web_fetch_tool_result_block(item: BetaWebFetchToolResultBlock, provider_name: str) -> BuiltinToolReturnPart: |
| 1158 | + return BuiltinToolReturnPart( |
| 1159 | + provider_name=provider_name, |
| 1160 | + tool_name=WebFetchTool.kind, |
| 1161 | + # Store just the content field (BetaWebFetchBlock) which has {content, type, url, retrieved_at} |
| 1162 | + content=item.content.model_dump(mode='json'), |
| 1163 | + tool_call_id=item.tool_use_id, |
| 1164 | + ) |
| 1165 | + |
| 1166 | + |
1100 | 1167 | def _map_mcp_server_use_block(item: BetaMCPToolUseBlock, provider_name: str) -> BuiltinToolCallPart: |
1101 | 1168 | return BuiltinToolCallPart( |
1102 | 1169 | provider_name=provider_name, |
|
0 commit comments