use sim_kernel::{ContentId, Expr, Symbol};
use crate::{
clock::GatewayClock,
content_id::{content_id_for_expr, request_content_id},
ids::GatewayIdGenerator,
objects::{GatewayEvent, GatewayRequest, GatewayResponse, GatewayRun},
runtime::redacted_gateway_request,
storage::GatewayStore,
};
use super::errors::OpenAiRouteError;
type RouteResult<T> = std::result::Result<T, OpenAiRouteError>;
#[derive(Clone, Debug)]
pub struct GatewayRunIdGenerators {
request: GatewayIdGenerator,
run: GatewayIdGenerator,
pub(crate) response: GatewayIdGenerator,
event: GatewayIdGenerator,
}
impl GatewayRunIdGenerators {
pub fn deterministic(start: u64) -> Self {
Self {
request: GatewayIdGenerator::deterministic("gwreq", start),
run: GatewayIdGenerator::deterministic("gwrun", start),
response: GatewayIdGenerator::deterministic("resp", start),
event: GatewayIdGenerator::deterministic("gwevt", start),
}
}
pub(crate) fn next_event_id(&mut self) -> sim_kernel::Result<String> {
self.event.next_id()
}
}
pub(crate) struct EventInput {
sequence: u64,
kind: Symbol,
payload: Expr,
}
impl EventInput {
pub(crate) fn new(sequence: u64, kind: &'static str, payload: Expr) -> Self {
Self::from_symbol(sequence, Symbol::new(kind), payload)
}
pub(crate) fn from_symbol(sequence: u64, kind: Symbol, payload: Expr) -> Self {
Self {
sequence,
kind,
payload,
}
}
}
#[derive(Default)]
pub(crate) struct EventLog {
pub(crate) content_ids: Vec<ContentId>,
pub(crate) events: Vec<GatewayEvent>,
}
pub(crate) fn append_event<S, C>(
store: &mut S,
ids: &mut GatewayRunIdGenerators,
clock: &mut C,
run_id: &str,
input: EventInput,
store_event: bool,
event_log: &mut EventLog,
) -> RouteResult<()>
where
S: GatewayStore,
C: GatewayClock,
{
let event = GatewayEvent::new(
ids.next_event_id().map_err(OpenAiRouteError::internal)?,
run_id,
input.sequence,
input.kind,
input.payload,
clock.now_ms().map_err(OpenAiRouteError::internal)?,
);
let id = content_id_for_expr(&event.to_expr()).map_err(OpenAiRouteError::internal)?;
if store_event {
store
.put_event(id.clone(), event.clone())
.map_err(OpenAiRouteError::internal)?;
}
event_log.content_ids.push(id);
event_log.events.push(event);
Ok(())
}
pub(crate) struct RunPrologue {
pub(crate) recorded_request: GatewayRequest,
pub(crate) request_content_id: ContentId,
pub(crate) run_id: String,
pub(crate) run_content_id: ContentId,
}
pub(crate) fn begin_run<S, C>(
store: &mut S,
ids: &mut GatewayRunIdGenerators,
clock: &mut C,
request: &GatewayRequest,
run_status: Option<Symbol>,
store_record: bool,
) -> RouteResult<RunPrologue>
where
S: GatewayStore,
C: GatewayClock,
{
let recorded_request = redacted_gateway_request(request).with_metadata(
ids.request.next_id().map_err(OpenAiRouteError::internal)?,
clock.now_ms().map_err(OpenAiRouteError::internal)?,
);
let request_content_id =
request_content_id(&recorded_request).map_err(OpenAiRouteError::internal)?;
if store_record {
store
.put_request(request_content_id.clone(), recorded_request.clone())
.map_err(OpenAiRouteError::internal)?;
}
let run_id = ids.run.next_id().map_err(OpenAiRouteError::internal)?;
let mut run = GatewayRun::new(
run_id.clone(),
request_content_id.clone(),
clock.now_ms().map_err(OpenAiRouteError::internal)?,
);
if let Some(status) = run_status {
run = run.with_status(status);
}
let run_content_id = content_id_for_expr(&run.to_expr()).map_err(OpenAiRouteError::internal)?;
if store_record {
store
.put_run(run_content_id.clone(), run)
.map_err(OpenAiRouteError::internal)?;
}
Ok(RunPrologue {
recorded_request,
request_content_id,
run_id,
run_content_id,
})
}
#[derive(Clone, Debug)]
pub struct GatewayRunExecution {
pub(crate) 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) events: Vec<GatewayEvent>,
pub(crate) response_id: Option<String>,
pub(crate) response_created_at_ms: Option<u64>,
pub(crate) response_content_id: Option<ContentId>,
}
impl GatewayRunExecution {
pub fn response(&self) -> &GatewayResponse {
&self.response
}
pub fn request_content_id(&self) -> Option<&ContentId> {
self.request_content_id.as_ref()
}
pub fn run_content_id(&self) -> Option<&ContentId> {
self.run_content_id.as_ref()
}
pub fn event_content_ids(&self) -> &[ContentId] {
&self.event_content_ids
}
pub fn events(&self) -> &[GatewayEvent] {
&self.events
}
pub fn response_id(&self) -> Option<&str> {
self.response_id.as_deref()
}
pub fn response_created_at_ms(&self) -> Option<u64> {
self.response_created_at_ms
}
pub fn response_content_id(&self) -> Option<&ContentId> {
self.response_content_id.as_ref()
}
pub(crate) fn error(error: OpenAiRouteError) -> Self {
Self {
response: error.into_response(),
request_content_id: None,
run_content_id: None,
event_content_ids: Vec::new(),
events: Vec::new(),
response_id: None,
response_created_at_ms: None,
response_content_id: None,
}
}
}
pub(crate) fn response_usage_expr(response: &Expr) -> Expr {
response_field(response, "usage")
.cloned()
.unwrap_or(Expr::Nil)
}
fn response_field<'a>(expr: &'a Expr, name: &str) -> Option<&'a Expr> {
let Expr::Map(entries) = expr else {
return None;
};
entries.iter().find_map(|(key, value)| match key {
Expr::Symbol(symbol) if symbol.namespace.is_none() && symbol.name.as_ref() == name => {
Some(value)
}
_ => None,
})
}