// system.mbt — orchestration facade (mirrors Python `system.py`)
//
// Wires storage + modules into the four user-facing operations:
// remember / recall / evolve / stats, plus list_all.
///|
/// Build a JSON object from key/value pairs.
pub fn obj(pairs : Array[(String, Json)]) -> Map[String, Json] {
let m : Map[String, Json] = Map([], capacity=0)
for p in pairs {
let (k, v) = p
m.set(k, v)
}
m
}
///|
/// The memory system facade. Holds a `Storage`; because `Storage` keeps its
/// index in a `Ref` cell, copying the facade still shares the same store.
pub struct MemorySystem {
storage : Storage
}
///|
/// Open a memory system rooted at `root`, optionally encrypted by `passphrase`.
pub fn MemorySystem::new(
root : String,
passphrase? : String = "",
) -> MemorySystem {
let crypto = if passphrase == "" {
CryptoProvider::disabled()
} else {
CryptoProvider::new(passphrase)
}
{ storage: Storage::new(root, crypto~) }
}
///|
/// Write a memory: validate -> classify -> dedup -> conflict -> store.
pub fn MemorySystem::remember(
self : MemorySystem,
content : String,
type_hint? : String = "",
source? : String = "user_input",
interactive? : Bool = true,
conflict_policy? : String = "ask",
) -> Map[String, Json] raise @fs.IOError {
let (ok, reason) = validate_input(content)
if !ok {
return obj([
("status", Json::string("rejected")),
("reason", Json::string(reason)),
])
}
let mtype = if type_hint == "" {
classify(content)
} else {
MemoryType::from_str(type_hint)
}
let candidates = self.storage.query_by_type(mtype)
let dup = find_duplicate(content, candidates)
if dup is Some(_) {
let d = dup.unwrap()
return obj([
("status", Json::string("duplicate")),
("entry_id", Json::string(d.id)),
("content", Json::string(d.content)),
])
}
let entry = MemoryEntry::create(content, mtype, source~)
let conflicts = detect_conflict(entry, candidates)
if conflicts.length() > 0 {
let decision = if interactive && conflict_policy == "ask" {
self.ask_conflict(entry, conflicts)
} else {
conflict_policy
}
if decision == "cancel" {
return obj([
("status", Json::string("cancelled")),
("conflicts", Json::array(conflicts.map(fn(c) { Json::string(c.id) }))),
])
}
if decision == "delete_existing" {
for c in conflicts {
ignore(self.storage.delete(c.id))
}
} else if decision == "merge" {
let merged = conflicts[0].content + " | " + content
let merged_entry = { ..conflicts[0], content: merged }
self.storage.put(merged_entry)
return obj([
("status", Json::string("merged")),
("entry_id", Json::string(conflicts[0].id)),
])
}
}
self.storage.put(entry)
obj([
("status", Json::string("stored")),
("entry_id", Json::string(entry.id)),
("type", Json::string(entry.mtype)),
("conflicts", Json::array(conflicts.map(fn(c) { Json::string(c.id) }))),
])
}
///|
/// Interactive conflict resolution (best-effort; falls back to "keep").
fn MemorySystem::ask_conflict(
_self : MemorySystem,
entry : MemoryEntry,
conflicts : Array[MemoryEntry],
) -> String {
println("\n[冲突检测] 新记忆与已有记忆存在潜在矛盾:")
println(" 新: " + entry.content)
for c in conflicts {
println(" 已有(" + c.id + "): " + c.content)
}
let raw = @fs.read_file_to_string("CON") catch {
_ => @fs.read_file_to_string("/dev/stdin") catch { _ => "" }
}
let line = raw.trim().to_owned().to_lower()
if line == "keep" ||
line == "delete" ||
line == "merge" ||
line == "cancel" ||
line == "delete_existing" {
if line == "delete" {
return "delete_existing"
}
return line
}
"keep"
}
///|
/// Retrieve memories ranked by similarity x priority x recency; updates stats.
pub fn MemorySystem::recall(
self : MemorySystem,
query : String,
top_k? : Int = 5,
) -> Array[Json] raise @fs.IOError {
let now = now_ms()
let results = graded_retrieval(self.storage, query, top_k~, now~)
let out : Array[Json] = []
for item in results {
let (entry, score) = item
let updated = {
..entry,
access_count: entry.access_count + 1,
last_accessed: now,
priority: recompute_priority(entry, now),
}
self.storage.put(updated)
out.push(
Json::object(
obj([
("id", Json::string(updated.id)),
("type", Json::string(updated.mtype)),
("content", Json::string(updated.content)),
("priority", Json::number(updated.priority)),
("score", Json::number(score)),
]),
),
)
}
out
}
///|
/// Run self-evolution over the store.
pub fn MemorySystem::evolve(
self : MemorySystem,
) -> Map[String, Json] raise @fs.IOError {
evolve(self.storage)
}
///|
/// Aggregate statistics.
pub fn MemorySystem::stats(self : MemorySystem) -> Map[String, Json] {
self.storage.stats()
}
///|
/// List all entries as JSON.
pub fn MemorySystem::list_all(self : MemorySystem) -> Array[Json] {
self.storage.all_entries().map(fn(e) { e.to_json() })
}
///|
/// Return the on-disk JSON file path for an entry, or `None` if absent.
/// Exposed so storage can be inspected (e.g. by encryption-roundtrip tests).
pub fn MemorySystem::entry_path(self : MemorySystem, id : String) -> String? {
match self.storage.get(id) {
Some(e) => Some(join(self.storage.root, e.mtype) + SEP + e.id + ".json")
None => None
}
}
///|
/// Maintenance helper: rewind an entry's access clock so self-evolution's
/// deprecation path can be exercised deterministically by tests. Returns
/// `true` if the entry existed and was updated.
pub fn MemorySystem::age_entry(
self : MemorySystem,
id : String,
last_accessed : Int64,
access_count : Int,
) -> Bool raise @fs.IOError {
match self.storage.get(id) {
Some(e) => {
let updated = { ..e, last_accessed, access_count }
self.storage.put(updated)
true
}
None => false
}
}