diff --git a/internal/translator/gemini-cli/openai/chat-completions/cli_openai_request.go b/internal/translator/gemini-cli/openai/chat-completions/cli_openai_request.go index d4a453f3..6ed48486 100644 --- a/internal/translator/gemini-cli/openai/chat-completions/cli_openai_request.go +++ b/internal/translator/gemini-cli/openai/chat-completions/cli_openai_request.go @@ -26,6 +26,20 @@ import ( // - []byte: The transformed request data in Gemini CLI API format func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bool) []byte { rawJSON := bytes.Clone(inputRawJSON) + var pathsToDelete []string + root := gjson.ParseBytes(rawJSON) + util.Walk(root, "", "additionalProperties", &pathsToDelete) + util.Walk(root, "", "$schema", &pathsToDelete) + util.Walk(root, "", "ref", &pathsToDelete) + + var err error + for _, p := range pathsToDelete { + rawJSON, err = sjson.DeleteBytes(rawJSON, p) + if err != nil { + continue + } + } + // Base envelope out := []byte(`{"project":"","request":{"contents":[],"generationConfig":{"thinkingConfig":{"include_thoughts":true}}},"model":"gemini-2.5-pro"}`) @@ -232,7 +246,7 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo } var pathsToType []string - root := gjson.ParseBytes(out) + root = gjson.ParseBytes(out) util.Walk(root, "", "type", &pathsToType) for _, p := range pathsToType { typeResult := gjson.GetBytes(out, p) diff --git a/internal/translator/gemini-cli/openai/chat-completions/cli_openai_response.go b/internal/translator/gemini-cli/openai/chat-completions/cli_openai_response.go index 86ea6b69..15f528cc 100644 --- a/internal/translator/gemini-cli/openai/chat-completions/cli_openai_response.go +++ b/internal/translator/gemini-cli/openai/chat-completions/cli_openai_response.go @@ -20,6 +20,7 @@ import ( // convertCliResponseToOpenAIChatParams holds parameters for response conversion. type convertCliResponseToOpenAIChatParams struct { UnixTimestamp int64 + FunctionIndex int } // ConvertCliResponseToOpenAI translates a single chunk of a streaming response from the @@ -40,6 +41,7 @@ func ConvertCliResponseToOpenAI(_ context.Context, _ string, originalRequestRawJ if *param == nil { *param = &convertCliResponseToOpenAIChatParams{ UnixTimestamp: 0, + FunctionIndex: 0, } } @@ -117,13 +119,18 @@ func ConvertCliResponseToOpenAI(_ context.Context, _ string, originalRequestRawJ } else if functionCallResult.Exists() { // Handle function call content. toolCallsResult := gjson.Get(template, "choices.0.delta.tool_calls") - if !toolCallsResult.Exists() || !toolCallsResult.IsArray() { + functionCallIndex := (*param).(*convertCliResponseToOpenAIChatParams).FunctionIndex + (*param).(*convertCliResponseToOpenAIChatParams).FunctionIndex++ + if toolCallsResult.Exists() && toolCallsResult.IsArray() { + functionCallIndex = len(toolCallsResult.Array()) + } else { template, _ = sjson.SetRaw(template, "choices.0.delta.tool_calls", `[]`) } - functionCallTemplate := `{"id": "","type": "function","function": {"name": "","arguments": ""}}` + functionCallTemplate := `{"id": "","index": 0,"type": "function","function": {"name": "","arguments": ""}}` fcName := functionCallResult.Get("name").String() functionCallTemplate, _ = sjson.Set(functionCallTemplate, "id", fmt.Sprintf("%s-%d", fcName, time.Now().UnixNano())) + functionCallTemplate, _ = sjson.Set(functionCallTemplate, "index", functionCallIndex) functionCallTemplate, _ = sjson.Set(functionCallTemplate, "function.name", fcName) if fcArgsResult := functionCallResult.Get("args"); fcArgsResult.Exists() { functionCallTemplate, _ = sjson.Set(functionCallTemplate, "function.arguments", fcArgsResult.Raw) diff --git a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go index 50f8f1b7..447bba31 100644 --- a/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go +++ b/internal/translator/gemini/openai/chat-completions/gemini_openai_request.go @@ -26,6 +26,20 @@ import ( // - []byte: The transformed request data in Gemini API format func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) []byte { rawJSON := bytes.Clone(inputRawJSON) + var pathsToDelete []string + root := gjson.ParseBytes(rawJSON) + util.Walk(root, "", "additionalProperties", &pathsToDelete) + util.Walk(root, "", "$schema", &pathsToDelete) + util.Walk(root, "", "ref", &pathsToDelete) + + var err error + for _, p := range pathsToDelete { + rawJSON, err = sjson.DeleteBytes(rawJSON, p) + if err != nil { + continue + } + } + // Base envelope out := []byte(`{"contents":[],"generationConfig":{"thinkingConfig":{"include_thoughts":true}}}`) @@ -257,7 +271,7 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool) } var pathsToType []string - root := gjson.ParseBytes(out) + root = gjson.ParseBytes(out) util.Walk(root, "", "type", &pathsToType) for _, p := range pathsToType { typeResult := gjson.GetBytes(out, p) diff --git a/internal/translator/gemini/openai/chat-completions/gemini_openai_response.go b/internal/translator/gemini/openai/chat-completions/gemini_openai_response.go index ab6cc19e..5f1161af 100644 --- a/internal/translator/gemini/openai/chat-completions/gemini_openai_response.go +++ b/internal/translator/gemini/openai/chat-completions/gemini_openai_response.go @@ -19,6 +19,7 @@ import ( // convertGeminiResponseToOpenAIChatParams holds parameters for response conversion. type convertGeminiResponseToOpenAIChatParams struct { UnixTimestamp int64 + FunctionIndex int } // ConvertGeminiResponseToOpenAI translates a single chunk of a streaming response from the @@ -39,6 +40,7 @@ func ConvertGeminiResponseToOpenAI(_ context.Context, _ string, originalRequestR if *param == nil { *param = &convertGeminiResponseToOpenAIChatParams{ UnixTimestamp: 0, + FunctionIndex: 0, } } @@ -120,13 +122,18 @@ func ConvertGeminiResponseToOpenAI(_ context.Context, _ string, originalRequestR } else if functionCallResult.Exists() { // Handle function call content. toolCallsResult := gjson.Get(template, "choices.0.delta.tool_calls") - if !toolCallsResult.Exists() || !toolCallsResult.IsArray() { + functionCallIndex := (*param).(*convertGeminiResponseToOpenAIChatParams).FunctionIndex + (*param).(*convertGeminiResponseToOpenAIChatParams).FunctionIndex++ + if toolCallsResult.Exists() && toolCallsResult.IsArray() { + functionCallIndex = len(toolCallsResult.Array()) + } else { template, _ = sjson.SetRaw(template, "choices.0.delta.tool_calls", `[]`) } - functionCallTemplate := `{"id": "","type": "function","function": {"name": "","arguments": ""}}` + functionCallTemplate := `{"id": "","index": 0,"type": "function","function": {"name": "","arguments": ""}}` fcName := functionCallResult.Get("name").String() functionCallTemplate, _ = sjson.Set(functionCallTemplate, "id", fmt.Sprintf("%s-%d", fcName, time.Now().UnixNano())) + functionCallTemplate, _ = sjson.Set(functionCallTemplate, "index", functionCallIndex) functionCallTemplate, _ = sjson.Set(functionCallTemplate, "function.name", fcName) if fcArgsResult := functionCallResult.Get("args"); fcArgsResult.Exists() { functionCallTemplate, _ = sjson.Set(functionCallTemplate, "function.arguments", fcArgsResult.Raw)