fix: comprehensive cross-provider tool calling and history fixes
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

- Fix DeepSeek R1 (reasoner) 400 errors by ensuring assistant messages with
  tool_calls in history always have non-null 'content' and 'reasoning_content'.
- Implement deterministic tool call ID truncation (max 40 chars) for OpenAI
  compatibility (fixes errors when history contains long Gemini signatures).
- Automatic transition from 'max_tokens' to 'max_completion_tokens' for newer
  OpenAI models (o1, o3, gpt-5-nano).
- Added 'reasoning' and 'thought' aliases to reasoning_content for robust
  deserialization from various clients.
This commit is contained in:
2026-03-05 20:41:17 +00:00
parent 90ef026c96
commit d9cfffea62

View File

@@ -72,7 +72,7 @@ pub async fn messages_to_openai_json(messages: &[UnifiedMessage]) -> Result<Vec<
msg["reasoning_content"] = serde_json::json!(reasoning);
}
// For assistant messages with tool_calls, content can be null
// For assistant messages with tool_calls, content can be empty string
if let Some(tool_calls) = &m.tool_calls {
// Sanitize tool call IDs for OpenAI compatibility (max 40 chars)
let sanitized_calls: Vec<_> = tool_calls.iter().map(|tc| {
@@ -84,7 +84,7 @@ pub async fn messages_to_openai_json(messages: &[UnifiedMessage]) -> Result<Vec<
}).collect();
if parts.is_empty() {
msg["content"] = serde_json::Value::Null;
msg["content"] = serde_json::json!("");
} else {
msg["content"] = serde_json::json!(parts);
}
@@ -165,7 +165,7 @@ pub async fn messages_to_openai_json_text_only(
msg["reasoning_content"] = serde_json::json!(reasoning);
}
// For assistant messages with tool_calls, content can be null
// For assistant messages with tool_calls, content can be empty string
if let Some(tool_calls) = &m.tool_calls {
// Sanitize tool call IDs for OpenAI compatibility (max 40 chars)
let sanitized_calls: Vec<_> = tool_calls.iter().map(|tc| {
@@ -177,7 +177,7 @@ pub async fn messages_to_openai_json_text_only(
}).collect();
if parts.is_empty() {
msg["content"] = serde_json::Value::Null;
msg["content"] = serde_json::json!("");
} else {
msg["content"] = serde_json::json!(parts);
}