use sim_kernel::{ContentId, Expr, Result, Symbol};
use crate::objects::{GatewayResponse, content_id_expr};
use super::vector::GatewayVectorStore;
pub const GATEWAY_FILE_KIND: &str = "openai-gateway/file";
pub const GATEWAY_BATCH_KIND: &str = "openai-gateway/batch";
pub const GATEWAY_THREAD_KIND: &str = "openai-gateway/thread";
pub const GATEWAY_THREAD_MESSAGE_KIND: &str = "openai-gateway/thread-message";
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StoredGatewayResponse {
response_id: String,
content_id: ContentId,
response: GatewayResponse,
pub(crate) request_content_id: Option<ContentId>,
pub(crate) run_content_id: Option<ContentId>,
pub(crate) event_content_ids: Vec<ContentId>,
pub(crate) parent_response_id: Option<String>,
}
impl StoredGatewayResponse {
pub fn new(
response_id: impl Into<String>,
content_id: ContentId,
response: GatewayResponse,
) -> Self {
Self {
response_id: response_id.into(),
content_id,
response,
request_content_id: None,
run_content_id: None,
event_content_ids: Vec::new(),
parent_response_id: None,
}
}
pub fn response_id(&self) -> &str {
&self.response_id
}
pub fn content_id(&self) -> &ContentId {
&self.content_id
}
pub fn response(&self) -> &GatewayResponse {
&self.response
}
}
pub trait GatewayResponseObjectStore {
fn put_response_object(&mut self, response: StoredGatewayResponse) -> Result<()>;
fn response_object(&self, response_id: &str) -> Option<StoredGatewayResponse>;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GatewayBatchStatus {
Queued,
InProgress,
Completed,
Cancelled,
}
impl GatewayBatchStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Queued => "queued",
Self::InProgress => "in_progress",
Self::Completed => "completed",
Self::Cancelled => "cancelled",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct GatewayBatchCounts {
total: u64,
completed: u64,
failed: u64,
cancelled: u64,
}
impl GatewayBatchCounts {
pub fn new(total: u64, completed: u64, failed: u64, cancelled: u64) -> Self {
Self {
total,
completed,
failed,
cancelled,
}
}
pub fn total(&self) -> u64 {
self.total
}
pub fn completed(&self) -> u64 {
self.completed
}
pub fn failed(&self) -> u64 {
self.failed
}
pub fn cancelled(&self) -> u64 {
self.cancelled
}
pub fn to_expr(self) -> Expr {
Expr::Map(vec![
field("total", Expr::String(self.total.to_string())),
field("completed", Expr::String(self.completed.to_string())),
field("failed", Expr::String(self.failed.to_string())),
field("cancelled", Expr::String(self.cancelled.to_string())),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GatewayBatch {
id: String,
input_file_id: String,
endpoint: String,
status: GatewayBatchStatus,
output_file_id: Option<String>,
error_file_id: Option<String>,
created_at_ms: u64,
completed_at_ms: Option<u64>,
cancelled_at_ms: Option<u64>,
request_counts: GatewayBatchCounts,
}
impl GatewayBatch {
pub fn new(
id: impl Into<String>,
input_file_id: impl Into<String>,
endpoint: impl Into<String>,
created_at_ms: u64,
request_counts: GatewayBatchCounts,
) -> Self {
Self {
id: id.into(),
input_file_id: input_file_id.into(),
endpoint: endpoint.into(),
status: GatewayBatchStatus::Queued,
output_file_id: None,
error_file_id: None,
created_at_ms,
completed_at_ms: None,
cancelled_at_ms: None,
request_counts,
}
}
pub fn complete(
mut self,
output_file_id: Option<String>,
error_file_id: Option<String>,
completed_at_ms: u64,
request_counts: GatewayBatchCounts,
) -> Self {
self.status = GatewayBatchStatus::Completed;
self.output_file_id = output_file_id;
self.error_file_id = error_file_id;
self.completed_at_ms = Some(completed_at_ms);
self.request_counts = request_counts;
self
}
pub fn cancel(mut self, cancelled_at_ms: u64) -> Self {
let queued = self.request_counts.total.saturating_sub(
self.request_counts.completed
+ self.request_counts.failed
+ self.request_counts.cancelled,
);
self.status = GatewayBatchStatus::Cancelled;
self.cancelled_at_ms = Some(cancelled_at_ms);
self.request_counts.cancelled += queued;
self
}
pub fn id(&self) -> &str {
&self.id
}
pub fn input_file_id(&self) -> &str {
&self.input_file_id
}
pub fn endpoint(&self) -> &str {
&self.endpoint
}
pub fn status(&self) -> &GatewayBatchStatus {
&self.status
}
pub fn output_file_id(&self) -> Option<&str> {
self.output_file_id.as_deref()
}
pub fn error_file_id(&self) -> Option<&str> {
self.error_file_id.as_deref()
}
pub fn created_at_ms(&self) -> u64 {
self.created_at_ms
}
pub fn completed_at_ms(&self) -> Option<u64> {
self.completed_at_ms
}
pub fn cancelled_at_ms(&self) -> Option<u64> {
self.cancelled_at_ms
}
pub fn request_counts(&self) -> GatewayBatchCounts {
self.request_counts
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("kind", Expr::String(GATEWAY_BATCH_KIND.to_owned())),
field("id", Expr::String(self.id.clone())),
field("input-file-id", Expr::String(self.input_file_id.clone())),
field("endpoint", Expr::String(self.endpoint.clone())),
field("status", Expr::Symbol(Symbol::new(self.status.as_str()))),
optional_string_field("output-file-id", self.output_file_id.as_deref()),
optional_string_field("error-file-id", self.error_file_id.as_deref()),
field(
"created-at-ms",
Expr::String(self.created_at_ms.to_string()),
),
optional_u64_field("completed-at-ms", self.completed_at_ms),
optional_u64_field("cancelled-at-ms", self.cancelled_at_ms),
field("request-counts", self.request_counts.to_expr()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GatewayFileStorageRef {
Memory {
content_id: ContentId,
},
TableFs {
path: String,
},
}
impl GatewayFileStorageRef {
pub fn memory(content_id: ContentId) -> Self {
Self::Memory { content_id }
}
pub fn table_fs(path: impl Into<String>) -> Self {
Self::TableFs { path: path.into() }
}
pub fn to_expr(&self) -> Expr {
match self {
Self::Memory { content_id } => Expr::Map(vec![
field("kind", Expr::Symbol(Symbol::new("memory"))),
field("content-id", content_id_expr(content_id)),
]),
Self::TableFs { path } => Expr::Map(vec![
field("kind", Expr::Symbol(Symbol::new("table-fs"))),
field("path", Expr::String(path.clone())),
]),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GatewayFile {
id: String,
filename: String,
purpose: String,
bytes: u64,
created_at_ms: u64,
storage_ref: GatewayFileStorageRef,
}
impl GatewayFile {
pub fn new(
id: impl Into<String>,
filename: impl Into<String>,
purpose: impl Into<String>,
bytes: u64,
created_at_ms: u64,
storage_ref: GatewayFileStorageRef,
) -> Self {
Self {
id: id.into(),
filename: filename.into(),
purpose: purpose.into(),
bytes,
created_at_ms,
storage_ref,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn filename(&self) -> &str {
&self.filename
}
pub fn purpose(&self) -> &str {
&self.purpose
}
pub fn bytes(&self) -> u64 {
self.bytes
}
pub fn created_at_ms(&self) -> u64 {
self.created_at_ms
}
pub fn storage_ref(&self) -> &GatewayFileStorageRef {
&self.storage_ref
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("kind", Expr::String(GATEWAY_FILE_KIND.to_owned())),
field("id", Expr::String(self.id.clone())),
field("filename", Expr::String(self.filename.clone())),
field("purpose", Expr::String(self.purpose.clone())),
field("bytes", Expr::String(self.bytes.to_string())),
field(
"created-at-ms",
Expr::String(self.created_at_ms.to_string()),
),
field("storage-ref", self.storage_ref.to_expr()),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GatewayThread {
id: String,
created_at_ms: u64,
metadata: Vec<(String, String)>,
}
impl GatewayThread {
pub fn new(id: impl Into<String>, created_at_ms: u64, metadata: Vec<(String, String)>) -> Self {
Self {
id: id.into(),
created_at_ms,
metadata,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn created_at_ms(&self) -> u64 {
self.created_at_ms
}
pub fn metadata(&self) -> &[(String, String)] {
&self.metadata
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("kind", Expr::String(GATEWAY_THREAD_KIND.to_owned())),
field("id", Expr::String(self.id.clone())),
field(
"created-at-ms",
Expr::String(self.created_at_ms.to_string()),
),
field("metadata", metadata_expr(&self.metadata)),
])
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GatewayThreadMessage {
id: String,
thread_id: String,
role: String,
content: String,
created_at_ms: u64,
}
impl GatewayThreadMessage {
pub fn new(
id: impl Into<String>,
thread_id: impl Into<String>,
role: impl Into<String>,
content: impl Into<String>,
created_at_ms: u64,
) -> Self {
Self {
id: id.into(),
thread_id: thread_id.into(),
role: role.into(),
content: content.into(),
created_at_ms,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn thread_id(&self) -> &str {
&self.thread_id
}
pub fn role(&self) -> &str {
&self.role
}
pub fn content(&self) -> &str {
&self.content
}
pub fn created_at_ms(&self) -> u64 {
self.created_at_ms
}
pub fn to_expr(&self) -> Expr {
Expr::Map(vec![
field("kind", Expr::String(GATEWAY_THREAD_MESSAGE_KIND.to_owned())),
field("id", Expr::String(self.id.clone())),
field("thread-id", Expr::String(self.thread_id.clone())),
field("role", Expr::String(self.role.clone())),
field("content", Expr::String(self.content.clone())),
field(
"created-at-ms",
Expr::String(self.created_at_ms.to_string()),
),
])
}
}
pub trait GatewayStateStore {
fn put_file(&mut self, file: GatewayFile, bytes: Vec<u8>) -> Result<()>;
fn file(&self, file_id: &str) -> Option<GatewayFile>;
fn file_bytes(&self, file_id: &str) -> Option<Vec<u8>>;
fn put_batch(&mut self, batch: GatewayBatch) -> Result<()>;
fn batch(&self, batch_id: &str) -> Option<GatewayBatch>;
fn put_thread(&mut self, thread: GatewayThread) -> Result<()>;
fn thread(&self, thread_id: &str) -> Option<GatewayThread>;
fn put_thread_message(&mut self, message: GatewayThreadMessage) -> Result<()>;
fn thread_messages(&self, thread_id: &str) -> Vec<GatewayThreadMessage>;
fn put_vector_store(&mut self, vector_store: GatewayVectorStore) -> Result<()>;
fn vector_store(&self, vector_store_id: &str) -> Option<GatewayVectorStore>;
}
fn metadata_expr(metadata: &[(String, String)]) -> Expr {
Expr::Map(
metadata
.iter()
.map(|(key, value)| (Expr::String(key.clone()), Expr::String(value.clone())))
.collect(),
)
}
use sim_value::build::entry as field;
fn optional_string_field(name: &str, value: Option<&str>) -> (Expr, Expr) {
field(
name,
value
.map(|value| Expr::String(value.to_owned()))
.unwrap_or(Expr::Nil),
)
}
fn optional_u64_field(name: &str, value: Option<u64>) -> (Expr, Expr) {
field(
name,
value
.map(|value| Expr::String(value.to_string()))
.unwrap_or(Expr::Nil),
)
}