From ae896d6430a618abe5fc131bd971efdc0384673e Mon Sep 17 00:00:00 2001 From: tianyao Date: Thu, 23 Jul 2026 10:07:18 +0000 Subject: [PATCH] fix(pricing): add OpenAI/Codex model pricing, stop hardcoding cost to $0 Codex CLI sessions always reported $0 estimated cost because estimatedCostUsd was hardcoded in codex.ts, and the shared pricing table had no entries for any OpenAI/GPT model at all. Adds pricing for the current GPT-5.x/Codex, GPT-4.x, and o-series models, and wires codex.ts to actually calculate cost from usage. Co-Authored-By: Claude Sonnet 5 --- cli/src/providers/codex.ts | 10 ++++++++- cli/src/utils/pricing.test.ts | 7 ++++++- cli/src/utils/pricing.ts | 38 +++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/cli/src/providers/codex.ts b/cli/src/providers/codex.ts index 0f95d950..712a067f 100644 --- a/cli/src/providers/codex.ts +++ b/cli/src/providers/codex.ts @@ -4,6 +4,7 @@ import * as os from 'os'; import type { SessionProvider } from './types.js'; import type { ParsedSession, ParsedMessage, ToolCall, ToolResult, SessionUsage, MessageUsage } from '../types.js'; import { generateTitle, detectSessionCharacter } from '../parser/titles.js'; +import { calculateCost } from '../utils/pricing.js'; /** * OpenAI Codex CLI session provider. @@ -657,7 +658,14 @@ function buildSession( totalOutputTokens: totalOutput, cacheCreationTokens: 0, cacheReadTokens: totalCached, - estimatedCostUsd: 0, // Codex pricing not public + estimatedCostUsd: calculateCost([{ + model: model || 'unknown', + usage: { + input_tokens: totalInput, + output_tokens: totalOutput, + cache_read_input_tokens: totalCached, + }, + }]), modelsUsed: model ? [model] : [], primaryModel: model || 'unknown', usageSource: 'jsonl', diff --git a/cli/src/utils/pricing.test.ts b/cli/src/utils/pricing.test.ts index 219f6e39..8a761ed8 100644 --- a/cli/src/utils/pricing.test.ts +++ b/cli/src/utils/pricing.test.ts @@ -24,10 +24,15 @@ describe('getModelPricing', () => { }); it('returns default pricing for unknown model', () => { - const pricing = getModelPricing('gpt-4o-mini'); + const pricing = getModelPricing('some-unrecognized-model-xyz'); expect(pricing).toEqual({ input: 3, output: 15 }); }); + it('returns exact match for a gpt-5.x codex model', () => { + const pricing = getModelPricing('gpt-5.6-terra'); + expect(pricing).toEqual({ input: 2.5, output: 15 }); + }); + it('returns default pricing for empty string', () => { const pricing = getModelPricing(''); expect(pricing).toEqual({ input: 3, output: 15 }); diff --git a/cli/src/utils/pricing.ts b/cli/src/utils/pricing.ts index c2a103df..3d0aa37f 100644 --- a/cli/src/utils/pricing.ts +++ b/cli/src/utils/pricing.ts @@ -48,6 +48,44 @@ const MODEL_PRICING: Record = { 'gemini-1.5-flash': { input: 0.075, output: 0.3 }, 'gemini-3.1-pro': { input: 3.5, output: 10.5 }, 'gemini-3.1-flash': { input: 0.1, output: 0.4 }, + + // OpenAI GPT-5.x family (incl. Codex CLI models) — longest/most-specific keys first + 'gpt-5.6-luna': { input: 1, output: 6 }, + 'gpt-5.6-sol': { input: 5, output: 30 }, + 'gpt-5.6-terra': { input: 2.5, output: 15 }, + 'gpt-5.6': { input: 5, output: 30 }, // alias for gpt-5.6-sol + 'gpt-5.5-pro': { input: 30, output: 180 }, + 'gpt-5.5': { input: 5, output: 30 }, + 'gpt-5.4-mini': { input: 0.75, output: 4.5 }, + 'gpt-5.4-nano': { input: 0.2, output: 1.25 }, + 'gpt-5.4-pro': { input: 30, output: 180 }, + 'gpt-5.4': { input: 2.5, output: 15 }, + 'gpt-5.3-codex': { input: 1.75, output: 14 }, + 'gpt-5.2': { input: 1.75, output: 14 }, + 'gpt-5.1': { input: 1.25, output: 10 }, + 'gpt-5-codex': { input: 1.25, output: 10 }, + 'gpt-5-mini': { input: 0.25, output: 2 }, + 'gpt-5-nano': { input: 0.05, output: 0.4 }, + 'gpt-5-pro': { input: 15, output: 120 }, + 'gpt-5': { input: 1.25, output: 10 }, + + // OpenAI GPT-4.x family + 'gpt-4.1-mini': { input: 0.4, output: 1.6 }, + 'gpt-4.1-nano': { input: 0.1, output: 0.4 }, + 'gpt-4.1': { input: 2, output: 8 }, + 'gpt-4o-mini': { input: 0.15, output: 0.6 }, + 'gpt-4o': { input: 2.5, output: 10 }, + + // OpenAI o-series (reasoning models) + 'o1-mini': { input: 1.1, output: 4.4 }, + 'o1-pro': { input: 150, output: 600 }, + 'o1': { input: 15, output: 60 }, + 'o3-deep-research': { input: 10, output: 40 }, + 'o3-mini': { input: 1.1, output: 4.4 }, + 'o3-pro': { input: 20, output: 80 }, + 'o3': { input: 2, output: 8 }, + 'o4-mini-deep-research': { input: 2, output: 8 }, + 'o4-mini': { input: 1.1, output: 4.4 }, }; // Default fallback pricing (sonnet-level)