diff --git a/src/gateway/http.rs b/src/gateway/http.rs index a455a6b..87baab9 100644 --- a/src/gateway/http.rs +++ b/src/gateway/http.rs @@ -947,6 +947,49 @@ pub async fn get_memories( Ok(Json(json!({ "memories": memories }))) } +#[derive(Deserialize)] +pub struct PutMemoryBody { + content: String, + importance: Option, +} + +pub async fn put_memory( + State(state): State>, + Path(key): Path, + Json(body): Json, +) -> Result, ApiError> { + let existing = state + .storage + .get_memory_by_key(&key) + .await + .map_err(ApiError::internal)? + .ok_or_else(|| ApiError::not_found("memory not found"))?; + let mut updated = existing; + updated.content = body.content; + if let Some(importance) = body.importance { + updated.importance = importance.clamp(0.0, 1.0); + } + updated.updated_at = chrono::Utc::now().to_rfc3339(); + state + .storage + .upsert_memory(&updated) + .await + .map_err(ApiError::internal)?; + Ok(Json(json!({ "updated": true, "key": key }))) +} + +pub async fn delete_memory( + State(state): State>, + Path(key): Path, +) -> Result, ApiError> { + state + .storage + .delete_memory(&key) + .await + .map_err(ApiError::internal)?; + Ok(Json(json!({ "deleted": true }))) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 36fc4cc..00a4432 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -599,6 +599,10 @@ fn build_router(state: Arc) -> Router { .route("/api/jobs", routing::get(http::get_jobs)) .route("/api/jobs/{id}/runs", routing::get(http::get_job_runs)) .route("/api/memories", routing::get(http::get_memories)) + .route( + "/api/memories/{key}", + routing::put(http::put_memory).delete(http::delete_memory), + ) .route( "/api/chat/{client_id}/uploads", routing::post(http::upload_file).layer(axum::extract::DefaultBodyLimit::disable()), diff --git a/src/storage/memory.rs b/src/storage/memory.rs index ffc8fc7..5e2db2d 100644 --- a/src/storage/memory.rs +++ b/src/storage/memory.rs @@ -42,6 +42,17 @@ impl super::Storage { parse_memory_rows(&rows) } + pub async fn get_memory_by_key(&self, key: &str) -> Result, StorageError> { + let rows = sqlx::query( + "SELECT id, key, content, category, importance, session_id, created_at, updated_at FROM memories WHERE key = ?", + ) + .bind(key) + .fetch_all(self.pool()) + .await?; + let mut entries = parse_memory_rows(&rows)?; + Ok(entries.pop()) + } + /// Store or update a memory entry (upsert by key). pub async fn upsert_memory(&self, entry: &MemoryEntry) -> Result<(), StorageError> { let category_str = entry.category.as_str();