From a03c574bef70b515863f3f8f6ecb3290f5c93ab0 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 5 Jul 2026 12:04:37 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=A2=20fix:=20Raise=20Claude=20Sonnet?= =?UTF-8?q?=204.6=20Output=20Cap=20(#14115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- api/utils/tokens.spec.js | 73 ++++++++++++++++++- .../api/src/endpoints/anthropic/llm.spec.ts | 23 +++++- packages/api/src/utils/tokens.ts | 65 ++++++++++++++++- packages/data-provider/specs/bedrock.spec.ts | 33 ++++++++- packages/data-provider/src/bedrock.ts | 14 +++- packages/data-provider/src/schemas.spec.ts | 58 +++++++++++++-- packages/data-provider/src/schemas.ts | 6 +- 7 files changed, 255 insertions(+), 17 deletions(-) diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index 9089bd8a41..6fac8a01f1 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -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), ); }); diff --git a/packages/api/src/endpoints/anthropic/llm.spec.ts b/packages/api/src/endpoints/anthropic/llm.spec.ts index 9d242b35b0..fe93b50884 100644 --- a/packages/api/src/endpoints/anthropic/llm.spec.ts +++ b/packages/api/src/endpoints/anthropic/llm.spec.ts @@ -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', () => { diff --git a/packages/api/src/utils/tokens.ts b/packages/api/src/utils/tokens.ts index e607f78106..4a18358e22 100644 --- a/packages/api/src/utils/tokens.ts +++ b/packages/api/src/utils/tokens.ts @@ -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; } diff --git a/packages/data-provider/specs/bedrock.spec.ts b/packages/data-provider/specs/bedrock.spec.ts index 6a793b964d..7952925131 100644 --- a/packages/data-provider/specs/bedrock.spec.ts +++ b/packages/data-provider/specs/bedrock.spec.ts @@ -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; + const output = bedrockOutputParser(parsed as Record); + const amrf = output.additionalModelRequestFields as Record; + 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) => { diff --git a/packages/data-provider/src/bedrock.ts b/packages/data-provider/src/bedrock.ts index 35f3268d4e..3aad248075 100644 --- a/packages/data-provider/src/bedrock.ts +++ b/packages/data-provider/src/bedrock.ts @@ -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)); } diff --git a/packages/data-provider/src/schemas.spec.ts b/packages/data-provider/src/schemas.spec.ts index 25f1808143..d15974f13b 100644 --- a/packages/data-provider/src/schemas.spec.ts +++ b/packages/data-provider/src/schemas.spec.ts @@ -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); }); diff --git a/packages/data-provider/src/schemas.ts b/packages/data-provider/src/schemas.ts index 50375e9ebc..47e40bf589 100644 --- a/packages/data-provider/src/schemas.ts +++ b/packages/data-provider/src/schemas.ts @@ -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; }