diff --git a/mempalace/fleet_api.py b/mempalace/fleet_api.py index 8004f0b37..76b3c0beb 100644 --- a/mempalace/fleet_api.py +++ b/mempalace/fleet_api.py @@ -2,7 +2,7 @@ """ fleet_api.py — Lightweight HTTP API for the shared fleet palace. -Exposes fleet memory search over HTTP so that Alpha servers and other +Exposes fleet memory search and recording over HTTP so that Alpha servers and other wizard deployments can query the palace without direct filesystem access. Endpoints: @@ -16,6 +16,10 @@ Endpoints: GET /wings Returns {"wings": ["bezalel", ...]} — distinct wizard wings present + POST /record + Body: {"text": "...", "room": "...", "wing": "...", "source_file": "...", "metadata": {...}} + Returns {"success": true, "id": "..."} + Error responses use {"error": ""} with appropriate HTTP status codes. Usage: @@ -25,7 +29,7 @@ Usage: # Custom host/port/palace: FLEET_PALACE_PATH=/data/fleet python mempalace/fleet_api.py --host 0.0.0.0 --port 8080 -Refs: #1078, #1075 +Refs: #1078, #1075, #1085 """ from __future__ import annotations @@ -131,6 +135,52 @@ def _handle_wings(handler: BaseHTTPRequestHandler) -> None: _json_response(handler, 200, {"wings": wings}) +def _handle_record(handler: BaseHTTPRequestHandler) -> None: + """Handle POST /record to add a new memory.""" + content_length = int(handler.headers.get("Content-Length", 0)) + if not content_length: + _json_response(handler, 400, {"error": "Missing request body"}) + return + + try: + body = json.loads(handler.rfile.read(content_length)) + except json.JSONDecodeError: + _json_response(handler, 400, {"error": "Invalid JSON body"}) + return + + text = body.get("text", "").strip() + if not text: + _json_response(handler, 400, {"error": "Missing required field: text"}) + return + + room = body.get("room", "general") + wing = body.get("wing") + source_file = body.get("source_file", "") + metadata = body.get("metadata", {}) + + try: + from nexus.mempalace.searcher import add_memory, MemPalaceUnavailable + except ImportError as exc: + _json_response(handler, 503, {"error": f"MemPalace module not available: {exc}"}) + return + + try: + # Note: add_memory uses MEMPALACE_PATH by default. + # For fleet_api, we should probably use FLEET_PALACE_PATH. + palace_path = _get_palace_path() + doc_id = add_memory( + text=text, + room=room, + wing=wing, + palace_path=palace_path, + source_file=source_file, + extra_metadata=metadata + ) + _json_response(handler, 201, {"success": True, "id": doc_id}) + except Exception as exc: + _json_response(handler, 503, {"error": str(exc)}) + + class FleetAPIHandler(BaseHTTPRequestHandler): """Request handler for the fleet memory API.""" @@ -155,6 +205,18 @@ class FleetAPIHandler(BaseHTTPRequestHandler): "endpoints": ["/health", "/search", "/wings"], }) + def do_POST(self) -> None: # noqa: N802 + parsed = urlparse(self.path) + path = parsed.path.rstrip("/") or "/" + + if path == "/record": + _handle_record(self) + else: + _json_response(self, 404, { + "error": f"Unknown endpoint: {path}", + "endpoints": ["/record"], + }) + def make_server(host: str = DEFAULT_HOST, port: int = DEFAULT_PORT) -> HTTPServer: return HTTPServer((host, port), FleetAPIHandler)