pub mod config;
pub mod handlers;
pub mod tunable;
use std::sync::Mutex;
use async_trait::async_trait;
use serde_json::Value;
use khive_runtime::pack::PackRuntime;
use khive_runtime::{KhiveRuntime, RuntimeError, VerbRegistry};
use khive_types::{Pack, VerbDef};
use crate::config::RecallConfig;
pub struct MemoryPack {
runtime: KhiveRuntime,
config: Mutex<RecallConfig>,
}
impl MemoryPack {
pub(crate) fn active_config(&self) -> RecallConfig {
self.config.lock().unwrap().clone()
}
}
impl Pack for MemoryPack {
const NAME: &'static str = "memory";
const NOTE_KINDS: &'static [&'static str] = &["memory"];
const ENTITY_KINDS: &'static [&'static str] = &[];
const VERBS: &'static [VerbDef] = &MEMORY_VERBS;
const REQUIRES: &'static [&'static str] = &["kg"];
}
static MEMORY_VERBS: [VerbDef; 6] = [
VerbDef {
name: "remember",
description: "Create a memory note with salience and decay",
},
VerbDef {
name: "recall",
description: "Recall memory notes with decay-aware hybrid ranking",
},
VerbDef {
name: "recall.embed",
description: "Return the embedding vector used by memory recall",
},
VerbDef {
name: "recall.candidates",
description: "Return raw memory recall candidates by retrieval source",
},
VerbDef {
name: "recall.fuse",
description: "Return fused memory recall candidates before final scoring",
},
VerbDef {
name: "recall.score",
description: "Score a memory recall candidate and return score breakdown",
},
];
impl MemoryPack {
pub fn new(runtime: KhiveRuntime) -> Self {
Self {
runtime,
config: Mutex::new(RecallConfig::default()),
}
}
}
struct MemoryPackFactory;
impl khive_runtime::PackFactory for MemoryPackFactory {
fn name(&self) -> &'static str {
"memory"
}
fn requires(&self) -> &'static [&'static str] {
&["kg"]
}
fn create(&self, runtime: KhiveRuntime) -> Box<dyn khive_runtime::PackRuntime> {
Box::new(MemoryPack::new(runtime))
}
}
inventory::submit! { khive_runtime::PackRegistration(&MemoryPackFactory) }
#[async_trait]
impl PackRuntime for MemoryPack {
fn name(&self) -> &str {
<MemoryPack as Pack>::NAME
}
fn note_kinds(&self) -> &'static [&'static str] {
<MemoryPack as Pack>::NOTE_KINDS
}
fn entity_kinds(&self) -> &'static [&'static str] {
<MemoryPack as Pack>::ENTITY_KINDS
}
fn verbs(&self) -> &'static [VerbDef] {
&MEMORY_VERBS
}
fn requires(&self) -> &'static [&'static str] {
<MemoryPack as Pack>::REQUIRES
}
async fn dispatch(
&self,
verb: &str,
params: Value,
registry: &VerbRegistry,
) -> Result<Value, RuntimeError> {
match verb {
"remember" => self.handle_remember(params).await,
"recall" => self.handle_recall(params, registry).await,
"recall.embed" => self.handle_recall_embed(params).await,
"recall.candidates" => self.handle_recall_candidates(params).await,
"recall.fuse" => self.handle_recall_fuse(params, registry).await,
"recall.score" => self.handle_recall_score(params).await,
_ => Err(RuntimeError::InvalidInput(format!(
"memory pack does not handle verb {verb:?}"
))),
}
}
}