Skip to content

teff.tool.tool

teff.tool.tool

Abstract base for all tools.

Classes:

Name Description
Tool

Abstract base class for tools callable by nodes.

Functions:

Name Description
coerce_args

Coerce tool-call arguments to match the tool's type hints.

Tool

Abstract base class for tools callable by nodes.

Subclasses define name and description as class attributes, then implement run (sync) and/or arun (async).

Attributes:

Name Type Description
name str

Unique tool name (defaults to lowercase class name).

description str

Human-readable description for LLM tool selection.

schema dict | None

Optional JSON Schema dict for the tool's arguments. When set (e.g. by :class:~teff.tool.mcp.McpTool), it is used as-is instead of being inferred from the run/arun signature.

Methods:

Name Description
arun

Execute the tool asynchronously.

run

Execute the tool synchronously.

Source code in teff/tool/tool.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class Tool:
    """Abstract base class for tools callable by nodes.

    Subclasses define *name* and *description* as class attributes,
    then implement *run* (sync) and/or *arun* (async).

    Attributes:
        name: Unique tool name (defaults to lowercase class name).
        description: Human-readable description for LLM tool selection.
        schema: Optional JSON Schema dict for the tool's arguments.  When
            set (e.g. by :class:`~teff.tool.mcp.McpTool`), it is used as-is
            instead of being inferred from the ``run``/``arun`` signature.
    """

    name: str = ""
    description: str = ""
    schema: dict | None = None

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if cls.name == "":
            cls.name = cls.__name__.lower()

    def __init__(self):
        pass

    def run(self, **kwargs):
        """Execute the tool synchronously.

        Args:
            **kwargs: Tool-specific keyword arguments.

        Returns:
            Tool-specific result (typically a string).
        """
        raise NotImplementedError

    async def arun(self, **kwargs):
        """Execute the tool asynchronously.

        Falls back to *run* via ``asyncio.to_thread`` if not overridden.

        Args:
            **kwargs: Tool-specific keyword arguments.

        Returns:
            Tool-specific result (typically a string).
        """
        return await asyncio.to_thread(self.run, **kwargs)

arun async

arun(**kwargs)

Execute the tool asynchronously.

Falls back to run via asyncio.to_thread if not overridden.

Parameters:

Name Type Description Default
**kwargs

Tool-specific keyword arguments.

{}

Returns:

Type Description

Tool-specific result (typically a string).

Source code in teff/tool/tool.py
84
85
86
87
88
89
90
91
92
93
94
95
async def arun(self, **kwargs):
    """Execute the tool asynchronously.

    Falls back to *run* via ``asyncio.to_thread`` if not overridden.

    Args:
        **kwargs: Tool-specific keyword arguments.

    Returns:
        Tool-specific result (typically a string).
    """
    return await asyncio.to_thread(self.run, **kwargs)

run

run(**kwargs)

Execute the tool synchronously.

Parameters:

Name Type Description Default
**kwargs

Tool-specific keyword arguments.

{}

Returns:

Type Description

Tool-specific result (typically a string).

Source code in teff/tool/tool.py
73
74
75
76
77
78
79
80
81
82
def run(self, **kwargs):
    """Execute the tool synchronously.

    Args:
        **kwargs: Tool-specific keyword arguments.

    Returns:
        Tool-specific result (typically a string).
    """
    raise NotImplementedError

coerce_args

coerce_args(tool, kwargs)

Coerce tool-call arguments to match the tool's type hints.

LLMs often pass values as strings (e.g. k="1" instead of 1); coerce them so tools receive properly typed arguments. Optional hints (float | None, Optional[int]) are unwrapped before matching.

Source code in teff/tool/tool.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def coerce_args(tool: "Tool", kwargs: dict) -> dict:
    """Coerce tool-call arguments to match the tool's type hints.

    LLMs often pass values as strings (e.g. ``k="1"`` instead of ``1``);
    coerce them so tools receive properly typed arguments.  Optional hints
    (``float | None``, ``Optional[int]``) are unwrapped before matching.
    """
    method = tool.arun if type(tool).run is Tool.run else tool.run
    try:
        hints = typing.get_type_hints(method)
    except Exception:
        hints = {}
    for key, value in kwargs.items():
        if value is None:
            continue
        tp = _unwrap_optional(hints.get(key))
        if tp is int and not isinstance(value, int):
            kwargs[key] = int(value)
        elif tp is float and not isinstance(value, float):
            kwargs[key] = float(value)
        elif tp is bool and not isinstance(value, bool):
            kwargs[key] = str(value).lower() in ("true", "1")
        elif tp is str and not isinstance(value, str):
            kwargs[key] = str(value)
    return kwargs