router.fetch() accepts two HTTP protocols and normalizes both into a RouterRequest before the policy or executor sees them.
Requests must be
POST with a JSON body. Responses use the same protocol as the request.
Normalized Request
RouterRequest.items is the canonical ordered conversation — messages, tool calls, tool results, prior-turn reasoning, and hosted tool calls. Tools, generation settings, reasoning, response format, metadata, and the stream flag are separate normalized fields.
The shared contract covers system/developer/user/assistant/tool messages, text and image input (image detail is auto, low, or high), function tools with choice controls, temperature/top-p/token limits/stop sequences, reasoning controls, text/JSON output formats, metadata, and streaming.
Pure Adapters
Normalize without HTTP dispatch:model becomes request.requestedModel but doesn’t force the policy to choose a matching candidate.
Inspect The Selection
- OpenAI
- Anthropic
requested_model echoes the model the caller sent; selected_model is the model that served. The field matches the managed platform’s wire shape, minus the platform-only conversation_id. OpenAI streams include dari_routing in the first completion chunk.
Streaming
Setstream: true in either protocol. Router Core translates executor events into the caller’s SSE format. It primes the stream before committing the HTTP response, so provider connection failures return a normal JSON error.
OpenAI stream_options is accepted for ecosystem compatibility and ignored — usage is always emitted. Matching OpenAI’s shape, streamed usage arrives after the finish chunk in a final chunk with an empty choices array, before data: [DONE].
After headers are committed, stream failures are emitted as SSE error events. An empty assistant reply yields a terminal finish event with no content.
The policy and executor share one abort signal — request or response-body cancellation aborts both and closes the stream iterator.
Tools
Both protocols normalize tool declarations, choice controls, assistant calls, and results. Arguments may be a JSON string or object in the normalized contract. Stream validation: tool calls must start before their argument deltas and end beforefinish. Anthropic output is serialized into sequential content blocks even when an executor produces overlapping tool calls.
Hosted Tools
Provider-hostedweb_search calls are part of the contract on the OpenAI protocol — they serialize as function-style tool calls named web_search whose arguments carry the provider’s replayable payload. The Anthropic serialization cannot represent them: output containing a hosted tool call on /v1/messages fails with a configuration error (hosted_tool_call_unrepresentable), so route hosted-tool models over the OpenAI protocol.
Reasoning
Reasoning is part of the shared contract. OpenAI callers receive readable thinking asreasoning_content (streamed as deltas) and encrypted continuations as reasoning_details; Anthropic callers receive thinking blocks. Every Anthropic thinking block gets a signature — the provider continuation when one exists, otherwise a portable dari-ir-v1 envelope so the block replays through the request parser. Redacted thinking has no streamable text and is emitted as one complete redacted_thinking block.
Unsupported Features
Router Core implements a shared subset, not full parity. OpenAI Responses, audio, logprobs, multiple choices, prediction, MCP servers, and other provider-specific features are outside this HTTP handler.Recognized but rejected fields fail with validation errors
Recognized but rejected fields fail with validation errors
A custom executor can use provider-specific features outside
router.fetch, but those features don’t become part of the portable request contract.Errors
Framework failures useRouterFrameworkError with a kind identifying the boundary and a code for the condition.
Config is checked at
createRouter() time. Input, policy output, and executor output are checked at each boundary. HTTP errors are serialized in the caller’s protocol shape. A known route with a non-POST method returns 405 with an Allow: POST header.