Update main.py
Browse files
main.py
CHANGED
@@ -8,6 +8,9 @@ import uuid
|
|
8 |
from typing import AsyncGenerator, Dict, List, Any
|
9 |
|
10 |
import aiohttp
|
|
|
|
|
|
|
11 |
from fastapi import FastAPI, Request, HTTPException
|
12 |
from fastapi.middleware.cors import CORSMiddleware
|
13 |
from fastapi.responses import StreamingResponse
|
@@ -30,14 +33,9 @@ logger = logging.getLogger("proxy")
|
|
30 |
# βββ Config βββ
|
31 |
BLACKBOX_URL = "https://www.blackbox.ai/api/chat"
|
32 |
REQUEST_TIMEOUT = 300
|
33 |
-
WORKER_COUNT = 10
|
34 |
|
35 |
# βββ Headers βββ
|
36 |
HEADERS = {
|
37 |
-
"authority": "www.blackbox.ai",
|
38 |
-
"method": "POST",
|
39 |
-
"path": "/api/chat",
|
40 |
-
"scheme": "https",
|
41 |
"accept": "*/*",
|
42 |
"accept-encoding": "gzip, deflate, br, zstd",
|
43 |
"accept-language": "en-US,en;q=0.9",
|
@@ -69,8 +67,6 @@ app.add_middleware(
|
|
69 |
)
|
70 |
|
71 |
HTTP_SESSION: aiohttp.ClientSession = None
|
72 |
-
REQUEST_QUEUE: asyncio.Queue = asyncio.Queue()
|
73 |
-
WORKER_TASKS: List[asyncio.Task] = []
|
74 |
|
75 |
class RetryableStatusError(Exception):
|
76 |
def __init__(self, status: int, text: str):
|
@@ -155,7 +151,7 @@ def log_retry(retry_state):
|
|
155 |
rid = retry_state.kwargs.get("request_id", "unknown")
|
156 |
attempt = retry_state.attempt_number
|
157 |
err = retry_state.outcome.exception()
|
158 |
-
logger.warning("[%s] retry %s/
|
159 |
|
160 |
@retry(
|
161 |
stop=stop_after_attempt(3),
|
@@ -168,20 +164,30 @@ def log_retry(retry_state):
|
|
168 |
async def get_blackbox_response(*, data, stream: bool, request_id: str) -> AsyncGenerator[str, None]:
|
169 |
global HTTP_SESSION
|
170 |
if not HTTP_SESSION:
|
|
|
|
|
|
|
|
|
|
|
171 |
HTTP_SESSION = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT))
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
|
|
|
|
|
|
|
|
|
|
185 |
|
186 |
@app.middleware("http")
|
187 |
async def add_request_id(request: Request, call_next):
|
|
|
8 |
from typing import AsyncGenerator, Dict, List, Any
|
9 |
|
10 |
import aiohttp
|
11 |
+
import brotli
|
12 |
+
import sys
|
13 |
+
import importlib
|
14 |
from fastapi import FastAPI, Request, HTTPException
|
15 |
from fastapi.middleware.cors import CORSMiddleware
|
16 |
from fastapi.responses import StreamingResponse
|
|
|
33 |
# βββ Config βββ
|
34 |
BLACKBOX_URL = "https://www.blackbox.ai/api/chat"
|
35 |
REQUEST_TIMEOUT = 300
|
|
|
36 |
|
37 |
# βββ Headers βββ
|
38 |
HEADERS = {
|
|
|
|
|
|
|
|
|
39 |
"accept": "*/*",
|
40 |
"accept-encoding": "gzip, deflate, br, zstd",
|
41 |
"accept-language": "en-US,en;q=0.9",
|
|
|
67 |
)
|
68 |
|
69 |
HTTP_SESSION: aiohttp.ClientSession = None
|
|
|
|
|
70 |
|
71 |
class RetryableStatusError(Exception):
|
72 |
def __init__(self, status: int, text: str):
|
|
|
151 |
rid = retry_state.kwargs.get("request_id", "unknown")
|
152 |
attempt = retry_state.attempt_number
|
153 |
err = retry_state.outcome.exception()
|
154 |
+
logger.warning("[%s] retry %s/3 due to %s", rid, attempt, err)
|
155 |
|
156 |
@retry(
|
157 |
stop=stop_after_attempt(3),
|
|
|
164 |
async def get_blackbox_response(*, data, stream: bool, request_id: str) -> AsyncGenerator[str, None]:
|
165 |
global HTTP_SESSION
|
166 |
if not HTTP_SESSION:
|
167 |
+
try:
|
168 |
+
importlib.import_module("brotli")
|
169 |
+
except ImportError:
|
170 |
+
logger.error("Missing Brotli module. Install with: pip install brotli")
|
171 |
+
raise HTTPException(status_code=502, detail="Missing Brotli module on server")
|
172 |
HTTP_SESSION = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT))
|
173 |
+
|
174 |
+
try:
|
175 |
+
async with HTTP_SESSION.post(BLACKBOX_URL, json=data, headers=HEADERS, timeout=REQUEST_TIMEOUT) as resp:
|
176 |
+
if resp.status != 200:
|
177 |
+
body = await resp.text()
|
178 |
+
logger.error("[%s] Upstream %s error: %s", request_id, BLACKBOX_URL, resp.status)
|
179 |
+
if resp.status in RETRYABLE_STATUSES:
|
180 |
+
raise RetryableStatusError(resp.status, body)
|
181 |
+
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status}")
|
182 |
+
if stream:
|
183 |
+
async for chunk in resp.content.iter_any():
|
184 |
+
if chunk:
|
185 |
+
yield chunk.decode("utf-8", "ignore")
|
186 |
+
else:
|
187 |
+
yield await resp.text()
|
188 |
+
except aiohttp.ContentEncodingError as e:
|
189 |
+
logger.error("[%s] Brotli decode failed: %s", request_id, e)
|
190 |
+
raise HTTPException(status_code=502, detail="Install Brotli for br-encoded content") from e
|
191 |
|
192 |
@app.middleware("http")
|
193 |
async def add_request_id(request: Request, call_next):
|