1use std::collections::{BTreeMap, BTreeSet};
4
5pub fn intervention_support(
9 mut points: Vec<eredu_core::intervention::InterventionPoint>,
10 capture: &eredu_core::capture::CaptureDiscovery,
11 mechanisms: &eredu_core::intervention::InterventionMechanisms,
12) -> eredu_core::intervention::InterventionDiscovery {
13 use eredu_core::{intervention::*, ObservationSupportStatus as S};
14 for point in &mut points {
15 point
16 .operations
17 .retain(|kind| mechanisms.operations.contains(kind));
18 point
19 .dtypes
20 .retain(|dtype| mechanisms.dtypes.contains(dtype));
21 point
22 .score_stages
23 .retain(|stage| mechanisms.score_stages.contains(stage));
24 let path = if point.routing.is_some() {
25 eredu_core::RoutingObservationField::SelectedExperts.path(&point.path)
26 } else {
27 point.path.clone()
28 };
29 if let Some(support) = capture.support.points.iter().find(|p| p.path == path) {
30 point.prefill = support.prefill.clone();
31 point.decode = support.decode.clone();
32 }
33 if point.axes.is_empty()
34 || point.operations.is_empty()
35 || (point.routing.is_none() && point.dtypes.is_empty())
36 {
37 point.prefill = S::Unsupported(
38 "required intervention geometry or native mechanism is not declared".into(),
39 );
40 point.decode = point.prefill.clone();
41 }
42 }
43 InterventionDiscovery {
44 schema_version: INTERVENTION_SCHEMA_VERSION,
45 artifact_identity: capture.artifact_identity.clone(),
46 session_identity: None,
47 points,
48 }
49}
50
51#[derive(Debug, Clone, Copy)]
53pub struct ObservationExecutionContext {
54 pub activation_inspection: bool,
56 pub partitioned: bool,
58 pub selected: bool,
60 pub mechanisms: eredu_core::ObservationMechanisms,
62}
63
64pub fn observation_support(
66 catalog: &eredu_core::ObservationCatalog,
67 context: ObservationExecutionContext,
68) -> eredu_core::ObservationSupportReport {
69 eredu_core::ObservationSupportReport {
70 schema_version: eredu_core::DISCOVERY_SCHEMA_VERSION,
71 capture: Default::default(),
72 points: catalog
73 .points
74 .iter()
75 .map(|point| eredu_core::ObservationSupport {
76 path: point.path.clone(),
77 prefill: point_support(point, point.prefill, context),
78 decode: point_support(point, point.decode, context),
79 floating_to_f32: context.mechanisms.floating_to_f32,
80 })
81 .collect(),
82 }
83}
84
85fn point_support(
86 point: &eredu_core::ObservationPoint,
87 phase_available: bool,
88 context: ObservationExecutionContext,
89) -> eredu_core::ObservationSupportStatus {
90 use eredu_core::{ObservationRequirement as R, ObservationSupportStatus as S};
91 if !phase_available {
92 return S::Unsupported("The architecture does not emit this point in this phase".into());
93 }
94 if !context.selected {
95 return S::Unverified("No admitted execution configuration".into());
96 }
97 if !context.activation_inspection {
98 return S::Unsupported("Selected session does not enable activation inspection".into());
99 }
100 if !context.mechanisms.activation_tensors {
101 return S::Unsupported("Backend has not declared tensor capture support".into());
102 }
103 if point.requirements.contains(&R::RoutingEvents) && !context.mechanisms.routing_tensors {
104 return S::Unsupported("Backend does not collect normalized routing events".into());
105 }
106 if context.partitioned {
107 return S::Unverified(
108 "Partition-local ownership and routing observation coverage are not yet described"
109 .into(),
110 );
111 }
112 if point.requirements.contains(&R::MediaInput) {
113 return S::Conditional("Requires the corresponding media input during prefill".into());
114 }
115 if point.requirements.contains(&R::PredictionExecution) {
116 return S::Conditional("Requires the corresponding prediction execution group".into());
117 }
118 S::Supported
119}
120
121pub struct TargetStateTap<'a, T> {
123 pub layer: usize,
125 pub value: &'a T,
127}
128
129#[derive(Debug, Clone)]
131pub struct TargetStateCapture<T> {
132 requested: Vec<usize>,
133 captured: BTreeMap<usize, T>,
134}
135
136impl<T> TargetStateCapture<T> {
137 pub fn new(
139 requested: impl IntoIterator<Item = usize>,
140 ) -> Result<Self, TargetStateCaptureError> {
141 let requested = requested.into_iter().collect::<Vec<_>>();
142 if requested.is_empty() {
143 return Err(TargetStateCaptureError::Empty);
144 }
145 let mut unique = BTreeSet::new();
146 if let Some(duplicate) = requested.iter().find(|layer| !unique.insert(**layer)) {
147 return Err(TargetStateCaptureError::DuplicateRequest(*duplicate));
148 }
149 Ok(Self {
150 requested,
151 captured: BTreeMap::new(),
152 })
153 }
154
155 pub fn wants(&self, layer: usize) -> bool {
157 self.requested.contains(&layer)
158 }
159
160 pub fn capture(&mut self, tap: TargetStateTap<'_, T>) -> Result<(), TargetStateCaptureError>
162 where
163 T: Clone,
164 {
165 if !self.wants(tap.layer) {
166 return Err(TargetStateCaptureError::Unrequested(tap.layer));
167 }
168 if self.captured.insert(tap.layer, tap.value.clone()).is_some() {
169 return Err(TargetStateCaptureError::DuplicateCapture(tap.layer));
170 }
171 Ok(())
172 }
173
174 pub fn into_ordered(mut self) -> Result<Vec<T>, TargetStateCaptureError> {
176 let mut ordered = Vec::with_capacity(self.requested.len());
177 for layer in self.requested {
178 ordered.push(
179 self.captured
180 .remove(&layer)
181 .ok_or(TargetStateCaptureError::Missing(layer))?,
182 );
183 }
184 Ok(ordered)
185 }
186}
187
188#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
190pub enum TargetStateCaptureError {
191 #[error("target-state capture requires at least one layer")]
193 Empty,
194 #[error("target-state layer {0} was requested more than once")]
196 DuplicateRequest(usize),
197 #[error("target-state layer {0} was not requested")]
199 Unrequested(usize),
200 #[error("target-state layer {0} was captured more than once")]
202 DuplicateCapture(usize),
203 #[error("target-state layer {0} was not captured")]
205 Missing(usize),
206}
207
208pub struct RoutingObservation<'a, T> {
210 pub path: &'a str,
212 pub selected_experts: &'a T,
214 pub selected_scores: &'a T,
216 pub coefficients: &'a T,
218 pub routed_output: &'a T,
220 pub local_routed_output: Option<&'a T>,
222 pub reduced_routed_output: Option<&'a T>,
224 pub shared_output: Option<&'a T>,
226 pub combined_output: Option<&'a T>,
228 pub expert_count: i32,
230}
231
232impl<T> RoutingObservation<'_, T> {
233 pub fn for_each_tensor(&self, mut visit: impl FnMut(String, &T)) {
235 use eredu_core::RoutingObservationField as Field;
236 for (field, value) in [
237 (Field::SelectedExperts, Some(self.selected_experts)),
238 (Field::SelectedScores, Some(self.selected_scores)),
239 (Field::Coefficients, Some(self.coefficients)),
240 (Field::RoutedOutput, Some(self.routed_output)),
241 (Field::LocalRoutedOutput, self.local_routed_output),
242 (Field::ReducedRoutedOutput, self.reduced_routed_output),
243 (Field::SharedOutput, self.shared_output),
244 (Field::CombinedOutput, self.combined_output),
245 ] {
246 if let Some(value) = value {
247 visit(field.path(self.path), value);
248 }
249 }
250 }
251}
252
253pub trait ActivationObserver<T, E> {
255 fn routing_control(
258 &mut self,
259 _path: &str,
260 _token_rows: u64,
261 ) -> Result<Option<eredu_nn::routing_intervention::GroupSelectionControl>, E> {
262 Ok(None)
263 }
264
265 fn routing_applied(
267 &mut self,
268 _path: &str,
269 _original: Option<RoutingDecision<'_, T>>,
270 _effective: RoutingDecision<'_, T>,
271 ) -> Result<(), E> {
272 Ok(())
273 }
274
275 fn routing_failed(&mut self, _path: &str, _message: &str) {}
277
278 fn finish(&mut self) -> Result<(), E> {
281 Ok(())
282 }
283
284 fn observe(&mut self, path: &str, value: &T) -> Result<(), E>;
286
287 fn intervene(&mut self, _path: &str, _value: &T) -> Result<Option<T>, E> {
289 Ok(None)
290 }
291
292 fn observe_routing(&mut self, _routing: RoutingObservation<'_, T>) -> Result<(), E> {
294 Ok(())
295 }
296}
297
298pub struct RoutingDecision<'a, T> {
300 pub ids: &'a T,
302 pub coefficients: &'a T,
304}
305
306impl<'a, T> From<&'a eredu_nn::GroupSelection<T>> for RoutingDecision<'a, T> {
307 fn from(routes: &'a eredu_nn::GroupSelection<T>) -> Self {
308 Self {
309 ids: routes.group_indices(),
310 coefficients: routes.coefficients(),
311 }
312 }
313}
314
315pub fn observe_and_intervene<T, E, O>(observer: &mut O, path: &str, value: &T) -> Result<T, E>
318where
319 T: Clone,
320 O: ActivationObserver<T, E> + ?Sized,
321{
322 observer.observe(path, value)?;
323 Ok(observer
324 .intervene(path, value)?
325 .unwrap_or_else(|| value.clone()))
326}
327
328pub fn observe_model_logits<T, E, O>(observer: &mut O, logits: &T) -> Result<T, E>
334where
335 T: Clone,
336 O: ActivationObserver<T, E> + ?Sized,
337{
338 observe_and_intervene(observer, eredu_core::MODEL_LOGITS_OBSERVATION_PATH, logits)
339}
340
341#[derive(Debug, Default, Clone, Copy)]
343pub struct NoopObserver;
344
345impl<T, E> ActivationObserver<T, E> for NoopObserver {
346 fn observe(&mut self, _path: &str, _value: &T) -> Result<(), E> {
347 Ok(())
348 }
349}
350
351impl<T, E, F> ActivationObserver<T, E> for F
352where
353 F: FnMut(&str, &T) -> Result<(), E>,
354{
355 fn observe(&mut self, path: &str, value: &T) -> Result<(), E> {
356 self(path, value)
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn noop_observer_is_static_and_passthrough() {
366 fn observe<O: ActivationObserver<i32, ()>>(observer: &mut O) {
367 observer.observe("layer.output", &7).unwrap();
368 assert_eq!(observer.intervene("layer.output", &7).unwrap(), None);
369 }
370 observe(&mut NoopObserver);
371 }
372
373 #[test]
374 fn observed_activation_can_be_replaced_without_an_erased_hot_path() {
375 struct ReplacingObserver {
376 observed: Vec<String>,
377 }
378
379 impl ActivationObserver<i32, ()> for ReplacingObserver {
380 fn observe(&mut self, path: &str, _value: &i32) -> Result<(), ()> {
381 self.observed.push(path.into());
382 Ok(())
383 }
384
385 fn intervene(&mut self, path: &str, value: &i32) -> Result<Option<i32>, ()> {
386 Ok((path == "model.layers.0.output").then_some(value + 4))
387 }
388 }
389
390 let mut observer = ReplacingObserver {
391 observed: Vec::new(),
392 };
393 let output = observe_and_intervene(&mut observer, "model.layers.0.output", &3).unwrap();
394 assert_eq!(output, 7);
395 assert_eq!(observer.observed, ["model.layers.0.output"]);
396 }
397
398 #[test]
399 fn final_logits_observation_returns_the_intervention() {
400 struct ReplacingLogits;
401
402 impl ActivationObserver<i32, ()> for ReplacingLogits {
403 fn observe(&mut self, path: &str, value: &i32) -> Result<(), ()> {
404 assert_eq!(path, eredu_core::MODEL_LOGITS_OBSERVATION_PATH);
405 assert_eq!(*value, 3);
406 Ok(())
407 }
408
409 fn intervene(&mut self, path: &str, value: &i32) -> Result<Option<i32>, ()> {
410 assert_eq!(path, eredu_core::MODEL_LOGITS_OBSERVATION_PATH);
411 Ok(Some(value + 4))
412 }
413 }
414
415 assert_eq!(observe_model_logits(&mut ReplacingLogits, &3), Ok(7));
416 }
417
418 #[test]
419 fn target_states_are_captured_once_in_request_order() {
420 let mut capture = TargetStateCapture::new([5, 1, 3]).unwrap();
421 assert!(capture.wants(1));
422 assert!(!capture.wants(2));
423 capture
424 .capture(TargetStateTap {
425 layer: 1,
426 value: &10,
427 })
428 .unwrap();
429 capture
430 .capture(TargetStateTap {
431 layer: 5,
432 value: &50,
433 })
434 .unwrap();
435 capture
436 .capture(TargetStateTap {
437 layer: 3,
438 value: &30,
439 })
440 .unwrap();
441 assert_eq!(capture.into_ordered().unwrap(), [50, 10, 30]);
442 }
443
444 #[test]
445 fn target_state_capture_rejects_duplicates_omissions_and_unrequested_layers() {
446 assert_eq!(
447 TargetStateCapture::<i32>::new([2, 2]).unwrap_err(),
448 TargetStateCaptureError::DuplicateRequest(2)
449 );
450 let mut capture = TargetStateCapture::new([2]).unwrap();
451 assert_eq!(
452 capture
453 .capture(TargetStateTap {
454 layer: 3,
455 value: &7,
456 })
457 .unwrap_err(),
458 TargetStateCaptureError::Unrequested(3)
459 );
460 assert_eq!(
461 capture.into_ordered().unwrap_err(),
462 TargetStateCaptureError::Missing(2)
463 );
464 }
465}