From 13197329cedd98589e9446c8a7dfac257b293a79 Mon Sep 17 00:00:00 2001 From: Sean Callan Date: Wed, 10 Jun 2026 08:40:47 -0400 Subject: [PATCH 1/2] feat(models): add Fable 5 as a selectable Anthropic model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Fable 5 as a new Opus-class Anthropic model, selectable in the picker without changing the default (Opus 4.8 stays default). Mirrors the Opus 4.8 transition but for a brand-new, non-aliased name with a 1M-context variant. - modelRegistry: add `claude-fable-5` (200K) and `claude-fable-5[1m]` (1M) entries plus the 1M -> 200K fallback. The `[1m]` suffix lets `is1mContextModel` classify the 1M variant with no special case. - modelCapabilities: Fable 5 joins the effort, xhigh, and max sets (not fast — fast mode is Opus-4.6-only). - pricing: `claude-fable-5` at $30/$150 per Mtok (2x Opus). The prefix `starts_with` lookup means one entry also covers the `[1m]` variant. - Refresh the `xhigh requires Opus 4.7+` strings (CLI help, command palette) to include Fable 5. - Docs: model table, default-model row, and `--model` flag examples. The concrete `claude-` prefixed id passes the `is_claude_code_model_alias` gate for free, so no Rust routing/validation or default-model changes are required — the id flows verbatim to the Claude CLI `--model` arg and every picker/slash-command surface renders it from the registry. --- .../docs/features/agent-configuration.mdx | 4 +++- site/src/content/docs/features/cli-client.mdx | 4 ++-- site/src/content/docs/features/settings.mdx | 2 +- src-cli/src/commands/chat.rs | 2 +- .../components/chat/modelCapabilities.test.ts | 20 +++++++++++++++++++ .../src/components/chat/modelCapabilities.ts | 8 ++++---- .../src/components/chat/modelRegistry.test.ts | 1 + src/ui/src/components/chat/modelRegistry.ts | 7 +++++++ .../components/command-palette/commands.ts | 2 +- src/usage/pricing.rs | 19 ++++++++++++++++++ 10 files changed, 59 insertions(+), 10 deletions(-) diff --git a/site/src/content/docs/features/agent-configuration.mdx b/site/src/content/docs/features/agent-configuration.mdx index 8e46fa6fa..a8e9e813e 100644 --- a/site/src/content/docs/features/agent-configuration.mdx +++ b/site/src/content/docs/features/agent-configuration.mdx @@ -19,6 +19,8 @@ Select a model from the dropdown in the chat header. Available models: | **Sonnet 4.6** | `sonnet` | Fast and capable | | **Sonnet 4.6 1M** | `claude-sonnet-4-6[1m]` | Sonnet 4.6 with a 1M context window (extra usage) | | **Haiku 4.5** | `haiku` | Fastest, most affordable | +| **Fable 5** | `claude-fable-5` | Opus-class model with full effort support, incl. XHigh (200K context) | +| **Fable 5 1M** | `claude-fable-5[1m]` | Fable 5 with a 1M context window | Earlier-generation models (Opus 4.7, Opus 4.7 1M, Opus 4.6, Opus 4.6 1M, Opus 4.5, Sonnet 4.5, Haiku 3.5) remain selectable in the picker as legacy entries. @@ -49,7 +51,7 @@ Control how much reasoning the agent applies to each response: | **Low** | Fast, minimal reasoning | Opus, Sonnet | | **Medium** | Balanced | Opus, Sonnet | | **High** | Deep reasoning | Opus, Sonnet | -| **XHigh** | Extended reasoning budget | Opus 4.7+ only | +| **XHigh** | Extended reasoning budget | Opus 4.7+, Fable 5 | | **Max** | Maximum reasoning budget | Opus and Sonnet (effort-capable models) | Select the effort level from the dropdown in the chat header, next to the model selector. diff --git a/site/src/content/docs/features/cli-client.mdx b/site/src/content/docs/features/cli-client.mdx index 73f5f9ed7..0791bf261 100644 --- a/site/src/content/docs/features/cli-client.mdx +++ b/site/src/content/docs/features/cli-client.mdx @@ -151,11 +151,11 @@ Boolean flags are tri-state — pass `--plan` to force on, `--no-plan` to force | Flag | Values | Effect | |---|---|---| -| `--model` | `opus`, `sonnet`, `haiku`, `claude-opus-4-8`, ... | Override model for this turn | +| `--model` | `opus`, `sonnet`, `haiku`, `claude-opus-4-8`, `claude-fable-5`, ... | Override model for this turn | | `--plan` / `--no-plan` | — | Plan mode (read-only until approved) | | `--thinking` / `--no-thinking` | — | Extended thinking | | `--fast` / `--no-fast` | — | Fast mode (built-in support: Opus 4.6) | -| `--effort` | `low`, `medium`, `high`, `xhigh`, `max` | Reasoning effort (`xhigh` requires Opus 4.7+) | +| `--effort` | `low`, `medium`, `high`, `xhigh`, `max` | Reasoning effort (`xhigh` requires Opus 4.7+ or Fable 5) | | `--chrome` / `--no-chrome` | — | Chrome browser tool | | `--disable-1m-context` | — | Suppress Max-plan auto-upgrade to a 1M context window | | `--permission` | `default`, `acceptEdits`, `bypassPermissions` | Permission level | diff --git a/site/src/content/docs/features/settings.mdx b/site/src/content/docs/features/settings.mdx index ddcd49f07..e63f99c90 100644 --- a/site/src/content/docs/features/settings.mdx +++ b/site/src/content/docs/features/settings.mdx @@ -33,7 +33,7 @@ Default values applied to all new agent sessions. Per-workspace overrides are av | Setting | Description | Default | |---------|-------------|---------| -| Default model | Model for new chats (Opus 4.8 1M, Opus 4.8, Sonnet 4.6, Sonnet 4.6 1M, Haiku 4.5; older models such as Opus 4.7 / 4.6 / 4.5 remain selectable behind the picker's "More" disclosure) | — | +| Default model | Model for new chats (Opus 4.8 1M, Opus 4.8, Sonnet 4.6, Sonnet 4.6 1M, Haiku 4.5, Fable 5, Fable 5 1M; older models such as Opus 4.7 / 4.6 / 4.5 remain selectable behind the picker's "More" disclosure) | — | | Default effort / Codex intelligence | Claude models use effort levels (`auto`, `low`, `medium`, `high`, `xhigh`, `max` where supported). Codex uses intelligence levels (`low`, `medium`, `high`, `xhigh`). | Auto / High | | Default thinking | Enable extended thinking for Claude models. Codex does not expose a reasoning on/off toggle; use Codex intelligence instead. | Off | | Show thinking / reasoning blocks | Display Claude thinking blocks or Codex reasoning summaries in the chat UI. | Off | diff --git a/src-cli/src/commands/chat.rs b/src-cli/src/commands/chat.rs index a24cd440e..168232e85 100644 --- a/src-cli/src/commands/chat.rs +++ b/src-cli/src/commands/chat.rs @@ -149,7 +149,7 @@ pub enum Action { #[arg(long = "no-fast", overrides_with = "fast", hide = true)] no_fast: bool, /// Effort level: `low`, `medium`, `high`, `xhigh`, `max` - /// (`xhigh` requires Opus 4.7+). + /// (`xhigh` requires Opus 4.7+ or Fable 5). #[arg(long)] effort: Option, /// Enable Chrome browser mode for this session. Pair: `--no-chrome` diff --git a/src/ui/src/components/chat/modelCapabilities.test.ts b/src/ui/src/components/chat/modelCapabilities.test.ts index 3fe5d23d4..b68bc475e 100644 --- a/src/ui/src/components/chat/modelCapabilities.test.ts +++ b/src/ui/src/components/chat/modelCapabilities.test.ts @@ -18,6 +18,10 @@ describe("isFastSupported", () => { expect(isFastSupported("claude-opus-4-7")).toBe(false); }); + it("returns false for claude-fable-5 (Opus-class but fast is Opus-4.6-only)", () => { + expect(isFastSupported("claude-fable-5")).toBe(false); + }); + it("returns false for sonnet", () => { expect(isFastSupported("sonnet")).toBe(false); }); @@ -36,6 +40,14 @@ describe("isEffortSupported", () => { expect(isEffortSupported("claude-opus-4-8")).toBe(true); }); + it("returns true for claude-fable-5", () => { + expect(isEffortSupported("claude-fable-5")).toBe(true); + }); + + it("returns true for claude-fable-5[1m]", () => { + expect(isEffortSupported("claude-fable-5[1m]")).toBe(true); + }); + it("returns true for claude-opus-4-7", () => { expect(isEffortSupported("claude-opus-4-7")).toBe(true); }); @@ -74,6 +86,10 @@ describe("isXhighEffortAllowed", () => { expect(isXhighEffortAllowed("claude-opus-4-8")).toBe(true); }); + it("returns true for claude-fable-5", () => { + expect(isXhighEffortAllowed("claude-fable-5")).toBe(true); + }); + it("returns true for claude-opus-4-7", () => { expect(isXhighEffortAllowed("claude-opus-4-7")).toBe(true); }); @@ -104,6 +120,10 @@ describe("isMaxEffortAllowed", () => { expect(isMaxEffortAllowed("claude-opus-4-8")).toBe(true); }); + it("returns true for claude-fable-5", () => { + expect(isMaxEffortAllowed("claude-fable-5")).toBe(true); + }); + it("returns true for claude-opus-4-7", () => { expect(isMaxEffortAllowed("claude-opus-4-7")).toBe(true); }); diff --git a/src/ui/src/components/chat/modelCapabilities.ts b/src/ui/src/components/chat/modelCapabilities.ts index 60509cbcc..10e4d2ba3 100644 --- a/src/ui/src/components/chat/modelCapabilities.ts +++ b/src/ui/src/components/chat/modelCapabilities.ts @@ -2,13 +2,13 @@ const FAST_SUPPORTED_MODELS = new Set(["claude-opus-4-6", "claude-opus-4-6[1m]"]); /** Models that support effort levels. */ -const EFFORT_SUPPORTED_MODELS = new Set(["opus", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-7[1m]", "claude-opus-4-6", "claude-opus-4-6[1m]", "sonnet", "claude-sonnet-4-6[1m]"]); +const EFFORT_SUPPORTED_MODELS = new Set(["opus", "claude-opus-4-8", "claude-fable-5", "claude-fable-5[1m]", "claude-opus-4-7", "claude-opus-4-7[1m]", "claude-opus-4-6", "claude-opus-4-6[1m]", "sonnet", "claude-sonnet-4-6[1m]"]); -/** Models that support the "xhigh" effort level (Opus 4.7+ only). */ -const XHIGH_EFFORT_MODELS = new Set(["opus", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-7[1m]"]); +/** Models that support the "xhigh" effort level (Opus 4.7+ and Fable 5). */ +const XHIGH_EFFORT_MODELS = new Set(["opus", "claude-opus-4-8", "claude-fable-5", "claude-fable-5[1m]", "claude-opus-4-7", "claude-opus-4-7[1m]"]); /** Models that support the "max" effort level. */ -const MAX_EFFORT_MODELS = new Set(["opus", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-7[1m]", "claude-opus-4-6", "claude-opus-4-6[1m]", "sonnet", "claude-sonnet-4-6[1m]"]); +const MAX_EFFORT_MODELS = new Set(["opus", "claude-opus-4-8", "claude-fable-5", "claude-fable-5[1m]", "claude-opus-4-7", "claude-opus-4-7[1m]", "claude-opus-4-6", "claude-opus-4-6[1m]", "sonnet", "claude-sonnet-4-6[1m]"]); export function isFastSupported(model: string): boolean { return FAST_SUPPORTED_MODELS.has(model); diff --git a/src/ui/src/components/chat/modelRegistry.test.ts b/src/ui/src/components/chat/modelRegistry.test.ts index 3e1ded40d..d1083b5cd 100644 --- a/src/ui/src/components/chat/modelRegistry.test.ts +++ b/src/ui/src/components/chat/modelRegistry.test.ts @@ -64,6 +64,7 @@ describe("modelRegistry", () => { describe("get1mFallback", () => { it("maps 1M models to their 200K equivalents", () => { expect(get1mFallback("opus")).toBe("claude-opus-4-8"); + expect(get1mFallback("claude-fable-5[1m]")).toBe("claude-fable-5"); expect(get1mFallback("claude-opus-4-7[1m]")).toBe("claude-opus-4-7"); expect(get1mFallback("claude-sonnet-4-6[1m]")).toBe("sonnet"); expect(get1mFallback("claude-opus-4-6[1m]")).toBe("claude-opus-4-6"); diff --git a/src/ui/src/components/chat/modelRegistry.ts b/src/ui/src/components/chat/modelRegistry.ts index 4b08beceb..e474e9bac 100644 --- a/src/ui/src/components/chat/modelRegistry.ts +++ b/src/ui/src/components/chat/modelRegistry.ts @@ -58,6 +58,7 @@ export function is1mContextModel(modelId: string): boolean { const NON_1M_FALLBACKS: Record = { "opus": "claude-opus-4-8", + "claude-fable-5[1m]": "claude-fable-5", "claude-opus-4-7[1m]": "claude-opus-4-7", "claude-sonnet-4-6[1m]": "sonnet", "claude-opus-4-6[1m]": "claude-opus-4-6", @@ -80,6 +81,12 @@ export const MODELS: readonly Model[] = [ { id: "sonnet", label: "Sonnet 4.6", group: "Claude Code", extraUsage: false, contextWindowTokens: 200_000 }, { id: "claude-sonnet-4-6[1m]", label: "Sonnet 4.6 1M", group: "Claude Code", extraUsage: true, contextWindowTokens: 1_000_000 }, { id: "haiku", label: "Haiku 4.5", group: "Claude Code", extraUsage: false, contextWindowTokens: 200_000 }, + // Fable 5 is an Opus-class Anthropic model (effort incl. xhigh/max, 1M variant). + // It has no bare alias — the concrete id carries through to the Claude CLI `--model` + // arg and passes the `claude-` prefix gate in `gateway_translate.rs`. The 1M variant + // uses the `[1m]` suffix convention so `is1mContextModel` detects it without a special case. + { id: "claude-fable-5", label: "Fable 5", group: "Claude Code", extraUsage: false, contextWindowTokens: 200_000 }, + { id: "claude-fable-5[1m]", label: "Fable 5 1M", group: "Claude Code", extraUsage: false, contextWindowTokens: 1_000_000 }, { id: "claude-opus-4-7", label: "Opus 4.7", group: "Claude Code", extraUsage: false, legacy: true, contextWindowTokens: 200_000 }, { id: "claude-opus-4-7[1m]", label: "Opus 4.7 1M", group: "Claude Code", extraUsage: false, legacy: true, contextWindowTokens: 1_000_000 }, { id: "claude-opus-4-6", label: "Opus 4.6", group: "Claude Code", extraUsage: false, legacy: true, contextWindowTokens: 200_000 }, diff --git a/src/ui/src/components/command-palette/commands.ts b/src/ui/src/components/command-palette/commands.ts index a59d72ef6..4f0472bae 100644 --- a/src/ui/src/components/command-palette/commands.ts +++ b/src/ui/src/components/command-palette/commands.ts @@ -234,7 +234,7 @@ export function buildEffortCommands( { id: "low", label: "Low", description: "Fast, minimal reasoning" }, { id: "medium", label: "Medium", description: "Balanced" }, { id: "high", label: "High", description: "Deep reasoning" }, - { id: "xhigh", label: "Extra High", description: "Extended reasoning (Opus 4.7+)" }, + { id: "xhigh", label: "Extra High", description: "Extended reasoning (Opus 4.7+, Fable 5)" }, { id: "max", label: "Max", description: "Full budget" }, ]; const levels = !isEffortSupported(selectedModel) diff --git a/src/usage/pricing.rs b/src/usage/pricing.rs index b0a33eb08..fa8c1a7bb 100644 --- a/src/usage/pricing.rs +++ b/src/usage/pricing.rs @@ -93,6 +93,15 @@ const PRICING_TABLE: &[(&str, ModelPricing)] = &[ }, ), // -- Anthropic (only used when local-aggregating, e.g. via Pi) ----- + // Fable 5 is priced at 2x Opus. The `[1m]` variant matches this same + // prefix via `starts_with`, so a single entry covers both ids. + ( + "claude-fable-5", + ModelPricing { + prompt_per_mtok_usd: 30.00, + completion_per_mtok_usd: 150.00, + }, + ), ( "claude-opus-4-8", ModelPricing { @@ -147,6 +156,16 @@ mod tests { assert!((p.prompt_per_mtok_usd - 1.25).abs() < f64::EPSILON); } + #[test] + fn lookup_fable_5_priced_at_2x_opus() { + let p = lookup("claude-fable-5").expect("claude-fable-5 has pricing"); + assert!((p.prompt_per_mtok_usd - 30.00).abs() < f64::EPSILON); + assert!((p.completion_per_mtok_usd - 150.00).abs() < f64::EPSILON); + // The 1M variant shares the bare prefix via `starts_with`. + let one_m = lookup("claude-fable-5[1m]").expect("1M variant resolves to same pricing"); + assert!((one_m.prompt_per_mtok_usd - 30.00).abs() < f64::EPSILON); + } + #[test] fn lookup_handles_provider_prefix() { // Pi qualifies model ids as `/`; the table is From 6a40faf0461969d5ee2d2df1d283c9c97d3827d8 Mon Sep 17 00:00:00 2001 From: Sean Callan Date: Wed, 10 Jun 2026 13:02:31 -0400 Subject: [PATCH 2/2] docs(models): list Fable 5 in all effort-capable rows of the effort table The Reasoning Effort table's Max row still read "Opus and Sonnet", but Fable 5 was added to MAX_EFFORT_MODELS, making the docs inaccurate (flagged in PR review). Fable 5 is a new model family not covered by "Opus"/"Sonnet", and it supports effort generally, so list it in the Low/Medium/High/Max rows too for consistency with the already-updated XHigh row. --- site/src/content/docs/features/agent-configuration.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/content/docs/features/agent-configuration.mdx b/site/src/content/docs/features/agent-configuration.mdx index a8e9e813e..eb6467427 100644 --- a/site/src/content/docs/features/agent-configuration.mdx +++ b/site/src/content/docs/features/agent-configuration.mdx @@ -48,11 +48,11 @@ Control how much reasoning the agent applies to each response: | Level | Description | Available Models | |-------|-------------|-----------------| | **Auto** | Let Claude decide (default) | All models | -| **Low** | Fast, minimal reasoning | Opus, Sonnet | -| **Medium** | Balanced | Opus, Sonnet | -| **High** | Deep reasoning | Opus, Sonnet | +| **Low** | Fast, minimal reasoning | Opus, Sonnet, Fable 5 | +| **Medium** | Balanced | Opus, Sonnet, Fable 5 | +| **High** | Deep reasoning | Opus, Sonnet, Fable 5 | | **XHigh** | Extended reasoning budget | Opus 4.7+, Fable 5 | -| **Max** | Maximum reasoning budget | Opus and Sonnet (effort-capable models) | +| **Max** | Maximum reasoning budget | Opus, Sonnet, Fable 5 (effort-capable models) | Select the effort level from the dropdown in the chat header, next to the model selector.