use aonyx_core::MemoryStore;
use aonyx_memory::{DiaryEntry, DiaryStore, Entity, KgStore, Relation};
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use axum::Json;
use serde::{Deserialize, Serialize};
use crate::error::{ApiError, ApiResult};
use crate::state::ApiState;
#[derive(Debug, Deserialize)]
pub struct SearchParams {
pub q: String,
pub k: Option<usize>,
}
#[derive(Debug, Serialize)]
pub struct SearchHit {
pub content: String,
pub score: f32,
}
pub async fn search(
State(state): State<ApiState>,
Query(params): Query<SearchParams>,
) -> ApiResult<Json<Vec<SearchHit>>> {
if params.q.trim().is_empty() {
return Err(ApiError::BadRequest("missing query `q`".into()));
}
let k = params.k.unwrap_or(10).clamp(1, 100);
let hits = state.palace.hybrid_search(¶ms.q, k).await?;
Ok(Json(
hits.into_iter()
.map(|(content, score)| SearchHit { content, score })
.collect(),
))
}
#[derive(Debug, Deserialize)]
pub struct DiaryParams {
pub project: Option<String>,
pub limit: Option<usize>,
}
pub async fn diary_list(
State(state): State<ApiState>,
Query(params): Query<DiaryParams>,
) -> ApiResult<Json<Vec<DiaryEntry>>> {
let project = state.project_or_default(params.project);
let limit = params.limit.unwrap_or(50).clamp(1, 500);
let entries = state.palace.diary.recent(&project, limit).await?;
Ok(Json(entries))
}
#[derive(Debug, Deserialize)]
pub struct DiaryAppendRequest {
pub project: Option<String>,
pub content: String,
}
pub async fn diary_append(
State(state): State<ApiState>,
Json(req): Json<DiaryAppendRequest>,
) -> ApiResult<StatusCode> {
if req.content.trim().is_empty() {
return Err(ApiError::BadRequest("empty diary content".into()));
}
let project = state.project_or_default(req.project);
state.palace.diary_append(&project, &req.content).await?;
Ok(StatusCode::CREATED)
}
#[derive(Debug, Deserialize)]
pub struct KgParams {
pub limit: Option<usize>,
}
pub async fn kg_entities(
State(state): State<ApiState>,
Query(params): Query<KgParams>,
) -> ApiResult<Json<Vec<Entity>>> {
let limit = params.limit.unwrap_or(100).clamp(1, 1000);
Ok(Json(state.palace.kg.list_entities(limit).await?))
}
pub async fn kg_entity_by_name(
State(state): State<ApiState>,
Path(name): Path<String>,
) -> ApiResult<Json<Vec<Entity>>> {
Ok(Json(state.palace.kg.find_entities_by_name(&name).await?))
}
pub async fn kg_relations(
State(state): State<ApiState>,
Query(params): Query<KgParams>,
) -> ApiResult<Json<Vec<Relation>>> {
let limit = params.limit.unwrap_or(100).clamp(1, 1000);
Ok(Json(state.palace.kg.list_relations(limit).await?))
}