import logging import os from fastapi import FastAPI, Request from fastapi.responses import JSONResponse, Response import httpx try: from dotenv import load_dotenv load_dotenv() except ImportError: pass logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI() UPSTREAM_URL = os.getenv( "LLM_UPSTREAM_URL", "https://llm.corp.septem.pro/v1/chat/completions", ).strip() OPENROUTER_KEY = os.getenv("OPENROUTER_API_KEY", "").strip() def _client_timeout() -> httpx.Timeout: total = float(os.getenv("LLM_PROXY_TIMEOUT_SECONDS", "120")) connect = float(os.getenv("LLM_PROXY_CONNECT_TIMEOUT", "15")) return httpx.Timeout(total, connect=connect) @app.post("/v1/chat/completions") async def proxy(req: Request): if not OPENROUTER_KEY: logger.error("OPENROUTER_API_KEY is not set (env or .env)") return JSONResponse( status_code=503, content={ "error": "Service unavailable", "details": "Set OPENROUTER_API_KEY for the LLM proxy (or add it to .env).", }, ) try: body = await req.json() except Exception as e: return JSONResponse( status_code=400, content={"error": "Invalid JSON", "details": str(e)}, ) verify = os.getenv("HTTPX_VERIFY", "true").lower() not in ("0", "false", "no") async with httpx.AsyncClient(verify=verify) as client: try: r = await client.post( UPSTREAM_URL, headers={ "Authorization": f"Bearer {OPENROUTER_KEY}", "HTTP-Referer": os.getenv("LLM_HTTP_REFERER", "http://localhost"), "X-Title": os.getenv("LLM_PROXY_X_TITLE", "rag-engine"), }, json=body, timeout=_client_timeout(), ) except httpx.RequestError as e: logger.error( "LLM upstream unreachable (%s): %s", UPSTREAM_URL, e, exc_info=True, ) return JSONResponse( status_code=502, content={ "error": "Bad gateway", "details": str(e), "upstream": UPSTREAM_URL, "hint": "Проверьте VPN, DNS, доступность хоста и переменную LLM_UPSTREAM_URL.", }, ) if r.status_code >= 500: logger.warning( "LLM upstream HTTP %s, body (prefix): %s", r.status_code, (r.text or "")[:800], ) return Response( content=r.content, status_code=r.status_code, media_type=r.headers.get("content-type", "application/json"), )