runifold_model/
invocation.rs1use std::{future::Future, pin::Pin, sync::Arc, time::Duration};
2
3use futures_core::Stream;
4use futures_util::{
5 StreamExt,
6 future::{Either, select},
7};
8use runifold_core::{CancellationToken, Instant, InvocationId, RunContext, RunId};
9
10use crate::{
11 ModelCapabilities, ModelError, ModelErrorKind, ModelRef, ModelRequest, ModelResponse,
12 ModelStreamAccumulator, ModelStreamEvent,
13};
14
15#[cfg(not(target_arch = "wasm32"))]
17pub type ModelFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
18
19#[cfg(target_arch = "wasm32")]
21pub type ModelFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
22
23#[cfg(not(target_arch = "wasm32"))]
25pub type ModelEventStream =
26 Pin<Box<dyn Stream<Item = Result<ModelStreamEvent, ModelError>> + Send + 'static>>;
27
28#[cfg(target_arch = "wasm32")]
30pub type ModelEventStream =
31 Pin<Box<dyn Stream<Item = Result<ModelStreamEvent, ModelError>> + 'static>>;
32
33#[derive(Clone, Debug)]
39pub struct ModelCallContext {
40 invocation_id: InvocationId,
41 run_id: Option<RunId>,
42 deadline: Option<Instant>,
43 cancellation: CancellationToken,
44}
45
46impl ModelCallContext {
47 pub fn new() -> Self {
49 Self {
50 invocation_id: InvocationId::new(),
51 run_id: None,
52 deadline: None,
53 cancellation: CancellationToken::new(),
54 }
55 }
56
57 pub fn for_run(run: &RunContext) -> Self {
59 Self {
60 invocation_id: InvocationId::new(),
61 run_id: Some(run.run_id()),
62 deadline: run.deadline(),
63 cancellation: run.cancellation().child_token(),
64 }
65 }
66
67 pub const fn invocation_id(&self) -> InvocationId {
69 self.invocation_id
70 }
71
72 pub const fn run_id(&self) -> Option<RunId> {
74 self.run_id
75 }
76
77 pub const fn deadline(&self) -> Option<Instant> {
79 self.deadline
80 }
81
82 pub fn remaining(&self) -> Option<Duration> {
84 self.deadline
85 .map(|deadline| deadline.saturating_duration_since(Instant::now()))
86 }
87
88 pub const fn cancellation(&self) -> &CancellationToken {
90 &self.cancellation
91 }
92
93 #[must_use]
95 pub fn with_deadline(mut self, deadline: Instant) -> Self {
96 self.deadline = Some(
97 self.deadline
98 .map_or(deadline, |current| current.min(deadline)),
99 );
100 self
101 }
102
103 #[must_use]
108 pub fn with_cancellation(mut self, cancellation: &CancellationToken) -> Self {
109 self.cancellation = cancellation.child_token();
110 self
111 }
112
113 #[must_use]
119 pub fn child_attempt(&self) -> Self {
120 Self {
121 invocation_id: InvocationId::new(),
122 run_id: self.run_id,
123 deadline: self.deadline,
124 cancellation: self.cancellation.child_token(),
125 }
126 }
127}
128
129impl Default for ModelCallContext {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135pub trait Model: Send + Sync {
141 fn capabilities<'a>(
143 &'a self,
144 model: &'a ModelRef,
145 ) -> ModelFuture<'a, Result<ModelCapabilities, ModelError>>;
146
147 fn stream(
149 &self,
150 request: ModelRequest,
151 context: ModelCallContext,
152 ) -> ModelFuture<'_, Result<ModelEventStream, ModelError>>;
153
154 fn invoke(
156 &self,
157 request: ModelRequest,
158 context: ModelCallContext,
159 ) -> ModelFuture<'_, Result<ModelResponse, ModelError>> {
160 Box::pin(async move {
161 let cancellation = context.cancellation().clone();
162 let stream_future = self.stream(request, context);
163 let mut stream =
164 match select(Box::pin(cancellation.cancelled()), Box::pin(stream_future)).await {
165 Either::Left(_) => return Err(cancelled_error()),
166 Either::Right((result, _)) => result?,
167 };
168
169 let mut accumulator = ModelStreamAccumulator::new();
170 loop {
171 let next = stream.next();
172 match select(Box::pin(cancellation.cancelled()), Box::pin(next)).await {
173 Either::Left(_) => return Err(cancelled_error()),
174 Either::Right((Some(event), _)) => {
175 if let Some(response) = accumulator.push(event?)? {
176 return Ok(response);
177 }
178 }
179 Either::Right((None, _)) => {
180 return Err(ModelError::local(
181 ModelErrorKind::Protocol,
182 "model stream ended before a terminal response event",
183 ));
184 }
185 }
186 }
187 })
188 }
189}
190
191impl<T> Model for Arc<T>
192where
193 T: Model + ?Sized,
194{
195 fn capabilities<'a>(
196 &'a self,
197 model: &'a ModelRef,
198 ) -> ModelFuture<'a, Result<ModelCapabilities, ModelError>> {
199 (**self).capabilities(model)
200 }
201
202 fn stream(
203 &self,
204 request: ModelRequest,
205 context: ModelCallContext,
206 ) -> ModelFuture<'_, Result<ModelEventStream, ModelError>> {
207 (**self).stream(request, context)
208 }
209}
210
211pub trait ProviderModel: Model {
218 fn provider(&self) -> &str;
220
221 fn model_ref(&self, model: impl Into<String>) -> ModelRef
223 where
224 Self: Sized,
225 {
226 ModelRef::new(self.provider(), model)
227 }
228}
229
230impl<T> ProviderModel for Arc<T>
231where
232 T: ProviderModel + ?Sized,
233{
234 fn provider(&self) -> &str {
235 (**self).provider()
236 }
237}
238
239fn cancelled_error() -> ModelError {
240 ModelError::local(ModelErrorKind::Cancelled, "model invocation was cancelled")
241}