浏览代码

feat: claude web search tool 计费

creamlike1024 7 月之前
父节点
当前提交
961bc874d2
共有 4 个文件被更改,包括 38 次插入4 次删除
  1. 9 4
      dto/claude.go
  2. 4 0
      relay/channel/claude/relay-claude.go
  3. 16 0
      relay/relay-text.go
  4. 9 0
      setting/operation_setting/tools.go

+ 9 - 4
dto/claude.go

@@ -324,8 +324,13 @@ func (c *ClaudeResponse) GetIndex() int {
 }
 
 type ClaudeUsage struct {
-	InputTokens              int `json:"input_tokens"`
-	CacheCreationInputTokens int `json:"cache_creation_input_tokens"`
-	CacheReadInputTokens     int `json:"cache_read_input_tokens"`
-	OutputTokens             int `json:"output_tokens"`
+	InputTokens              int                  `json:"input_tokens"`
+	CacheCreationInputTokens int                  `json:"cache_creation_input_tokens"`
+	CacheReadInputTokens     int                  `json:"cache_read_input_tokens"`
+	OutputTokens             int                  `json:"output_tokens"`
+	ServerToolUse            *ClaudeServerToolUse `json:"server_tool_use"`
+}
+
+type ClaudeServerToolUse struct {
+	WebSearchRequests int `json:"web_search_requests"`
 }

+ 4 - 0
relay/channel/claude/relay-claude.go

@@ -745,6 +745,10 @@ func HandleClaudeResponseData(c *gin.Context, info *relaycommon.RelayInfo, claud
 		responseData = data
 	}
 
+	if claudeResponse.Usage.ServerToolUse != nil && claudeResponse.Usage.ServerToolUse.WebSearchRequests > 0 {
+		c.Set("claude_web_search_requests", claudeResponse.Usage.ServerToolUse.WebSearchRequests)
+	}
+
 	common.IOCopyBytesGracefully(c, nil, responseData)
 	return nil
 }

+ 16 - 0
relay/relay-text.go

@@ -382,6 +382,7 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
 	// openai web search 工具计费
 	var dWebSearchQuota decimal.Decimal
 	var webSearchPrice float64
+	// response api 格式工具计费
 	if relayInfo.ResponsesUsageInfo != nil {
 		if webSearchTool, exists := relayInfo.ResponsesUsageInfo.BuiltInTools[dto.BuildInToolWebSearchPreview]; exists && webSearchTool.CallCount > 0 {
 			// 计算 web search 调用的配额 (配额 = 价格 * 调用次数 / 1000 * 分组倍率)
@@ -404,6 +405,17 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
 		extraContent += fmt.Sprintf("Web Search 调用 1 次,上下文大小 %s,调用花费 %s",
 			searchContextSize, dWebSearchQuota.String())
 	}
+	// claude web search tool 计费
+	var dClaudeWebSearchQuota decimal.Decimal
+	var claudeWebSearchPrice float64
+	claudeWebSearchCallCount := ctx.GetInt("claude_web_search_requests")
+	if claudeWebSearchCallCount > 0 {
+		claudeWebSearchPrice = operation_setting.GetClaudeWebSearchPricePerThousand()
+		dClaudeWebSearchQuota = decimal.NewFromFloat(claudeWebSearchPrice).
+			Div(decimal.NewFromInt(1000)).Mul(dGroupRatio).Mul(dQuotaPerUnit).Mul(decimal.NewFromInt(int64(claudeWebSearchCallCount)))
+		extraContent += fmt.Sprintf("Claude Web Search 调用 %d 次,调用花费 %s",
+			claudeWebSearchCallCount, dClaudeWebSearchQuota.String())
+	}
 	// file search tool 计费
 	var dFileSearchQuota decimal.Decimal
 	var fileSearchPrice float64
@@ -527,6 +539,10 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
 			other["web_search_call_count"] = 1
 			other["web_search_price"] = webSearchPrice
 		}
+	} else if !dClaudeWebSearchQuota.IsZero() {
+		other["web_search"] = true
+		other["web_search_call_count"] = claudeWebSearchCallCount
+		other["web_search_price"] = claudeWebSearchPrice
 	}
 	if !dFileSearchQuota.IsZero() && relayInfo.ResponsesUsageInfo != nil {
 		if fileSearchTool, exists := relayInfo.ResponsesUsageInfo.BuiltInTools[dto.BuildInToolFileSearch]; exists {

+ 9 - 0
setting/operation_setting/tools.go

@@ -23,6 +23,15 @@ const (
 	Gemini20FlashInputAudioPrice            = 0.70
 )
 
+const (
+	// Claude Web search
+	ClaudeWebSearchPrice = 10.00
+)
+
+func GetClaudeWebSearchPricePerThousand() float64 {
+	return ClaudeWebSearchPrice
+}
+
 func GetWebSearchPricePerThousand(modelName string, contextSize string) float64 {
 	// 确定模型类型
 	// https://platform.openai.com/docs/pricing Web search 价格按模型类型和 search context size 收费