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:
@@ -1,5 +1,6 @@
|
||||
pub mod room_history;
|
||||
pub mod room_info;
|
||||
pub mod script;
|
||||
pub mod search;
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -10,6 +11,7 @@ use opensearch::OpenSearch;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::context::ResponseContext;
|
||||
|
||||
pub struct ToolRegistry {
|
||||
opensearch: OpenSearch,
|
||||
@@ -122,10 +124,44 @@ impl ToolRegistry {
|
||||
"required": ["room_id"]
|
||||
}),
|
||||
),
|
||||
Tool::new(
|
||||
"run_script".into(),
|
||||
"Execute a TypeScript/JavaScript snippet in a sandboxed runtime. \
|
||||
Use this for math, date calculations, data transformations, or any \
|
||||
computation that needs precision. The script has access to:\n\
|
||||
- sol.search(query, opts?) — search the message archive. opts: \
|
||||
{ room?, sender?, after?, before?, limit?, semantic? }\n\
|
||||
- sol.rooms() — list joined rooms (returns array of {name, id, members})\n\
|
||||
- sol.members(roomName) — get room members (returns array of {name, id})\n\
|
||||
- sol.fetch(url) — HTTP GET (allowlisted domains only)\n\
|
||||
- sol.memory.get(query?) — retrieve internal notes relevant to the query\n\
|
||||
- sol.memory.set(content, category?) — save an internal note for later reference\n\
|
||||
- sol.fs.read(path), sol.fs.write(path, content), sol.fs.list(path?) — \
|
||||
sandboxed temp filesystem for intermediate files\n\
|
||||
- console.log() to produce output\n\
|
||||
All sol.* methods are async — use await. The last expression value is \
|
||||
also captured. Output is truncated to 4096 chars."
|
||||
.into(),
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"description": "TypeScript or JavaScript code to execute"
|
||||
}
|
||||
},
|
||||
"required": ["code"]
|
||||
}),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub async fn execute(&self, name: &str, arguments: &str) -> anyhow::Result<String> {
|
||||
pub async fn execute(
|
||||
&self,
|
||||
name: &str,
|
||||
arguments: &str,
|
||||
response_ctx: &ResponseContext,
|
||||
) -> anyhow::Result<String> {
|
||||
match name {
|
||||
"search_archive" => {
|
||||
search::search_archive(
|
||||
@@ -145,6 +181,16 @@ impl ToolRegistry {
|
||||
}
|
||||
"list_rooms" => room_info::list_rooms(&self.matrix).await,
|
||||
"get_room_members" => room_info::get_room_members(&self.matrix, arguments).await,
|
||||
"run_script" => {
|
||||
script::run_script(
|
||||
&self.opensearch,
|
||||
&self.matrix,
|
||||
&self.config,
|
||||
arguments,
|
||||
response_ctx,
|
||||
)
|
||||
.await
|
||||
}
|
||||
_ => anyhow::bail!("Unknown tool: {name}"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user