This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
engram-retired/crates/engram-server/src/routes/core.rs
T

219 lines
7.0 KiB
Rust

/// 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 }))
}