Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions homeassistant/components/anthropic/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@
CONF_NAME,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers import config_validation as cv, llm
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.selector import (
NumberSelector,
Expand All @@ -52,6 +51,8 @@
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
CONF_WEB_FETCH,
CONF_WEB_FETCH_MAX_USES,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
Expand Down Expand Up @@ -459,6 +460,14 @@ async def async_step_model(
CONF_WEB_SEARCH_USER_LOCATION,
default=DEFAULT[CONF_WEB_SEARCH_USER_LOCATION],
): bool,
vol.Optional(
CONF_WEB_FETCH,
default=DEFAULT[CONF_WEB_FETCH],
): bool,
vol.Optional(
CONF_WEB_FETCH_MAX_USES,
default=DEFAULT[CONF_WEB_FETCH_MAX_USES],
): cv.positive_int,
Comment thread
Shulyaka marked this conversation as resolved.
}
)

Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/anthropic/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
CONF_THINKING_BUDGET = "thinking_budget"
CONF_THINKING_EFFORT = "thinking_effort"
CONF_TOOL_SEARCH = "tool_search"
CONF_WEB_FETCH = "web_fetch"
CONF_WEB_FETCH_MAX_USES = "web_fetch_max_uses"
CONF_WEB_SEARCH = "web_search"
CONF_WEB_SEARCH_USER_LOCATION = "user_location"
CONF_WEB_SEARCH_MAX_USES = "web_search_max_uses"
Expand Down Expand Up @@ -45,6 +47,8 @@ class PromptCaching(StrEnum):
CONF_THINKING_BUDGET: MIN_THINKING_BUDGET,
CONF_THINKING_EFFORT: "low",
CONF_TOOL_SEARCH: False,
CONF_WEB_FETCH: False,
CONF_WEB_FETCH_MAX_USES: 5,
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_WEB_SEARCH_MAX_USES: 5,
Expand Down
70 changes: 53 additions & 17 deletions homeassistant/components/anthropic/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
Base64PDFSourceParam,
BashCodeExecutionToolResultBlock,
CitationsDelta,
CitationsWebSearchResultLocation,
CitationWebSearchResultLocationParam,
CodeExecutionTool20250825Param,
CodeExecutionToolResultBlock,
CodeExecutionToolResultBlockParamContentParam,
Expand Down Expand Up @@ -65,6 +63,9 @@
ToolUseBlock,
ToolUseBlockParam,
Usage,
WebFetchTool20250910Param,
WebFetchTool20260209Param,
WebFetchToolResultBlock,
WebSearchTool20250305Param,
WebSearchTool20260209Param,
WebSearchToolResultBlock,
Expand All @@ -80,6 +81,9 @@
from anthropic.types.tool_search_tool_result_block_param import (
Content as ToolSearchToolResultBlockParamContentParam,
)
from anthropic.types.web_fetch_tool_result_block_param import (
Content as WebFetchToolResultBlockParamContentParam,
)
import voluptuous as vol
from voluptuous_openapi import convert

Expand All @@ -91,7 +95,7 @@
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import slugify
from homeassistant.util.json import JsonObjectType
from homeassistant.util.json import JsonArrayType, JsonObjectType

from .const import (
CONF_CHAT_MODEL,
Expand All @@ -101,6 +105,8 @@
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_TOOL_SEARCH,
CONF_WEB_FETCH,
CONF_WEB_FETCH_MAX_USES,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
Expand Down Expand Up @@ -191,17 +197,9 @@ def add_citation(self, citation: TextCitation) -> None:
"""Add a citation to the current detail."""
if not self.citation_details:
self.citation_details.append(CitationDetails())
citation_param: TextCitationParam | None = None
if isinstance(citation, CitationsWebSearchResultLocation):
citation_param = CitationWebSearchResultLocationParam(
type="web_search_result_location",
title=citation.title,
url=citation.url,
cited_text=citation.cited_text,
encrypted_index=citation.encrypted_index,
)
if citation_param:
self.citation_details[-1].citations.append(citation_param)
self.citation_details[-1].citations.append(
cast(TextCitationParam, citation.to_dict())
)

def delete_empty(self) -> None:
"""Delete empty citation details."""
Expand Down Expand Up @@ -272,6 +270,15 @@ def _convert_content( # noqa: C901
content.tool_result,
),
}
elif content.tool_name == "web_fetch":
tool_result_block = {
"type": "web_fetch_tool_result",
"tool_use_id": content.tool_call_id,
"content": cast(
WebFetchToolResultBlockParamContentParam,
content.tool_result,
),
}
else:
tool_result_block = {
"type": "tool_result",
Expand Down Expand Up @@ -398,6 +405,7 @@ def _convert_content( # noqa: C901
id=tool_call.id,
name=cast(
Literal[
"web_fetch",
"web_search",
"code_execution",
"bash_code_execution",
Expand All @@ -411,6 +419,7 @@ def _convert_content( # noqa: C901
if tool_call.external
and tool_call.tool_name
in [
"web_fetch",
"web_search",
"code_execution",
"bash_code_execution",
Expand Down Expand Up @@ -573,6 +582,7 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
elif isinstance(
response.content_block,
(
WebFetchToolResultBlock,
WebSearchToolResultBlock,
CodeExecutionToolResultBlock,
BashCodeExecutionToolResultBlock,
Expand All @@ -593,7 +603,7 @@ async def _transform_stream( # noqa: C901 - This is complex, but better to have
),
"tool_result": {
"content": cast(
JsonObjectType, response.content_block.to_dict()["content"]
JsonArrayType, response.content_block.to_dict()["content"]
)
}
if isinstance(response.content_block.content, list)
Expand Down Expand Up @@ -718,6 +728,7 @@ async def _get_model_args( # noqa: C901
"GetLiveContext",
"code_execution",
"web_search",
"web_fetch",
]

system = chat_log.content[0]
Expand Down Expand Up @@ -791,11 +802,14 @@ async def _get_model_args( # noqa: C901
]

if options[CONF_CODE_EXECUTION]:
# The `web_search_20260209` tool automatically enables `code_execution_20260120` tool
# The `web_search_20260209` and `web_fetch_20260209` tools
# automatically enable `code_execution_20260120` tool
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_WEB_SEARCH]
or (
not options[CONF_WEB_SEARCH] and not options[CONF_WEB_FETCH]
)
):
tools.append(
CodeExecutionTool20250825Param(
Expand Down Expand Up @@ -833,6 +847,28 @@ async def _get_model_args( # noqa: C901
}
tools.append(web_search)

if options[CONF_WEB_FETCH]:
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options[CONF_CODE_EXECUTION]
):
tools.append(
WebFetchTool20250910Param(
name="web_fetch",
type="web_fetch_20250910",
max_uses=options[CONF_WEB_FETCH_MAX_USES],
)
)
else:
tools.append(
WebFetchTool20260209Param(
name="web_fetch",
type="web_fetch_20260209",
max_uses=options[CONF_WEB_FETCH_MAX_USES],
)
)

# Handle attachments by adding them to the last user message
last_content = chat_log.content[-1]
if last_content.role == "user" and last_content.attachments:
Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/anthropic/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data::thinking_effort%]",
"tool_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data::tool_search%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data::user_location%]",
"web_fetch": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_fetch%]",
"web_fetch_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_fetch_max_uses%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search_max_uses%]"
},
Expand All @@ -90,6 +92,8 @@
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::thinking_effort%]",
"tool_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::tool_search%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::user_location%]",
"web_fetch": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_fetch%]",
"web_fetch_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_fetch_max_uses%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search_max_uses%]"
},
Expand Down Expand Up @@ -149,6 +153,8 @@
"thinking_effort": "Thinking effort",
"tool_search": "Enable tool search tool",
"user_location": "Include home location",
"web_fetch": "Enable web fetch",
"web_fetch_max_uses": "Maximum web fetches",
"web_search": "Enable web search",
"web_search_max_uses": "Maximum web searches"
},
Expand All @@ -159,6 +165,8 @@
"thinking_effort": "Control how many tokens Claude uses when responding, trading off between response thoroughness and token efficiency",
"tool_search": "Enable dynamic tool discovery instead of preloading all tools into the context",
"user_location": "Localize search results based on home location",
"web_fetch": "The web fetch tool allows Claude to retrieve full content from specified web pages and PDF documents to augment Claude's context with live web content",
"web_fetch_max_uses": "Limit the number of web fetches performed per response",
"web_search": "The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff",
"web_search_max_uses": "Limit the number of searches performed per response"
},
Expand Down
29 changes: 29 additions & 0 deletions tests/components/anthropic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
ThinkingTypes,
ToolSearchToolResultBlock,
ToolUseBlock,
WebFetchToolResultBlock,
WebSearchResultBlock,
WebSearchToolResultBlock,
WebSearchToolResultError,
Expand All @@ -47,6 +48,9 @@
from anthropic.types.tool_search_tool_result_block import (
Content as ToolSearchToolResultBlockContent,
)
from anthropic.types.web_fetch_tool_result_block import (
Content as WebFetchToolResultBlockContent,
)

model_list = [
ModelInfo(
Expand Down Expand Up @@ -641,3 +645,28 @@ def create_tool_search_result_block(
),
RawContentBlockStopEvent(index=index, type="content_block_stop"),
]


def create_web_fetch_result_block(
index: int,
id: str,
results: WebFetchToolResultBlockContent,
caller: Caller | None = None,
) -> list[RawMessageStreamEvent]:
"""Create a server tool result block for web fetch results."""
if caller is None:
caller = DirectCaller(type="direct")

return [
RawContentBlockStartEvent(
type="content_block_start",
content_block=WebFetchToolResultBlock(
type="web_fetch_tool_result",
tool_use_id=id,
content=results,
caller=caller,
),
index=index,
),
RawContentBlockStopEvent(index=index, type="content_block_stop"),
]
Loading
Loading