docs: concept annexation, provider/reference docs, ACT-1 groundwork
Mosaic concepts pages now own the adapted content; source/license metadata under docs/reference/concepts. Adds ACT-1 agent-context planning capture, pinned concept test package + preparation utility, foundation observation notes (durability, evidence, federation, onboarding, workflow), and the #1495 consolidation assessment. TOOLS.md updated for the host-dev launcher.
This commit is contained in:
@@ -0,0 +1,487 @@
|
||||
---
|
||||
summary: "Use OpenRouter's unified API to access many models in OpenClaw"
|
||||
read_when:
|
||||
- You want a single API key for many LLMs
|
||||
- You want to run models via OpenRouter in OpenClaw
|
||||
- You want to use OpenRouter for image generation
|
||||
- You want to use OpenRouter for music generation
|
||||
- You want to use OpenRouter for video generation
|
||||
title: "OpenRouter"
|
||||
---
|
||||
|
||||
OpenRouter routes requests to many models behind one API and one key. It is
|
||||
OpenAI-compatible, so OpenClaw talks to it over the same
|
||||
`openai-completions`-style transport used for other proxy providers.
|
||||
|
||||
## Getting started
|
||||
|
||||
<Tabs>
|
||||
<Tab title="OAuth">
|
||||
<Steps>
|
||||
<Step title="Run OAuth onboarding">
|
||||
```bash
|
||||
openclaw onboard --auth-choice openrouter-oauth
|
||||
```
|
||||
|
||||
OpenClaw opens OpenRouter's browser sign-in flow (PKCE), exchanges the
|
||||
code for an OpenRouter API key, and stores it in the default
|
||||
OpenRouter auth profile. On remote/headless hosts, OpenClaw prints the
|
||||
sign-in URL and asks you to paste the redirect URL after signing in.
|
||||
</Step>
|
||||
<Step title="(Optional) Switch to a specific model">
|
||||
Onboarding defaults to `openrouter/auto`. Pick a concrete model later:
|
||||
|
||||
```bash
|
||||
openclaw models set openrouter/<provider>/<model>
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
<Tab title="API key">
|
||||
<Steps>
|
||||
<Step title="Get your API key">
|
||||
Create an API key at [openrouter.ai/keys](https://openrouter.ai/keys).
|
||||
</Step>
|
||||
<Step title="Run API-key onboarding">
|
||||
```bash
|
||||
openclaw onboard --auth-choice openrouter-api-key
|
||||
```
|
||||
</Step>
|
||||
<Step title="(Optional) Switch to a specific model">
|
||||
Onboarding defaults to `openrouter/auto`. Pick a concrete model later:
|
||||
|
||||
```bash
|
||||
openclaw models set openrouter/<provider>/<model>
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Config example
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { vars: { OPENROUTER_API_KEY: "sk-or-..." } },
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openrouter/auto" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Model references
|
||||
|
||||
<Note>
|
||||
Model refs follow the pattern `openrouter/<provider>/<model>`. For the full list of
|
||||
available providers and models, see [/concepts/model-providers](/concepts/model-providers).
|
||||
</Note>
|
||||
|
||||
Bundled starter models enrich a nonempty public catalog. A failed live request
|
||||
reports a discovery failure rather than substituting these rows; a successful
|
||||
empty response stays empty:
|
||||
|
||||
| Model ref | Notes |
|
||||
| --------------------------------- | ---------------------------- |
|
||||
| `openrouter/auto` | OpenRouter automatic routing |
|
||||
| `openrouter/moonshotai/kimi-k2.6` | Kimi K2.6 via MoonshotAI |
|
||||
| `openrouter/moonshotai/kimi-k2.5` | Kimi K2.5 via MoonshotAI |
|
||||
|
||||
Any other `openrouter/<provider>/<model>` ref, including
|
||||
`openrouter/openrouter/fusion` (see [Fusion router](#fusion-router)), resolves
|
||||
dynamically against OpenRouter's live model catalog.
|
||||
|
||||
## Image generation
|
||||
|
||||
OpenRouter can back the `image_generate` tool. Set an OpenRouter image model
|
||||
under `agents.defaults.mediaModels.image`:
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { vars: { OPENROUTER_API_KEY: "sk-or-..." } },
|
||||
agents: {
|
||||
defaults: {
|
||||
mediaModels: {
|
||||
image: {
|
||||
primary: "openrouter/google/gemini-3.1-flash-image-preview",
|
||||
timeoutMs: 180000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw sends canonical OpenRouter image requests to the dedicated image API
|
||||
(`POST /api/v1/images`). Gemini image models additionally receive
|
||||
`aspect_ratio` and `resolution` hints, and image edits pass source images as
|
||||
`input_references`. Generated images come back as base64 (`b64_json`) with an
|
||||
optional `media_type`; when `media_type` is absent, OpenClaw sniffs the image
|
||||
format from the bytes.
|
||||
|
||||
Configured custom OpenRouter `baseUrl` destinations retain the existing
|
||||
chat-completions image route for compatibility with proxies that do not expose
|
||||
the dedicated endpoint. Use `agents.defaults.mediaModels.image.timeoutMs` for
|
||||
slower models; the `image_generate` tool's per-call `timeoutMs` still wins.
|
||||
|
||||
## Video generation
|
||||
|
||||
OpenRouter can back the `video_generate` tool through its asynchronous
|
||||
`/videos` API. Set an OpenRouter video model under
|
||||
`agents.defaults.mediaModels.video`:
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { vars: { OPENROUTER_API_KEY: "sk-or-..." } },
|
||||
agents: {
|
||||
defaults: {
|
||||
mediaModels: {
|
||||
video: {
|
||||
primary: "openrouter/google/veo-3.1-fast",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw submits text-to-video and image-to-video jobs, polls the returned
|
||||
`polling_url`, and downloads the finished video from OpenRouter's
|
||||
`unsigned_urls` or the job content endpoint. Reference images default to
|
||||
first/last-frame images; images tagged `reference_image` are sent as input
|
||||
references instead. The bundled `google/veo-3.1-fast` default supports 4/6/8
|
||||
second durations, `720P`/`1080P` resolutions, and `16:9`/`9:16` aspect ratios.
|
||||
Video-to-video is not supported: the upstream API only accepts text and image
|
||||
references.
|
||||
|
||||
## Music generation
|
||||
|
||||
OpenRouter can back the `music_generate` tool through chat-completions audio
|
||||
output. Set an OpenRouter audio model under
|
||||
`agents.defaults.mediaModels.music`:
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { vars: { OPENROUTER_API_KEY: "sk-or-..." } },
|
||||
agents: {
|
||||
defaults: {
|
||||
mediaModels: {
|
||||
music: {
|
||||
primary: "openrouter/google/lyria-3-pro-preview",
|
||||
timeoutMs: 180000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The bundled OpenRouter music provider defaults to `google/lyria-3-pro-preview`
|
||||
and also exposes `google/lyria-3-clip-preview`. OpenClaw sends `modalities:
|
||||
["text", "audio"]`, streams the response, collects the audio chunks, and saves
|
||||
the result as generated media for channel delivery. Lyria models accept one
|
||||
reference image through the shared `music_generate image=...` parameter.
|
||||
Streaming audio, transcript retention, and the derived SSE event envelope are
|
||||
bounded by `agents.defaults.mediaMaxMb` (the default audio cap is 16 MB).
|
||||
|
||||
## Text-to-speech
|
||||
|
||||
OpenRouter can act as a TTS provider through its OpenAI-compatible
|
||||
`/audio/speech` endpoint.
|
||||
|
||||
```json5
|
||||
{
|
||||
tts: {
|
||||
auto: "always",
|
||||
provider: "openrouter",
|
||||
providers: {
|
||||
openrouter: {
|
||||
model: "hexgrad/kokoro-82m",
|
||||
speakerVoice: "af_alloy",
|
||||
responseFormat: "mp3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If `tts.providers.openrouter.apiKey` is omitted, TTS falls back to
|
||||
`models.providers.openrouter.apiKey`, then `OPENROUTER_API_KEY`.
|
||||
|
||||
## Speech-to-text (inbound audio)
|
||||
|
||||
OpenRouter can transcribe inbound voice/audio attachments through the shared
|
||||
`tools.media.audio` path, using its STT endpoint (`/audio/transcriptions`).
|
||||
This applies to any channel plugin that forwards inbound voice/audio into
|
||||
media understanding preflight.
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
media: {
|
||||
models: [
|
||||
{
|
||||
provider: "openrouter",
|
||||
model: "openai/whisper-large-v3-turbo",
|
||||
capabilities: ["audio"],
|
||||
},
|
||||
],
|
||||
audio: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw sends OpenRouter STT requests as JSON with base64 audio under
|
||||
`input_audio` (OpenRouter's STT contract), not as multipart OpenAI form
|
||||
uploads.
|
||||
|
||||
## Fusion router
|
||||
|
||||
OpenRouter Fusion sends one OpenClaw model ref to several OpenRouter models in
|
||||
parallel, has OpenRouter judge their answers, and returns one final response
|
||||
through the normal OpenRouter endpoint. The upstream model slug is
|
||||
`openrouter/fusion`, so the OpenClaw model ref carries both the OpenClaw
|
||||
provider prefix and the upstream OpenRouter namespace:
|
||||
|
||||
```bash
|
||||
openclaw models set openrouter/openrouter/fusion
|
||||
```
|
||||
|
||||
Configure Fusion's panel and judge through the model's `params.extraBody`;
|
||||
those fields forward directly into the OpenRouter chat-completions request
|
||||
body. Fusion works with either OAuth or API-key onboarding; if you use OAuth,
|
||||
omit the `env.vars.OPENROUTER_API_KEY` line below.
|
||||
|
||||
```json5
|
||||
{
|
||||
env: { vars: { OPENROUTER_API_KEY: "sk-or-..." } },
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openrouter/openrouter/fusion" },
|
||||
models: {
|
||||
"openrouter/openrouter/fusion": {
|
||||
params: {
|
||||
extraBody: {
|
||||
plugins: [
|
||||
{
|
||||
id: "fusion",
|
||||
analysis_models: [
|
||||
"google/gemini-3.5-flash",
|
||||
"moonshotai/kimi-k2.6",
|
||||
"deepseek/deepseek-v4-pro",
|
||||
],
|
||||
model: "google/gemini-3.5-flash",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`analysis_models` is the parallel panel; `model` inside the Fusion plugin
|
||||
config is the judge model. Do not set top-level `tool_choice` to `"required"`
|
||||
in normal agent/chat turns to try to force Fusion: OpenClaw turns can include
|
||||
its own tool definitions, and a top-level required tool choice may pick one of
|
||||
those instead of the Fusion router. When this Fusion plugin config is present,
|
||||
OpenClaw adds a sanitized system-prompt note listing the configured analysis
|
||||
models and judge model, so the agent can answer questions about its own Fusion
|
||||
panel. Other `extraBody` fields are not copied into the prompt.
|
||||
|
||||
Fusion is slower by design: OpenRouter fans the prompt out to multiple
|
||||
analysis models, then runs a judge/synthesis step, so latency runs higher than
|
||||
a direct single-model request. Use it for deliberate, high-quality answers or
|
||||
escalation paths, not as a latency-sensitive default. Keep the panel small and
|
||||
pick faster analysis/judge models for quicker responses.
|
||||
|
||||
Test a configured ref with a one-shot local call:
|
||||
|
||||
```bash
|
||||
openclaw infer model run --local \
|
||||
--model openrouter/openrouter/fusion \
|
||||
--prompt "Reply with exactly: FUSION_OK" \
|
||||
--json
|
||||
```
|
||||
|
||||
## Authentication and headers
|
||||
|
||||
OpenRouter uses a Bearer token from your API key. OpenRouter OAuth is a PKCE
|
||||
login flow that issues an OpenRouter API key, so OpenClaw stores the result in
|
||||
the same `openrouter:default` API-key auth profile used by manual API-key
|
||||
setup.
|
||||
|
||||
To sign in or rotate the stored key on an existing install without rerunning
|
||||
full onboarding:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider openrouter --method oauth
|
||||
openclaw models auth login --provider openrouter --method api-key
|
||||
```
|
||||
|
||||
On verified OpenRouter requests (`https://openrouter.ai/api/v1`), OpenClaw adds
|
||||
OpenRouter's documented app-attribution headers:
|
||||
|
||||
| Header | Value |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| `HTTP-Referer` | `https://openclaw.ai` |
|
||||
| `X-OpenRouter-Title` | `OpenClaw` |
|
||||
| `X-OpenRouter-Categories` | `cli-agent,cloud-agent,programming-app,creative-writing,writing-assistant,general-chat,personal-agent` |
|
||||
|
||||
<Warning>
|
||||
If you repoint the OpenRouter provider at some other proxy or base URL, OpenClaw
|
||||
does **not** inject those OpenRouter-specific headers or Anthropic cache markers.
|
||||
</Warning>
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Response caching">
|
||||
OpenRouter response caching is opt-in. Enable it per model:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openrouter/auto": {
|
||||
params: {
|
||||
responseCache: true,
|
||||
responseCacheTtlSeconds: 300,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw sends `X-OpenRouter-Cache: true` and, when configured,
|
||||
`X-OpenRouter-Cache-TTL`. `responseCacheClear: true` forces a refresh for
|
||||
the current request and stores the replacement response. Snake_case
|
||||
aliases (`response_cache`, `response_cache_ttl_seconds`,
|
||||
`response_cache_clear`) are accepted, as is `responseCacheTtl` /
|
||||
`response_cache_ttl` without the `Seconds` suffix.
|
||||
|
||||
This is separate from provider prompt caching and from OpenRouter's
|
||||
Anthropic `cache_control` markers. It only applies on verified
|
||||
`openrouter.ai` routes, not custom proxy base URLs.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Anthropic cache markers">
|
||||
On verified OpenRouter routes, Anthropic model refs keep OpenRouter's
|
||||
Anthropic `cache_control` markers for better prompt-cache reuse on
|
||||
system/developer prompt blocks.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Anthropic reasoning prefill">
|
||||
On verified OpenRouter routes, Anthropic model refs with reasoning enabled
|
||||
drop trailing assistant prefill turns before the request reaches
|
||||
OpenRouter, matching Anthropic's requirement that reasoning conversations
|
||||
end with a user turn.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Thinking / reasoning injection">
|
||||
On supported non-`auto` routes, OpenClaw maps the selected thinking level
|
||||
to OpenRouter proxy reasoning payloads. `openrouter/auto` and unsupported
|
||||
model hints skip that injection. Stale `openrouter/hunter-alpha` refs also
|
||||
skip it, because OpenRouter could return final answer text in reasoning
|
||||
fields on that retired route.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="DeepSeek V4 reasoning replay">
|
||||
On verified OpenRouter routes, `openrouter/deepseek/deepseek-v4-flash` and
|
||||
`openrouter/deepseek/deepseek-v4-pro` fill missing `reasoning_content` on
|
||||
replayed assistant turns, keeping thinking/tool conversations in DeepSeek
|
||||
V4's required follow-up shape. OpenClaw sends OpenRouter-supported
|
||||
`reasoning.effort` values for these routes: `xhigh`/`max` map to `xhigh`,
|
||||
every other non-off level maps to `high`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="OpenAI-only request shaping">
|
||||
OpenRouter runs through the proxy-style OpenAI-compatible path, so native
|
||||
OpenAI-only request shaping such as `serviceTier`, Responses `store`,
|
||||
OpenAI reasoning-compat payloads, and prompt-cache hints is not forwarded.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Gemini-backed routes">
|
||||
Gemini-backed OpenRouter refs stay on the proxy-Gemini path: OpenClaw keeps
|
||||
Gemini thought-signature sanitation there, but does not enable native
|
||||
Gemini replay validation or bootstrap rewrites.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Provider routing metadata">
|
||||
OpenRouter supports a `provider` request object for underlying provider
|
||||
routing. Configure a default policy for all OpenRouter text-model requests
|
||||
with `models.providers.openrouter.params.provider`:
|
||||
|
||||
```json5
|
||||
{
|
||||
models: {
|
||||
providers: {
|
||||
openrouter: {
|
||||
params: {
|
||||
provider: {
|
||||
sort: "latency",
|
||||
require_parameters: true,
|
||||
data_collection: "deny",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw forwards that object to OpenRouter as the request `provider`
|
||||
payload. Use OpenRouter's documented snake_case fields, including `sort`,
|
||||
`only`, `ignore`, `order`, `allow_fallbacks`, `require_parameters`,
|
||||
`data_collection`, `quantizations`, `max_price`, `preferred_max_latency`,
|
||||
`preferred_min_throughput`, `zdr`, and `enforce_distillable_text`.
|
||||
|
||||
Per-model params override the provider-wide routing object:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"openrouter/anthropic/claude-sonnet-4-6": {
|
||||
params: {
|
||||
provider: {
|
||||
order: ["anthropic"],
|
||||
allow_fallbacks: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
This only applies on OpenRouter chat-completions routes. Direct Anthropic,
|
||||
Google, OpenAI, or custom provider routes ignore OpenRouter routing params.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Related
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Model selection" href="/concepts/model-providers" icon="layers">
|
||||
Choosing providers, model refs, and failover behavior.
|
||||
</Card>
|
||||
<Card title="Configuration reference" href="/gateway/configuration-reference" icon="gear">
|
||||
Full config reference for agents, models, and providers.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Reference in New Issue
Block a user