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:
@@ -1150,8 +1150,7 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
if let usage = response.usage {
|
||||
messages[index].tokens = usage.completionTokens
|
||||
if let model = selectedModel {
|
||||
let hasPricing = model.pricing.prompt > 0 || model.pricing.completion > 0
|
||||
let cost: Double? = hasPricing ? Self.calculateCost(usage: usage, pricing: model.pricing) : nil
|
||||
let cost = Self.resolveCost(usage: usage, pricing: model.pricing)
|
||||
messages[index].cost = cost
|
||||
sessionStats.addMessage(inputTokens: usage.promptTokens, outputTokens: usage.completionTokens, cost: cost)
|
||||
}
|
||||
@@ -1214,8 +1213,7 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
if let usage = totalTokens {
|
||||
messages[index].tokens = usage.completionTokens
|
||||
if let model = selectedModel {
|
||||
let hasPricing = model.pricing.prompt > 0 || model.pricing.completion > 0
|
||||
let cost: Double? = hasPricing ? Self.calculateCost(usage: usage, pricing: model.pricing) : nil
|
||||
let cost = Self.resolveCost(usage: usage, pricing: model.pricing)
|
||||
messages[index].cost = cost
|
||||
sessionStats.addMessage(inputTokens: usage.promptTokens, outputTokens: usage.completionTokens, cost: cost)
|
||||
}
|
||||
@@ -1859,8 +1857,7 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
if willAutoContinue && finalContent.isEmpty {
|
||||
// Nothing worth showing yet — still record usage/cost for this turn.
|
||||
if let usage = totalUsage, let model = selectedModel {
|
||||
let hasPricing = model.pricing.prompt > 0 || model.pricing.completion > 0
|
||||
let cost: Double? = hasPricing ? Self.calculateCost(usage: usage, pricing: model.pricing) : nil
|
||||
let cost = Self.resolveCost(usage: usage, pricing: model.pricing)
|
||||
sessionStats.addMessage(
|
||||
inputTokens: usage.promptTokens,
|
||||
outputTokens: usage.completionTokens,
|
||||
@@ -1884,8 +1881,7 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
|
||||
// Calculate cost
|
||||
if let usage = totalUsage, let model = selectedModel {
|
||||
let hasPricing = model.pricing.prompt > 0 || model.pricing.completion > 0
|
||||
let cost: Double? = hasPricing ? Self.calculateCost(usage: usage, pricing: model.pricing) : nil
|
||||
let cost = Self.resolveCost(usage: usage, pricing: model.pricing)
|
||||
if let index = messages.lastIndex(where: { $0.id == assistantMessage.id }) {
|
||||
messages[index].cost = cost
|
||||
}
|
||||
@@ -2604,6 +2600,16 @@ Don't narrate future actions ("Let me...") - just use the tools.
|
||||
return inputCost + cacheReadCost + cacheWriteCost + outputCost
|
||||
}
|
||||
|
||||
/// Resolves a response's cost, preferring the provider's actual billed amount
|
||||
/// (`usage.rawCostUSD` — e.g. OpenRouter's `usage.include` cost, needed for models priced
|
||||
/// outside plain per-token rates like per-image generation) over token-based calculation.
|
||||
/// Falls back to `nil` when neither the raw cost nor per-token pricing is available.
|
||||
nonisolated static func resolveCost(usage: ChatResponse.Usage, pricing: ModelInfo.Pricing) -> Double? {
|
||||
if let raw = usage.rawCostUSD { return raw }
|
||||
guard pricing.prompt > 0 || pricing.completion > 0 else { return nil }
|
||||
return calculateCost(usage: usage, pricing: pricing)
|
||||
}
|
||||
|
||||
/// Summarize a chunk of messages into a concise summary
|
||||
private func summarizeMessageChunk(_ messages: [Message]) async -> String? {
|
||||
guard let provider = providerRegistry.getProvider(for: currentProvider),
|
||||
|
||||
Reference in New Issue
Block a user