Archived
feat: Engram sync layer — swarm memory protocol, peer delta sync, distributed activation
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
/// Core Engram API routes — nodes, edges, activation, search.
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use engram_core::types::{Edge, MemoryTier, Node, NodeType, RelationType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
// ── Stats ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StatsResponse {
|
||||
pub nodes: usize,
|
||||
pub edges: usize,
|
||||
}
|
||||
|
||||
pub async fn get_stats(
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<StatsResponse>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let nodes = db.node_count().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let edges = db.edge_count().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(StatsResponse { nodes, edges }))
|
||||
}
|
||||
|
||||
// ── Nodes ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateNodeRequest {
|
||||
pub node_type: NodeType,
|
||||
pub embedding: Vec<f32>,
|
||||
pub content: Vec<u8>,
|
||||
pub tier: MemoryTier,
|
||||
pub importance: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateNodeResponse {
|
||||
pub id: Uuid,
|
||||
}
|
||||
|
||||
pub async fn create_node(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<CreateNodeRequest>,
|
||||
) -> Result<Json<CreateNodeResponse>, StatusCode> {
|
||||
let node = Node::new(req.node_type, req.embedding, req.content, req.tier, req.importance);
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let id = db.put_node(node).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(CreateNodeResponse { id }))
|
||||
}
|
||||
|
||||
pub async fn get_node(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<Node>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
match db.get_node(id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? {
|
||||
Some(node) => Ok(Json(node)),
|
||||
None => Err(StatusCode::NOT_FOUND),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Edges ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateEdgeRequest {
|
||||
pub from_id: Uuid,
|
||||
pub to_id: Uuid,
|
||||
pub relation: RelationType,
|
||||
pub weight: f32,
|
||||
}
|
||||
|
||||
pub async fn create_edge(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<CreateEdgeRequest>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
let edge = Edge::new(req.from_id, req.to_id, req.relation, req.weight);
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
db.put_edge(edge).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
pub async fn get_edges_from(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<Vec<Edge>>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let edges = db.get_edges_from(id).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(edges))
|
||||
}
|
||||
|
||||
// ── Activation ────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ActivateRequest {
|
||||
pub seeds: Vec<Uuid>,
|
||||
pub query_embedding: Vec<f32>,
|
||||
#[serde(default = "default_depth")]
|
||||
pub max_depth: u8,
|
||||
#[serde(default = "default_limit")]
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
fn default_depth() -> u8 { 3 }
|
||||
fn default_limit() -> usize { 10 }
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ActivateResponse {
|
||||
pub results: Vec<ActivatedNodeJson>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ActivatedNodeJson {
|
||||
pub node: Node,
|
||||
pub activation_strength: f32,
|
||||
pub hops: u8,
|
||||
}
|
||||
|
||||
pub async fn activate(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<ActivateRequest>,
|
||||
) -> Result<Json<ActivateResponse>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let results = db
|
||||
.activate(&req.seeds, &req.query_embedding, req.max_depth, req.limit)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(Json(ActivateResponse {
|
||||
results: results
|
||||
.into_iter()
|
||||
.map(|a| ActivatedNodeJson {
|
||||
node: a.node,
|
||||
activation_strength: a.activation_strength,
|
||||
hops: a.hops,
|
||||
})
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
// ── Search ────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
pub embedding: Vec<f32>,
|
||||
#[serde(default = "default_limit")]
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SearchResponse {
|
||||
pub results: Vec<ScoredNodeJson>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ScoredNodeJson {
|
||||
pub node: Node,
|
||||
pub score: f32,
|
||||
}
|
||||
|
||||
pub async fn search_embedding(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<SearchRequest>,
|
||||
) -> Result<Json<SearchResponse>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let results = db
|
||||
.search_embedding(&req.embedding, req.limit)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(Json(SearchResponse {
|
||||
results: results
|
||||
.into_iter()
|
||||
.map(|s| ScoredNodeJson { node: s.node, score: s.score })
|
||||
.collect(),
|
||||
}))
|
||||
}
|
||||
|
||||
// ── Decay / Consolidate ───────────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DecayRequest {
|
||||
pub factor: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct DecayResponse {
|
||||
pub nodes_updated: usize,
|
||||
}
|
||||
|
||||
pub async fn decay(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<DecayRequest>,
|
||||
) -> Result<Json<DecayResponse>, StatusCode> {
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let nodes_updated = db.decay(req.factor).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(DecayResponse { nodes_updated }))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ConsolidateResponse {
|
||||
pub promoted: usize,
|
||||
}
|
||||
|
||||
pub async fn consolidate(
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<ConsolidateResponse>, StatusCode> {
|
||||
use engram_core::ConsolidationConfig;
|
||||
let db = state.db.lock().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
let report = db
|
||||
.consolidate(&ConsolidationConfig::default())
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(ConsolidateResponse { promoted: report.promoted }))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user