86 lines
2.7 KiB
Python
86 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Return deterministic OpenAI-compatible replies for Switchyard worker routes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import time
|
|
import uuid
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from typing import Any
|
|
|
|
from routing_catalog import resolve_worker_route
|
|
|
|
|
|
PORT = int(os.environ.get("HERMES_WORKER_ROUTE_BROKER_PORT", "9007"))
|
|
PREFIX = "worker/"
|
|
|
|
|
|
def _reply(model: str) -> dict[str, Any]:
|
|
"""Build the minimal response used only to expose Switchyard's decision."""
|
|
return {
|
|
"id": f"worker-route-{uuid.uuid4().hex}",
|
|
"object": "chat.completion",
|
|
"created": int(time.time()),
|
|
"model": model,
|
|
"choices": [
|
|
{
|
|
"index": 0,
|
|
"message": {"role": "assistant", "content": model},
|
|
"finish_reason": "stop",
|
|
}
|
|
],
|
|
"usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
|
|
}
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
"""Serve a local-only target sink after Switchyard selects a worker."""
|
|
|
|
protocol_version = "HTTP/1.1"
|
|
|
|
def _send(self, status: int, payload: dict[str, Any]) -> None:
|
|
body = json.dumps(payload, separators=(",", ":")).encode("utf-8")
|
|
self.send_response(status)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.send_header("Content-Length", str(len(body)))
|
|
self.end_headers()
|
|
self.wfile.write(body)
|
|
|
|
def do_GET(self) -> None: # noqa: N802
|
|
if self.path == "/health":
|
|
self._send(200, {"status": "ok"})
|
|
return
|
|
self._send(404, {"error": "not found"})
|
|
|
|
def do_POST(self) -> None: # noqa: N802
|
|
if self.path not in {"/v1/chat/completions", "/chat/completions"}:
|
|
self._send(404, {"error": "not found"})
|
|
return
|
|
try:
|
|
length = int(self.headers.get("Content-Length", "0"))
|
|
if length <= 0 or length > 1024 * 1024:
|
|
raise ValueError("invalid request length")
|
|
payload = json.loads(self.rfile.read(length))
|
|
model = str(payload.get("model") or "")
|
|
if not model.startswith(PREFIX):
|
|
raise ValueError("unsupported worker route")
|
|
model = resolve_worker_route(model)
|
|
except (ValueError, TypeError, json.JSONDecodeError) as exc:
|
|
self._send(400, {"error": str(exc)})
|
|
return
|
|
self._send(200, _reply(model))
|
|
|
|
def log_message(self, format: str, *args: Any) -> None:
|
|
print(f"worker-route-broker {self.address_string()} {format % args}", flush=True)
|
|
|
|
|
|
def main() -> None:
|
|
server = ThreadingHTTPServer(("0.0.0.0", PORT), Handler)
|
|
server.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|