fix(openai): correctly map tool_call indexes in Responses API stream
- The OpenAI Responses API uses 'output_index' to identify items in the response. - If a response starts with text (output_index 0) followed by a tool call (output_index 1), the standard Chat Completions streaming format requires the first tool call to have index 0. - Previously, the proxy was passing output_index (1) as the tool_call index, causing client-side SDKs to fail parsing the stream and silently drop the tool calls. - Implemented a local mapping within the stream to ensure tool_call indexes are always dense and start at 0.
This commit is contained in:
@@ -731,6 +731,8 @@ impl super::Provider for OpenAIProvider {
|
|||||||
let mut es = es;
|
let mut es = es;
|
||||||
let mut content_buffer = String::new();
|
let mut content_buffer = String::new();
|
||||||
let mut has_tool_calls = false;
|
let mut has_tool_calls = false;
|
||||||
|
let mut tool_index_map = std::collections::HashMap::<u32, u32>::new();
|
||||||
|
let mut next_tool_index = 0u32;
|
||||||
|
|
||||||
while let Some(event) = es.next().await {
|
while let Some(event) = es.next().await {
|
||||||
match event {
|
match event {
|
||||||
@@ -765,8 +767,15 @@ impl super::Provider for OpenAIProvider {
|
|||||||
let call_id = item.get("call_id").and_then(|v| v.as_str());
|
let call_id = item.get("call_id").and_then(|v| v.as_str());
|
||||||
let name = item.get("name").and_then(|v| v.as_str());
|
let name = item.get("name").and_then(|v| v.as_str());
|
||||||
|
|
||||||
|
let out_idx = chunk.get("output_index").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
|
||||||
|
let tc_idx = *tool_index_map.entry(out_idx).or_insert_with(|| {
|
||||||
|
let i = next_tool_index;
|
||||||
|
next_tool_index += 1;
|
||||||
|
i
|
||||||
|
});
|
||||||
|
|
||||||
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: tc_idx,
|
||||||
id: call_id.map(|s| s.to_string()),
|
id: call_id.map(|s| s.to_string()),
|
||||||
call_type: Some("function".to_string()),
|
call_type: Some("function".to_string()),
|
||||||
function: Some(crate::models::FunctionCallDelta {
|
function: Some(crate::models::FunctionCallDelta {
|
||||||
@@ -780,8 +789,16 @@ impl super::Provider for OpenAIProvider {
|
|||||||
"response.function_call_arguments.delta" => {
|
"response.function_call_arguments.delta" => {
|
||||||
if let Some(delta) = chunk.get("delta").and_then(|v| v.as_str()) {
|
if let Some(delta) = chunk.get("delta").and_then(|v| v.as_str()) {
|
||||||
has_tool_calls = true;
|
has_tool_calls = true;
|
||||||
|
|
||||||
|
let out_idx = chunk.get("output_index").and_then(|v| v.as_u64()).unwrap_or(0) as u32;
|
||||||
|
let tc_idx = *tool_index_map.entry(out_idx).or_insert_with(|| {
|
||||||
|
let i = next_tool_index;
|
||||||
|
next_tool_index += 1;
|
||||||
|
i
|
||||||
|
});
|
||||||
|
|
||||||
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: tc_idx,
|
||||||
id: None,
|
id: None,
|
||||||
call_type: None,
|
call_type: None,
|
||||||
function: Some(crate::models::FunctionCallDelta {
|
function: Some(crate::models::FunctionCallDelta {
|
||||||
|
|||||||
Reference in New Issue
Block a user