"""Path-based reverse proxy for the HF Space. Forwards requests to two backend uvicorn workers: - ICD API on http://127.0.0.1:8000 (served at "/") - CPT API on http://127.0.0.1:8001 (served at "/cpt/*") Listens on 0.0.0.0:7860, which is the public port HF Spaces exposes. """ from __future__ import annotations import logging import os from aiohttp import ClientSession, ClientTimeout, web logger = logging.getLogger("proxy") logging.basicConfig( level=os.environ.get("PROXY_LOG_LEVEL", "INFO"), format="%(asctime)s %(levelname)s proxy: %(message)s", ) ICD_BACKEND = os.environ.get("ICD_BACKEND_URL", "http://127.0.0.1:8000") CPT_BACKEND = os.environ.get("CPT_BACKEND_URL", "http://127.0.0.1:8001") PROXY_PORT = int(os.environ.get("PROXY_PORT", "7860")) PROXY_HOST = os.environ.get("PROXY_HOST", "0.0.0.0") LANDING_HTML = """ Clinical Code Retrieval API

Clinical Code Retrieval API

Hybrid semantic + graph retrieval for ICD-10-CM and CPT/HCPCS.

ICD-10-CM API (at root path)

CPT/HCPCS API (at /cpt/*)

Diagnostics

""" async def _proxy_path(request: web.Request, backend: str, path_override: str | None = None) -> web.StreamResponse: """Forward ``request`` to ``backend``. If ``path_override`` is given, the forwarded URL uses that path (with the original query string preserved). Used to strip the "/cpt" prefix before forwarding to the CPT backend. """ if path_override is not None: target_url = f"{backend}{path_override}" if request.rel_url.query_string: target_url = f"{target_url}?{request.rel_url.query_string}" else: target_url = f"{backend}{request.rel_url}" headers = {k: v for k, v in request.headers.items() if k.lower() != "host"} body = await request.read() timeout = ClientTimeout(total=None, sock_connect=10, sock_read=600) async with ClientSession(timeout=timeout) as session: async with session.request( method=request.method, url=target_url, headers=headers, data=body, allow_redirects=False, ) as resp: payload = await resp.read() proxied_headers = dict(resp.headers) for h in ( "Content-Length", "Content-Encoding", "Transfer-Encoding", "Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "TE", "Trailer", "Upgrade", ): proxied_headers.pop(h, None) return web.Response( body=payload, status=resp.status, headers=proxied_headers, ) async def handle_icd(request: web.Request) -> web.StreamResponse: return await _proxy_path(request, ICD_BACKEND) async def handle_cpt(request: web.Request) -> web.StreamResponse: # Strip the "/cpt" prefix so the CPT app sees /retrieve, /docs, etc. stripped = request.match_info["path"] new_path = f"/{stripped}" if stripped else "/" return await _proxy_path(request, CPT_BACKEND, path_override=new_path) async def handle_landing(request: web.Request) -> web.Response: return web.Response(text=LANDING_HTML, content_type="text/html") async def handle_healthz(request: web.Request) -> web.Response: """Ping both backends and return a small JSON status.""" timeout = ClientTimeout(total=5) statuses: dict[str, str] = {} details: dict[str, str] = {} async with ClientSession(timeout=timeout) as session: for name, url in (("icd", ICD_BACKEND), ("cpt", CPT_BACKEND)): try: async with session.get(f"{url}/health") as resp: statuses[name] = "up" if resp.status == 200 else f"http_{resp.status}" details[name] = f"{url}/health -> {resp.status}" except Exception as exc: # noqa: BLE001 statuses[name] = "down" details[name] = f"{url}/health -> {type(exc).__name__}: {exc}" overall_ok = all(v == "up" for v in statuses.values()) return web.json_response( {"status": "ok" if overall_ok else "degraded", "backends": statuses, "details": details}, status=200 if overall_ok else 503, ) def build_app() -> web.Application: app = web.Application(client_max_size=64 * 1024 * 1024) # 64 MB request body app.router.add_get("/", handle_landing) app.router.add_get("/healthz", handle_healthz) async def handle_cpt_root(request: web.Request) -> web.StreamResponse: return await _proxy_path(request, CPT_BACKEND, path_override="/") # Bare /cpt prefix (no trailing slash) goes to the CPT root. app.router.add_route("*", "/cpt", handle_cpt_root) # Everything under /cpt/* goes to the CPT backend (prefix stripped). app.router.add_route("*", "/cpt/{path:.*}", handle_cpt) # Everything else goes to the ICD backend. app.router.add_route("*", "/{path:.*}", handle_icd) return app def main() -> None: logger.info("Starting proxy on %s:%s", PROXY_HOST, PROXY_PORT) logger.info(" ICD backend: %s (served at /)", ICD_BACKEND) logger.info(" CPT backend: %s (served at /cpt/*)", CPT_BACKEND) web.run_app(build_app(), host=PROXY_HOST, port=PROXY_PORT, access_log=None) if __name__ == "__main__": main()