diff --git a/src/providers/openai.rs b/src/providers/openai.rs index 2333d7a2..0e5c5839 100644 --- a/src/providers/openai.rs +++ b/src/providers/openai.rs @@ -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!({