File size: 4,811 Bytes
734b5b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from __future__ import annotations
import sys, os, threading, webbrowser, http.server, json
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from src.core.command_router import CommandRouter
from src.core.models import JobConfig, PDFOperation
from src.core.orchestrator import OfflinePDFOrchestrator


UI_HTML = Path(__file__).parent / "web" / "orchestrator.html"
PORT     = 18472
COMMAND_ROUTER = CommandRouter(project_root=Path(__file__).parent.parent.parent)


class _Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=str(UI_HTML.parent), **kw)
    def log_message(self, *_): pass

    def do_POST(self):
        if self.path == "/run":
            length  = int(self.headers.get("Content-Length", 0))
            body    = self.rfile.read(length)
            payload = json.loads(body)
            result  = _run_job(payload)
            resp    = json.dumps(result).encode()
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(resp)))
            self.send_header("Access-Control-Allow-Origin", "*")
            self.end_headers()
            self.wfile.write(resp)
        elif self.path == "/team-plan":
            length  = int(self.headers.get("Content-Length", 0))
            body    = self.rfile.read(length)
            payload = json.loads(body)
            result  = COMMAND_ROUTER.plan_command(payload)
            resp    = json.dumps(result).encode()
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(resp)))
            self.send_header("Access-Control-Allow-Origin", "*")
            self.end_headers()
            self.wfile.write(resp)
        else:
            self.send_response(404); self.end_headers()

    def do_GET(self):
        if self.path == "/team-config":
            result = {
                "success": True,
                "team": COMMAND_ROUTER.describe_team(),
                "available_pipelines": COMMAND_ROUTER.orchestrator.list_available_pipelines(),
            }
            resp = json.dumps(result).encode()
            self.send_response(200)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(resp)))
            self.send_header("Access-Control-Allow-Origin", "*")
            self.end_headers()
            self.wfile.write(resp)
            return
        super().do_GET()

    def do_OPTIONS(self):
        self.send_response(204)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()


def _run_job(payload: dict) -> dict:
    try:
        ops = []
        for item in payload.get("operations", []):
            ops.append(PDFOperation(**item))
        input_pdf  = Path(payload.get("input_pdf", ""))
        output_dir = Path(payload.get("output_dir", Path.home() / "pdf-output"))
        if not input_pdf.exists():
            return {"success": False, "error": f"Input PDF not found: {input_pdf}"}
        cfg = JobConfig(
            input_pdf    = input_pdf,
            output_dir   = output_dir,
            run_ocr      = payload.get("force_ocr", False),
            force_ocr    = payload.get("force_ocr", False),
            ocr_language = payload.get("ocr_language", "eng"),
            operations   = ops,
        )
        orc    = OfflinePDFOrchestrator(output_dir / "_jobs")
        result = orc.run(cfg)
        return {
            "success":   result.success,
            "job_id":    result.job_id,
            "final_pdf": str(result.final_pdf) if result.final_pdf else "",
            "log_file":  str(result.log_file),
            "stages":    result.stages,
            "errors":    result.errors,
        }
    except Exception as exc:
        return {"success": False, "error": str(exc)}


def _start_server():
    server = http.server.HTTPServer(("127.0.0.1", PORT), _Handler)
    server.serve_forever()


def launch():
    t = threading.Thread(target=_start_server, daemon=True)
    t.start()
    url = f"http://127.0.0.1:{PORT}/orchestrator.html"
    print(f"Opening PDF Orchestrator UI at {url}")
    webbrowser.open(url)

    # Keep process alive
    import time
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("Shutting down.")


if __name__ == "__main__":
    launch()