mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
refactor(translator): replace client.Content structs with JSON-based content generation for more efficient handling of Claude requests
This commit is contained in:
@@ -7,10 +7,8 @@ package claude
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
client "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/common"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
@@ -42,27 +40,30 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
rawJSON = bytes.Replace(rawJSON, []byte(`"url":{"type":"string","format":"uri",`), []byte(`"url":{"type":"string",`), -1)
|
rawJSON = bytes.Replace(rawJSON, []byte(`"url":{"type":"string","format":"uri",`), []byte(`"url":{"type":"string",`), -1)
|
||||||
|
|
||||||
// system instruction
|
// system instruction
|
||||||
var systemInstruction *client.Content
|
systemInstructionJSON := ""
|
||||||
|
hasSystemInstruction := false
|
||||||
systemResult := gjson.GetBytes(rawJSON, "system")
|
systemResult := gjson.GetBytes(rawJSON, "system")
|
||||||
if systemResult.IsArray() {
|
if systemResult.IsArray() {
|
||||||
systemResults := systemResult.Array()
|
systemResults := systemResult.Array()
|
||||||
systemInstruction = &client.Content{Role: "user", Parts: []client.Part{}}
|
systemInstructionJSON = `{"role":"user","parts":[]}`
|
||||||
for i := 0; i < len(systemResults); i++ {
|
for i := 0; i < len(systemResults); i++ {
|
||||||
systemPromptResult := systemResults[i]
|
systemPromptResult := systemResults[i]
|
||||||
systemTypePromptResult := systemPromptResult.Get("type")
|
systemTypePromptResult := systemPromptResult.Get("type")
|
||||||
if systemTypePromptResult.Type == gjson.String && systemTypePromptResult.String() == "text" {
|
if systemTypePromptResult.Type == gjson.String && systemTypePromptResult.String() == "text" {
|
||||||
systemPrompt := systemPromptResult.Get("text").String()
|
systemPrompt := systemPromptResult.Get("text").String()
|
||||||
systemPart := client.Part{Text: systemPrompt}
|
partJSON := `{}`
|
||||||
systemInstruction.Parts = append(systemInstruction.Parts, systemPart)
|
if systemPrompt != "" {
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "text", systemPrompt)
|
||||||
|
}
|
||||||
|
systemInstructionJSON, _ = sjson.SetRaw(systemInstructionJSON, "parts.-1", partJSON)
|
||||||
|
hasSystemInstruction = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(systemInstruction.Parts) == 0 {
|
|
||||||
systemInstruction = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// contents
|
// contents
|
||||||
contents := make([]client.Content, 0)
|
contentsJSON := "[]"
|
||||||
|
hasContents := false
|
||||||
messagesResult := gjson.GetBytes(rawJSON, "messages")
|
messagesResult := gjson.GetBytes(rawJSON, "messages")
|
||||||
if messagesResult.IsArray() {
|
if messagesResult.IsArray() {
|
||||||
messageResults := messagesResult.Array()
|
messageResults := messagesResult.Array()
|
||||||
@@ -76,7 +77,8 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
if role == "assistant" {
|
if role == "assistant" {
|
||||||
role = "model"
|
role = "model"
|
||||||
}
|
}
|
||||||
clientContent := client.Content{Role: role, Parts: []client.Part{}}
|
clientContentJSON := `{"role":"","parts":[]}`
|
||||||
|
clientContentJSON, _ = sjson.Set(clientContentJSON, "role", role)
|
||||||
contentsResult := messageResult.Get("content")
|
contentsResult := messageResult.Get("content")
|
||||||
if contentsResult.IsArray() {
|
if contentsResult.IsArray() {
|
||||||
contentResults := contentsResult.Array()
|
contentResults := contentsResult.Array()
|
||||||
@@ -90,25 +92,39 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
if signatureResult.Exists() {
|
if signatureResult.Exists() {
|
||||||
signature = signatureResult.String()
|
signature = signatureResult.String()
|
||||||
}
|
}
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{Text: prompt, Thought: true, ThoughtSignature: signature})
|
partJSON := `{}`
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "thought", true)
|
||||||
|
if prompt != "" {
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "text", prompt)
|
||||||
|
}
|
||||||
|
if signature != "" {
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "thoughtSignature", signature)
|
||||||
|
}
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "text" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "text" {
|
||||||
prompt := contentResult.Get("text").String()
|
prompt := contentResult.Get("text").String()
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{Text: prompt})
|
partJSON := `{}`
|
||||||
|
if prompt != "" {
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "text", prompt)
|
||||||
|
}
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_use" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_use" {
|
||||||
functionName := contentResult.Get("name").String()
|
functionName := contentResult.Get("name").String()
|
||||||
functionArgs := contentResult.Get("input").String()
|
functionArgs := contentResult.Get("input").String()
|
||||||
functionID := contentResult.Get("id").String()
|
functionID := contentResult.Get("id").String()
|
||||||
var args map[string]any
|
if gjson.Valid(functionArgs) {
|
||||||
if err := json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
argsResult := gjson.Parse(functionArgs)
|
||||||
if strings.Contains(modelName, "claude") {
|
if argsResult.IsObject() {
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{
|
partJSON := `{}`
|
||||||
FunctionCall: &client.FunctionCall{ID: functionID, Name: functionName, Args: args},
|
if !strings.Contains(modelName, "claude") {
|
||||||
})
|
partJSON, _ = sjson.Set(partJSON, "thoughtSignature", geminiCLIClaudeThoughtSignature)
|
||||||
} else {
|
}
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{
|
if functionID != "" {
|
||||||
FunctionCall: &client.FunctionCall{ID: functionID, Name: functionName, Args: args},
|
partJSON, _ = sjson.Set(partJSON, "functionCall.id", functionID)
|
||||||
ThoughtSignature: geminiCLIClaudeThoughtSignature,
|
}
|
||||||
})
|
partJSON, _ = sjson.Set(partJSON, "functionCall.name", functionName)
|
||||||
|
partJSON, _ = sjson.SetRaw(partJSON, "functionCall.args", argsResult.Raw)
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
||||||
@@ -121,41 +137,70 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
}
|
}
|
||||||
functionResponseResult := contentResult.Get("content")
|
functionResponseResult := contentResult.Get("content")
|
||||||
|
|
||||||
|
functionResponseJSON := `{}`
|
||||||
|
functionResponseJSON, _ = sjson.Set(functionResponseJSON, "id", toolCallID)
|
||||||
|
functionResponseJSON, _ = sjson.Set(functionResponseJSON, "name", funcName)
|
||||||
|
|
||||||
responseData := ""
|
responseData := ""
|
||||||
if functionResponseResult.Type == gjson.String {
|
if functionResponseResult.Type == gjson.String {
|
||||||
responseData = functionResponseResult.String()
|
responseData = functionResponseResult.String()
|
||||||
|
functionResponseJSON, _ = sjson.Set(functionResponseJSON, "response.result", responseData)
|
||||||
|
} else if functionResponseResult.IsArray() {
|
||||||
|
frResults := functionResponseResult.Array()
|
||||||
|
if len(frResults) == 1 {
|
||||||
|
functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", frResults[0].Raw)
|
||||||
|
} else {
|
||||||
|
functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", functionResponseResult.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if functionResponseResult.IsObject() {
|
||||||
|
functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", functionResponseResult.Raw)
|
||||||
} else {
|
} else {
|
||||||
responseData = contentResult.Get("content").Raw
|
functionResponseJSON, _ = sjson.SetRaw(functionResponseJSON, "response.result", functionResponseResult.Raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
functionResponse := client.FunctionResponse{ID: toolCallID, Name: funcName, Response: map[string]interface{}{"result": responseData}}
|
partJSON := `{}`
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{FunctionResponse: &functionResponse})
|
partJSON, _ = sjson.SetRaw(partJSON, "functionResponse", functionResponseJSON)
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
}
|
}
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "image" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "image" {
|
||||||
sourceResult := contentResult.Get("source")
|
sourceResult := contentResult.Get("source")
|
||||||
if sourceResult.Get("type").String() == "base64" {
|
if sourceResult.Get("type").String() == "base64" {
|
||||||
inlineData := &client.InlineData{
|
inlineDataJSON := `{}`
|
||||||
MimeType: sourceResult.Get("media_type").String(),
|
if mimeType := sourceResult.Get("media_type").String(); mimeType != "" {
|
||||||
Data: sourceResult.Get("data").String(),
|
inlineDataJSON, _ = sjson.Set(inlineDataJSON, "mime_type", mimeType)
|
||||||
}
|
}
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{InlineData: inlineData})
|
if data := sourceResult.Get("data").String(); data != "" {
|
||||||
|
inlineDataJSON, _ = sjson.Set(inlineDataJSON, "data", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
partJSON := `{}`
|
||||||
|
partJSON, _ = sjson.SetRaw(partJSON, "inlineData", inlineDataJSON)
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contents = append(contents, clientContent)
|
contentsJSON, _ = sjson.SetRaw(contentsJSON, "-1", clientContentJSON)
|
||||||
|
hasContents = true
|
||||||
} else if contentsResult.Type == gjson.String {
|
} else if contentsResult.Type == gjson.String {
|
||||||
prompt := contentsResult.String()
|
prompt := contentsResult.String()
|
||||||
contents = append(contents, client.Content{Role: role, Parts: []client.Part{{Text: prompt}}})
|
partJSON := `{}`
|
||||||
|
if prompt != "" {
|
||||||
|
partJSON, _ = sjson.Set(partJSON, "text", prompt)
|
||||||
|
}
|
||||||
|
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
||||||
|
contentsJSON, _ = sjson.SetRaw(contentsJSON, "-1", clientContentJSON)
|
||||||
|
hasContents = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tools
|
// tools
|
||||||
var tools []client.ToolDeclaration
|
toolsJSON := ""
|
||||||
|
toolDeclCount := 0
|
||||||
toolsResult := gjson.GetBytes(rawJSON, "tools")
|
toolsResult := gjson.GetBytes(rawJSON, "tools")
|
||||||
if toolsResult.IsArray() {
|
if toolsResult.IsArray() {
|
||||||
tools = make([]client.ToolDeclaration, 1)
|
toolsJSON = `[{"functionDeclarations":[]}]`
|
||||||
tools[0].FunctionDeclarations = make([]any, 0)
|
|
||||||
toolsResults := toolsResult.Array()
|
toolsResults := toolsResult.Array()
|
||||||
for i := 0; i < len(toolsResults); i++ {
|
for i := 0; i < len(toolsResults); i++ {
|
||||||
toolResult := toolsResults[i]
|
toolResult := toolsResults[i]
|
||||||
@@ -166,30 +211,23 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|||||||
tool, _ = sjson.SetRaw(tool, "parametersJsonSchema", inputSchema)
|
tool, _ = sjson.SetRaw(tool, "parametersJsonSchema", inputSchema)
|
||||||
tool, _ = sjson.Delete(tool, "strict")
|
tool, _ = sjson.Delete(tool, "strict")
|
||||||
tool, _ = sjson.Delete(tool, "input_examples")
|
tool, _ = sjson.Delete(tool, "input_examples")
|
||||||
var toolDeclaration any
|
toolsJSON, _ = sjson.SetRaw(toolsJSON, "0.functionDeclarations.-1", tool)
|
||||||
if err := json.Unmarshal([]byte(tool), &toolDeclaration); err == nil {
|
toolDeclCount++
|
||||||
tools[0].FunctionDeclarations = append(tools[0].FunctionDeclarations, toolDeclaration)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tools = make([]client.ToolDeclaration, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build output Gemini CLI request JSON
|
// Build output Gemini CLI request JSON
|
||||||
out := `{"model":"","request":{"contents":[]}}`
|
out := `{"model":"","request":{"contents":[]}}`
|
||||||
out, _ = sjson.Set(out, "model", modelName)
|
out, _ = sjson.Set(out, "model", modelName)
|
||||||
if systemInstruction != nil {
|
if hasSystemInstruction {
|
||||||
b, _ := json.Marshal(systemInstruction)
|
out, _ = sjson.SetRaw(out, "request.systemInstruction", systemInstructionJSON)
|
||||||
out, _ = sjson.SetRaw(out, "request.systemInstruction", string(b))
|
|
||||||
}
|
}
|
||||||
if len(contents) > 0 {
|
if hasContents {
|
||||||
b, _ := json.Marshal(contents)
|
out, _ = sjson.SetRaw(out, "request.contents", contentsJSON)
|
||||||
out, _ = sjson.SetRaw(out, "request.contents", string(b))
|
|
||||||
}
|
}
|
||||||
if len(tools) > 0 && len(tools[0].FunctionDeclarations) > 0 {
|
if toolDeclCount > 0 {
|
||||||
b, _ := json.Marshal(tools)
|
out, _ = sjson.SetRaw(out, "request.tools", toolsJSON)
|
||||||
out, _ = sjson.SetRaw(out, "request.tools", string(b))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when type==enabled
|
// Map Anthropic thinking -> Gemini thinkingBudget/include_thoughts when type==enabled
|
||||||
|
|||||||
Reference in New Issue
Block a user