@@ -5,7 +5,31 @@ enum CostUsagePricing {
55 let inputCostPerToken : Double
66 let outputCostPerToken : Double
77 let cacheReadInputCostPerToken : Double ?
8+ let thresholdTokens : Int ?
9+ let inputCostPerTokenAboveThreshold : Double ?
10+ let outputCostPerTokenAboveThreshold : Double ?
11+ let cacheReadInputCostPerTokenAboveThreshold : Double ?
812 let displayLabel : String ?
13+
14+ init (
15+ inputCostPerToken: Double ,
16+ outputCostPerToken: Double ,
17+ cacheReadInputCostPerToken: Double ? ,
18+ thresholdTokens: Int ? = nil ,
19+ inputCostPerTokenAboveThreshold: Double ? = nil ,
20+ outputCostPerTokenAboveThreshold: Double ? = nil ,
21+ cacheReadInputCostPerTokenAboveThreshold: Double ? = nil ,
22+ displayLabel: String ? = nil )
23+ {
24+ self . inputCostPerToken = inputCostPerToken
25+ self . outputCostPerToken = outputCostPerToken
26+ self . cacheReadInputCostPerToken = cacheReadInputCostPerToken
27+ self . thresholdTokens = thresholdTokens
28+ self . inputCostPerTokenAboveThreshold = inputCostPerTokenAboveThreshold
29+ self . outputCostPerTokenAboveThreshold = outputCostPerTokenAboveThreshold
30+ self . cacheReadInputCostPerTokenAboveThreshold = cacheReadInputCostPerTokenAboveThreshold
31+ self . displayLabel = displayLabel
32+ }
933 }
1034
1135 struct ClaudePricing : Sendable {
@@ -27,11 +51,26 @@ enum CostUsagePricing {
2751 outputCostPerToken: 1e-5 ,
2852 cacheReadInputCostPerToken: 1.25e-7 ,
2953 displayLabel: nil ) ,
54+ " gpt-5-chat " : CodexPricing (
55+ inputCostPerToken: 1.25e-6 ,
56+ outputCostPerToken: 1e-5 ,
57+ cacheReadInputCostPerToken: 1.25e-7 ,
58+ displayLabel: nil ) ,
59+ " gpt-5-chat-latest " : CodexPricing (
60+ inputCostPerToken: 1.25e-6 ,
61+ outputCostPerToken: 1e-5 ,
62+ cacheReadInputCostPerToken: 1.25e-7 ,
63+ displayLabel: nil ) ,
3064 " gpt-5-codex " : CodexPricing (
3165 inputCostPerToken: 1.25e-6 ,
3266 outputCostPerToken: 1e-5 ,
3367 cacheReadInputCostPerToken: 1.25e-7 ,
3468 displayLabel: nil ) ,
69+ " gpt-5-codex-mini " : CodexPricing (
70+ inputCostPerToken: 2.5e-7 ,
71+ outputCostPerToken: 2e-6 ,
72+ cacheReadInputCostPerToken: 2.5e-8 ,
73+ displayLabel: nil ) ,
3574 " gpt-5-mini " : CodexPricing (
3675 inputCostPerToken: 2.5e-7 ,
3776 outputCostPerToken: 2e-6 ,
@@ -52,6 +91,11 @@ enum CostUsagePricing {
5291 outputCostPerToken: 1e-5 ,
5392 cacheReadInputCostPerToken: 1.25e-7 ,
5493 displayLabel: nil ) ,
94+ " gpt-5.1-chat-latest " : CodexPricing (
95+ inputCostPerToken: 1.25e-6 ,
96+ outputCostPerToken: 1e-5 ,
97+ cacheReadInputCostPerToken: 1.25e-7 ,
98+ displayLabel: nil ) ,
5599 " gpt-5.1-codex " : CodexPricing (
56100 inputCostPerToken: 1.25e-6 ,
57101 outputCostPerToken: 1e-5 ,
@@ -72,6 +116,16 @@ enum CostUsagePricing {
72116 outputCostPerToken: 1.4e-5 ,
73117 cacheReadInputCostPerToken: 1.75e-7 ,
74118 displayLabel: nil ) ,
119+ " gpt-5.2-chat " : CodexPricing (
120+ inputCostPerToken: 1.75e-6 ,
121+ outputCostPerToken: 1.4e-5 ,
122+ cacheReadInputCostPerToken: 1.75e-7 ,
123+ displayLabel: nil ) ,
124+ " gpt-5.2-chat-latest " : CodexPricing (
125+ inputCostPerToken: 1.75e-6 ,
126+ outputCostPerToken: 1.4e-5 ,
127+ cacheReadInputCostPerToken: 1.75e-7 ,
128+ displayLabel: nil ) ,
75129 " gpt-5.2-codex " : CodexPricing (
76130 inputCostPerToken: 1.75e-6 ,
77131 outputCostPerToken: 1.4e-5 ,
@@ -87,6 +141,16 @@ enum CostUsagePricing {
87141 outputCostPerToken: 1.4e-5 ,
88142 cacheReadInputCostPerToken: 1.75e-7 ,
89143 displayLabel: nil ) ,
144+ " gpt-5.3 " : CodexPricing (
145+ inputCostPerToken: 1.75e-6 ,
146+ outputCostPerToken: 1.4e-5 ,
147+ cacheReadInputCostPerToken: 1.75e-7 ,
148+ displayLabel: nil ) ,
149+ " gpt-5.3-chat-latest " : CodexPricing (
150+ inputCostPerToken: 1.75e-6 ,
151+ outputCostPerToken: 1.4e-5 ,
152+ cacheReadInputCostPerToken: 1.75e-7 ,
153+ displayLabel: nil ) ,
90154 " gpt-5.3-codex-spark " : CodexPricing (
91155 inputCostPerToken: 0 ,
92156 outputCostPerToken: 0 ,
@@ -218,22 +282,44 @@ enum CostUsagePricing {
218282 ]
219283
220284 static func normalizeCodexModel( _ raw: String ) -> String {
285+ var trimmed = self . displayCodexModel ( raw)
286+ if let snapshotBase = self . codexSnapshotBaseModel ( trimmed) {
287+ trimmed = snapshotBase
288+ }
289+ if self . codex [ trimmed] != nil {
290+ return trimmed
291+ }
292+ if let codexRange = trimmed. range ( of: " -codex " ) , !trimmed. contains ( " -codex-mini " ) {
293+ let base = String ( trimmed [ ..< codexRange. lowerBound] )
294+ if self . codex [ base] != nil { return base }
295+ }
296+ return trimmed
297+ }
298+
299+ static func displayCodexModel( _ raw: String ) -> String {
221300 var trimmed = raw. trimmingCharacters ( in: . whitespacesAndNewlines)
222301 if trimmed. hasPrefix ( " openai/ " ) {
223302 trimmed = String ( trimmed. dropFirst ( " openai/ " . count) )
224303 }
304+ return trimmed
305+ }
225306
226- if self . codex [ trimmed] != nil {
227- return trimmed
228- }
307+ private static func codexSnapshotBaseModel( _ raw: String ) -> String ? {
308+ let patterns = [
309+ #"-\d{4}-\d{2}-\d{2}$"# ,
310+ #"-\d{8}$"# ,
311+ ]
229312
230- if let datedSuffix = trimmed. range ( of: #"-\d{4}-\d{2}-\d{2}$"# , options: . regularExpression) {
231- let base = String ( trimmed [ ..< datedSuffix. lowerBound] )
232- if self . codex [ base] != nil {
233- return base
313+ for pattern in patterns {
314+ if let range = raw. range ( of: pattern, options: . regularExpression) {
315+ let base = String ( raw [ ..< range. lowerBound] )
316+ if self . codex [ base] != nil {
317+ return base
318+ }
234319 }
235320 }
236- return trimmed
321+
322+ return nil
237323 }
238324
239325 static func codexDisplayLabel( model: String ) -> String ? {
@@ -275,10 +361,28 @@ enum CostUsagePricing {
275361 guard let pricing = self . codex [ key] else { return nil }
276362 let cached = min ( max ( 0 , cachedInputTokens) , max ( 0 , inputTokens) )
277363 let nonCached = max ( 0 , inputTokens - cached)
278- let cachedRate = pricing. cacheReadInputCostPerToken ?? pricing. inputCostPerToken
279- return Double ( nonCached) * pricing. inputCostPerToken
280- + Double( cached) * cachedRate
281- + Double( max ( 0 , outputTokens) ) * pricing. outputCostPerToken
364+ let effectiveInputTokens = max ( 0 , inputTokens)
365+ let useAboveThresholdPricing = if let threshold = pricing. thresholdTokens {
366+ effectiveInputTokens > threshold
367+ } else {
368+ false
369+ }
370+ let inputRate = useAboveThresholdPricing
371+ ? ( pricing. inputCostPerTokenAboveThreshold ?? pricing. inputCostPerToken)
372+ : pricing. inputCostPerToken
373+ let outputRate = useAboveThresholdPricing
374+ ? ( pricing. outputCostPerTokenAboveThreshold ?? pricing. outputCostPerToken)
375+ : pricing. outputCostPerToken
376+ let cacheRate = useAboveThresholdPricing
377+ ? ( pricing. cacheReadInputCostPerTokenAboveThreshold ?? pricing. cacheReadInputCostPerToken)
378+ : pricing. cacheReadInputCostPerToken
379+
380+ if cached > 0 , cacheRate == nil {
381+ return nil
382+ }
383+ return Double ( nonCached) * inputRate
384+ + Double( cached) * ( cacheRate ?? 0 )
385+ + Double( max ( 0 , outputTokens) ) * outputRate
282386 }
283387
284388 static func claudeCostUSD(
0 commit comments