Spaces:
Paused
Paused
| 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 | |
| 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 # Подключаем наш менеджер жизненного цикла | |
| ) | |
| # --- ЭНДПОИНТЫ --- | |
| async def home(): | |
| return {"status": "ok", "message": "Robust, non-streaming OpenAI proxy is working."} | |
| 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) |