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 config: Mutex<RecallConfig>,
20}
21
22impl MemoryPack {
23 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
39static MEMORY_VERBS: [VerbDef; 6] = [
43 VerbDef {
45 name: "remember",
46 description: "Create a memory note with salience and decay",
47 },
48 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
80struct 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}