Rooni commited on
Commit
c6769df
·
verified ·
1 Parent(s): 64ef914

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +18 -0
  2. app.py +50 -26
  3. requirements.txt +3 -2
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Указываем базовый образ Python
2
+ FROM python:3.9-slim
3
+
4
+ # Устанавливаем рабочую директорию внутри контейнера
5
+ WORKDIR /code
6
+
7
+ # Копируем файл с зависимостями
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ # Устанавливаем зависимости
11
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+
13
+ # Копируем остальной код приложения
14
+ COPY ./ /code/
15
+
16
+ # Указываем команду для запуска приложения
17
+ # Она будет исполнена, когда контейнер запустится
18
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py CHANGED
@@ -1,43 +1,67 @@
1
  import os
2
- import requests
3
- from flask import Flask, request, Response
4
 
5
- app = Flask(__name__)
 
 
 
6
 
7
- OR_BASE_URL = "https://openrouter.ai/api"
8
 
 
 
 
9
 
10
- @app.route("/", methods=["GET"])
11
- def home():
12
- return {"status": "ok", "message": "OpenRouter proxy is working :3"}
 
 
 
13
 
 
 
 
14
 
15
- @app.route("/<path:endpoint>", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
16
- def proxy(endpoint):
17
- url = f"{OR_BASE_URL}/{endpoint}"
18
-
19
- # Копируем все заголовки, кроме host и content-length
20
- headers = {k: v for k, v in request.headers if k.lower() not in ["host", "content-length"]}
21
 
 
 
22
  try:
23
- resp = requests.request(
 
 
 
 
 
 
 
 
 
24
  method=request.method,
25
  url=url,
26
  headers=headers,
27
- params=request.args,
28
- data=request.get_data(),
29
- allow_redirects=False,
30
- stream=True,
31
  )
32
 
33
- excluded_headers = ["content-encoding", "transfer-encoding", "connection"]
34
- response_headers = [(k, v) for k, v in resp.raw.headers.items() if k.lower() not in excluded_headers]
 
 
 
 
35
 
36
- return Response(resp.content, resp.status_code, response_headers)
 
 
 
 
 
 
37
 
 
 
 
38
  except Exception as e:
39
- return {"error": str(e)}, 500
40
-
41
-
42
- if __name__ == "__main__":
43
- app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
 
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://api.openai.com/v1"
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,
43
  headers=headers,
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)
 
 
 
requirements.txt CHANGED
@@ -1,2 +1,3 @@
1
- flask
2
- requests
 
 
1
+ fastapi
2
+ uvicorn
3
+ httpx