feat(service): 扩展 CRS 同步和定价服务支持 Gemini
- CRS 同步服务新增 Gemini 账号同步逻辑(+273行) - 定价服务扩展 Gemini 模型定价计算(+99行) - 更新 Token 刷新服务集成 Gemini - 更新相关单元测试
This commit is contained in:
@@ -393,27 +393,32 @@ func (s *PricingService) GetModelPricing(modelName string) *LiteLLMModelPricing
|
||||
return nil
|
||||
}
|
||||
|
||||
// 标准化模型名称
|
||||
modelLower := strings.ToLower(modelName)
|
||||
// 标准化模型名称(同时兼容 "models/xxx"、VertexAI 资源名等前缀)
|
||||
modelLower := strings.ToLower(strings.TrimSpace(modelName))
|
||||
lookupCandidates := s.buildModelLookupCandidates(modelLower)
|
||||
|
||||
// 1. 精确匹配
|
||||
if pricing, ok := s.pricingData[modelLower]; ok {
|
||||
return pricing
|
||||
}
|
||||
if pricing, ok := s.pricingData[modelName]; ok {
|
||||
return pricing
|
||||
for _, candidate := range lookupCandidates {
|
||||
if candidate == "" {
|
||||
continue
|
||||
}
|
||||
if pricing, ok := s.pricingData[candidate]; ok {
|
||||
return pricing
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理常见的模型名称变体
|
||||
// claude-opus-4-5-20251101 -> claude-opus-4.5-20251101
|
||||
normalized := strings.ReplaceAll(modelLower, "-4-5-", "-4.5-")
|
||||
if pricing, ok := s.pricingData[normalized]; ok {
|
||||
return pricing
|
||||
for _, candidate := range lookupCandidates {
|
||||
normalized := strings.ReplaceAll(candidate, "-4-5-", "-4.5-")
|
||||
if pricing, ok := s.pricingData[normalized]; ok {
|
||||
return pricing
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 尝试模糊匹配(去掉版本号后缀)
|
||||
// claude-opus-4-5-20251101 -> claude-opus-4.5
|
||||
baseName := s.extractBaseName(modelLower)
|
||||
baseName := s.extractBaseName(lookupCandidates[0])
|
||||
for key, pricing := range s.pricingData {
|
||||
keyBase := s.extractBaseName(strings.ToLower(key))
|
||||
if keyBase == baseName {
|
||||
@@ -422,18 +427,84 @@ func (s *PricingService) GetModelPricing(modelName string) *LiteLLMModelPricing
|
||||
}
|
||||
|
||||
// 4. 基于模型系列匹配(Claude)
|
||||
if pricing := s.matchByModelFamily(modelLower); pricing != nil {
|
||||
if pricing := s.matchByModelFamily(lookupCandidates[0]); pricing != nil {
|
||||
return pricing
|
||||
}
|
||||
|
||||
// 5. OpenAI 模型回退策略
|
||||
if strings.HasPrefix(modelLower, "gpt-") {
|
||||
return s.matchOpenAIModel(modelLower)
|
||||
if strings.HasPrefix(lookupCandidates[0], "gpt-") {
|
||||
return s.matchOpenAIModel(lookupCandidates[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PricingService) buildModelLookupCandidates(modelLower string) []string {
|
||||
// Prefer canonical model name first (this also improves billing compatibility with "models/xxx").
|
||||
candidates := []string{
|
||||
normalizeModelNameForPricing(modelLower),
|
||||
modelLower,
|
||||
}
|
||||
for _, cand := range []string{
|
||||
strings.TrimPrefix(modelLower, "models/"),
|
||||
lastSegment(modelLower),
|
||||
lastSegment(strings.TrimPrefix(modelLower, "models/")),
|
||||
} {
|
||||
candidates = append(candidates, cand)
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(candidates))
|
||||
out := make([]string, 0, len(candidates))
|
||||
for _, c := range candidates {
|
||||
c = strings.TrimSpace(c)
|
||||
if c == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[c]; ok {
|
||||
continue
|
||||
}
|
||||
seen[c] = struct{}{}
|
||||
out = append(out, c)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return []string{modelLower}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func normalizeModelNameForPricing(model string) string {
|
||||
// Common Gemini/VertexAI forms:
|
||||
// - models/gemini-2.0-flash-exp
|
||||
// - publishers/google/models/gemini-1.5-pro
|
||||
// - projects/.../locations/.../publishers/google/models/gemini-1.5-pro
|
||||
model = strings.TrimSpace(model)
|
||||
model = strings.TrimLeft(model, "/")
|
||||
|
||||
if strings.HasPrefix(model, "models/") {
|
||||
model = strings.TrimPrefix(model, "models/")
|
||||
}
|
||||
if strings.HasPrefix(model, "publishers/google/models/") {
|
||||
model = strings.TrimPrefix(model, "publishers/google/models/")
|
||||
}
|
||||
|
||||
if idx := strings.LastIndex(model, "/publishers/google/models/"); idx != -1 {
|
||||
model = model[idx+len("/publishers/google/models/"):]
|
||||
}
|
||||
if idx := strings.LastIndex(model, "/models/"); idx != -1 {
|
||||
model = model[idx+len("/models/"):]
|
||||
}
|
||||
|
||||
model = strings.TrimLeft(model, "/")
|
||||
return model
|
||||
}
|
||||
|
||||
func lastSegment(model string) string {
|
||||
if idx := strings.LastIndex(model, "/"); idx != -1 {
|
||||
return model[idx+1:]
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// extractBaseName 提取基础模型名称(去掉日期版本号)
|
||||
func (s *PricingService) extractBaseName(model string) string {
|
||||
// 移除日期后缀 (如 -20251101, -20241022)
|
||||
|
||||
Reference in New Issue
Block a user