Fix OpenAI empty choices panic (#5248)

Signed-off-by: Arya Pratap Singh <notaryasingh@gmail.com>
This commit is contained in:
Arya Pratap Singh
2025-10-18 20:49:13 +05:30
committed by GitHub
parent c662039674
commit 3b04b2ebf4
+36 -17
View File
@@ -7,6 +7,7 @@ use crate::providers::utils::{
}; };
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use async_stream::try_stream; use async_stream::try_stream;
use chrono;
use futures::Stream; use futures::Stream;
use rmcp::model::{ use rmcp::model::{
object, AnnotateAble, CallToolRequestParam, Content, ErrorCode, ErrorData, RawContent, object, AnnotateAble, CallToolRequestParam, Content, ErrorCode, ErrorData, RawContent,
@@ -281,7 +282,18 @@ pub fn format_tools(tools: &[Tool]) -> anyhow::Result<Vec<Value>> {
/// Convert OpenAI's API response to internal Message format /// Convert OpenAI's API response to internal Message format
pub fn response_to_message(response: &Value) -> anyhow::Result<Message> { pub fn response_to_message(response: &Value) -> anyhow::Result<Message> {
let original = &response["choices"][0]["message"]; let Some(original) = response
.get("choices")
.and_then(|c| c.get(0))
.and_then(|m| m.get("message"))
else {
return Ok(Message::new(
Role::Assistant,
chrono::Utc::now().timestamp(),
Vec::new(),
));
};
let mut content = Vec::new(); let mut content = Vec::new();
if let Some(text) = original.get("content") { if let Some(text) = original.get("content") {
@@ -465,12 +477,14 @@ where
if chunk.choices.is_empty() { if chunk.choices.is_empty() {
yield (None, usage) yield (None, usage)
} else if let Some(tool_calls) = &chunk.choices[0].delta.tool_calls { } else if chunk.choices[0].delta.tool_calls.as_ref().is_some_and(|tc| !tc.is_empty()) {
let mut tool_call_data: std::collections::HashMap<i32, (String, String, String)> = std::collections::HashMap::new(); let mut tool_call_data: std::collections::HashMap<i32, (String, String, String)> = std::collections::HashMap::new();
for tool_call in tool_calls { if let Some(tool_calls) = &chunk.choices[0].delta.tool_calls {
if let (Some(index), Some(id), Some(name)) = (tool_call.index, &tool_call.id, &tool_call.function.name) { for tool_call in tool_calls {
tool_call_data.insert(index, (id.clone(), name.clone(), tool_call.function.arguments.clone())); if let (Some(index), Some(id), Some(name)) = (tool_call.index, &tool_call.id, &tool_call.function.name) {
tool_call_data.insert(index, (id.clone(), name.clone(), tool_call.function.arguments.clone()));
}
} }
} }
@@ -489,23 +503,27 @@ where
let tool_chunk: StreamingChunk = serde_json::from_str(line) let tool_chunk: StreamingChunk = serde_json::from_str(line)
.map_err(|e| anyhow!("Failed to parse streaming chunk: {}: {:?}", e, &line))?; .map_err(|e| anyhow!("Failed to parse streaming chunk: {}: {:?}", e, &line))?;
if let Some(delta_tool_calls) = &tool_chunk.choices[0].delta.tool_calls { if !tool_chunk.choices.is_empty() {
for delta_call in delta_tool_calls { if let Some(delta_tool_calls) = &tool_chunk.choices[0].delta.tool_calls {
if let Some(index) = delta_call.index { for delta_call in delta_tool_calls {
if let Some((_, _, ref mut args)) = tool_call_data.get_mut(&index) { if let Some(index) = delta_call.index {
args.push_str(&delta_call.function.arguments); if let Some((_, _, ref mut args)) = tool_call_data.get_mut(&index) {
} else if let (Some(id), Some(name)) = (&delta_call.id, &delta_call.function.name) { args.push_str(&delta_call.function.arguments);
tool_call_data.insert(index, (id.clone(), name.clone(), delta_call.function.arguments.clone())); } else if let (Some(id), Some(name)) = (&delta_call.id, &delta_call.function.name) {
tool_call_data.insert(index, (id.clone(), name.clone(), delta_call.function.arguments.clone()));
}
} }
} }
} else {
done = true;
}
if tool_chunk.choices[0].finish_reason == Some("tool_calls".to_string()) {
done = true;
} }
} else { } else {
done = true; done = true;
} }
if tool_chunk.choices[0].finish_reason == Some("tool_calls".to_string()) {
done = true;
}
} }
} else { } else {
break; break;
@@ -563,7 +581,8 @@ where
Some(msg), Some(msg),
usage, usage,
) )
} else if let Some(text) = &chunk.choices[0].delta.content { } else if chunk.choices[0].delta.content.is_some() {
let text = chunk.choices[0].delta.content.as_ref().unwrap();
let mut msg = Message::new( let mut msg = Message::new(
Role::Assistant, Role::Assistant,
chrono::Utc::now().timestamp(), chrono::Utc::now().timestamp(),