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):
|