"""openrouter-secrets: forward OpenAI-style chat requests to OpenRouter (stdlib only). The interesting part is what this file does NOT do: it never reads OPENROUTER_API_KEY and never sets an Authorization header. The request goes to http://openrouter.ai through m64's gate ($HTTP_PROXY); the gate adds the key, because capsule.toml asks it to in [[secrets.inject]], and sends it on over TLS. """ import json import os import urllib.error import urllib.request from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer PORT = int(os.environ["M64_PORT"]) # Plain http on purpose: the gate does TLS. https here would hide the request # from the gate, so it could not add the key. UPSTREAM = "http://openrouter.ai/api/v1/chat/completions" UPSTREAM_MODEL = "deepseek/deepseek-v4.1-flash" PUBLIC_MODEL = "deepseek-v4.1-flash" MAX_BODY = 8 * 1024 * 1024 # ProxyHandler() reads HTTP_PROXY from the environment, so every call goes via the gate. OPENER = urllib.request.build_opener(urllib.request.ProxyHandler()) class Handler(BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" def log_message(self, fmt, *args): if self.path != "/health": super().log_message(fmt, *args) def _json(self, status, obj): data = json.dumps(obj).encode() self.send_response(status) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) def do_GET(self): if self.path == "/health": return self._json(200, {"status": "ok"}) if self.path == "/v1/models": return self._json(200, {"object": "list", "data": [{"id": PUBLIC_MODEL, "object": "model", "owned_by": "openrouter"}]}) return self._json(404, {"error": "not found"}) def do_POST(self): if self.path != "/v1/chat/completions": return self._json(404, {"error": "not found"}) length = int(self.headers.get("Content-Length") or 0) if not 0 < length <= MAX_BODY: return self._json(400, {"error": "body missing or too large"}) try: body = json.loads(self.rfile.read(length)) except ValueError: return self._json(400, {"error": "body must be JSON"}) body["model"] = UPSTREAM_MODEL req = urllib.request.Request(UPSTREAM, data=json.dumps(body).encode(), method="POST", headers={"Content-Type": "application/json"}) # no Authorization: the gate adds it try: resp = OPENER.open(req, timeout=600) except urllib.error.HTTPError as e: resp = e # relay OpenRouter's own error (401 = no or bad key, 429 = rate limit) except (urllib.error.URLError, OSError) as e: return self._json(502, {"error": f"openrouter.ai unreachable ({type(e).__name__}); was the network granted?"}) with resp: # Relay the answer as it arrives, so "stream": true works chunk by chunk. self.send_response(resp.status if hasattr(resp, "status") else resp.code) self.send_header("Content-Type", resp.headers.get("Content-Type", "application/json")) self.send_header("Transfer-Encoding", "chunked") self.end_headers() try: while data := resp.read1(16384): self.wfile.write(b"%x\r\n%s\r\n" % (len(data), data)) self.wfile.flush() self.wfile.write(b"0\r\n\r\n") except (BrokenPipeError, ConnectionResetError): self.close_connection = True def main(): server = ThreadingHTTPServer(("0.0.0.0", PORT), Handler) server.daemon_threads = True with open(os.environ["M64_STATUS"], "a") as f: f.write(json.dumps({"phase": "ready", "detail": f"forwarding to {UPSTREAM_MODEL}"}) + "\n") server.serve_forever() if __name__ == "__main__": main()