Skip to main content
Slow tools can stream progress or hand back a “resolve later” handle so a single long-running call never freezes the whole turn.

Quick Start

1

Stream progress from a tool

Add an on_progress=None parameter. The executor detects it and wires updates through automatically — no other changes needed.
2

Defer a long-running job

Return defer(...) and the model sees the note immediately — no blocking on a 10-minute render.

How It Works

The executor inspects each tool’s signature, forwards progress it emits, and records a deferred handle without blocking.

Which return type do I choose?

Pick the simplest option that fits how long your tool runs.

Configuration Options

ToolProgress describes a single incremental update a tool emits while working. DeferredToolResult is a handle a tool returns when it kicks off background work. The defer() factory builds a DeferredToolResult; handle_id defaults to a generated uuid.uuid4().hex when omitted. The enriched ToolResult carries these extra fields alongside result.

Common Patterns

Async tool with progress

An async def tool is awaited natively — no asyncio.run wrapper needed.

Deferred job resolved by handle

Return defer(...) now, then resolve the job later by its handle_id.

Structured error inspection

Read structured_error to get the error type and message instead of a flattened string.

Best Practices

Tools without an on_progress parameter are called the old way. No change is needed unless you want progress — the executor auto-detects the parameter via inspect.signature.
The executor swallows exceptions raised by an on_progress callback and keeps the tool running. A broken UI channel never kills a tool call.
The executor stamps tool_call_id and function_name automatically. Only set them yourself when you relay updates from another tool.
Job queues, video renders, and batch pipelines belong behind a defer() handle so the turn continues while the work runs.

Tool Progress Streaming

Event/sink-based progress via emit_tool_progress()

Async Tool Safety

Rules for async tools inside sync flows

Structured LLM Errors

How structured errors surface to the model

Custom Tools

Building your own tools