Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

src/tools/

Generic tool registry — the dispatcher pattern an LLM-driven agent uses to call typed tools. Each tool is a function StrictModel -> StrictModel; the registry maps a name to (input_schema, callable) and validates the dict-shaped input against the schema before dispatch. Layer-wise sits below agent / api / eval, above data / models (enforced by import-linter).

Key interfaces

  • registry.Registry — instance class. Construct one per agent/test fixture; the module also exposes a global registry singleton that the example echo_tool self-registers into at import time.
    • register(name, input_schema) — decorator factory. Wraps a function and inserts the (input_schema, fn) pair under name. Raises ValueError on duplicate registration.
    • dispatch(name, raw_input)name-lookup → input_schema.model_validate(raw_input) → call. Pydantic ValidationError propagates on bad input (wrong type or unknown keys via StrictModel.extra="forbid").
    • names() — sorted list of registered tool names; cheap introspection for the agent's tool-listing prompt.
  • registry.UnknownToolErrorKeyError subclass raised by dispatch when name isn't registered. Caught and rendered as a tool-call-error event by the agent loop.
  • registry.registry — the module-global Registry instance. Real agents use this so the echo_tool is reachable from any consumer; tests construct private Registry() instances to avoid cross-test contamination.
  • registry.echo_tool + EchoToolInput + EchoToolOutput — example tool demonstrating the layer. Ships pre-registered under name "echo" to give a working dispatch path on a fresh clone.

Conventions

  • Inputs and outputs are StrictModels. Avoid raw dicts — the extra="forbid" posture is the entire reason this pattern beats hand-rolled if/elif dispatch over JSON.
  • Tools are pure functions of their input. State the tool needs (DB connection, HTTP client) is injected via partial / closure at registration time, not via module globals.
  • Add a tool = add a module. Real tools beyond the example get their own file under src/tools/ and self-register the same way echo_tool does. Keep registry.py itself focused on the dispatcher.