💰 feat: Add gpt-5.3 context window and pricing (#12049)

* 💰 feat: Add gpt-5.3 context window and pricing

* 💰 feat: Add OpenAI cached input pricing and `gpt-5.2-pro` model

    - Add cached input pricing (write/read) for gpt-4o, gpt-4.1, gpt-5.x,
      o1, o3, o4-mini models with correct per-family discount tiers
    - Add gpt-5.2-pro pricing ($21/$168), context window, and max output
    - Pro models (gpt-5-pro, gpt-5.2-pro) correctly excluded from cache
      pricing as OpenAI does not support caching for these

* 🔍 fix: Address review findings for OpenAI pricing

- Add o1-preview to cacheTokenValues (50% discount, same as o1)
- Fix comment to enumerate all models per discount tier
- Add cache tests for dated variants (gpt-4o-2024-08-06, etc.)
- Add gpt-5-mini/gpt-5-nano to 10% ratio invariant test
- Replace forEach with for...of in new test code
- Fix inconsistent test description phrasing
- Add gpt-5.3-preview to context window tests
This commit is contained in:
Danny Avila 2026-03-03 20:44:05 -05:00 committed by GitHub
parent 474001c140
commit d3622844ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 156 additions and 2 deletions

View file

@ -52,6 +52,13 @@ describe('getValueKey', () => {
expect(getValueKey('openai/gpt-5.2')).toBe('gpt-5.2');
});
it('should return "gpt-5.3" for model name containing "gpt-5.3"', () => {
expect(getValueKey('gpt-5.3')).toBe('gpt-5.3');
expect(getValueKey('gpt-5.3-chat-latest')).toBe('gpt-5.3');
expect(getValueKey('gpt-5.3-codex')).toBe('gpt-5.3');
expect(getValueKey('openai/gpt-5.3')).toBe('gpt-5.3');
});
it('should return "gpt-3.5-turbo-1106" for model name containing "gpt-3.5-turbo-1106"', () => {
expect(getValueKey('gpt-3.5-turbo-1106-some-other-info')).toBe('gpt-3.5-turbo-1106');
expect(getValueKey('openai/gpt-3.5-turbo-1106')).toBe('gpt-3.5-turbo-1106');
@ -138,6 +145,12 @@ describe('getValueKey', () => {
expect(getValueKey('gpt-5-pro-preview')).toBe('gpt-5-pro');
});
it('should return "gpt-5.2-pro" for model name containing "gpt-5.2-pro"', () => {
expect(getValueKey('gpt-5.2-pro')).toBe('gpt-5.2-pro');
expect(getValueKey('gpt-5.2-pro-2025-03-01')).toBe('gpt-5.2-pro');
expect(getValueKey('openai/gpt-5.2-pro')).toBe('gpt-5.2-pro');
});
it('should return "gpt-4o" for model type of "gpt-4o"', () => {
expect(getValueKey('gpt-4o-2024-08-06')).toBe('gpt-4o');
expect(getValueKey('gpt-4o-2024-08-06-0718')).toBe('gpt-4o');
@ -336,6 +349,18 @@ describe('getMultiplier', () => {
);
});
it('should return the correct multiplier for gpt-5.2-pro', () => {
expect(getMultiplier({ model: 'gpt-5.2-pro', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.2-pro'].prompt,
);
expect(getMultiplier({ model: 'gpt-5.2-pro', tokenType: 'completion' })).toBe(
tokenValues['gpt-5.2-pro'].completion,
);
expect(getMultiplier({ model: 'openai/gpt-5.2-pro', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.2-pro'].prompt,
);
});
it('should return the correct multiplier for gpt-5.1', () => {
expect(getMultiplier({ model: 'gpt-5.1', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.1'].prompt,
@ -360,6 +385,21 @@ describe('getMultiplier', () => {
);
});
it('should return the correct multiplier for gpt-5.3', () => {
expect(getMultiplier({ model: 'gpt-5.3', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.3'].prompt,
);
expect(getMultiplier({ model: 'gpt-5.3', tokenType: 'completion' })).toBe(
tokenValues['gpt-5.3'].completion,
);
expect(getMultiplier({ model: 'gpt-5.3-codex', tokenType: 'prompt' })).toBe(
tokenValues['gpt-5.3'].prompt,
);
expect(getMultiplier({ model: 'openai/gpt-5.3', tokenType: 'completion' })).toBe(
tokenValues['gpt-5.3'].completion,
);
});
it('should return the correct multiplier for gpt-4o', () => {
const valueKey = getValueKey('gpt-4o-2024-08-06');
expect(getMultiplier({ valueKey, tokenType: 'prompt' })).toBe(tokenValues['gpt-4o'].prompt);
@ -1326,6 +1366,62 @@ describe('getCacheMultiplier', () => {
).toBeNull();
});
it('should return correct cache multipliers for OpenAI models', () => {
const openaiCacheModels = [
'gpt-4o',
'gpt-4o-mini',
'gpt-4.1',
'gpt-4.1-mini',
'gpt-4.1-nano',
'gpt-5',
'gpt-5.1',
'gpt-5.2',
'gpt-5.3',
'gpt-5-mini',
'gpt-5-nano',
'o1',
'o1-mini',
'o1-preview',
'o3',
'o3-mini',
'o4-mini',
];
for (const model of openaiCacheModels) {
expect(getCacheMultiplier({ model, cacheType: 'write' })).toBe(cacheTokenValues[model].write);
expect(getCacheMultiplier({ model, cacheType: 'read' })).toBe(cacheTokenValues[model].read);
}
});
it('should return correct cache multipliers for OpenAI dated variants', () => {
expect(getCacheMultiplier({ model: 'gpt-4o-2024-08-06', cacheType: 'read' })).toBe(
cacheTokenValues['gpt-4o'].read,
);
expect(getCacheMultiplier({ model: 'gpt-4.1-2026-01-01', cacheType: 'read' })).toBe(
cacheTokenValues['gpt-4.1'].read,
);
expect(getCacheMultiplier({ model: 'gpt-5.3-codex', cacheType: 'read' })).toBe(
cacheTokenValues['gpt-5.3'].read,
);
expect(getCacheMultiplier({ model: 'openai/gpt-5.3', cacheType: 'write' })).toBe(
cacheTokenValues['gpt-5.3'].write,
);
});
it('should return null for pro models that do not support caching', () => {
expect(getCacheMultiplier({ model: 'gpt-5-pro', cacheType: 'read' })).toBeNull();
expect(getCacheMultiplier({ model: 'gpt-5-pro', cacheType: 'write' })).toBeNull();
expect(getCacheMultiplier({ model: 'gpt-5.2-pro', cacheType: 'read' })).toBeNull();
expect(getCacheMultiplier({ model: 'gpt-5.2-pro', cacheType: 'write' })).toBeNull();
});
it('should have consistent 10% cache read pricing for gpt-5.x models', () => {
const gpt5CacheModels = ['gpt-5', 'gpt-5.1', 'gpt-5.2', 'gpt-5.3', 'gpt-5-mini', 'gpt-5-nano'];
for (const model of gpt5CacheModels) {
expect(cacheTokenValues[model].read).toBeCloseTo(cacheTokenValues[model].write * 0.1, 10);
}
});
it('should handle models with "bedrock/" prefix', () => {
expect(
getCacheMultiplier({