diff --git a/internal/translator/antigravity/openai/responses/antigravity_openai-responses_request.go b/internal/translator/antigravity/openai/responses/antigravity_openai-responses_request.go index cbe4757c..65d4dcd8 100644 --- a/internal/translator/antigravity/openai/responses/antigravity_openai-responses_request.go +++ b/internal/translator/antigravity/openai/responses/antigravity_openai-responses_request.go @@ -3,12 +3,12 @@ package responses import ( "bytes" - . "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini-cli/gemini" + . "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/antigravity/gemini" . "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/gemini/openai/responses" ) func ConvertOpenAIResponsesRequestToAntigravity(modelName string, inputRawJSON []byte, stream bool) []byte { rawJSON := bytes.Clone(inputRawJSON) rawJSON = ConvertOpenAIResponsesRequestToGemini(modelName, rawJSON, stream) - return ConvertGeminiRequestToGeminiCLI(modelName, rawJSON, stream) + return ConvertGeminiRequestToAntigravity(modelName, rawJSON, stream) } diff --git a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go index b21ef724..b8e471e3 100644 --- a/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go +++ b/internal/translator/gemini/openai/responses/gemini_openai-responses_request.go @@ -67,39 +67,101 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte // even when the message.role is "user". We split such items into distinct Gemini messages // with roles derived from the content type to match docs/convert-2.md. if contentArray := item.Get("content"); contentArray.Exists() && contentArray.IsArray() { + currentRole := "" + var currentParts []string + + flush := func() { + if currentRole == "" || len(currentParts) == 0 { + currentParts = nil + return + } + one := `{"role":"","parts":[]}` + one, _ = sjson.Set(one, "role", currentRole) + for _, part := range currentParts { + one, _ = sjson.SetRaw(one, "parts.-1", part) + } + out, _ = sjson.SetRaw(out, "contents.-1", one) + currentParts = nil + } + contentArray.ForEach(func(_, contentItem gjson.Result) bool { contentType := contentItem.Get("type").String() if contentType == "" { contentType = "input_text" } + + effRole := "user" + if itemRole != "" { + switch strings.ToLower(itemRole) { + case "assistant", "model": + effRole = "model" + default: + effRole = strings.ToLower(itemRole) + } + } + if contentType == "output_text" { + effRole = "model" + } + if effRole == "assistant" { + effRole = "model" + } + + if currentRole != "" && effRole != currentRole { + flush() + currentRole = "" + } + if currentRole == "" { + currentRole = effRole + } + + var partJSON string switch contentType { case "input_text", "output_text": if text := contentItem.Get("text"); text.Exists() { - effRole := "user" - if itemRole != "" { - switch strings.ToLower(itemRole) { - case "assistant", "model": - effRole = "model" - default: - effRole = strings.ToLower(itemRole) + partJSON = `{"text":""}` + partJSON, _ = sjson.Set(partJSON, "text", text.String()) + } + case "input_image": + imageURL := contentItem.Get("image_url").String() + if imageURL == "" { + imageURL = contentItem.Get("url").String() + } + if imageURL != "" { + mimeType := "application/octet-stream" + data := "" + if strings.HasPrefix(imageURL, "data:") { + trimmed := strings.TrimPrefix(imageURL, "data:") + mediaAndData := strings.SplitN(trimmed, ";base64,", 2) + if len(mediaAndData) == 2 { + if mediaAndData[0] != "" { + mimeType = mediaAndData[0] + } + data = mediaAndData[1] + } else { + mediaAndData = strings.SplitN(trimmed, ",", 2) + if len(mediaAndData) == 2 { + if mediaAndData[0] != "" { + mimeType = mediaAndData[0] + } + data = mediaAndData[1] + } } } - if contentType == "output_text" { - effRole = "model" + if data != "" { + partJSON = `{"inline_data":{"mime_type":"","data":""}}` + partJSON, _ = sjson.Set(partJSON, "inline_data.mime_type", mimeType) + partJSON, _ = sjson.Set(partJSON, "inline_data.data", data) } - if effRole == "assistant" { - effRole = "model" - } - one := `{"role":"","parts":[]}` - one, _ = sjson.Set(one, "role", effRole) - textPart := `{"text":""}` - textPart, _ = sjson.Set(textPart, "text", text.String()) - one, _ = sjson.SetRaw(one, "parts.-1", textPart) - out, _ = sjson.SetRaw(out, "contents.-1", one) } } + + if partJSON != "" { + currentParts = append(currentParts, partJSON) + } return true }) + + flush() } case "function_call":