1use std::collections::{BTreeMap, BTreeSet};
4
5pub struct TargetStateTap<'a, T> {
7 pub layer: usize,
9 pub value: &'a T,
11}
12
13#[derive(Debug, Clone)]
15pub struct TargetStateCapture<T> {
16 requested: Vec<usize>,
17 captured: BTreeMap<usize, T>,
18}
19
20impl<T> TargetStateCapture<T> {
21 pub fn new(
23 requested: impl IntoIterator<Item = usize>,
24 ) -> Result<Self, TargetStateCaptureError> {
25 let requested = requested.into_iter().collect::<Vec<_>>();
26 if requested.is_empty() {
27 return Err(TargetStateCaptureError::Empty);
28 }
29 let mut unique = BTreeSet::new();
30 if let Some(duplicate) = requested.iter().find(|layer| !unique.insert(**layer)) {
31 return Err(TargetStateCaptureError::DuplicateRequest(*duplicate));
32 }
33 Ok(Self {
34 requested,
35 captured: BTreeMap::new(),
36 })
37 }
38
39 pub fn wants(&self, layer: usize) -> bool {
41 self.requested.contains(&layer)
42 }
43
44 pub fn capture(&mut self, tap: TargetStateTap<'_, T>) -> Result<(), TargetStateCaptureError>
46 where
47 T: Clone,
48 {
49 if !self.wants(tap.layer) {
50 return Err(TargetStateCaptureError::Unrequested(tap.layer));
51 }
52 if self.captured.insert(tap.layer, tap.value.clone()).is_some() {
53 return Err(TargetStateCaptureError::DuplicateCapture(tap.layer));
54 }
55 Ok(())
56 }
57
58 pub fn into_ordered(mut self) -> Result<Vec<T>, TargetStateCaptureError> {
60 let mut ordered = Vec::with_capacity(self.requested.len());
61 for layer in self.requested {
62 ordered.push(
63 self.captured
64 .remove(&layer)
65 .ok_or(TargetStateCaptureError::Missing(layer))?,
66 );
67 }
68 Ok(ordered)
69 }
70}
71
72#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
74pub enum TargetStateCaptureError {
75 #[error("target-state capture requires at least one layer")]
77 Empty,
78 #[error("target-state layer {0} was requested more than once")]
80 DuplicateRequest(usize),
81 #[error("target-state layer {0} was not requested")]
83 Unrequested(usize),
84 #[error("target-state layer {0} was captured more than once")]
86 DuplicateCapture(usize),
87 #[error("target-state layer {0} was not captured")]
89 Missing(usize),
90}
91
92pub struct RoutingObservation<'a, T> {
94 pub path: &'a str,
96 pub selected_experts: &'a T,
98 pub selected_scores: &'a T,
100 pub coefficients: &'a T,
102 pub routed_output: &'a T,
104 pub local_routed_output: Option<&'a T>,
106 pub reduced_routed_output: Option<&'a T>,
108 pub shared_output: Option<&'a T>,
110 pub combined_output: Option<&'a T>,
112 pub expert_count: i32,
114}
115
116pub trait ActivationObserver<T, E> {
118 fn observe(&mut self, path: &str, value: &T) -> Result<(), E>;
120
121 fn intervene(&mut self, _path: &str, _value: &T) -> Result<Option<T>, E> {
123 Ok(None)
124 }
125
126 fn observe_routing(&mut self, _routing: RoutingObservation<'_, T>) -> Result<(), E> {
128 Ok(())
129 }
130}
131
132pub fn observe_and_intervene<T, E, O>(observer: &mut O, path: &str, value: &T) -> Result<T, E>
135where
136 T: Clone,
137 O: ActivationObserver<T, E> + ?Sized,
138{
139 observer.observe(path, value)?;
140 Ok(observer
141 .intervene(path, value)?
142 .unwrap_or_else(|| value.clone()))
143}
144
145pub fn observe_model_logits<T, E, O>(observer: &mut O, logits: &T) -> Result<T, E>
151where
152 T: Clone,
153 O: ActivationObserver<T, E> + ?Sized,
154{
155 observe_and_intervene(observer, eredu_core::MODEL_LOGITS_OBSERVATION_PATH, logits)
156}
157
158#[derive(Debug, Default, Clone, Copy)]
160pub struct NoopObserver;
161
162impl<T, E> ActivationObserver<T, E> for NoopObserver {
163 fn observe(&mut self, _path: &str, _value: &T) -> Result<(), E> {
164 Ok(())
165 }
166}
167
168impl<T, E, F> ActivationObserver<T, E> for F
169where
170 F: FnMut(&str, &T) -> Result<(), E>,
171{
172 fn observe(&mut self, path: &str, value: &T) -> Result<(), E> {
173 self(path, value)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn noop_observer_is_static_and_passthrough() {
183 fn observe<O: ActivationObserver<i32, ()>>(observer: &mut O) {
184 observer.observe("layer.output", &7).unwrap();
185 assert_eq!(observer.intervene("layer.output", &7).unwrap(), None);
186 }
187 observe(&mut NoopObserver);
188 }
189
190 #[test]
191 fn observed_activation_can_be_replaced_without_an_erased_hot_path() {
192 struct ReplacingObserver {
193 observed: Vec<String>,
194 }
195
196 impl ActivationObserver<i32, ()> for ReplacingObserver {
197 fn observe(&mut self, path: &str, _value: &i32) -> Result<(), ()> {
198 self.observed.push(path.into());
199 Ok(())
200 }
201
202 fn intervene(&mut self, path: &str, value: &i32) -> Result<Option<i32>, ()> {
203 Ok((path == "model.layers.0.output").then_some(value + 4))
204 }
205 }
206
207 let mut observer = ReplacingObserver {
208 observed: Vec::new(),
209 };
210 let output = observe_and_intervene(&mut observer, "model.layers.0.output", &3).unwrap();
211 assert_eq!(output, 7);
212 assert_eq!(observer.observed, ["model.layers.0.output"]);
213 }
214
215 #[test]
216 fn final_logits_observation_returns_the_intervention() {
217 struct ReplacingLogits;
218
219 impl ActivationObserver<i32, ()> for ReplacingLogits {
220 fn observe(&mut self, path: &str, value: &i32) -> Result<(), ()> {
221 assert_eq!(path, eredu_core::MODEL_LOGITS_OBSERVATION_PATH);
222 assert_eq!(*value, 3);
223 Ok(())
224 }
225
226 fn intervene(&mut self, path: &str, value: &i32) -> Result<Option<i32>, ()> {
227 assert_eq!(path, eredu_core::MODEL_LOGITS_OBSERVATION_PATH);
228 Ok(Some(value + 4))
229 }
230 }
231
232 assert_eq!(observe_model_logits(&mut ReplacingLogits, &3), Ok(7));
233 }
234
235 #[test]
236 fn target_states_are_captured_once_in_request_order() {
237 let mut capture = TargetStateCapture::new([5, 1, 3]).unwrap();
238 assert!(capture.wants(1));
239 assert!(!capture.wants(2));
240 capture
241 .capture(TargetStateTap {
242 layer: 1,
243 value: &10,
244 })
245 .unwrap();
246 capture
247 .capture(TargetStateTap {
248 layer: 5,
249 value: &50,
250 })
251 .unwrap();
252 capture
253 .capture(TargetStateTap {
254 layer: 3,
255 value: &30,
256 })
257 .unwrap();
258 assert_eq!(capture.into_ordered().unwrap(), [50, 10, 30]);
259 }
260
261 #[test]
262 fn target_state_capture_rejects_duplicates_omissions_and_unrequested_layers() {
263 assert_eq!(
264 TargetStateCapture::<i32>::new([2, 2]).unwrap_err(),
265 TargetStateCaptureError::DuplicateRequest(2)
266 );
267 let mut capture = TargetStateCapture::new([2]).unwrap();
268 assert_eq!(
269 capture
270 .capture(TargetStateTap {
271 layer: 3,
272 value: &7,
273 })
274 .unwrap_err(),
275 TargetStateCaptureError::Unrequested(3)
276 );
277 assert_eq!(
278 capture.into_ordered().unwrap_err(),
279 TargetStateCaptureError::Missing(2)
280 );
281 }
282}