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}
28
29impl StoredGatewayResponse {
30 pub fn new(
32 response_id: impl Into<String>,
33 content_id: ContentId,
34 response: GatewayResponse,
35 ) -> Self {
36 Self {
37 response_id: response_id.into(),
38 content_id,
39 response,
40 request_content_id: None,
41 run_content_id: None,
42 event_content_ids: Vec::new(),
43 parent_response_id: None,
44 }
45 }
46
47 pub fn response_id(&self) -> &str {
49 &self.response_id
50 }
51
52 pub fn content_id(&self) -> &ContentId {
54 &self.content_id
55 }
56
57 pub fn response(&self) -> &GatewayResponse {
59 &self.response
60 }
61}
62
63pub trait GatewayResponseObjectStore {
65 fn put_response_object(&mut self, response: StoredGatewayResponse) -> Result<()>;
67 fn response_object(&self, response_id: &str) -> Option<StoredGatewayResponse>;
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73pub enum GatewayBatchStatus {
74 Queued,
76 InProgress,
78 Completed,
80 Cancelled,
82}
83
84impl GatewayBatchStatus {
85 pub fn as_str(&self) -> &'static str {
87 match self {
88 Self::Queued => "queued",
89 Self::InProgress => "in_progress",
90 Self::Completed => "completed",
91 Self::Cancelled => "cancelled",
92 }
93 }
94}
95
96#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
98pub struct GatewayBatchCounts {
99 total: u64,
100 completed: u64,
101 failed: u64,
102 cancelled: u64,
103}
104
105impl GatewayBatchCounts {
106 pub fn new(total: u64, completed: u64, failed: u64, cancelled: u64) -> Self {
108 Self {
109 total,
110 completed,
111 failed,
112 cancelled,
113 }
114 }
115
116 pub fn total(&self) -> u64 {
118 self.total
119 }
120
121 pub fn completed(&self) -> u64 {
123 self.completed
124 }
125
126 pub fn failed(&self) -> u64 {
128 self.failed
129 }
130
131 pub fn cancelled(&self) -> u64 {
133 self.cancelled
134 }
135
136 pub fn to_expr(self) -> Expr {
138 Expr::Map(vec![
139 field("total", Expr::String(self.total.to_string())),
140 field("completed", Expr::String(self.completed.to_string())),
141 field("failed", Expr::String(self.failed.to_string())),
142 field("cancelled", Expr::String(self.cancelled.to_string())),
143 ])
144 }
145}
146
147#[derive(Clone, Debug, PartialEq, Eq)]
149pub struct GatewayBatch {
150 id: String,
151 input_file_id: String,
152 endpoint: String,
153 status: GatewayBatchStatus,
154 output_file_id: Option<String>,
155 error_file_id: Option<String>,
156 created_at_ms: u64,
157 completed_at_ms: Option<u64>,
158 cancelled_at_ms: Option<u64>,
159 request_counts: GatewayBatchCounts,
160}
161
162impl GatewayBatch {
163 pub fn new(
165 id: impl Into<String>,
166 input_file_id: impl Into<String>,
167 endpoint: impl Into<String>,
168 created_at_ms: u64,
169 request_counts: GatewayBatchCounts,
170 ) -> Self {
171 Self {
172 id: id.into(),
173 input_file_id: input_file_id.into(),
174 endpoint: endpoint.into(),
175 status: GatewayBatchStatus::Queued,
176 output_file_id: None,
177 error_file_id: None,
178 created_at_ms,
179 completed_at_ms: None,
180 cancelled_at_ms: None,
181 request_counts,
182 }
183 }
184
185 pub fn complete(
188 mut self,
189 output_file_id: Option<String>,
190 error_file_id: Option<String>,
191 completed_at_ms: u64,
192 request_counts: GatewayBatchCounts,
193 ) -> Self {
194 self.status = GatewayBatchStatus::Completed;
195 self.output_file_id = output_file_id;
196 self.error_file_id = error_file_id;
197 self.completed_at_ms = Some(completed_at_ms);
198 self.request_counts = request_counts;
199 self
200 }
201
202 pub fn cancel(mut self, cancelled_at_ms: u64) -> Self {
205 let queued = self.request_counts.total.saturating_sub(
206 self.request_counts.completed
207 + self.request_counts.failed
208 + self.request_counts.cancelled,
209 );
210 self.status = GatewayBatchStatus::Cancelled;
211 self.cancelled_at_ms = Some(cancelled_at_ms);
212 self.request_counts.cancelled += queued;
213 self
214 }
215
216 pub fn id(&self) -> &str {
218 &self.id
219 }
220
221 pub fn input_file_id(&self) -> &str {
223 &self.input_file_id
224 }
225
226 pub fn endpoint(&self) -> &str {
228 &self.endpoint
229 }
230
231 pub fn status(&self) -> &GatewayBatchStatus {
233 &self.status
234 }
235
236 pub fn output_file_id(&self) -> Option<&str> {
238 self.output_file_id.as_deref()
239 }
240
241 pub fn error_file_id(&self) -> Option<&str> {
243 self.error_file_id.as_deref()
244 }
245
246 pub fn created_at_ms(&self) -> u64 {
248 self.created_at_ms
249 }
250
251 pub fn completed_at_ms(&self) -> Option<u64> {
253 self.completed_at_ms
254 }
255
256 pub fn cancelled_at_ms(&self) -> Option<u64> {
258 self.cancelled_at_ms
259 }
260
261 pub fn request_counts(&self) -> GatewayBatchCounts {
263 self.request_counts
264 }
265
266 pub fn to_expr(&self) -> Expr {
268 Expr::Map(vec![
269 field("kind", Expr::String(GATEWAY_BATCH_KIND.to_owned())),
270 field("id", Expr::String(self.id.clone())),
271 field("input-file-id", Expr::String(self.input_file_id.clone())),
272 field("endpoint", Expr::String(self.endpoint.clone())),
273 field("status", Expr::Symbol(Symbol::new(self.status.as_str()))),
274 optional_string_field("output-file-id", self.output_file_id.as_deref()),
275 optional_string_field("error-file-id", self.error_file_id.as_deref()),
276 field(
277 "created-at-ms",
278 Expr::String(self.created_at_ms.to_string()),
279 ),
280 optional_u64_field("completed-at-ms", self.completed_at_ms),
281 optional_u64_field("cancelled-at-ms", self.cancelled_at_ms),
282 field("request-counts", self.request_counts.to_expr()),
283 ])
284 }
285}
286
287#[derive(Clone, Debug, PartialEq, Eq)]
289pub enum GatewayFileStorageRef {
290 Memory {
292 content_id: ContentId,
294 },
295 TableFs {
297 path: String,
299 },
300}
301
302impl GatewayFileStorageRef {
303 pub fn memory(content_id: ContentId) -> Self {
305 Self::Memory { content_id }
306 }
307
308 pub fn table_fs(path: impl Into<String>) -> Self {
310 Self::TableFs { path: path.into() }
311 }
312
313 pub fn to_expr(&self) -> Expr {
315 match self {
316 Self::Memory { content_id } => Expr::Map(vec![
317 field("kind", Expr::Symbol(Symbol::new("memory"))),
318 field("content-id", content_id_expr(content_id)),
319 ]),
320 Self::TableFs { path } => Expr::Map(vec![
321 field("kind", Expr::Symbol(Symbol::new("table-fs"))),
322 field("path", Expr::String(path.clone())),
323 ]),
324 }
325 }
326}
327
328#[derive(Clone, Debug, PartialEq, Eq)]
330pub struct GatewayFile {
331 id: String,
332 filename: String,
333 purpose: String,
334 bytes: u64,
335 created_at_ms: u64,
336 storage_ref: GatewayFileStorageRef,
337}
338
339impl GatewayFile {
340 pub fn new(
342 id: impl Into<String>,
343 filename: impl Into<String>,
344 purpose: impl Into<String>,
345 bytes: u64,
346 created_at_ms: u64,
347 storage_ref: GatewayFileStorageRef,
348 ) -> Self {
349 Self {
350 id: id.into(),
351 filename: filename.into(),
352 purpose: purpose.into(),
353 bytes,
354 created_at_ms,
355 storage_ref,
356 }
357 }
358
359 pub fn id(&self) -> &str {
361 &self.id
362 }
363
364 pub fn filename(&self) -> &str {
366 &self.filename
367 }
368
369 pub fn purpose(&self) -> &str {
371 &self.purpose
372 }
373
374 pub fn bytes(&self) -> u64 {
376 self.bytes
377 }
378
379 pub fn created_at_ms(&self) -> u64 {
381 self.created_at_ms
382 }
383
384 pub fn storage_ref(&self) -> &GatewayFileStorageRef {
386 &self.storage_ref
387 }
388
389 pub fn to_expr(&self) -> Expr {
391 Expr::Map(vec![
392 field("kind", Expr::String(GATEWAY_FILE_KIND.to_owned())),
393 field("id", Expr::String(self.id.clone())),
394 field("filename", Expr::String(self.filename.clone())),
395 field("purpose", Expr::String(self.purpose.clone())),
396 field("bytes", Expr::String(self.bytes.to_string())),
397 field(
398 "created-at-ms",
399 Expr::String(self.created_at_ms.to_string()),
400 ),
401 field("storage-ref", self.storage_ref.to_expr()),
402 ])
403 }
404}
405
406#[derive(Clone, Debug, PartialEq, Eq)]
408pub struct GatewayThread {
409 id: String,
410 created_at_ms: u64,
411 metadata: Vec<(String, String)>,
412}
413
414impl GatewayThread {
415 pub fn new(id: impl Into<String>, created_at_ms: u64, metadata: Vec<(String, String)>) -> Self {
417 Self {
418 id: id.into(),
419 created_at_ms,
420 metadata,
421 }
422 }
423
424 pub fn id(&self) -> &str {
426 &self.id
427 }
428
429 pub fn created_at_ms(&self) -> u64 {
431 self.created_at_ms
432 }
433
434 pub fn metadata(&self) -> &[(String, String)] {
436 &self.metadata
437 }
438
439 pub fn to_expr(&self) -> Expr {
441 Expr::Map(vec![
442 field("kind", Expr::String(GATEWAY_THREAD_KIND.to_owned())),
443 field("id", Expr::String(self.id.clone())),
444 field(
445 "created-at-ms",
446 Expr::String(self.created_at_ms.to_string()),
447 ),
448 field("metadata", metadata_expr(&self.metadata)),
449 ])
450 }
451}
452
453#[derive(Clone, Debug, PartialEq, Eq)]
455pub struct GatewayThreadMessage {
456 id: String,
457 thread_id: String,
458 role: String,
459 content: String,
460 created_at_ms: u64,
461}
462
463impl GatewayThreadMessage {
464 pub fn new(
466 id: impl Into<String>,
467 thread_id: impl Into<String>,
468 role: impl Into<String>,
469 content: impl Into<String>,
470 created_at_ms: u64,
471 ) -> Self {
472 Self {
473 id: id.into(),
474 thread_id: thread_id.into(),
475 role: role.into(),
476 content: content.into(),
477 created_at_ms,
478 }
479 }
480
481 pub fn id(&self) -> &str {
483 &self.id
484 }
485
486 pub fn thread_id(&self) -> &str {
488 &self.thread_id
489 }
490
491 pub fn role(&self) -> &str {
493 &self.role
494 }
495
496 pub fn content(&self) -> &str {
498 &self.content
499 }
500
501 pub fn created_at_ms(&self) -> u64 {
503 self.created_at_ms
504 }
505
506 pub fn to_expr(&self) -> Expr {
508 Expr::Map(vec![
509 field("kind", Expr::String(GATEWAY_THREAD_MESSAGE_KIND.to_owned())),
510 field("id", Expr::String(self.id.clone())),
511 field("thread-id", Expr::String(self.thread_id.clone())),
512 field("role", Expr::String(self.role.clone())),
513 field("content", Expr::String(self.content.clone())),
514 field(
515 "created-at-ms",
516 Expr::String(self.created_at_ms.to_string()),
517 ),
518 ])
519 }
520}
521
522pub trait GatewayStateStore {
525 fn put_file(&mut self, file: GatewayFile, bytes: Vec<u8>) -> Result<()>;
527 fn file(&self, file_id: &str) -> Option<GatewayFile>;
529 fn file_bytes(&self, file_id: &str) -> Option<Vec<u8>>;
531
532 fn put_batch(&mut self, batch: GatewayBatch) -> Result<()>;
534 fn batch(&self, batch_id: &str) -> Option<GatewayBatch>;
536
537 fn put_thread(&mut self, thread: GatewayThread) -> Result<()>;
539 fn thread(&self, thread_id: &str) -> Option<GatewayThread>;
541
542 fn put_thread_message(&mut self, message: GatewayThreadMessage) -> Result<()>;
544 fn thread_messages(&self, thread_id: &str) -> Vec<GatewayThreadMessage>;
546
547 fn put_vector_store(&mut self, vector_store: GatewayVectorStore) -> Result<()>;
549 fn vector_store(&self, vector_store_id: &str) -> Option<GatewayVectorStore>;
551}
552
553fn metadata_expr(metadata: &[(String, String)]) -> Expr {
554 Expr::Map(
555 metadata
556 .iter()
557 .map(|(key, value)| (Expr::String(key.clone()), Expr::String(value.clone())))
558 .collect(),
559 )
560}
561
562use sim_value::build::entry as field;
563
564fn optional_string_field(name: &str, value: Option<&str>) -> (Expr, Expr) {
565 field(
566 name,
567 value
568 .map(|value| Expr::String(value.to_owned()))
569 .unwrap_or(Expr::Nil),
570 )
571}
572
573fn optional_u64_field(name: &str, value: Option<u64>) -> (Expr, Expr) {
574 field(
575 name,
576 value
577 .map(|value| Expr::String(value.to_string()))
578 .unwrap_or(Expr::Nil),
579 )
580}