95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
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"),
|
|
)
|