feat: per-user auto-memory with ResponseContext
Three memory channels: hidden tool (sol.memory.set/get in scripts), pre-response injection (relevant memories loaded into system prompt), and post-response extraction (ministral-3b extracts facts after each response). User isolation enforced at Rust level — user_id derived from Matrix sender, never from script arguments. New modules: context (ResponseContext), memory (schema, store, extractor). ResponseContext threaded through responder → tools → script runtime. OpenSearch index sol_user_memory created on startup alongside archive.
This commit is contained in:
@@ -25,6 +25,8 @@ pub struct OpenSearchConfig {
|
||||
pub flush_interval_ms: u64,
|
||||
#[serde(default = "default_embedding_pipeline")]
|
||||
pub embedding_pipeline: String,
|
||||
#[serde(default = "default_memory_index")]
|
||||
pub memory_index: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
@@ -59,6 +61,30 @@ pub struct BehaviorConfig {
|
||||
pub backfill_on_join: bool,
|
||||
#[serde(default = "default_backfill_limit")]
|
||||
pub backfill_limit: usize,
|
||||
#[serde(default)]
|
||||
pub instant_responses: bool,
|
||||
#[serde(default = "default_cooldown_after_response_ms")]
|
||||
pub cooldown_after_response_ms: u64,
|
||||
#[serde(default = "default_evaluation_context_window")]
|
||||
pub evaluation_context_window: usize,
|
||||
#[serde(default = "default_detect_sol_in_conversation")]
|
||||
pub detect_sol_in_conversation: bool,
|
||||
#[serde(default)]
|
||||
pub evaluation_prompt_active: Option<String>,
|
||||
#[serde(default)]
|
||||
pub evaluation_prompt_passive: Option<String>,
|
||||
#[serde(default = "default_reaction_threshold")]
|
||||
pub reaction_threshold: f32,
|
||||
#[serde(default = "default_reaction_enabled")]
|
||||
pub reaction_enabled: bool,
|
||||
#[serde(default = "default_script_timeout_secs")]
|
||||
pub script_timeout_secs: u64,
|
||||
#[serde(default = "default_script_max_heap_mb")]
|
||||
pub script_max_heap_mb: usize,
|
||||
#[serde(default)]
|
||||
pub script_fetch_allowlist: Vec<String>,
|
||||
#[serde(default = "default_memory_extraction_enabled")]
|
||||
pub memory_extraction_enabled: bool,
|
||||
}
|
||||
|
||||
fn default_batch_size() -> usize { 50 }
|
||||
@@ -68,15 +94,24 @@ fn default_model() -> String { "mistral-medium-latest".into() }
|
||||
fn default_evaluation_model() -> String { "ministral-3b-latest".into() }
|
||||
fn default_research_model() -> String { "mistral-large-latest".into() }
|
||||
fn default_max_tool_iterations() -> usize { 5 }
|
||||
fn default_response_delay_min_ms() -> u64 { 2000 }
|
||||
fn default_response_delay_max_ms() -> u64 { 8000 }
|
||||
fn default_response_delay_min_ms() -> u64 { 100 }
|
||||
fn default_response_delay_max_ms() -> u64 { 2300 }
|
||||
fn default_spontaneous_delay_min_ms() -> u64 { 15000 }
|
||||
fn default_spontaneous_delay_max_ms() -> u64 { 60000 }
|
||||
fn default_spontaneous_threshold() -> f32 { 0.7 }
|
||||
fn default_spontaneous_threshold() -> f32 { 0.85 }
|
||||
fn default_cooldown_after_response_ms() -> u64 { 15000 }
|
||||
fn default_evaluation_context_window() -> usize { 25 }
|
||||
fn default_detect_sol_in_conversation() -> bool { true }
|
||||
fn default_reaction_threshold() -> f32 { 0.6 }
|
||||
fn default_reaction_enabled() -> bool { true }
|
||||
fn default_room_context_window() -> usize { 30 }
|
||||
fn default_dm_context_window() -> usize { 100 }
|
||||
fn default_backfill_on_join() -> bool { true }
|
||||
fn default_backfill_limit() -> usize { 10000 }
|
||||
fn default_script_timeout_secs() -> u64 { 5 }
|
||||
fn default_script_max_heap_mb() -> usize { 64 }
|
||||
fn default_memory_index() -> String { "sol_user_memory".into() }
|
||||
fn default_memory_extraction_enabled() -> bool { true }
|
||||
|
||||
impl Config {
|
||||
pub fn load(path: &str) -> anyhow::Result<Self> {
|
||||
@@ -155,19 +190,23 @@ backfill_limit = 5000
|
||||
assert_eq!(config.opensearch.batch_size, 50);
|
||||
assert_eq!(config.opensearch.flush_interval_ms, 2000);
|
||||
assert_eq!(config.opensearch.embedding_pipeline, "tuwunel_embedding_pipeline");
|
||||
assert_eq!(config.opensearch.memory_index, "sol_user_memory");
|
||||
assert_eq!(config.mistral.default_model, "mistral-medium-latest");
|
||||
assert_eq!(config.mistral.evaluation_model, "ministral-3b-latest");
|
||||
assert_eq!(config.mistral.research_model, "mistral-large-latest");
|
||||
assert_eq!(config.mistral.max_tool_iterations, 5);
|
||||
assert_eq!(config.behavior.response_delay_min_ms, 2000);
|
||||
assert_eq!(config.behavior.response_delay_max_ms, 8000);
|
||||
assert_eq!(config.behavior.response_delay_min_ms, 100);
|
||||
assert_eq!(config.behavior.response_delay_max_ms, 2300);
|
||||
assert_eq!(config.behavior.spontaneous_delay_min_ms, 15000);
|
||||
assert_eq!(config.behavior.spontaneous_delay_max_ms, 60000);
|
||||
assert!((config.behavior.spontaneous_threshold - 0.7).abs() < f32::EPSILON);
|
||||
assert!((config.behavior.spontaneous_threshold - 0.85).abs() < f32::EPSILON);
|
||||
assert!(!config.behavior.instant_responses);
|
||||
assert_eq!(config.behavior.cooldown_after_response_ms, 15000);
|
||||
assert_eq!(config.behavior.room_context_window, 30);
|
||||
assert_eq!(config.behavior.dm_context_window, 100);
|
||||
assert!(config.behavior.backfill_on_join);
|
||||
assert_eq!(config.behavior.backfill_limit, 10000);
|
||||
assert!(config.behavior.memory_extraction_enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user