Add @tool decorator for zero-boilerplate tool definition#105
Merged
Conversation
Implement the @tool decorator (chainweaver/decorators.py) that creates a Tool from a type-annotated function by introspecting parameter and return type hints. - Auto-generate input_schema via pydantic.create_model() from function params - Output schema uses the return type annotation (must be a BaseModel subclass) - Tool name defaults to function name; overridable via name= kwarg - Description falls back to docstring if not provided - Decorated function remains directly callable with original signature - Clear ChainWeaverError on missing/insufficient type hints - Supports Annotated[T, Field(...)] for richer schemas - 21 tests covering basic use, custom name, docstring fallback, missing hints errors, default values, no-parameter tools, Annotated metadata, and round-trip with FlowExecutor - Export tool in chainweaver/__init__.__all__ - Update AGENTS.md repo map and architecture.md module boundaries Closes #67
There was a problem hiding this comment.
Pull request overview
Adds a new @chainweaver.tool decorator to reduce boilerplate when defining Tool instances from type-annotated Python functions, plus supporting tests and documentation updates to reflect the new module boundary.
Changes:
- Introduce
chainweaver.decorators.toolwhich builds a_DecoratedTool(aToolsubclass) by introspecting function signatures and type hints. - Export
toolfromchainweaver.__init__as part of the public API surface. - Add a new test suite validating decorator behavior (schema generation, description/name behavior, callability, and executor round-trips) and update architecture/docs maps.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
chainweaver/decorators.py |
Implements the @tool decorator and _DecoratedTool wrapper with dynamic Pydantic input schema creation. |
chainweaver/__init__.py |
Re-exports tool in __all__ to make it part of the public API. |
tests/test_decorators.py |
Adds coverage for core decorator behaviors and integration with FlowExecutor. |
AGENTS.md |
Updates the repo map to include the new decorators.py module. |
docs/agent-context/architecture.md |
Extends the module boundaries table to document decorators.py. |
You can also share your feedback on Copilot code review. Take the survey.
added 4 commits
March 19, 2026 15:03
The adapter calls fn(**inp.model_dump()), which raises TypeError for positional-only params (def f(x, /): ...). Reject them upfront with a clear ChainWeaverError, consistent with existing *args/**kwargs handling. Add unit test for the new guard.
Unresolved forward refs or invalid annotations in get_type_hints() raised raw NameError/TypeError instead of ChainWeaverError. Catch and re-raise with an actionable message including function name and original error. Add unit test for unresolvable forward reference.
Replace bare ChainWeaverError at all 6 raise sites in decorators.py with ToolDefinitionError(function_name, detail), aligning with invariant #4 (structured context attributes on all exceptions). - Add ToolDefinitionError to exceptions.py - Export in __init__.py __all__ - Add to README error table - Tighten test assertions to match ToolDefinitionError
- Add decorator quickstart subsection after the existing verbose example - Add decorators.py to README architecture tree - Add examples/decorator_tool.py with runnable before/after comparison Completes the 'Add decorator usage to README quickstart and examples' task from issue #67.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Add a
@chainweaver.tooldecorator that creates a validToolfrom a type-annotated Python function, reducing tool definition boilerplate from 8+ lines to 2-3 lines.Files
chainweaver/decorators.py(new) — Decorator implementation usinginspect.signature(),typing.get_type_hints(), andpydantic.create_model()to auto-generate input schemas from function parameters. Returns a_DecoratedTool(subclass ofTool) that is also directly callable.chainweaver/__init__.py— Import and exporttoolin__all__.tests/test_decorators.py(new) — 21 tests across 9 test classes.AGENTS.md— Updated repo map to includedecorators.py.docs/agent-context/architecture.md— Addeddecorators.pyto module boundaries table.Why
Closes #67. Defining a tool required separate Pydantic input/output models, a function, and a
Tool()constructor call (8+ lines). The decorator introspects type hints to eliminate this boilerplate while preserving full compatibility with the existingTool()constructor.How verified
All four repo validation commands run locally:
ruff check chainweaver/ tests/ examples/ruff format --check chainweaver/ tests/ examples/python -m mypy chainweaver/python -m pytest tests/ -vThe 1 failure (
TestToolZeroDivisionError) is a pre-existing issue: Python 3.14 changed theZeroDivisionErrormessage from "integer division or modulo by zero" to "division by zero". Unrelated to this PR.Test coverage
chainweaver/decorators.py— 100% coverage (44/44 statements).Tests cover:
Annotated[T, Field(...)]metadata preservationTradeoffs / risks
create_model()dynamic models: The auto-generated input schemas are dynamically created Pydantic models. They work identically to hand-written models but won't appear in static analysis tools' type graph. This is an inherent tradeoff of the convenience layer.Tool()constructor is completely unchanged. The decorator is a pure addition.Doc / agent instruction updates
decorators.pydecorators.pyentry