fix(zen): emit cost chunk in client-facing format, not upstream format (#16817)

This commit is contained in:
Kit Langton
2026-03-20 23:10:34 -04:00
committed by GitHub
parent 5dc47905a9
commit 6a64177589
6 changed files with 19 additions and 11 deletions
@@ -24,7 +24,7 @@ import {
FreeUsageLimitError, FreeUsageLimitError,
SubscriptionUsageLimitError, SubscriptionUsageLimitError,
} from "./error" } from "./error"
import { createBodyConverter, createStreamPartConverter, createResponseConverter, UsageInfo } from "./provider/provider" import { buildCostChunk, createBodyConverter, createStreamPartConverter, createResponseConverter, UsageInfo } from "./provider/provider"
import { anthropicHelper } from "./provider/anthropic" import { anthropicHelper } from "./provider/anthropic"
import { googleHelper } from "./provider/google" import { googleHelper } from "./provider/google"
import { openaiHelper } from "./provider/openai" import { openaiHelper } from "./provider/openai"
@@ -90,7 +90,7 @@ export async function handler(
const projectId = input.request.headers.get("x-opencode-project") ?? "" const projectId = input.request.headers.get("x-opencode-project") ?? ""
const ocClient = input.request.headers.get("x-opencode-client") ?? "" const ocClient = input.request.headers.get("x-opencode-client") ?? ""
logger.metric({ logger.metric({
is_tream: isStream, is_stream: isStream,
session: sessionId, session: sessionId,
request: requestId, request: requestId,
client: ocClient, client: ocClient,
@@ -230,7 +230,7 @@ export async function handler(
const body = JSON.stringify( const body = JSON.stringify(
responseConverter({ responseConverter({
...json, ...json,
cost: calculateOccuredCost(billingSource, costInfo), cost: calculateOccurredCost(billingSource, costInfo),
}), }),
) )
logger.metric({ response_length: body.length }) logger.metric({ response_length: body.length })
@@ -274,8 +274,8 @@ export async function handler(
await trialLimiter?.track(usageInfo) await trialLimiter?.track(usageInfo)
await trackUsage(sessionId, billingSource, authInfo, modelInfo, providerInfo, usageInfo, costInfo) await trackUsage(sessionId, billingSource, authInfo, modelInfo, providerInfo, usageInfo, costInfo)
await reload(billingSource, authInfo, costInfo) await reload(billingSource, authInfo, costInfo)
const cost = calculateOccuredCost(billingSource, costInfo) const cost = calculateOccurredCost(billingSource, costInfo)
c.enqueue(encoder.encode(usageParser.buidlCostChunk(cost))) c.enqueue(encoder.encode(buildCostChunk(opts.format, cost)))
} }
c.close() c.close()
return return
@@ -818,7 +818,7 @@ export async function handler(
} }
} }
function calculateOccuredCost(billingSource: BillingSource, costInfo: CostInfo) { function calculateOccurredCost(billingSource: BillingSource, costInfo: CostInfo) {
return billingSource === "balance" ? (costInfo.totalCostInCent / 100).toFixed(8) : "0" return billingSource === "balance" ? (costInfo.totalCostInCent / 100).toFixed(8) : "0"
} }
@@ -167,7 +167,6 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) =>
} }
}, },
retrieve: () => usage, retrieve: () => usage,
buidlCostChunk: (cost: string) => `event: ping\ndata: ${JSON.stringify({ type: "ping", cost })}\n\n`,
} }
}, },
normalizeUsage: (usage: Usage) => ({ normalizeUsage: (usage: Usage) => ({
@@ -56,7 +56,6 @@ export const googleHelper: ProviderHelper = ({ providerModel }) => ({
usage = json.usageMetadata usage = json.usageMetadata
}, },
retrieve: () => usage, retrieve: () => usage,
buidlCostChunk: (cost: string) => `data: ${JSON.stringify({ type: "ping", cost })}\n\n`,
} }
}, },
normalizeUsage: (usage: Usage) => { normalizeUsage: (usage: Usage) => {
@@ -54,7 +54,6 @@ export const oaCompatHelper: ProviderHelper = () => ({
usage = json.usage usage = json.usage
}, },
retrieve: () => usage, retrieve: () => usage,
buidlCostChunk: (cost: string) => `data: ${JSON.stringify({ choices: [], cost })}\n\n`,
} }
}, },
normalizeUsage: (usage: Usage) => { normalizeUsage: (usage: Usage) => {
@@ -44,7 +44,6 @@ export const openaiHelper: ProviderHelper = () => ({
usage = json.response.usage usage = json.response.usage
}, },
retrieve: () => usage, retrieve: () => usage,
buidlCostChunk: (cost: string) => `event: ping\ndata: ${JSON.stringify({ type: "ping", cost })}\n\n`,
} }
}, },
normalizeUsage: (usage: Usage) => { normalizeUsage: (usage: Usage) => {
@@ -43,7 +43,6 @@ export type ProviderHelper = (input: { reqModel: string; providerModel: string }
createUsageParser: () => { createUsageParser: () => {
parse: (chunk: string) => void parse: (chunk: string) => void
retrieve: () => any retrieve: () => any
buidlCostChunk: (cost: string) => string
} }
normalizeUsage: (usage: any) => UsageInfo normalizeUsage: (usage: any) => UsageInfo
} }
@@ -162,6 +161,19 @@ export interface CommonChunk {
} }
} }
export function buildCostChunk(format: ZenData.Format, cost: string): string {
switch (format) {
case "anthropic":
return `event: ping\ndata: ${JSON.stringify({ type: "ping", cost })}\n\n`
case "openai":
return `event: ping\ndata: ${JSON.stringify({ type: "ping", cost })}\n\n`
case "oa-compat":
return `data: ${JSON.stringify({ choices: [], cost })}\n\n`
default:
return `data: ${JSON.stringify({ type: "ping", cost })}\n\n`
}
}
export function createBodyConverter(from: ZenData.Format, to: ZenData.Format) { export function createBodyConverter(from: ZenData.Format, to: ZenData.Format) {
return (body: any): any => { return (body: any): any => {
if (from === to) return body if (from === to) return body