use super::core::finish;
use super::{AgentError, AgentResult};
use crate::listener::{AsyncEventListener, EventListener, Printer, RunEvent};
use crate::message::ToolCall;
use futures_util::{Stream, StreamExt};
use std::io::Write;
use std::pin::Pin;
use std::task::{Context, Poll};
#[derive(Debug)]
#[non_exhaustive]
pub enum AgentEvent<D> {
TextDelta(String),
ReasoningDelta(String),
ToolCall(ToolCall),
ToolResult {
call_id: String,
tool: String,
content: String,
is_error: bool,
},
Finished(AgentResult<D>),
}
#[must_use = "streams do nothing unless polled"]
pub struct AgentStream<D> {
inner: Pin<Box<dyn Stream<Item = Result<AgentEvent<D>, AgentError>> + Send>>,
}
impl<D> AgentStream<D> {
pub(super) fn new(
inner: impl Stream<Item = Result<AgentEvent<D>, AgentError>> + Send + 'static,
) -> Self {
Self {
inner: Box::pin(inner),
}
}
pub async fn finish(self) -> Result<AgentResult<D>, AgentError> {
finish(self).await
}
pub async fn listen(
mut self,
mut listener: impl EventListener,
) -> Result<AgentResult<D>, AgentError> {
while let Some(event) = self.inner.next().await {
let event = event?;
listener
.on_event(&event.as_run_event())
.map_err(AgentError::Listener)?;
if let AgentEvent::Finished(result) = event {
return Ok(result);
}
}
unreachable!("the agent loop ends with a result or an error")
}
pub async fn listen_async(
mut self,
mut listener: impl AsyncEventListener,
) -> Result<AgentResult<D>, AgentError> {
while let Some(event) = self.inner.next().await {
let event = event?;
listener
.on_event(&event.as_run_event())
.await
.map_err(AgentError::Listener)?;
if let AgentEvent::Finished(result) = event {
return Ok(result);
}
}
unreachable!("the agent loop ends with a result or an error")
}
pub async fn print_to(self, out: impl Write + Send) -> Result<AgentResult<D>, AgentError> {
self.listen(Printer::new(out)).await
}
pub async fn print_to_async(
self,
out: impl futures_util::io::AsyncWrite + Unpin + Send,
) -> Result<AgentResult<D>, AgentError> {
self.listen_async(Printer::new(out)).await
}
#[cfg(feature = "tokio")]
pub async fn print_to_tokio(
self,
out: impl tokio::io::AsyncWrite + Unpin + Send,
) -> Result<AgentResult<D>, AgentError> {
self.listen_async(Printer::tokio(out)).await
}
}
impl<D> AgentEvent<D> {
pub fn as_run_event(&self) -> RunEvent<'_> {
match self {
AgentEvent::TextDelta(text) => RunEvent::TextDelta(text),
AgentEvent::ReasoningDelta(text) => RunEvent::ReasoningDelta(text),
AgentEvent::ToolCall(call) => RunEvent::ToolCall(call),
AgentEvent::ToolResult {
call_id,
tool,
content,
is_error,
} => RunEvent::ToolResult {
call_id,
tool,
content,
is_error: *is_error,
},
AgentEvent::Finished(result) => RunEvent::Finished {
new_messages: &result.new_messages,
usage: &result.usage,
stop_reason: result.stop_reason.as_ref(),
iterations: result.iterations,
},
}
}
}
impl<D> Stream for AgentStream<D> {
type Item = Result<AgentEvent<D>, AgentError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.inner.as_mut().poll_next(cx)
}
}