Skip to content

symfonic.core.tool_interface

tool_interface

Phase 2 tool interface and registry — framework-agnostic tool contracts.

Defines the async Tool protocol and a ToolRegistry that supports registration, lookup by name, and capability-based filtering. Separate from Phase 1's LangChain-based tools/registry.py.

ConflictError

Bases: StorageError

Write conflict (e.g. optimistic-lock failure, duplicate key).

Tool

Bases: Protocol

Tool interface. All tools are async-only.

ToolRegistry

ToolRegistry()

Register tools, filter by capability, retrieve by name.

Raises ConflictError on duplicate name registration.

Source code in src/symfonic/core/tool_interface.py
def __init__(self) -> None:
    self._tools: dict[str, Tool] = {}

all_tools

all_tools() -> list[Tool]

Return all registered tools.

Source code in src/symfonic/core/tool_interface.py
def all_tools(self) -> list[Tool]:
    """Return all registered tools."""
    return list(self._tools.values())

get

get(name: str) -> Tool | None

Return a tool by name, or None if not found.

Source code in src/symfonic/core/tool_interface.py
def get(self, name: str) -> Tool | None:
    """Return a tool by name, or None if not found."""
    return self._tools.get(name)

get_active_tools

get_active_tools(
    deps: Any, *, debug: bool = False
) -> list[Tool]

Return tools whose requires are all satisfied by deps.

Source code in src/symfonic/core/tool_interface.py
def get_active_tools(
    self, deps: Any, *, debug: bool = False
) -> list[Tool]:
    """Return tools whose requires are all satisfied by deps."""
    result: list[Tool] = []
    for tool in self._tools.values():
        satisfied = all(
            deps.has(cap) if hasattr(deps, "has") else False
            for cap in tool.requires
        )
        if satisfied:
            result.append(tool)
        elif debug:
            logger.debug(
                "Tool '%s' excluded — missing capabilities",
                tool.name,
            )
    return result

register

register(tool: Tool) -> None

Register a tool. Raises ConflictError if name already taken.

Source code in src/symfonic/core/tool_interface.py
def register(self, tool: Tool) -> None:
    """Register a tool. Raises ConflictError if name already taken."""
    if tool.name in self._tools:
        raise ConflictError(
            f"Tool '{tool.name}' is already registered"
        )
    self._tools[tool.name] = tool