Upload main.go
Browse files
main.go
CHANGED
|
@@ -65,14 +65,7 @@ var modelRules = []ModelRule{
|
|
| 65 |
Keywords: []string{"deepseek"},
|
| 66 |
Target: "deepseek-v3",
|
| 67 |
},
|
| 68 |
-
|
| 69 |
-
Keywords: []string{"claude", "3", "sonnet"},
|
| 70 |
-
Target: "claude-3-sonnet-20240229",
|
| 71 |
-
},
|
| 72 |
-
{
|
| 73 |
-
Keywords: []string{"claude", "3", "7"},
|
| 74 |
-
Target: "claude-3-7-sonnet-latest",
|
| 75 |
-
},
|
| 76 |
{
|
| 77 |
Keywords: []string{"claude", "3", "5", "sonnet"},
|
| 78 |
Target: "claude-3-5-sonnet-latest",
|
|
@@ -81,6 +74,10 @@ var modelRules = []ModelRule{
|
|
| 81 |
Keywords: []string{"claude", "3", "5", "haiku"},
|
| 82 |
Target: "claude-3-5-haiku-latest",
|
| 83 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
{
|
| 85 |
Keywords: []string{"claude", "3", "opus"},
|
| 86 |
Target: "claude-3-opus-latest",
|
|
@@ -89,6 +86,11 @@ var modelRules = []ModelRule{
|
|
| 89 |
Keywords: []string{"claude", "3", "haiku"},
|
| 90 |
Target: "claude-3-haiku-20240307",
|
| 91 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
{
|
| 93 |
Keywords: []string{"gemini", "2.0"},
|
| 94 |
Target: "gemini-2.0-flash",
|
|
@@ -107,17 +109,43 @@ var modelRules = []ModelRule{
|
|
| 107 |
func matchModelName(input string) string {
|
| 108 |
// 转换为小写进行匹配
|
| 109 |
input = strings.ToLower(input)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
// 特殊规则:OpenAI GPT-4o
|
| 112 |
if (strings.Contains(input, "gpt") && strings.Contains(input, "4o")) ||
|
| 113 |
strings.Contains(input, "o1") ||
|
| 114 |
strings.Contains(input, "o3") {
|
|
|
|
| 115 |
return "gpt-4o"
|
| 116 |
}
|
| 117 |
|
| 118 |
// 特殊规则:OpenAI GPT-4
|
| 119 |
if (strings.Contains(input, "gpt") && strings.Contains(input, "3") && strings.Contains(input, "5")) ||
|
| 120 |
(strings.Contains(input, "gpt") && strings.Contains(input, "4") && !strings.Contains(input, "4o")) {
|
|
|
|
| 121 |
return "gpt-4"
|
| 122 |
}
|
| 123 |
|
|
@@ -131,11 +159,13 @@ func matchModelName(input string) string {
|
|
| 131 |
}
|
| 132 |
}
|
| 133 |
if matches {
|
|
|
|
| 134 |
return rule.Target
|
| 135 |
}
|
| 136 |
}
|
| 137 |
|
| 138 |
// 如果没有匹配到,返回原始输入
|
|
|
|
| 139 |
return input
|
| 140 |
}
|
| 141 |
|
|
@@ -196,6 +226,16 @@ func countTokensWithClaude(req TokenCountRequest) (TokenCountResponse, error) {
|
|
| 196 |
// 准备请求Anthropic API
|
| 197 |
log.Printf("开始Claude API请求: 模型=%s, 消息数量=%d", req.Model, len(req.Messages))
|
| 198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
client := &http.Client{}
|
| 200 |
data, err := json.Marshal(req)
|
| 201 |
if err != nil {
|
|
@@ -230,9 +270,19 @@ func countTokensWithClaude(req TokenCountRequest) (TokenCountResponse, error) {
|
|
| 230 |
errorBody, _ = io.ReadAll(response.Body)
|
| 231 |
log.Printf("错误: Claude API返回非200状态码: %d, 响应体: %s", response.StatusCode, string(errorBody))
|
| 232 |
|
| 233 |
-
|
| 234 |
-
|
|
|
|
|
|
|
| 235 |
return TokenCountResponse{}, fmt.Errorf("Claude API验证失败,请检查API Key是否有效: %s", string(errorBody))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
}
|
| 237 |
|
| 238 |
return TokenCountResponse{}, fmt.Errorf("Claude API返回错误状态码: %d, 响应: %s", response.StatusCode, string(errorBody))
|
|
@@ -529,8 +579,34 @@ func countTokens(c *gin.Context) {
|
|
| 529 |
|
| 530 |
if err != nil {
|
| 531 |
log.Printf("计算token失败: %v", err)
|
| 532 |
-
|
| 533 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
}
|
| 535 |
|
| 536 |
// 返回结果
|
|
|
|
| 65 |
Keywords: []string{"deepseek"},
|
| 66 |
Target: "deepseek-v3",
|
| 67 |
},
|
| 68 |
+
// 先放更具体的规则
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
{
|
| 70 |
Keywords: []string{"claude", "3", "5", "sonnet"},
|
| 71 |
Target: "claude-3-5-sonnet-latest",
|
|
|
|
| 74 |
Keywords: []string{"claude", "3", "5", "haiku"},
|
| 75 |
Target: "claude-3-5-haiku-latest",
|
| 76 |
},
|
| 77 |
+
{
|
| 78 |
+
Keywords: []string{"claude", "3", "7"},
|
| 79 |
+
Target: "claude-3-7-sonnet-latest",
|
| 80 |
+
},
|
| 81 |
{
|
| 82 |
Keywords: []string{"claude", "3", "opus"},
|
| 83 |
Target: "claude-3-opus-latest",
|
|
|
|
| 86 |
Keywords: []string{"claude", "3", "haiku"},
|
| 87 |
Target: "claude-3-haiku-20240307",
|
| 88 |
},
|
| 89 |
+
// 再放一般规则
|
| 90 |
+
{
|
| 91 |
+
Keywords: []string{"claude", "3", "sonnet"},
|
| 92 |
+
Target: "claude-3-sonnet-20240229",
|
| 93 |
+
},
|
| 94 |
{
|
| 95 |
Keywords: []string{"gemini", "2.0"},
|
| 96 |
Target: "gemini-2.0-flash",
|
|
|
|
| 109 |
func matchModelName(input string) string {
|
| 110 |
// 转换为小写进行匹配
|
| 111 |
input = strings.ToLower(input)
|
| 112 |
+
log.Printf("正在匹配模型名称: %s", input)
|
| 113 |
+
|
| 114 |
+
// 特殊处理 Claude 3.5 系列
|
| 115 |
+
if strings.Contains(input, "claude") && strings.Contains(input, "3.5") ||
|
| 116 |
+
strings.Contains(input, "claude") && strings.Contains(input, "3") && strings.Contains(input, "5") {
|
| 117 |
+
if strings.Contains(input, "sonnet") {
|
| 118 |
+
log.Printf("匹配到Claude 3.5 Sonnet")
|
| 119 |
+
return "claude-3-5-sonnet-latest"
|
| 120 |
+
} else if strings.Contains(input, "haiku") {
|
| 121 |
+
log.Printf("匹配到Claude 3.5 Haiku")
|
| 122 |
+
return "claude-3-5-haiku-latest"
|
| 123 |
+
} else {
|
| 124 |
+
// 默认为Sonnet
|
| 125 |
+
log.Printf("匹配到Claude 3.5 (默认使用Sonnet)")
|
| 126 |
+
return "claude-3-5-sonnet-latest"
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
// 特殊处理 Claude 3.7 系列
|
| 131 |
+
if strings.Contains(input, "claude") && strings.Contains(input, "3.7") ||
|
| 132 |
+
strings.Contains(input, "claude") && strings.Contains(input, "3") && strings.Contains(input, "7") {
|
| 133 |
+
log.Printf("匹配到Claude 3.7")
|
| 134 |
+
return "claude-3-7-sonnet-latest"
|
| 135 |
+
}
|
| 136 |
|
| 137 |
// 特殊规则:OpenAI GPT-4o
|
| 138 |
if (strings.Contains(input, "gpt") && strings.Contains(input, "4o")) ||
|
| 139 |
strings.Contains(input, "o1") ||
|
| 140 |
strings.Contains(input, "o3") {
|
| 141 |
+
log.Printf("匹配到GPT-4o")
|
| 142 |
return "gpt-4o"
|
| 143 |
}
|
| 144 |
|
| 145 |
// 特殊规则:OpenAI GPT-4
|
| 146 |
if (strings.Contains(input, "gpt") && strings.Contains(input, "3") && strings.Contains(input, "5")) ||
|
| 147 |
(strings.Contains(input, "gpt") && strings.Contains(input, "4") && !strings.Contains(input, "4o")) {
|
| 148 |
+
log.Printf("匹配到GPT-4")
|
| 149 |
return "gpt-4"
|
| 150 |
}
|
| 151 |
|
|
|
|
| 159 |
}
|
| 160 |
}
|
| 161 |
if matches {
|
| 162 |
+
log.Printf("通过规则匹配到: %s", rule.Target)
|
| 163 |
return rule.Target
|
| 164 |
}
|
| 165 |
}
|
| 166 |
|
| 167 |
// 如果没有匹配到,返回原始输入
|
| 168 |
+
log.Printf("没有匹配到任何规则,使用原始输入: %s", input)
|
| 169 |
return input
|
| 170 |
}
|
| 171 |
|
|
|
|
| 226 |
// 准备请求Anthropic API
|
| 227 |
log.Printf("开始Claude API请求: 模型=%s, 消息数量=%d", req.Model, len(req.Messages))
|
| 228 |
|
| 229 |
+
// 验证消息格式
|
| 230 |
+
for i, msg := range req.Messages {
|
| 231 |
+
if msg.Content == "" {
|
| 232 |
+
log.Printf("警告: 消息 #%d 内容为空,可能导致请求失败", i)
|
| 233 |
+
}
|
| 234 |
+
if msg.Role != "user" && msg.Role != "assistant" {
|
| 235 |
+
log.Printf("警告: 消息 #%d 角色'%s'不是标准角色(user/assistant),可能导致请求失败", i, msg.Role)
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
client := &http.Client{}
|
| 240 |
data, err := json.Marshal(req)
|
| 241 |
if err != nil {
|
|
|
|
| 270 |
errorBody, _ = io.ReadAll(response.Body)
|
| 271 |
log.Printf("错误: Claude API返回非200状态码: %d, 响应体: %s", response.StatusCode, string(errorBody))
|
| 272 |
|
| 273 |
+
// 检查常见错误
|
| 274 |
+
errorStr := string(errorBody)
|
| 275 |
+
if response.StatusCode == http.StatusUnauthorized || strings.Contains(errorStr, "invalid_api_key") {
|
| 276 |
+
log.Printf("错误: Claude API密钥无效或过期")
|
| 277 |
return TokenCountResponse{}, fmt.Errorf("Claude API验证失败,请检查API Key是否有效: %s", string(errorBody))
|
| 278 |
+
} else if response.StatusCode == http.StatusBadRequest {
|
| 279 |
+
if strings.Contains(errorStr, "empty content") {
|
| 280 |
+
log.Printf("错误: 请求包含空内容的消息")
|
| 281 |
+
return TokenCountResponse{}, fmt.Errorf("请求格式错误: 消息不能有空内容: %s", string(errorBody))
|
| 282 |
+
} else if strings.Contains(errorStr, "invalid_request_error") {
|
| 283 |
+
log.Printf("错误: 无效的请求格式")
|
| 284 |
+
return TokenCountResponse{}, fmt.Errorf("无效的请求格式: %s", string(errorBody))
|
| 285 |
+
}
|
| 286 |
}
|
| 287 |
|
| 288 |
return TokenCountResponse{}, fmt.Errorf("Claude API返回错误状态码: %d, 响应: %s", response.StatusCode, string(errorBody))
|
|
|
|
| 579 |
|
| 580 |
if err != nil {
|
| 581 |
log.Printf("计算token失败: %v", err)
|
| 582 |
+
|
| 583 |
+
// 对所有API调用失败的情况尝试使用GPT-4o估算
|
| 584 |
+
log.Printf("API调用失败,尝试使用GPT-4o估算: 原始模型=%s, 错误=%v", req.Model, err)
|
| 585 |
+
|
| 586 |
+
// 创建新的请求,使用GPT-4o
|
| 587 |
+
gptReq := req
|
| 588 |
+
gptReq.Model = "gpt-4o"
|
| 589 |
+
|
| 590 |
+
// 使用OpenAI API进行估算
|
| 591 |
+
estimatedResult, estimateErr := countTokensWithOpenAI(gptReq)
|
| 592 |
+
|
| 593 |
+
if estimateErr == nil {
|
| 594 |
+
log.Printf("使用GPT-4o估算成功: 模型=%s, 估算tokens=%d", originalModel, estimatedResult.InputTokens)
|
| 595 |
+
|
| 596 |
+
// 返回估算值,但添加警告信息和原始错误
|
| 597 |
+
c.JSON(http.StatusOK, gin.H{
|
| 598 |
+
"input_tokens": estimatedResult.InputTokens,
|
| 599 |
+
"warning": fmt.Sprintf("Token calculation for model '%s' failed. This is an estimation based on gpt-4o and may not be accurate.", originalModel),
|
| 600 |
+
"estimated_with": "gpt-4o",
|
| 601 |
+
"original_error": err.Error(),
|
| 602 |
+
})
|
| 603 |
+
return
|
| 604 |
+
} else {
|
| 605 |
+
log.Printf("使用GPT-4o估算也失败: %v", estimateErr)
|
| 606 |
+
// 如果GPT-4o估算也失败,返回原始错误
|
| 607 |
+
c.JSON(http.StatusInternalServerError, ErrorResponse{Error: err.Error()})
|
| 608 |
+
return
|
| 609 |
+
}
|
| 610 |
}
|
| 611 |
|
| 612 |
// 返回结果
|