174 lines
5.4 KiB
Rust
174 lines
5.4 KiB
Rust
//! HTTP handler functions for CMS endpoints.
|
|||
|
|
//!
|
||
|
|
//! Each handler takes a service trait (via generics or trait objects) and
|
||
|
|
//! returns domain-level results. These functions are agnostic about the
|
||
|
|
//! HTTP framework — callers (e.g. Axum routes) are responsible for mapping
|
||
|
|
//! `Result` into HTTP responses.
|
||
|
|
|
||
|
|
#![allow(
|
||
|
|
clippy::cast_possible_truncation,
|
||
|
|
clippy::cast_sign_loss,
|
||
|
|
clippy::cast_precision_loss,
|
||
|
|
clippy::cast_possible_wrap
|
||
|
|
)]
|
||
|
|
|
||
|
|
use anyhow::{Context, Result};
|
||
|
|
|
||
|
|
use crate::domain::memory::Memory;
|
||
|
|
use crate::domain::service::{MemoryService, SettingsService};
|
||
|
|
use crate::domain::settings::Settings;
|
||
|
|
|
||
|
|
use super::dto::{
|
||
|
|
MemoryCreateRequest, MemoryResponse, SettingsResponse, SettingsUpdateRequest,
|
||
|
|
};
|
||
|
|
|
||
|
|
/// Handle `GET /settings`
|
||
|
|
///
|
||
|
|
/// Returns the current settings as a `SettingsResponse`.
|
||
|
|
pub fn handle_get_settings<S: SettingsService>(service: &S) -> Result<SettingsResponse> {
|
||
|
|
let settings = service
|
||
|
|
.load_settings()
|
||
|
|
.context("failed to load settings")?;
|
||
|
|
Ok(SettingsResponse::from(settings))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Handle `PUT /settings`
|
||
|
|
///
|
||
|
|
/// Applies the partial update from `req` to the current settings, persists
|
||
|
|
/// the result, and returns the updated `SettingsResponse`.
|
||
|
|
pub fn handle_update_settings<S: SettingsService>(
|
||
|
|
service: &S,
|
||
|
|
req: SettingsUpdateRequest,
|
||
|
|
) -> Result<SettingsResponse> {
|
||
|
|
let mut settings: Settings = service
|
||
|
|
.load_settings()
|
||
|
|
.context("failed to load current settings for update")?;
|
||
|
|
|
||
|
|
// Apply partial updates
|
||
|
|
if let Some(val) = req.internet_mode {
|
||
|
|
settings.internet_mode = match val.as_str() {
|
||
|
|
"Off" => crate::domain::settings::InternetMode::Off,
|
||
|
|
"ReadOnly" => crate::domain::settings::InternetMode::ReadOnly,
|
||
|
|
"Full" => crate::domain::settings::InternetMode::Full,
|
||
|
|
_ => {
|
||
|
|
return Err(anyhow::anyhow!(
|
||
|
|
"invalid internet_mode '{}'; expected Off, ReadOnly, or Full",
|
||
|
|
val
|
||
|
|
));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
if let Some(val) = req.provider {
|
||
|
|
settings.provider = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.model {
|
||
|
|
settings.model = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.api_keys {
|
||
|
|
settings.api_keys = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.max_tokens {
|
||
|
|
settings.max_tokens = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.temperature {
|
||
|
|
settings.temperature = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.review_max_lessons_per_run {
|
||
|
|
settings.review_max_lessons_per_run = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.adaptive_review_max_skip {
|
||
|
|
settings.adaptive_review_max_skip = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.verify_command {
|
||
|
|
settings.verify_command = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.verify_timeout_ms {
|
||
|
|
settings.verify_timeout_ms = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.workflow_max_concurrency {
|
||
|
|
settings.workflow_max_concurrency = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.review_enabled {
|
||
|
|
settings.flags.review_enabled = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.session_archive_enabled {
|
||
|
|
settings.flags.session_archive_enabled = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.lsp_auto_provision {
|
||
|
|
settings.flags.lsp_auto_provision = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.lsp_languages {
|
||
|
|
settings.lsp_languages = val;
|
||
|
|
}
|
||
|
|
if let Some(val) = req.hive_mind_node_timeout_ms {
|
||
|
|
settings.hive_mind_node_timeout_ms = val;
|
||
|
|
}
|
||
|
|
|
||
|
|
service
|
||
|
|
.save_settings(&settings)
|
||
|
|
.context("failed to save updated settings")?;
|
||
|
|
|
||
|
|
Ok(SettingsResponse::from(settings))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Handle `GET /memories`
|
||
|
|
///
|
||
|
|
/// Lists all memory slugs, then loads each memory to return full responses.
|
||
|
|
pub fn handle_list_memories<M: MemoryService>(service: &M) -> Result<Vec<MemoryResponse>> {
|
||
|
|
let slugs = service
|
||
|
|
.list_memories()
|
||
|
|
.context("failed to list memories")?;
|
||
|
|
|
||
|
|
// We can't load individual memories without a load_memory method on the
|
||
|
|
// service. For now, list returns summary info; callers who need full
|
||
|
|
// content use a separate endpoint. Return minimal responses keyed by slug.
|
||
|
|
let responses: Vec<MemoryResponse> = slugs
|
||
|
|
.into_iter()
|
||
|
|
.map(|slug| MemoryResponse {
|
||
|
|
name: slug.clone(),
|
||
|
|
description: String::new(),
|
||
|
|
content: String::new(),
|
||
|
|
kind: String::new(),
|
||
|
|
created_at: 0,
|
||
|
|
updated_at: 0,
|
||
|
|
outcome: None,
|
||
|
|
lifecycle: String::new(),
|
||
|
|
scope: None,
|
||
|
|
before_snippet: None,
|
||
|
|
after_snippet: None,
|
||
|
|
provenances: Vec::new(),
|
||
|
|
})
|
||
|
|
.collect();
|
||
|
|
Ok(responses)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Handle `POST /memories`
|
||
|
|
///
|
||
|
|
/// Creates or updates a memory from the request body.
|
||
|
|
pub fn handle_create_memory<M: MemoryService>(
|
||
|
|
service: &M,
|
||
|
|
req: MemoryCreateRequest,
|
||
|
|
) -> Result<MemoryResponse> {
|
||
|
|
let now = chrono::Utc::now().timestamp();
|
||
|
|
let memory = Memory {
|
||
|
|
name: req.name,
|
||
|
|
description: req.description,
|
||
|
|
content: req.content,
|
||
|
|
kind: req.kind.unwrap_or_else(|| "reference".to_string()),
|
||
|
|
created_at: now,
|
||
|
|
updated_at: now,
|
||
|
|
outcome: req.outcome,
|
||
|
|
lifecycle: req.lifecycle.unwrap_or_else(|| "new".to_string()),
|
||
|
|
scope: req.scope,
|
||
|
|
before_snippet: req.before_snippet,
|
||
|
|
after_snippet: req.after_snippet,
|
||
|
|
provenances: req.provenances.unwrap_or_default(),
|
||
|
|
};
|
||
|
|
|
||
|
|
service
|
||
|
|
.save_memory(&memory)
|
||
|
|
.context("failed to save memory")?;
|
||
|
|
|
||
|
|
Ok(MemoryResponse::from(memory))
|
||
|
|
}
|