fix(translator): improve tool response handling for non-string content
- Added `setToolCallOutputContent` to process various content types, including arrays and fallback cases. - Implemented robust handling for specific tool output types like text, image URLs, and files, ensuring proper serialization. - Improved fallback logic to handle unexpected or missing data. Fixed: #2313 Closes: #2349
This commit is contained in:
@@ -121,13 +121,13 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b
|
||||
case "tool":
|
||||
// Handle tool response messages as top-level function_call_output objects
|
||||
toolCallID := m.Get("tool_call_id").String()
|
||||
content := m.Get("content").String()
|
||||
content := m.Get("content")
|
||||
|
||||
// Create function_call_output object
|
||||
funcOutput := []byte(`{}`)
|
||||
funcOutput, _ = sjson.SetBytes(funcOutput, "type", "function_call_output")
|
||||
funcOutput, _ = sjson.SetBytes(funcOutput, "call_id", toolCallID)
|
||||
funcOutput, _ = sjson.SetBytes(funcOutput, "output", content)
|
||||
funcOutput = setToolCallOutputContent(funcOutput, content)
|
||||
out, _ = sjson.SetRawBytes(out, "input.-1", funcOutput)
|
||||
|
||||
default:
|
||||
@@ -359,6 +359,91 @@ func ConvertOpenAIRequestToCodex(modelName string, inputRawJSON []byte, stream b
|
||||
return out
|
||||
}
|
||||
|
||||
func setToolCallOutputContent(funcOutput []byte, content gjson.Result) []byte {
|
||||
switch {
|
||||
case content.Type == gjson.String:
|
||||
funcOutput, _ = sjson.SetBytes(funcOutput, "output", content.String())
|
||||
case content.IsArray():
|
||||
output := []byte(`[]`)
|
||||
for _, item := range content.Array() {
|
||||
output = appendToolOutputContentPart(output, item)
|
||||
}
|
||||
funcOutput, _ = sjson.SetRawBytes(funcOutput, "output", output)
|
||||
default:
|
||||
fallbackOutput := content.Raw
|
||||
if fallbackOutput == "" {
|
||||
fallbackOutput = content.String()
|
||||
}
|
||||
funcOutput, _ = sjson.SetBytes(funcOutput, "output", fallbackOutput)
|
||||
}
|
||||
return funcOutput
|
||||
}
|
||||
|
||||
func appendToolOutputContentPart(output []byte, item gjson.Result) []byte {
|
||||
switch item.Get("type").String() {
|
||||
case "text":
|
||||
part := []byte(`{}`)
|
||||
part, _ = sjson.SetBytes(part, "type", "input_text")
|
||||
part, _ = sjson.SetBytes(part, "text", item.Get("text").String())
|
||||
output, _ = sjson.SetRawBytes(output, "-1", part)
|
||||
case "image_url":
|
||||
imageURL := item.Get("image_url.url").String()
|
||||
fileID := item.Get("image_url.file_id").String()
|
||||
if imageURL == "" && fileID == "" {
|
||||
return appendToolOutputFallbackPart(output, item)
|
||||
}
|
||||
part := []byte(`{}`)
|
||||
part, _ = sjson.SetBytes(part, "type", "input_image")
|
||||
if imageURL != "" {
|
||||
part, _ = sjson.SetBytes(part, "image_url", imageURL)
|
||||
}
|
||||
if fileID != "" {
|
||||
part, _ = sjson.SetBytes(part, "file_id", fileID)
|
||||
}
|
||||
if detail := item.Get("image_url.detail").String(); detail != "" {
|
||||
part, _ = sjson.SetBytes(part, "detail", detail)
|
||||
}
|
||||
output, _ = sjson.SetRawBytes(output, "-1", part)
|
||||
case "file":
|
||||
fileID := item.Get("file.file_id").String()
|
||||
fileData := item.Get("file.file_data").String()
|
||||
fileURL := item.Get("file.file_url").String()
|
||||
if fileID == "" && fileData == "" && fileURL == "" {
|
||||
return appendToolOutputFallbackPart(output, item)
|
||||
}
|
||||
part := []byte(`{}`)
|
||||
part, _ = sjson.SetBytes(part, "type", "input_file")
|
||||
if fileID != "" {
|
||||
part, _ = sjson.SetBytes(part, "file_id", fileID)
|
||||
}
|
||||
if fileData != "" {
|
||||
part, _ = sjson.SetBytes(part, "file_data", fileData)
|
||||
}
|
||||
if fileURL != "" {
|
||||
part, _ = sjson.SetBytes(part, "file_url", fileURL)
|
||||
}
|
||||
if filename := item.Get("file.filename").String(); filename != "" {
|
||||
part, _ = sjson.SetBytes(part, "filename", filename)
|
||||
}
|
||||
output, _ = sjson.SetRawBytes(output, "-1", part)
|
||||
default:
|
||||
output = appendToolOutputFallbackPart(output, item)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func appendToolOutputFallbackPart(output []byte, item gjson.Result) []byte {
|
||||
text := item.Raw
|
||||
if text == "" {
|
||||
text = item.String()
|
||||
}
|
||||
part := []byte(`{}`)
|
||||
part, _ = sjson.SetBytes(part, "type", "input_text")
|
||||
part, _ = sjson.SetBytes(part, "text", text)
|
||||
output, _ = sjson.SetRawBytes(output, "-1", part)
|
||||
return output
|
||||
}
|
||||
|
||||
// shortenNameIfNeeded applies the simple shortening rule for a single name.
|
||||
// If the name length exceeds 64, it will try to preserve the "mcp__" prefix and last segment.
|
||||
// Otherwise it truncates to 64 characters.
|
||||
|
||||
Reference in New Issue
Block a user