1use sim_kernel::{ContentId, Expr, Result, Symbol};
2
3use crate::objects::{GatewayResponse, content_id_expr};
4
5use super::vector::GatewayVectorStore;
6
7pub const GATEWAY_FILE_KIND: &str = "openai-gateway/file";
9pub const GATEWAY_BATCH_KIND: &str = "openai-gateway/batch";
11pub const GATEWAY_THREAD_KIND: &str = "openai-gateway/thread";
13pub const GATEWAY_THREAD_MESSAGE_KIND: &str = "openai-gateway/thread-message";
15
16#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct StoredGatewayResponse {
20 response_id: String,
21 content_id: ContentId,
22 response: GatewayResponse,
23 pub(crate) request_content_id: Option<ContentId>,
24 pub(crate) run_content_id: Option<ContentId>,
25 pub(crate) event_content_ids: Vec<ContentId>,
26 pub(crate) parent_response_id: Option<String>,
27 pub(crate) owner_key_id: Option<String>,
28}
29
30impl StoredGatewayResponse {
31 pub fn new(
33 response_id: impl Into<String>,
34 content_id: ContentId,
35 response: GatewayResponse,
36 ) -> Self {
37 Self {
38 response_id: response_id.into(),
39 content_id,
40 response,
41 request_content_id: None,
42 run_content_id: None,
43 event_content_ids: Vec::new(),
44 parent_response_id: None,
45 owner_key_id: None,
46 }
47 }
48
49 pub fn response_id(&self) -> &str {
51 &self.response_id
52 }
53
54 pub fn content_id(&self) -> &ContentId {
56 &self.content_id
57 }
58
59 pub fn response(&self) -> &GatewayResponse {
61 &self.response
62 }
63
64 pub fn owner_key_id(&self) -> Option<&str> {
66 self.owner_key_id.as_deref()
67 }
68}
69
70pub trait GatewayResponseObjectStore {
72 fn put_response_object(&mut self, response: StoredGatewayResponse) -> Result<()>;
74 fn response_object(&self, response_id: &str) -> Option<StoredGatewayResponse>;
76}
77
78#[derive(Clone, Debug, PartialEq, Eq)]
80pub enum GatewayBatchStatus {
81 Queued,
83 InProgress,
85 Completed,
87 Cancelled,
89}
90
91impl GatewayBatchStatus {
92 pub fn as_str(&self) -> &'static str {
94 match self {
95 Self::Queued => "queued",
96 Self::InProgress => "in_progress",
97 Self::Completed => "completed",
98 Self::Cancelled => "cancelled",
99 }
100 }
101}
102
103#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
105pub struct GatewayBatchCounts {
106 total: u64,
107 completed: u64,
108 failed: u64,
109 cancelled: u64,
110}
111
112impl GatewayBatchCounts {
113 pub fn new(total: u64, completed: u64, failed: u64, cancelled: u64) -> Self {
115 Self {
116 total,
117 completed,
118 failed,
119 cancelled,
120 }
121 }
122
123 pub fn total(&self) -> u64 {
125 self.total
126 }
127
128 pub fn completed(&self) -> u64 {
130 self.completed
131 }
132
133 pub fn failed(&self) -> u64 {
135 self.failed
136 }
137
138 pub fn cancelled(&self) -> u64 {
140 self.cancelled
141 }
142
143 pub fn to_expr(self) -> Expr {
145 Expr::Map(vec![
146 field("total", Expr::String(self.total.to_string())),
147 field("completed", Expr::String(self.completed.to_string())),
148 field("failed", Expr::String(self.failed.to_string())),
149 field("cancelled", Expr::String(self.cancelled.to_string())),
150 ])
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct GatewayBatch {
157 id: String,
158 input_file_id: String,
159 endpoint: String,
160 status: GatewayBatchStatus,
161 output_file_id: Option<String>,
162 error_file_id: Option<String>,
163 created_at_ms: u64,
164 completed_at_ms: Option<u64>,
165 cancelled_at_ms: Option<u64>,
166 request_counts: GatewayBatchCounts,
167}
168
169impl GatewayBatch {
170 pub fn new(
172 id: impl Into<String>,
173 input_file_id: impl Into<String>,
174 endpoint: impl Into<String>,
175 created_at_ms: u64,
176 request_counts: GatewayBatchCounts,
177 ) -> Self {
178 Self {
179 id: id.into(),
180 input_file_id: input_file_id.into(),
181 endpoint: endpoint.into(),
182 status: GatewayBatchStatus::Queued,
183 output_file_id: None,
184 error_file_id: None,
185 created_at_ms,
186 completed_at_ms: None,
187 cancelled_at_ms: None,
188 request_counts,
189 }
190 }
191
192 pub fn complete(
195 mut self,
196 output_file_id: Option<String>,
197 error_file_id: Option<String>,
198 completed_at_ms: u64,
199 request_counts: GatewayBatchCounts,
200 ) -> Self {
201 self.status = GatewayBatchStatus::Completed;
202 self.output_file_id = output_file_id;
203 self.error_file_id = error_file_id;
204 self.completed_at_ms = Some(completed_at_ms);
205 self.request_counts = request_counts;
206 self
207 }
208
209 pub fn cancel(mut self, cancelled_at_ms: u64) -> Self {
212 let queued = self.request_counts.total.saturating_sub(
213 self.request_counts.completed
214 + self.request_counts.failed
215 + self.request_counts.cancelled,
216 );
217 self.status = GatewayBatchStatus::Cancelled;
218 self.cancelled_at_ms = Some(cancelled_at_ms);
219 self.request_counts.cancelled += queued;
220 self
221 }
222
223 pub fn id(&self) -> &str {
225 &self.id
226 }
227
228 pub fn input_file_id(&self) -> &str {
230 &self.input_file_id
231 }
232
233 pub fn endpoint(&self) -> &str {
235 &self.endpoint
236 }
237
238 pub fn status(&self) -> &GatewayBatchStatus {
240 &self.status
241 }
242
243 pub fn output_file_id(&self) -> Option<&str> {
245 self.output_file_id.as_deref()
246 }
247
248 pub fn error_file_id(&self) -> Option<&str> {
250 self.error_file_id.as_deref()
251 }
252
253 pub fn created_at_ms(&self) -> u64 {
255 self.created_at_ms
256 }
257
258 pub fn completed_at_ms(&self) -> Option<u64> {
260 self.completed_at_ms
261 }
262
263 pub fn cancelled_at_ms(&self) -> Option<u64> {
265 self.cancelled_at_ms
266 }
267
268 pub fn request_counts(&self) -> GatewayBatchCounts {
270 self.request_counts
271 }
272
273 pub fn to_expr(&self) -> Expr {
275 Expr::Map(vec![
276 field("kind", Expr::String(GATEWAY_BATCH_KIND.to_owned())),
277 field("id", Expr::String(self.id.clone())),
278 field("input-file-id", Expr::String(self.input_file_id.clone())),
279 field("endpoint", Expr::String(self.endpoint.clone())),
280 field("status", Expr::Symbol(Symbol::new(self.status.as_str()))),
281 optional_string_field("output-file-id", self.output_file_id.as_deref()),
282 optional_string_field("error-file-id", self.error_file_id.as_deref()),
283 field(
284 "created-at-ms",
285 Expr::String(self.created_at_ms.to_string()),
286 ),
287 optional_u64_field("completed-at-ms", self.completed_at_ms),
288 optional_u64_field("cancelled-at-ms", self.cancelled_at_ms),
289 field("request-counts", self.request_counts.to_expr()),
290 ])
291 }
292}
293
294#[derive(Clone, Debug, PartialEq, Eq)]
296pub enum GatewayFileStorageRef {
297 Memory {
299 content_id: ContentId,
301 },
302 TableFs {
304 path: String,
306 },
307}
308
309impl GatewayFileStorageRef {
310 pub fn memory(content_id: ContentId) -> Self {
312 Self::Memory { content_id }
313 }
314
315 pub fn table_fs(path: impl Into<String>) -> Self {
317 Self::TableFs { path: path.into() }
318 }
319
320 pub fn to_expr(&self) -> Expr {
322 match self {
323 Self::Memory { content_id } => Expr::Map(vec![
324 field("kind", Expr::Symbol(Symbol::new("memory"))),
325 field("content-id", content_id_expr(content_id)),
326 ]),
327 Self::TableFs { path } => Expr::Map(vec![
328 field("kind", Expr::Symbol(Symbol::new("table-fs"))),
329 field("path", Expr::String(path.clone())),
330 ]),
331 }
332 }
333}
334
335#[derive(Clone, Debug, PartialEq, Eq)]
337pub struct GatewayFile {
338 id: String,
339 filename: String,
340 purpose: String,
341 bytes: u64,
342 created_at_ms: u64,
343 storage_ref: GatewayFileStorageRef,
344}
345
346impl GatewayFile {
347 pub fn new(
349 id: impl Into<String>,
350 filename: impl Into<String>,
351 purpose: impl Into<String>,
352 bytes: u64,
353 created_at_ms: u64,
354 storage_ref: GatewayFileStorageRef,
355 ) -> Self {
356 Self {
357 id: id.into(),
358 filename: filename.into(),
359 purpose: purpose.into(),
360 bytes,
361 created_at_ms,
362 storage_ref,
363 }
364 }
365
366 pub fn id(&self) -> &str {
368 &self.id
369 }
370
371 pub fn filename(&self) -> &str {
373 &self.filename
374 }
375
376 pub fn purpose(&self) -> &str {
378 &self.purpose
379 }
380
381 pub fn bytes(&self) -> u64 {
383 self.bytes
384 }
385
386 pub fn created_at_ms(&self) -> u64 {
388 self.created_at_ms
389 }
390
391 pub fn storage_ref(&self) -> &GatewayFileStorageRef {
393 &self.storage_ref
394 }
395
396 pub fn to_expr(&self) -> Expr {
398 Expr::Map(vec![
399 field("kind", Expr::String(GATEWAY_FILE_KIND.to_owned())),
400 field("id", Expr::String(self.id.clone())),
401 field("filename", Expr::String(self.filename.clone())),
402 field("purpose", Expr::String(self.purpose.clone())),
403 field("bytes", Expr::String(self.bytes.to_string())),
404 field(
405 "created-at-ms",
406 Expr::String(self.created_at_ms.to_string()),
407 ),
408 field("storage-ref", self.storage_ref.to_expr()),
409 ])
410 }
411}
412
413#[derive(Clone, Debug, PartialEq, Eq)]
415pub struct GatewayThread {
416 id: String,
417 created_at_ms: u64,
418 metadata: Vec<(String, String)>,
419}
420
421impl GatewayThread {
422 pub fn new(id: impl Into<String>, created_at_ms: u64, metadata: Vec<(String, String)>) -> Self {
424 Self {
425 id: id.into(),
426 created_at_ms,
427 metadata,
428 }
429 }
430
431 pub fn id(&self) -> &str {
433 &self.id
434 }
435
436 pub fn created_at_ms(&self) -> u64 {
438 self.created_at_ms
439 }
440
441 pub fn metadata(&self) -> &[(String, String)] {
443 &self.metadata
444 }
445
446 pub fn to_expr(&self) -> Expr {
448 Expr::Map(vec![
449 field("kind", Expr::String(GATEWAY_THREAD_KIND.to_owned())),
450 field("id", Expr::String(self.id.clone())),
451 field(
452 "created-at-ms",
453 Expr::String(self.created_at_ms.to_string()),
454 ),
455 field("metadata", metadata_expr(&self.metadata)),
456 ])
457 }
458}
459
460#[derive(Clone, Debug, PartialEq, Eq)]
462pub struct GatewayThreadMessage {
463 id: String,
464 thread_id: String,
465 role: String,
466 content: String,
467 created_at_ms: u64,
468}
469
470impl GatewayThreadMessage {
471 pub fn new(
473 id: impl Into<String>,
474 thread_id: impl Into<String>,
475 role: impl Into<String>,
476 content: impl Into<String>,
477 created_at_ms: u64,
478 ) -> Self {
479 Self {
480 id: id.into(),
481 thread_id: thread_id.into(),
482 role: role.into(),
483 content: content.into(),
484 created_at_ms,
485 }
486 }
487
488 pub fn id(&self) -> &str {
490 &self.id
491 }
492
493 pub fn thread_id(&self) -> &str {
495 &self.thread_id
496 }
497
498 pub fn role(&self) -> &str {
500 &self.role
501 }
502
503 pub fn content(&self) -> &str {
505 &self.content
506 }
507
508 pub fn created_at_ms(&self) -> u64 {
510 self.created_at_ms
511 }
512
513 pub fn to_expr(&self) -> Expr {
515 Expr::Map(vec![
516 field("kind", Expr::String(GATEWAY_THREAD_MESSAGE_KIND.to_owned())),
517 field("id", Expr::String(self.id.clone())),
518 field("thread-id", Expr::String(self.thread_id.clone())),
519 field("role", Expr::String(self.role.clone())),
520 field("content", Expr::String(self.content.clone())),
521 field(
522 "created-at-ms",
523 Expr::String(self.created_at_ms.to_string()),
524 ),
525 ])
526 }
527}
528
529pub trait GatewayStateStore {
532 fn put_file(&mut self, file: GatewayFile, bytes: Vec<u8>) -> Result<()>;
534 fn file(&self, file_id: &str) -> Option<GatewayFile>;
536 fn file_bytes(&self, file_id: &str) -> Option<Vec<u8>>;
538
539 fn put_batch(&mut self, batch: GatewayBatch) -> Result<()>;
541 fn batch(&self, batch_id: &str) -> Option<GatewayBatch>;
543
544 fn put_thread(&mut self, thread: GatewayThread) -> Result<()>;
546 fn thread(&self, thread_id: &str) -> Option<GatewayThread>;
548
549 fn put_thread_message(&mut self, message: GatewayThreadMessage) -> Result<()>;
551 fn thread_messages(&self, thread_id: &str) -> Vec<GatewayThreadMessage>;
553
554 fn put_vector_store(&mut self, vector_store: GatewayVectorStore) -> Result<()>;
556 fn vector_store(&self, vector_store_id: &str) -> Option<GatewayVectorStore>;
558}
559
560fn metadata_expr(metadata: &[(String, String)]) -> Expr {
561 Expr::Map(
562 metadata
563 .iter()
564 .map(|(key, value)| (Expr::String(key.clone()), Expr::String(value.clone())))
565 .collect(),
566 )
567}
568
569use sim_value::build::entry as field;
570
571fn optional_string_field(name: &str, value: Option<&str>) -> (Expr, Expr) {
572 field(
573 name,
574 value
575 .map(|value| Expr::String(value.to_owned()))
576 .unwrap_or(Expr::Nil),
577 )
578}
579
580fn optional_u64_field(name: &str, value: Option<u64>) -> (Expr, Expr) {
581 field(
582 name,
583 value
584 .map(|value| Expr::String(value.to_string()))
585 .unwrap_or(Expr::Nil),
586 )
587}