1use std::{
2 collections::VecDeque,
3 future::Future,
4 pin::Pin,
5 sync::{Arc, Mutex, MutexGuard},
6 task::{Context, Poll},
7};
8
9use futures_core::Stream;
10use runifold_core::Usage;
11use runifold_model::{ModelStreamEvent, ToolCall};
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 AgentError, AgentFuture, AgentOutcome, TerminalRequirementFailure, TerminalReviewVerdictKind,
16};
17
18#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[non_exhaustive]
21pub enum CallableKind {
22 Tool,
24 Agent,
26}
27
28#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
30#[non_exhaustive]
31pub enum AgentStreamEvent {
32 Started {
34 agent: String,
36 },
37 TurnStarted {
39 turn: u32,
41 },
42 Model {
44 turn: u32,
46 event: ModelStreamEvent,
48 },
49 ContextRetrieved {
51 source: String,
53 documents: usize,
55 },
56 CallableStarted {
58 turn: u32,
60 kind: CallableKind,
62 call: ToolCall,
64 },
65 CallableCompleted {
67 turn: u32,
69 kind: CallableKind,
71 call_id: String,
73 name: String,
75 success: bool,
77 },
78 UsageUpdated {
80 usage: Usage,
82 },
83 Completed {
85 outcome: AgentOutcome,
87 },
88 TerminalRepairScheduled {
90 attempt: u32,
92 failure: TerminalRequirementFailure,
94 },
95 TurnReviewStarted {
97 turn: u32,
99 },
100 TurnReviewCompleted {
102 turn: u32,
104 verdict: TerminalReviewVerdictKind,
106 },
107 TurnReviewRepairScheduled {
109 attempt: u32,
111 turn: u32,
113 },
114 TerminalReviewStarted {
116 attempt: u32,
118 },
119 TerminalReviewCompleted {
121 attempt: u32,
123 verdict: TerminalReviewVerdictKind,
125 },
126 TerminalReviewRepairScheduled {
128 attempt: u32,
130 },
131}
132
133pub(crate) trait AgentObserver: Send + Sync {
134 fn emit(&self, event: AgentStreamEvent);
135
136 fn backpressured(&self) -> bool {
137 false
138 }
139}
140
141#[derive(Debug)]
142pub(crate) struct NoopObserver;
143
144impl AgentObserver for NoopObserver {
145 fn emit(&self, _event: AgentStreamEvent) {}
146}
147
148#[derive(Clone, Debug, Default)]
149pub(crate) struct BufferedObserver {
150 events: Arc<Mutex<VecDeque<AgentStreamEvent>>>,
151}
152
153impl BufferedObserver {
154 pub(crate) fn events(&self) -> Arc<Mutex<VecDeque<AgentStreamEvent>>> {
155 self.events.clone()
156 }
157}
158
159impl AgentObserver for BufferedObserver {
160 fn emit(&self, event: AgentStreamEvent) {
161 self.events
162 .lock()
163 .unwrap_or_else(std::sync::PoisonError::into_inner)
164 .push_back(event);
165 }
166
167 fn backpressured(&self) -> bool {
168 true
169 }
170}
171
172pub(crate) async fn emit_agent_event(observer: &dyn AgentObserver, event: AgentStreamEvent) {
173 observer.emit(event);
174 if observer.backpressured() {
175 YieldOnce::new().await;
176 }
177}
178
179struct YieldOnce {
180 yielded: bool,
181}
182
183impl YieldOnce {
184 const fn new() -> Self {
185 Self { yielded: false }
186 }
187}
188
189impl Future for YieldOnce {
190 type Output = ();
191
192 fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
193 if self.yielded {
194 Poll::Ready(())
195 } else {
196 self.yielded = true;
197 context.waker().wake_by_ref();
198 Poll::Pending
199 }
200 }
201}
202
203#[must_use = "streams do nothing unless polled"]
205pub struct AgentEventStream<'a> {
206 execution: Option<AgentFuture<'a, Result<AgentOutcome, AgentError>>>,
207 events: Arc<Mutex<VecDeque<AgentStreamEvent>>>,
208 failure: Option<AgentError>,
209 finished: bool,
210}
211
212impl<'a> AgentEventStream<'a> {
213 pub(crate) fn new(
214 execution: AgentFuture<'a, Result<AgentOutcome, AgentError>>,
215 events: Arc<Mutex<VecDeque<AgentStreamEvent>>>,
216 ) -> Self {
217 Self {
218 execution: Some(execution),
219 events,
220 failure: None,
221 finished: false,
222 }
223 }
224
225 fn events(&self) -> MutexGuard<'_, VecDeque<AgentStreamEvent>> {
226 self.events
227 .lock()
228 .unwrap_or_else(std::sync::PoisonError::into_inner)
229 }
230
231 fn pop_event(&self) -> Option<AgentStreamEvent> {
232 self.events().pop_front()
233 }
234}
235
236impl Stream for AgentEventStream<'_> {
237 type Item = Result<AgentStreamEvent, AgentError>;
238
239 fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
240 let this = self.get_mut();
241 if let Some(event) = this.pop_event() {
242 return Poll::Ready(Some(Ok(event)));
243 }
244 if let Some(execution) = this.execution.as_mut() {
245 match execution.as_mut().poll(context) {
246 Poll::Pending => {
247 return this
248 .pop_event()
249 .map_or(Poll::Pending, |event| Poll::Ready(Some(Ok(event))));
250 }
251 Poll::Ready(Ok(_outcome)) => {
252 this.execution = None;
253 }
254 Poll::Ready(Err(error)) => {
255 this.execution = None;
256 this.failure = Some(error);
257 }
258 }
259 }
260 if let Some(event) = this.pop_event() {
261 return Poll::Ready(Some(Ok(event)));
262 }
263 if let Some(error) = this.failure.take() {
264 return Poll::Ready(Some(Err(error)));
265 }
266 if this.execution.is_none() {
267 this.finished = true;
268 }
269 if this.finished {
270 Poll::Ready(None)
271 } else {
272 Poll::Pending
273 }
274 }
275}
276
277impl std::fmt::Debug for AgentEventStream<'_> {
278 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279 formatter
280 .debug_struct("AgentEventStream")
281 .field("queued_events", &self.events().len())
282 .field("has_execution", &self.execution.is_some())
283 .field("has_failure", &self.failure.is_some())
284 .field("finished", &self.finished)
285 .finish()
286 }
287}