mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 13:08:03 +08:00
Merge pull request #5540 from luckydududu/fix/channel-pricing-model-name-normalization
fix(channel): 定价冲突检测与定价缓存键的归一化对齐(#4754 修复残留)
This commit is contained in:
@@ -962,19 +962,35 @@ func conflictsBetween(a, b modelEntry) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// toModelEntry 将模型名转换为 modelEntry
|
||||
// toModelEntry 将模型名转换为 modelEntry(用于模型映射的冲突检测)。
|
||||
// 归一化必须与 expandMappingToCache 写缓存键的方式一致:映射缓存只做 strings.ToLower。
|
||||
func toModelEntry(pattern string) modelEntry {
|
||||
prefix, isWild := splitWildcardSuffix(strings.ToLower(pattern))
|
||||
return modelEntry{pattern: pattern, prefix: prefix, wildcard: isWild}
|
||||
}
|
||||
|
||||
// toPricingModelEntry 将模型名转换为 modelEntry(用于模型定价的冲突检测)。
|
||||
//
|
||||
// 与 toModelEntry 的区别:定价缓存的键走 normalizeChannelPricingModelName
|
||||
// (额外做 TrimSpace,并把 claude-* 的 "." 换成 "-"),冲突检测必须用同一套归一化,
|
||||
// 否则两个校验时看着不同、写进缓存后键相同的定价会互相静默覆盖。
|
||||
func toPricingModelEntry(pattern string) modelEntry {
|
||||
// 先剥通配符再归一化,与 expandPricingToCache 的处理顺序保持一致
|
||||
prefix, isWild := splitWildcardSuffix(pattern)
|
||||
return modelEntry{
|
||||
pattern: pattern,
|
||||
prefix: normalizeChannelPricingModelName(prefix),
|
||||
wildcard: isWild,
|
||||
}
|
||||
}
|
||||
|
||||
// validateNoConflictingModels 检查定价列表中是否有冲突模型模式(同一平台下)。
|
||||
// 冲突包括:精确重复、通配符之间的前缀包含、通配符与精确名的前缀匹配。
|
||||
func validateNoConflictingModels(pricingList []ChannelModelPricing) error {
|
||||
byPlatform := make(map[string][]modelEntry)
|
||||
for _, p := range pricingList {
|
||||
for _, model := range p.Models {
|
||||
byPlatform[p.Platform] = append(byPlatform[p.Platform], toModelEntry(model))
|
||||
byPlatform[p.Platform] = append(byPlatform[p.Platform], toPricingModelEntry(model))
|
||||
}
|
||||
}
|
||||
for platform, entries := range byPlatform {
|
||||
|
||||
@@ -414,6 +414,44 @@ func TestValidateNoConflictingModels(t *testing.T) {
|
||||
wantErr: true,
|
||||
errContains: "conflict",
|
||||
},
|
||||
// 以下三例:冲突检测必须与 normalizeChannelPricingModelName 用同一套归一化,
|
||||
// 否则校验放行、写进缓存后键相同,后写的定价会静默覆盖前一条。
|
||||
{
|
||||
name: "claude_dot_and_hyphen_spelling_conflict",
|
||||
pricingList: []ChannelModelPricing{
|
||||
{Platform: "anthropic", Models: []string{"claude-sonnet-4.5"}},
|
||||
{Platform: "anthropic", Models: []string{"claude-sonnet-4-5"}},
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "conflict",
|
||||
},
|
||||
{
|
||||
name: "claude_dot_and_hyphen_spelling_conflict_wildcard",
|
||||
pricingList: []ChannelModelPricing{
|
||||
{Platform: "anthropic", Models: []string{"claude-sonnet-4.5*"}},
|
||||
{Platform: "anthropic", Models: []string{"claude-sonnet-4-5-x"}},
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "conflict",
|
||||
},
|
||||
{
|
||||
name: "surrounding_whitespace_conflict",
|
||||
pricingList: []ChannelModelPricing{
|
||||
{Platform: "openai", Models: []string{"gpt-5.6"}},
|
||||
{Platform: "openai", Models: []string{" gpt-5.6 "}},
|
||||
},
|
||||
wantErr: true,
|
||||
errContains: "conflict",
|
||||
},
|
||||
{
|
||||
// 只有 claude-* 前缀才做 "." → "-",别把其它平台也一起归一化了
|
||||
name: "non_claude_dot_spelling_is_not_normalized",
|
||||
pricingList: []ChannelModelPricing{
|
||||
{Platform: "openai", Models: []string{"gpt-5.6"}},
|
||||
{Platform: "openai", Models: []string{"gpt-5-6"}},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -469,6 +507,16 @@ func TestValidateNoConflictingMappings(t *testing.T) {
|
||||
wantErr: true,
|
||||
errContains: "conflict",
|
||||
},
|
||||
{
|
||||
// 映射缓存(expandMappingToCache)只做 strings.ToLower,不做定价那套
|
||||
// "." → "-",所以这两个源模式在缓存里是两个不同的键、并不冲突。
|
||||
// 这条用来卡住:定价侧的归一化修复不能顺手套到映射侧,否则会误报冲突。
|
||||
name: "mapping keeps dot and hyphen spelling separate",
|
||||
mapping: map[string]map[string]string{
|
||||
"anthropic": {"claude-sonnet-4.5": "a", "claude-sonnet-4-5": "b"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard vs exact conflict",
|
||||
mapping: map[string]map[string]string{
|
||||
|
||||
Reference in New Issue
Block a user