fix: 缓存 token 计费按上游协议区分口径

- 新增 ComputeCost:OpenAI 系 prompt 含缓存读需扣减(异常数据钳制为 0);
  Anthropic input_tokens 不含缓存,按原值计费,缓存写按输入价 ×1.25(原实现误用输出价)
- recordUsage 传入上游协议(用量语义跟随解析它的上游响应,而非客户端协议)
- 补充单元测试覆盖两种协议口径与边界情况
This commit is contained in:
Sakurasan
2026-09-02 02:36:28 +08:00
parent 0628d5050f
commit ca4dc4b3b7
3 changed files with 116 additions and 12 deletions
+29
View File
@@ -0,0 +1,29 @@
package proxy
import (
"opencatd-open/internal/proxy/convert"
)
// cacheWriteInputMultiplier 缓存写(cache creation)相对输入价的倍数。
// Anthropic 官方口径:缓存写按基础输入价的 1.25 倍计费(5m TTL);OpenAI 系无缓存写概念。
const cacheWriteInputMultiplier = 1.25
// ComputeCost 按上游协议的 token 语义计算一次请求的费用(USD)。
// 价格均为每百万 token 的 USD 单价。tok 的 token 语义由解析它的上游协议决定:
// - chat / responses(OpenAI 系):prompt_tokens 包含缓存读,
// 非缓存输入 = input − cacheRead;该协议没有缓存写,cacheCreation 恒为 0。
// - messages(Anthropic):input_tokens 不含缓存读/写(三个字段相互独立),
// 非缓存输入 = input 原值,不得再扣减;缓存写按输入价 ×1.25。
func ComputeCost(upstreamProto string, input, output, cacheRead, cacheCreation int, inputPrice, outputPrice, cacheReadPrice float64) float64 {
uncached := input
if upstreamProto != convert.ProtoMessages {
uncached -= cacheRead
if uncached < 0 {
uncached = 0
}
}
return (float64(uncached)*inputPrice +
float64(cacheRead)*cacheReadPrice +
float64(cacheCreation)*inputPrice*cacheWriteInputMultiplier +
float64(output)*outputPrice) / 1e6
}