windlass.tools¶
tools
¶
Tools — turning Python functions into agent capabilities.
The whole surface is one decorator::
from windlass import tool
@tool
def get_weather(city: str, units: str = "celsius") -> dict:
# Docstring summary becomes the model-facing description, and a
# Google-style argument section becomes the per-parameter descriptions.
# See :func:`tool` for a fully documented example.
return {"city": city, "temp": 18, "units": units}
The schema comes from the type hints, the description from the docstring, and
the result is a :class:~windlass.interfaces.tool.Tool that any Windlass agent can
bind. Async functions work identically; sync functions are run on a worker thread
so a slow tool never blocks the event loop.
This module also provides :class:ToolRegistry, which agents use to hold their
bound tools and execute calls — including parallel execution when a model
requests several at once.
FunctionTool
¶
FunctionTool(
fn: Callable[..., Any],
*,
name: str | None = None,
description: str | None = None,
parameters: dict[str, Any] | None = None,
timeout: float | None = None,
requires_approval: bool = False,
**config: Any
)
Bases: Tool
A :class:~windlass.interfaces.tool.Tool backed by a Python function.
Normally produced by the :func:tool decorator rather than constructed
directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any]
|
The function to wrap. May be sync or async. |
required |
name
|
str | None
|
Tool name. Defaults to the function's name. |
None
|
description
|
str | None
|
Model-facing description. Defaults to the docstring summary. |
None
|
parameters
|
dict[str, Any] | None
|
JSON Schema override. Derived from the signature by default. |
None
|
timeout
|
float | None
|
Seconds before the call is abandoned. |
None
|
requires_approval
|
bool
|
Pause for human approval before running. |
False
|
**config
|
Any
|
Forwarded to :class: |
{}
|
Attributes:
| Name | Type | Description |
|---|---|---|
fn |
The wrapped function, still callable directly. |
|
is_async |
Whether the function is a coroutine function. |
Example
def add(a: int, b: int) -> int: ... '''Add two integers.''' ... return a + b t = FunctionTool(add) t.run(a=2, b=3).data 5
Source code in src\windlass\tools\__init__.py
acall
async
¶
Invoke the wrapped function.
Sync functions run on a worker thread, so a blocking HTTP call inside a tool cannot stall the agent's event loop.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs
|
Any
|
Arguments matching the schema. |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The function's return value. |
Raises:
| Type | Description |
|---|---|
ValidationError
|
When a required argument is missing. |
Exception
|
Anything the function raises. |
Source code in src\windlass\tools\__init__.py
ToolRegistry
¶
The set of tools bound to one agent.
Holds tools by name, renders provider-specific schemas, and executes calls — in parallel when the model requests several at once, which is where a lot of agent latency quietly hides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tools
|
Sequence[Tool] | None
|
Initial tools to bind. |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
tools |
dict[str, Tool]
|
Bound tools, keyed by name. |
Example
@tool ... def double(x: int) -> int: ... '''Double a number.''' ... return x * 2 registry = ToolRegistry([double]) registry.execute(ToolCall(name="double", arguments={"x": 4})).data 8
Source code in src\windlass\tools\__init__.py
add
¶
Bind a tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
Tool | Callable[..., Any]
|
A :class: |
required |
Returns:
| Type | Description |
|---|---|
Tool
|
The bound tool. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Source code in src\windlass\tools\__init__.py
extend
¶
remove
¶
get
¶
Return a bound tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Tool name. |
required |
Returns:
| Type | Description |
|---|---|
Tool
|
The tool. |
Raises:
| Type | Description |
|---|---|
ToolNotFoundError
|
When no tool of that name is bound. The message lists what is available. |
Source code in src\windlass\tools\__init__.py
names
¶
schemas
¶
Return every bound tool's definition in a provider's format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
style
|
str
|
|
'openai'
|
Returns:
| Type | Description |
|---|---|
list[dict[str, Any]]
|
Tool definitions, or |
list[dict[str, Any]]
|
lets an agent pass |
Source code in src\windlass\tools\__init__.py
aexecute
async
¶
Execute one tool call.
An unknown tool produces an error :class:~windlass.core.types.ToolResult
rather than an exception, so the agent can tell the model it hallucinated
a tool and let it recover.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
call
|
ToolCall
|
The call to execute. |
required |
Returns:
| Type | Description |
|---|---|
ToolResult
|
The result. |
Source code in src\windlass\tools\__init__.py
execute
¶
aexecute_many
async
¶
aexecute_many(
calls: Sequence[ToolCall], *, parallel: bool = True, limit: int = 8
) -> list[ToolResult]
Execute several tool calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calls
|
Sequence[ToolCall]
|
The calls to execute. |
required |
parallel
|
bool
|
Run them concurrently. Models routinely request three or four independent lookups in one turn; running those serially triples the user's wait for no reason. |
True
|
limit
|
int
|
Maximum concurrent executions. |
8
|
Returns:
| Type | Description |
|---|---|
list[ToolResult]
|
Results in the same order as |
Example
import asyncio @tool ... def echo(text: str) -> str: ... '''Echo text.''' ... return text registry = ToolRegistry([echo]) calls = [ToolCall(name="echo", arguments={"text": t}) for t in "ab"][r.content for r in asyncio.run(registry.aexecute_many(calls))] ['a', 'b']
Source code in src\windlass\tools\__init__.py
execute_many
¶
Blocking :meth:aexecute_many.
Source code in src\windlass\tools\__init__.py
needs_approval
¶
Return the calls whose tools require human approval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calls
|
Sequence[ToolCall]
|
Calls the model requested. |
required |
Returns:
| Type | Description |
|---|---|
list[ToolCall]
|
The subset that must be approved before running. |
Source code in src\windlass\tools\__init__.py
describe
¶
render_result
¶
Render a tool's return value as text for the model.
Strings pass through; everything else is JSON-encoded when possible and
repr-ed otherwise. Keeping this in one place means every tool's output
looks the same to the model regardless of who wrote it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
Any
|
The tool's return value. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A string safe to place in a |
Example
render_result({"a": 1}) '{"a": 1}' render_result("plain") 'plain'
Source code in src\windlass\interfaces\tool.py
function_schema
¶
function_schema(
fn: Callable[..., Any],
*,
name: str | None = None,
description: str | None = None,
skip: tuple[str, ...] = ("self", "cls")
) -> dict[str, Any]
Build a JSON Schema parameters object for a function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[..., Any]
|
The function to inspect. Type hints are resolved against its module
globals, so |
required |
name
|
str | None
|
Override for the tool name. Defaults to |
None
|
description
|
str | None
|
Override for the description. Defaults to the docstring summary. |
None
|
skip
|
tuple[str, ...]
|
Parameter names to omit. |
('self', 'cls')
|
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A JSON Schema object with |
dict[str, Any]
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
Note
*args and **kwargs are skipped — providers cannot express them,
and a tool that needs them almost always wants an explicit list or dict
parameter instead.
Example
def greet(name: str, excited: bool = False) -> str: ... '''Greet someone. ... ... Args: ... name: Who to greet. ... excited: Add an exclamation mark. ... ''' ... return name schema = function_schema(greet) schema["properties"]["name"]["description"] 'Who to greet.' schema["required"]['name']
Source code in src\windlass\tools\schema.py
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | |
parse_docstring
¶
Split a Google-style docstring into a summary and per-argument descriptions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
str | None
|
The raw docstring, or |
required |
Returns:
| Type | Description |
|---|---|
str
|
A |
dict[str, str]
|
before the first section header. |
Example
summary, args = parse_docstring( ... "Add numbers.\n\nArgs:\n a: First.\n b: Second.\n" ... ) summary, args["a"] ('Add numbers.', 'First.')
Source code in src\windlass\tools\schema.py
python_type_to_schema
¶
Convert a Python type annotation into a JSON Schema fragment.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
annotation
|
Any
|
The annotation to convert. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
A JSON Schema fragment. |
Example
python_type_to_schema(list[str]) {'type': 'array', 'items': {'type': 'string'}} python_type_to_schema(int | None)
Source code in src\windlass\tools\schema.py
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | |
tool
¶
tool(
fn: _F | None = None,
*,
name: str | None = None,
description: str | None = None,
parameters: dict[str, Any] | None = None,
timeout: float | None = None,
requires_approval: bool = False,
register: bool = False
) -> Any
Turn a Python function into an agent-callable tool.
Works bare (@tool) or with arguments (@tool(timeout=5)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
_F | None
|
The function, when used bare. |
None
|
name
|
str | None
|
Tool name shown to the model. Defaults to the function name. |
None
|
description
|
str | None
|
Model-facing description. Defaults to the docstring summary. Write a good one — it is how the model decides when to call this. |
None
|
parameters
|
dict[str, Any] | None
|
JSON Schema override. Derived from type hints by default. |
None
|
timeout
|
float | None
|
Seconds before the call is abandoned. |
None
|
requires_approval
|
bool
|
Pause the agent for human approval before running. Use this for anything that spends money or changes state. |
False
|
register
|
bool
|
Also add the tool to the global registry under |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
Any
|
class: |
Raises:
| Type | Description |
|---|---|
ValidationError
|
If the resulting name is not provider-legal. |
Note
The docstring is not decoration. Its summary becomes the model-facing description, and its Google-style argument section becomes the per-parameter descriptions in the JSON schema. Both are prompt text — the model decides when to call the tool, and what to pass, from them.
Example
Bare::
@tool
def search(query: str) -> list[str]:
# Docstring: "Search the product catalogue." plus an argument
# section describing `query`.
return db.search(query)
Configured::
@tool(name="charge_card", requires_approval=True, timeout=30)
async def charge(amount_cents: int, customer_id: str) -> dict:
# Approval-gated: the agent pauses before this ever runs.
return await payments.charge(customer_id, amount_cents)
Source code in src\windlass\tools\__init__.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | |