Skip to content
Registry

provider-anthropic

v1.2.26

Anthropic Messages API provider worker; implements provider::anthropic::stream and provider::anthropic::refresh_models behind llm-router.

license: Apache-2.0anthropicclaudellmmessagesprovider

Install

bash
iii trigger compose::add worker=provider-anthropic@1.2.26

Run in your project with the iii engine running. Exact versions are immutable and digest-pinned.

provider-anthropic

Claude models behind llm-router. Install this worker next to the router, give it an API key, and the Anthropic catalog — streaming, extended thinking, tool use, vision, automatic prompt caching — appears behind the router's single front door (router::chat / router::complete). You never call this worker directly: the router invokes it worker-to-worker, and iii-permissions.yaml blocks agent access to provider::anthropic::*.

Install

iii trigger compose::add worker=provider-anthropic

The provider does nothing on its own — it plugs into the router:

iii trigger compose::add worker=llm-router

iii trigger compose::add resolves each worker and its dependencies, writes exact declarations to worker-compose.yaml, and reconciles the Compose project.

Quickstart

Give the router a credential: paste a key into the anthropic slice of the engine's llm-router configuration entry, or set ANTHROPIC_API_KEY in the router's environment.

{ "providers": { "anthropic": { "api_key": "sk-ant-…" } } }

The router picks up the change and kicks model discovery; Claude models land in the catalog (router::models::list). Then make a first call — Node shown, any SDK works:

const res = await iii.trigger('router::complete', {
  model: 'claude-sonnet-4-6',
  messages: [{ role: 'user', content: [{ type: 'text', text: 'Hello' }], timestamp: Date.now() }],
}, { timeout_ms: 320_000 }); // outer timeout ≥ the router's 300s stream budget
// res: { message, usage, provider, model } — message is the final AssistantMessage

For token-by-token streaming, call router::chat with an iii channel — the walkthrough lives in llm-router's Quickstart. Request extended thinking with thinking_level; the worker maps the level to an Anthropic thinking budget from the model's catalog record. xhigh degrades to high on models that don't support it, and the level is dropped on models that don't support thinking at all.

Configuration

All operator configuration lives in the router's llm-router entry — this worker keeps no config of its own:

"anthropic": {
  "api_key": "sk-ant-…",                               // or ANTHROPIC_API_KEY in the router's env
  "api_url": "https://api.anthropic.com/v1/messages",  // override for proxies / gateways
  "max_tokens": 8192                                   // output ceiling when a request sets none
}

Worker-side environment variables:

Variable Default Meaning
PROVIDER_ANTHROPIC_CACHE enabled 0/false disables automatic prompt-cache markers
PROVIDER_ANTHROPIC_CACHE_TTL 1h TTL of the shared-prefix markers on the sectioned path; 5m restores the default cache
PROVIDER_ANTHROPIC_PREFIX_MISMATCH drop_block thinking.block_binding.prefix_mismatch_behavior sent (with the thinking-binding-controls-2026-08-01 beta) on every thinking request: drop_block drops a replayed thinking block whose prefix was edited, error returns the 400 instead, off (also 0/false/none) sends neither the field nor the beta (for gateways that reject it). Case-insensitive; any other value logs one warning and uses drop_block. On accounts created before 2026-08-31, sending the field is what turns enforcement on: after a prefix edit, replayed thinking blocks that sit after the edited element and were minted before the edit (every such block, for a system/tools edit), which still reached the model without the field, are now dropped on every later request; blocks before the edited element and blocks minted after the edit survive; off restores the old behaviour. Dropped blocks become one anthropic dropped replayed thinking blocks: […] warning on the message and a worker-log warn with the session id and model when the list arrives (aborted and errored responses included; grep the reason, e.g. prefix_binding_mismatch)
PROVIDER_ANTHROPIC_CAPTURE_DIR unset Debug only, absolute path (a relative one logs one warning and captures nothing): append every request that has a session id to /.jsonl (bytes outside [a-z0-9.-], _ and uppercase included, written as _xx hex; the body plus anthropic-beta; auth headers are never written; on Unix the dir it creates is 0700 and each file 0600). The files hold user content, tool output, and images: keep them out of the repo and delete them after use
III_WS_URL ws://127.0.0.1:49134 engine WebSocket to attach to when --url is not set

The binary also takes the standard worker CLI flags: --url (engine WebSocket), --manifest (print the registry manifest and exit), and --config (accepted but ignored with a warning — provider config comes from the llm-router configuration entry).

Prompt caching needs no setup: markers go on the system prompt, the tools tail, and the last stable assistant turn whenever the prefix is big enough to be worth a cache write. When the router forwards system_sections, the system field becomes one text block per section and the marker moves to the block flagged cache_boundary (the frozen agent-profile prefix) whatever its size — Anthropic applies its per-model token minimum over the whole prefix, tools included, and silently skips a short one — so the per-session tail after it no longer invalidates the shared entry. At most two system blocks are marked, keeping the total at Anthropic's four. On that sectioned path the boundary block (and the tools marker ahead of it) use the 1-hour cache (ttl: "1h", 2x base on the one write, reads unchanged) so a profile stays warm across sessions up to an hour apart; the per-turn messages anchor keeps the 5-minute default, which also satisfies the longer-before-shorter TTL rule. PROVIDER_ANTHROPIC_CACHE_TTL=5m goes back to 5 minutes everywhere.

Models

The catalog slice is live GET /v1/models: model ids, display names, context windows, output ceilings, and the capability flags (adaptive thinking, xhigh effort, vision) all come from the API on every refresh, so a newly shipped model (Opus 5.5, Sonnet 5, Fable 5.1, …) appears in the picker as soon as the API lists it — no code or SDK change required. Rows the API marks as thinking-capable but not adaptive-capable (the pre-4.6 generation) are dropped because this provider only implements adaptive thinking; the haiku family is the exception and stays with thinking gated off.

The API does not publish pricing, so that is the one hand-maintained table: src/curated.rs, USD per MTok keyed by base model id (date suffixes stripped). A model missing there still routes and shows up; it only loses cost enrichment, and the harness refuses max_cost_usd budgets on it. Update the table against platform.claude.com/docs/en/about-claude/pricing when Anthropic ships or reprices a model.

Notes

  • Token counting: provider::anthropic::count_tokens (behind router::count_tokens) posts the assembled prompt to the messages endpoint's count_tokens sibling for an exact provider-metered count; it never runs the model and costs nothing.
  • Structured output: the Messages API has no native JSON mode; every catalog record declares supports_structured_output: false, and a forwarded response_format is reported in warnings and ignored.
  • Errors: 401/403 → auth_expired, 429 → rate_limited, 413/context → context_overflow, 5xx/network → transient, other 4xx → permanent. The worker never retries — the router owns retry policy.
  • Credentials: resolved per request via router::provider::resolve (config slice → ANTHROPIC_API_KEY on the router → none). Both api_key (x-api-key) and oauth (Bearer) shapes work; v1 performs no OAuth refresh.
  • Identity binding: the router issues a registration_token on first registration, persisted in state (scope provider-anthropic). If that state is lost the router rejects re-registration — clear the binding on the router side to re-pair.

Tests

cargo test                                            # unit (pure modules + TCP stubs)
III_ENGINE_BIN=$(which iii) cargo test --test integration -- --test-threads=1

The integration suite spawns a real engine, the real router (path dep), this provider, and a local stub upstream — no external API calls anywhere.

Search the registry

Find workers, kits and executables, or jump to a page.