File size: 3,314 Bytes
03c899c
c6769df
 
5c4cc48
f4cda89
5c4cc48
36c9cd4
5c4cc48
f4cda89
5c4cc48
c6769df
 
 
 
f4cda89
5c4cc48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c6769df
 
5c4cc48
f4cda89
c6769df
 
f4cda89
c6769df
 
 
 
5c4cc48
 
 
c6769df
f4cda89
 
 
c6769df
f4cda89
 
c6769df
 
 
 
f4cda89
c6769df
 
 
 
 
 
5c4cc48
 
 
f4cda89
5c4cc48
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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)