Fix $0.00 cost on OpenRouter image generation models
Image-gen models are billed per-image/per-request, not at the plain per-token prompt/completion rates ModelInfo.Pricing captures — so token-based cost calculation was always near-zero for them regardless of actual spend. Now requests OpenRouter's usage.include=true, decodes the actual billed usage.cost, and prefers it over calculated cost via a new resolveCost() helper (covers the dedicated Images API, regular chat completions, streaming, and the tool-calling loop alike).
This commit is contained in:
@@ -82,6 +82,36 @@ struct ChatViewModelPureLogicTests {
|
||||
#expect(ChatViewModel.calculateCost(usage: usage, pricing: pricing) == 0.0)
|
||||
}
|
||||
|
||||
// MARK: - resolveCost
|
||||
|
||||
@Test("Raw provider-billed cost wins over token-based pricing when present")
|
||||
func resolveCostPrefersRawCostUSD() {
|
||||
let usage = ChatResponse.Usage(promptTokens: 1_000_000, completionTokens: 1_000_000, totalTokens: 2_000_000, rawCostUSD: 0.19)
|
||||
let pricing = ModelInfo.Pricing(prompt: 3.0, completion: 15.0)
|
||||
#expect(ChatViewModel.resolveCost(usage: usage, pricing: pricing) == 0.19)
|
||||
}
|
||||
|
||||
@Test("Raw cost of zero is trusted, not treated as missing")
|
||||
func resolveCostTrustsZeroRawCost() {
|
||||
let usage = ChatResponse.Usage(promptTokens: 0, completionTokens: 0, totalTokens: 0, rawCostUSD: 0.0)
|
||||
let pricing = ModelInfo.Pricing(prompt: 3.0, completion: 15.0)
|
||||
#expect(ChatViewModel.resolveCost(usage: usage, pricing: pricing) == 0.0)
|
||||
}
|
||||
|
||||
@Test("Falls back to token-based pricing when no raw cost is reported")
|
||||
func resolveCostFallsBackToCalculation() {
|
||||
let usage = ChatResponse.Usage(promptTokens: 1_000_000, completionTokens: 1_000_000, totalTokens: 2_000_000)
|
||||
let pricing = ModelInfo.Pricing(prompt: 3.0, completion: 15.0)
|
||||
#expect(ChatViewModel.resolveCost(usage: usage, pricing: pricing) == 18.0)
|
||||
}
|
||||
|
||||
@Test("Returns nil when neither raw cost nor per-token pricing is available")
|
||||
func resolveCostNilWhenNoDataAvailable() {
|
||||
let usage = ChatResponse.Usage(promptTokens: 1_000_000, completionTokens: 1_000_000, totalTokens: 2_000_000)
|
||||
let pricing = ModelInfo.Pricing(prompt: 0, completion: 0)
|
||||
#expect(ChatViewModel.resolveCost(usage: usage, pricing: pricing) == nil)
|
||||
}
|
||||
|
||||
// MARK: - draftFingerprint
|
||||
|
||||
@Test("Identical message content produces the same fingerprint")
|
||||
|
||||
Reference in New Issue
Block a user