use axum::{
Router,
routing::{get, post},
};
use clap::Parser;
use cortex_mem_core::{
config::Config, llm::create_llm_client, memory::MemoryManager,
vector_store::qdrant::QdrantVectorStore,
};
use std::{path::PathBuf, sync::Arc};
use tokio::net::TcpListener;
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;
use tracing::info;
use tracing_subscriber;
mod handlers;
mod models;
mod optimization_handlers;
use handlers::{
batch_delete_memories, batch_update_memories, create_memory, delete_memory, get_memory, health_check, list_memories, search_memories, update_memory, get_llm_status, llm_health_check,
};
use optimization_handlers::{
analyze_optimization, cancel_optimization, cleanup_history, get_optimization_history,
get_optimization_statistics, get_optimization_status, start_optimization,
OptimizationJobState,
};
#[derive(Clone)]
pub struct AppState {
pub memory_manager: Arc<MemoryManager>,
pub optimization_jobs: Arc<tokio::sync::RwLock<std::collections::HashMap<String, OptimizationJobState>>>,
}
#[derive(Parser)]
#[command(name = "cortex-mem-service")]
#[command(about = "Cortex Memory HTTP Service")]
#[command(author = "Sopaco")]
#[command(version)]
struct Cli {
#[arg(short, long, default_value = "config.toml")]
config: PathBuf,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
let cli = Cli::parse();
let config = Config::load(&cli.config)?;
let memory_manager = create_memory_manager(&config).await?;
let app_state = AppState {
memory_manager: Arc::new(memory_manager),
optimization_jobs: Arc::new(tokio::sync::RwLock::new(std::collections::HashMap::new())),
};
let app = Router::new()
.route("/health", get(health_check))
.route("/memories", post(create_memory).get(list_memories))
.route("/memories/search", post(search_memories))
.route(
"/memories/{id}",
get(get_memory).put(update_memory).delete(delete_memory),
)
.route("/memories/batch/delete", post(batch_delete_memories))
.route("/memories/batch/update", post(batch_update_memories))
.route("/optimization", post(start_optimization))
.route("/optimization/{job_id}", get(get_optimization_status))
.route("/optimization/{job_id}/cancel", post(cancel_optimization))
.route("/optimization/history", get(get_optimization_history))
.route("/optimization/analyze", post(analyze_optimization))
.route("/optimization/statistics", get(get_optimization_statistics))
.route("/optimization/cleanup", post(cleanup_history))
.route("/llm/status", get(get_llm_status))
.route("/llm/health-check", get(llm_health_check))
.layer(
ServiceBuilder::new()
.layer(CorsLayer::permissive())
.into_inner(),
)
.with_state(app_state);
let addr = format!("{}:{}", config.server.host, config.server.port);
info!("Starting cortex-mem-service on {}", addr);
let listener = TcpListener::bind(&addr).await?;
axum::serve(listener, app).await?;
Ok(())
}
async fn create_memory_manager(
config: &Config,
) -> Result<MemoryManager, Box<dyn std::error::Error>> {
let vector_store = QdrantVectorStore::new(&config.qdrant).await?;
let llm_client = create_llm_client(&config.llm, &config.embedding)?;
let memory_manager =
MemoryManager::new(Box::new(vector_store), llm_client, config.memory.clone());
info!("Memory manager initialized successfully");
Ok(memory_manager)
}