- nocmem Python service (mem/): FastAPI wrapper around NuoNuo's Hopfield-Hebbian memory, with /recall, /ingest, /store, /stats endpoints - NOC integration: auto recall after user message (injected as system msg), async ingest after LLM response (fire-and-forget) - Recall: cosine pre-filter (threshold 0.35) + Hopfield attention (β=32), top_k=3, KV-cache friendly (appended after user msg, not in system prompt) - Ingest: LLM extraction + paraphrase augmentation, heuristic fallback - Wired into main.rs, life.rs (agent done), http.rs (api chat) - Config: optional `nocmem.endpoint` in config.yaml - Includes benchmarks: LongMemEval (R@5=94.0%), efficiency, noise vs scale - Design doc: doc/nocmem.md
95 lines
2.1 KiB
Rust
95 lines
2.1 KiB
Rust
use serde::Deserialize;
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
pub struct Config {
|
|
#[serde(default = "default_name")]
|
|
pub name: String,
|
|
pub tg: TgConfig,
|
|
pub auth: AuthConfig,
|
|
pub session: SessionConfig,
|
|
#[serde(default)]
|
|
pub backend: BackendConfig,
|
|
#[serde(default)]
|
|
pub whisper_url: Option<String>,
|
|
#[serde(default)]
|
|
pub gitea: Option<GiteaConfig>,
|
|
#[serde(default)]
|
|
pub nocmem: Option<NocmemConfig>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
pub struct NocmemConfig {
|
|
pub endpoint: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
#[allow(dead_code)]
|
|
pub struct GiteaConfig {
|
|
pub url: String,
|
|
/// Direct token or read from token_file at startup
|
|
#[serde(default)]
|
|
pub token: String,
|
|
#[serde(default)]
|
|
pub token_file: Option<String>,
|
|
#[serde(default = "default_webhook_port")]
|
|
pub webhook_port: u16,
|
|
#[serde(default)]
|
|
pub webhook_secret: Option<String>,
|
|
}
|
|
|
|
impl GiteaConfig {
|
|
/// Resolve token: if token_file is set and token is empty, read from file
|
|
pub fn resolve_token(&mut self) {
|
|
if self.token.is_empty() {
|
|
if let Some(path) = &self.token_file {
|
|
match std::fs::read_to_string(path) {
|
|
Ok(t) => self.token = t.trim().to_string(),
|
|
Err(e) => tracing::error!("failed to read gitea token_file {path}: {e}"),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn default_webhook_port() -> u16 {
|
|
9800
|
|
}
|
|
|
|
fn default_name() -> String {
|
|
"noc".to_string()
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Default)]
|
|
#[serde(tag = "type")]
|
|
pub enum BackendConfig {
|
|
#[serde(rename = "claude")]
|
|
#[default]
|
|
Claude,
|
|
#[serde(rename = "openai")]
|
|
OpenAI {
|
|
endpoint: String,
|
|
model: String,
|
|
#[serde(default = "default_api_key")]
|
|
api_key: String,
|
|
},
|
|
}
|
|
|
|
fn default_api_key() -> String {
|
|
"unused".to_string()
|
|
}
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
pub struct TgConfig {
|
|
pub key: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
pub struct AuthConfig {
|
|
pub passphrase: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
pub struct SessionConfig {
|
|
pub refresh_hour: u32,
|
|
}
|