Skip to main content
A custom executor replaces provider execution for one or more model declarations. Use one when a model isn’t in Pi’s catalog, needs a private transport, or requires provider-specific behavior outside the shared HTTP contract. Policies stay independent — they choose a model, Router Core dispatches to whatever executor that model declares.

Return A Completion

Completion content can include text, tool calls, reasoning, and hosted web_search calls. Finish reason must be stop, length, or tool_calls.

Return A Stream

The normalized stream contract uses seven event types:
reasoning_delta streams readable thinking text; reasoning_end closes the block and may carry the provider identity and stable item ID used to sign plain reasoning, or an encrypted continuation. Redacted thinking is a bare reasoning_end with redacted: true and no prior deltas.Router Core validates ordering: tool calls must start before their deltas end, an index can’t mix text, reasoning, and open tool calls, open tools and open reasoning blocks must close before finish, and nothing follows finish.

Declare Capabilities

Custom models need explicit capability metadata so Router Core knows which requests they can handle:
The satisfies RouterModel ascription matters for standalone declarations: without it, reasoningEfforts infers as string[] and the object no longer type-checks when passed to createRouter. Objects written inline inside models: [...] are contextually typed and don’t need it. When omitted: provider defaults to the prefix before /, API defaults to the executor name, reasoning efforts default to ["off"], all capabilities default to false.

Cancellation And Cleanup

The executor receives the same AbortSignal as the policy. Request cancellation or response-body cancellation abort it. Router Core calls return() on the stream iterator when streaming ends early. Release transport resources in a finally block.

Retry And Fallback

The executor owns same-model retries — Router Core doesn’t classify provider errors or decide what’s retryable. Cross-model fallback belongs to createRouter: with fallback: { enabled: true }, when the selected model’s executor call fails, the router retries once on the first other eligible candidate before returning an error. requiresDifferentProvider: true restricts the fallback to another provider. The response reports the model that actually served — in dari_routing and the X-Router-Selected-Model header — and if the fallback model doesn’t support the decision’s reasoning effort, its own default effort is used. A lease pinning the failed primary is dropped so the next turn selects fresh.
For richer behavior — error classification, retry budgets, billing-aware fallback chains — host the phased API yourself or use the managed platform.