use crate::server::Server;
use anyhow::{Context, Result, anyhow};
use mnem_core::codec::json_to_ipld;
use mnem_core::id::EdgeId;
use mnem_core::objects::{Edge, Node};
use serde_json::Value;
pub(in crate::tools) fn commit_relation(server: &mut Server, args: Value) -> Result<String> {
let allow_labels = server.allow_labels;
let subject = args
.get("subject")
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("missing 'subject'"))?
.to_string();
let predicate = args
.get("predicate")
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("missing 'predicate' (edge type, e.g. 'works_at')"))?
.to_string();
let object = args
.get("object")
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("missing 'object'"))?
.to_string();
let subject_kind = if allow_labels {
args.get("subject_kind")
.and_then(Value::as_str)
.filter(|s| !s.trim().is_empty())
.unwrap_or(Node::DEFAULT_NTYPE)
.to_string()
} else {
Node::DEFAULT_NTYPE.to_string()
};
let object_kind = if allow_labels {
args.get("object_kind")
.and_then(Value::as_str)
.filter(|s| !s.trim().is_empty())
.unwrap_or(Node::DEFAULT_NTYPE)
.to_string()
} else {
Node::DEFAULT_NTYPE.to_string()
};
let anchor = args
.get("anchor")
.and_then(Value::as_str)
.filter(|s| !s.trim().is_empty())
.unwrap_or("name")
.to_string();
let agent_id = args
.get("agent_id")
.and_then(Value::as_str)
.unwrap_or("mnem mcp")
.to_string();
let message = args
.get("message")
.and_then(Value::as_str)
.unwrap_or("mnem_mcp commit_relation")
.to_string();
let repo = server.load_repo()?;
let mut tx = repo.start_transaction();
#[cfg(feature = "summarize")]
let opt_embedder = crate::tools::embed::resolve_embed_cfg(server.repo_path())
.and_then(|pc| mnem_embed_providers::open(&pc).ok());
let subject_value = json_to_ipld(&Value::String(subject.clone()))?;
let subject_id = tx
.resolve_or_create_node(&subject_kind, &anchor, subject_value.clone())
.with_context(|| format!("resolve_or_create subject `{subject}` ({subject_kind})"))?;
let mut subject_node =
Node::new(subject_id, subject_kind.clone()).with_prop(anchor.clone(), subject_value);
if let Some(Value::Object(map)) = args.get("subject_props") {
for (k, v) in map {
subject_node = subject_node.with_prop(k.clone(), json_to_ipld(v)?);
}
}
let subject_cid = tx.add_node(&subject_node)?;
#[cfg(feature = "summarize")]
if let Some(ref embedder) = opt_embedder {
if let Ok(vec) = embedder.embed(&subject) {
let model = embedder.model().to_string();
let emb = mnem_embed_providers::to_embedding(&model, &vec);
let _ = tx.set_embedding(subject_cid, model, emb);
}
}
let object_value = json_to_ipld(&Value::String(object.clone()))?;
let object_id = tx
.resolve_or_create_node(&object_kind, &anchor, object_value.clone())
.with_context(|| format!("resolve_or_create object `{object}` ({object_kind})"))?;
let mut object_node =
Node::new(object_id, object_kind.clone()).with_prop(anchor.clone(), object_value);
if let Some(Value::Object(map)) = args.get("object_props") {
for (k, v) in map {
object_node = object_node.with_prop(k.clone(), json_to_ipld(v)?);
}
}
let object_cid = tx.add_node(&object_node)?;
#[cfg(feature = "summarize")]
if let Some(ref embedder) = opt_embedder {
if let Ok(vec) = embedder.embed(&object) {
let model = embedder.model().to_string();
let emb = mnem_embed_providers::to_embedding(&model, &vec);
let _ = tx.set_embedding(object_cid, model, emb);
}
}
let mut edge = Edge::new(EdgeId::new_v7(), predicate.as_str(), subject_id, object_id);
if let Some(Value::Object(map)) = args.get("edge_props") {
for (k, v) in map {
edge = edge.with_prop(k.clone(), json_to_ipld(v)?);
}
}
tx.add_edge(&edge)?;
let opts = mnem_core::repo::CommitOptions::new(agent_id.as_str(), message.as_str());
let new_repo = tx.commit_opts(opts)?;
let mut out = String::new();
out.push_str("mnem_commit_relation: ok\n");
out.push_str(&format!(" op_id: {}\n", new_repo.op_id()));
out.push_str(&format!(
" commit_cid: {}\n",
new_repo
.view()
.heads
.first()
.map_or_else(|| "<none>".to_string(), ToString::to_string)
));
out.push_str(&format!(
" subject: {} [{}] {}\n",
subject_id.to_uuid_string(),
subject_kind,
subject
));
out.push_str(&format!(" predicate: {predicate}\n"));
out.push_str(&format!(
" object: {} [{}] {}\n",
object_id.to_uuid_string(),
object_kind,
object
));
Ok(out)
}