import os import httpx from fastapi import FastAPI, Request, Response from contextlib import asynccontextmanager # --- КОНФИГУРАЦИЯ --- OR_BASE_URL = "https://openrouter.ai/api" CLIENT_TIMEOUT = 280.0 # "Hop-by-hop" заголовки, которые не должны проксироваться HOP_BY_HOP_HEADERS = { "connection", "keep-alive", "proxy-authenticate", "proxy-authorization", "te", "trailers", "transfer-encoding", "upgrade", } # --- УПРАВЛЕНИЕ ЖИЗНЕННЫМ ЦИКЛОМ КЛИЕНТА --- # Это самый надежный способ управлять httpx.AsyncClient в FastAPI @asynccontextmanager async def lifespan(app: FastAPI): # Код, который выполняется при старте сервера async with httpx.AsyncClient(base_url=OR_BASE_URL, timeout=CLIENT_TIMEOUT) as client: print("INFO: HTTPX Client started.") # Передаем созданный клиент в состояние приложения app.state.http_client = client yield # Код, который выполняется при остановке сервера (клиент закрывается автоматически) print("INFO: HTTPX Client closed.") # --- ПРИЛОЖЕНИЕ FASTAPI --- app = FastAPI( title="Robust OpenAI Proxy", description="A non-streaming proxy with proper client lifecycle management.", lifespan=lifespan # Подключаем наш менеджер жизненного цикла ) # --- ЭНДПОИНТЫ --- @app.get("/") async def home(): return {"status": "ok", "message": "Robust, non-streaming OpenAI proxy is working."} @app.api_route("/{endpoint:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"]) async def proxy(endpoint: str, request: Request): try: url = httpx.URL(path=f"/{endpoint}", query=request.url.query.encode("utf-8")) headers = {k: v for k, v in request.headers.items() if k.lower() != "host"} body = await request.body() # Получаем клиент из состояния приложения, а не из глобальной переменной client: httpx.AsyncClient = request.app.state.http_client resp_openai = await client.request( method=request.method, url=url, headers=headers, content=body ) response_headers = { k: v for k, v in resp_openai.headers.items() if k.lower() not in HOP_BY_HOP_HEADERS } return Response( content=resp_openai.content, status_code=resp_openai.status_code, headers=response_headers ) except httpx.RequestError as e: # Эта ошибка все еще возможна, если OpenAI недоступен print(f"!!! HTTPX Request Error Caught: {e!r}") return Response(content=f"Could not connect to OpenAI API. Details: {e!r}", status_code=502) except Exception as e: # Эта ошибка ловит все остальное, что и вызывало 500 print(f"!!! Generic Error Caught: {e!r}") return Response(content=f"An internal proxy error occurred. Details: {e!r}", status_code=500)