fix(openai): implement role-based content mapping for Responses API
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled

This commit is contained in:
2026-03-17 18:23:50 +00:00
parent 5a9086b883
commit 754ee9cb84

View File

@@ -115,17 +115,20 @@ impl super::Provider for OpenAIProvider {
let role = m["role"].as_str().unwrap_or("user");
let mut content = m.get("content").cloned().unwrap_or(serde_json::json!([]));
// Map "text" -> "input_text" and "image_url" -> "input_image" for Responses API
// Map content types based on role for Responses API
if let Some(content_array) = content.as_array_mut() {
for part in content_array {
if let Some(part_obj) = part.as_object_mut() {
if let Some(t) = part_obj.get("type").and_then(|v| v.as_str()) {
match t {
"text" => {
part_obj.insert("type".to_string(), serde_json::json!("input_text"));
let new_type = if role == "assistant" { "output_text" } else { "input_text" };
part_obj.insert("type".to_string(), serde_json::json!(new_type));
}
"image_url" => {
part_obj.insert("type".to_string(), serde_json::json!("input_image"));
// Assistant typically doesn't have image_url in history this way, but for safety:
let new_type = if role == "assistant" { "output_image" } else { "input_image" };
part_obj.insert("type".to_string(), serde_json::json!(new_type));
if let Some(img_url) = part_obj.remove("image_url") {
part_obj.insert("image".to_string(), img_url);
}
@@ -136,7 +139,8 @@ impl super::Provider for OpenAIProvider {
}
}
} else if let Some(text) = content.as_str() {
content = serde_json::json!([{ "type": "input_text", "text": text }]);
let new_type = if role == "assistant" { "output_text" } else { "input_text" };
content = serde_json::json!([{ "type": new_type, "text": text }]);
}
input_parts.push(serde_json::json!({
@@ -350,17 +354,20 @@ impl super::Provider for OpenAIProvider {
let role = m["role"].as_str().unwrap_or("user");
let mut content = m.get("content").cloned().unwrap_or(serde_json::json!([]));
// Map "text" -> "input_text" and "image_url" -> "input_image" for Responses API
// Map content types based on role for Responses API
if let Some(content_array) = content.as_array_mut() {
for part in content_array {
if let Some(part_obj) = part.as_object_mut() {
if let Some(t) = part_obj.get("type").and_then(|v| v.as_str()) {
match t {
"text" => {
part_obj.insert("type".to_string(), serde_json::json!("input_text"));
let new_type = if role == "assistant" { "output_text" } else { "input_text" };
part_obj.insert("type".to_string(), serde_json::json!(new_type));
}
"image_url" => {
part_obj.insert("type".to_string(), serde_json::json!("input_image"));
// Assistant typically doesn't have image_url in history this way, but for safety:
let new_type = if role == "assistant" { "output_image" } else { "input_image" };
part_obj.insert("type".to_string(), serde_json::json!(new_type));
if let Some(img_url) = part_obj.remove("image_url") {
part_obj.insert("image".to_string(), img_url);
}
@@ -371,7 +378,8 @@ impl super::Provider for OpenAIProvider {
}
}
} else if let Some(text) = content.as_str() {
content = serde_json::json!([{ "type": "input_text", "text": text }]);
let new_type = if role == "assistant" { "output_text" } else { "input_text" };
content = serde_json::json!([{ "type": new_type, "text": text }]);
}
input_parts.push(serde_json::json!({