Files
Sakurasan ca4dc4b3b7 fix: 缓存 token 计费按上游协议区分口径
- 新增 ComputeCost:OpenAI 系 prompt 含缓存读需扣减(异常数据钳制为 0);
  Anthropic input_tokens 不含缓存,按原值计费,缓存写按输入价 ×1.25(原实现误用输出价)
- recordUsage 传入上游协议(用量语义跟随解析它的上游响应,而非客户端协议)
- 补充单元测试覆盖两种协议口径与边界情况
2026-09-02 02:36:28 +08:00

76 lines
2.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package proxy
import "testing"
func TestComputeCost(t *testing.T) {
// 标准三价:输入 0.5 / 输出 1.5 / 缓存读 0.05($/M)
const (
inPrice = 0.5
outPrice = 1.5
cchPrice = 0.05
)
tests := []struct {
name string
upstreamProto string
input int
output int
cacheRead int
cacheCreation int
inP float64
outP float64
cchP float64
want float64
}{
{
// OpenAI:prompt 含缓存读,需扣减:(11000−10000)×0.5 + 10000×0.05 + 500×1.5
name: "openai prompt includes cache read",
upstreamProto: "chat",
input: 11000, output: 500, cacheRead: 10000,
inP: inPrice, outP: outPrice, cchP: cchPrice,
want: (1000*inPrice + 10000*cchPrice + 500*outPrice) / 1e6,
},
{
// Anthropic:input 不含缓存;缓存写按输入价 ×1.25
name: "anthropic cache write at 1.25x input price",
upstreamProto: "messages",
input: 1000, output: 500, cacheRead: 10000, cacheCreation: 2000,
inP: inPrice, outP: outPrice, cchP: cchPrice,
want: (1000*inPrice + 10000*cchPrice + 2000*inPrice*1.25 + 500*outPrice) / 1e6,
},
{
// Anthropic 口径不得扣减缓存读(否则这里非缓存输入会算成负数)
name: "anthropic does not subtract cache read",
upstreamProto: "messages",
input: 100, output: 0, cacheRead: 1000,
inP: inPrice, outP: outPrice, cchP: cchPrice,
want: (100*inPrice + 1000*cchPrice) / 1e6,
},
{
// OpenAI 异常数据:cached > prompt 时非缓存输入钳制为 0,不出现负费用
name: "openai clamps negative uncached input",
upstreamProto: "responses",
input: 100, output: 0, cacheRead: 5000,
inP: inPrice, outP: outPrice, cchP: cchPrice,
want: (5000 * cchPrice) / 1e6,
},
{
// 未配置价格时费用为 0
name: "no prices no cost",
upstreamProto: "messages",
input: 1000, output: 1000, cacheRead: 1000, cacheCreation: 1000,
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ComputeCost(tt.upstreamProto, tt.input, tt.output, tt.cacheRead, tt.cacheCreation, tt.inP, tt.outP, tt.cchP)
if diff := got - tt.want; diff > 1e-12 || diff < -1e-12 {
t.Fatalf("ComputeCost(%q, in=%d, out=%d, cr=%d, cw=%d) = %v, want %v",
tt.upstreamProto, tt.input, tt.output, tt.cacheRead, tt.cacheCreation, got, tt.want)
}
})
}
}