Files

124 lines
3.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package shared
import (
"encoding/json"
"github.com/qianfree/team-api/relaykit/dto"
)
// MapOpenAIToolsToClaudeTools 将 OpenAI Tool[] 转换为 Claude ClaudeTool[]。
func MapOpenAIToolsToClaudeTools(tools []dto.Tool) []dto.ClaudeTool {
claudeTools := make([]dto.ClaudeTool, 0, len(tools))
for _, tool := range tools {
if tool.Type != "function" {
continue
}
claudeTool := dto.ClaudeTool{
Name: tool.Function.Name,
Description: tool.Function.Description,
}
// 转换参数(JSON Schema)。Claude 要求 custom tool 必须携带 input_schema,
// 而 OpenAI 允许无参函数省略 parameters——缺省时补一个空对象 schema,
// 否则 InputSchema 为 nil 被 omitempty 抹掉,上游直接 400。
if tool.Function.Parameters != nil {
claudeTool.InputSchema = tool.Function.Parameters
} else {
claudeTool.InputSchema = map[string]any{
"type": "object",
"properties": map[string]any{},
}
}
claudeTools = append(claudeTools, claudeTool)
}
return claudeTools
}
// MapClaudeToolsToOpenAITools 将 Claude ClaudeTool[] 转换为 OpenAI Tool[]。
// 服务端内置工具(type 非空且非 custom,如 web_search_*)在 chat 协议无对应物,
// 跳过而非伪装成自定义函数。
func MapClaudeToolsToOpenAITools(tools []dto.ClaudeTool) []dto.Tool {
openaiTools := make([]dto.Tool, 0, len(tools))
for _, tool := range tools {
if tool.Type != "" && tool.Type != "custom" {
continue
}
openaiTool := dto.Tool{
Type: "function",
Function: dto.FunctionDef{
Name: tool.Name,
Description: tool.Description,
Parameters: tool.InputSchema,
},
}
openaiTools = append(openaiTools, openaiTool)
}
return openaiTools
}
// MapClaudeToolCallsToOpenAI 将 Claude tool_use ContentBlock[] 转换为 OpenAI ToolCall[]。
func MapClaudeToolCallsToOpenAI(blocks []dto.ClaudeContentBlock) []dto.ToolCall {
toolCalls := make([]dto.ToolCall, 0)
for _, block := range blocks {
if block.Type != "tool_use" {
continue
}
// 将 Input 序列化为 JSON 字符串
arguments := "{}"
if block.Input != nil {
if data, err := json.Marshal(block.Input); err == nil {
arguments = string(data)
}
}
toolCalls = append(toolCalls, dto.ToolCall{
ID: block.ID,
Type: "function",
Function: dto.FunctionCall{
Name: block.Name,
Arguments: arguments,
},
})
}
return toolCalls
}
// MapOpenAIToolCallsToClaude 将 OpenAI ToolCall[] 转换为 Claude tool_use ContentBlock[]。
func MapOpenAIToolCallsToClaude(toolCalls []dto.ToolCall) []dto.ClaudeContentBlock {
blocks := make([]dto.ClaudeContentBlock, 0, len(toolCalls))
for _, tc := range toolCalls {
if tc.Type != "function" {
continue
}
// 将 Arguments JSON 字符串解析为 map
var input map[string]any
if tc.Function.Arguments != "" {
_ = json.Unmarshal([]byte(tc.Function.Arguments), &input)
}
if input == nil {
input = make(map[string]any)
}
blocks = append(blocks, dto.ClaudeContentBlock{
Type: "tool_use",
ID: tc.ID,
Name: tc.Function.Name,
Input: input,
})
}
return blocks
}