use crate::observer::ObserverId;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Selector {
LayerOutput(usize),
AllLayerOutputs,
FinalLogits,
OutputText,
AttnQ { layer: usize },
AttnK { layer: usize },
AttnV { layer: usize },
}
#[derive(Debug, Clone)]
pub struct CompiledEntry {
pub selector: Selector,
pub observers: Vec<ObserverId>,
}
#[derive(Debug)]
pub struct ObservationPlan {
pub entries: Vec<CompiledEntry>,
pub selector_count: usize,
pub registration_count: usize,
}
pub struct Registration {
pub selector: Selector,
pub observer: ObserverId,
}
impl ObservationPlan {
pub fn compile(registrations: Vec<Registration>) -> Self {
let registration_count = registrations.len();
let mut selector_index: HashMap<SelectorKey, Vec<ObserverId>> = HashMap::new();
for reg in registrations {
let selectors = match reg.selector {
Selector::AllLayerOutputs => {
vec![reg.selector]
}
s => vec![s],
};
for selector in selectors {
let key = SelectorKey::from(&selector);
selector_index
.entry(key)
.or_default()
.push(reg.observer.clone());
}
}
let selector_count = selector_index.len();
let mut entries: Vec<CompiledEntry> = selector_index
.into_iter()
.map(|(key, observers)| CompiledEntry {
selector: key.into(),
observers,
})
.collect();
entries.sort_by_key(|e| selector_sort_key(&e.selector));
ObservationPlan {
entries,
selector_count,
registration_count,
}
}
pub fn unique_selector_count(&self) -> usize {
self.selector_count
}
pub fn registration_count(&self) -> usize {
self.registration_count
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum SelectorKey {
LayerOutput(usize),
AllLayerOutputs,
FinalLogits,
OutputText,
AttnQ(usize),
AttnK(usize),
AttnV(usize),
}
impl From<&Selector> for SelectorKey {
fn from(s: &Selector) -> Self {
match s {
Selector::LayerOutput(n) => SelectorKey::LayerOutput(*n),
Selector::AllLayerOutputs => SelectorKey::AllLayerOutputs,
Selector::FinalLogits => SelectorKey::FinalLogits,
Selector::OutputText => SelectorKey::OutputText,
Selector::AttnQ { layer } => SelectorKey::AttnQ(*layer),
Selector::AttnK { layer } => SelectorKey::AttnK(*layer),
Selector::AttnV { layer } => SelectorKey::AttnV(*layer),
}
}
}
impl From<SelectorKey> for Selector {
fn from(k: SelectorKey) -> Self {
match k {
SelectorKey::LayerOutput(n) => Selector::LayerOutput(n),
SelectorKey::AllLayerOutputs => Selector::AllLayerOutputs,
SelectorKey::FinalLogits => Selector::FinalLogits,
SelectorKey::OutputText => Selector::OutputText,
SelectorKey::AttnQ(n) => Selector::AttnQ { layer: n },
SelectorKey::AttnK(n) => Selector::AttnK { layer: n },
SelectorKey::AttnV(n) => Selector::AttnV { layer: n },
}
}
}
fn selector_sort_key(s: &Selector) -> (u32, usize) {
match s {
Selector::LayerOutput(n) => (0, *n),
Selector::AllLayerOutputs => (0, usize::MAX),
Selector::AttnQ { layer } => (1, *layer),
Selector::AttnK { layer } => (2, *layer),
Selector::AttnV { layer } => (3, *layer),
Selector::FinalLogits => (4, 0),
Selector::OutputText => (5, 0),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::observer::ObserverId;
#[test]
fn deduplicates_shared_selectors() {
let regs = vec![
Registration {
selector: Selector::FinalLogits,
observer: ObserverId("vault_oracle".into()),
},
Registration {
selector: Selector::FinalLogits,
observer: ObserverId("candle_compare".into()),
},
];
let plan = ObservationPlan::compile(regs);
assert_eq!(
plan.unique_selector_count(),
1,
"should deduplicate to 1 selector"
);
assert_eq!(
plan.registration_count(),
2,
"should record 2 registrations"
);
let entry = &plan.entries[0];
assert_eq!(
entry.observers.len(),
2,
"both observers should be in broadcast list"
);
}
#[test]
fn independent_selectors_not_merged() {
let regs = vec![
Registration {
selector: Selector::LayerOutput(0),
observer: ObserverId("vault_oracle".into()),
},
Registration {
selector: Selector::FinalLogits,
observer: ObserverId("candle_compare".into()),
},
];
let plan = ObservationPlan::compile(regs);
assert_eq!(plan.unique_selector_count(), 2);
}
#[test]
fn layer_outputs_sort_before_logits() {
let regs = vec![
Registration {
selector: Selector::FinalLogits,
observer: ObserverId("a".into()),
},
Registration {
selector: Selector::LayerOutput(5),
observer: ObserverId("b".into()),
},
Registration {
selector: Selector::LayerOutput(0),
observer: ObserverId("c".into()),
},
];
let plan = ObservationPlan::compile(regs);
assert!(matches!(plan.entries[0].selector, Selector::LayerOutput(0)));
assert!(matches!(plan.entries[1].selector, Selector::LayerOutput(5)));
assert!(matches!(plan.entries[2].selector, Selector::FinalLogits));
}
}