diff --git a/rig-bedrock/src/streaming.rs b/rig-bedrock/src/streaming.rs index c7daef022..004c7e2f8 100644 --- a/rig-bedrock/src/streaming.rs +++ b/rig-bedrock/src/streaming.rs @@ -107,10 +107,9 @@ impl CompletionModel { } if !text.is_empty() { - yield Ok(RawStreamingChoice::Reasoning { + yield Ok(RawStreamingChoice::ReasoningDelta { reasoning: text.clone(), id: None, - signature: None, }) } }, diff --git a/rig-core/src/agent/prompt_request/streaming.rs b/rig-core/src/agent/prompt_request/streaming.rs index cf9407be7..d2fcf375d 100644 --- a/rig-core/src/agent/prompt_request/streaming.rs +++ b/rig-core/src/agent/prompt_request/streaming.rs @@ -359,6 +359,10 @@ where yield Ok(MultiTurnStreamItem::stream_item(StreamedAssistantContent::Reasoning(rig::message::Reasoning { reasoning, id, signature }))); did_call_tool = false; }, + Ok(StreamedAssistantContent::ReasoningDelta { reasoning, id }) => { + yield Ok(MultiTurnStreamItem::stream_item(StreamedAssistantContent::ReasoningDelta { reasoning, id })); + did_call_tool = false; + }, Ok(StreamedAssistantContent::Final(final_resp)) => { if let Some(usage) = final_resp.token_usage() { aggregated_usage += usage; }; if is_text_response { diff --git a/rig-core/src/providers/anthropic/streaming.rs b/rig-core/src/providers/anthropic/streaming.rs index f98882862..1ebe012e2 100644 --- a/rig-core/src/providers/anthropic/streaming.rs +++ b/rig-core/src/providers/anthropic/streaming.rs @@ -323,10 +323,9 @@ fn handle_event( state.thinking.push_str(thinking); } - Some(Ok(RawStreamingChoice::Reasoning { + Some(Ok(RawStreamingChoice::ReasoningDelta { id: None, reasoning: thinking.clone(), - signature: None, })) } ContentDelta::SignatureDelta { signature } => { @@ -504,11 +503,11 @@ mod tests { let choice = result.unwrap().unwrap(); match choice { - RawStreamingChoice::Reasoning { id, reasoning, .. } => { + RawStreamingChoice::ReasoningDelta { id, reasoning, .. } => { assert_eq!(id, None); assert_eq!(reasoning, "Analyzing the request..."); } - _ => panic!("Expected Reasoning choice"), + _ => panic!("Expected ReasoningDelta choice"), } // Verify thinking state was updated @@ -584,10 +583,10 @@ mod tests { let choice = result.unwrap().unwrap(); match choice { - RawStreamingChoice::Reasoning { reasoning, .. } => { + RawStreamingChoice::ReasoningDelta { reasoning, .. } => { assert_eq!(reasoning, "Thinking while tool is active..."); } - _ => panic!("Expected Reasoning choice"), + _ => panic!("Expected ReasoningDelta choice"), } // Tool call state should remain unchanged diff --git a/rig-core/src/providers/deepseek.rs b/rig-core/src/providers/deepseek.rs index 2b1d42b64..5ab673d86 100644 --- a/rig-core/src/providers/deepseek.rs +++ b/rig-core/src/providers/deepseek.rs @@ -879,10 +879,9 @@ where // DeepSeek-specific reasoning stream if let Some(content) = &delta.reasoning_content { - yield Ok(crate::streaming::RawStreamingChoice::Reasoning { - reasoning: content.to_string(), + yield Ok(crate::streaming::RawStreamingChoice::ReasoningDelta { id: None, - signature: None, + reasoning: content.to_string() }); } diff --git a/rig-core/src/providers/gemini/streaming.rs b/rig-core/src/providers/gemini/streaming.rs index 5c6295125..aa7894de0 100644 --- a/rig-core/src/providers/gemini/streaming.rs +++ b/rig-core/src/providers/gemini/streaming.rs @@ -158,7 +158,10 @@ where thought: Some(true), .. } => { - yield Ok(streaming::RawStreamingChoice::Reasoning { reasoning: text.clone(), id: None, signature: None }); + yield Ok(streaming::RawStreamingChoice::ReasoningDelta { + id: None, + reasoning: text.clone(), + }); }, Part { part: PartKind::Text(text), diff --git a/rig-core/src/providers/groq.rs b/rig-core/src/providers/groq.rs index ed7327a82..766c249ea 100644 --- a/rig-core/src/providers/groq.rs +++ b/rig-core/src/providers/groq.rs @@ -780,10 +780,9 @@ where if let Some(choice) = data.choices.first() { match &choice.delta { StreamingDelta::Reasoning { reasoning } => { - yield Ok(crate::streaming::RawStreamingChoice::Reasoning { + yield Ok(crate::streaming::RawStreamingChoice::ReasoningDelta { id: None, reasoning: reasoning.to_string(), - signature: None, }); } diff --git a/rig-core/src/providers/ollama.rs b/rig-core/src/providers/ollama.rs index 64e42460e..828480488 100644 --- a/rig-core/src/providers/ollama.rs +++ b/rig-core/src/providers/ollama.rs @@ -738,10 +738,9 @@ where if let Some(thinking_content) = thinking && !thinking_content.is_empty() { thinking_response += &thinking_content; - yield RawStreamingChoice::Reasoning { - reasoning: thinking_content, + yield RawStreamingChoice::ReasoningDelta { id: None, - signature: None, + reasoning: thinking_content, }; } diff --git a/rig-core/src/providers/openai/responses_api/streaming.rs b/rig-core/src/providers/openai/responses_api/streaming.rs index 4c0186f3e..d78fe9b3f 100644 --- a/rig-core/src/providers/openai/responses_api/streaming.rs +++ b/rig-core/src/providers/openai/responses_api/streaming.rs @@ -117,8 +117,8 @@ pub enum ItemChunkKind { ReasoningSummaryPartAdded(SummaryPartChunk), #[serde(rename = "response.reasoning_summary_part.done")] ReasoningSummaryPartDone(SummaryPartChunk), - #[serde(rename = "response.reasoning_summary_text.added")] - ReasoningSummaryTextAdded(SummaryTextChunk), + #[serde(rename = "response.reasoning_summary_text.delta")] + ReasoningSummaryTextDelta(SummaryTextChunk), #[serde(rename = "response.reasoning_summary_text.done")] ReasoningSummaryTextDone(SummaryTextChunk), } @@ -295,7 +295,11 @@ where }) .collect::>() .join("\n"); - yield Ok(streaming::RawStreamingChoice::Reasoning { reasoning, id: Some(id.to_string()), signature: None }) + yield Ok(streaming::RawStreamingChoice::Reasoning { + id: Some(id.to_string()), + reasoning, + signature: None, + }) } _ => continue } @@ -304,6 +308,9 @@ where combined_text.push_str(&delta.delta); yield Ok(streaming::RawStreamingChoice::Message(delta.delta.clone())) } + ItemChunkKind::ReasoningSummaryTextDelta(delta) => { + yield Ok(streaming::RawStreamingChoice::ReasoningDelta { id: None, reasoning: delta.delta.clone() }) + } ItemChunkKind::RefusalDelta(delta) => { combined_text.push_str(&delta.delta); yield Ok(streaming::RawStreamingChoice::Message(delta.delta.clone())) diff --git a/rig-core/src/providers/openrouter/streaming.rs b/rig-core/src/providers/openrouter/streaming.rs index b07c8761c..f79722af7 100644 --- a/rig-core/src/providers/openrouter/streaming.rs +++ b/rig-core/src/providers/openrouter/streaming.rs @@ -252,10 +252,9 @@ where // Streamed reasoning content if let Some(reasoning) = &delta.reasoning && !reasoning.is_empty() { - yield Ok(streaming::RawStreamingChoice::Reasoning { + yield Ok(streaming::RawStreamingChoice::ReasoningDelta { reasoning: reasoning.clone(), id: None, - signature: None, }); } diff --git a/rig-core/src/streaming.rs b/rig-core/src/streaming.rs index f3f90dae5..465719b0f 100644 --- a/rig-core/src/streaming.rs +++ b/rig-core/src/streaming.rs @@ -79,12 +79,17 @@ where }, /// A tool call partial/delta ToolCallDelta { id: String, delta: String }, - /// A reasoning chunk + /// A reasoning (in its entirety) Reasoning { id: Option, reasoning: String, signature: Option, }, + /// A reasoning partial/delta + ReasoningDelta { + id: Option, + reasoning: String, + }, /// The final response object, must be yielded if you want the /// `response` field to be populated on the `StreamingCompletionResponse` @@ -231,15 +236,19 @@ where id, reasoning, signature, - } => { + } => Poll::Ready(Some(Ok(StreamedAssistantContent::Reasoning(Reasoning { + id, + reasoning: vec![reasoning], + signature, + })))), + RawStreamingChoice::ReasoningDelta { id, reasoning } => { // Forward the streaming tokens to the outer stream // and concat the text together stream.reasoning = format!("{}{}", stream.reasoning, reasoning); - Poll::Ready(Some(Ok(StreamedAssistantContent::Reasoning(Reasoning { + Poll::Ready(Some(Ok(StreamedAssistantContent::ReasoningDelta { id, - reasoning: vec![reasoning], - signature, - })))) + reasoning, + }))) } RawStreamingChoice::ToolCall { id, @@ -362,6 +371,12 @@ impl Stream for StreamingResultDyn { reasoning, signature, }))), + RawStreamingChoice::ReasoningDelta { id, reasoning } => { + Poll::Ready(Some(Ok(RawStreamingChoice::ReasoningDelta { + id, + reasoning, + }))) + } RawStreamingChoice::ToolCall { id, name, @@ -514,6 +529,10 @@ mod tests { print!("{reasoning}"); std::io::Write::flush(&mut std::io::stdout()).unwrap(); } + Ok(StreamedAssistantContent::ReasoningDelta { reasoning, .. }) => { + println!("Reasoning delta: {reasoning}"); + chunk_count += 1; + } Err(e) => { eprintln!("Error: {e:?}"); break; @@ -555,8 +574,15 @@ mod tests { pub enum StreamedAssistantContent { Text(Text), ToolCall(ToolCall), - ToolCallDelta { id: String, delta: String }, + ToolCallDelta { + id: String, + delta: String, + }, Reasoning(Reasoning), + ReasoningDelta { + id: Option, + reasoning: String, + }, Final(R), }