From 961bc874d2c4c20fa9e2bb10874733e9be869ae0 Mon Sep 17 00:00:00 2001 From: creamlike1024 Date: Tue, 15 Jul 2025 18:57:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20claude=20web=20search=20tool=20?= =?UTF-8?q?=E8=AE=A1=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dto/claude.go | 13 +++++++++---- relay/channel/claude/relay-claude.go | 4 ++++ relay/relay-text.go | 16 ++++++++++++++++ setting/operation_setting/tools.go | 9 +++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/dto/claude.go b/dto/claude.go index 3dde9b95..1a325120 100644 --- a/dto/claude.go +++ b/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"` } diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index a6b92389..2dc58340 100644 --- a/relay/channel/claude/relay-claude.go +++ b/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 } diff --git a/relay/relay-text.go b/relay/relay-text.go index e0c8f047..daff38f0 100644 --- a/relay/relay-text.go +++ b/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 { diff --git a/setting/operation_setting/tools.go b/setting/operation_setting/tools.go index a401b923..a59090ce 100644 --- a/setting/operation_setting/tools.go +++ b/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 收费