Skip to content

Commit eb4e1cd

Browse files
committed
fix(billing): bill fast/priority by client intent, not upstream-reported tier
resolveBillingServiceTier previously let any concrete upstream tier override the client's intent, so when a Codex backend downgraded a fast request to service_tier="default" the usage log was billed at base rate — defeating the 2x multiplier added in fb0a97a. Now: if the client explicitly requested fast/priority, billing is locked to priority regardless of what upstream reports. Only when the client did NOT request a priority tier do we fall back to the upstream's actual tier. Root cause of #158: reproduced locally by sending matching default/fast gpt-5.4-mini requests and observing identical account_billed in usage_logs despite rate_multiplier=2.
1 parent fb0a97a commit eb4e1cd

2 files changed

Lines changed: 16 additions & 17 deletions

File tree

proxy/translator.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,26 +1815,24 @@ func resolveServiceTier(actualTier, requestedTier string) string {
18151815
return final
18161816
}
18171817

1818-
// resolveBillingServiceTier keeps UI tier normalization separate from billing:
1819-
// fast/priority intent is billed as priority only when the upstream does not
1820-
// report a concrete tier, or when it confirms fast/priority. Any concrete
1821-
// upstream tier wins so billing follows the actual tier reported by upstream.
1818+
// resolveBillingServiceTier 按"请求意图"决定计费 tier
1819+
// 只要客户端显式请求 fast/priority,就锁定为 priority 计费(×2),
1820+
// 不被上游降级值(如 default)掩盖——保证 fast 用户不会因为上游通道波动而漏费。
1821+
// 客户端没指定 tier 时,才回退到上游实际报告的 tier
18221822
func resolveBillingServiceTier(actualTier, requestedTier string) string {
1823-
actualTier = strings.ToLower(strings.TrimSpace(actualTier))
1824-
if actualTier != "" {
1825-
if actualTier == "priority" || actualTier == "fast" {
1826-
return "priority"
1827-
}
1828-
return actualTier
1823+
requestedTier = strings.ToLower(strings.TrimSpace(requestedTier))
1824+
if requestedTier == "priority" || requestedTier == "fast" {
1825+
return "priority"
18291826
}
18301827

1831-
requestedTier = strings.ToLower(strings.TrimSpace(requestedTier))
1832-
switch requestedTier {
1833-
case "priority", "fast":
1828+
actualTier = strings.ToLower(strings.TrimSpace(actualTier))
1829+
if actualTier == "priority" || actualTier == "fast" {
18341830
return "priority"
1835-
default:
1836-
return requestedTier
18371831
}
1832+
if actualTier != "" {
1833+
return actualTier
1834+
}
1835+
return requestedTier
18381836
}
18391837

18401838
// 上游不支持的 JSON Schema 验证约束关键字

proxy/translator_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,9 @@ func TestResolveBillingServiceTier(t *testing.T) {
6565
want string
6666
}{
6767
{name: "actual priority wins", actual: "priority", requested: "fast", want: "priority"},
68-
{name: "actual default downgrade wins", actual: "default", requested: "fast", want: "default"},
69-
{name: "unknown concrete actual tier wins", actual: "burst", requested: "fast", want: "burst"},
68+
{name: "requested fast bills priority even when upstream downgrades to default", actual: "default", requested: "fast", want: "priority"},
69+
{name: "requested fast bills priority even when upstream reports unknown tier", actual: "burst", requested: "fast", want: "priority"},
70+
{name: "upstream concrete tier wins when client did not request fast", actual: "burst", requested: "", want: "burst"},
7071
{name: "requested fast fallback bills priority", actual: "", requested: "fast", want: "priority"},
7172
{name: "requested priority fallback bills priority", actual: "", requested: "priority", want: "priority"},
7273
{name: "default stays default", actual: "default", requested: "", want: "default"},

0 commit comments

Comments
 (0)