use std::collections::BTreeMap;
use crate::types::{ContentBlock, FinishReason, StreamEvent, TextBlock, ToolUseBlock};
use crate::{Error, Result};
#[derive(Debug, Default)]
pub struct PartialToolCall {
pub id: Option<String>,
pub name: Option<String>,
pub arguments: String,
}
pub struct StreamBuffers {
text: String,
reasoning: String,
capture_reasoning: bool,
tool_calls: BTreeMap<u32, PartialToolCall>,
finish_reason: Option<FinishReason>,
}
impl StreamBuffers {
pub fn new() -> Self {
Self {
text: String::new(),
reasoning: String::new(),
capture_reasoning: false,
tool_calls: BTreeMap::new(),
finish_reason: None,
}
}
pub fn set_capture_reasoning(&mut self, capture: bool) {
self.capture_reasoning = capture;
}
pub fn push_text(&mut self, text: &str) {
self.text.push_str(text);
}
pub fn push_reasoning(&mut self, reasoning: &str) {
if self.capture_reasoning {
self.reasoning.push_str(reasoning);
}
}
pub fn tool_call(&mut self, index: u32) -> &mut PartialToolCall {
self.tool_calls.entry(index).or_default()
}
pub fn open_tool_call(&mut self, index: u32) -> Option<&mut PartialToolCall> {
self.tool_calls.get_mut(&index)
}
pub fn record_finish(&mut self, reason: FinishReason) {
if self.finish_reason.is_none() {
self.finish_reason = Some(reason);
}
}
pub fn flush(&mut self) -> Result<Vec<StreamEvent>> {
let mut events = Vec::new();
if !self.reasoning.is_empty() {
events.push(StreamEvent::Reasoning(std::mem::take(&mut self.reasoning)));
}
if !self.text.is_empty() {
events.push(StreamEvent::Block(ContentBlock::Text(TextBlock::new(
std::mem::take(&mut self.text),
))));
}
for partial in std::mem::take(&mut self.tool_calls).into_values() {
let (Some(id), Some(name)) = (partial.id, partial.name) else {
continue;
};
let input = if partial.arguments.is_empty() {
serde_json::json!({})
} else {
serde_json::from_str(&partial.arguments).map_err(|error| {
Error::stream(format!(
"Failed to parse tool call arguments for '{name}': {error}"
))
})?
};
events.push(StreamEvent::Block(ContentBlock::ToolUse(
ToolUseBlock::new(id, name, input),
)));
}
Ok(events)
}
pub fn finalize(&mut self) -> Result<Vec<StreamEvent>> {
let mut events = self.flush()?;
events.push(StreamEvent::Finish(
self.finish_reason
.take()
.unwrap_or(FinishReason::Unspecified),
));
Ok(events)
}
}
impl Default for StreamBuffers {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests;