Skip to main content

kernel/event/record/
prefix_held.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! One wait a model call took at its endpoint's gate behind a pre-warm
7//! of the prefix a building shares, so a person can read off the history
8//! why a call went out late and what the pre-warm cost
9//! (`crates/kernel/spec/Event/Record.lean` D61, gateway D43).
10//!
11//! The pre-warm's request is absent for the reason `ModelCalled` carries
12//! no body: its bytes are the shared part of the request that waited,
13//! which `prompt_assembled` already recorded.
14
15use serde::{Deserialize, Serialize};
16
17use crate::budget::UsdMicros;
18use crate::error::AxError;
19use crate::model::ModelUsage;
20
21/// `prefix_held`: one attempt's wait at its endpoint's gate.
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
24pub struct PrefixHeld {
25    /// Whole microseconds from reaching the gate to being let on toward
26    /// a ticket, on the gate's injected monotonic clock.
27    pub waited_us: u64,
28    /// How the pre-warm this call waited for ended.
29    pub ended: PrewarmEnd,
30    /// What the pre-warm cost or why it was refused; only on the line of
31    /// the call that sent it, so one request is counted once.
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub sent: Option<PrewarmSent>,
34}
35
36/// How one pre-warm ended, as every call that waited for it saw it.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
38#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
39#[serde(rename_all = "snake_case")]
40pub enum PrewarmEnd {
41    /// The provider answered it.
42    Answered,
43    /// The provider refused it, or it failed on the way.
44    Refused,
45    /// `HOLD_MAX` passed before it came back, or it found no permit in
46    /// time.
47    TimedOut,
48    /// The thread that sent it stopped before it came back.
49    Abandoned,
50}
51
52/// The pre-warm's own answer, on the line of the call that sent it.
53///
54/// `Refused` is tried first when reading, as in `CacheRenewed`: every
55/// field of `Answered` may be absent, so it would read any line.
56#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
57#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
58#[serde(untagged)]
59pub enum PrewarmSent {
60    /// The failure the pre-warm came back with, unchanged.
61    Refused {
62        /// The failure the provider or the gate returned.
63        refused: AxError,
64    },
65    /// The provider answered the pre-warm.
66    Answered {
67        /// Four token counts, as the provider reported them. Absent when
68        /// it reported none, which is not the same fact as zero.
69        #[serde(default, skip_serializing_if = "Option::is_none")]
70        #[cfg_attr(
71            feature = "schema",
72            schemars(with = "Option<std::collections::BTreeMap<String, u64>>")
73        )]
74        usage: Option<ModelUsage>,
75        /// What the provider itself billed, when it returns a figure.
76        #[serde(default, skip_serializing_if = "Option::is_none")]
77        billed_usd_micros: Option<UsdMicros>,
78    },
79}