Skip to main content

khive_pack_memory/
lib.rs

1pub mod config;
2pub mod handlers;
3pub mod tunable;
4
5use std::sync::Mutex;
6
7use async_trait::async_trait;
8use serde_json::Value;
9
10use khive_runtime::pack::PackRuntime;
11use khive_runtime::{KhiveRuntime, RuntimeError, VerbRegistry};
12use khive_types::{Pack, VerbDef};
13
14use crate::config::RecallConfig;
15
16pub struct MemoryPack {
17    runtime: KhiveRuntime,
18    /// Active recall config.
19    config: Mutex<RecallConfig>,
20}
21
22impl MemoryPack {
23    /// Return a clone of the current active `RecallConfig`.
24    ///
25    /// Handlers call this to pick up the latest tuned parameters.
26    pub(crate) fn active_config(&self) -> RecallConfig {
27        self.config.lock().unwrap().clone()
28    }
29}
30
31impl Pack for MemoryPack {
32    const NAME: &'static str = "memory";
33    const NOTE_KINDS: &'static [&'static str] = &["memory"];
34    const ENTITY_KINDS: &'static [&'static str] = &[];
35    const VERBS: &'static [VerbDef] = &MEMORY_VERBS;
36    const REQUIRES: &'static [&'static str] = &["kg"];
37}
38
39// ADR-060: Illocutionary classification (Searle 1976)
40//   Commissive — commits caller to a persistent change
41//   Assertive — retrieves/presents state of affairs
42static MEMORY_VERBS: [VerbDef; 6] = [
43    // Commissive: commits a memory to the namespace
44    VerbDef {
45        name: "remember",
46        description: "Create a memory note with salience and decay",
47    },
48    // Assertive: retrieves memory notes via decay-aware ranking
49    VerbDef {
50        name: "recall",
51        description: "Recall memory notes with decay-aware hybrid ranking",
52    },
53    VerbDef {
54        name: "recall.embed",
55        description: "Return the embedding vector used by memory recall",
56    },
57    VerbDef {
58        name: "recall.candidates",
59        description: "Return raw memory recall candidates by retrieval source",
60    },
61    VerbDef {
62        name: "recall.fuse",
63        description: "Return fused memory recall candidates before final scoring",
64    },
65    VerbDef {
66        name: "recall.score",
67        description: "Score a memory recall candidate and return score breakdown",
68    },
69];
70
71impl MemoryPack {
72    pub fn new(runtime: KhiveRuntime) -> Self {
73        Self {
74            runtime,
75            config: Mutex::new(RecallConfig::default()),
76        }
77    }
78}
79
80// ── ADR-063: inventory self-registration ─────────────────────────────────────
81
82struct MemoryPackFactory;
83
84impl khive_runtime::PackFactory for MemoryPackFactory {
85    fn name(&self) -> &'static str {
86        "memory"
87    }
88
89    fn requires(&self) -> &'static [&'static str] {
90        &["kg"]
91    }
92
93    fn create(&self, runtime: KhiveRuntime) -> Box<dyn khive_runtime::PackRuntime> {
94        Box::new(MemoryPack::new(runtime))
95    }
96}
97
98inventory::submit! { khive_runtime::PackRegistration(&MemoryPackFactory) }
99
100#[async_trait]
101impl PackRuntime for MemoryPack {
102    fn name(&self) -> &str {
103        <MemoryPack as Pack>::NAME
104    }
105
106    fn note_kinds(&self) -> &'static [&'static str] {
107        <MemoryPack as Pack>::NOTE_KINDS
108    }
109
110    fn entity_kinds(&self) -> &'static [&'static str] {
111        <MemoryPack as Pack>::ENTITY_KINDS
112    }
113
114    fn verbs(&self) -> &'static [VerbDef] {
115        &MEMORY_VERBS
116    }
117
118    fn requires(&self) -> &'static [&'static str] {
119        <MemoryPack as Pack>::REQUIRES
120    }
121
122    async fn dispatch(
123        &self,
124        verb: &str,
125        params: Value,
126        registry: &VerbRegistry,
127    ) -> Result<Value, RuntimeError> {
128        match verb {
129            "remember" => self.handle_remember(params).await,
130            "recall" => self.handle_recall(params, registry).await,
131            "recall.embed" => self.handle_recall_embed(params).await,
132            "recall.candidates" => self.handle_recall_candidates(params).await,
133            "recall.fuse" => self.handle_recall_fuse(params, registry).await,
134            "recall.score" => self.handle_recall_score(params).await,
135            _ => Err(RuntimeError::InvalidInput(format!(
136                "memory pack does not handle verb {verb:?}"
137            ))),
138        }
139    }
140}