skip to content
$worker

provider-anthropic

v1.1.3

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

iiiverified
286 installs79 in 7d7 today
install
$iii worker add provider-anthropic
  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64 · x86

exact versions are immutable; binary and bundle artifacts are digest-pinned.

README.md

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 worker add provider-anthropic

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

iii worker add llm-router

iii 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 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
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 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 iii-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.