fix(openai): correctly parse Responses API tool call events
- The Responses API does not use 'response.item.delta' for tool calls. - It uses 'response.output_item.added' to initialize the function call. - It uses 'response.function_call_arguments.delta' for the payload stream. - Updated the streaming parser to catch these events and correctly yield ToolCallDelta objects. - This restores proper streaming of tool calls back to the client.
This commit is contained in:
@@ -757,15 +757,11 @@ impl super::Provider for OpenAIProvider {
|
|||||||
content_buffer.push_str(delta);
|
content_buffer.push_str(delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"response.item.delta" => {
|
"response.output_item.added" => {
|
||||||
if let Some(delta) = chunk.get("delta") {
|
if let Some(item) = chunk.get("item") {
|
||||||
let t = delta.get("type").and_then(|v| v.as_str()).unwrap_or("");
|
if item.get("type").and_then(|v| v.as_str()) == Some("function_call") {
|
||||||
if t == "function_call" {
|
let call_id = item.get("call_id").and_then(|v| v.as_str());
|
||||||
let call_id = delta.get("call_id")
|
let name = item.get("name").and_then(|v| v.as_str());
|
||||||
.or_else(|| chunk.get("item_id"))
|
|
||||||
.and_then(|v| v.as_str());
|
|
||||||
let name = delta.get("name").and_then(|v| v.as_str());
|
|
||||||
let arguments = delta.get("arguments").and_then(|v| v.as_str());
|
|
||||||
|
|
||||||
tool_calls = Some(vec![crate::models::ToolCallDelta {
|
tool_calls = Some(vec![crate::models::ToolCallDelta {
|
||||||
index: chunk.get("output_index").and_then(|v| v.as_u64()).unwrap_or(0) as u32,
|
index: chunk.get("output_index").and_then(|v| v.as_u64()).unwrap_or(0) as u32,
|
||||||
@@ -773,17 +769,26 @@ impl super::Provider for OpenAIProvider {
|
|||||||
call_type: Some("function".to_string()),
|
call_type: Some("function".to_string()),
|
||||||
function: Some(crate::models::FunctionCallDelta {
|
function: Some(crate::models::FunctionCallDelta {
|
||||||
name: name.map(|s| s.to_string()),
|
name: name.map(|s| s.to_string()),
|
||||||
arguments: arguments.map(|s| s.to_string()),
|
arguments: Some("".to_string()), // Start with empty arguments
|
||||||
}),
|
}),
|
||||||
}]);
|
}]);
|
||||||
} else if t == "message" {
|
|
||||||
if let Some(text) = delta.get("text").and_then(|v| v.as_str()) {
|
|
||||||
content_buffer.push_str(text);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"response.function_call_arguments.delta" => {
|
||||||
|
if let Some(delta) = chunk.get("delta").and_then(|v| v.as_str()) {
|
||||||
|
tool_calls = Some(vec![crate::models::ToolCallDelta {
|
||||||
|
index: chunk.get("output_index").and_then(|v| v.as_u64()).unwrap_or(0) as u32,
|
||||||
|
id: None,
|
||||||
|
call_type: None,
|
||||||
|
function: Some(crate::models::FunctionCallDelta {
|
||||||
|
name: None,
|
||||||
|
arguments: Some(delta.to_string()),
|
||||||
|
}),
|
||||||
|
}]);
|
||||||
}
|
}
|
||||||
"response.output_text.done" | "response.item.done" | "response.done" => {
|
}
|
||||||
|
"response.output_text.done" | "response.item.done" | "response.completed" => {
|
||||||
finish_reason = Some("stop".to_string());
|
finish_reason = Some("stop".to_string());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -890,12 +895,18 @@ impl super::Provider for OpenAIProvider {
|
|||||||
Ok(_) => continue,
|
Ok(_) => continue,
|
||||||
Err(reqwest_eventsource::Error::StreamEnded) => break,
|
Err(reqwest_eventsource::Error::StreamEnded) => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
tracing::error!("Responses stream encountered an error: {}", e);
|
||||||
// Attempt to probe for the actual error body
|
// Attempt to probe for the actual error body
|
||||||
|
let mut probe_body_no_stream = probe_body.clone();
|
||||||
|
if let Some(obj) = probe_body_no_stream.as_object_mut() {
|
||||||
|
obj.remove("stream");
|
||||||
|
}
|
||||||
|
|
||||||
let probe_resp = probe_client
|
let probe_resp = probe_client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.header("Authorization", format!("Bearer {}", api_key))
|
.header("Authorization", format!("Bearer {}", api_key))
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.json(&probe_body)
|
.json(&probe_body_no_stream)
|
||||||
.send()
|
.send()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user