Files
contractor/deploy/convert_server.py
T

144 lines
5.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Contracts VM server — thin HTTP router. All logic in db/ and services/."""
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
from urllib.parse import urlparse, parse_qs
import json, re
# ── DB auto-seed ──────────────────────────────────────────────────────────
from db import prompts as db_prompts
from db.connection import DB_CONFIG
db_prompts.seed_defaults()
# ── Services ──────────────────────────────────────────────────────────────
from services.upload import handle_upload
from services.unzip import handle_unzip
from services.process import run_pipeline
from llm_prompt import build_prompt
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads = True
class Handler(BaseHTTPRequestHandler):
def do_OPTIONS(self):
self.send_response(200)
self._send_cors()
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def do_GET(self):
parsed = urlparse(self.path)
if parsed.path == "/process-v2":
self._handle_process_v2(parsed)
elif parsed.path == "/health":
self._json({"ok": True, "db": DB_CONFIG["dbname"]})
else:
self.send_error(404)
def do_POST(self):
if self.path == "/upload":
self._handle_upload()
elif self.path == "/unzip-upload":
self._handle_unzip()
elif self.path == "/llm-ops":
self._handle_llm_ops()
else:
self.send_error(404)
# ── /process-v2 (SSE) ─────────────────────────────────────────────────
def _handle_process_v2(self, parsed):
params = parse_qs(parsed.query)
cid = params.get("contract_id", [None])[0]
if not cid:
self.send_error(400, "contract_id required")
return
if not re.fullmatch(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', cid, re.I):
self.send_error(400, "invalid contract_id format")
return
self.send_response(200)
self.send_header("Content-Type", "text/event-stream; charset=utf-8")
self.send_header("Cache-Control", "no-cache")
self.send_header("Connection", "keep-alive")
self.end_headers()
self.wfile.write(b": ok\n\n")
self.wfile.flush()
order_ids = params.get("order", [None])[0] or ""
try:
run_pipeline(cid, order_ids, self._sse, build_prompt)
except Exception as e:
self._sse({"type": "error", "message": str(e)})
def _sse(self, data):
self.wfile.write(f"data: {json.dumps(data, ensure_ascii=False)}\n\n".encode())
self.wfile.flush()
# ── /upload ───────────────────────────────────────────────────────────
def _handle_upload(self):
try:
content_type = self.headers.get("Content-Type", "")
content_length = int(self.headers.get("Content-Length", 0))
result = handle_upload(self.rfile, content_type, content_length)
self._json(result)
except Exception as e:
self._json({"ok": False, "error": str(e)}, 500)
# ── /unzip-upload ─────────────────────────────────────────────────────
def _handle_unzip(self):
try:
content_length = int(self.headers.get("Content-Length", 0))
result = handle_unzip(self.rfile, content_length)
self._json(result)
except Exception as e:
self._json({"ok": False, "error": str(e)}, 500)
# ── /llm-ops (debug) ──────────────────────────────────────────────────
def _handle_llm_ops(self):
from services.llm import call_llm
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length))
try:
result, prompt_id = call_llm(
body.get("current_spec", []),
body.get("doc_text", ""),
build_prompt,
)
self._json(result)
except Exception as e:
self._json({"error": str(e)}, 502)
# ── Helpers ───────────────────────────────────────────────────────────
def _json(self, data, status=200):
self.send_response(status)
self._send_cors()
self.send_header("Content-Type", "application/json; charset=utf-8")
self.end_headers()
self.wfile.write(json.dumps(data, ensure_ascii=False).encode())
def _send_cors(self):
self.send_header("Access-Control-Allow-Origin", "*")
def log_message(self, format, *args):
pass
if __name__ == "__main__":
import os
port = int(os.environ.get("PORT", "8766"))
server = ThreadingHTTPServer(("0.0.0.0", port), Handler)
print(f"Contracts VM server on :{port}, db={DB_CONFIG['dbname']}")
try:
server.serve_forever()
except KeyboardInterrupt:
server.shutdown()