mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
**feat(translator): add ThoughtSignature handling in Gemini request transformations**
This commit is contained in:
@@ -62,6 +62,9 @@ type Part struct {
|
|||||||
// InlineData contains base64-encoded data with its MIME type (e.g., images).
|
// InlineData contains base64-encoded data with its MIME type (e.g., images).
|
||||||
InlineData *InlineData `json:"inlineData,omitempty"`
|
InlineData *InlineData `json:"inlineData,omitempty"`
|
||||||
|
|
||||||
|
// ThoughtSignature is a provider-required signature that accompanies certain parts.
|
||||||
|
ThoughtSignature string `json:"thoughtSignature,omitempty"`
|
||||||
|
|
||||||
// FunctionCall represents a tool call requested by the model.
|
// FunctionCall represents a tool call requested by the model.
|
||||||
FunctionCall *FunctionCall `json:"functionCall,omitempty"`
|
FunctionCall *FunctionCall `json:"functionCall,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const geminiCLIClaudeThoughtSignature = "skip_thought_signature_validator"
|
||||||
|
|
||||||
// ConvertClaudeRequestToCLI parses and transforms a Claude Code API request into Gemini CLI API format.
|
// ConvertClaudeRequestToCLI parses and transforms a Claude Code API request into Gemini CLI API format.
|
||||||
// It extracts the model name, system instruction, message contents, and tool declarations
|
// 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 Gemini CLI API.
|
// from the raw JSON request and returns them in the format expected by the Gemini CLI API.
|
||||||
@@ -89,7 +91,10 @@ func ConvertClaudeRequestToCLI(modelName string, inputRawJSON []byte, _ bool) []
|
|||||||
functionArgs := contentResult.Get("input").String()
|
functionArgs := contentResult.Get("input").String()
|
||||||
var args map[string]any
|
var args map[string]any
|
||||||
if err := json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
if err := json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{FunctionCall: &client.FunctionCall{Name: functionName, Args: args}})
|
clientContent.Parts = append(clientContent.Parts, client.Part{
|
||||||
|
FunctionCall: &client.FunctionCall{Name: functionName, Args: args},
|
||||||
|
ThoughtSignature: geminiCLIClaudeThoughtSignature,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
||||||
toolCallID := contentResult.Get("tool_use_id").String()
|
toolCallID := contentResult.Get("tool_use_id").String()
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const geminiCLIFunctionThoughtSignature = "skip_thought_signature_validator"
|
||||||
|
|
||||||
// ConvertOpenAIRequestToGeminiCLI converts an OpenAI Chat Completions request (raw JSON)
|
// ConvertOpenAIRequestToGeminiCLI converts an OpenAI Chat Completions request (raw JSON)
|
||||||
// into a complete Gemini CLI request JSON. All JSON construction uses sjson and lookups use gjson.
|
// into a complete Gemini CLI request JSON. All JSON construction uses sjson and lookups use gjson.
|
||||||
//
|
//
|
||||||
@@ -239,6 +241,7 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|||||||
fargs := tc.Get("function.arguments").String()
|
fargs := tc.Get("function.arguments").String()
|
||||||
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".functionCall.name", fname)
|
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".functionCall.name", fname)
|
||||||
node, _ = sjson.SetRawBytes(node, "parts."+itoa(p)+".functionCall.args", []byte(fargs))
|
node, _ = sjson.SetRawBytes(node, "parts."+itoa(p)+".functionCall.args", []byte(fargs))
|
||||||
|
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".thoughtSignature", geminiCLIFunctionThoughtSignature)
|
||||||
p++
|
p++
|
||||||
if fid != "" {
|
if fid != "" {
|
||||||
fIDs = append(fIDs, fid)
|
fIDs = append(fIDs, fid)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const geminiClaudeThoughtSignature = "skip_thought_signature_validator"
|
||||||
|
|
||||||
// ConvertClaudeRequestToGemini parses a Claude API request and returns a complete
|
// ConvertClaudeRequestToGemini parses a Claude API request and returns a complete
|
||||||
// Gemini CLI request body (as JSON bytes) ready to be sent via SendRawMessageStream.
|
// Gemini CLI request body (as JSON bytes) ready to be sent via SendRawMessageStream.
|
||||||
// All JSON transformations are performed using gjson/sjson.
|
// All JSON transformations are performed using gjson/sjson.
|
||||||
@@ -82,7 +84,10 @@ func ConvertClaudeRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
functionArgs := contentResult.Get("input").String()
|
functionArgs := contentResult.Get("input").String()
|
||||||
var args map[string]any
|
var args map[string]any
|
||||||
if err := json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
if err := json.Unmarshal([]byte(functionArgs), &args); err == nil {
|
||||||
clientContent.Parts = append(clientContent.Parts, client.Part{FunctionCall: &client.FunctionCall{Name: functionName, Args: args}})
|
clientContent.Parts = append(clientContent.Parts, client.Part{
|
||||||
|
FunctionCall: &client.FunctionCall{Name: functionName, Args: args},
|
||||||
|
ThoughtSignature: geminiClaudeThoughtSignature,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_result" {
|
||||||
toolCallID := contentResult.Get("tool_use_id").String()
|
toolCallID := contentResult.Get("tool_use_id").String()
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const geminiFunctionThoughtSignature = "skip_thought_signature_validator"
|
||||||
|
|
||||||
// ConvertOpenAIRequestToGemini converts an OpenAI Chat Completions request (raw JSON)
|
// ConvertOpenAIRequestToGemini converts an OpenAI Chat Completions request (raw JSON)
|
||||||
// into a complete Gemini request JSON. All JSON construction uses sjson and lookups use gjson.
|
// into a complete Gemini request JSON. All JSON construction uses sjson and lookups use gjson.
|
||||||
//
|
//
|
||||||
@@ -264,6 +266,7 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|||||||
fargs := tc.Get("function.arguments").String()
|
fargs := tc.Get("function.arguments").String()
|
||||||
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".functionCall.name", fname)
|
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".functionCall.name", fname)
|
||||||
node, _ = sjson.SetRawBytes(node, "parts."+itoa(p)+".functionCall.args", []byte(fargs))
|
node, _ = sjson.SetRawBytes(node, "parts."+itoa(p)+".functionCall.args", []byte(fargs))
|
||||||
|
node, _ = sjson.SetBytes(node, "parts."+itoa(p)+".thoughtSignature", geminiFunctionThoughtSignature)
|
||||||
p++
|
p++
|
||||||
if fid != "" {
|
if fid != "" {
|
||||||
fIDs = append(fIDs, fid)
|
fIDs = append(fIDs, fid)
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const geminiResponsesThoughtSignature = "skip_thought_signature_validator"
|
||||||
|
|
||||||
func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte, stream bool) []byte {
|
func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte, stream bool) []byte {
|
||||||
rawJSON := bytes.Clone(inputRawJSON)
|
rawJSON := bytes.Clone(inputRawJSON)
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte
|
|||||||
modelContent := `{"role":"model","parts":[]}`
|
modelContent := `{"role":"model","parts":[]}`
|
||||||
functionCall := `{"functionCall":{"name":"","args":{}}}`
|
functionCall := `{"functionCall":{"name":"","args":{}}}`
|
||||||
functionCall, _ = sjson.Set(functionCall, "functionCall.name", name)
|
functionCall, _ = sjson.Set(functionCall, "functionCall.name", name)
|
||||||
|
functionCall, _ = sjson.Set(functionCall, "thoughtSignature", geminiResponsesThoughtSignature)
|
||||||
|
|
||||||
// Parse arguments JSON string and set as args object
|
// Parse arguments JSON string and set as args object
|
||||||
if arguments != "" {
|
if arguments != "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user