1use std::error::Error;
2use std::fmt;
3use std::sync::Arc;
4
5use crate::model_executor::ExecutorRequestOrigin;
6
7use super::{
8 has_active, BoundExecutionResourceMaintenance, ExecutionEvent, ExecutionEventCursor,
9 ExecutionEventKind, RequestIdentity, RunId, TrustedExecutionEventContext,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct ExecutionEventSinkError {
14 message: String,
15}
16
17impl ExecutionEventSinkError {
18 pub fn new(message: impl Into<String>) -> Self {
19 Self {
20 message: message.into(),
21 }
22 }
23
24 pub fn message(&self) -> &str {
25 &self.message
26 }
27}
28
29impl fmt::Display for ExecutionEventSinkError {
30 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31 formatter.write_str(&self.message)
32 }
33}
34
35impl Error for ExecutionEventSinkError {}
36
37mod event_sink_seal {
38 pub struct Seal;
39}
40
41pub struct EventEmissionPermit {
45 event: ExecutionEvent,
46 _seal: event_sink_seal::Seal,
47}
48
49impl EventEmissionPermit {
50 pub fn event(&self) -> &ExecutionEvent {
51 &self.event
52 }
53
54 pub fn into_event(self) -> ExecutionEvent {
55 self.event
56 }
57}
58
59pub struct EventBatchEmissionPermit {
62 events: Vec<ExecutionEvent>,
63 _seal: event_sink_seal::Seal,
64}
65
66impl EventBatchEmissionPermit {
67 pub fn events(&self) -> &[ExecutionEvent] {
68 &self.events
69 }
70
71 pub fn into_events(self) -> Vec<ExecutionEvent> {
72 self.events
73 }
74}
75
76#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
77pub enum ExecutionEventCapturePolicy {
78 #[default]
79 AllFrames,
80 FirstFramePerRequest,
81 LifecycleOnly,
82}
83
84impl ExecutionEventCapturePolicy {
85 pub const fn captures_frame(self, completed_frames: u64) -> bool {
86 match self {
87 Self::AllFrames => true,
88 Self::FirstFramePerRequest | Self::LifecycleOnly => completed_frames == 0,
89 }
90 }
91
92 pub const fn records_event(self, kind: ExecutionEventKind) -> bool {
93 match self {
94 Self::AllFrames | Self::FirstFramePerRequest => true,
95 Self::LifecycleOnly => matches!(
96 kind,
97 ExecutionEventKind::RequestAccepted
98 | ExecutionEventKind::PlanBuilt
99 | ExecutionEventKind::FailureObserved
100 | ExecutionEventKind::SequenceCompleted
101 | ExecutionEventKind::SequenceAborted
102 | ExecutionEventKind::RequestCompleted
103 | ExecutionEventKind::RequestFailed
104 ),
105 }
106 }
107
108 pub const fn as_str(self) -> &'static str {
109 match self {
110 Self::AllFrames => "all_frames",
111 Self::FirstFramePerRequest => "first_frame_per_request",
112 Self::LifecycleOnly => "lifecycle_only",
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
118pub enum ExecutionEventSinkEnablement {
119 None,
120 All,
121 #[default]
122 PerKind,
123}
124
125pub trait ExecutionEventSink: Send + Sync {
126 fn enablement(&self) -> ExecutionEventSinkEnablement {
132 ExecutionEventSinkEnablement::PerKind
133 }
134
135 fn is_enabled(&self, kind: ExecutionEventKind) -> bool;
136
137 fn device_timing_mode(&self) -> super::super::DeviceTimingMode {
138 super::super::DeviceTimingMode::Off
139 }
140
141 fn capture_policy(&self) -> ExecutionEventCapturePolicy {
142 ExecutionEventCapturePolicy::AllFrames
143 }
144
145 fn capture_policy_for_request(
146 &self,
147 _origin: ExecutorRequestOrigin,
148 ) -> ExecutionEventCapturePolicy {
149 self.capture_policy()
150 }
151
152 fn record_device_submission_attribution(
153 &self,
154 _attribution: &super::super::BoundDeviceSubmissionAttribution,
155 ) -> Result<(), ExecutionEventSinkError> {
156 Ok(())
157 }
158
159 fn record_physical_device_submission_timing(
160 &self,
161 _completion: &super::super::OperationCompletionReceipt,
162 ) -> Result<(), ExecutionEventSinkError> {
163 Ok(())
164 }
165
166 fn records_execution_resource_maintenance(&self) -> bool {
170 false
171 }
172
173 fn record_execution_resource_maintenance(
174 &self,
175 _maintenance: BoundExecutionResourceMaintenance,
176 ) -> Result<(), ExecutionEventSinkError> {
177 Ok(())
178 }
179
180 fn records_prefix_restore_decisions(&self) -> bool {
183 false
184 }
185
186 fn record_prefix_restore_decision(
187 &self,
188 _observation: &crate::model_executor::PrefixRestoreObservation<'_>,
189 ) -> Result<(), ExecutionEventSinkError> {
190 Ok(())
191 }
192
193 fn record(&self, permit: EventEmissionPermit) -> Result<(), ExecutionEventSinkError>;
194
195 fn record_batch(
198 &self,
199 permit: EventBatchEmissionPermit,
200 ) -> Result<(), ExecutionEventSinkError> {
201 for event in permit.into_events() {
202 self.record(EventEmissionPermit {
203 event,
204 _seal: event_sink_seal::Seal,
205 })?;
206 }
207 Ok(())
208 }
209}
210
211enum ExecutionEventSinkHandle<'sink, S>
212where
213 S: ExecutionEventSink + ?Sized,
214{
215 Borrowed(&'sink S),
216 Shared(Arc<S>),
217}
218
219impl<S> ExecutionEventSinkHandle<'_, S>
220where
221 S: ExecutionEventSink + ?Sized,
222{
223 #[inline]
224 fn as_sink(&self) -> &S {
225 match self {
226 Self::Borrowed(sink) => *sink,
227 Self::Shared(sink) => sink.as_ref(),
228 }
229 }
230}
231
232pub struct ExecutionEventEmitter<'sink, S = dyn ExecutionEventSink>
233where
234 S: ExecutionEventSink + ?Sized,
235{
236 sink: ExecutionEventSinkHandle<'sink, S>,
237 cursor: ExecutionEventCursor,
238 capture_policy: ExecutionEventCapturePolicy,
239 sink_enablement: ExecutionEventSinkEnablement,
240 sink_failed: bool,
241}
242
243impl<'sink, S> ExecutionEventEmitter<'sink, S>
244where
245 S: ExecutionEventSink + ?Sized,
246{
247 #[inline]
248 pub fn new(sink: &'sink S, run_id: RunId, request_id: RequestIdentity) -> Self {
249 let sink_enablement = sink.enablement();
250 let capture_policy = if sink_enablement == ExecutionEventSinkEnablement::None {
251 ExecutionEventCapturePolicy::AllFrames
252 } else {
253 sink.capture_policy()
254 };
255 Self {
256 sink: ExecutionEventSinkHandle::Borrowed(sink),
257 cursor: ExecutionEventCursor::new(run_id, request_id),
258 capture_policy,
259 sink_enablement,
260 sink_failed: false,
261 }
262 }
263}
264
265impl ExecutionEventEmitter<'static, dyn ExecutionEventSink> {
266 pub fn from_shared(
271 sink: Arc<dyn ExecutionEventSink>,
272 run_id: RunId,
273 request_id: RequestIdentity,
274 ) -> ExecutionEventEmitter<'static> {
275 let sink_enablement = sink.enablement();
276 let capture_policy = if sink_enablement == ExecutionEventSinkEnablement::None {
277 ExecutionEventCapturePolicy::AllFrames
278 } else {
279 sink.capture_policy()
280 };
281 Self::from_shared_parts(sink, run_id, request_id, capture_policy, sink_enablement)
282 }
283
284 pub fn from_shared_with_capture_policy(
285 sink: Arc<dyn ExecutionEventSink>,
286 run_id: RunId,
287 request_id: RequestIdentity,
288 capture_policy: ExecutionEventCapturePolicy,
289 ) -> ExecutionEventEmitter<'static> {
290 let sink_enablement = sink.enablement();
291 Self::from_shared_parts(sink, run_id, request_id, capture_policy, sink_enablement)
292 }
293
294 fn from_shared_parts(
295 sink: Arc<dyn ExecutionEventSink>,
296 run_id: RunId,
297 request_id: RequestIdentity,
298 capture_policy: ExecutionEventCapturePolicy,
299 sink_enablement: ExecutionEventSinkEnablement,
300 ) -> ExecutionEventEmitter<'static> {
301 ExecutionEventEmitter {
302 sink: ExecutionEventSinkHandle::Shared(sink),
303 cursor: ExecutionEventCursor::new(run_id, request_id),
304 capture_policy,
305 sink_enablement,
306 sink_failed: false,
307 }
308 }
309}
310
311impl<'sink, S> ExecutionEventEmitter<'sink, S>
312where
313 S: ExecutionEventSink + ?Sized,
314{
315 #[inline]
316 fn records_event(&self, event: &ExecutionEvent) -> bool {
317 self.capture_policy.records_event(event.kind())
318 && match self.sink_enablement {
319 ExecutionEventSinkEnablement::None => false,
320 ExecutionEventSinkEnablement::All => true,
321 ExecutionEventSinkEnablement::PerKind => {
322 self.sink.as_sink().is_enabled(event.kind())
323 }
324 }
325 }
326
327 #[inline]
328 fn validate_next(
329 cursor: &mut ExecutionEventCursor,
330 event: &ExecutionEvent,
331 context: &TrustedExecutionEventContext<'_>,
332 ) -> Result<(), ExecutionEventSinkError> {
333 match event.kind() {
334 ExecutionEventKind::FrameStarted | ExecutionEventKind::NodeStarted => context
335 .active_binding()
336 .ok_or_else(|| {
337 ExecutionEventSinkError::new(
338 "active execution emission lacks live sequence evidence",
339 )
340 })?
341 .ensure_open_for_emission()
342 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
343 ExecutionEventKind::OperationSubmitted
344 | ExecutionEventKind::NodeRetired
345 | ExecutionEventKind::FrameCompleted => context
346 .active_binding()
347 .ok_or_else(|| {
348 ExecutionEventSinkError::new(
349 "active execution emission lacks live sequence evidence",
350 )
351 })?
352 .ensure_live_for_emission()
353 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
354 ExecutionEventKind::FailureObserved if has_active(event.identity().parts()) => context
355 .active_binding()
356 .ok_or_else(|| {
357 ExecutionEventSinkError::new(
358 "active execution emission lacks live sequence evidence",
359 )
360 })?
361 .ensure_live_for_emission()
362 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))?,
363 _ => {}
364 }
365 cursor
366 .observe_candidate_in_place(event, context)
367 .map_err(|error| ExecutionEventSinkError::new(error.to_string()))
368 }
369
370 #[inline]
371 pub fn emit(
372 &mut self,
373 event: ExecutionEvent,
374 context: &TrustedExecutionEventContext<'_>,
375 ) -> Result<(), ExecutionEventSinkError> {
376 if self.sink_enablement == ExecutionEventSinkEnablement::None {
377 let mut next_cursor = self.cursor.clone();
378 Self::validate_next(&mut next_cursor, &event, context)?;
379 self.cursor = next_cursor;
380 return Ok(());
381 }
382 if self.sink_failed {
383 return Err(ExecutionEventSinkError::new(
384 "execution event emitter is sealed after a sink failure",
385 ));
386 }
387 let mut next_cursor = self.cursor.clone();
388 Self::validate_next(&mut next_cursor, &event, context)?;
389 if self.records_event(&event) {
390 let permit = EventEmissionPermit {
391 event,
392 _seal: event_sink_seal::Seal,
393 };
394 if let Err(error) = self.sink.as_sink().record(permit) {
395 self.sink_failed = true;
396 return Err(error);
397 }
398 }
399 self.cursor = next_cursor;
400 Ok(())
401 }
402
403 pub fn emit_batch(
404 &mut self,
405 events: Vec<ExecutionEvent>,
406 contexts: &[TrustedExecutionEventContext<'_>],
407 ) -> Result<(), ExecutionEventSinkError> {
408 if self.sink_enablement == ExecutionEventSinkEnablement::None {
409 if events.len() != contexts.len() {
410 return Err(ExecutionEventSinkError::new(
411 "execution event batch context count differs from event count",
412 ));
413 }
414 if events.is_empty() {
415 return Ok(());
416 }
417 let mut next_cursor = self.cursor.clone();
418 for (event, context) in events.iter().zip(contexts) {
419 Self::validate_next(&mut next_cursor, event, context)?;
420 }
421 self.cursor = next_cursor;
422 return Ok(());
423 }
424 if self.sink_failed {
425 return Err(ExecutionEventSinkError::new(
426 "execution event emitter is sealed after a sink failure",
427 ));
428 }
429 if events.len() != contexts.len() {
430 return Err(ExecutionEventSinkError::new(
431 "execution event batch context count differs from event count",
432 ));
433 }
434 if events.is_empty() {
435 return Ok(());
436 }
437
438 let mut next_cursor = self.cursor.clone();
439 for (event, context) in events.iter().zip(contexts) {
440 Self::validate_next(&mut next_cursor, event, context)?;
441 }
442
443 let all_enabled = events.iter().all(|event| self.records_event(event));
444 if all_enabled {
445 let permit = EventBatchEmissionPermit {
446 events,
447 _seal: event_sink_seal::Seal,
448 };
449 if let Err(error) = self.sink.as_sink().record_batch(permit) {
450 self.sink_failed = true;
451 return Err(error);
452 }
453 } else {
454 for event in events {
455 if !self.records_event(&event) {
456 continue;
457 }
458 let permit = EventEmissionPermit {
459 event,
460 _seal: event_sink_seal::Seal,
461 };
462 if let Err(error) = self.sink.as_sink().record(permit) {
463 self.sink_failed = true;
464 return Err(error);
465 }
466 }
467 }
468 self.cursor = next_cursor;
469 Ok(())
470 }
471
472 pub fn cursor(&self) -> &ExecutionEventCursor {
473 &self.cursor
474 }
475
476 pub const fn sink_failed(&self) -> bool {
477 self.sink_failed
478 }
479}
480
481#[derive(Debug, Default)]
482pub struct DisabledExecutionEventSink;
483
484impl ExecutionEventSink for DisabledExecutionEventSink {
485 fn enablement(&self) -> ExecutionEventSinkEnablement {
486 ExecutionEventSinkEnablement::None
487 }
488
489 fn is_enabled(&self, _kind: ExecutionEventKind) -> bool {
490 false
491 }
492
493 fn record(&self, _permit: EventEmissionPermit) -> Result<(), ExecutionEventSinkError> {
494 Ok(())
495 }
496}