1use std::sync::Arc;
9
10use futures::StreamExt;
11use futures::stream::{self, BoxStream};
12use oino_agent::{StreamFn, StreamRequest, now_ms};
13use oino_ai::{
14 AssistantMessage, AssistantMessageEvent, ErrorReason, JsonValue, PartialAssembler,
15 ProviderEvent, StopReason,
16};
17use serde_json::json;
18use tokio_util::sync::CancellationToken;
19
20use crate::extensions::api::ExtensionCallError;
21use crate::extensions::runner::{ExtensionSet, LoadedExtension};
22
23#[must_use]
25pub fn no_provider_message(api: &str) -> String {
26 format!("No provider extension implements API \"{api}\"")
27}
28
29#[derive(Clone, Debug)]
31pub struct ProviderStream {
32 extensions: Arc<ExtensionSet>,
33}
34
35impl ProviderStream {
36 #[must_use]
38 pub const fn new(extensions: Arc<ExtensionSet>) -> Self {
39 Self { extensions }
40 }
41
42 fn component(&self, api: &str) -> Option<Arc<LoadedExtension>> {
43 self.extensions
44 .extensions()
45 .iter()
46 .find(|extension| {
47 extension.instance.provider().is_some()
48 && extension.state().apis.iter().any(|a| a == api)
49 })
50 .cloned()
51 }
52}
53
54#[must_use]
56pub fn request_json(request: &StreamRequest) -> JsonValue {
57 let mut value = json!({
58 "model": request.model,
59 "context": request.context,
60 "options": request.options,
61 });
62 if let Some(key) = &request.api_key {
63 value["apiKey"] = json!(key);
64 }
65 value
66}
67
68fn failure(
69 mut message: AssistantMessage,
70 error: &str,
71 cancel: &CancellationToken,
72) -> AssistantMessageEvent {
73 let aborted = cancel.is_cancelled();
74 message.stop_reason = if aborted {
75 StopReason::Aborted
76 } else {
77 StopReason::Error
78 };
79 message.error_message = Some(error.to_owned());
80 AssistantMessageEvent::Error {
81 reason: if aborted {
82 ErrorReason::Aborted
83 } else {
84 ErrorReason::Error
85 },
86 error: message,
87 }
88}
89
90enum Phase {
91 Connect(Arc<LoadedExtension>, JsonValue),
92 Streaming(BoxStream<'static, Result<ProviderEvent, ExtensionCallError>>),
93 Done,
94}
95
96impl StreamFn for ProviderStream {
97 fn stream(&self, request: StreamRequest) -> BoxStream<'static, AssistantMessageEvent> {
98 let model = &request.model;
99 let template = AssistantMessage::empty(&model.api, &model.provider, &model.id, now_ms());
100 let cancel = request.cancel.clone();
101 let Some(component) = self.component(&model.api) else {
102 let event = failure(template, &no_provider_message(&model.api), &cancel);
103 return Box::pin(stream::once(async move { event }));
104 };
105 let body = request_json(&request);
106 let assembler = PartialAssembler::new(template);
107 let state = (Phase::Connect(component, body), assembler, cancel);
108 Box::pin(
109 stream::unfold(state, |(phase, mut assembler, cancel)| async move {
110 match phase {
111 Phase::Done => None,
112 Phase::Connect(component, body) => {
113 let Some(provider) = component.instance.provider() else {
114 let event = failure(
115 assembler.partial().clone(),
116 "Provider unavailable",
117 &cancel,
118 );
119 return Some((vec![event], (Phase::Done, assembler, cancel)));
120 };
121 match provider.stream(body, cancel.clone()).await {
122 Ok(events) => {
123 Some((Vec::new(), (Phase::Streaming(events), assembler, cancel)))
124 }
125 Err(error) => {
126 let event =
127 failure(assembler.partial().clone(), &error.message, &cancel);
128 Some((vec![event], (Phase::Done, assembler, cancel)))
129 }
130 }
131 }
132 Phase::Streaming(mut events) => {
133 let next = tokio::select! {
134 biased;
135 () = cancel.cancelled() => None,
136 next = events.next() => Some(next),
137 };
138 match next {
139 None => {
140 let event = failure(
141 assembler.partial().clone(),
142 "Request was aborted",
143 &cancel,
144 );
145 Some((vec![event], (Phase::Done, assembler, cancel)))
146 }
147 Some(Some(Ok(event))) => {
148 let terminal = matches!(
149 event,
150 ProviderEvent::Done { .. } | ProviderEvent::Error { .. }
151 );
152 let out = assembler.apply(event);
153 let phase = if terminal {
154 Phase::Done
155 } else {
156 Phase::Streaming(events)
157 };
158 Some((vec![out], (phase, assembler, cancel)))
159 }
160 Some(Some(Err(error))) => {
161 let event =
162 failure(assembler.partial().clone(), &error.message, &cancel);
163 Some((vec![event], (Phase::Done, assembler, cancel)))
164 }
165 Some(None) => {
166 let event = failure(
167 assembler.partial().clone(),
168 "Provider stream ended without a final event",
169 &cancel,
170 );
171 Some((vec![event], (Phase::Done, assembler, cancel)))
172 }
173 }
174 }
175 }
176 })
177 .flat_map(stream::iter),
178 )
179 }
180}