diff --git a/crates/uteke-cli/src/cli.rs b/crates/uteke-cli/src/cli.rs index fa5915b..373c797 100644 --- a/crates/uteke-cli/src/cli.rs +++ b/crates/uteke-cli/src/cli.rs @@ -691,7 +691,7 @@ pub enum RoomCommands { /// Room ID room_id: String, }, - /// Generate a structured document from room memories + /// Generate a structured document from room memories (API: POST /room/summary) Document { /// Room ID room_id: String, diff --git a/crates/uteke-cli/src/commands/room.rs b/crates/uteke-cli/src/commands/room.rs index 92b30b3..af7fc65 100644 --- a/crates/uteke-cli/src/commands/room.rs +++ b/crates/uteke-cli/src/commands/room.rs @@ -236,8 +236,8 @@ pub(crate) fn run( RoomCommands::Document { room_id } => { let doc = uteke - .room_document(room_id) - .map_err(|e| format!("Failed to generate room document: {e}"))? + .room_summary_document(room_id) + .map_err(|e| format!("Failed to generate room summary document: {e}"))? .ok_or_else(|| format!("Room not found: {room_id}"))?; if cli.json { diff --git a/crates/uteke-cli/src/output.rs b/crates/uteke-cli/src/output.rs index ac8d6bb..d2c7a93 100644 --- a/crates/uteke-cli/src/output.rs +++ b/crates/uteke-cli/src/output.rs @@ -304,7 +304,7 @@ pub(crate) fn print_aging_preview_human(memories: &[uteke_core::Memory]) { } } -/// Print a room document in human-readable format. +/// Print a room summary document in human-readable format. pub(crate) fn print_room_document_human(doc: &uteke_core::RoomDocument) { // Header let title = doc.title.as_deref().unwrap_or(&doc.room_id); diff --git a/crates/uteke-core/src/memory/rooms.rs b/crates/uteke-core/src/memory/rooms.rs index fdf951f..531c69d 100644 --- a/crates/uteke-core/src/memory/rooms.rs +++ b/crates/uteke-core/src/memory/rooms.rs @@ -745,7 +745,7 @@ impl super::Store { /// Returns `None` if the room does not exist. /// Sections: pinned first, then grouped by type (decision, fact, procedure, preference, context). /// Empty sections are omitted. - pub fn room_document(&self, room_id: &str) -> Result, Error> { + pub fn room_summary_document(&self, room_id: &str) -> Result, Error> { let room = match self.get_room(room_id)? { Some(r) => r, None => return Ok(None), @@ -1493,22 +1493,22 @@ mod tests { assert!(summary.is_none()); } - // ── room_document ─────────────────────────────────────────── + // ── room_summary_document ─────────────────────────────────────────── #[test] - fn room_document_empty_room() { + fn room_summary_document_empty_room() { let store = Store::open(":memory:").unwrap(); store .create_room("doc-empty", Some("Doc Empty"), "default") .unwrap(); - let doc = store.room_document("doc-empty").unwrap().unwrap(); + let doc = store.room_summary_document("doc-empty").unwrap().unwrap(); assert_eq!(doc.room_id, "doc-empty"); assert!(doc.sections.is_empty()); } #[test] - fn room_document_with_memories() { + fn room_summary_document_with_memories() { let store = Store::open(":memory:").unwrap(); store .create_room("doc-room", Some("Doc Room"), "default") @@ -1533,7 +1533,7 @@ mod tests { .link_memory_to_room("doc-room", "mem-doc3", "alice", "lead") .unwrap(); - let doc = store.room_document("doc-room").unwrap().unwrap(); + let doc = store.room_summary_document("doc-room").unwrap().unwrap(); assert_eq!(doc.room_id, "doc-room"); assert!(doc.sections.len() >= 2); @@ -1543,9 +1543,9 @@ mod tests { } #[test] - fn room_document_nonexistent_returns_none() { + fn room_summary_document_nonexistent_returns_none() { let store = Store::open(":memory:").unwrap(); - let doc = store.room_document("nope").unwrap(); + let doc = store.room_summary_document("nope").unwrap(); assert!(doc.is_none()); } diff --git a/crates/uteke-core/src/rooms.rs b/crates/uteke-core/src/rooms.rs index f298601..31a7bce 100644 --- a/crates/uteke-core/src/rooms.rs +++ b/crates/uteke-core/src/rooms.rs @@ -145,9 +145,10 @@ impl crate::Uteke { self.store.room_summary_with_docs(room_id) } - /// Generate a structured document from room memories. - pub fn room_document(&self, room_id: &str) -> Result, Error> { - self.store.room_document(room_id) + /// Generate a structured summary document from room memories. + /// API endpoint: POST /room/summary (#735) + pub fn room_summary_document(&self, room_id: &str) -> Result, Error> { + self.store.room_summary_document(room_id) } // ── Room ↔ Document junction (v15, #689) ───────────────────────────── @@ -275,7 +276,7 @@ mod tests { .unwrap(); // Empty room → no sections - let doc = uteke.room_document("doc-room").unwrap().unwrap(); + let doc = uteke.room_summary_document("doc-room").unwrap().unwrap(); assert!(doc.sections.is_empty()); } @@ -287,14 +288,14 @@ mod tests { .remember_in_room("Some fact", &[], None, None, "fact", "doc-room", "bob") .unwrap(); - let doc = uteke.room_document("doc-room").unwrap().unwrap(); + let doc = uteke.room_summary_document("doc-room").unwrap().unwrap(); assert_eq!(doc.room_id, "doc-room"); assert_eq!(doc.sections.len(), 1); // Research & Facts } #[test] fn room_document_nonexistent_returns_none() { let uteke = open_in_memory(); - assert!(uteke.room_document("nope").unwrap().is_none()); + assert!(uteke.room_summary_document("nope").unwrap().is_none()); } // ── Room ↔ Document junction ────────────────────────────────── diff --git a/crates/uteke-mcp/src/lib.rs b/crates/uteke-mcp/src/lib.rs index 410e9b7..2e340dd 100644 --- a/crates/uteke-mcp/src/lib.rs +++ b/crates/uteke-mcp/src/lib.rs @@ -1558,7 +1558,7 @@ fn exec_room_document(uteke: &Uteke, args: &Value) -> Result let room_id = args["room_id"].as_str().ok_or("Missing 'room_id'")?; let doc = uteke - .room_document(room_id) + .room_summary_document(room_id) .map_err(|e| format!("Failed: {e}"))?; let doc = match doc { diff --git a/crates/uteke-server/src/handlers.rs b/crates/uteke-server/src/handlers.rs index bd869ab..2baab8d 100644 --- a/crates/uteke-server/src/handlers.rs +++ b/crates/uteke-server/src/handlers.rs @@ -9,7 +9,7 @@ use std::sync::Mutex; use serde::Deserialize; use tiny_http::{Header, Method, Request, Response, StatusCode}; -use tracing::error; +use tracing::{error, warn}; use uteke_core::Uteke; @@ -57,7 +57,7 @@ pub fn route(uteke: &Mutex, ctx: &ReqCtx, req: &mut Request) -> Response< "/stats", "/room/recall", "/room/summary", - "/room/document", + "/room/summary-document", "/room/stats", "/room/document/list", "/doc/get", @@ -646,14 +646,38 @@ pub fn route(uteke: &Mutex, ctx: &ReqCtx, req: &mut Request) -> Response< } } - // ── Room Document ──────────────────────────────────────────────── + // ── Room Summary Document ──────────────────────────────────────── + (Method::Post, "/room/summary-document") => { + #[derive(Deserialize)] + struct RoomSummaryDocumentRequest { + room_id: String, + } + match read_body::(req.as_reader()) { + Ok(req_data) => match uteke.room_summary_document(&req_data.room_id) { + Ok(Some(doc)) => ctx.ok_response_for(req, &doc), + Ok(None) => ctx.error_response_for( + req, + 404, + format!("Room not found: {}", req_data.room_id), + ), + Err(e) => { + error!("Internal error: {e}"); + ctx.error_response_for(req, 500, "Internal server error") + } + }, + Err(e) => ctx.error_response_for(req, 400, e), + } + } + + // ── DEPRECATED: POST /room/document → /room/summary-document (#735) (Method::Post, "/room/document") => { + warn!("DEPRECATED: POST /room/document is renamed to POST /room/summary-document (see #735)"); #[derive(Deserialize)] struct RoomDocumentRequest { room_id: String, } match read_body::(req.as_reader()) { - Ok(req_data) => match uteke.room_document(&req_data.room_id) { + Ok(req_data) => match uteke.room_summary_document(&req_data.room_id) { Ok(Some(doc)) => ctx.ok_response_for(req, &doc), Ok(None) => ctx.error_response_for( req, diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 78807ff..61ff9ed 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -730,7 +730,7 @@ When `[server] enabled = true` is set in config, the CLI auto-routes commands th | GET | `/room/list` | List rooms (supports `?namespace=`) | | POST | `/room/recall` | Recall from a room | | POST | `/room/summary` | Room summary | -| POST | `/room/document` | Generate document from room | +| POST | `/room/summary-document` | Generate summary document from room (#735) | | POST | `/room/stats` | Room statistics | | DELETE | `/room/delete` | Delete a room | | POST | `/room/document/list` | List documents linked to a room (#689) | diff --git a/docs/docker.md b/docs/docker.md index ed54f4b..fab8474 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -265,7 +265,7 @@ Both transports expose the same tools (MCP protocol version `2025-06-18`): | `uteke_room_delete` | Delete a room | | `uteke_room_stats` | Room statistics | | `uteke_room_summary` | Room topic summary (tag clustering, no LLM) | -| `uteke_room_document` | Generate structured document from room | +| `uteke_room_document` | Generate summary document from room (→ `POST /room/summary-document`) | | `uteke_tags_list` | List all tags with counts (#566) | | `uteke_tags_rename` | Rename a tag across all memories (#566) | | `uteke_tags_delete` | Delete a tag from all memories (#566) | diff --git a/docs/mcp.md b/docs/mcp.md index 5979cba..dce2be0 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -160,7 +160,7 @@ Both transports expose the same 29 tools (MCP protocol version `2025-06-18`): | `uteke_room_delete` | Delete a room | | `uteke_room_stats` | Room statistics | | `uteke_room_summary` | Room topic summary (tag clustering, no LLM) | -| `uteke_room_document` | Generate structured document from room | +| `uteke_room_document` | Generate summary document from room (→ `POST /room/summary-document`) | | `uteke_tags_list` | List all tags with counts (#566) | | `uteke_tags_rename` | Rename a tag across all memories (#566) | | `uteke_tags_delete` | Delete a tag from all memories (#566) | diff --git a/docs/rooms.md b/docs/rooms.md index c38c3a5..08b4e97 100644 --- a/docs/rooms.md +++ b/docs/rooms.md @@ -28,6 +28,8 @@ uteke room recall "project-kickoff" --query "database decision" Combine room memories into a cohesive document: +> **Note:** The CLI command remains `uteke room document`, but the underlying HTTP API route has been renamed from `POST /room/document` to `POST /room/summary-document`. + ```bash uteke room document "project-kickoff" ```