From d5b2a8580e9d533411a63a0d040090d64aad7c50 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 2 Sep 2026 10:45:54 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=96=20feat:=20Add=20Claude=20Fable=205?= =?UTF-8?q?.1=20Support=20(#15512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Fable 5.1 (`claude-fable-5-1`, GA 2026-09-01) succeeds Claude Fable 5 at the same input/output prices, with a 1M context window and 128K max output. The Mythos-class helpers added for Fable 5 already match it — `isMythosClassModel` tests `claude-(?:fable|mythos)[-.]?\d`, so adaptive thinking, summarized-display opt-in, sampling omission, 1M context, the 128K output clamp, prompt caching, the Vertex multi-region gate, and the Bedrock PDF exemption all cover 5.1 without a code change. What was missing was registration and pricing. Cache reads are the one rate that differs, and the substring matcher would have silently gotten it wrong: `claude-fable-5-1` falls through to the `claude-fable-5` key, which prices cache hits at the usual 0.1x base input ($1/MTok). Fable 5.1 and Mythos 5.1 price them at 0.025x ($0.25/MTok), so every cache read would have billed 4x — on exactly the long agentic sessions this model is built for. - Register `claude-fable-5-1` in the shared Anthropic list and `global.anthropic.claude-fable-5-1` in the Bedrock list - Add 1M context / 128K output entries for `claude-fable-5-1` and `claude-mythos-5-1` - Add $10/$50 rates and `{ write: 12.5, read: 0.25 }` cache rates for both - Update the `.env.example` and Vertex `librechat.example.yaml` examples - Test that 5.1 resolves to its own key rather than collapsing onto 5.0, across token maps, pricing, cache rates, and Mythos-class detection The three documented breaking changes from Fable 5 need no handling here: LibreChat never sends a forced `tool_choice` on the Anthropic path (the agents SDK omits the param when unset, so the API default `auto` applies); thinking blocks an earlier model can't read are dropped server-side, unbilled; and history is rebuilt from stored content parts rather than replayed signed thinking blocks, so the prefix-binding check has nothing to reject. Refs #15506 --- .env.example | 15 +++++---- api/utils/tokens.spec.js | 34 ++++++++++++++++++++ librechat.example.yaml | 5 ++- packages/api/src/utils/tokens.ts | 4 +++ packages/data-provider/specs/bedrock.spec.ts | 2 ++ packages/data-provider/src/config.ts | 2 ++ packages/data-schemas/src/methods/tx.spec.ts | 32 ++++++++++++++++++ packages/data-schemas/src/methods/tx.ts | 5 +++ 8 files changed, 91 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index ddd9b4c6b1..1ad728ac8c 100644 --- a/.env.example +++ b/.env.example @@ -419,14 +419,14 @@ PROXY= #============# ANTHROPIC_API_KEY=user_provided -# ANTHROPIC_MODELS=claude-fable-5,claude-opus-5,claude-opus-4-8,claude-opus-4-7,claude-sonnet-5,claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 +# ANTHROPIC_MODELS=claude-fable-5-1,claude-fable-5,claude-opus-5,claude-opus-4-8,claude-opus-4-7,claude-sonnet-5,claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 # ANTHROPIC_REVERSE_PROXY= # Set to true to use Anthropic models through Google Vertex AI instead of direct API # ANTHROPIC_USE_VERTEX= # Supports regional locations like us-east5 and multi-region locations: us, eu, global # IMPORTANT: specific regional endpoints (us-east5, europe-west1, ...) only serve Claude Sonnet 4.6 -# and earlier. Newer models (Opus 4.7+, Opus 5, Sonnet 5, Fable 5) require `global` or a multi-region +# and earlier. Newer models (Opus 4.7+, Opus 5, Sonnet 5, Fable 5/5.1) require `global` or a multi-region # location (`us`/`eu`) and will 404 on a specific region. `global` also avoids the 10% regional premium. # ANTHROPIC_VERTEX_REGION=us-east5 @@ -490,8 +490,8 @@ ANTHROPIC_API_KEY=user_provided # Note: This example list is not meant to be exhaustive. If omitted, all known, supported model IDs will be included for you. # Claude 4+ models cannot be invoked on-demand by their bare `anthropic.` foundation-model ID; Bedrock requires a # cross-region inference profile (`global.` or `us.`) for those. The `global.` profile has no regional pricing premium. -# BEDROCK_AWS_MODELS=global.anthropic.claude-fable-5,global.anthropic.claude-opus-5,global.anthropic.claude-opus-4-8,global.anthropic.claude-opus-4-7,global.anthropic.claude-sonnet-5,global.anthropic.claude-sonnet-4-6,global.anthropic.claude-opus-4-6-v1,global.anthropic.claude-haiku-4-5-20251001-v1:0,meta.llama3-1-8b-instruct-v1:0 -# US-only routing alternative: us.anthropic.claude-fable-5,us.anthropic.claude-opus-5,us.anthropic.claude-opus-4-8,us.anthropic.claude-opus-4-7,us.anthropic.claude-sonnet-5,us.anthropic.claude-sonnet-4-6,us.anthropic.claude-opus-4-6-v1 +# BEDROCK_AWS_MODELS=global.anthropic.claude-fable-5-1,global.anthropic.claude-fable-5,global.anthropic.claude-opus-5,global.anthropic.claude-opus-4-8,global.anthropic.claude-opus-4-7,global.anthropic.claude-sonnet-5,global.anthropic.claude-sonnet-4-6,global.anthropic.claude-opus-4-6-v1,global.anthropic.claude-haiku-4-5-20251001-v1:0,meta.llama3-1-8b-instruct-v1:0 +# US-only routing alternative: us.anthropic.claude-fable-5-1,us.anthropic.claude-fable-5,us.anthropic.claude-opus-5,us.anthropic.claude-opus-4-8,us.anthropic.claude-opus-4-7,us.anthropic.claude-sonnet-5,us.anthropic.claude-sonnet-4-6,us.anthropic.claude-opus-4-6-v1 # List the profiles available to your account with: aws bedrock list-inference-profiles --region # See all Bedrock model IDs here: https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns @@ -503,9 +503,10 @@ ANTHROPIC_API_KEY=user_provided # The following models are not support due to not supporting conversation history: # ai21.j2-ultra-v1, cohere.command-text-v14, cohere.command-light-text-v14 -# Claude Mythos-class models (anthropic.claude-fable-5, anthropic.claude-mythos-5) are inference-profile -# only on Bedrock — use a profile ID (e.g. us.anthropic.claude-fable-5) — and require opting into Anthropic -# data sharing via the Bedrock Data Retention API/console before they can be invoked. +# Claude Mythos-class models (anthropic.claude-fable-5-1, anthropic.claude-mythos-5-1, and their 5.0 +# predecessors) are inference-profile only on Bedrock — use a profile ID (e.g. us.anthropic.claude-fable-5-1) +# — and require opting into Anthropic data sharing via the Bedrock Data Retention API/console before they +# can be invoked. #============# # Google # diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index 73f10239be..338c20ad89 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -1640,6 +1640,40 @@ describe('Claude Model Tests', () => { }); }); + it('should return correct context length for Claude Fable 5.1 (1M)', () => { + expect(maxTokensMap[EModelEndpoint.anthropic]['claude-fable-5-1']).toBe(1000000); + expect(maxTokensMap[EModelEndpoint.anthropic]['claude-mythos-5-1']).toBe(1000000); + expect(getModelMaxTokens('claude-fable-5-1', EModelEndpoint.anthropic)).toBe(1000000); + expect(getModelMaxTokens('claude-mythos-5-1', EModelEndpoint.anthropic)).toBe(1000000); + }); + + it('should return correct max output tokens for Claude Fable 5.1 (128K)', () => { + const { getModelMaxOutputTokens } = require('@librechat/api'); + expect(maxOutputTokensMap[EModelEndpoint.anthropic]['claude-fable-5-1']).toBe(128000); + expect(maxOutputTokensMap[EModelEndpoint.anthropic]['claude-mythos-5-1']).toBe(128000); + expect(getModelMaxOutputTokens('claude-fable-5-1', EModelEndpoint.anthropic)).toBe(128000); + expect(getModelMaxOutputTokens('claude-mythos-5-1', EModelEndpoint.anthropic)).toBe(128000); + }); + + it('should match model names correctly for Claude Fable 5.1 without collapsing to Fable 5', () => { + const modelVariations = [ + 'claude-fable-5-1', + 'claude-fable-5-1-20260901', + 'claude-fable-5-1-latest', + 'anthropic/claude-fable-5-1', + 'claude-fable-5-1/anthropic', + 'anthropic.claude-fable-5-1', + ]; + + modelVariations.forEach((model) => { + expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-fable-5-1'); + }); + + expect(matchModelName('claude-fable-5-20260609', EModelEndpoint.anthropic)).toBe( + 'claude-fable-5', + ); + }); + it('should return correct context length for Claude Sonnet 4.6 (1M)', () => { expect(getModelMaxTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe( maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], diff --git a/librechat.example.yaml b/librechat.example.yaml index e1038a38ae..4ea9a3b3e7 100644 --- a/librechat.example.yaml +++ b/librechat.example.yaml @@ -656,7 +656,7 @@ endpoints: # # Available regions: us-east5, us-central1, europe-west1, europe-west4, asia-southeast1 # # Multi-region endpoints: us, eu, global # # IMPORTANT: specific regional endpoints only serve Claude Sonnet 4.6 and earlier. Newer - # # models (Opus 4.7+, Opus 5, Sonnet 5, Fable 5) require "global" or a multi-region value + # # models (Opus 4.7+, Opus 5, Sonnet 5, Fable 5/5.1) require "global" or a multi-region value # # ("us"/"eu") and will 404 on a specific region. "global" also avoids the 10% regional premium. # region: "us-east5" # # Path to Google service account key file (optional) @@ -675,6 +675,7 @@ endpoints: # # Option 1: Simple array (legacy format - model name = deployment name) # # Use this if you want the technical model IDs to show in the UI # # models: + # # - "claude-fable-5-1" # # - "claude-fable-5" # # - "claude-opus-5" # # - "claude-opus-4-8" @@ -688,6 +689,8 @@ endpoints: # # The deploymentName is the actual Vertex AI model ID used for API calls # # You can use friendly names (avoid spaces for cleaner YAML) or technical IDs as keys # models: + # claude-fable-5.1: + # deploymentName: claude-fable-5-1 # claude-fable-5: # deploymentName: claude-fable-5 # claude-opus-5: diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index b3c543bb94..3166c0584f 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -183,6 +183,8 @@ const anthropicModels = { 'claude-opus-5': 1000000, 'claude-fable-5': 1000000, 'claude-mythos-5': 1000000, + 'claude-fable-5-1': 1000000, + 'claude-mythos-5-1': 1000000, }; const ANTHROPIC_CONTEXT_1M = 1000000; @@ -539,6 +541,8 @@ const anthropicMaxOutputs = { 'claude-opus-5': 128000, 'claude-fable-5': 128000, 'claude-mythos-5': 128000, + 'claude-fable-5-1': 128000, + 'claude-mythos-5-1': 128000, 'claude-3.5-sonnet': 8192, 'claude-3-5-sonnet': 8192, 'claude-3.7-sonnet': 128000, diff --git a/packages/data-provider/specs/bedrock.spec.ts b/packages/data-provider/specs/bedrock.spec.ts index a312659575..317972315e 100644 --- a/packages/data-provider/specs/bedrock.spec.ts +++ b/packages/data-provider/specs/bedrock.spec.ts @@ -29,6 +29,8 @@ describe('isMythosClassModel (single source of truth for Fable/Mythos)', () => { expect(isMythosClassModel(`anthropic.claude-${family}-5`)).toBe(true); expect(isMythosClassModel(`us.anthropic.claude-${family}-5`)).toBe(true); expect(isMythosClassModel(`claude-${family}-5-20260609`)).toBe(true); + expect(isMythosClassModel(`claude-${family}-5-1`)).toBe(true); + expect(isMythosClassModel(`global.anthropic.claude-${family}-5-1`)).toBe(true); }); }); diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts index acba2a7001..4e9e33822f 100644 --- a/packages/data-provider/src/config.ts +++ b/packages/data-provider/src/config.ts @@ -2663,6 +2663,7 @@ const sharedOpenAIModels = [ ]; const sharedAnthropicModels = [ + 'claude-fable-5-1', 'claude-fable-5', 'claude-opus-5', 'claude-opus-4-8', @@ -2698,6 +2699,7 @@ const sharedAnthropicModels = [ * availability); Opus 4.1 has no global profile, so it uses `us.`. */ export const bedrockModels = [ + 'global.anthropic.claude-fable-5-1', 'global.anthropic.claude-fable-5', 'global.anthropic.claude-opus-5', 'global.anthropic.claude-opus-4-8', diff --git a/packages/data-schemas/src/methods/tx.spec.ts b/packages/data-schemas/src/methods/tx.spec.ts index 4a3af83bbf..5344927137 100644 --- a/packages/data-schemas/src/methods/tx.spec.ts +++ b/packages/data-schemas/src/methods/tx.spec.ts @@ -2661,6 +2661,38 @@ describe('Claude Model Tests', () => { ); }); + it('should pin Claude Fable 5.1 pricing to $10 / $50 per MTok', () => { + expect(tokenValues['claude-fable-5-1']).toEqual({ prompt: 10, completion: 50 }); + expect(tokenValues['claude-mythos-5-1']).toEqual({ prompt: 10, completion: 50 }); + }); + + it('should charge Claude Fable 5.1 cache reads at a quarter of the Fable 5 rate', () => { + expect(cacheTokenValues['claude-fable-5-1']).toEqual({ write: 12.5, read: 0.25 }); + expect(cacheTokenValues['claude-mythos-5-1']).toEqual({ write: 12.5, read: 0.25 }); + expect(getCacheMultiplier({ model: 'claude-fable-5-1', cacheType: 'read' })).toBe(0.25); + expect(getCacheMultiplier({ model: 'claude-fable-5-1', cacheType: 'write' })).toBe(12.5); + expect(getCacheMultiplier({ model: 'claude-fable-5', cacheType: 'read' })).toBe(1); + }); + + it('should handle Claude Fable 5.1 model name variations without collapsing to Fable 5', () => { + const modelVariations = [ + 'claude-fable-5-1', + 'claude-fable-5-1-20260901', + 'claude-fable-5-1-latest', + 'anthropic/claude-fable-5-1', + 'claude-fable-5-1/anthropic', + 'anthropic.claude-fable-5-1', + 'global.anthropic.claude-fable-5-1', + ]; + + modelVariations.forEach((model) => { + expect(getValueKey(model)).toBe('claude-fable-5-1'); + expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(0.25); + }); + + expect(getValueKey('claude-fable-5-20260609')).toBe('claude-fable-5'); + }); + it('should return correct prompt and completion rates for Claude Sonnet 5', () => { expect(getMultiplier({ model: 'claude-sonnet-5', tokenType: 'prompt' })).toBe( tokenValues['claude-sonnet-5'].prompt, diff --git a/packages/data-schemas/src/methods/tx.ts b/packages/data-schemas/src/methods/tx.ts index b608f54bf9..b8a6392262 100644 --- a/packages/data-schemas/src/methods/tx.ts +++ b/packages/data-schemas/src/methods/tx.ts @@ -186,6 +186,8 @@ export const tokenValues: Record 'claude-opus-5': { prompt: 5, completion: 25 }, 'claude-fable-5': { prompt: 10, completion: 50 }, 'claude-mythos-5': { prompt: 10, completion: 50 }, + 'claude-fable-5-1': { prompt: 10, completion: 50 }, + 'claude-mythos-5-1': { prompt: 10, completion: 50 }, 'claude-sonnet-4': { prompt: 3, completion: 15 }, 'claude-sonnet-4-5': { prompt: 3, completion: 15 }, 'claude-sonnet-4-6': { prompt: 3, completion: 15 }, @@ -367,6 +369,9 @@ export const cacheTokenValues: Record = 'claude-opus-5': { write: 6.25, read: 0.5 }, 'claude-fable-5': { write: 12.5, read: 1 }, 'claude-mythos-5': { write: 12.5, read: 1 }, + // Fable/Mythos 5.1 cache reads are 0.025x base input, not the usual 0.1x. + 'claude-fable-5-1': { write: 12.5, read: 0.25 }, + 'claude-mythos-5-1': { write: 12.5, read: 0.25 }, 'gpt-4o': { write: 2.5, read: 1.25 }, 'gpt-4o-mini': { write: 0.15, read: 0.075 }, 'gpt-4.1': { write: 2, read: 0.5 },