Coverage for src / lexigram / contracts / ai / tools.py: 76%
42 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Tools + LCEL contracts for G-03 parity.
3Defines tool decorators and classes analogous to LangChain's tool system.
4"""
6from __future__ import annotations
8import asyncio
9from collections.abc import Callable
10from dataclasses import dataclass
11from functools import wraps
12from typing import Any
15@dataclass(frozen=True)
16class Tool:
17 """Base tool class (like LangChain's BaseTool).
19 Attributes:
20 name: Tool name.
21 description: Tool description.
22 func: The underlying function.
23 """
25 name: str
26 description: str = ""
27 func: Callable[..., Any] | None = None
29 def invoke(self, *args: Any, **kwargs: Any) -> Any:
30 """Synchronously invoke the tool."""
31 if self.func is None:
32 raise ValueError("Tool function not set")
33 return self.func(*args, **kwargs)
35 async def ainvoke(self, *args: Any, **kwargs: Any) -> Any:
36 """Asynchronously invoke the tool."""
37 if self.func is None:
38 raise ValueError("Tool function not set")
39 if asyncio.iscoroutinefunction(self.func):
40 return await self.func(*args, **kwargs)
41 return self.func(*args, **kwargs)
44class StructuredTool:
45 """Tool with structured input/output (like LangChain's StructuredTool).
47 Analogous to LangChain's StructuredTool for tools with typed parameters.
48 """
50 def __init__(
51 self,
52 name: str,
53 description: str,
54 func: Callable[..., Any],
55 ) -> None:
56 self.name = name
57 self.description = description
58 self.func = func
60 def invoke(self, *args: Any, **kwargs: Any) -> Any:
61 """Synchronously invoke the tool."""
62 return self.func(*args, **kwargs)
64 async def ainvoke(self, *args: Any, **kwargs: Any) -> Any:
65 """Asynchronously invoke the tool."""
66 if asyncio.iscoroutinefunction(self.func):
67 return await self.func(*args, **kwargs)
68 return self.func(*args, **kwargs)
71def tool(name: str | None = None) -> Callable[[Callable[..., Any]], Tool]:
72 """Decorator to create a tool from a function (like @tool).
74 Args:
75 name: Optional tool name. Defaults to function name.
77 Returns:
78 A decorator that creates a Tool from a function.
80 Example:
81 @tool("add")
82 def add(a: int, b: int) -> int:
83 '''Add two numbers.'''
84 return a + b
85 """
87 def decorator(func: Callable[..., Any]) -> Tool:
88 tool_name = name or func.__name__
89 tool_description = func.__doc__ or ""
91 @wraps(func)
92 def wrapper(*args: Any, **kwargs: Any) -> Any:
93 return func(*args, **kwargs)
95 return Tool(
96 name=tool_name,
97 description=tool_description.strip(),
98 func=func,
99 )
101 return decorator
104__all__ = [
105 "StructuredTool",
106 "Tool",
107 "tool",
108]