说明:这是一次 Agent 分析报告。报告基于公开源码、OpenAI 官方接口契约,以及一次受控请求复现实验整理;不包含任何密钥、账号凭据或私有请求正文。
摘要
当前 codex2api 对 fast / priority 请求的计费逻辑是:只要客户端请求了 fast 或 priority,即使上游最终响应里的 response.service_tier 是 default,usage log 仍记录为 fast,并按 priority / fast 价格计费。
这与 OpenAI Responses API 对 service_tier 的说明可能不一致。OpenAI 官方契约说明:响应体里的 service_tier 表示实际用于服务本次请求的 processing mode,且可能不同于请求参数。
When the service_tier parameter is set, the response body will include the service_tier value based on the processing mode actually used to serve the request. This response value may be different from the value set in the parameter.
因此,如果计费口径应为“按实际获得的服务等级计费”,则应优先使用响应中的 actual service_tier,而不是请求中的 intended service_tier。
复现现象
受控请求:
{
"model": "gpt-5.5",
"input": "Reply with exactly: ok",
"stream": true,
"service_tier": "priority",
"max_output_tokens": 16,
"reasoning": { "effort": "minimal" }
}
raw SSE 终态返回:
{
"type": "response.completed",
"response": {
"service_tier": "default",
"usage": {
"input_tokens": 108,
"output_tokens": 17,
"total_tokens": 125
}
}
}
但 codex2api usage log 中记录为:
service_tier = fast
input_tokens = 108
cached_tokens = 0
output_tokens = 17
reasoning_tokens = 10
input_price_per_mtoken = 12.5
output_price_per_mtoken = 75
cache_read_price_per_mtoken = 1.25
account_billed = 0.002625
user_billed = 0.002625
如果按 actual default 计费,金额应为:
108 * 5 / 1_000_000 + 17 * 30 / 1_000_000 = 0.00105
实际按 priority / fast 价格计为:
108 * 12.5 / 1_000_000 + 17 * 75 / 1_000_000 = 0.002625
源码路径
1. 请求 tier 会被提取为 requested tier
proxy/handler.go 会从请求体提取 service_tier:
serviceTier := extractServiceTier(rawBody)
2. 上游响应的 actual tier 会被读取
streaming /v1/responses 成功路径会读取终态 SSE:
if eventType == "response.completed" {
usage = extractUsageFromResult(parsed.Get("response.usage"))
if tier := parsed.Get("response.service_tier").String(); tier != "" {
actualServiceTier = tier
}
gotTerminal = true
}
并且 SSE payload 是原样向下游转发的,因此 downstream 看到的 response.service_tier = default 不是 codex2api 伪造的,而是上游实际返回值。
3. usage log tier 被 request intent 覆盖
proxy/translator.go 中:
func resolveServiceTier(actualTier, requestedTier string) string {
requestedTier = strings.TrimSpace(requestedTier)
if requestedTier == "fast" || requestedTier == "priority" {
return "fast"
}
...
}
因此:
resolveServiceTier("default", "priority") == "fast"
4. billing tier 也被 request intent 覆盖
当前 main 中:
func resolveBillingServiceTier(actualTier, requestedTier string) string {
requestedTier = strings.ToLower(strings.TrimSpace(requestedTier))
if requestedTier == "priority" || requestedTier == "fast" {
return "priority"
}
actualTier = strings.ToLower(strings.TrimSpace(actualTier))
if actualTier == "priority" || actualTier == "fast" {
return "priority"
}
if actualTier != "" {
return actualTier
}
return requestedTier
}
因此:
resolveBillingServiceTier("default", "priority") == "priority"
5. InsertUsageLog 使用 BillingServiceTier 计算金额
database/postgres.go 中:
billingServiceTier := log.BillingServiceTier
if billingServiceTier == "" {
billingServiceTier = log.ServiceTier
}
accountBilled := calculateCost(
log.InputTokens,
log.OutputTokens,
log.CachedTokens,
billingModel,
billingServiceTier,
)
这会导致持久化的 service_tier = fast 和 account_billed = priority-priced amount,但无法保留 upstream actual tier。
历史背景
PR #153 Fix/fast pricing 曾经引入过“actual default downgrade wins”的测试逻辑:
actual default downgrade wins: actual=default, requested=fast, want=default
但后续 commit eb4e1cd451f317485d7a2b54bac53e53fed13bab 明确改成:
fix(billing): bill fast/priority by client intent, not upstream-reported tier
提交说明中写道:当 Codex backend 把 fast request 降级为 service_tier="default" 时,仍应按 priority 计费,以避免 fast 用户“漏费”。
这个行为也许是当前项目的产品策略,但它和 OpenAI Responses API 的 response.service_tier 语义不同:OpenAI 响应字段代表 actual processing mode,而不是 request intent。
期望行为
如果项目希望“按实际服务等级计费”,建议:
billingServiceTier 优先使用 actual response tier:
resolveBillingServiceTier("default", "priority") == "default"
- 日志中拆分三个概念,避免 UI / 对账歧义:
requested_service_tier = priority
actual_service_tier = default
billing_service_tier = default 或 priority(取决于项目策略)
- 如果项目坚持“按请求 fast/priority 意图计费”,建议在 UI / API 字段上明确标注:
service_tier = requested/billing intent, not actual response tier
否则用户会误以为 usage log 中的 fast 是上游实际返回的 tier。
影响
在上游实际返回 default 时,当前逻辑仍可能按 priority / fast 价格计费。对于需要按 OpenAI actual response tier 对账的场景,会出现:
响应 actual tier: default
codex2api usage tier: fast
codex2api billing tier: priority
这会造成用户侧与网关 / 上游 actual tier 的成本核算不一致。
摘要
当前 codex2api 对
fast/priority请求的计费逻辑是:只要客户端请求了fast或priority,即使上游最终响应里的response.service_tier是default,usage log 仍记录为fast,并按 priority / fast 价格计费。这与 OpenAI Responses API 对
service_tier的说明可能不一致。OpenAI 官方契约说明:响应体里的service_tier表示实际用于服务本次请求的 processing mode,且可能不同于请求参数。因此,如果计费口径应为“按实际获得的服务等级计费”,则应优先使用响应中的 actual
service_tier,而不是请求中的 intendedservice_tier。复现现象
受控请求:
{ "model": "gpt-5.5", "input": "Reply with exactly: ok", "stream": true, "service_tier": "priority", "max_output_tokens": 16, "reasoning": { "effort": "minimal" } }raw SSE 终态返回:
{ "type": "response.completed", "response": { "service_tier": "default", "usage": { "input_tokens": 108, "output_tokens": 17, "total_tokens": 125 } } }但 codex2api usage log 中记录为:
如果按 actual
default计费,金额应为:实际按 priority / fast 价格计为:
源码路径
1. 请求 tier 会被提取为 requested tier
proxy/handler.go会从请求体提取service_tier:2. 上游响应的 actual tier 会被读取
streaming
/v1/responses成功路径会读取终态 SSE:并且 SSE payload 是原样向下游转发的,因此 downstream 看到的
response.service_tier = default不是 codex2api 伪造的,而是上游实际返回值。3. usage log tier 被 request intent 覆盖
proxy/translator.go中:因此:
4. billing tier 也被 request intent 覆盖
当前 main 中:
因此:
5. InsertUsageLog 使用 BillingServiceTier 计算金额
database/postgres.go中:这会导致持久化的
service_tier = fast和account_billed = priority-priced amount,但无法保留 upstream actual tier。历史背景
PR #153
Fix/fast pricing曾经引入过“actual default downgrade wins”的测试逻辑:但后续 commit
eb4e1cd451f317485d7a2b54bac53e53fed13bab明确改成:提交说明中写道:当 Codex backend 把 fast request 降级为
service_tier="default"时,仍应按 priority 计费,以避免 fast 用户“漏费”。这个行为也许是当前项目的产品策略,但它和 OpenAI Responses API 的
response.service_tier语义不同:OpenAI 响应字段代表 actual processing mode,而不是 request intent。期望行为
如果项目希望“按实际服务等级计费”,建议:
billingServiceTier优先使用 actual response tier:否则用户会误以为 usage log 中的
fast是上游实际返回的 tier。影响
在上游实际返回
default时,当前逻辑仍可能按 priority / fast 价格计费。对于需要按 OpenAI actual response tier 对账的场景,会出现:这会造成用户侧与网关 / 上游 actual tier 的成本核算不一致。