fix(providers): handle tool messages in text_only message converter
messages_to_openai_json_text_only() was missing tool-calling support, causing DeepSeek 400 errors when conversations included tool turns. Now mirrors messages_to_openai_json() logic for tool-role messages (tool_call_id, name) and assistant tool_calls, with images replaced by "[Image]" text.
This commit is contained in:
@@ -84,11 +84,41 @@ pub async fn messages_to_openai_json(messages: &[UnifiedMessage]) -> Result<Vec<
|
||||
/// Convert messages to OpenAI-compatible JSON, but replace images with a
|
||||
/// text placeholder "[Image]". Useful for providers that don't support
|
||||
/// multimodal in streaming mode or at all.
|
||||
///
|
||||
/// Handles tool-calling messages identically to `messages_to_openai_json`:
|
||||
/// assistant messages with `tool_calls`, and tool-role messages with
|
||||
/// `tool_call_id`/`name`.
|
||||
pub async fn messages_to_openai_json_text_only(
|
||||
messages: &[UnifiedMessage],
|
||||
) -> Result<Vec<serde_json::Value>, AppError> {
|
||||
let mut result = Vec::new();
|
||||
for m in messages {
|
||||
// Tool-role messages: { role: "tool", content: "...", tool_call_id: "...", name: "..." }
|
||||
if m.role == "tool" {
|
||||
let text_content = m
|
||||
.content
|
||||
.first()
|
||||
.map(|p| match p {
|
||||
ContentPart::Text { text } => text.clone(),
|
||||
ContentPart::Image(_) => "[Image]".to_string(),
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut msg = serde_json::json!({
|
||||
"role": "tool",
|
||||
"content": text_content
|
||||
});
|
||||
if let Some(tool_call_id) = &m.tool_call_id {
|
||||
msg["tool_call_id"] = serde_json::json!(tool_call_id);
|
||||
}
|
||||
if let Some(name) = &m.name {
|
||||
msg["name"] = serde_json::json!(name);
|
||||
}
|
||||
result.push(msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build content parts for non-tool messages (images become "[Image]" text)
|
||||
let mut parts = Vec::new();
|
||||
for p in &m.content {
|
||||
match p {
|
||||
@@ -100,10 +130,26 @@ pub async fn messages_to_openai_json_text_only(
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push(serde_json::json!({
|
||||
"role": m.role,
|
||||
"content": parts
|
||||
}));
|
||||
|
||||
let mut msg = serde_json::json!({ "role": m.role });
|
||||
|
||||
// For assistant messages with tool_calls, content can be null
|
||||
if let Some(tool_calls) = &m.tool_calls {
|
||||
if parts.is_empty() {
|
||||
msg["content"] = serde_json::Value::Null;
|
||||
} else {
|
||||
msg["content"] = serde_json::json!(parts);
|
||||
}
|
||||
msg["tool_calls"] = serde_json::json!(tool_calls);
|
||||
} else {
|
||||
msg["content"] = serde_json::json!(parts);
|
||||
}
|
||||
|
||||
if let Some(name) = &m.name {
|
||||
msg["name"] = serde_json::json!(name);
|
||||
}
|
||||
|
||||
result.push(msg);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user