This commit is contained in:
137
app/src-tauri/src/commands/memory.rs
Normal file
137
app/src-tauri/src/commands/memory.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
// CC Sesija A §2.1 Tasks A1 + A4 — memory + KG + identity Tauri commands.
|
||||
//
|
||||
// Architecture: thin HTTP proxies to local sidecar (Fastify, port from ServiceState).
|
||||
// Tauri Rust shell intentionally has no direct sqlite/sqlite-vec dependencies — all
|
||||
// substrate access goes through the sidecar so that the storage layer (FrameStore,
|
||||
// HybridSearch, KnowledgeGraph, wiki-compiler) lives in one process.
|
||||
//
|
||||
// Command surface (4 commands here; wiki commands moved to commands/wiki.rs at A4):
|
||||
// recall_memory → GET /api/memory/search
|
||||
// save_memory → POST /api/memory/frames
|
||||
// search_entities → GET /api/memory/graph
|
||||
// get_identity → GET /api/identity (route not yet in sidecar — graceful 404
|
||||
// → placeholder; A1.1 follow-up adds route)
|
||||
//
|
||||
// Shared HTTP helpers extracted to commands/http.rs at A4 (rule of two — wiki.rs is
|
||||
// the second consumer).
|
||||
|
||||
use serde_json::{json, Value};
|
||||
use tauri::State;
|
||||
|
||||
use crate::commands::http::{http_get, http_post, parse_json, sidecar_url};
|
||||
use crate::service::ServiceState;
|
||||
|
||||
/// Recall memory frames matching `query`. Optional `scope` (all|personal|workspace),
|
||||
/// `limit` (default sidecar-side ~20), `workspace_id` (workspace mind to search).
|
||||
#[tauri::command]
|
||||
pub async fn recall_memory(
|
||||
state: State<'_, ServiceState>,
|
||||
query: String,
|
||||
scope: Option<String>,
|
||||
limit: Option<u32>,
|
||||
workspace_id: Option<String>,
|
||||
) -> Result<Value, String> {
|
||||
let mut url = format!(
|
||||
"{}?q={}",
|
||||
sidecar_url(state.port, "/api/memory/search"),
|
||||
urlencoding::encode(&query)
|
||||
);
|
||||
if let Some(s) = scope {
|
||||
url.push_str(&format!("&scope={}", urlencoding::encode(&s)));
|
||||
}
|
||||
if let Some(l) = limit {
|
||||
url.push_str(&format!("&limit={}", l));
|
||||
}
|
||||
if let Some(ws) = workspace_id {
|
||||
url.push_str(&format!("&workspace={}", urlencoding::encode(&ws)));
|
||||
}
|
||||
|
||||
let resp = http_get(&url).await?;
|
||||
parse_json(resp).await
|
||||
}
|
||||
|
||||
/// Persist a new memory frame. `content` is required. Optional `workspace_id`,
|
||||
/// `importance` (low|normal|high|critical per @waggle/core Importance enum), `source`
|
||||
/// (one of: user_stated, tool_verified, agent_inferred, import, system).
|
||||
#[tauri::command]
|
||||
pub async fn save_memory(
|
||||
state: State<'_, ServiceState>,
|
||||
content: String,
|
||||
workspace_id: Option<String>,
|
||||
importance: Option<String>,
|
||||
source: Option<String>,
|
||||
) -> Result<Value, String> {
|
||||
let mut body = json!({ "content": content });
|
||||
if let Some(ws) = workspace_id {
|
||||
body["workspace"] = json!(ws);
|
||||
}
|
||||
if let Some(imp) = importance {
|
||||
body["importance"] = json!(imp);
|
||||
}
|
||||
if let Some(src) = source {
|
||||
body["source"] = json!(src);
|
||||
}
|
||||
|
||||
let url = sidecar_url(state.port, "/api/memory/frames");
|
||||
let resp = http_post(&url, &body).await?;
|
||||
parse_json(resp).await
|
||||
}
|
||||
|
||||
/// Search the knowledge graph for entities/relations. Returns `{nodes, edges}` shape
|
||||
/// from sidecar's KnowledgeGraph layer. `scope` defaults to "all" if not provided.
|
||||
#[tauri::command]
|
||||
pub async fn search_entities(
|
||||
state: State<'_, ServiceState>,
|
||||
workspace_id: Option<String>,
|
||||
scope: Option<String>,
|
||||
) -> Result<Value, String> {
|
||||
let mut url = sidecar_url(state.port, "/api/memory/graph").to_string();
|
||||
let mut params: Vec<String> = Vec::new();
|
||||
if let Some(ws) = workspace_id {
|
||||
params.push(format!("workspace={}", urlencoding::encode(&ws)));
|
||||
}
|
||||
if let Some(s) = scope {
|
||||
params.push(format!("scope={}", urlencoding::encode(&s)));
|
||||
}
|
||||
if !params.is_empty() {
|
||||
url.push('?');
|
||||
url.push_str(¶ms.join("&"));
|
||||
}
|
||||
|
||||
let resp = http_get(&url).await?;
|
||||
parse_json(resp).await
|
||||
}
|
||||
|
||||
/// Get the user identity record from sidecar's IdentityLayer.
|
||||
///
|
||||
/// Backed by /api/identity sidecar route (A1.1 shipped 2026-04-30). The route
|
||||
/// always returns a 200 with either configured: true + IdentityLayer fields,
|
||||
/// or configured: false + null fields + optional _note. The 404 + transport-
|
||||
/// error fallbacks here remain as defense-in-depth — they were the original
|
||||
/// pre-A1.1 placeholders and now only fire on hard sidecar outages.
|
||||
#[tauri::command]
|
||||
pub async fn get_identity(state: State<'_, ServiceState>) -> Result<Value, String> {
|
||||
let url = sidecar_url(state.port, "/api/identity");
|
||||
match http_get(&url).await {
|
||||
Ok(resp) if resp.status().as_u16() == 404 => Ok(identity_placeholder(
|
||||
"sidecar route 404 (unexpected post-A1.1)",
|
||||
)),
|
||||
Ok(resp) => parse_json(resp).await,
|
||||
Err(_) => Ok(identity_placeholder("sidecar unreachable")),
|
||||
}
|
||||
}
|
||||
|
||||
fn identity_placeholder(note: &str) -> Value {
|
||||
json!({
|
||||
"configured": false,
|
||||
"name": null,
|
||||
"role": null,
|
||||
"department": null,
|
||||
"personality": null,
|
||||
"capabilities": null,
|
||||
"system_prompt": null,
|
||||
"created_at": null,
|
||||
"updated_at": null,
|
||||
"_note": note
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user