Rooni commited on
Commit
5c4cc48
·
verified ·
1 Parent(s): 36c9cd4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -27
app.py CHANGED
@@ -1,42 +1,53 @@
1
  import os
2
  import httpx
3
  from fastapi import FastAPI, Request, Response
 
4
 
5
- app = FastAPI(
6
- title="Simple OpenAI Proxy",
7
- description="A non-streaming proxy that waits for the full response from OpenAI."
8
- )
9
-
10
  OR_BASE_URL = "https://openrouter.ai/api"
 
11
 
12
- # Создаем один асинхронный клиент на все приложение
13
- # Увеличим таймаут по умолчанию, т.к. генерация может быть долгой
14
- client = httpx.AsyncClient(base_url=OR_BASE_URL, timeout=120.0)
15
-
16
- # "Hop-by-hop" заголовки, которые не должны проксироваться.
17
- # Они относятся к конкретному соединению, а не к сообщению в целом.
18
  HOP_BY_HOP_HEADERS = {
19
  "connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
20
  "te", "trailers", "transfer-encoding", "upgrade",
21
  }
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  @app.get("/")
24
  async def home():
25
- return {"status": "ok", "message": "Simple, non-streaming OpenAI proxy is working."}
26
-
27
 
28
  @app.api_route("/{endpoint:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
29
  async def proxy(endpoint: str, request: Request):
30
  try:
31
- # 1. Формируем URL для запроса к OpenAI
32
  url = httpx.URL(path=f"/{endpoint}", query=request.url.query.encode("utf-8"))
33
-
34
- # 2. Копируем заголовки и тело из входящего запроса
35
  headers = {k: v for k, v in request.headers.items() if k.lower() != "host"}
36
  body = await request.body()
37
 
38
- # 3. Отправляем запрос и ЖДЕМ ПОЛНОГО ОТВЕТА
39
- # Никакого stream=True. Мы ждем, пока `httpx` полностью загрузит ответ.
 
40
  resp_openai = await client.request(
41
  method=request.method,
42
  url=url,
@@ -44,24 +55,21 @@ async def proxy(endpoint: str, request: Request):
44
  content=body
45
  )
46
 
47
- # 4. Фильтруем "hop-by-hop" заголовки из ответа OpenAI
48
- # Все остальные заголовки (Content-Type, openai-*, и т.д.) будут сохранены
49
  response_headers = {
50
  k: v for k, v in resp_openai.headers.items()
51
  if k.lower() not in HOP_BY_HOP_HEADERS
52
  }
53
 
54
- # 5. Возвращаем обычный `Response` со всеми данными от OpenAI
55
- # Это не стриминг. Мы возвращаем готовый, полный ответ.
56
  return Response(
57
  content=resp_openai.content,
58
  status_code=resp_openai.status_code,
59
  headers=response_headers
60
  )
61
-
62
  except httpx.RequestError as e:
63
- # Ошибка сети при обращении к OpenAI
64
- return Response(content=f"Error connecting to OpenAI API: {e}", status_code=502) # 502 Bad Gateway
 
65
  except Exception as e:
66
- # Внутренняя ошибка в самом прокси-сервере
67
- return Response(content=f"Internal proxy error: {e}", status_code=500)
 
 
1
  import os
2
  import httpx
3
  from fastapi import FastAPI, Request, Response
4
+ from contextlib import asynccontextmanager
5
 
6
+ # --- КОНФИГУРАЦИЯ ---
 
 
 
 
7
  OR_BASE_URL = "https://openrouter.ai/api"
8
+ CLIENT_TIMEOUT = 280.0
9
 
10
+ # "Hop-by-hop" заголовки, которые не должны проксироваться
 
 
 
 
 
11
  HOP_BY_HOP_HEADERS = {
12
  "connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
13
  "te", "trailers", "transfer-encoding", "upgrade",
14
  }
15
 
16
+ # --- УПРАВЛЕНИЕ ЖИЗНЕННЫМ ЦИКЛОМ КЛИЕНТА ---
17
+ # Это самый надежный способ управлять httpx.AsyncClient в FastAPI
18
+ @asynccontextmanager
19
+ async def lifespan(app: FastAPI):
20
+ # Код, который выполняется при старте сервера
21
+ async with httpx.AsyncClient(base_url=OR_BASE_URL, timeout=CLIENT_TIMEOUT) as client:
22
+ print("INFO: HTTPX Client started.")
23
+ # Передаем созданный клиент в состояние приложения
24
+ app.state.http_client = client
25
+ yield
26
+ # Код, который выполняется при остановке сервера (клиент закрывается автоматически)
27
+ print("INFO: HTTPX Client closed.")
28
+
29
+ # --- ПРИЛОЖЕНИЕ FASTAPI ---
30
+ app = FastAPI(
31
+ title="Robust OpenAI Proxy",
32
+ description="A non-streaming proxy with proper client lifecycle management.",
33
+ lifespan=lifespan # Подключаем наш менеджер жизненного цикла
34
+ )
35
+
36
+ # --- ЭНДПОИНТЫ ---
37
  @app.get("/")
38
  async def home():
39
+ return {"status": "ok", "message": "Robust, non-streaming OpenAI proxy is working."}
 
40
 
41
  @app.api_route("/{endpoint:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
42
  async def proxy(endpoint: str, request: Request):
43
  try:
 
44
  url = httpx.URL(path=f"/{endpoint}", query=request.url.query.encode("utf-8"))
 
 
45
  headers = {k: v for k, v in request.headers.items() if k.lower() != "host"}
46
  body = await request.body()
47
 
48
+ # Получаем клиент из состояния приложения, а не из глобальной переменной
49
+ client: httpx.AsyncClient = request.app.state.http_client
50
+
51
  resp_openai = await client.request(
52
  method=request.method,
53
  url=url,
 
55
  content=body
56
  )
57
 
 
 
58
  response_headers = {
59
  k: v for k, v in resp_openai.headers.items()
60
  if k.lower() not in HOP_BY_HOP_HEADERS
61
  }
62
 
 
 
63
  return Response(
64
  content=resp_openai.content,
65
  status_code=resp_openai.status_code,
66
  headers=response_headers
67
  )
 
68
  except httpx.RequestError as e:
69
+ # Эта ошибка все еще возможна, если OpenAI недоступен
70
+ print(f"!!! HTTPX Request Error Caught: {e!r}")
71
+ return Response(content=f"Could not connect to OpenAI API. Details: {e!r}", status_code=502)
72
  except Exception as e:
73
+ # Эта ошибка ловит все остальное, что и вызывало 500
74
+ print(f"!!! Generic Error Caught: {e!r}")
75
+ return Response(content=f"An internal proxy error occurred. Details: {e!r}", status_code=500)