Skip to main content

mesh_llm_runtime_event_contracts/
facts.rs

1use crate::{
2    DiagnosticEventKind, EventSystemHealthEventKind, FactData, FactMetadata, GenerationEventKind,
3    KvRuntimeStateEventKind, ModelAvailabilityEventKind, ModelLoadingEventKind,
4    ModelPreparationEventKind, ModelUnloadingEventKind, NativeRuntimeEventKind,
5    NodeAvailabilityEventKind, PrefillEventKind, RequestEventKind, ResourceHealthEventKind,
6    ScopeIdentities, SessionEventKind, StageTopologyEventKind,
7};
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct FamilyFact<K> {
11    kind: K,
12    data: FactData,
13    metadata: Option<FactMetadata>,
14}
15
16impl<K> FamilyFact<K> {
17    #[must_use]
18    pub fn new(kind: K) -> Self {
19        Self {
20            kind,
21            data: FactData::default(),
22            metadata: None,
23        }
24    }
25
26    #[must_use]
27    pub const fn with_data(kind: K, data: FactData) -> Self {
28        Self {
29            kind,
30            data,
31            metadata: None,
32        }
33    }
34
35    /// Construct a fact with producer metadata. Existing constructors remain
36    /// source-compatible; callers that need a native or reconciled source use
37    /// this additive constructor.
38    #[must_use]
39    pub fn with_metadata(kind: K, data: FactData, metadata: FactMetadata) -> Self {
40        Self {
41            kind,
42            data,
43            metadata: Some(metadata),
44        }
45    }
46
47    #[must_use]
48    pub const fn kind(&self) -> &K {
49        &self.kind
50    }
51
52    #[must_use]
53    pub const fn data(&self) -> &FactData {
54        &self.data
55    }
56
57    #[must_use]
58    pub const fn metadata(&self) -> Option<&FactMetadata> {
59        self.metadata.as_ref()
60    }
61
62    /// Fill only missing scope fields from a known operation scope.
63    ///
64    /// Synthetic facts may have some explicit identities already. Those values
65    /// remain authoritative; this helper only supplies fields omitted by the
66    /// synthetic producer and never changes producer metadata.
67    #[must_use]
68    pub fn with_scope(mut self, fallback: &ScopeIdentities) -> Self {
69        if self.data.scope.model_id.is_none() {
70            self.data.scope.model_id = fallback.model_id.clone();
71        }
72        if self.data.scope.topology_id.is_none() {
73            self.data.scope.topology_id = fallback.topology_id.clone();
74        }
75        if self.data.scope.stage.is_none() {
76            self.data.scope.stage = fallback.stage.clone();
77        }
78        if self.data.scope.session_id.is_none() {
79            self.data.scope.session_id = fallback.session_id.clone();
80        }
81        if self.data.scope.request_id.is_none() {
82            self.data.scope.request_id = fallback.request_id.clone();
83        }
84        if self.data.scope.device_id.is_none() {
85            self.data.scope.device_id = fallback.device_id.clone();
86        }
87        self
88    }
89}
90
91pub type NativeRuntimeFact = FamilyFact<NativeRuntimeEventKind>;
92pub type ModelPreparationFact = FamilyFact<ModelPreparationEventKind>;
93pub type ModelLoadingFact = FamilyFact<ModelLoadingEventKind>;
94pub type ModelAvailabilityFact = FamilyFact<ModelAvailabilityEventKind>;
95pub type ModelUnloadingFact = FamilyFact<ModelUnloadingEventKind>;
96pub type StageTopologyFact = FamilyFact<StageTopologyEventKind>;
97pub type SessionFact = FamilyFact<SessionEventKind>;
98pub type RequestFact = FamilyFact<RequestEventKind>;
99pub type PrefillFact = FamilyFact<PrefillEventKind>;
100pub type GenerationFact = FamilyFact<GenerationEventKind>;
101pub type KvRuntimeStateFact = FamilyFact<KvRuntimeStateEventKind>;
102pub type ResourceHealthFact = FamilyFact<ResourceHealthEventKind>;
103pub type DiagnosticFact = FamilyFact<DiagnosticEventKind>;
104pub type NodeAvailabilityFact = FamilyFact<NodeAvailabilityEventKind>;
105pub type EventSystemHealthFact = FamilyFact<EventSystemHealthEventKind>;
106
107#[derive(Debug, Clone, PartialEq)]
108pub enum RuntimeFact {
109    NativeRuntime(NativeRuntimeFact),
110    ModelPreparation(ModelPreparationFact),
111    ModelLoading(ModelLoadingFact),
112    ModelAvailability(ModelAvailabilityFact),
113    ModelUnloading(ModelUnloadingFact),
114    StageTopology(StageTopologyFact),
115    Session(SessionFact),
116    Request(RequestFact),
117    Prefill(PrefillFact),
118    Generation(GenerationFact),
119    KvRuntimeState(KvRuntimeStateFact),
120    ResourceHealth(ResourceHealthFact),
121    Diagnostic(DiagnosticFact),
122    NodeAvailability(NodeAvailabilityFact),
123    EventSystemHealth(EventSystemHealthFact),
124}
125
126impl RuntimeFact {
127    #[must_use]
128    pub const fn data(&self) -> &FactData {
129        match self {
130            Self::NativeRuntime(fact) => fact.data(),
131            Self::ModelPreparation(fact) => fact.data(),
132            Self::ModelLoading(fact) => fact.data(),
133            Self::ModelAvailability(fact) => fact.data(),
134            Self::ModelUnloading(fact) => fact.data(),
135            Self::StageTopology(fact) => fact.data(),
136            Self::Session(fact) => fact.data(),
137            Self::Request(fact) => fact.data(),
138            Self::Prefill(fact) => fact.data(),
139            Self::Generation(fact) => fact.data(),
140            Self::KvRuntimeState(fact) => fact.data(),
141            Self::ResourceHealth(fact) => fact.data(),
142            Self::Diagnostic(fact) => fact.data(),
143            Self::NodeAvailability(fact) => fact.data(),
144            Self::EventSystemHealth(fact) => fact.data(),
145        }
146    }
147
148    #[must_use]
149    pub fn metadata(&self) -> Option<&FactMetadata> {
150        match self {
151            Self::NativeRuntime(fact) => fact.metadata(),
152            Self::ModelPreparation(fact) => fact.metadata(),
153            Self::ModelLoading(fact) => fact.metadata(),
154            Self::ModelAvailability(fact) => fact.metadata(),
155            Self::ModelUnloading(fact) => fact.metadata(),
156            Self::StageTopology(fact) => fact.metadata(),
157            Self::Session(fact) => fact.metadata(),
158            Self::Request(fact) => fact.metadata(),
159            Self::Prefill(fact) => fact.metadata(),
160            Self::Generation(fact) => fact.metadata(),
161            Self::KvRuntimeState(fact) => fact.metadata(),
162            Self::ResourceHealth(fact) => fact.metadata(),
163            Self::Diagnostic(fact) => fact.metadata(),
164            Self::NodeAvailability(fact) => fact.metadata(),
165            Self::EventSystemHealth(fact) => fact.metadata(),
166        }
167    }
168
169    /// Attach producer metadata while preserving the existing family kind
170    /// and fact data. This keeps metadata additive to `RuntimeFact`.
171    #[must_use]
172    pub fn with_metadata(self, metadata: FactMetadata) -> Self {
173        match self {
174            Self::NativeRuntime(fact) => {
175                Self::NativeRuntime(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
176            }
177            Self::ModelPreparation(fact) => {
178                Self::ModelPreparation(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
179            }
180            Self::ModelLoading(fact) => {
181                Self::ModelLoading(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
182            }
183            Self::ModelAvailability(fact) => {
184                Self::ModelAvailability(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
185            }
186            Self::ModelUnloading(fact) => {
187                Self::ModelUnloading(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
188            }
189            Self::StageTopology(fact) => {
190                Self::StageTopology(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
191            }
192            Self::Session(fact) => {
193                Self::Session(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
194            }
195            Self::Request(fact) => {
196                Self::Request(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
197            }
198            Self::Prefill(fact) => {
199                Self::Prefill(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
200            }
201            Self::Generation(fact) => {
202                Self::Generation(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
203            }
204            Self::KvRuntimeState(fact) => {
205                Self::KvRuntimeState(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
206            }
207            Self::ResourceHealth(fact) => {
208                Self::ResourceHealth(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
209            }
210            Self::Diagnostic(fact) => {
211                Self::Diagnostic(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
212            }
213            Self::NodeAvailability(fact) => {
214                Self::NodeAvailability(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
215            }
216            Self::EventSystemHealth(fact) => {
217                Self::EventSystemHealth(FamilyFact::with_metadata(fact.kind, fact.data, metadata))
218            }
219        }
220    }
221
222    /// Fill missing scope fields on a synthetic fact without changing its
223    /// explicit identities or producer metadata.
224    #[must_use]
225    pub fn with_scope(self, fallback: &ScopeIdentities) -> Self {
226        match self {
227            Self::NativeRuntime(fact) => Self::NativeRuntime(fact.with_scope(fallback)),
228            Self::ModelPreparation(fact) => Self::ModelPreparation(fact.with_scope(fallback)),
229            Self::ModelLoading(fact) => Self::ModelLoading(fact.with_scope(fallback)),
230            Self::ModelAvailability(fact) => Self::ModelAvailability(fact.with_scope(fallback)),
231            Self::ModelUnloading(fact) => Self::ModelUnloading(fact.with_scope(fallback)),
232            Self::StageTopology(fact) => Self::StageTopology(fact.with_scope(fallback)),
233            Self::Session(fact) => Self::Session(fact.with_scope(fallback)),
234            Self::Request(fact) => Self::Request(fact.with_scope(fallback)),
235            Self::Prefill(fact) => Self::Prefill(fact.with_scope(fallback)),
236            Self::Generation(fact) => Self::Generation(fact.with_scope(fallback)),
237            Self::KvRuntimeState(fact) => Self::KvRuntimeState(fact.with_scope(fallback)),
238            Self::ResourceHealth(fact) => Self::ResourceHealth(fact.with_scope(fallback)),
239            Self::Diagnostic(fact) => Self::Diagnostic(fact.with_scope(fallback)),
240            Self::NodeAvailability(fact) => Self::NodeAvailability(fact.with_scope(fallback)),
241            Self::EventSystemHealth(fact) => Self::EventSystemHealth(fact.with_scope(fallback)),
242        }
243    }
244
245    #[must_use]
246    pub const fn kind_id(&self) -> &'static str {
247        match self {
248            Self::NativeRuntime(fact) => fact.kind().as_str(),
249            Self::ModelPreparation(fact) => fact.kind().as_str(),
250            Self::ModelLoading(fact) => fact.kind().as_str(),
251            Self::ModelAvailability(fact) => fact.kind().as_str(),
252            Self::ModelUnloading(fact) => fact.kind().as_str(),
253            Self::StageTopology(fact) => fact.kind().as_str(),
254            Self::Session(fact) => fact.kind().as_str(),
255            Self::Request(fact) => fact.kind().as_str(),
256            Self::Prefill(fact) => fact.kind().as_str(),
257            Self::Generation(fact) => fact.kind().as_str(),
258            Self::KvRuntimeState(fact) => fact.kind().as_str(),
259            Self::ResourceHealth(fact) => fact.kind().as_str(),
260            Self::Diagnostic(fact) => fact.kind().as_str(),
261            Self::NodeAvailability(fact) => fact.kind().as_str(),
262            Self::EventSystemHealth(fact) => fact.kind().as_str(),
263        }
264    }
265}