mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: update tiered pricing for Gemini 2.5 Pro, Sonnet 4/4.5, and Qwen 3 Max across all providers
This commit is contained in:
parent
5e34db13c8
commit
7cfe770fc3
5 changed files with 174 additions and 253 deletions
|
|
@ -26,6 +26,15 @@ export const bedrockModels = {
|
|||
minTokensPerCachePoint: 1024,
|
||||
maxCachePoints: 4,
|
||||
cachableFields: ["system", "messages", "tools"],
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0,
|
||||
outputPrice: 22.5,
|
||||
cacheWritesPrice: 7.5,
|
||||
cacheReadsPrice: 0.6,
|
||||
},
|
||||
],
|
||||
},
|
||||
"amazon.nova-pro-v1:0": {
|
||||
maxTokens: 5000,
|
||||
|
|
@ -90,6 +99,15 @@ export const bedrockModels = {
|
|||
minTokensPerCachePoint: 1024,
|
||||
maxCachePoints: 4,
|
||||
cachableFields: ["system", "messages", "tools"],
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0,
|
||||
outputPrice: 22.5,
|
||||
cacheWritesPrice: 7.5,
|
||||
cacheReadsPrice: 0.6,
|
||||
},
|
||||
],
|
||||
},
|
||||
"anthropic.claude-opus-4-1-20250805-v1:0": {
|
||||
maxTokens: 8192,
|
||||
|
|
|
|||
|
|
@ -138,24 +138,19 @@ export const geminiModels = {
|
|||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheWritesPrice: 4.5,
|
||||
inputPrice: 1.25, // Base price for ≤200k tokens
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
maxThinkingTokens: 32_768,
|
||||
supportsReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.31,
|
||||
},
|
||||
{
|
||||
contextWindow: Infinity,
|
||||
inputPrice: 2.5,
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5, // >200k tokens
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -164,22 +159,17 @@ export const geminiModels = {
|
|||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheWritesPrice: 4.5,
|
||||
inputPrice: 1.25, // Base price for ≤200k tokens
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.31,
|
||||
},
|
||||
{
|
||||
contextWindow: Infinity,
|
||||
inputPrice: 2.5,
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5, // >200k tokens
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -188,24 +178,19 @@ export const geminiModels = {
|
|||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheWritesPrice: 4.5,
|
||||
inputPrice: 1.25, // Base price for ≤200k tokens
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
maxThinkingTokens: 32_768,
|
||||
supportsReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.31,
|
||||
},
|
||||
{
|
||||
contextWindow: Infinity,
|
||||
inputPrice: 2.5,
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5, // >200k tokens
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -222,25 +207,20 @@ export const geminiModels = {
|
|||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheWritesPrice: 4.5,
|
||||
inputPrice: 1.25, // Base price for ≤200k tokens
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
maxThinkingTokens: 32_768,
|
||||
supportsReasoningBudget: true,
|
||||
requiredReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.31,
|
||||
},
|
||||
{
|
||||
contextWindow: Infinity,
|
||||
inputPrice: 2.5,
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5, // >200k tokens
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -61,49 +61,79 @@ export const vertexModels = {
|
|||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
"gemini-2.5-pro-preview-05-06": {
|
||||
maxTokens: 65_535,
|
||||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
"gemini-2.5-pro-preview-06-05": {
|
||||
maxTokens: 65_535,
|
||||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
maxThinkingTokens: 32_768,
|
||||
supportsReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
"gemini-2.5-pro": {
|
||||
maxTokens: 64_000,
|
||||
contextWindow: 1_048_576,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.125,
|
||||
cacheWritesPrice: 1.625,
|
||||
maxThinkingTokens: 32_768,
|
||||
supportsReasoningBudget: true,
|
||||
requiredReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000,
|
||||
inputPrice: 1.25,
|
||||
outputPrice: 10,
|
||||
cacheReadsPrice: 0.31,
|
||||
},
|
||||
{
|
||||
contextWindow: Infinity,
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.625,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -173,6 +203,15 @@ export const vertexModels = {
|
|||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
supportsReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0,
|
||||
outputPrice: 22.5,
|
||||
cacheWritesPrice: 7.5,
|
||||
cacheReadsPrice: 0.6,
|
||||
},
|
||||
],
|
||||
},
|
||||
"claude-sonnet-4-5@20250929": {
|
||||
maxTokens: 8192,
|
||||
|
|
@ -184,6 +223,15 @@ export const vertexModels = {
|
|||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
supportsReasoningBudget: true,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0,
|
||||
outputPrice: 22.5,
|
||||
cacheWritesPrice: 7.5,
|
||||
cacheReadsPrice: 0.6,
|
||||
},
|
||||
],
|
||||
},
|
||||
"claude-haiku-4-5@20251001": {
|
||||
maxTokens: 8192,
|
||||
|
|
@ -216,6 +264,15 @@ export const vertexModels = {
|
|||
outputPrice: 75.0,
|
||||
cacheWritesPrice: 18.75,
|
||||
cacheReadsPrice: 1.5,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 30.0,
|
||||
outputPrice: 112.5,
|
||||
cacheWritesPrice: 37.5,
|
||||
cacheReadsPrice: 3.0,
|
||||
},
|
||||
],
|
||||
},
|
||||
"claude-3-7-sonnet@20250219:thinking": {
|
||||
maxTokens: 64_000,
|
||||
|
|
|
|||
|
|
@ -263,5 +263,56 @@ export const parseOpenRouterModel = ({
|
|||
modelInfo.maxTokens = 32768
|
||||
}
|
||||
|
||||
// Add tiered pricing for Gemini 2.5 Pro models on OpenRouter
|
||||
if (id.includes("gemini-2.5-pro") || id.includes("gemini/2.5-pro")) {
|
||||
modelInfo.inputPrice = 1.25
|
||||
modelInfo.outputPrice = 10
|
||||
modelInfo.cacheReadsPrice = 0.125
|
||||
modelInfo.cacheWritesPrice = 1.625
|
||||
modelInfo.tiers = [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 2.5,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.25,
|
||||
cacheWritesPrice: 2.875,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// Add tiered pricing for Claude Sonnet 4 and 4.5 on OpenRouter
|
||||
if (id === "anthropic/claude-sonnet-4" || id === "anthropic/claude-sonnet-4.5") {
|
||||
modelInfo.inputPrice = 3.0
|
||||
modelInfo.outputPrice = 15.0
|
||||
modelInfo.cacheWritesPrice = 3.75
|
||||
modelInfo.cacheReadsPrice = 0.3
|
||||
modelInfo.tiers = [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0,
|
||||
outputPrice: 22.5,
|
||||
cacheWritesPrice: 7.5,
|
||||
cacheReadsPrice: 0.6,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// Add tiered pricing for Qwen 3 Max on OpenRouter
|
||||
if (id.toLowerCase().includes("qwen") && id.toLowerCase().includes("max")) {
|
||||
modelInfo.inputPrice = 1.2
|
||||
modelInfo.outputPrice = 6
|
||||
modelInfo.cacheReadsPrice = 0.24
|
||||
modelInfo.cacheWritesPrice = 0 // Free
|
||||
modelInfo.tiers = [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 3,
|
||||
outputPrice: 15,
|
||||
cacheReadsPrice: 0.6,
|
||||
cacheWritesPrice: 0, // Free
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
return modelInfo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,190 +221,5 @@ describe("Cost Utility", () => {
|
|||
expect(result.totalInputTokens).toBe(6000) // Total already includes cache
|
||||
expect(result.totalOutputTokens).toBe(500)
|
||||
})
|
||||
|
||||
describe("tiered pricing", () => {
|
||||
const modelWithTiers: ModelInfo = {
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0, // $3 per million tokens (<= 200K)
|
||||
outputPrice: 15.0, // $15 per million tokens (<= 200K)
|
||||
cacheWritesPrice: 3.75, // $3.75 per million tokens (<= 200K)
|
||||
cacheReadsPrice: 0.3, // $0.30 per million tokens (<= 200K)
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000, // 1M tokens
|
||||
inputPrice: 6.0, // $6 per million tokens (> 200K)
|
||||
outputPrice: 22.5, // $22.50 per million tokens (> 200K)
|
||||
cacheWritesPrice: 7.5, // $7.50 per million tokens (> 200K)
|
||||
cacheReadsPrice: 0.6, // $0.60 per million tokens (> 200K)
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
it("should use base prices when total input tokens are below 200K", () => {
|
||||
const result = calculateApiCostAnthropic(modelWithTiers, 50_000, 10_000, 50_000, 50_000)
|
||||
|
||||
// Total input: 50K + 50K + 50K = 150K (below 200K threshold)
|
||||
// Should use base prices: $3/$15
|
||||
// Input cost: (3.0 / 1_000_000) * 50_000 = 0.15
|
||||
// Output cost: (15.0 / 1_000_000) * 10_000 = 0.15
|
||||
// Cache writes: (3.75 / 1_000_000) * 50_000 = 0.1875
|
||||
// Cache reads: (0.3 / 1_000_000) * 50_000 = 0.015
|
||||
// Total: 0.15 + 0.15 + 0.1875 + 0.015 = 0.5025
|
||||
expect(result.totalInputTokens).toBe(150_000)
|
||||
expect(result.totalOutputTokens).toBe(10_000)
|
||||
expect(result.totalCost).toBeCloseTo(0.5025, 6)
|
||||
})
|
||||
|
||||
it("should use tier prices when total input tokens exceed 200K", () => {
|
||||
const result = calculateApiCostAnthropic(modelWithTiers, 100_000, 20_000, 100_000, 100_000)
|
||||
|
||||
// Total input: 100K + 100K + 100K = 300K (above 200K, below 1M)
|
||||
// Should use tier prices: $6/$22.50
|
||||
// Input cost: (6.0 / 1_000_000) * 100_000 = 0.6
|
||||
// Output cost: (22.5 / 1_000_000) * 20_000 = 0.45
|
||||
// Cache writes: (7.5 / 1_000_000) * 100_000 = 0.75
|
||||
// Cache reads: (0.6 / 1_000_000) * 100_000 = 0.06
|
||||
// Total: 0.6 + 0.45 + 0.75 + 0.06 = 1.86
|
||||
expect(result.totalInputTokens).toBe(300_000)
|
||||
expect(result.totalOutputTokens).toBe(20_000)
|
||||
expect(result.totalCost).toBeCloseTo(1.86, 6)
|
||||
})
|
||||
|
||||
it("should use the highest tier prices when exceeding all tier thresholds", () => {
|
||||
const result = calculateApiCostAnthropic(modelWithTiers, 500_000, 50_000, 300_000, 300_000)
|
||||
|
||||
// Total input: 500K + 300K + 300K = 1.1M (above 1M threshold)
|
||||
// Should use highest tier prices: $6/$22.50 (last tier)
|
||||
// Input cost: (6.0 / 1_000_000) * 500_000 = 3.0
|
||||
// Output cost: (22.5 / 1_000_000) * 50_000 = 1.125
|
||||
// Cache writes: (7.5 / 1_000_000) * 300_000 = 2.25
|
||||
// Cache reads: (0.6 / 1_000_000) * 300_000 = 0.18
|
||||
// Total: 3.0 + 1.125 + 2.25 + 0.18 = 6.555
|
||||
expect(result.totalInputTokens).toBe(1_100_000)
|
||||
expect(result.totalOutputTokens).toBe(50_000)
|
||||
expect(result.totalCost).toBeCloseTo(6.555, 6)
|
||||
})
|
||||
|
||||
it("should handle partial tier definitions", () => {
|
||||
// Model where tier only overrides some prices
|
||||
const modelPartialTiers: ModelInfo = {
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0,
|
||||
outputPrice: 15.0,
|
||||
cacheWritesPrice: 3.75,
|
||||
cacheReadsPrice: 0.3,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000,
|
||||
inputPrice: 6.0, // Only input price changes
|
||||
// output, cacheWrites, cacheReads prices should fall back to base
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const result = calculateApiCostAnthropic(modelPartialTiers, 100_000, 20_000, 100_000, 100_000)
|
||||
|
||||
// Total input: 300K (uses tier)
|
||||
// Input cost: (6.0 / 1_000_000) * 100_000 = 0.6 (tier price)
|
||||
// Output cost: (15.0 / 1_000_000) * 20_000 = 0.3 (base price)
|
||||
// Cache writes: (3.75 / 1_000_000) * 100_000 = 0.375 (base price)
|
||||
// Cache reads: (0.3 / 1_000_000) * 100_000 = 0.03 (base price)
|
||||
// Total: 0.6 + 0.3 + 0.375 + 0.03 = 1.305
|
||||
expect(result.totalInputTokens).toBe(300_000)
|
||||
expect(result.totalOutputTokens).toBe(20_000)
|
||||
expect(result.totalCost).toBeCloseTo(1.305, 6)
|
||||
})
|
||||
|
||||
it("should handle multiple tiers correctly", () => {
|
||||
const modelMultipleTiers: ModelInfo = {
|
||||
contextWindow: 128_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 0.075, // <= 128K
|
||||
outputPrice: 0.3,
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 200_000, // First tier
|
||||
inputPrice: 0.15,
|
||||
outputPrice: 0.6,
|
||||
},
|
||||
{
|
||||
contextWindow: 1_000_000, // Second tier
|
||||
inputPrice: 0.3,
|
||||
outputPrice: 1.2,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
// Test below first threshold (128K)
|
||||
let result = calculateApiCostAnthropic(modelMultipleTiers, 50_000, 10_000)
|
||||
expect(result.totalCost).toBeCloseTo((0.075 * 50 + 0.3 * 10) / 1000, 6)
|
||||
|
||||
// Test between first and second threshold (150K)
|
||||
result = calculateApiCostAnthropic(modelMultipleTiers, 150_000, 10_000)
|
||||
expect(result.totalCost).toBeCloseTo((0.15 * 150 + 0.6 * 10) / 1000, 6)
|
||||
|
||||
// Test above second threshold (500K)
|
||||
result = calculateApiCostAnthropic(modelMultipleTiers, 500_000, 10_000)
|
||||
expect(result.totalCost).toBeCloseTo((0.3 * 500 + 1.2 * 10) / 1000, 6)
|
||||
})
|
||||
})
|
||||
|
||||
describe("tiered pricing for OpenAI", () => {
|
||||
const modelWithTiers: ModelInfo = {
|
||||
contextWindow: 200_000,
|
||||
supportsImages: true,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0, // $3 per million tokens (<= 200K)
|
||||
outputPrice: 15.0, // $15 per million tokens (<= 200K)
|
||||
cacheWritesPrice: 3.75, // $3.75 per million tokens (<= 200K)
|
||||
cacheReadsPrice: 0.3, // $0.30 per million tokens (<= 200K)
|
||||
tiers: [
|
||||
{
|
||||
contextWindow: 1_000_000, // 1M tokens
|
||||
inputPrice: 6.0, // $6 per million tokens (> 200K)
|
||||
outputPrice: 22.5, // $22.50 per million tokens (> 200K)
|
||||
cacheWritesPrice: 7.5, // $7.50 per million tokens (> 200K)
|
||||
cacheReadsPrice: 0.6, // $0.60 per million tokens (> 200K)
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
it("should use tier prices for OpenAI when total input tokens exceed threshold", () => {
|
||||
// Total input: 300K (includes all tokens)
|
||||
const result = calculateApiCostOpenAI(modelWithTiers, 300_000, 20_000, 100_000, 100_000)
|
||||
|
||||
// Total input is 300K (above 200K, below 1M) - uses tier pricing
|
||||
// Non-cached input: 300K - 100K - 100K = 100K
|
||||
// Input cost: (6.0 / 1_000_000) * 100_000 = 0.6
|
||||
// Output cost: (22.5 / 1_000_000) * 20_000 = 0.45
|
||||
// Cache writes: (7.5 / 1_000_000) * 100_000 = 0.75
|
||||
// Cache reads: (0.6 / 1_000_000) * 100_000 = 0.06
|
||||
// Total: 0.6 + 0.45 + 0.75 + 0.06 = 1.86
|
||||
expect(result.totalInputTokens).toBe(300_000)
|
||||
expect(result.totalOutputTokens).toBe(20_000)
|
||||
expect(result.totalCost).toBeCloseTo(1.86, 6)
|
||||
})
|
||||
|
||||
it("should use base prices for OpenAI when total input tokens are below threshold", () => {
|
||||
// Total input: 150K (includes all tokens)
|
||||
const result = calculateApiCostOpenAI(modelWithTiers, 150_000, 10_000, 50_000, 50_000)
|
||||
|
||||
// Total input is 150K (below 200K) - uses base pricing
|
||||
// Non-cached input: 150K - 50K - 50K = 50K
|
||||
// Input cost: (3.0 / 1_000_000) * 50_000 = 0.15
|
||||
// Output cost: (15.0 / 1_000_000) * 10_000 = 0.15
|
||||
// Cache writes: (3.75 / 1_000_000) * 50_000 = 0.1875
|
||||
// Cache reads: (0.3 / 1_000_000) * 50_000 = 0.015
|
||||
// Total: 0.15 + 0.15 + 0.1875 + 0.015 = 0.5025
|
||||
expect(result.totalInputTokens).toBe(150_000)
|
||||
expect(result.totalOutputTokens).toBe(10_000)
|
||||
expect(result.totalCost).toBeCloseTo(0.5025, 6)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue