provider-anthropic
v1.1.3Anthropic Messages API provider worker; implements provider::anthropic::stream and provider::anthropic::refresh_models behind llm-router.
- macOS: arm64 · x64
- Linux: arm64 · armv7 · x64
- Windows: arm64 · x64 · x86
exact versions are immutable; binary and bundle artifacts are digest-pinned.
readme
open as markdownprovider-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 worker add provider-anthropicThe provider does nothing on its own — it plugs into the router:
iii worker add llm-routeriii worker add fetches the binary, writes a config block into
~/.iii/config.yaml, and the engine starts the worker on the next
iii start.
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 AssistantMessageFor 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 |
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.
Models
The catalog slice is live GET /v1/models merged with a curated capability
snapshot — context windows, output ceilings, thinking budgets, pricing
(USD per MTok). Live ids the snapshot doesn't know get conservative
defaults; curated aliases the API doesn't enumerate are kept, so the catalog
has no cold hole before first discovery. The snapshot lives in
src/curated.rs — update it against models.dev when
Anthropic ships new models; discovery only supplies bare ids.
Notes
- Structured output: the Messages API has no native JSON mode; every
catalog record declares
supports_structured_output: false, and a forwardedresponse_formatis reported inwarningsand 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_KEYon the router → none). Bothapi_key(x-api-key) andoauth(Bearer) shapes work; v1 performs no OAuth refresh. - Identity binding: the router issues a
registration_tokenon first registration, persisted in iii-state (scopeprovider-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=1The integration suite spawns a real engine, the real router (path dep), this provider, and a local stub upstream — no external API calls anywhere.