mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-11 08:43:48 +00:00
🧢 fix: Raise Claude Sonnet 4.6 Output Cap (#14115)
* fix: raise Claude Sonnet 4.6 output cap * fix: handle Sonnet 4.6 token edge cases * fix: align Sonnet token aliases * test: update Bedrock Sonnet output cap expectation * fix: align future Sonnet token aliases * fix: cap Bedrock Sonnet 4.6 default output * fix: support double-digit Sonnet 4 minors * style: format Sonnet token helpers * fix: match number-first Sonnet aliases
This commit is contained in:
parent
2d4ef52c22
commit
a03c574bef
7 changed files with 255 additions and 17 deletions
|
|
@ -1587,6 +1587,9 @@ describe('Claude Model Tests', () => {
|
|||
expect(getModelMaxTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe(
|
||||
maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
|
||||
);
|
||||
expect(getModelMaxTokens('claude-sonnet-4.6', EModelEndpoint.anthropic)).toBe(
|
||||
maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4.6'],
|
||||
);
|
||||
expect(getModelMaxTokens('claude-sonnet-4-6')).toBe(
|
||||
maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
|
||||
);
|
||||
|
|
@ -1595,11 +1598,77 @@ describe('Claude Model Tests', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should return correct max output tokens for Claude Sonnet 4.6 (64K)', () => {
|
||||
it('should return correct max output tokens for Claude Sonnet 4.6 (128K)', () => {
|
||||
const { getModelMaxOutputTokens } = require('@librechat/api');
|
||||
expect(getModelMaxOutputTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe(
|
||||
maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'],
|
||||
);
|
||||
expect(getModelMaxOutputTokens('claude-sonnet-4.6', EModelEndpoint.anthropic)).toBe(
|
||||
maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4.6'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct context length for Claude Sonnet 4.7+ aliases (1M)', () => {
|
||||
[
|
||||
'claude-sonnet-4-7',
|
||||
'claude-sonnet-4.7',
|
||||
'claude-sonnet-4-8',
|
||||
'claude-sonnet-4.8',
|
||||
'claude-sonnet-4-9',
|
||||
'claude-sonnet-4.9',
|
||||
'claude-sonnet-4-10',
|
||||
'claude-sonnet-4.10',
|
||||
'claude-4-10-sonnet',
|
||||
'claude-4.10-sonnet',
|
||||
'anthropic.claude-4-10-sonnet',
|
||||
].forEach((model) => {
|
||||
expect(getModelMaxTokens(model, EModelEndpoint.anthropic)).toBe(1000000);
|
||||
});
|
||||
expect(getModelMaxTokens('anthropic.claude-4-10-sonnet', EModelEndpoint.bedrock)).toBe(1000000);
|
||||
});
|
||||
|
||||
it('should return correct max output tokens for Claude Sonnet 4.7+ aliases (128K)', () => {
|
||||
const { getModelMaxOutputTokens } = require('@librechat/api');
|
||||
[
|
||||
'claude-sonnet-4-7',
|
||||
'claude-sonnet-4.7',
|
||||
'claude-sonnet-4-8',
|
||||
'claude-sonnet-4.8',
|
||||
'claude-sonnet-4-9',
|
||||
'claude-sonnet-4.9',
|
||||
'claude-sonnet-4-10',
|
||||
'claude-sonnet-4.10',
|
||||
'claude-4-10-sonnet',
|
||||
'claude-4.10-sonnet',
|
||||
].forEach((model) => {
|
||||
expect(getModelMaxOutputTokens(model, EModelEndpoint.anthropic)).toBe(128000);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not treat dated Claude Sonnet 4 IDs as Claude Sonnet 4.6+ aliases', () => {
|
||||
const { getModelMaxOutputTokens } = require('@librechat/api');
|
||||
expect(getModelMaxTokens('claude-sonnet-4-20250514', EModelEndpoint.anthropic)).toBe(
|
||||
maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4'],
|
||||
);
|
||||
expect(getModelMaxOutputTokens('claude-sonnet-4-20250514', EModelEndpoint.anthropic)).toBe(
|
||||
maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4'],
|
||||
);
|
||||
expect(getModelMaxTokens('claude-4-20250514-sonnet', EModelEndpoint.bedrock)).toBe(
|
||||
maxTokensMap[EModelEndpoint.bedrock]['claude-4'],
|
||||
);
|
||||
});
|
||||
|
||||
it('should keep double-digit Claude Sonnet 4 minor names when matching models', () => {
|
||||
expect(matchModelName('claude-sonnet-4-10', EModelEndpoint.anthropic)).toBe(
|
||||
'claude-sonnet-4-10',
|
||||
);
|
||||
expect(matchModelName('claude-sonnet-4.10-latest', EModelEndpoint.anthropic)).toBe(
|
||||
'claude-sonnet-4.10-latest',
|
||||
);
|
||||
expect(matchModelName('claude-4-10-sonnet', EModelEndpoint.bedrock)).toBe('claude-4-10-sonnet');
|
||||
expect(matchModelName('anthropic.claude-4.10-sonnet', EModelEndpoint.bedrock)).toBe(
|
||||
'anthropic.claude-4.10-sonnet',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle Claude Sonnet 4.6 model name variations', () => {
|
||||
|
|
@ -1650,7 +1719,7 @@ describe('Claude Model Tests', () => {
|
|||
expect(getModelMaxOutputTokens('claude-sonnet-5', EModelEndpoint.anthropic)).toBe(
|
||||
maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-5'],
|
||||
);
|
||||
expect(getModelMaxOutputTokens('claude-sonnet-5', EModelEndpoint.anthropic)).toBeGreaterThan(
|
||||
expect(getModelMaxOutputTokens('claude-sonnet-5', EModelEndpoint.anthropic)).toBe(
|
||||
getModelMaxOutputTokens('claude-sonnet-4-6', EModelEndpoint.anthropic),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -957,8 +957,8 @@ describe('getLLMConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should default future Claude 4.x Sonnet/Haiku models to 64K (future-proofing)', () => {
|
||||
const testCases = ['claude-sonnet-4-20250514', 'claude-sonnet-4-9', 'claude-haiku-4-8'];
|
||||
it('should keep Claude 4.x Sonnet before 4.6 and Haiku models at 64K', () => {
|
||||
const testCases = ['claude-sonnet-4-20250514', 'claude-sonnet-4-5', 'claude-haiku-4-8'];
|
||||
|
||||
testCases.forEach((model) => {
|
||||
const result = getLLMConfig('test-key', {
|
||||
|
|
@ -968,6 +968,23 @@ describe('getLLMConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should default Claude Sonnet 4.6+ models to 128K tokens', () => {
|
||||
const testCases = [
|
||||
'claude-sonnet-4-6',
|
||||
'claude-sonnet-4.6',
|
||||
'claude-sonnet-4-9',
|
||||
'claude-sonnet-4-10',
|
||||
'claude-sonnet-4.10',
|
||||
];
|
||||
|
||||
testCases.forEach((model) => {
|
||||
const result = getLLMConfig('test-key', {
|
||||
modelOptions: { model },
|
||||
});
|
||||
expect(result.llmConfig.maxTokens).toBe(128000);
|
||||
});
|
||||
});
|
||||
|
||||
it('should default future Claude 4.x Opus models (future-proofing)', () => {
|
||||
// opus-4-0 through opus-4-4 get 32K
|
||||
const opus32kModels = ['claude-opus-4-0', 'claude-opus-4-1', 'claude-opus-4-4'];
|
||||
|
|
@ -1116,7 +1133,7 @@ describe('getLLMConfig', () => {
|
|||
|
||||
expect((result.llmConfig.thinking as unknown as { type: string }).type).toBe('adaptive');
|
||||
expect(result.llmConfig.thinking).not.toHaveProperty('budget_tokens');
|
||||
expect(result.llmConfig.maxTokens).toBe(64000);
|
||||
expect(result.llmConfig.maxTokens).toBe(128000);
|
||||
});
|
||||
|
||||
it('should set effort via output_config for Sonnet 4.6', () => {
|
||||
|
|
|
|||
|
|
@ -156,6 +156,13 @@ const anthropicModels = {
|
|||
'claude-sonnet-4': 200000,
|
||||
'claude-sonnet-4-5': 200000,
|
||||
'claude-sonnet-4-6': 1000000,
|
||||
'claude-sonnet-4.6': 1000000,
|
||||
'claude-sonnet-4-7': 1000000,
|
||||
'claude-sonnet-4.7': 1000000,
|
||||
'claude-sonnet-4-8': 1000000,
|
||||
'claude-sonnet-4.8': 1000000,
|
||||
'claude-sonnet-4-9': 1000000,
|
||||
'claude-sonnet-4.9': 1000000,
|
||||
'claude-sonnet-5': 1000000,
|
||||
'claude-opus-4-6': 1000000,
|
||||
'claude-opus-4-7': 1000000,
|
||||
|
|
@ -164,6 +171,41 @@ const anthropicModels = {
|
|||
'claude-mythos-5': 1000000,
|
||||
};
|
||||
|
||||
const ANTHROPIC_SONNET_4_6_PLUS_CONTEXT = 1000000;
|
||||
const ANTHROPIC_SONNET_4_6_PLUS_OUTPUT = 128000;
|
||||
const ANTHROPIC_SONNET_4_6_PLUS_PATTERN =
|
||||
/(?:claude-sonnet[-.]?4[-.]?(?:[6-9]|\d{2})|claude[-.]?4[-.]?(?:[6-9]|\d{2})[-.]?sonnet)(?=$|[^0-9])/;
|
||||
|
||||
function usesAnthropicContextMap(endpoint: EModelEndpoint): boolean {
|
||||
return (
|
||||
endpoint === EModelEndpoint.anthropic ||
|
||||
endpoint === EModelEndpoint.bedrock ||
|
||||
endpoint === EModelEndpoint.openAI ||
|
||||
endpoint === EModelEndpoint.agents ||
|
||||
endpoint === EModelEndpoint.custom
|
||||
);
|
||||
}
|
||||
|
||||
function getAnthropicSonnet46PlusContext(
|
||||
modelName: string,
|
||||
endpoint: EModelEndpoint,
|
||||
): number | undefined {
|
||||
if (!usesAnthropicContextMap(endpoint) || !ANTHROPIC_SONNET_4_6_PLUS_PATTERN.test(modelName)) {
|
||||
return undefined;
|
||||
}
|
||||
return ANTHROPIC_SONNET_4_6_PLUS_CONTEXT;
|
||||
}
|
||||
|
||||
function getAnthropicSonnet46PlusOutput(
|
||||
modelName: string,
|
||||
endpoint: EModelEndpoint,
|
||||
): number | undefined {
|
||||
if (endpoint !== EModelEndpoint.anthropic || !ANTHROPIC_SONNET_4_6_PLUS_PATTERN.test(modelName)) {
|
||||
return undefined;
|
||||
}
|
||||
return ANTHROPIC_SONNET_4_6_PLUS_OUTPUT;
|
||||
}
|
||||
|
||||
const deepseekModels = {
|
||||
deepseek: 128000,
|
||||
'deepseek-chat': 128000,
|
||||
|
|
@ -408,7 +450,14 @@ const anthropicMaxOutputs = {
|
|||
'claude-3-opus': 4096,
|
||||
'claude-haiku-4-5': 64000,
|
||||
'claude-sonnet-4': 64000,
|
||||
'claude-sonnet-4-6': 64000,
|
||||
'claude-sonnet-4-6': 128000,
|
||||
'claude-sonnet-4.6': 128000,
|
||||
'claude-sonnet-4-7': 128000,
|
||||
'claude-sonnet-4.7': 128000,
|
||||
'claude-sonnet-4-8': 128000,
|
||||
'claude-sonnet-4.8': 128000,
|
||||
'claude-sonnet-4-9': 128000,
|
||||
'claude-sonnet-4.9': 128000,
|
||||
'claude-sonnet-5': 128000,
|
||||
'claude-opus-4': 32000,
|
||||
'claude-opus-4-5': 64000,
|
||||
|
|
@ -530,6 +579,10 @@ export function getModelMaxTokens(
|
|||
return overrideValue;
|
||||
}
|
||||
}
|
||||
const sonnet46PlusValue = getAnthropicSonnet46PlusContext(modelName, endpoint);
|
||||
if (sonnet46PlusValue != null) {
|
||||
return sonnet46PlusValue;
|
||||
}
|
||||
return getModelTokenValue(modelName, maxTokensMap[endpoint as keyof typeof maxTokensMap]);
|
||||
}
|
||||
|
||||
|
|
@ -553,6 +606,10 @@ export function getModelMaxOutputTokens(
|
|||
return overrideValue;
|
||||
}
|
||||
}
|
||||
const sonnet46PlusValue = getAnthropicSonnet46PlusOutput(modelName, endpoint);
|
||||
if (sonnet46PlusValue != null) {
|
||||
return sonnet46PlusValue;
|
||||
}
|
||||
return getModelTokenValue(
|
||||
modelName,
|
||||
maxOutputTokensMap[endpoint as keyof typeof maxOutputTokensMap],
|
||||
|
|
@ -591,6 +648,12 @@ export function matchModelName(
|
|||
}
|
||||
|
||||
const matchedPattern = findMatchingPattern(modelName, tokensMap);
|
||||
if (
|
||||
(matchedPattern === 'claude-sonnet-4' || matchedPattern === 'claude-4') &&
|
||||
getAnthropicSonnet46PlusContext(modelName, endpoint) != null
|
||||
) {
|
||||
return modelName;
|
||||
}
|
||||
return matchedPattern || modelName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,16 @@ describe('supportsAdaptiveThinking', () => {
|
|||
expect(supportsAdaptiveThinking('claude-sonnet-4-7')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for double-digit claude-sonnet-4 minors', () => {
|
||||
expect(supportsAdaptiveThinking('claude-sonnet-4-10')).toBe(true);
|
||||
expect(supportsAdaptiveThinking('claude-sonnet-4.10')).toBe(true);
|
||||
expect(supportsAdaptiveThinking('claude-4-10-sonnet')).toBe(true);
|
||||
});
|
||||
|
||||
test('should not parse Sonnet 4 date suffixes as double-digit minors', () => {
|
||||
expect(supportsAdaptiveThinking('claude-sonnet-4-20250514')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for anthropic.claude-sonnet-4-6 (Bedrock)', () => {
|
||||
expect(supportsAdaptiveThinking('anthropic.claude-sonnet-4-6')).toBe(true);
|
||||
});
|
||||
|
|
@ -166,6 +176,16 @@ describe('supportsContext1m', () => {
|
|||
expect(supportsContext1m('claude-sonnet-4-6')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for double-digit claude-sonnet-4 minors', () => {
|
||||
expect(supportsContext1m('claude-sonnet-4-10')).toBe(true);
|
||||
expect(supportsContext1m('claude-sonnet-4.10')).toBe(true);
|
||||
expect(supportsContext1m('claude-4-10-sonnet')).toBe(true);
|
||||
});
|
||||
|
||||
test('should not treat Sonnet 4 date suffixes as 1M context models', () => {
|
||||
expect(supportsContext1m('claude-sonnet-4-20250514')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for anthropic.claude-sonnet-4-6 (Bedrock)', () => {
|
||||
expect(supportsContext1m('anthropic.claude-sonnet-4-6')).toBe(true);
|
||||
});
|
||||
|
|
@ -1222,6 +1242,17 @@ describe('bedrockInputParser', () => {
|
|||
expect(output.maxOutputTokens).toBeUndefined();
|
||||
});
|
||||
|
||||
test('defaults Bedrock Claude Sonnet 4.6 adaptive maxTokens to its Bedrock max output when unset', () => {
|
||||
const parsed = bedrockInputParser.parse({
|
||||
model: 'anthropic.claude-sonnet-4-6',
|
||||
}) as Record<string, unknown>;
|
||||
const output = bedrockOutputParser(parsed as Record<string, unknown>);
|
||||
const amrf = output.additionalModelRequestFields as Record<string, unknown>;
|
||||
expect(amrf.thinking).toEqual({ type: 'adaptive' });
|
||||
expect(output.maxTokens).toBe(64000);
|
||||
expect(output.maxOutputTokens).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should respect user-provided maxTokens for adaptive model', () => {
|
||||
const parsed = bedrockInputParser.parse({
|
||||
model: 'anthropic.claude-opus-4-6-v1',
|
||||
|
|
@ -1289,7 +1320,7 @@ describe('bedrockInputParser', () => {
|
|||
// thinking models here but are not matched by the family-first reset()
|
||||
// regex; they must still resolve to the real ceiling, not the 8192 fallback.
|
||||
test.each([
|
||||
['anthropic.claude-4-7-sonnet', 64000],
|
||||
['anthropic.claude-4-7-sonnet', 128000],
|
||||
['claude-5-sonnet', 128000],
|
||||
['anthropic.claude-4-8-opus', 128000],
|
||||
])('defaults number-first adaptive alias %s to its real max output', (model, expected) => {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { z } from 'zod';
|
|||
import * as s from './schemas';
|
||||
|
||||
const DEFAULT_THINKING_BUDGET = 2000;
|
||||
const BEDROCK_CLAUDE_SONNET_4_6_MAX_OUTPUT = 64000;
|
||||
export const BEDROCK_OUTPUT_128K_BETA = 'output-128k-2025-02-19';
|
||||
export const BEDROCK_FINE_GRAINED_TOOL_STREAMING_BETA = 'fine-grained-tool-streaming-2025-05-14';
|
||||
|
||||
|
|
@ -93,16 +94,16 @@ function parseOpusVersion(model: string): { major: number; minor: number } | nul
|
|||
}
|
||||
|
||||
/** Extracts sonnet major/minor version from both naming formats.
|
||||
* Uses single-digit minor capture to avoid matching date suffixes (e.g., -20250514). */
|
||||
* Uses bounded minor capture to avoid matching date suffixes (e.g., -20250514). */
|
||||
function parseSonnetVersion(model: string): { major: number; minor: number } | null {
|
||||
const nameFirst = model.match(/claude-sonnet[-.]?(\d+)(?:[-.](\d)(?!\d))?/);
|
||||
const nameFirst = model.match(/claude-sonnet[-.]?(\d+)(?:[-.](\d{1,2})(?!\d))?/);
|
||||
if (nameFirst) {
|
||||
return {
|
||||
major: parseInt(nameFirst[1], 10),
|
||||
minor: nameFirst[2] != null ? parseInt(nameFirst[2], 10) : 0,
|
||||
};
|
||||
}
|
||||
const numFirst = model.match(/claude-(\d+)(?:[-.](\d)(?!\d))?-sonnet/);
|
||||
const numFirst = model.match(/claude-(\d+)(?:[-.](\d{1,2})(?!\d))?-sonnet/);
|
||||
if (numFirst) {
|
||||
return {
|
||||
major: parseInt(numFirst[1], 10),
|
||||
|
|
@ -683,6 +684,10 @@ function toFamilyFirstClaudeId(model: string): string {
|
|||
return model.replace(/claude-(\d+(?:[-.]\d+)?)-(sonnet|opus|haiku)/, 'claude-$2-$1');
|
||||
}
|
||||
|
||||
function isBedrockClaudeSonnet46(model: string): boolean {
|
||||
return /claude-sonnet[-.]?4[-.]?6(?=$|[^0-9])/.test(toFamilyFirstClaudeId(model));
|
||||
}
|
||||
|
||||
/**
|
||||
* Thinking tokens share the `maxTokens` output budget with tool-call arguments
|
||||
* (e.g. a `create_file` `content`), so a low default truncates large authored
|
||||
|
|
@ -695,6 +700,9 @@ function resolveThinkingMaxTokens(data: AnthropicInput): number {
|
|||
return explicit;
|
||||
}
|
||||
const model = typeof data.model === 'string' ? data.model : '';
|
||||
if (isBedrockClaudeSonnet46(model)) {
|
||||
return BEDROCK_CLAUDE_SONNET_4_6_MAX_OUTPUT;
|
||||
}
|
||||
return s.anthropicSettings.maxOutputTokens.reset(toFamilyFirstClaudeId(model));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ describe('anthropicSettings', () => {
|
|||
describe('maxOutputTokens.reset()', () => {
|
||||
const { reset } = anthropicSettings.maxOutputTokens;
|
||||
|
||||
describe('Claude Sonnet 4.x models (64K limit)', () => {
|
||||
describe('Claude Sonnet 4/4.5 models (64K limit)', () => {
|
||||
it('should return 64K for claude-sonnet-4', () => {
|
||||
expect(reset('claude-sonnet-4')).toBe(64000);
|
||||
});
|
||||
|
|
@ -19,8 +19,31 @@ describe('anthropicSettings', () => {
|
|||
expect(reset('claude-sonnet-4-5')).toBe(64000);
|
||||
});
|
||||
|
||||
it('should return 64K for claude-sonnet-4-6', () => {
|
||||
expect(reset('claude-sonnet-4-6')).toBe(64000);
|
||||
it('should return 64K for dated claude-sonnet-4', () => {
|
||||
expect(reset('claude-sonnet-4-20250514')).toBe(64000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Claude Sonnet 4.6+ models (128K limit)', () => {
|
||||
it('should return 128K for claude-sonnet-4-6', () => {
|
||||
expect(reset('claude-sonnet-4-6')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should return 128K for claude-sonnet-4.6', () => {
|
||||
expect(reset('claude-sonnet-4.6')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should return 128K for claude-sonnet-4-7', () => {
|
||||
expect(reset('claude-sonnet-4-7')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should return 128K for claude-sonnet-4-9', () => {
|
||||
expect(reset('claude-sonnet-4-9')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should return 128K for double-digit claude-sonnet-4 minors', () => {
|
||||
expect(reset('claude-sonnet-4-10')).toBe(128000);
|
||||
expect(reset('claude-sonnet-4.10')).toBe(128000);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -261,11 +284,15 @@ describe('anthropicSettings', () => {
|
|||
describe('maxOutputTokens.set()', () => {
|
||||
const { set } = anthropicSettings.maxOutputTokens;
|
||||
|
||||
describe('Claude Sonnet and Haiku 4+ models (64K cap)', () => {
|
||||
describe('Claude Sonnet 4/4.5 and Haiku 4+ models (64K cap)', () => {
|
||||
it('should cap at 64K for claude-sonnet-4 when value exceeds', () => {
|
||||
expect(set(100000, 'claude-sonnet-4')).toBe(64000);
|
||||
});
|
||||
|
||||
it('should cap at 64K for dated claude-sonnet-4 when value exceeds', () => {
|
||||
expect(set(100000, 'claude-sonnet-4-20250514')).toBe(64000);
|
||||
});
|
||||
|
||||
it('should allow 50K for claude-sonnet-4', () => {
|
||||
expect(set(50000, 'claude-sonnet-4')).toBe(50000);
|
||||
});
|
||||
|
|
@ -275,7 +302,28 @@ describe('anthropicSettings', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Claude Sonnet 5+ models (128K cap)', () => {
|
||||
describe('Claude Sonnet 4.6+ models (128K cap)', () => {
|
||||
it('should allow 100K for claude-sonnet-4-6', () => {
|
||||
expect(set(100000, 'claude-sonnet-4-6')).toBe(100000);
|
||||
});
|
||||
|
||||
it('should cap at 128K for claude-sonnet-4-6 when value exceeds', () => {
|
||||
expect(set(150000, 'claude-sonnet-4-6')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should allow 100K for claude-sonnet-4.6', () => {
|
||||
expect(set(100000, 'claude-sonnet-4.6')).toBe(100000);
|
||||
});
|
||||
|
||||
it('should cap at 128K for claude-sonnet-4.6 when value exceeds', () => {
|
||||
expect(set(150000, 'claude-sonnet-4.6')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should cap at 128K for double-digit claude-sonnet-4 minors', () => {
|
||||
expect(set(100000, 'claude-sonnet-4-10')).toBe(100000);
|
||||
expect(set(150000, 'claude-sonnet-4.10')).toBe(128000);
|
||||
});
|
||||
|
||||
it('should allow 100K for claude-sonnet-5', () => {
|
||||
expect(set(100000, 'claude-sonnet-5')).toBe(100000);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -487,6 +487,8 @@ const CLAUDE_4_64K_MAX_OUTPUT = 64000 as const;
|
|||
const CLAUDE_32K_MAX_OUTPUT = 32000 as const;
|
||||
const DEFAULT_MAX_OUTPUT = 8192 as const;
|
||||
const LEGACY_ANTHROPIC_MAX_OUTPUT = 4096 as const;
|
||||
const CLAUDE_SONNET_128K_OUTPUT_PATTERN =
|
||||
/claude-sonnet[-.]?(?:4[-.]?(?:[6-9]|\d{2})|[5-9]|\d{2,})(?=$|[^0-9])/;
|
||||
|
||||
/**
|
||||
* Claude "Mythos-class" model families — new top-level classes (peers of
|
||||
|
|
@ -547,7 +549,7 @@ export const anthropicSettings = {
|
|||
return ANTHROPIC_MAX_OUTPUT;
|
||||
}
|
||||
|
||||
if (/claude-sonnet[-.]?(?:[5-9]|\d{2,})/.test(modelName)) {
|
||||
if (CLAUDE_SONNET_128K_OUTPUT_PATTERN.test(modelName)) {
|
||||
return ANTHROPIC_MAX_OUTPUT;
|
||||
}
|
||||
|
||||
|
|
@ -580,7 +582,7 @@ export const anthropicSettings = {
|
|||
return value;
|
||||
}
|
||||
|
||||
if (/claude-sonnet[-.]?(?:[5-9]|\d{2,})/.test(modelName)) {
|
||||
if (CLAUDE_SONNET_128K_OUTPUT_PATTERN.test(modelName)) {
|
||||
if (value > ANTHROPIC_MAX_OUTPUT) {
|
||||
return ANTHROPIC_MAX_OUTPUT;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue