mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-29 16:20:27 +00:00
fix: preserve plaza 1h pricing and align interval fallbacks
This commit is contained in:
@@ -19,13 +19,14 @@ const ContextPricingBasisWholeRequest ContextPricingBasis = "whole_request"
|
||||
// ContextPricingTier (MinTokens, MaxTokens] 区间内的有效 per-token 单价(USD)。
|
||||
// nil 表示该项无价/不计费;MaxTokens 为 nil 表示无上限。
|
||||
type ContextPricingTier struct {
|
||||
MinTokens int
|
||||
MaxTokens *int
|
||||
Label string
|
||||
Input *float64
|
||||
Output *float64
|
||||
CacheWrite *float64
|
||||
CacheRead *float64
|
||||
MinTokens int
|
||||
MaxTokens *int
|
||||
Label string
|
||||
Input *float64
|
||||
Output *float64
|
||||
CacheWrite *float64
|
||||
CacheWrite1h *float64
|
||||
CacheRead *float64
|
||||
}
|
||||
|
||||
// TimePricingPeriod 分时倍率时段:配置时区当天 [StartTime, EndTime) 内整单费用乘 Multiplier。
|
||||
@@ -289,6 +290,10 @@ func probeContextTier(seg contextSegment, resolved *ResolvedPricing, probe func(
|
||||
return tier, err
|
||||
}
|
||||
// 输出价只随上下文所在档变化:固定上下文 c,对输出 token 数做差商(固定部分相减抵消)。
|
||||
tier.CacheWrite1h, err = probeComponentPrice(func(n int) UsageTokens { return UsageTokens{CacheCreationTokens: n, CacheCreation1hTokens: n} }, c, delta, probe)
|
||||
if err != nil {
|
||||
return tier, err
|
||||
}
|
||||
tier.Output, err = probeComponentPrice(func(n int) UsageTokens { return UsageTokens{InputTokens: c, OutputTokens: n} }, 0, contextProbeDelta, probe)
|
||||
if err != nil {
|
||||
return tier, err
|
||||
@@ -298,6 +303,7 @@ func probeContextTier(seg contextSegment, resolved *ResolvedPricing, probe func(
|
||||
tier.Input = contextPricePtr(tier.Input, explicit.input)
|
||||
tier.Output = contextPricePtr(tier.Output, explicit.output)
|
||||
tier.CacheWrite = contextPricePtr(tier.CacheWrite, explicit.cacheWrite)
|
||||
tier.CacheWrite1h = contextPricePtr(tier.CacheWrite1h, explicit.cacheWrite1h)
|
||||
tier.CacheRead = contextPricePtr(tier.CacheRead, explicit.cacheRead)
|
||||
return tier, nil
|
||||
}
|
||||
@@ -341,7 +347,7 @@ func roundContextPrice(v float64) float64 {
|
||||
}
|
||||
|
||||
type explicitContextFields struct {
|
||||
input, output, cacheWrite, cacheRead bool
|
||||
input, output, cacheWrite, cacheWrite1h, cacheRead bool
|
||||
}
|
||||
|
||||
// explicitContextPricingFields 判断各项是否被分组卡/渠道定价(含命中区间)显式配置。
|
||||
@@ -355,11 +361,13 @@ func explicitContextPricingFields(resolved *ResolvedPricing, contextTokens int)
|
||||
out.input = cp.InputPrice != nil
|
||||
out.output = cp.OutputPrice != nil
|
||||
out.cacheWrite = cp.CacheWritePrice != nil
|
||||
out.cacheWrite1h = cp.CacheWrite1hPrice != nil || cp.CacheWritePrice != nil
|
||||
out.cacheRead = cp.CacheReadPrice != nil
|
||||
if iv := FindMatchingInterval(resolved.Intervals, contextTokens); iv != nil {
|
||||
out.input = out.input || iv.InputPrice != nil
|
||||
out.output = out.output || iv.OutputPrice != nil
|
||||
out.cacheWrite = out.cacheWrite || iv.CacheWritePrice != nil
|
||||
out.cacheWrite1h = out.cacheWrite1h || iv.CacheWrite1hPrice != nil || iv.CacheWritePrice != nil
|
||||
out.cacheRead = out.cacheRead || iv.CacheReadPrice != nil
|
||||
}
|
||||
return out
|
||||
@@ -393,7 +401,7 @@ func mergeEqualContextTiers(tiers []ContextPricingTier) []ContextPricingTier {
|
||||
|
||||
func sameContextPrices(a, b ContextPricingTier) bool {
|
||||
return samePricePtr(a.Input, b.Input) && samePricePtr(a.Output, b.Output) &&
|
||||
samePricePtr(a.CacheWrite, b.CacheWrite) && samePricePtr(a.CacheRead, b.CacheRead)
|
||||
samePricePtr(a.CacheWrite, b.CacheWrite) && samePricePtr(a.CacheWrite1h, b.CacheWrite1h) && samePricePtr(a.CacheRead, b.CacheRead)
|
||||
}
|
||||
|
||||
func samePricePtr(a, b *float64) bool {
|
||||
|
||||
@@ -385,6 +385,24 @@ func TestResolveContextPricingSchedule_NilResolver(t *testing.T) {
|
||||
require.Nil(t, sched)
|
||||
}
|
||||
|
||||
func TestPlazaSchedulePreservesCacheWrite1h(t *testing.T) {
|
||||
cp := sonnetChannel(PricingInterval{MinTokens: 272000, CacheWriteMultiplier: testPtrFloat64(2)})
|
||||
cp[0].CacheWritePrice = testPtrFloat64(12.5e-6)
|
||||
cp[0].CacheWrite1hPrice = testPtrFloat64(20e-6)
|
||||
group := enabledGroup(PlatformAnthropic)
|
||||
bs, resolver := newScheduleTestEnv(t, scheduleScenario{channel: cp, groupPlatform: PlatformAnthropic})
|
||||
sched, err := bs.ResolveContextPricingSchedule(context.Background(), resolver, ContextPricingScheduleInput{Model: "claude-sonnet-4", Group: group, Platform: PlatformAnthropic})
|
||||
require.NoError(t, err)
|
||||
display := plazaPricingFromSchedule(&cp[0], sched)
|
||||
requirePrice(t, testPtrFloat64(20e-6), display.CacheWrite1hPrice, "base 1h")
|
||||
require.Len(t, display.Intervals, 2)
|
||||
requirePrice(t, testPtrFloat64(25e-6), display.Intervals[1].CacheWritePrice, "tier 5m")
|
||||
requirePrice(t, testPtrFloat64(40e-6), display.Intervals[1].CacheWrite1hPrice, "tier 1h")
|
||||
a := ContextPricingTier{CacheWrite1h: testPtrFloat64(1)}
|
||||
b := ContextPricingTier{CacheWrite1h: testPtrFloat64(2)}
|
||||
require.Len(t, mergeEqualContextTiers([]ContextPricingTier{a, b}), 2)
|
||||
}
|
||||
|
||||
// --- 对账:阶梯表推算的费用必须等于真实计费函数 ---
|
||||
|
||||
type tokenKind int
|
||||
|
||||
@@ -269,6 +269,7 @@ func plazaPricingFromSchedule(raw *ChannelModelPricing, sched *ContextPricingSch
|
||||
out.InputPrice = first.Input
|
||||
out.OutputPrice = first.Output
|
||||
out.CacheWritePrice = first.CacheWrite
|
||||
out.CacheWrite1hPrice = first.CacheWrite1h
|
||||
out.CacheReadPrice = first.CacheRead
|
||||
if len(sched.Tiers) > 1 {
|
||||
out.Intervals = plazaIntervalsFromTiers(sched.Tiers)
|
||||
@@ -280,14 +281,15 @@ func plazaIntervalsFromTiers(tiers []ContextPricingTier) []PricingInterval {
|
||||
intervals := make([]PricingInterval, 0, len(tiers))
|
||||
for i, t := range tiers {
|
||||
intervals = append(intervals, PricingInterval{
|
||||
MinTokens: t.MinTokens,
|
||||
MaxTokens: t.MaxTokens,
|
||||
TierLabel: t.Label,
|
||||
InputPrice: t.Input,
|
||||
OutputPrice: t.Output,
|
||||
CacheWritePrice: t.CacheWrite,
|
||||
CacheReadPrice: t.CacheRead,
|
||||
SortOrder: i,
|
||||
MinTokens: t.MinTokens,
|
||||
MaxTokens: t.MaxTokens,
|
||||
TierLabel: t.Label,
|
||||
InputPrice: t.Input,
|
||||
OutputPrice: t.Output,
|
||||
CacheWritePrice: t.CacheWrite,
|
||||
CacheWrite1hPrice: t.CacheWrite1h,
|
||||
CacheReadPrice: t.CacheRead,
|
||||
SortOrder: i,
|
||||
})
|
||||
}
|
||||
return intervals
|
||||
|
||||
Reference in New Issue
Block a user