forked from kent/consciousness
memory_links: return typed Vec<LinkInfo> with node weights
- hippocampus::memory_links now returns Vec<LinkInfo> with key, link_strength, and node_weight for each neighbor - Unified memory_tool! macro: mut/ref as token, single main rule - All tools use serde serialize/deserialize for RPC consistency - jsonargs handlers now work in client mode (RPC to daemon) - cli/graph.rs formats LinkInfo for display Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
359955f838
commit
598f0112a4
3 changed files with 67 additions and 57 deletions
|
|
@ -209,6 +209,12 @@ macro_rules! memory_tool {
|
|||
(@param_type Option<u32>) => { Option<u32> };
|
||||
(@param_type Option<f64>) => { Option<f64> };
|
||||
|
||||
// Serialize result for jsonargs
|
||||
(@serialize $t:ty, $result:expr) => { serde_json::to_string(&$result)? };
|
||||
|
||||
// Deserialize RPC response
|
||||
(@deserialize $t:ty, $json:expr) => { serde_json::from_str(&$json).map_err(|e| anyhow::anyhow!("{}", e)) };
|
||||
|
||||
// Serialize to JSON for RPC
|
||||
(@insert_json $map:ident, $name:ident, str) => {
|
||||
$map.insert(stringify!($name).into(), serde_json::json!($name));
|
||||
|
|
@ -241,64 +247,45 @@ macro_rules! memory_tool {
|
|||
if let Some(v) = $name { $map.insert(stringify!($name).into(), serde_json::json!(v)); }
|
||||
};
|
||||
|
||||
// ── Main rules ─────────────────────────────────────────────────
|
||||
|
||||
// Mutable store variant
|
||||
($name:ident, mut $(, $($arg:ident : [$($typ:tt)+]),* $(,)?)?) => {
|
||||
paste::paste! {
|
||||
async fn [<jsonargs_ $name>](agent: &Option<std::sync::Arc<crate::agent::Agent>>, args: &serde_json::Value) -> Result<String> {
|
||||
$($(let $arg = memory_tool!(@extract args, $arg, $($typ)+);)*)?
|
||||
let prov = get_provenance(agent).await;
|
||||
match access() {
|
||||
StoreAccess::Daemon(arc) => {
|
||||
let mut store = arc.lock().await;
|
||||
crate::hippocampus::$name(&mut store, &prov $($(, $arg)*)?)
|
||||
}
|
||||
StoreAccess::Client => anyhow::bail!("jsonargs called in client mode"),
|
||||
StoreAccess::None(err) => anyhow::bail!("{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn $name(agent: Option<&crate::agent::Agent> $($(, $arg: memory_tool!(@param_type $($typ)+))*)?) -> Result<String> {
|
||||
let prov = match agent {
|
||||
Some(a) => a.state.lock().await.provenance.clone(),
|
||||
None => "manual".to_string(),
|
||||
};
|
||||
|
||||
match access() {
|
||||
StoreAccess::Daemon(arc) => {
|
||||
let mut store = arc.lock().await;
|
||||
crate::hippocampus::$name(&mut store, &prov $($(, $arg)*)?)
|
||||
}
|
||||
StoreAccess::Client => {
|
||||
#[allow(unused_mut)]
|
||||
let mut map = serde_json::Map::new();
|
||||
$($(memory_tool!(@insert_json map, $arg, $($typ)+);)*)?
|
||||
memory_rpc(stringify!($name), serde_json::Value::Object(map))
|
||||
}
|
||||
StoreAccess::None(err) => anyhow::bail!("{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Call hippocampus with appropriate mutability
|
||||
(@call mut, $name:ident, $store:ident, $prov:expr $(, $arg:expr)*) => {
|
||||
crate::hippocampus::$name(&mut $store, $prov $(, $arg)*)
|
||||
};
|
||||
(@call ref, $name:ident, $store:ident, $prov:expr $(, $arg:expr)*) => {
|
||||
crate::hippocampus::$name(&$store, $prov $(, $arg)*)
|
||||
};
|
||||
|
||||
// Immutable store variant
|
||||
($name:ident, ref $(, $($arg:ident : [$($typ:tt)+]),* $(,)?)?) => {
|
||||
// ── Main rules ─────────────────────────────────────────────────
|
||||
|
||||
// Shorthand: mut/ref without return type defaults to String
|
||||
($name:ident, $m:ident $(, $($arg:ident : [$($typ:tt)+]),* $(,)?)?) => {
|
||||
memory_tool!($name, $m -> String $(, $($arg : [$($typ)+]),*)?);
|
||||
};
|
||||
|
||||
// Full form with return type
|
||||
($name:ident, $m:ident -> $ret:ty $(, $($arg:ident : [$($typ:tt)+]),* $(,)?)?) => {
|
||||
paste::paste! {
|
||||
async fn [<jsonargs_ $name>](agent: &Option<std::sync::Arc<crate::agent::Agent>>, args: &serde_json::Value) -> Result<String> {
|
||||
$($(let $arg = memory_tool!(@extract args, $arg, $($typ)+);)*)?
|
||||
let prov = get_provenance(agent).await;
|
||||
match access() {
|
||||
StoreAccess::Daemon(arc) => {
|
||||
let store = arc.lock().await;
|
||||
crate::hippocampus::$name(&store, &prov $($(, $arg)*)?)
|
||||
#[allow(unused_mut)]
|
||||
let mut store = arc.lock().await;
|
||||
let result: $ret = memory_tool!(@call $m, $name, store, &prov $($(, $arg)*)?)?;
|
||||
Ok(memory_tool!(@serialize $ret, result))
|
||||
}
|
||||
StoreAccess::Client => {
|
||||
#[allow(unused_mut)]
|
||||
let mut map = serde_json::Map::new();
|
||||
$($(memory_tool!(@insert_json map, $arg, $($typ)+);)*)?
|
||||
memory_rpc(stringify!($name), serde_json::Value::Object(map))
|
||||
}
|
||||
StoreAccess::Client => anyhow::bail!("jsonargs called in client mode"),
|
||||
StoreAccess::None(err) => anyhow::bail!("{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn $name(agent: Option<&crate::agent::Agent> $($(, $arg: memory_tool!(@param_type $($typ)+))*)?) -> Result<String> {
|
||||
pub async fn $name(agent: Option<&crate::agent::Agent> $($(, $arg: memory_tool!(@param_type $($typ)+))*)?) -> Result<$ret> {
|
||||
let prov = match agent {
|
||||
Some(a) => a.state.lock().await.provenance.clone(),
|
||||
None => "manual".to_string(),
|
||||
|
|
@ -306,14 +293,16 @@ macro_rules! memory_tool {
|
|||
|
||||
match access() {
|
||||
StoreAccess::Daemon(arc) => {
|
||||
let store = arc.lock().await;
|
||||
crate::hippocampus::$name(&store, &prov $($(, $arg)*)?)
|
||||
#[allow(unused_mut)]
|
||||
let mut store = arc.lock().await;
|
||||
memory_tool!(@call $m, $name, store, &prov $($(, $arg)*)?)
|
||||
}
|
||||
StoreAccess::Client => {
|
||||
#[allow(unused_mut)]
|
||||
let mut map = serde_json::Map::new();
|
||||
$($(memory_tool!(@insert_json map, $arg, $($typ)+);)*)?
|
||||
memory_rpc(stringify!($name), serde_json::Value::Object(map))
|
||||
let json = memory_rpc(stringify!($name), serde_json::Value::Object(map))?;
|
||||
memory_tool!(@deserialize $ret, json)
|
||||
}
|
||||
StoreAccess::None(err) => anyhow::bail!("{}", err),
|
||||
}
|
||||
|
|
@ -327,7 +316,6 @@ macro_rules! memory_tool {
|
|||
memory_tool!(memory_render, ref, key: [str], raw: [Option<bool>]);
|
||||
memory_tool!(memory_write, mut, key: [str], content: [str]);
|
||||
memory_tool!(memory_search, ref, keys: [Vec<String>], max_hops: [Option<u32>], edge_decay: [Option<f64>], min_activation: [Option<f64>], limit: [Option<usize>]);
|
||||
memory_tool!(memory_links, ref, key: [str]);
|
||||
memory_tool!(memory_link_set, mut, source: [str], target: [str], strength: [f32]);
|
||||
memory_tool!(memory_link_add, mut, source: [str], target: [str]);
|
||||
memory_tool!(memory_delete, mut, key: [str]);
|
||||
|
|
@ -337,6 +325,11 @@ memory_tool!(memory_rename, mut, old_key: [str], new_key: [str]);
|
|||
memory_tool!(memory_supersede, mut, old_key: [str], new_key: [str], reason: [Option<&str>]);
|
||||
memory_tool!(memory_query, ref, query: [str], format: [Option<&str>]);
|
||||
|
||||
// Re-export LinkInfo for callers
|
||||
pub use crate::hippocampus::LinkInfo;
|
||||
|
||||
memory_tool!(memory_links, ref -> Vec<LinkInfo>, key: [str]);
|
||||
|
||||
// ── Journal tools ──────────────────────────────────────────────
|
||||
|
||||
memory_tool!(journal_tail, ref, count: [Option<u64>], level: [Option<u64>], format: [Option<&str>], after: [Option<&str>]);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,12 @@ pub async fn cmd_link(key: &[String]) -> Result<(), String> {
|
|||
return Err("link requires a key".into());
|
||||
}
|
||||
let key = key.join(" ");
|
||||
let result = memory::memory_links(None, &key).await
|
||||
let links = memory::memory_links(None, &key).await
|
||||
.map_err(|e| e.to_string())?;
|
||||
print!("{}", result);
|
||||
println!("Neighbors of '{}':", key);
|
||||
for link in links {
|
||||
println!(" ({:.2}) {} [w={:.2}]", link.link_strength, link.key, link.node_weight);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,15 +84,29 @@ pub fn memory_search(
|
|||
.collect::<Vec<_>>().join("\n"))
|
||||
}
|
||||
|
||||
pub fn memory_links(store: &Store, _provenance: &str, key: &str) -> Result<String> {
|
||||
/// Info about a linked neighbor node.
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LinkInfo {
|
||||
pub key: String,
|
||||
pub link_strength: f32,
|
||||
pub node_weight: f32,
|
||||
}
|
||||
|
||||
pub fn memory_links(store: &Store, _provenance: &str, key: &str) -> Result<Vec<LinkInfo>> {
|
||||
let node = MemoryNode::from_store(store, key)
|
||||
.ok_or_else(|| anyhow::anyhow!("node not found: {}", key))?;
|
||||
let mut out = format!("Neighbors of '{}':\n", key);
|
||||
for (target, strength, is_new) in &node.links {
|
||||
let tag = if *is_new { " (new)" } else { "" };
|
||||
out.push_str(&format!(" ({:.2}) {}{}\n", strength, target, tag));
|
||||
let mut links = Vec::new();
|
||||
for (target, strength, _is_new) in &node.links {
|
||||
let node_weight = store.nodes.get(target.as_str())
|
||||
.map(|n| n.weight)
|
||||
.unwrap_or(0.5);
|
||||
links.push(LinkInfo {
|
||||
key: target.clone(),
|
||||
link_strength: *strength,
|
||||
node_weight,
|
||||
});
|
||||
}
|
||||
Ok(out)
|
||||
Ok(links)
|
||||
}
|
||||
|
||||
pub fn memory_link_set(store: &mut Store, _provenance: &str, source: &str, target: &str, strength: f32) -> Result<String> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue