Source code for pipecat.workers.llm.tool_decorator
#
# Copyright (c) 2026, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#
"""Decorator for marking methods as LLM tools."""
[docs]
def tool(fn=None, *, cancel_on_interruption=True, timeout=None):
"""Mark a method as a tool.
On ``LLMWorker`` subclasses, decorated methods are automatically
registered with the LLM via ``register_direct_function`` and
included in ``build_tools()``.
Can be used with or without arguments::
@tool
async def my_tool(self, params, arg: str):
...
@tool(cancel_on_interruption=False, timeout=60)
async def my_tool(self, params, arg: str):
...
Args:
fn: The function to decorate (when used without arguments).
cancel_on_interruption: Whether to cancel this tool call when
an interruption occurs. Defaults to True. Only applies to
``LLMWorker`` tools.
timeout: Optional timeout in seconds for this tool call.
Defaults to None (uses the LLM service default).
"""
def decorator(fn):
fn.is_llm_tool = True
fn.cancel_on_interruption = cancel_on_interruption
fn.timeout = timeout
return fn
if fn is not None:
return decorator(fn)
return decorator
def _collect_tools(obj) -> list:
"""Collect all ``@tool`` decorated bound methods from an object.
Walks the MRO so that overridden methods in subclasses take
precedence over base-class definitions.
"""
seen: set[str] = set()
tools = []
for cls in type(obj).__mro__:
for name, val in cls.__dict__.items():
if name in seen:
continue
seen.add(name)
if callable(val) and getattr(val, "is_llm_tool", False):
tools.append(getattr(obj, name))
return tools