server: # Loopback by default: reachable from the network is worth deciding on # deliberately. Set it to 0.0.0.0 to serve other machines. The container image # sets TINYROUTER_HOST=0.0.0.0 itself, since a published port cannot reach a # process listening only on loopback inside it. host: ${TINYROUTER_HOST:-127.0.0.1} port: 8080 api_key: ${TINYROUTER_API_KEY} # Or name several callers instead, so one can be revoked by deleting a line. # Set api_key or api_keys, never both. # api_keys: # alice: ${ALICE_KEY} # ci: ${CI_KEY} # /healthz, /readyz and /metrics are open by default, and disclose which # providers you use, how many tokens you spend, and which are failing right # now. Set this to put /readyz and /metrics behind api_key; /healthz stays # open so container probes keep working. protect_observability: false # Bun's own connection idle timeout, in seconds. 0 disables it. If set, it # must be longer than body_timeout_ms below, or it would close the connection # before that timeout could answer. idle_timeout_seconds: 0 # Bounds the gap between request-body chunks: a slow upload is fine, one # that stops arriving is not. body_timeout_ms: 30000 # Browser origins allowed to read the observability endpoints (/, /healthz, # /readyz, /metrics, /v1/models). Omitted means no response carries CORS # headers at all. Exact origins only - no "*", no paths - and completions are # never shared, so enabling this cannot let a page spend your provider credit. # allow_origin: ["http://localhost:5173"] routing: retries: 0 retry_statuses: [429, 500, 502, 503, 504, 529] # Retries of the same target wait for the provider's Retry-After when sent, # otherwise an exponential jittered delay; a single wait never exceeds # backoff_max_ms. Fallback to the next target is always immediate. backoff_initial_ms: 200 backoff_max_ms: 2000 # Demote a provider that keeps failing to a last resort instead of paying its # timeout on every request. Off by default; set failures to enable. circuit_breaker: failures: 0 cooldown_ms: 30000 # Optional deterministic request filters, applied in order per attempted # provider. A filter with a providers list applies only to those providers, # so local targets can stay unfiltered. See "Request filters" in the README # for the built-in pattern names. # filters: # - type: block # patterns: [anthropic_api_key, openai_api_key, aws_access_key, private_key] # - type: redact # patterns: [email] # providers: [openai, anthropic, gemini] providers: openai: type: openai api_key: ${OPENAI_API_KEY} # Bounds provider progress: this long for response headers, then this long # again for each gap between body chunks. A slow model is fine; a silent # provider is cut loose instead of hanging the client. timeout_ms: 60000 anthropic: type: anthropic api_key: ${ANTHROPIC_API_KEY} timeout_ms: 60000 # Claude Opus 4.7 and later reject a non-default temperature or top_p, and # the newest models also reject forced tool use, with an upstream 400 that # ends routing. If this provider's targets are such models, drop the one and # skip targets for the other. See "Anthropic provider options" in # docs/routing.md. # sampling_parameters: drop # forced_tool_choice: skip gemini: type: gemini api_key: ${GEMINI_API_KEY} timeout_ms: 60000 # Model IDs are passed to the provider verbatim and differ by account. If a # target returns 404, list what your key can reach - see "Finding model IDs" # in the README. routes: fast: - openai/gpt-5-mini # Claude 4.5 models reject the adaptive thinking that reasoning_effort # becomes, and that 400 ends routing. If clients send reasoning_effort, # use a model that accepts it, such as claude-sonnet-4-6. - anthropic/claude-haiku-4-5 smart: - anthropic/claude-sonnet-4-6 - openai/gpt-5.2 economical: - gemini/gemini-3.6-flash - openai/gpt-5-mini