feat: 支持opus-4.7

This commit is contained in:
shaw
2026-04-17 09:37:25 +08:00
parent e6e73b4f52
commit a789c8c4c7
7 changed files with 99 additions and 45 deletions

View File

@@ -656,65 +656,95 @@ func (s *PricingService) extractBaseName(model string) string {
// matchByModelFamily 基于模型系列匹配
func (s *PricingService) matchByModelFamily(model string) *LiteLLMModelPricing {
// Claude模型系列匹配规则
familyPatterns := map[string][]string{
"opus-4.6": {"claude-opus-4.6", "claude-opus-4-6"},
"opus-4.5": {"claude-opus-4.5", "claude-opus-4-5"},
"opus-4": {"claude-opus-4", "claude-3-opus"},
"sonnet-4.5": {"claude-sonnet-4.5", "claude-sonnet-4-5"},
"sonnet-4": {"claude-sonnet-4", "claude-3-5-sonnet"},
"sonnet-3.5": {"claude-3-5-sonnet", "claude-3.5-sonnet"},
"sonnet-3": {"claude-3-sonnet"},
"haiku-3.5": {"claude-3-5-haiku", "claude-3.5-haiku"},
"haiku-3": {"claude-3-haiku"},
// modelFamily 定义一个模型系列匹配和定价查找规则
type modelFamily struct {
name string // 系列名称
match []string // 用于将模型归类到此系列的模式strings.Contains 匹配)
pricing []string // 用于在定价数据中查找价格的模式nil 则复用 match可包含低版本 fallback
}
// 确定模型属于哪个系列
var matchedFamily string
for family, patterns := range familyPatterns {
for _, pattern := range patterns {
// 按特异性降序排列:高版本号在前,避免 "claude-opus-4"opus-4 系列
// 因子串关系误匹配 "claude-opus-4-7"opus-4.7 系列)。
// 注意:原 map 实现存在 Go map 迭代随机性导致的同类 bug此处改为有序切片修复。
families := []modelFamily{
{name: "opus-4.7", match: []string{"claude-opus-4-7", "claude-opus-4.7"}, pricing: []string{"claude-opus-4-7", "claude-opus-4.7", "claude-opus-4-6"}},
{name: "opus-4.6", match: []string{"claude-opus-4-6", "claude-opus-4.6"}},
{name: "opus-4.5", match: []string{"claude-opus-4-5", "claude-opus-4.5"}},
{name: "opus-4", match: []string{"claude-opus-4", "claude-3-opus"}},
{name: "sonnet-4.5", match: []string{"claude-sonnet-4-5", "claude-sonnet-4.5"}},
{name: "sonnet-4", match: []string{"claude-sonnet-4", "claude-3-5-sonnet"}},
{name: "sonnet-3.5", match: []string{"claude-3-5-sonnet", "claude-3.5-sonnet"}},
{name: "sonnet-3", match: []string{"claude-3-sonnet"}},
{name: "haiku-3.5", match: []string{"claude-3-5-haiku", "claude-3.5-haiku"}},
{name: "haiku-3", match: []string{"claude-3-haiku"}},
}
// Phase 1: 按有序切片归类(最具体的系列优先匹配)
var matched *modelFamily
for i := range families {
for _, pattern := range families[i].match {
if strings.Contains(model, pattern) || strings.Contains(model, strings.ReplaceAll(pattern, "-", "")) {
matchedFamily = family
matched = &families[i]
break
}
}
if matchedFamily != "" {
if matched != nil {
break
}
}
if matchedFamily == "" {
// 简单的系列匹配
if strings.Contains(model, "opus") {
if strings.Contains(model, "4.5") || strings.Contains(model, "4-5") {
matchedFamily = "opus-4.5"
} else {
matchedFamily = "opus-4"
// Phase 2: 二次兜底——当模型 ID 不含已知模式串时,按关键字粗分
if matched == nil {
var fallbackName string
switch {
case strings.Contains(model, "opus"):
switch {
case strings.Contains(model, "4.7") || strings.Contains(model, "4-7"):
fallbackName = "opus-4.7"
case strings.Contains(model, "4.6") || strings.Contains(model, "4-6"):
fallbackName = "opus-4.6"
case strings.Contains(model, "4.5") || strings.Contains(model, "4-5"):
fallbackName = "opus-4.5"
default:
fallbackName = "opus-4"
}
} else if strings.Contains(model, "sonnet") {
if strings.Contains(model, "4.5") || strings.Contains(model, "4-5") {
matchedFamily = "sonnet-4.5"
} else if strings.Contains(model, "3-5") || strings.Contains(model, "3.5") {
matchedFamily = "sonnet-3.5"
} else {
matchedFamily = "sonnet-4"
case strings.Contains(model, "sonnet"):
switch {
case strings.Contains(model, "4.5") || strings.Contains(model, "4-5"):
fallbackName = "sonnet-4.5"
case strings.Contains(model, "3-5") || strings.Contains(model, "3.5"):
fallbackName = "sonnet-3.5"
default:
fallbackName = "sonnet-4"
}
} else if strings.Contains(model, "haiku") {
if strings.Contains(model, "3-5") || strings.Contains(model, "3.5") {
matchedFamily = "haiku-3.5"
} else {
matchedFamily = "haiku-3"
case strings.Contains(model, "haiku"):
switch {
case strings.Contains(model, "3-5") || strings.Contains(model, "3.5"):
fallbackName = "haiku-3.5"
default:
fallbackName = "haiku-3"
}
}
if fallbackName != "" {
for i := range families {
if families[i].name == fallbackName {
matched = &families[i]
break
}
}
}
}
if matchedFamily == "" {
if matched == nil {
return nil
}
// 在价格数据中查找该系列的模型
patterns := familyPatterns[matchedFamily]
for _, pattern := range patterns {
// Phase 3: 在定价数据中查找该系列的价格
lookups := matched.pricing
if lookups == nil {
lookups = matched.match
}
for _, pattern := range lookups {
for key, pricing := range s.pricingData {
keyLower := strings.ToLower(key)
if strings.Contains(keyLower, pattern) {