mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 08:31:33 +00:00
docs(anthropic): document opus 5 runtime contracts
This commit is contained in:
@@ -342,72 +342,62 @@ Related Anthropic docs:
|
||||
|
||||
</Note>
|
||||
|
||||
## Safety refusal fallback (Claude Fable 5)
|
||||
## Safety refusal fallback (Claude Opus 5 and Fable 5)
|
||||
|
||||
<Warning>
|
||||
Using Claude Fable 5 means also using Claude Opus 4.8. Fable 5 ships with
|
||||
safety classifiers that can decline a request, and Anthropic's sanctioned
|
||||
recovery is to have `claude-opus-4-8` serve that turn. OpenClaw opts into this
|
||||
automatically for direct API-key requests, so some Fable turns are answered
|
||||
and billed as Claude Opus 4.8. If your policy or budget cannot accept
|
||||
Opus-served turns, do not select `anthropic/claude-fable-5`.
|
||||
Claude Opus 5 and Fable 5 can route a safety-classifier refusal to another
|
||||
Claude model. OpenClaw opts into Anthropic's recommended per-category routing
|
||||
for direct API-key requests. A fallback-served turn is billed at the model
|
||||
that answered. If your policy requires every turn to stay on the requested
|
||||
model, do not use these models through the automatic fallback path.
|
||||
</Warning>
|
||||
|
||||
### Why this exists
|
||||
|
||||
Fable 5 classifiers return `stop_reason: "refusal"` on requests in restricted
|
||||
domains, and they also false-positive on benign-adjacent work (security
|
||||
tooling, life sciences, or even asking the model to reproduce its raw
|
||||
reasoning). Without a fallback, the turn dies with an error even though
|
||||
another Claude model would happily serve it - Anthropic's own refusal message
|
||||
tells API integrators to configure a fallback model.
|
||||
Opus 5 and Fable 5 classifiers return `stop_reason: "refusal"` on requests in
|
||||
restricted domains. Without a fallback, the turn ends with an error even when
|
||||
Anthropic has a recommended model for that refusal category.
|
||||
|
||||
### How it works
|
||||
|
||||
1. For every direct API-key request to `anthropic/claude-fable-5`, OpenClaw
|
||||
sends Anthropic's server-side fallback opt-in: the
|
||||
`server-side-fallback-2026-06-01` beta header plus
|
||||
`fallbacks: [{"model": "claude-opus-4-8"}]`. Claude Opus 4.8 is the only
|
||||
fallback target Anthropic permits for Fable 5.
|
||||
1. For every direct API-key request to `anthropic/claude-opus-5` or
|
||||
`anthropic/claude-fable-5`, OpenClaw sends the
|
||||
`server-side-fallback-2026-07-01` beta header plus
|
||||
`fallbacks: "default"`. Anthropic selects the recommended model for the
|
||||
reported refusal category.
|
||||
2. Only a safety-classifier decline triggers the fallback. Rate limits,
|
||||
overloads, and server errors behave exactly as before and go through
|
||||
OpenClaw's normal [model failover](/concepts/model-failover).
|
||||
3. The rescue happens inside the same call. A decline before any output is
|
||||
invisible apart from latency; the whole answer comes from Opus 4.8. On a
|
||||
invisible apart from latency; the whole answer comes from the serving
|
||||
model. On a
|
||||
mid-stream decline the partial text is kept as the prefix the fallback
|
||||
model continues from, while the declined model's reasoning and tool calls
|
||||
are discarded per Anthropic's replay rules (they must not be echoed back or
|
||||
executed).
|
||||
4. If Claude Opus 4.8 declines as well, the turn surfaces the refusal as an
|
||||
error, exactly like before this feature.
|
||||
4. If the recommended model declines as well, the turn surfaces the refusal
|
||||
as an error.
|
||||
|
||||
The fallback happens at the Anthropic API level, so `claude-opus-4-8` does not
|
||||
need to be in your configured model list or fallback chain - a Fable-capable
|
||||
API key can always serve Opus.
|
||||
The fallback happens at the Anthropic API level, so the serving model does not
|
||||
need to be in your configured OpenClaw fallback chain.
|
||||
|
||||
### Observability and billing
|
||||
|
||||
- A fallback-served turn records a `provider_fallback` diagnostic on the
|
||||
assistant message naming `fromModel` and `toModel`, and the message's
|
||||
`responseModel` reports `claude-opus-4-8`.
|
||||
- Anthropic bills per attempt: a decline before output is free, and the rescue
|
||||
bills at Claude Opus 4.8 rates (currently half of Fable 5 rates). OpenClaw's
|
||||
per-turn cost estimate prices fallback-served turns at Opus rates to match.
|
||||
- A mid-stream decline additionally bills the already-streamed Fable partial
|
||||
`responseModel` reports the model that answered.
|
||||
- Anthropic bills the fallback attempt at the serving model's rates. OpenClaw
|
||||
prices known Opus 4.8 fallback-served turns at Opus 4.8 rates.
|
||||
- A mid-stream decline additionally bills the already-streamed primary-model partial
|
||||
on Anthropic's side; that portion is reported in the API's per-attempt
|
||||
usage but not folded into OpenClaw's per-turn estimate.
|
||||
|
||||
### Scope
|
||||
|
||||
Applies to `anthropic/claude-fable-5` with API-key auth against
|
||||
`api.anthropic.com`. OAuth (Claude CLI subscription reuse), proxy base URLs,
|
||||
Bedrock, Vertex, and Foundry requests are unchanged and still surface
|
||||
refusals as errors there.
|
||||
|
||||
Verified live: a benign prompt asking Fable 5 to reproduce its raw chain of
|
||||
thought is declined with `category: "reasoning_extraction"` when sent without
|
||||
fallbacks, and the same prompt through OpenClaw returns a normal Opus-served
|
||||
answer with the `provider_fallback` diagnostic attached.
|
||||
Applies to `anthropic/claude-opus-5` and `anthropic/claude-fable-5` with
|
||||
API-key auth against `api.anthropic.com`. OAuth (including Claude CLI
|
||||
subscription reuse), proxy base URLs, Bedrock, Vertex, and Foundry requests
|
||||
are unchanged and still surface refusals as errors there.
|
||||
|
||||
See Anthropic's [refusals and fallback
|
||||
guide](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback)
|
||||
@@ -481,19 +471,20 @@ OpenClaw supports Anthropic's prompt caching feature for API-key auth.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Fast mode">
|
||||
OpenClaw's shared `/fast` toggle sets Anthropic's `service_tier` field for direct API-key traffic to `api.anthropic.com`.
|
||||
For Claude Opus 5 and Opus 4.8, OpenClaw's shared `/fast` toggle uses
|
||||
Anthropic's native fast mode for direct API-key traffic to `api.anthropic.com`.
|
||||
|
||||
| Command | Maps to |
|
||||
|---------|---------|
|
||||
| `/fast on` | `service_tier: "auto"` |
|
||||
| `/fast off` | `service_tier: "standard_only"` |
|
||||
| --- | --- |
|
||||
| `/fast on` | `speed: "fast"` plus `fast-mode-2026-02-01` |
|
||||
| `/fast off` | Standard speed; no `speed` field |
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-6": {
|
||||
"anthropic/claude-opus-5": {
|
||||
params: { fastMode: true },
|
||||
},
|
||||
},
|
||||
@@ -503,10 +494,12 @@ OpenClaw supports Anthropic's prompt caching feature for API-key auth.
|
||||
```
|
||||
|
||||
<Note>
|
||||
- Only applies to direct `api.anthropic.com` requests made with an API key. OAuth/subscription-token requests and proxy routes never get a `service_tier` field.
|
||||
- Native fast mode is a research preview for Claude Opus 5 and Opus 4.8. It can deliver up to 2.5x higher output-token throughput and is billed at `$10/$50` per million input/output tokens. OpenClaw applies the same 2x multiplier to cache pricing in its cost estimate.
|
||||
- Native fast mode only applies to direct `api.anthropic.com` requests made with an API key. OAuth/subscription-token requests, Claude CLI, proxies, Bedrock, Vertex, and Foundry never receive the beta or `speed` field.
|
||||
- Accounts need fast-mode access and a non-zero fast-mode rate limit. Anthropic returns a fast-specific `429` when the separate fast quota is exhausted or zero.
|
||||
- For other direct Anthropic models, `/fast` retains the existing Priority Tier mapping: on uses `service_tier: "auto"` and off uses `service_tier: "standard_only"`.
|
||||
- Explicit `serviceTier` or `service_tier` params override `/fast` when both are set.
|
||||
- Claude Opus 5 and Sonnet 5 do not support Priority Tier, so OpenClaw omits `service_tier` for those models.
|
||||
- On accounts without Priority Tier capacity, `service_tier: "auto"` may resolve to `standard`.
|
||||
- Claude Sonnet 5 supports neither native fast mode nor Priority Tier, so OpenClaw omits both fields.
|
||||
|
||||
</Note>
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { applyExtraParamsToAgent } from "./embedded-agent-runner/extra-params.js";
|
||||
import { isLiveTestEnabled } from "./live-test-helpers.js";
|
||||
import { isLiveAuthDrift, isLiveBillingDrift } from "./live-test-provider-drift.test-support.js";
|
||||
|
||||
const OPENAI_KEY = process.env.OPENAI_API_KEY ?? "";
|
||||
const ANTHROPIC_KEY = process.env.ANTHROPIC_API_KEY ?? "";
|
||||
@@ -119,56 +118,65 @@ describeLive("embedded agent extra params (live)", () => {
|
||||
});
|
||||
|
||||
describeAnthropicLive("embedded agent extra params (anthropic live)", () => {
|
||||
it("verifies Anthropic fast-mode service_tier semantics against the live API", async () => {
|
||||
it("verifies Claude Opus 5 default fallback against the live API", async () => {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
"x-api-key": ANTHROPIC_KEY,
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-beta": "server-side-fallback-2026-07-01",
|
||||
};
|
||||
|
||||
const runProbe = async (serviceTier: "auto" | "standard_only") => {
|
||||
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
model: "claude-sonnet-4-6",
|
||||
max_tokens: 32,
|
||||
service_tier: serviceTier,
|
||||
messages: [{ role: "user", content: "Reply with OK." }],
|
||||
}),
|
||||
});
|
||||
const json = (await res.json()) as {
|
||||
error?: { message?: string };
|
||||
stop_reason?: string;
|
||||
usage?: { service_tier?: string };
|
||||
};
|
||||
const errorMessage = json.error?.message ?? `HTTP ${res.status}`;
|
||||
if (!res.ok) {
|
||||
if (isLiveBillingDrift(errorMessage)) {
|
||||
console.warn(`[anthropic:live] skip service_tier ${serviceTier}: billing drift`);
|
||||
return null;
|
||||
}
|
||||
if (isLiveAuthDrift(errorMessage)) {
|
||||
console.warn(`[anthropic:live] skip service_tier ${serviceTier}: auth drift`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
expect(res.ok, errorMessage).toBe(true);
|
||||
return json;
|
||||
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({
|
||||
model: "claude-opus-5",
|
||||
max_tokens: 32,
|
||||
fallbacks: "default",
|
||||
messages: [{ role: "user", content: "Reply with OK." }],
|
||||
}),
|
||||
});
|
||||
const json = (await res.json()) as {
|
||||
error?: { message?: string };
|
||||
model?: string;
|
||||
stop_reason?: string;
|
||||
};
|
||||
|
||||
const standard = await runProbe("standard_only");
|
||||
if (!standard) {
|
||||
return;
|
||||
}
|
||||
expect(standard.usage?.service_tier).toBe("standard");
|
||||
expect(standard.stop_reason).toBe("end_turn");
|
||||
expect(res.ok, json.error?.message ?? `HTTP ${res.status}`).toBe(true);
|
||||
expect(json.model).toBe("claude-opus-5");
|
||||
expect(json.stop_reason).toBe("end_turn");
|
||||
}, 45_000);
|
||||
|
||||
const fast = await runProbe("auto");
|
||||
if (!fast) {
|
||||
it("verifies Claude Opus 5 native fast-mode contract against the live API", async () => {
|
||||
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"x-api-key": ANTHROPIC_KEY,
|
||||
"anthropic-version": "2023-06-01",
|
||||
"anthropic-beta": "fast-mode-2026-02-01",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "claude-opus-5",
|
||||
max_tokens: 32,
|
||||
speed: "fast",
|
||||
messages: [{ role: "user", content: "Reply with OK." }],
|
||||
}),
|
||||
});
|
||||
const json = (await res.json()) as {
|
||||
error?: { message?: string; type?: string };
|
||||
stop_reason?: string;
|
||||
usage?: { speed?: string };
|
||||
};
|
||||
|
||||
if (!res.ok) {
|
||||
expect(res.status).toBe(429);
|
||||
expect(json.error?.type).toBe("rate_limit_error");
|
||||
expect(json.error?.message).toContain("fast mode");
|
||||
return;
|
||||
}
|
||||
expect(["standard", "priority"]).toContain(fast.usage?.service_tier);
|
||||
expect(fast.stop_reason).toBe("end_turn");
|
||||
|
||||
expect(json.usage?.speed).toBe("fast");
|
||||
expect(json.stop_reason).toBe("end_turn");
|
||||
}, 45_000);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user