Skip to main content

khive_brain_core/
tunable.rs

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