ferrin_core/stream_text/
result.rs1use std::collections::VecDeque;
4use std::fmt;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8use std::task::Context;
9use std::task::Poll;
10
11use ferrin_spec::BoxStream;
12use ferrin_spec::PartId;
13use futures_core::Stream;
14use futures_util::StreamExt;
15use futures_util::stream;
16use serde::de::DeserializeOwned;
17use tokio::sync::oneshot;
18
19use super::StreamEvent;
20use crate::error::Error;
21use crate::generate_text::GenerateTextResult;
22use crate::output::ArrayElements;
23use crate::output::OutputHandler;
24use crate::output::PartialOutput;
25
26pub type EventStream = BoxStream<'static, StreamEvent>;
28
29pub struct Completion<O> {
35 receiver: oneshot::Receiver<Result<GenerateTextResult<O>, Error>>,
36}
37
38impl<O> Completion<O> {
39 pub(crate) fn new(receiver: oneshot::Receiver<Result<GenerateTextResult<O>, Error>>) -> Self {
40 Self { receiver }
41 }
42}
43
44impl<O> Future for Completion<O> {
45 type Output = Result<GenerateTextResult<O>, Error>;
46
47 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
48 match Pin::new(&mut self.receiver).poll(cx) {
49 Poll::Ready(Ok(result)) => Poll::Ready(result),
50 Poll::Ready(Err(_)) => Poll::Ready(Err(Error::Cancelled)),
51 Poll::Pending => Poll::Pending,
52 }
53 }
54}
55
56impl<O> fmt::Debug for Completion<O> {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 f.write_str("Completion(..)")
59 }
60}
61
62pub struct StreamTextResult<O> {
68 pub(crate) call_id: String,
69 pub(crate) events: EventStream,
70 pub(crate) completion: Completion<O>,
71 pub(crate) output: Arc<dyn OutputHandler<O>>,
72}
73
74impl<O> fmt::Debug for StreamTextResult<O> {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("StreamTextResult")
77 .field("call_id", &self.call_id)
78 .finish_non_exhaustive()
79 }
80}
81
82impl<O> StreamTextResult<O> {
83 #[must_use]
85 pub fn call_id(&self) -> &str {
86 &self.call_id
87 }
88
89 #[must_use]
91 pub fn split(self) -> (EventStream, Completion<O>) {
92 (self.events, self.completion)
93 }
94
95 pub fn events(&mut self) -> &mut EventStream {
97 &mut self.events
98 }
99
100 pub async fn consume(self) -> Result<GenerateTextResult<O>, Error> {
106 let (mut events, completion) = self.split();
107 while events.next().await.is_some() {}
108 completion.await
109 }
110}
111
112impl<O: Send + 'static> StreamTextResult<O> {
113 pub fn text_stream(self) -> impl Stream<Item = Result<String, Error>> + Send {
116 stream::unfold(Some((self.events, self.completion)), |state| async move {
117 let (mut events, completion) = state?;
118 loop {
119 match events.next().await {
120 Some(StreamEvent::TextDelta { text, .. }) => {
121 return Some((Ok(text), Some((events, completion))));
122 }
123 Some(_) => {}
124 None => {
125 return match completion.await {
126 Ok(_) => None,
127 Err(error) => Some((Err(error), None)),
128 };
129 }
130 }
131 }
132 })
133 }
134
135 pub fn partial_output_stream(self) -> impl Stream<Item = PartialOutput<O>> + Send {
141 let state = PartialState {
142 events: self.events,
143 handler: self.output,
144 first_text: None,
145 text: String::new(),
146 last: None,
147 };
148 stream::unfold(Some(state), |state| async move {
149 let mut state = state?;
150 loop {
151 let event = state.events.next().await?;
152 if let Some(output) = state.handle(&event) {
153 return Some((output, Some(state)));
154 }
155 }
156 })
157 }
158}
159
160struct PartialState<O> {
161 events: EventStream,
162 handler: Arc<dyn OutputHandler<O>>,
163 first_text: Option<PartId>,
164 text: String,
165 last: Option<String>,
166}
167
168impl<O: 'static> PartialState<O> {
169 fn reset(&mut self) {
170 self.first_text = None;
171 self.text.clear();
172 self.last = None;
173 }
174
175 fn handle(&mut self, event: &StreamEvent) -> Option<PartialOutput<O>> {
176 match event {
177 StreamEvent::StartStep { .. } | StreamEvent::RetryAttempt { .. } => {
178 self.reset();
179 None
180 }
181 StreamEvent::TextStart { id, .. } => {
182 if self.first_text.is_none() {
183 self.first_text = Some(id.clone());
184 }
185 None
186 }
187 StreamEvent::TextDelta { id, text, .. } => {
188 if self.first_text.as_ref() != Some(id) || text.is_empty() {
189 return None;
190 }
191 self.text.push_str(text);
192 let value = self.handler.parse_partial(&self.text)?;
193 let serialized = value.to_string();
194 if self.last.as_deref() == Some(serialized.as_str()) {
195 return None;
196 }
197 self.last = Some(serialized);
198 let typed = self.handler.typed_partial(&value);
199 Some(PartialOutput { value, typed })
200 }
201 _ => None,
202 }
203 }
204}
205
206impl<O> StreamTextResult<O>
207where
208 O: ArrayElements + Send + 'static,
209 O::Element: DeserializeOwned + Send,
210{
211 pub fn element_stream(self) -> impl Stream<Item = Result<O::Element, Error>> + Send {
214 let state = ElementState {
215 events: self.events,
216 completion: Some(self.completion),
217 handler: self.output,
218 first_text: None,
219 text: String::new(),
220 published: 0,
221 pending: VecDeque::new(),
222 };
223 stream::unfold(Some(state), |state| async move {
224 let mut state = state?;
225 loop {
226 if let Some(item) = state.pending.pop_front() {
227 return Some((item, Some(state)));
228 }
229 match state.events.next().await {
230 Some(event) => state.handle(&event),
231 None => {
232 let completion = state.completion.take()?;
233 return match completion.await {
234 Ok(_) => None,
235 Err(error) => Some((Err(error), None)),
236 };
237 }
238 }
239 }
240 })
241 }
242}
243
244struct ElementState<O: ArrayElements> {
245 events: EventStream,
246 completion: Option<Completion<O>>,
247 handler: Arc<dyn OutputHandler<O>>,
248 first_text: Option<PartId>,
249 text: String,
250 published: usize,
251 pending: VecDeque<Result<O::Element, Error>>,
252}
253
254impl<O> ElementState<O>
255where
256 O: ArrayElements + 'static,
257 O::Element: DeserializeOwned,
258{
259 fn handle(&mut self, event: &StreamEvent) {
260 match event {
261 StreamEvent::StartStep { .. } | StreamEvent::RetryAttempt { .. } => {
262 self.first_text = None;
263 self.text.clear();
264 self.published = 0;
265 }
266 StreamEvent::TextStart { id, .. } => {
267 if self.first_text.is_none() {
268 self.first_text = Some(id.clone());
269 }
270 }
271 StreamEvent::TextDelta { id, text, .. } => {
272 if self.first_text.as_ref() != Some(id) || text.is_empty() {
273 return;
274 }
275 self.text.push_str(text);
276 let Some(elements) = self.handler.parse_elements(&self.text) else {
277 return;
278 };
279 for element in elements.into_iter().skip(self.published) {
280 self.published += 1;
281 self.pending
282 .push_back(serde_json::from_value(element).map_err(Error::other));
283 }
284 }
285 _ => {}
286 }
287 }
288}