diff --git a/llm/ai-sdk/provider/stripe-language-model-v3.ts b/llm/ai-sdk/provider/stripe-language-model-v3.ts index 9ce96218..b9bd5b83 100644 --- a/llm/ai-sdk/provider/stripe-language-model-v3.ts +++ b/llm/ai-sdk/provider/stripe-language-model-v3.ts @@ -185,15 +185,22 @@ export class StripeLanguageModelV3 implements LanguageModelV3 { const model = modelId.substring('anthropic/'.length); + // Check both dash-form (claude-3-7-sonnet) and dot-form (claude-3.7-sonnet) + // since normalizeModelId converts version dashes to dots if ( model.includes('sonnet-4') || model.includes('claude-3-7-sonnet') || - model.includes('haiku-4-5') + model.includes('claude-3.7-sonnet') || + model.includes('haiku-4-5') || + model.includes('haiku-4.5') ) { return 64000; } else if (model.includes('opus-4')) { return 32000; - } else if (model.includes('claude-3-5-haiku')) { + } else if ( + model.includes('claude-3-5-haiku') || + model.includes('claude-3.5-haiku') + ) { return 8192; } else { return 4096; diff --git a/llm/ai-sdk/provider/stripe-language-model.ts b/llm/ai-sdk/provider/stripe-language-model.ts index 38c15f09..34c6fe16 100644 --- a/llm/ai-sdk/provider/stripe-language-model.ts +++ b/llm/ai-sdk/provider/stripe-language-model.ts @@ -208,19 +208,25 @@ export class StripeLanguageModel implements LanguageModelV2 { const model = modelId.substring('anthropic/'.length); // Claude Sonnet 4 models (including variants like sonnet-4-1) and 3.7 Sonnet - if (model.includes('sonnet-4') || - model.includes('claude-3-7-sonnet') || - model.includes('haiku-4-5')) { + // Check both dash-form (claude-3-7-sonnet) and dot-form (claude-3.7-sonnet) + // since normalizeModelId converts version dashes to dots + if (model.includes('sonnet-4') || + model.includes('claude-3-7-sonnet') || + model.includes('claude-3.7-sonnet') || + model.includes('haiku-4-5') || + model.includes('haiku-4.5')) { return 64000; // 64K tokens - } + } // Claude Opus 4 models (including variants like opus-4-1) else if (model.includes('opus-4')) { return 32000; // 32K tokens - } + } // Claude 3.5 Haiku - else if (model.includes('claude-3-5-haiku')) { + // Check both dash-form (claude-3-5-haiku) and dot-form (claude-3.5-haiku) + else if (model.includes('claude-3-5-haiku') || + model.includes('claude-3.5-haiku')) { return 8192; // 8K tokens - } + } // Default fallback for other Anthropic models else { return 4096; diff --git a/llm/ai-sdk/provider/tests/stripe-language-model-v3.test.ts b/llm/ai-sdk/provider/tests/stripe-language-model-v3.test.ts index 30958186..25827fed 100644 --- a/llm/ai-sdk/provider/tests/stripe-language-model-v3.test.ts +++ b/llm/ai-sdk/provider/tests/stripe-language-model-v3.test.ts @@ -422,6 +422,64 @@ describe('StripeLanguageModelV3', () => { expect(args.max_tokens).toBeUndefined(); }); + it('should apply correct defaults for normalized (dot-form) model IDs', () => { + const testCases = [ + {modelId: 'anthropic/claude-3.7-sonnet', expected: 64000}, + {modelId: 'anthropic/claude-3.5-haiku', expected: 8192}, + {modelId: 'anthropic/claude-haiku-4.5', expected: 64000}, + {modelId: 'anthropic/claude-opus-4', expected: 32000}, + {modelId: 'anthropic/claude-sonnet-4', expected: 64000}, + ]; + + testCases.forEach(({modelId, expected}) => { + const m = new StripeLanguageModelV3( + modelId, + {customerId: 'cus_test'}, + { + provider: 'stripe', + baseURL: 'https://llm.stripe.com', + headers: () => ({}), + } + ); + + const options: LanguageModelV3CallOptions = { + prompt: [], + }; + + // @ts-expect-error - Accessing private method for testing + const {args} = m.getArgs(options); + expect(args.max_tokens).toBe(expected); + }); + }); + + it('should apply correct defaults for dash-form model IDs (pre-normalization)', () => { + const testCases = [ + {modelId: 'anthropic/claude-3-7-sonnet', expected: 64000}, + {modelId: 'anthropic/claude-3-5-haiku', expected: 8192}, + {modelId: 'anthropic/claude-haiku-4-5', expected: 64000}, + ]; + + testCases.forEach(({modelId, expected}) => { + const m = new StripeLanguageModelV3( + modelId, + {customerId: 'cus_test'}, + { + provider: 'stripe', + baseURL: 'https://llm.stripe.com', + headers: () => ({}), + } + ); + + const options: LanguageModelV3CallOptions = { + prompt: [], + }; + + // @ts-expect-error - Accessing private method for testing + const {args} = m.getArgs(options); + expect(args.max_tokens).toBe(expected); + }); + }); + it('should allow user-provided maxOutputTokens to override default', () => { const sonnetModel = new StripeLanguageModelV3( 'anthropic/claude-sonnet-4', diff --git a/llm/ai-sdk/provider/tests/stripe-language-model.test.ts b/llm/ai-sdk/provider/tests/stripe-language-model.test.ts index 502380d4..3e89a4f0 100644 --- a/llm/ai-sdk/provider/tests/stripe-language-model.test.ts +++ b/llm/ai-sdk/provider/tests/stripe-language-model.test.ts @@ -421,6 +421,65 @@ describe('StripeLanguageModel', () => { expect(args.max_tokens).toBeUndefined(); }); + it('should apply correct defaults for normalized (dot-form) model IDs', () => { + // These are the IDs that normalizeModelId produces + const testCases = [ + {modelId: 'anthropic/claude-3.7-sonnet', expected: 64000}, + {modelId: 'anthropic/claude-3.5-haiku', expected: 8192}, + {modelId: 'anthropic/claude-haiku-4.5', expected: 64000}, + {modelId: 'anthropic/claude-opus-4', expected: 32000}, + {modelId: 'anthropic/claude-sonnet-4', expected: 64000}, + ]; + + testCases.forEach(({modelId, expected}) => { + const m = new StripeLanguageModel( + modelId, + {customerId: 'cus_test'}, + { + provider: 'stripe', + baseURL: 'https://llm.stripe.com', + headers: () => ({}), + } + ); + + const options: LanguageModelV2CallOptions = { + prompt: [], + }; + + // @ts-expect-error - Accessing private method for testing + const {args} = m.getArgs(options); + expect(args.max_tokens).toBe(expected); + }); + }); + + it('should apply correct defaults for dash-form model IDs (pre-normalization)', () => { + const testCases = [ + {modelId: 'anthropic/claude-3-7-sonnet', expected: 64000}, + {modelId: 'anthropic/claude-3-5-haiku', expected: 8192}, + {modelId: 'anthropic/claude-haiku-4-5', expected: 64000}, + ]; + + testCases.forEach(({modelId, expected}) => { + const m = new StripeLanguageModel( + modelId, + {customerId: 'cus_test'}, + { + provider: 'stripe', + baseURL: 'https://llm.stripe.com', + headers: () => ({}), + } + ); + + const options: LanguageModelV2CallOptions = { + prompt: [], + }; + + // @ts-expect-error - Accessing private method for testing + const {args} = m.getArgs(options); + expect(args.max_tokens).toBe(expected); + }); + }); + it('should allow user-provided maxOutputTokens to override default', () => { const sonnetModel = new StripeLanguageModel( 'anthropic/claude-sonnet-4',