sim_lib_openai_server/routes/
execution_record.rs1use sim_kernel::{ContentId, Expr, Symbol};
17
18use crate::{
19 clock::GatewayClock,
20 content_id::{content_id_for_expr, request_content_id},
21 ids::GatewayIdGenerator,
22 objects::{GatewayEvent, GatewayRequest, GatewayResponse, GatewayRun},
23 runtime::redacted_gateway_request,
24 storage::GatewayStore,
25};
26
27use super::errors::OpenAiRouteError;
28
29type RouteResult<T> = std::result::Result<T, OpenAiRouteError>;
30
31#[derive(Clone, Debug)]
39pub struct GatewayRunIdGenerators {
40 request: GatewayIdGenerator,
41 run: GatewayIdGenerator,
42 pub(crate) response: GatewayIdGenerator,
44 event: GatewayIdGenerator,
45}
46
47impl GatewayRunIdGenerators {
48 pub fn deterministic(start: u64) -> Self {
51 Self {
52 request: GatewayIdGenerator::deterministic("gwreq", start),
53 run: GatewayIdGenerator::deterministic("gwrun", start),
54 response: GatewayIdGenerator::deterministic("resp", start),
55 event: GatewayIdGenerator::deterministic("gwevt", start),
56 }
57 }
58
59 pub(crate) fn next_event_id(&mut self) -> sim_kernel::Result<String> {
60 self.event.next_id()
61 }
62}
63
64pub(crate) struct EventInput {
66 sequence: u64,
67 kind: Symbol,
68 payload: Expr,
69}
70
71impl EventInput {
72 pub(crate) fn new(sequence: u64, kind: &'static str, payload: Expr) -> Self {
73 Self::from_symbol(sequence, Symbol::new(kind), payload)
74 }
75
76 pub(crate) fn from_symbol(sequence: u64, kind: Symbol, payload: Expr) -> Self {
77 Self {
78 sequence,
79 kind,
80 payload,
81 }
82 }
83}
84
85#[derive(Default)]
87pub(crate) struct EventLog {
88 pub(crate) content_ids: Vec<ContentId>,
89 pub(crate) events: Vec<GatewayEvent>,
90}
91
92pub(crate) fn append_event<S, C>(
95 store: &mut S,
96 ids: &mut GatewayRunIdGenerators,
97 clock: &mut C,
98 run_id: &str,
99 input: EventInput,
100 store_event: bool,
101 event_log: &mut EventLog,
102) -> RouteResult<()>
103where
104 S: GatewayStore,
105 C: GatewayClock,
106{
107 let event = GatewayEvent::new(
108 ids.next_event_id().map_err(OpenAiRouteError::internal)?,
109 run_id,
110 input.sequence,
111 input.kind,
112 input.payload,
113 clock.now_ms().map_err(OpenAiRouteError::internal)?,
114 );
115 let id = content_id_for_expr(&event.to_expr()).map_err(OpenAiRouteError::internal)?;
116 if store_event {
117 store
118 .put_event(id.clone(), event.clone())
119 .map_err(OpenAiRouteError::internal)?;
120 }
121 event_log.content_ids.push(id);
122 event_log.events.push(event);
123 Ok(())
124}
125
126pub(crate) struct RunPrologue {
128 pub(crate) recorded_request: GatewayRequest,
129 pub(crate) request_content_id: ContentId,
130 pub(crate) run_id: String,
131 pub(crate) run_content_id: ContentId,
132}
133
134pub(crate) fn begin_run<S, C>(
143 store: &mut S,
144 ids: &mut GatewayRunIdGenerators,
145 clock: &mut C,
146 request: &GatewayRequest,
147 run_status: Option<Symbol>,
148 store_record: bool,
149) -> RouteResult<RunPrologue>
150where
151 S: GatewayStore,
152 C: GatewayClock,
153{
154 let recorded_request = redacted_gateway_request(request).with_metadata(
155 ids.request.next_id().map_err(OpenAiRouteError::internal)?,
156 clock.now_ms().map_err(OpenAiRouteError::internal)?,
157 );
158 let request_content_id =
159 request_content_id(&recorded_request).map_err(OpenAiRouteError::internal)?;
160 if store_record {
161 store
162 .put_request(request_content_id.clone(), recorded_request.clone())
163 .map_err(OpenAiRouteError::internal)?;
164 }
165
166 let run_id = ids.run.next_id().map_err(OpenAiRouteError::internal)?;
167 let mut run = GatewayRun::new(
168 run_id.clone(),
169 request_content_id.clone(),
170 clock.now_ms().map_err(OpenAiRouteError::internal)?,
171 );
172 if let Some(status) = run_status {
173 run = run.with_status(status);
174 }
175 let run_content_id = content_id_for_expr(&run.to_expr()).map_err(OpenAiRouteError::internal)?;
176 if store_record {
177 store
178 .put_run(run_content_id.clone(), run)
179 .map_err(OpenAiRouteError::internal)?;
180 }
181
182 Ok(RunPrologue {
183 recorded_request,
184 request_content_id,
185 run_id,
186 run_content_id,
187 })
188}
189
190#[derive(Clone, Debug)]
197pub struct GatewayRunExecution {
198 pub(crate) response: GatewayResponse,
199 pub(crate) request_content_id: Option<ContentId>,
200 pub(crate) run_content_id: Option<ContentId>,
201 pub(crate) event_content_ids: Vec<ContentId>,
202 pub(crate) events: Vec<GatewayEvent>,
203 pub(crate) response_id: Option<String>,
204 pub(crate) response_created_at_ms: Option<u64>,
205 pub(crate) response_content_id: Option<ContentId>,
206}
207
208impl GatewayRunExecution {
209 pub fn response(&self) -> &GatewayResponse {
211 &self.response
212 }
213
214 pub fn request_content_id(&self) -> Option<&ContentId> {
216 self.request_content_id.as_ref()
217 }
218
219 pub fn run_content_id(&self) -> Option<&ContentId> {
221 self.run_content_id.as_ref()
222 }
223
224 pub fn event_content_ids(&self) -> &[ContentId] {
226 &self.event_content_ids
227 }
228
229 pub fn events(&self) -> &[GatewayEvent] {
231 &self.events
232 }
233
234 pub fn response_id(&self) -> Option<&str> {
236 self.response_id.as_deref()
237 }
238
239 pub fn response_created_at_ms(&self) -> Option<u64> {
241 self.response_created_at_ms
242 }
243
244 pub fn response_content_id(&self) -> Option<&ContentId> {
246 self.response_content_id.as_ref()
247 }
248
249 pub(crate) fn error(error: OpenAiRouteError) -> Self {
250 Self {
251 response: error.into_response(),
252 request_content_id: None,
253 run_content_id: None,
254 event_content_ids: Vec::new(),
255 events: Vec::new(),
256 response_id: None,
257 response_created_at_ms: None,
258 response_content_id: None,
259 }
260 }
261}
262
263pub(crate) fn response_usage_expr(response: &Expr) -> Expr {
265 response_field(response, "usage")
266 .cloned()
267 .unwrap_or(Expr::Nil)
268}
269
270fn response_field<'a>(expr: &'a Expr, name: &str) -> Option<&'a Expr> {
271 let Expr::Map(entries) = expr else {
272 return None;
273 };
274 entries.iter().find_map(|(key, value)| match key {
275 Expr::Symbol(symbol) if symbol.namespace.is_none() && symbol.name.as_ref() == name => {
276 Some(value)
277 }
278 _ => None,
279 })
280}