#!/usr/bin/env python3 """人审工作台:只读 muse.db,写面仅 reviews / revisions / adopt。""" from __future__ import annotations import json import sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from urllib.parse import parse_qs, urlparse PROJECT_ROOT = next( parent for parent in (Path(__file__).resolve().parent, *Path(__file__).resolve().parents) if (parent / "AGENTS.md").is_file() and (parent / ".git").exists() ) if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from muse.flow.adopt import adopt_candidate # noqa: E402 from muse.store import add_review, connect, default_db_path # noqa: E402 WRITE_ACTIONS = frozenset({"reviews", "revisions", "adopt"}) def _db(): return connect(default_db_path()) def queue_payload() -> list[dict]: with _db() as conn: rows = conn.execute( "SELECT id, kind, created_at, output_text FROM runs ORDER BY created_at DESC LIMIT 50" ).fetchall() return [dict(row) for row in rows] def adopt(run_id: str, reviewer: str, reason: str) -> int: """采纳必须调用 muse.flow,不在 web 层直接写 reviews。""" return adopt_candidate(run_id, reviewer, reason) def revise(run_id: str, reviewer: str, reason: str, before_text: str, after_text: str) -> int: return add_review( run_id=run_id, target="candidate", action="revise", reviewer=reviewer, reason=reason, before_text=before_text, after_text=after_text, ) class Handler(BaseHTTPRequestHandler): def _send(self, code: int, body: str, content_type: str = "text/html; charset=utf-8") -> None: data = body.encode("utf-8") self.send_response(code) self.send_header("Content-Type", content_type) self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) def do_GET(self) -> None: # noqa: N802 parsed = urlparse(self.path) if parsed.path in {"/", "/queue"}: items = queue_payload() rows = "".join( f"
  • {item['id']} {item['kind']}
  • " for item in items ) self._send(200, f"

    待审队列

    ") return if parsed.path.startswith("/review/"): run_id = parsed.path.rsplit("/", 1)[-1] self._send(200, f"

    审查 {run_id}

    ") return self._send(404, "not found") def do_POST(self) -> None: # noqa: N802 parsed = urlparse(self.path) length = int(self.headers.get("Content-Length") or 0) raw = self.rfile.read(length).decode("utf-8") if length else "" form = {k: v[0] for k, v in parse_qs(raw).items()} action = parsed.path.strip("/") if action not in WRITE_ACTIONS: self._send(404, "unknown write") return reviewer = form.get("reviewer") or "" run_id = form.get("run_id") or "" reason = form.get("reason") or "" if action == "adopt": review_id = adopt(run_id, reviewer, reason) elif action == "revisions" or (action == "reviews" and form.get("action") == "revise"): review_id = revise( run_id, reviewer, reason, form.get("before_text") or "", form.get("after_text") or "", ) else: review_id = add_review( run_id=run_id, target=form.get("target") or "candidate", action=form.get("action") or "reject", reviewer=reviewer, reason=reason, ) self._send(200, json.dumps({"reviewId": review_id}), "application/json") def log_message(self, fmt: str, *args) -> None: return def serve(host: str = "127.0.0.1", port: int = 8770) -> None: ThreadingHTTPServer((host, port), Handler).serve_forever() if __name__ == "__main__": serve()