Skip to main content

khive_pack_brain/
tunable.rs

1use khive_runtime::pack::PackRuntime;
2use khive_runtime::RuntimeError;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::state::{BalancedRecallState, BetaPosterior};
7
8/// Packs that want auto-tuning implement this trait.
9///
10/// The brain discovers tunable packs at startup via the PackRegistry.
11/// `project_config` now receives a `BalancedRecallState` — the v1 profile
12/// state — rather than the old flat `BrainState` scalar map.
13pub trait PackTunable: PackRuntime {
14    fn parameter_space(&self) -> ParameterSpace;
15    fn project_config(&self, state: &BalancedRecallState) -> Value;
16    fn apply_config(&self, config: Value) -> Result<(), RuntimeError>;
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ParameterSpace {
21    pub parameters: Vec<ParameterDef>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ParameterDef {
26    pub name: String,
27    pub prior_alpha: f64,
28    pub prior_beta: f64,
29    pub bounds: (f64, f64),
30}
31
32impl ParameterDef {
33    pub fn prior(&self) -> BetaPosterior {
34        BetaPosterior::new(self.prior_alpha, self.prior_beta)
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn parameter_def_prior_returns_matching_beta_posterior() {
44        let def = ParameterDef {
45            name: "recall::relevance_weight".into(),
46            prior_alpha: 2.0,
47            prior_beta: 8.0,
48            bounds: (0.0, 1.0),
49        };
50        let prior = def.prior();
51        assert!((prior.alpha - 2.0).abs() < 1e-12);
52        assert!((prior.beta - 8.0).abs() < 1e-12);
53        assert!((prior.mean() - 0.2).abs() < 1e-12);
54    }
55
56    #[test]
57    fn parameter_space_serializes() {
58        let space = ParameterSpace {
59            parameters: vec![ParameterDef {
60                name: "p".into(),
61                prior_alpha: 1.0,
62                prior_beta: 1.0,
63                bounds: (0.0, 1.0),
64            }],
65        };
66        let json = serde_json::to_string(&space).unwrap();
67        let back: ParameterSpace = serde_json::from_str(&json).unwrap();
68        assert_eq!(back.parameters.len(), 1);
69        assert_eq!(back.parameters[0].name, "p");
70    }
71}