mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 04:20:50 +08:00
171 lines
6.3 KiB
Go
171 lines
6.3 KiB
Go
// Package code provides request translation functionality for Claude API.
|
|
// It handles parsing and transforming Claude API requests into the internal client format,
|
|
// extracting model information, system instructions, message contents, and tool declarations.
|
|
// The package also performs JSON data cleaning and transformation to ensure compatibility
|
|
// between Claude API format and the internal client's expected format.
|
|
package code
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"github.com/luispater/CLIProxyAPI/internal/client"
|
|
"github.com/tidwall/gjson"
|
|
"github.com/tidwall/sjson"
|
|
)
|
|
|
|
// ConvertClaudeCodeRequestToCli parses and transforms a Claude API request into internal client format.
|
|
// It extracts the model name, system instruction, message contents, and tool declarations
|
|
// from the raw JSON request and returns them in the format expected by the internal client.
|
|
func ConvertClaudeCodeRequestToCli(rawJSON []byte) (string, *client.Content, []client.Content, []client.ToolDeclaration) {
|
|
var pathsToDelete []string
|
|
root := gjson.ParseBytes(rawJSON)
|
|
walk(root, "", "additionalProperties", &pathsToDelete)
|
|
walk(root, "", "$schema", &pathsToDelete)
|
|
|
|
var err error
|
|
for _, p := range pathsToDelete {
|
|
rawJSON, err = sjson.DeleteBytes(rawJSON, p)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
}
|
|
rawJSON = bytes.Replace(rawJSON, []byte(`"url":{"type":"string","format":"uri",`), []byte(`"url":{"type":"string",`), -1)
|
|
|
|
// log.Debug(string(rawJSON))
|
|
modelName := "gemini-2.5-pro"
|
|
modelResult := gjson.GetBytes(rawJSON, "model")
|
|
if modelResult.Type == gjson.String {
|
|
modelName = modelResult.String()
|
|
}
|
|
|
|
contents := make([]client.Content, 0)
|
|
|
|
var systemInstruction *client.Content
|
|
|
|
systemResult := gjson.GetBytes(rawJSON, "system")
|
|
if systemResult.IsArray() {
|
|
systemResults := systemResult.Array()
|
|
systemInstruction = &client.Content{Role: "user", Parts: []client.Part{}}
|
|
for i := 0; i < len(systemResults); i++ {
|
|
systemPromptResult := systemResults[i]
|
|
systemTypePromptResult := systemPromptResult.Get("type")
|
|
if systemTypePromptResult.Type == gjson.String && systemTypePromptResult.String() == "text" {
|
|
systemPrompt := systemPromptResult.Get("text").String()
|
|
systemPart := client.Part{Text: systemPrompt}
|
|
systemInstruction.Parts = append(systemInstruction.Parts, systemPart)
|
|
}
|
|
}
|
|
if len(systemInstruction.Parts) == 0 {
|
|
systemInstruction = nil
|
|
}
|
|
}
|
|
|
|
messagesResult := gjson.GetBytes(rawJSON, "messages")
|
|
if messagesResult.IsArray() {
|
|
messageResults := messagesResult.Array()
|
|
for i := 0; i < len(messageResults); i++ {
|
|
messageResult := messageResults[i]
|
|
roleResult := messageResult.Get("role")
|
|
if roleResult.Type != gjson.String {
|
|
continue
|
|
}
|
|
role := roleResult.String()
|
|
if role == "assistant" {
|
|
role = "model"
|
|
}
|
|
clientContent := client.Content{Role: role, Parts: []client.Part{}}
|
|
|
|
contentsResult := messageResult.Get("content")
|
|
if contentsResult.IsArray() {
|
|
contentResults := contentsResult.Array()
|
|
for j := 0; j < len(contentResults); j++ {
|
|
contentResult := contentResults[j]
|
|
contentTypeResult := contentResult.Get("type")
|
|
if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "text" {
|
|
prompt := contentResult.Get("text").String()
|
|
clientContent.Parts = append(clientContent.Parts, client.Part{Text: prompt})
|
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_use" {
|
|
functionName := contentResult.Get("name").String()
|
|
functionArgs := contentResult.Get("input").String()
|
|
var args map[string]any
|
|
if err = json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
|
clientContent.Parts = append(clientContent.Parts, client.Part{
|
|
FunctionCall: &client.FunctionCall{
|
|
Name: functionName,
|
|
Args: args,
|
|
},
|
|
})
|
|
}
|
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
|
toolCallID := contentResult.Get("tool_use_id").String()
|
|
if toolCallID != "" {
|
|
funcName := toolCallID
|
|
toolCallIDs := strings.Split(toolCallID, "-")
|
|
if len(toolCallIDs) > 1 {
|
|
funcName = strings.Join(toolCallIDs[0:len(toolCallIDs)-1], "-")
|
|
}
|
|
responseData := contentResult.Get("content").String()
|
|
functionResponse := client.FunctionResponse{Name: funcName, Response: map[string]interface{}{"result": responseData}}
|
|
clientContent.Parts = append(clientContent.Parts, client.Part{FunctionResponse: &functionResponse})
|
|
}
|
|
}
|
|
}
|
|
contents = append(contents, clientContent)
|
|
} else if contentsResult.Type == gjson.String {
|
|
prompt := contentsResult.String()
|
|
contents = append(contents, client.Content{Role: role, Parts: []client.Part{{Text: prompt}}})
|
|
}
|
|
}
|
|
}
|
|
|
|
var tools []client.ToolDeclaration
|
|
toolsResult := gjson.GetBytes(rawJSON, "tools")
|
|
if toolsResult.IsArray() {
|
|
tools = make([]client.ToolDeclaration, 1)
|
|
tools[0].FunctionDeclarations = make([]any, 0)
|
|
toolsResults := toolsResult.Array()
|
|
for i := 0; i < len(toolsResults); i++ {
|
|
toolResult := toolsResults[i]
|
|
inputSchemaResult := toolResult.Get("input_schema")
|
|
if inputSchemaResult.Exists() && inputSchemaResult.IsObject() {
|
|
inputSchema := inputSchemaResult.Raw
|
|
inputSchema, _ = sjson.Delete(inputSchema, "additionalProperties")
|
|
inputSchema, _ = sjson.Delete(inputSchema, "$schema")
|
|
|
|
tool, _ := sjson.Delete(toolResult.Raw, "input_schema")
|
|
tool, _ = sjson.SetRaw(tool, "parameters", inputSchema)
|
|
var toolDeclaration any
|
|
if err = json.Unmarshal([]byte(tool), &toolDeclaration); err == nil {
|
|
tools[0].FunctionDeclarations = append(tools[0].FunctionDeclarations, toolDeclaration)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
tools = make([]client.ToolDeclaration, 0)
|
|
}
|
|
|
|
return modelName, systemInstruction, contents, tools
|
|
}
|
|
|
|
func walk(value gjson.Result, path, field string, pathsToDelete *[]string) {
|
|
switch value.Type {
|
|
case gjson.JSON:
|
|
value.ForEach(func(key, val gjson.Result) bool {
|
|
var childPath string
|
|
if path == "" {
|
|
childPath = key.String()
|
|
} else {
|
|
childPath = path + "." + key.String()
|
|
}
|
|
if key.String() == field {
|
|
*pathsToDelete = append(*pathsToDelete, childPath)
|
|
}
|
|
walk(val, childPath, field, pathsToDelete)
|
|
return true
|
|
})
|
|
case gjson.String, gjson.Number, gjson.True, gjson.False, gjson.Null:
|
|
}
|
|
}
|