feat(translator): improve system message handling and content indexing across translators

- Updated logic for processing system messages in `claude`, `gemini`, `gemini-cli`, and `antigravity` translators.
- Introduced indexing for `systemInstruction.parts` to ensure proper ordering and handling of multi-part content.
- Added safeguards for accurate content transformation and serialization.
This commit is contained in:
Luis Pater
2026-01-17 05:40:56 +08:00
parent bc7167e9fe
commit f8f3ad84fc
4 changed files with 47 additions and 15 deletions

View File

@@ -132,6 +132,7 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
} }
} }
systemPartIndex := 0
for i := 0; i < len(arr); i++ { for i := 0; i < len(arr); i++ {
m := arr[i] m := arr[i]
role := m.Get("role").String() role := m.Get("role").String()
@@ -141,16 +142,19 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
// system -> request.systemInstruction as a user message style // system -> request.systemInstruction as a user message style
if content.Type == gjson.String { if content.Type == gjson.String {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
out, _ = sjson.SetBytes(out, "request.systemInstruction.parts.0.text", content.String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), content.String())
systemPartIndex++
} else if content.IsObject() && content.Get("type").String() == "text" { } else if content.IsObject() && content.Get("type").String() == "text" {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
out, _ = sjson.SetBytes(out, "request.systemInstruction.parts.0.text", content.Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), content.Get("text").String())
systemPartIndex++
} else if content.IsArray() { } else if content.IsArray() {
contents := content.Array() contents := content.Array()
if len(contents) > 0 { if len(contents) > 0 {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
for j := 0; j < len(contents); j++ { for j := 0; j < len(contents); j++ {
out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", j), contents[j].Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), contents[j].Get("text").String())
systemPartIndex++
} }
} }
} }

View File

@@ -141,17 +141,35 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream
// Process messages and transform them to Claude Code format // Process messages and transform them to Claude Code format
if messages := root.Get("messages"); messages.Exists() && messages.IsArray() { if messages := root.Get("messages"); messages.Exists() && messages.IsArray() {
messageIndex := 0
systemMessageIndex := -1
messages.ForEach(func(_, message gjson.Result) bool { messages.ForEach(func(_, message gjson.Result) bool {
role := message.Get("role").String() role := message.Get("role").String()
contentResult := message.Get("content") contentResult := message.Get("content")
switch role { switch role {
case "system", "user", "assistant": case "system":
// Create Claude Code message with appropriate role mapping if systemMessageIndex == -1 {
if role == "system" { systemMsg := `{"role":"user","content":[]}`
role = "user" out, _ = sjson.SetRaw(out, "messages.-1", systemMsg)
systemMessageIndex = messageIndex
messageIndex++
} }
if contentResult.Exists() && contentResult.Type == gjson.String && contentResult.String() != "" {
textPart := `{"type":"text","text":""}`
textPart, _ = sjson.Set(textPart, "text", contentResult.String())
out, _ = sjson.SetRaw(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart)
} else if contentResult.Exists() && contentResult.IsArray() {
contentResult.ForEach(func(_, part gjson.Result) bool {
if part.Get("type").String() == "text" {
textPart := `{"type":"text","text":""}`
textPart, _ = sjson.Set(textPart, "text", part.Get("text").String())
out, _ = sjson.SetRaw(out, fmt.Sprintf("messages.%d.content.-1", systemMessageIndex), textPart)
}
return true
})
}
case "user", "assistant":
msg := `{"role":"","content":[]}` msg := `{"role":"","content":[]}`
msg, _ = sjson.Set(msg, "role", role) msg, _ = sjson.Set(msg, "role", role)
@@ -230,6 +248,7 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream
} }
out, _ = sjson.SetRaw(out, "messages.-1", msg) out, _ = sjson.SetRaw(out, "messages.-1", msg)
messageIndex++
case "tool": case "tool":
// Handle tool result messages conversion // Handle tool result messages conversion
@@ -240,6 +259,7 @@ func ConvertOpenAIRequestToClaude(modelName string, inputRawJSON []byte, stream
msg, _ = sjson.Set(msg, "content.0.tool_use_id", toolCallID) msg, _ = sjson.Set(msg, "content.0.tool_use_id", toolCallID)
msg, _ = sjson.Set(msg, "content.0.content", content) msg, _ = sjson.Set(msg, "content.0.content", content)
out, _ = sjson.SetRaw(out, "messages.-1", msg) out, _ = sjson.SetRaw(out, "messages.-1", msg)
messageIndex++
} }
return true return true
}) })

View File

@@ -129,6 +129,7 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
} }
} }
systemPartIndex := 0
for i := 0; i < len(arr); i++ { for i := 0; i < len(arr); i++ {
m := arr[i] m := arr[i]
role := m.Get("role").String() role := m.Get("role").String()
@@ -138,16 +139,19 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
// system -> request.systemInstruction as a user message style // system -> request.systemInstruction as a user message style
if content.Type == gjson.String { if content.Type == gjson.String {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
out, _ = sjson.SetBytes(out, "request.systemInstruction.parts.0.text", content.String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), content.String())
systemPartIndex++
} else if content.IsObject() && content.Get("type").String() == "text" { } else if content.IsObject() && content.Get("type").String() == "text" {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
out, _ = sjson.SetBytes(out, "request.systemInstruction.parts.0.text", content.Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), content.Get("text").String())
systemPartIndex++
} else if content.IsArray() { } else if content.IsArray() {
contents := content.Array() contents := content.Array()
if len(contents) > 0 { if len(contents) > 0 {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user")
for j := 0; j < len(contents); j++ { for j := 0; j < len(contents); j++ {
out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", j), contents[j].Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", systemPartIndex), contents[j].Get("text").String())
systemPartIndex++
} }
} }
} }

View File

@@ -129,6 +129,7 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
} }
} }
systemPartIndex := 0
for i := 0; i < len(arr); i++ { for i := 0; i < len(arr); i++ {
m := arr[i] m := arr[i]
role := m.Get("role").String() role := m.Get("role").String()
@@ -138,16 +139,19 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
// system -> system_instruction as a user message style // system -> system_instruction as a user message style
if content.Type == gjson.String { if content.Type == gjson.String {
out, _ = sjson.SetBytes(out, "system_instruction.role", "user") out, _ = sjson.SetBytes(out, "system_instruction.role", "user")
out, _ = sjson.SetBytes(out, "system_instruction.parts.0.text", content.String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("system_instruction.parts.%d.text", systemPartIndex), content.String())
systemPartIndex++
} else if content.IsObject() && content.Get("type").String() == "text" { } else if content.IsObject() && content.Get("type").String() == "text" {
out, _ = sjson.SetBytes(out, "system_instruction.role", "user") out, _ = sjson.SetBytes(out, "system_instruction.role", "user")
out, _ = sjson.SetBytes(out, "system_instruction.parts.0.text", content.Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("system_instruction.parts.%d.text", systemPartIndex), content.Get("text").String())
systemPartIndex++
} else if content.IsArray() { } else if content.IsArray() {
contents := content.Array() contents := content.Array()
if len(contents) > 0 { if len(contents) > 0 {
out, _ = sjson.SetBytes(out, "request.systemInstruction.role", "user") out, _ = sjson.SetBytes(out, "system_instruction.role", "user")
for j := 0; j < len(contents); j++ { for j := 0; j < len(contents); j++ {
out, _ = sjson.SetBytes(out, fmt.Sprintf("request.systemInstruction.parts.%d.text", j), contents[j].Get("text").String()) out, _ = sjson.SetBytes(out, fmt.Sprintf("system_instruction.parts.%d.text", systemPartIndex), contents[j].Get("text").String())
systemPartIndex++
} }
} }
} }