From fd3ecb1282d1f2ec17335e4841906bcd50aae157 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 21 Sep 2026 12:48:32 -0400 Subject: [PATCH] Set tool metadata in lawn_mower (#182704) Co-authored-by: Claude --- homeassistant/components/lawn_mower/llm.py | 25 +++++++++++++++++++--- tests/components/lawn_mower/test_llm.py | 18 +++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/lawn_mower/llm.py b/homeassistant/components/lawn_mower/llm.py index 16b9b5f7b8ce..6fe3b76067b3 100644 --- a/homeassistant/components/lawn_mower/llm.py +++ b/homeassistant/components/lawn_mower/llm.py @@ -4,13 +4,26 @@ from homeassistant.components.homeassistant import async_should_expose from homeassistant.components.llm import LLMTools from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import intent -from homeassistant.helpers.llm import LLM_API_ASSIST, IntentTool, LLMContext, Tool +from homeassistant.helpers.llm import ( + LLM_API_ASSIST, + IntentTool, + LLMContext, + Tool, + ToolAnnotations, +) from .const import DOMAIN from .intent import INTENT_LANW_MOWER_DOCK, INTENT_LANW_MOWER_START_MOWING +# Each intent sets a value on the user's own entities, so calling one again +# with the same arguments has no further effect. +LLM_ANNOTATIONS = ToolAnnotations(idempotent=True, open_world=False) + # Intents owned by this integration that are exposed as LLM tools. -LLM_INTENTS = (INTENT_LANW_MOWER_DOCK, INTENT_LANW_MOWER_START_MOWING) +LLM_INTENTS = { + INTENT_LANW_MOWER_DOCK: "Dock lawn mower", + INTENT_LANW_MOWER_START_MOWING: "Start mowing", +} @callback @@ -31,7 +44,13 @@ def async_get_tools( return None tools: list[Tool] = [ - IntentTool(f"{DOMAIN}__{handler.intent_type}", handler) + IntentTool( + f"{DOMAIN}__{handler.intent_type}", + handler, + title=LLM_INTENTS[handler.intent_type], + integration=DOMAIN, + annotations=LLM_ANNOTATIONS, + ) for handler in intent.async_get(hass) if handler.intent_type in LLM_INTENTS ] diff --git a/tests/components/lawn_mower/test_llm.py b/tests/components/lawn_mower/test_llm.py index a16946ca4c76..d50607bae596 100644 --- a/tests/components/lawn_mower/test_llm.py +++ b/tests/components/lawn_mower/test_llm.py @@ -44,7 +44,23 @@ async def _tool_names(hass: HomeAssistant) -> set[str]: async def test_intent_tool_exposed(hass: HomeAssistant) -> None: """Test the intent tool is offered for an exposed lawn_mower entity.""" - assert await _tool_names(hass) >= TOOL_NAMES + result = await llm_component.async_get_tools(hass, _llm_context(), "assist") + tools = {tool.name: tool for tool in result.tools} + assert tools.keys() >= TOOL_NAMES + + annotations = llm.ToolAnnotations(idempotent=True, open_world=False) + assert { + name: (tool.title, tool.integration, tool.annotations) + for name, tool in tools.items() + if name in TOOL_NAMES + } == { + "lawn_mower__HassLawnMowerDock": ("Dock lawn mower", "lawn_mower", annotations), + "lawn_mower__HassLawnMowerStartMowing": ( + "Start mowing", + "lawn_mower", + annotations, + ), + } async def test_intent_tool_not_exposed(hass: HomeAssistant) -> None: