11import type { ModelInfo } from "@roo-code/types"
22
3+ /**
4+ * Determine effective per‑million prices for this request based on model tiers.
5+ * If tiers are defined, pick the first tier whose contextWindow >= tierBasisTokens.
6+ * Fallback to the last tier when all tiers are below the observed tokens.
7+ */
8+ function selectTierPrices (
9+ modelInfo : ModelInfo ,
10+ tierBasisTokens : number ,
11+ ) : {
12+ inputPrice : number
13+ outputPrice : number
14+ cacheReadsPrice : number
15+ } {
16+ let inputPrice = modelInfo . inputPrice ?? 0
17+ let outputPrice = modelInfo . outputPrice ?? 0
18+ let cacheReadsPrice = modelInfo . cacheReadsPrice ?? 0
19+
20+ const tiers = ( modelInfo as ModelInfo ) . tiers
21+ if ( Array . isArray ( tiers ) && tiers . length > 0 ) {
22+ // If tiers are "service tiers" (e.g., OpenAI flex/priority), they will have a name.
23+ // Do NOT auto-select by tokens in that case. Pricing is chosen explicitly by the provider path.
24+ const hasNamedTiers = ( tiers as any [ ] ) . some (
25+ ( t ) => typeof ( t as any ) . name === "string" && ( t as any ) . name . length > 0 ,
26+ )
27+
28+ if ( ! hasNamedTiers ) {
29+ // Choose the smallest tier that can accommodate the request's input size
30+ let chosen =
31+ tiers . find (
32+ ( t ) =>
33+ tierBasisTokens <=
34+ ( t . contextWindow === Infinity ? Number . POSITIVE_INFINITY : ( t . contextWindow as number ) ) ,
35+ ) || tiers [ tiers . length - 1 ] !
36+
37+ inputPrice = chosen . inputPrice ?? inputPrice
38+ outputPrice = chosen . outputPrice ?? outputPrice
39+ cacheReadsPrice = chosen . cacheReadsPrice ?? cacheReadsPrice
40+ }
41+ }
42+
43+ return { inputPrice, outputPrice, cacheReadsPrice }
44+ }
45+
346function calculateApiCostInternal (
447 modelInfo : ModelInfo ,
548 inputTokens : number ,
649 outputTokens : number ,
750 cacheCreationInputTokens : number ,
851 cacheReadInputTokens : number ,
52+ // Use total input tokens (before cache deductions) to determine tier selection
53+ tierBasisTokens : number ,
954) : number {
10- const cacheWritesCost = ( ( modelInfo . cacheWritesPrice || 0 ) / 1_000_000 ) * cacheCreationInputTokens
11- const cacheReadsCost = ( ( modelInfo . cacheReadsPrice || 0 ) / 1_000_000 ) * cacheReadInputTokens
12- const baseInputCost = ( ( modelInfo . inputPrice || 0 ) / 1_000_000 ) * inputTokens
13- const outputCost = ( ( modelInfo . outputPrice || 0 ) / 1_000_000 ) * outputTokens
14- const totalCost = cacheWritesCost + cacheReadsCost + baseInputCost + outputCost
15- return totalCost
55+ const { inputPrice, outputPrice, cacheReadsPrice } = selectTierPrices ( modelInfo , tierBasisTokens )
56+
57+ const cacheWritesPrice = modelInfo . cacheWritesPrice || 0
58+ const cacheWritesCost = ( cacheWritesPrice / 1_000_000 ) * cacheCreationInputTokens
59+ const cacheReadsCost = ( cacheReadsPrice / 1_000_000 ) * cacheReadInputTokens
60+ const baseInputCost = ( inputPrice / 1_000_000 ) * inputTokens
61+ const outputCost = ( outputPrice / 1_000_000 ) * outputTokens
62+
63+ return cacheWritesCost + cacheReadsCost + baseInputCost + outputCost
1664}
1765
1866// For Anthropic compliant usage, the input tokens count does NOT include the
@@ -30,6 +78,8 @@ export function calculateApiCostAnthropic(
3078 outputTokens ,
3179 cacheCreationInputTokens || 0 ,
3280 cacheReadInputTokens || 0 ,
81+ // Tier basis for Anthropic protocol = actual input tokens (no cache included)
82+ inputTokens ,
3383 )
3484}
3585
@@ -51,6 +101,8 @@ export function calculateApiCostOpenAI(
51101 outputTokens ,
52102 cacheCreationInputTokensNum ,
53103 cacheReadInputTokensNum ,
104+ // Tier basis for OpenAI protocol = total input tokens before subtracting cache
105+ inputTokens ,
54106 )
55107}
56108
0 commit comments