Archived
219 lines
7.0 KiB
Rust
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 }))
|
|
}
|
|
|