tools
On this page
llm.tools — standalone module that defines the unified tool-calling schema, converts it to each provider's native shape, and parses each provider's response back into one shape. Has no dependency on BaseLLMInterface or any provider SDK; every provider module imports only the specific converter/parser pair it needs.
See Tool Calling for the full usage contract and per-provider conversion table — this page documents the module's exports directly.
ToolCall
@dataclass
class ToolCall:
id: Optional[str]
name: str
arguments: dict
The internal representation every parse_*_tool_calls() function returns a list of, before tool_calls_to_dicts() converts them to the plain dicts that actually reach calling code. id is None for providers/responses that don't carry a call id (Gemini always; Ollama when the model/server doesn't supply one).
_normalize_tool(tool: dict) -> dict
def _normalize_tool(tool: dict) -> dict:
name = tool.get("name")
if not name:
raise ValueError("Each tool must have a 'name'.")
description = tool.get("description", "")
schema = tool.get("input_schema") or tool.get("parameters") or {"type": "object", "properties": {}}
return {"name": name, "description": description, "schema": schema}
Internal helper called by every to_*_tools() converter. Accepts either input_schema (Anthropic-style key) or parameters (OpenAI-style key) for the JSON-schema object — whichever is present wins; if neither is given, an empty {"type": "object", "properties": {}} schema is used rather than raising. name is the only required field; a missing name raises ValueError immediately, uncaught by any of the to_*_tools() callers, so it propagates straight out of generate()/generate_async() on whichever provider you called (which then usually re-catches it as part of that provider's generic exception handling — see each provider's page for the exact resulting return shape).
Input converters
Every converter returns None if tools is falsy (None or []), so callers can do kwargs["tools"] = to_openai_tools(tools) unconditionally inside an if tools: guard without a separate None-check — though every provider module in this package still gates the whole block behind if tools: for clarity rather than relying on this.
Response parsers
Each parser returns [] (not None) when there are no tool calls in the response, so if calls: is always a safe truthiness check in calling code — every provider module in this package relies on exactly that pattern.
tool_calls_to_dicts(calls: list[ToolCall]) -> list[dict]
def tool_calls_to_dicts(calls: list[ToolCall]) -> list[dict]:
return [{"id": c.id, "name": c.name, "arguments": c.arguments} for c in calls]
The last step every provider's generate/generate_async calls before building its {"content", "tool_calls", "stop_reason"} return dict — converts the internal ToolCall dataclass list into the plain dicts that actually reach calling code, so nothing provider-internal (like the ToolCall type itself) leaks into the public return shape.
Related pages
- Tool Calling — the full usage contract, written for callers rather than as a module reference.
BaseLLMInterface— thetools/tool_choiceparameters this module's converters feed into.- Any concrete provider page for which converter/parser pair that specific class imports and how it wires them into
generate/generate_async.