Skip to content

Top level

The everyday names, imported from agent_adapter. Each loads on first use, so import agent_adapter stays cheap. Concepts: Tools, Registry, Executor.

Four names have no docstring of their own, so here is what they are:

Name What it is
default_registry The process-wide Registry, created on first access, then reused
tool, wrap default_registry.tool and default_registry.wrap: register a function on it
Decision An alias of ApprovalDecision
__version__ The installed version string

Agent Adapter: framework-independent tools, parallel execution and flows for LLM agents.

Names load on first use (PEP 562), so import agent_adapter stays cheap. Every other public name lives in its home subpackage: errors in agent_adapter.errors, the rest in agent_adapter.exec, .approval, .hooks, .cost, .cache, .flow and so on.

Decision = ApprovalDecision module-attribute

__version__ = '1.1.0' module-attribute

default_registry = Registry() module-attribute

tool = default_registry.tool module-attribute

wrap = default_registry.wrap module-attribute

AdapterError

Bases: Exception

Base class for every error raised by Agent Adapter.

ApprovalDecision

Bases: BaseModel

A reviewer's decision; edited_args replaces the arguments when set.

ApprovalDenied

Bases: AdapterError

A reviewer denied a tool call.

ApprovalPending

Bases: AdapterError

A tool call waits for a decision that arrives later (deferred approval).

ApprovalRequest

Bases: BaseModel

A call (or flow node) waiting for a human decision.

AutoApprove

ApprovalPort that approves every request.

AutoDeny

ApprovalPort that denies every request.

BudgetHook

Bases: Hook

Caps spend per run, thread, user, team or everything, optionally per UTC day or month.

Raises:

Type Description
ConfigError

invalid limits, scope, window, store or estimate; scope="team" without scope_key, or scope_key with another scope.

on_llm_end(inv, billed) async

Settle the hold with what the call billed (as the ledger prices it), or release it.

scope_of(ctx)

The effective scope and the id spend is counted under for ctx.

Raises:

Type Description
ConfigError

scope_key raised or returned something other than a non-empty str.

status(ctx, *, scope_id=None) async

The budget of ctx's scope, or of scope_id when given (an approver's view).

Raises:

Type Description
ConfigError

as scope_of.

with_config(config)

A new hook with config merged over this one's (store and keys carried over).

Raises:

Type Description
ConfigError

unknown keys or invalid values.

CallableModel

Wraps fn(request); it returns an LLMResponse, the reply text, or a response mapping.

fn may be async or sync; a sync fn runs in a worker thread (context variables are copied), so a blocking local model or HTTP client never stalls the event loop. A mapping holds LLMResponse fields, including usage (a Usage or its field names), which the ledger prices; provider, model and message are filled in when missing. Exceptions raised by fn propagate unchanged.

cache_key_fields()

LLM-cache key material: which host function answers.

generate(request) async

Call the host function (or drain stream_fn when request.stream is set).

Raises:

Type Description
ModelOutputInvalid

the result does not form a valid response.

ToolUsageError

fn returned an unsupported type.

ConfigError

request.compression is invalid or could not run.

stream(request) async

stream_fn's events, or the generated reply replayed as deltas.

With output_schema, text deltas are followed by PartialOutput unless stream_fn sends its own.

Raises:

Type Description
ModelOutputInvalid

stream_fn ended without StreamDone.

ConfigError

request.compression is invalid or could not run.

CallbackApproval

ApprovalPort over a sync or async function returning a decision or a bool.

request(request) async

Ask fn; a bool becomes a decision by "callback".

Raises:

Type Description
ToolUsageError

fn returned something other than a decision or a bool.

ApprovalPending

fn deferred the decision.

DeferredApproval

ApprovalPort for out-of-band review (HTTP server, queues); emits no events itself.

consume(record, request) async

Use the stored decision of record once; the record and request are not trusted.

The record is read again from the store; never looks the call up by call_id.

Raises:

Type Description
ApprovalPending

the stored record is not decided (nothing is consumed).

ApprovalDenied

the stored decision differs from record's, or the record was already consumed, e.g. by a concurrent resume (fails closed).

ToolUsageError

request is for another run, call, tool or arguments, or the HTTP tool server parked the record (nothing is consumed).

ApprovalNotFound

the store has no such record.

prepare(request) async

The record for (run_id, call_id), created pending when new; never consumes it.

Advisory: batch callers (approval.batch.ask_each) prepare every call first and act only on what consume returns for these records.

Raises:

Type Description
ToolUsageError

non-JSON arguments, the call id has a record for another tool or other arguments, or the HTTP tool server parked the record.

request(request) async

Return the decision for (run_id, call_id) once decided, consuming it.

A record only answers the request it was made for: same kind, tool and original arguments (args_digest). The decision's edited_args are what then runs.

Raises:

Type Description
ApprovalPending

no decision yet; the first request creates the record.

ApprovalDenied

a concurrent request consumed the same decision (fails closed).

ToolUsageError

the call id has a record for another tool or other arguments.

sqlite(path, *, max_records=10000, timeout=5.0) classmethod

A port on a SQLiteApprovalStore file, shared by every process on this host.

Raises:

Type Description
ConfigError

a bad path, max_records or timeout (see SQLiteApprovalStore).

Event dataclass

A trace event (see tracing.events for the catalogue).

to_dict()

JSON-safe dict.

Executor

Bases: Batches, SyncTwins

Runs tools with run guards, hooks, approval, cache, retries, ledger and events.

With no registry it uses the default one (@tool). Sync code uses the SyncTwins methods. Run state (guards, run locks) is shared with every executor made by replace.

hooks property

The executor's own hooks; a policy's hooks run before them and are not listed.

policy property

The policy every call's spec passes through (Policy.apply sets it), or None.

sink property

The executor's stable sink: the constructor's sink, then every added one.

__reduce__()

Refuse pickling with a clear error (it holds locks and loop-bound state).

Raises:

Type Description
ToolUsageError

always.

add_sink(sink)

Also send events to sink, beside the existing ones; contexts made earlier get it too.

Raises:

Type Description
ToolUsageError

sink is not an EventSink.

context(*, run_id=None, thread_id='', user_id='', state=None, extras=None)

A context for a new (or given) run, carrying this executor's ledger, cache and sink.

end_run(run_id)

Forget a run's guard counters and locks.

model(model, *, cache_ttl=None)

model with this executor's hooks, response cache, ledger and sink around it.

remove_sink(sink)

Stop sending events to a sink add_sink added; False when it was not added.

replace(**changes)

A new Executor with some constructor arguments replaced; self is unchanged.

Raises:

Type Description
ToolUsageError

an unknown argument name.

resume(approval_id, decision=None, ctx=None, *, decided_by='', reason='') async

Run a parked call once: decide it first if pending (decision and decided_by are then required), else apply the decision already in the store.

Raises:

Type Description
ToolUsageError

no approval store, a flow-node or HTTP-server approval, a pending record without decision or decided_by, or ctx of another run.

ApprovalNotFound

unknown or expired id (an expiry also sends approval.expired).

ApprovalAlreadyDecided

already used, or decided otherwise in the store.

run(tool, args=None, ctx=None, *, call_id=None) async

Run a tool (its name, spec or function) and return its value; failures raise.

Raises:

Type Description
ToolUsageError

tool is not registered here, or args is not a mapping.

run_call(call, ctx=None, *, registry=None) async

Run one call; every failure is a ToolResult, only control flow raises.

run_scope(**context_kwargs)

async with ex.run_scope() as ctx: emits run.started/run.finished, then ends the run.

run_spec(spec, args=None, ctx=None, *, call_id=None) async

Run spec (registered or not) and return its value; failures raise.

Raises:

Type Description
ToolUsageError

args is not a mapping.

FallbackModel

An LLMPort over models: the next one answers after a fall_on error.

Each port keeps its SDK retries, so a hop happens after those are spent; pass client_kwargs={"max_retries": 0} to the provider ports for fast failover. Ports after the first get the request with model=None, so each uses its own configured model. With metadata["call_id"], every hop (hop 0 and a single port too) gets {call_id}:hop{i}. Hops emit llm.fallback to sink, else to the current context's sink.

Raises:

Type Description
ConfigError

no models, or a fall_on entry that is not a ModelError subclass.

cache_key_fields()

LLM-cache key material: every port in order.

generate(request) async

The first successful reply in order; the last error when every port fails.

Raises:

Type Description
ModelError

a non-fall_on error (at once), or the last port's error.

stream(request) async

The first port's stream that starts; a port that fails after its first event is final.

Raises:

Type Description
ModelError

as generate, or any error once events have been yielded.

Flow

Bases: Generic[S]

A portable graph flow over a pydantic state model; every builder method returns self.

edge(src, dst)

Add static edges; several destinations run as parallel branches in the next superstep.

end(*names)

Mark end nodes: they have no exit, and the flow is done once nothing else is pending.

graph()

The validated graph as JSON-able nodes and edges (fan-out, fan-in, routes, loops, gates).

Raises:

Type Description
FlowDefinitionError

the flow is invalid.

node(name, action, *, concurrency=None, timeout=None, retries=0, pause=False, prompt=None, cache=None)

Add a node: fn(state) / fn(state, ctx), or a Call, Map, Join or SubFlow.

prompts()

The prompt of every node that has one, by node name.

route(src, route, *, max_loops=None, on_exhausted=None)

Add a conditional exit from src; max_loops caps how often it may revisit nodes.

start(name)

Set the start node (default: the first node added).

to_mermaid()

The validated graph as a mermaid flowchart.

Raises:

Type Description
FlowDefinitionError

the flow is invalid.

validate()

Check the definition and return its graph.

Raises:

Type Description
FlowDefinitionError

every problem found, listed together (a sub-flow cycle alone).

with_prompts(prompts)

A copy whose named nodes carry the given prompts; this flow is unchanged.

Raises:

Type Description
FlowDefinitionError

a name is not a node of this flow.

FlowRunner

Runs a Flow locally: nodes on the executor, supersteps, merges, routes and pauses.

__init__(executor, *, approval=None, hooks=(), sink=None, max_steps=100, concurrency=None, model=None, checkpoint_key=None, seal_encoder=None, seal_decoder=None, trace_state=False, checkpoints=None)

trace_state=True puts each node's updated values on node.finished (updates).

With checkpoints, every superstep ends with a sealed checkpoint in the store and each tool call is logged, so recover continues a crashed run without re-running a finished call.

Raises:

Type Description
ToolUsageError

a bad max_steps, concurrency, codec, hook, trace_state or checkpoints.

ConfigError

checkpoints other than a MemoryCheckpointStore without a checkpoint_key.

dump_checkpoint(checkpoint)

The checkpoint as sealed JSON text, for storage outside this process.

Raises:

Type Description
FlowError

the checkpoint is not sealed by this runner (checkpoint-tampered).

inspect(flow, run_id, ctx=None) async

A durable run's stored status and checkpoint, checked, without saving anything.

Raises:

Type Description
FlowError

no checkpoints= store, or the flow's graph changed.

(CheckpointNotFound, CheckpointConflict, CheckpointStoreCorrupt)

as recover.

ToolUsageError

ctx is for another run, user, thread or principal.

load_checkpoint(flow, data)

A checkpoint from dump_checkpoint text; its MAC is checked before anything is built.

Raises:

Type Description
FlowError

not a checkpoint, a wrong MAC (checkpoint-tampered), another graph, or a value that cannot be loaded (e.g. a custom type and no seal_decoder).

recover(flow, run_id, ctx=None, *, rerun_in_doubt=False) async

Continue a crashed run from its last checkpoint; finished tool calls replay.

A call that started but logged no result runs again only if its tool is idempotent=True, or with rerun_in_doubt=True after you checked it. A run that was paused comes back paused (resume it as usual).

Raises:

Type Description
FlowError

no checkpoints= store, or the flow's graph changed since the save.

CheckpointNotFound

nothing is stored for run_id.

CheckpointConflict

another worker took the run first, it already ended, or the checkpoint is for another flow.

CheckpointStoreCorrupt

the stored checkpoint or its signature does not check out.

RecoveryNeedsDecision

a call may have run and its tool is not idempotent.

recover_all(flow, *, rerun_in_doubt=False, limit=100) async

Recover every unfinished run of flow (a worker on startup), one at a time.

Each run id maps to its result, or to the error that stopped its recovery (another worker took it, a call needs a decision, a node failed); the other runs still go on.

Raises:

Type Description
FlowError

no checkpoints= store.

resume(flow, checkpoint, decision, ctx=None) async

Continue a paused run; decision answers checkpoint.pending (the port is not asked).

Only the paused node(s) of the paused superstep run again. A denied tool decision raises ApprovalDenied inside the node (a FlowError, or a skipped Map item).

Raises:

Type Description
FlowError

the checkpoint is from another flow or graph, has nothing pending, holds another state model, or is not sealed by this runner's key (checkpoint-tampered); as run.

ApprovalAlreadyDecided

the checkpoint was already resumed.

run(flow, state, ctx=None) async

Run flow from its start node until it is done, paused or denied.

Raises:

Type Description
FlowDefinitionError

the flow is invalid.

FlowError

a node, route or merge failed (node names the node when known).

FlowLoopExceeded

a route without on_exhausted passed max_loops, or max_steps.

FlowError

state-not-sealable: the run would pause but some part has no canonical form; the pending approval records are withdrawn first.

GuardrailHook

Bases: Hook

check(stage, payload): True passes; False or a reason string raises GuardrailBlocked.

Payloads: args the validated arguments, result the value of an ok result (failed results are not checked), llm_request the LLMRequest, llm_response the LLMResponse.

Hook

Override any phase; the defaults do nothing (return None).

before_llm(inv) async

A ShortCircuit's value must be an LLMResponse.

before_node(inv) async

A ShortCircuit's value must be the node's state update mapping.

config()

Tunable settings (read by the optimiser); empty when the hook has none.

config_schema() classmethod

JSON schema of config(), or None when the hook is not tunable.

on_llm_end(inv, billed) async

Runs once when the call ends, however it ends; billed is its ledger entry or None.

with_config(config)

A copy with config applied.

Raises:

Type Description
ToolUsageError

the hook is not tunable.

LLMRequest

Bases: BaseModel

One provider-neutral model call.

LLMResponse

Bases: BaseModel

A model reply, normalised across providers.

parse(model)

Validate the structured output (parsed, else text as JSON) into model.

Raises:

Type Description
ModelContentFiltered

the provider withheld the output.

ModelOutputInvalid

the output does not validate against model.

Ledger

A LedgerPort: idempotent on (run_id, entry_id); keeps the max_runs latest runs.

max_runs property

How many runs the in-memory index keeps.

prices property

The price table used for entries recorded without a cost.

sinks property

Sinks every recorded entry is forwarded to, in order.

__reduce__()

Refuse pickling with a clear error (it holds locks and loop-bound state).

Raises:

Type Description
ToolUsageError

always.

clear(run_id=None)

Forget one run's entries, or everything (sinks are not touched).

entries(run_id=None, *, thread_id=None)

Entries by run, least recently recorded run first; first-record order within a run.

record(entry)

Price entry if its cost is unknown, store it (replacing a same-id entry), fan out.

summary(run_id=None)

Cost, cache and parallelism summary for one run, or for every run kept.

total_usd(run_id=None, *, thread_id=None)

Sum of known costs; unknown costs count as 0.

LedgerEntry

Bases: BaseModel

One billable call; idempotent on (run_id, entry_id).

from_usage(usage, **fields) classmethod

An entry whose token counts come from usage; fields sets the rest.

LoggingHook

Bases: Hook

Logs <subject>.start|finish|error lines; errors log at WARNING or above.

Each record carries aa_event and aa_fields attributes for structured handlers, and a "<event> <json>" message. Arguments are redacted; those of confirm tools, message content and result values are never logged.

MemoryApprovalStore

In-process ApprovalStore; one threading.Lock makes it safe across event loops.

Every operation is synchronous under the lock (no awaits inside), so loops in several threads (run_sync) can share one store. Past max_records it evicts the earliest consumed record, else the oldest decided one, else the oldest pending one (WARNING for both: a decided record is lost before use); each eviction is O(1).

consume(approval_id) async

Mark a decided record used; True only for the caller that flipped it.

A pending record is never consumed (False): only a decision can be used up.

Raises:

Type Description
ApprovalNotFound

unknown id.

create(request) async

Store a pending record (empty id / created_at filled in), or return the open one.

Atomic per (run_id, call_id): while the call has an unconsumed record, that record is returned unchanged and nothing is stored.

Raises:

Type Description
ToolUsageError

another call's record already has this approval id.

decide(approval_id, decision) async

Record the decision; only the first decide of a record succeeds.

Raises:

Type Description
ApprovalNotFound

unknown id.

ApprovalAlreadyDecided

the record is no longer pending.

ToolUsageError

edited_args is not plain JSON (nothing is stored).

list(*, run_id=None, status=None, limit=100) async

Records newest first, filtered by run and status.

Raises:

Type Description
ConfigError

unknown status or a negative limit.

MemoryCache

LRU byte cache bounded by max_entries; TTLs in seconds on clock (monotonic).

delete(key) async

Remove key if present.

get(key) async

The value, or None when missing or expired.

set(key, value, ttl=None) async

Store value; ttl <= 0 removes the key; inf / nan never expire.

Message

Bases: BaseModel

One chat message.

MissingDependency

Bases: AdapterError, ImportError

An optional extra needed for this call is not installed.

Prices

An immutable price table; build one with default(), load() or merged().

version property

The table version recorded on priced ledger entries.

caps(model)

Capability flags for model; defaults for an unknown model.

cost(model, usage, *, at=None, seconds=0.0, attempts=1)

USD for one call of attempts provider requests lasting seconds; None if unpriced.

Raises:

Type Description
ConfigError

a per-second model got a negative or non-finite seconds, or a per-call model got attempts < 1.

default() classmethod

The packaged table (cost/prices_default.json), loaded once per process.

empty(version='empty') classmethod

A table with no models, for pricing only your own with merged().

Raises:

Type Description
ConfigError

version is empty.

load(source) classmethod

A table from a mapping or a JSON file path.

Raises:

Type Description
ConfigError

the file cannot be read, is not JSON, or the table is invalid.

merged(overrides)

A new table where each model, alias and unit in overrides replaces this one's.

Raises:

Type Description
ConfigError

the overrides are invalid.

price(model, *, at=None, prompt_tokens=0)

Prices for model on at (default today, UTC) at this prompt size; None if unpriced.

resolve(model)

The table key for model: exact name, alias, then longest match; None if unknown.

A row key matches when the rest is a snapshot suffix (-2025-04-14, -20250514, -0613, -001, -latest); a caps-only key ending in - matches its family; a key ending in * matches any id starting with what precedes it ("*": any id). The longest matched prefix wins; on a tie, row, then pattern, then family. caps() reads this key; price() skips caps-only entries (ResolvedPrice.model is its key).

unit_cost(item, units, *, at=None)

USD for units of item ("provider:model", or "provider:model:cache_storage").

item is looked up exactly, then by the longest * pattern in units. A storage item uses its model's cache_storage_per_hour first (0 for a free model).

RateLimitHook

Bases: Hook

Delays tool calls by spec.kind and model calls by "llm"; unlisted kinds never wait.

One bucket per kind is shared by every event loop and thread: its state sits under a threading.Lock, and each caller books a slot there (GCRA) and then sleeps in its own loop, so no asyncio primitive is shared between loops. A cancelled wait returns its slot only when it was the latest booking, so a slot is never handed out twice.

RedisApprovalStore

ApprovalStore on Redis (agent-adapter[redis]), shared by every worker and host.

client_factory() returns a redis.asyncio.Redis (one per event loop), e.g. lambda: Redis.from_url(url) (keep decode_responses=False). create and every status change are Lua scripts; decide and consume compare-and-set the stored record, so each decision is consumed once. Each record expires ttl_s seconds after it was created (None: never; then run Redis with maxmemory-policy noeviction). Backend errors propagate (fail closed). On Redis Cluster put a hash tag in prefix ("{approvals}:"): the scripts address record keys built from it.

Raises:

Type Description
ConfigError

prefix is not a string ending in ":", or ttl_s is not a finite number > 0 or None.

aclose() async

Close the running loop's client.

consume(approval_id) async

Mark a decided record used; True only for the caller that flipped it.

Raises:

Type Description
ApprovalNotFound

unknown or expired id.

create(request) async

Store a pending record (empty id / created_at filled in), or return the open one.

Raises:

Type Description
ToolUsageError

another call's record already has this approval id, or the record holds a value JSON cannot carry.

decide(approval_id, decision) async

Record the decision; only the first decide of a record succeeds.

Raises:

Type Description
ApprovalNotFound

unknown or expired id.

ApprovalAlreadyDecided

the record is no longer pending.

ToolUsageError

edited_args is not plain JSON.

find(run_id, call_id) async

The unconsumed record of the call, or None.

get(approval_id) async

The record. Raises: ApprovalNotFound.

list(*, run_id=None, status=None, limit=100) async

Records newest first, filtered by run and status (expired ones are skipped).

Raises:

Type Description
ConfigError

unknown status or a negative limit.

Registry

Holds ToolSpecs by name; executors and exports read from here.

__reduce__()

Refuse pickling with a clear error (it holds locks and loop-bound state).

Raises:

Type Description
ToolUsageError

always.

add(spec)

Register a spec after checking its name and schema portability.

Raises:

Type Description
DuplicateTool

the name is taken.

SchemaNotPortable

the schema would fail on some provider.

add_many(specs)

Register several specs, all or nothing.

Raises:

Type Description
DuplicateTool

a name is taken or repeated; nothing is registered.

SchemaNotPortable

a schema would fail on some provider; nothing is registered.

catalogue()

Markdown tool list for planner prompts, generated so it never drifts from code.

for_target(target)

A new registry with the tools exported to target (targets None or listing it).

get(name)

Return the spec for name.

Raises:

Type Description
ToolNotFound

no tool has that name.

names()

Tool names in registration order.

remove(name)

Unregister name and return its spec.

Raises:

Type Description
ToolNotFound

no tool has that name.

subset(*, names=None, tags=None, kinds=None)

A new registry holding only tools that match every given filter.

Raises:

Type Description
ToolNotFound

a requested name is not registered.

tool(func=None, /, **options)

tool(func: F) -> F
tool(
    func: None = None, /, **options: Any
) -> Callable[[F], F]

Decorator: register a function as a tool and return it unchanged.

wrap(func, **options)

Register existing code as a tool without editing it.

RunLimits dataclass

Per-run limits; per_kind caps concurrent calls of one tool kind within a run.

SQLiteApprovalStore

ApprovalStore in a SQLite file (WAL), safe across threads, loops and processes.

Every call runs in a worker thread (asyncio.to_thread) on its own connection. create, decide and consume hold BEGIN IMMEDIATE, so writers on every process are serialised; timeout is how long one waits for the file lock (seconds). Past max_records it evicts like MemoryApprovalStore: the earliest consumed record, else the oldest decided, else the oldest pending (WARNING).

Raises:

Type Description
ConfigError

a bad path, max_records or timeout (here); on first use, a database without WAL support, a foreign aa_approvals table or another schema.

consume(approval_id) async

Mark a decided record used; True only for the caller that flipped it.

Raises:

Type Description
ApprovalNotFound

unknown id.

create(request) async

Store a pending record (empty id / created_at filled in), or return the open one.

Raises:

Type Description
ToolUsageError

another call's record already has this approval id, or the record holds a value JSON cannot carry.

decide(approval_id, decision) async

Record the decision; only the first decide of a record succeeds.

Raises:

Type Description
ApprovalNotFound

unknown id.

ApprovalAlreadyDecided

the record is no longer pending.

ToolUsageError

edited_args is not plain JSON.

find(run_id, call_id) async

The unconsumed record of the call, or None.

get(approval_id) async

The record. Raises: ApprovalNotFound.

list(*, run_id=None, status=None, limit=100) async

Records newest first, filtered by run and status.

Raises:

Type Description
ConfigError

unknown status or a negative limit.

Step dataclass

One tool call of a plan; callable args receive the results of depends_on.

ToolCall

Bases: BaseModel

One tool call requested by a model or a host.

ToolContext dataclass

Run-scoped data a tool may read; never part of the tool's schema.

child(*, call_id, **changes)

A copy for a nested call; extras is copied and approval is not inherited.

emit(type, /, **data)

Send a custom.<type> event to the sink; a no-op without one.

ToolDef

Bases: BaseModel

A tool as model ports receive it.

ToolError

Bases: AdapterError

Raised by tool code to report a failure; the message is shown to the model.

ToolResult dataclass

Outcome of one tool call.

ok property

True when the call succeeded.

to_dict()

JSON-safe dict without the exception object.

unwrap()

The value; else the kept exception, else ApprovalPending (pending) or ToolFailed.

ToolSpec dataclass

A registered tool: callable, argument model and execution policy.

Equality and hashing are by identity: two specs built from the same function are different tools (they may differ in fixed, rename or policy).

call(validated, ctx=None) async

Call the function with already-validated args (sync ones in a worker thread).

A direct call is no executor attempt: current_attempt() is None inside it.

Raises:

Type Description
ToolUsageError

validated is not an instance of this tool's args_model.

invoke(args=None, ctx=None) async

Validate args, call the function, apply result.

Raises:

Type Description
ToolArgsInvalid

arguments are missing, unknown or the wrong type.

json_schema()

Neutral JSON schema of the LLM-supplied arguments (input_schema if set); a copy.

result_schema()

output_schema, else the return annotation's schema; None if absent or result set.

tool_def(*, strict=True)

The tool as model ports take it (see strict_downgraded).

validate_args(args)

Validate LLM-supplied arguments against the tool's model.

Raises:

Type Description
ToolArgsInvalid

arguments are missing, unknown or the wrong type.

Usage

Bases: BaseModel

Normalised token usage of one or more LLM calls.

prompt_tokens property

Every input token: billable, cached and cache-write.

acall(tool, /, **args) async

Run tool (a function, spec or name) on the default executor and return its value.

attach(target_or_name, registry=None, *, executor=None, **opts)

The framework's native tools for registry (default: default_registry).

target_or_name is an integration name ("langgraph", "openai-agents") or an object, class or module from that framework. executor runs every call (default: the process-wide default executor). Approval stays on the executor: an option such as approval=True is refused. Other opts go to an integration's own attach method.

Raises:

Type Description
ToolUsageError

an approval option, a bad registry or executor, or options the integration does not take.

IntegrationError

no installed integration matches; the message lists them.

MissingDependency

the integration's extra is not installed.

build_spec(func, *, name=None, description=None, fixed=None, hide=(), rename=None, result=None, args_model=None, **policy)

Build a ToolSpec from a function's signature and docstring.

Parameters:

Name Type Description Default
hide Iterable[str] | str

Parameter names (or one name) hidden from the LLM; their defaults apply.

()
rename Mapping[str, str] | None

Python parameter name -> name the LLM sees.

None
args_model type[BaseModel] | None

Use this model as the argument schema instead of the signature's types; its field names must equal the LLM-facing parameter names.

None
**policy Any

Execution options such as kind, tags, parallel_safe, timeout.

{}

Raises:

Type Description
ToolDefinitionError

the function cannot be exposed as a tool.

call(tool, /, **args)

acall from sync code, on the process's shared loop; raises like Executor.run.

configure(registry=None, **options)

Make Executor(registry, **options) the default (call, integrations) and return it.

Raises:

Type Description
TypeError

an unknown option name.

ToolUsageError

a bad option value (see Executor).

run_sync(work, *, timeout=None)

Run work to completion on a fresh event loop and return its result.

Without a running loop in this thread it uses asyncio.run. Inside a running loop (Jupyter, an async framework) it runs that loop in a one-shot worker thread with a copy of the current contextvars and blocks this thread until done. Loop-local values (LoopLocal) are closed before the loop ends.

Parameters:

Name Type Description Default
work Awaitable[T] | Callable[[], Awaitable[T]]

An awaitable, or a zero-argument callable returning one (called inside the loop).

required
timeout float | None

Seconds before the work is cancelled. In the worker-thread case the caller also stops waiting CALLER_GRACE_S later, even if the work blocks its loop.

None

Raises:

Type Description
TimeoutError

the work ran past timeout.

ToolUsageError

work is neither awaitable nor callable, or timeout is not a positive number.

run_tool_loop(model, registry, messages, *, max_steps=8, executor=None, ctx=None, output_schema=None, tool_choice='auto', concurrency=None, stage='tool_loop', request_options=None, compression=None, on_text=None, on_event=None, run_id=None, checkpoints=None, checkpoint_key=None) async

Call the model, run its tool calls, feed the results back, until it answers.

messages ending in an assistant turn whose tool calls lack results (a loop that stopped on pending or max_steps) resumes by running those calls first; pass the same ctx (or one with the same run_id) so approvals and run guards carry over.

With compression, the history is compressed before each model call and the loop goes on with the shorter history (also in the result); a context overflow runs the policy's if_still_too_long once and retries the call once.

With on_text, each model call streams through the port's stream() and every text chunk is passed to on_text (sync or async) as it arrives. on_event (also streaming) gets every other stream event: ToolCallDelta, PartialOutput, and the StreamDone that ends each model call.

With checkpoints (and a run_id, or a ctx with one), the history is saved signed after each step and every tool call is logged; recover_tool_loop continues the run after a crash without re-running a finished call. A paused run resumes here.

Raises:

Type Description
ToolUsageError

max_steps < 1, or bad request_options (reserved, unknown or invalid keys, or metadata["call_id"]), before anything runs.

ConfigError

compression is not a CompressionPolicy, or no context window is known for the model, before anything runs; a durable store without a key.

CheckpointConflict

checkpoints holds this run as running or ended.

ModelError

the model call failed.

set_default_executor(executor)

Make executor the default for every integration that is not given one.

Raises:

Type Description
ToolUsageError

executor is not an Executor.

spec_from_schema(name, description, input_schema, func, *, output_schema=None, portable=None, context_param=None, **policy)

A tool whose arguments are described by input_schema instead of a signature.

func receives the set arguments as keywords named exactly as the schema properties (plus context_param=ctx when set); unset optional arguments are not sent.

Parameters:

Name Type Description Default
portable bool | None

None checks the schema and records the result; True makes registry.add reject a non-portable schema; False skips the check.

None
**policy Any

Execution options as for build_spec (kind, tags, effects, ...).

{}

Raises:

Type Description
ToolDefinitionError

bad name, empty description, non-object schema or bad option.