Add decorator to define Python tools from Python functions

This commit is contained in:
Paulus Schoutsen
2025-08-17 20:59:10 +00:00
parent 9f17a8a943
commit 176f9c9f94

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Callable
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field as dc_field
from datetime import timedelta
from decimal import Decimal
@@ -613,7 +613,7 @@ class AssistAPI(API):
)
if exposed_domains:
tools.append(GetLiveContextTool())
tools.append(async_get_live_context_tool)
return tools
@@ -1141,29 +1141,59 @@ class TodoGetItemsTool(Tool):
return {"success": True, "result": items}
class GetLiveContextTool(Tool):
"""Tool for getting the current state of exposed entities.
class PythonTool(Tool):
"""Tool for executing Python code snippets."""
This returns state for all entities that have been exposed to
the assistant. This is different than the GetState intent, which
returns state for entities based on intent parameters.
"""
def __init__(
self,
*,
name: str,
description: str,
parameters: vol.Schema | None = None,
tool_func: Callable[
[HomeAssistant, ToolInput, LLMContext], Awaitable[JsonObjectType]
],
) -> None:
"""Initialize the Python tool."""
self.name = name
self.description = description
self.parameters = parameters or vol.Schema({})
self.tool_func = tool_func
name = "GetLiveContext"
description = (
"Provides real-time information about the CURRENT state, value, or mode of devices, sensors, entities, or areas. "
"Use this tool for: "
"1. Answering questions about current conditions (e.g., 'Is the light on?'). "
"2. As the first step in conditional actions (e.g., 'If the weather is rainy, turn off sprinklers' requires checking the weather first)."
@classmethod
def define(cls, name: str, parameters: vol.Schema | None = None) -> Callable:
"""Decorator to create a tool from a function."""
def decorator(func: Callable) -> PythonTool:
"""Decorate the function to create a tool."""
return cls(
name=name,
description=func.__doc__ or "",
parameters=parameters,
tool_func=func,
)
return decorator
async def async_call(
self,
self, hass: HomeAssistant, tool_input: ToolInput, llm_context: LLMContext
) -> JsonObjectType:
"""Call the Python function."""
return await self.tool_func(hass, tool_input, llm_context)
@PythonTool.define("GetLiveContext")
async def async_get_live_context_tool(
hass: HomeAssistant,
tool_input: ToolInput,
llm_context: LLMContext,
) -> JsonObjectType:
"""Get the current state of exposed entities."""
"""Provides real-time information about the CURRENT state, value, or mode of devices, sensors, entities, or areas.
Use this tool for:
1. Answering questions about current conditions (e.g., 'Is the light on?').
2. As the first step in conditional actions (e.g., 'If the weather is rainy, turn off sprinklers' requires checking the weather first).
"""
if llm_context.assistant is None:
# Note this doesn't happen in practice since this tool won't be
# exposed if no assistant is configured.