File size: 3,314 Bytes
03c899c
6ea66c7
9a65bf8
9940432
f4cda89
9940432
9e23a59
4f151a3
8a272dd
8d2b37c
8a272dd
ebfb44a
 
8a272dd
 
9940432
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ea66c7
 
9940432
f4cda89
6ea66c7
 
f4cda89
8a272dd
 
 
 
9940432
 
 
9a65bf8
f4cda89
8a272dd
f4cda89
8a272dd
f4cda89
9a65bf8
 
 
 
 
 
 
 
 
 
6ea66c7
 
9940432
845a5a8
9940432
f4cda89
9940432
845a5a8
9940432
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://api.openai.com/v1"
CLIENT_TIMEOUT = 400.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)