Merge pull request #4755 from wucm667/fix/issue-4754-channel-pricing-model-normalization

fix(billing): normalize channel pricing model names
This commit is contained in:
Wesley Liddick
2026-07-23 11:18:10 +08:00
committed by GitHub
2 changed files with 32 additions and 2 deletions
+13 -2
View File
@@ -60,6 +60,16 @@ type channelModelKey struct {
model string // lowercase
}
// normalizeChannelPricingModelName makes Anthropic's dot and hyphen spelling
// differences equivalent in channel pricing cache keys.
func normalizeChannelPricingModelName(model string) string {
model = strings.ToLower(strings.TrimSpace(model))
if strings.HasPrefix(model, "claude-") {
model = strings.ReplaceAll(model, ".", "-")
}
return model
}
// channelGroupPlatformKey 通配符定价缓存键
type channelGroupPlatformKey struct {
groupID int64
@@ -217,13 +227,13 @@ func expandPricingToCache(cache *channelCache, ch *Channel, gid int64, platform
gpKey := channelGroupPlatformKey{groupID: gid, platform: pricingPlatform}
for _, model := range pricing.Models {
if strings.HasSuffix(model, "*") {
prefix := strings.ToLower(strings.TrimSuffix(model, "*"))
prefix := normalizeChannelPricingModelName(strings.TrimSuffix(model, "*"))
cache.wildcardByGroupPlatform[gpKey] = append(cache.wildcardByGroupPlatform[gpKey], &wildcardPricingEntry{
prefix: prefix,
pricing: pricing,
})
} else {
key := channelModelKey{groupID: gid, platform: pricingPlatform, model: strings.ToLower(model)}
key := channelModelKey{groupID: gid, platform: pricingPlatform, model: normalizeChannelPricingModelName(model)}
cache.pricingByGroupModel[key] = pricing
}
}
@@ -402,6 +412,7 @@ func (c *channelCache) matchWildcardMapping(groupID int64, platform, modelLower
// lookupPricingAcrossPlatforms 在分组平台内查找模型定价。
// 各平台严格独立,只在本平台内查找(先精确匹配,再通配符)。
func lookupPricingAcrossPlatforms(cache *channelCache, groupID int64, groupPlatform, modelLower string) *ChannelModelPricing {
modelLower = normalizeChannelPricingModelName(modelLower)
for _, p := range matchingPlatforms(groupPlatform) {
key := channelModelKey{groupID: groupID, platform: p, model: modelLower}
if pricing, ok := cache.pricingByGroupModel[key]; ok {
@@ -675,6 +675,25 @@ func TestGetChannelModelPricing_CaseInsensitive(t *testing.T) {
require.Equal(t, int64(100), result.ID)
}
func TestGetChannelModelPricing_NormalizesDotsAndHyphens(t *testing.T) {
ch := Channel{
ID: 1,
Status: StatusActive,
GroupIDs: []int64{10},
ModelPricing: []ChannelModelPricing{
{ID: 100, Platform: "anthropic", Models: []string{"claude-opus-4.8"}, BillingMode: BillingModePerRequest, PerRequestPrice: testPtrFloat64(0.007)},
},
}
repo := makeStandardRepo(ch, map[int64]string{10: "anthropic"})
svc := newTestChannelService(repo)
result := svc.GetChannelModelPricing(context.Background(), 10, "claude-opus-4-8")
require.NotNil(t, result)
require.Equal(t, int64(100), result.ID)
require.Equal(t, BillingModePerRequest, result.BillingMode)
require.InDelta(t, 0.007, *result.PerRequestPrice, 1e-12)
}
func TestGetChannelModelPricing_WildcardMatch(t *testing.T) {
ch := Channel{
ID: 1,