Update main.py
Browse files
main.py
CHANGED
|
@@ -25,8 +25,6 @@ logger = logging.getLogger("proxy")
|
|
| 25 |
# βββ Config βββ
|
| 26 |
BLACKBOX_URL = "https://www.blackbox.ai/api/chat"
|
| 27 |
REQUEST_TIMEOUT = 300
|
| 28 |
-
|
| 29 |
-
# βββ Headers βββ
|
| 30 |
HEADERS = {
|
| 31 |
"authority": "www.blackbox.ai",
|
| 32 |
"method": "POST",
|
|
@@ -44,41 +42,27 @@ HEADERS = {
|
|
| 44 |
"sec-fetch-dest": "empty",
|
| 45 |
"sec-fetch-mode": "cors",
|
| 46 |
"sec-fetch-site": "same-origin",
|
| 47 |
-
"user-agent": (
|
| 48 |
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
| 49 |
-
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
| 50 |
-
"Chrome/136.0.0.0 Safari/537.36"
|
| 51 |
-
),
|
| 52 |
"content-type": "application/json",
|
| 53 |
}
|
| 54 |
|
| 55 |
# βββ FastAPI βββ
|
| 56 |
app = FastAPI()
|
| 57 |
-
app.add_middleware(
|
| 58 |
-
CORSMiddleware,
|
| 59 |
-
allow_origins=["*"],
|
| 60 |
-
allow_credentials=True,
|
| 61 |
-
allow_methods=["*"],
|
| 62 |
-
allow_headers=["*"],
|
| 63 |
-
)
|
| 64 |
-
|
| 65 |
HTTP_SESSION: aiohttp.ClientSession = None
|
| 66 |
RETRYABLE_STATUSES = {400, 429, 500, 502, 503, 504}
|
| 67 |
_ascii = string.ascii_letters + string.digits
|
| 68 |
|
| 69 |
-
# βββ Helpers βββ
|
| 70 |
def _rand(n, pool=_ascii): return ''.join(random.choice(pool) for _ in range(n))
|
| 71 |
def random_email(): return _rand(12) + "@gmail.com"
|
| 72 |
def random_id(): return _rand(21, string.digits)
|
| 73 |
def random_customer_id(): return "cus_" + _rand(12)
|
| 74 |
def generate_7char_id(): return _rand(7)
|
| 75 |
|
| 76 |
-
# βββ Payload Builder βββ
|
| 77 |
def build_payload(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 78 |
msg_id = generate_7char_id()
|
| 79 |
if messages:
|
| 80 |
messages[-1]["id"] = msg_id
|
| 81 |
-
|
| 82 |
now = int(time.time())
|
| 83 |
return {
|
| 84 |
"messages": messages,
|
|
@@ -109,13 +93,7 @@ def build_payload(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
| 109 |
"domains": None,
|
| 110 |
"vscodeClient": False,
|
| 111 |
"codeInterpreterMode": False,
|
| 112 |
-
"customProfile": {
|
| 113 |
-
"name": "",
|
| 114 |
-
"occupation": "",
|
| 115 |
-
"traits": [],
|
| 116 |
-
"additionalInfo": "",
|
| 117 |
-
"enableNewChats": False
|
| 118 |
-
},
|
| 119 |
"session": {
|
| 120 |
"user": {
|
| 121 |
"name": _rand(10),
|
|
@@ -124,14 +102,14 @@ def build_payload(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
| 124 |
"id": random_id()
|
| 125 |
},
|
| 126 |
"expires": "2025-06-09T19:36:08.220Z",
|
| 127 |
-
"isNewUser":
|
| 128 |
},
|
| 129 |
"isPremium": True,
|
| 130 |
"subscriptionCache": {
|
| 131 |
"status": "PREMIUM",
|
| 132 |
"customerId": random_customer_id(),
|
| 133 |
-
"expiryTimestamp": now + 60 * 86400,
|
| 134 |
-
"lastChecked": int(time.time() * 1000),
|
| 135 |
"isTrialSubscription": False
|
| 136 |
},
|
| 137 |
"beastMode": False,
|
|
@@ -139,7 +117,6 @@ def build_payload(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
| 139 |
"designerMode": False
|
| 140 |
}
|
| 141 |
|
| 142 |
-
# βββ Retry Logic βββ
|
| 143 |
class RetryableStatusError(Exception):
|
| 144 |
def __init__(self, status: int, text: str):
|
| 145 |
super().__init__(f"status={status} body={text[:100]}...")
|
|
@@ -148,19 +125,13 @@ def log_retry(retry_state):
|
|
| 148 |
rid = retry_state.kwargs.get("request_id", "unknown")
|
| 149 |
logger.warning("[%s] retry %s/3 due to %s", rid, retry_state.attempt_number, retry_state.outcome.exception())
|
| 150 |
|
| 151 |
-
@retry(
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
retry=retry_if_exception_type(
|
| 155 |
-
(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError, RetryableStatusError)
|
| 156 |
-
),
|
| 157 |
-
before_sleep=log_retry
|
| 158 |
-
)
|
| 159 |
async def get_blackbox_response(*, data, stream: bool, request_id: str) -> AsyncGenerator[str, None]:
|
| 160 |
global HTTP_SESSION
|
| 161 |
if not HTTP_SESSION:
|
| 162 |
HTTP_SESSION = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT))
|
| 163 |
-
|
| 164 |
async with HTTP_SESSION.post(BLACKBOX_URL, json=data, headers=HEADERS, timeout=REQUEST_TIMEOUT) as resp:
|
| 165 |
if resp.status != 200:
|
| 166 |
body = await resp.text()
|
|
@@ -168,7 +139,6 @@ async def get_blackbox_response(*, data, stream: bool, request_id: str) -> Async
|
|
| 168 |
if resp.status in RETRYABLE_STATUSES:
|
| 169 |
raise RetryableStatusError(resp.status, body)
|
| 170 |
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status}")
|
| 171 |
-
|
| 172 |
if stream:
|
| 173 |
async for chunk in resp.content.iter_any():
|
| 174 |
if chunk:
|
|
@@ -176,7 +146,6 @@ async def get_blackbox_response(*, data, stream: bool, request_id: str) -> Async
|
|
| 176 |
else:
|
| 177 |
yield await resp.text()
|
| 178 |
|
| 179 |
-
# βββ Middleware βββ
|
| 180 |
@app.middleware("http")
|
| 181 |
async def add_request_id(request: Request, call_next):
|
| 182 |
request.state.request_id = rid = str(uuid.uuid4())
|
|
@@ -186,7 +155,6 @@ async def add_request_id(request: Request, call_next):
|
|
| 186 |
logger.info("[%s] finished in %.2fs", rid, time.perf_counter() - start)
|
| 187 |
return resp
|
| 188 |
|
| 189 |
-
# βββ Routes βββ
|
| 190 |
@app.get("/")
|
| 191 |
async def root():
|
| 192 |
return {"message": "API is running"}
|
|
@@ -204,15 +172,76 @@ async def chat_completions(request: Request):
|
|
| 204 |
if not messages:
|
| 205 |
raise HTTPException(status_code=400, detail="Missing 'messages'")
|
| 206 |
stream = body.get("stream", False)
|
| 207 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
|
| 209 |
if not stream:
|
| 210 |
-
chunks
|
| 211 |
async for part in get_blackbox_response(data=payload, stream=False, request_id=rid):
|
| 212 |
if part.startswith("Error:"):
|
| 213 |
raise HTTPException(status_code=502, detail=part)
|
| 214 |
chunks.append(part)
|
| 215 |
-
answer = "".join(chunks) or "No response."
|
| 216 |
return {
|
| 217 |
"id": str(uuid.uuid4()),
|
| 218 |
"object": "chat.completion",
|
|
@@ -220,9 +249,9 @@ async def chat_completions(request: Request):
|
|
| 220 |
"model": "DeepResearch",
|
| 221 |
"choices": [{
|
| 222 |
"index": 0,
|
| 223 |
-
"message": {"role": "assistant", "content":
|
| 224 |
-
"finish_reason": "stop"
|
| 225 |
-
}]
|
| 226 |
}
|
| 227 |
|
| 228 |
async def event_stream():
|
|
|
|
| 25 |
# βββ Config βββ
|
| 26 |
BLACKBOX_URL = "https://www.blackbox.ai/api/chat"
|
| 27 |
REQUEST_TIMEOUT = 300
|
|
|
|
|
|
|
| 28 |
HEADERS = {
|
| 29 |
"authority": "www.blackbox.ai",
|
| 30 |
"method": "POST",
|
|
|
|
| 42 |
"sec-fetch-dest": "empty",
|
| 43 |
"sec-fetch-mode": "cors",
|
| 44 |
"sec-fetch-site": "same-origin",
|
| 45 |
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
"content-type": "application/json",
|
| 47 |
}
|
| 48 |
|
| 49 |
# βββ FastAPI βββ
|
| 50 |
app = FastAPI()
|
| 51 |
+
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
HTTP_SESSION: aiohttp.ClientSession = None
|
| 53 |
RETRYABLE_STATUSES = {400, 429, 500, 502, 503, 504}
|
| 54 |
_ascii = string.ascii_letters + string.digits
|
| 55 |
|
|
|
|
| 56 |
def _rand(n, pool=_ascii): return ''.join(random.choice(pool) for _ in range(n))
|
| 57 |
def random_email(): return _rand(12) + "@gmail.com"
|
| 58 |
def random_id(): return _rand(21, string.digits)
|
| 59 |
def random_customer_id(): return "cus_" + _rand(12)
|
| 60 |
def generate_7char_id(): return _rand(7)
|
| 61 |
|
|
|
|
| 62 |
def build_payload(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 63 |
msg_id = generate_7char_id()
|
| 64 |
if messages:
|
| 65 |
messages[-1]["id"] = msg_id
|
|
|
|
| 66 |
now = int(time.time())
|
| 67 |
return {
|
| 68 |
"messages": messages,
|
|
|
|
| 93 |
"domains": None,
|
| 94 |
"vscodeClient": False,
|
| 95 |
"codeInterpreterMode": False,
|
| 96 |
+
"customProfile": {"name": "", "occupation": "", "traits": [], "additionalInfo": "", "enableNewChats": False},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
"session": {
|
| 98 |
"user": {
|
| 99 |
"name": _rand(10),
|
|
|
|
| 102 |
"id": random_id()
|
| 103 |
},
|
| 104 |
"expires": "2025-06-09T19:36:08.220Z",
|
| 105 |
+
"isNewUser": False
|
| 106 |
},
|
| 107 |
"isPremium": True,
|
| 108 |
"subscriptionCache": {
|
| 109 |
"status": "PREMIUM",
|
| 110 |
"customerId": random_customer_id(),
|
| 111 |
+
"expiryTimestamp": now + 60 * 86400,
|
| 112 |
+
"lastChecked": int(time.time() * 1000),
|
| 113 |
"isTrialSubscription": False
|
| 114 |
},
|
| 115 |
"beastMode": False,
|
|
|
|
| 117 |
"designerMode": False
|
| 118 |
}
|
| 119 |
|
|
|
|
| 120 |
class RetryableStatusError(Exception):
|
| 121 |
def __init__(self, status: int, text: str):
|
| 122 |
super().__init__(f"status={status} body={text[:100]}...")
|
|
|
|
| 125 |
rid = retry_state.kwargs.get("request_id", "unknown")
|
| 126 |
logger.warning("[%s] retry %s/3 due to %s", rid, retry_state.attempt_number, retry_state.outcome.exception())
|
| 127 |
|
| 128 |
+
@retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10), retry=retry_if_exception_type(
|
| 129 |
+
(aiohttp.ClientConnectionError, aiohttp.ClientResponseError, asyncio.TimeoutError, RetryableStatusError)),
|
| 130 |
+
before_sleep=log_retry)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
async def get_blackbox_response(*, data, stream: bool, request_id: str) -> AsyncGenerator[str, None]:
|
| 132 |
global HTTP_SESSION
|
| 133 |
if not HTTP_SESSION:
|
| 134 |
HTTP_SESSION = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT))
|
|
|
|
| 135 |
async with HTTP_SESSION.post(BLACKBOX_URL, json=data, headers=HEADERS, timeout=REQUEST_TIMEOUT) as resp:
|
| 136 |
if resp.status != 200:
|
| 137 |
body = await resp.text()
|
|
|
|
| 139 |
if resp.status in RETRYABLE_STATUSES:
|
| 140 |
raise RetryableStatusError(resp.status, body)
|
| 141 |
raise HTTPException(status_code=502, detail=f"Upstream error {resp.status}")
|
|
|
|
| 142 |
if stream:
|
| 143 |
async for chunk in resp.content.iter_any():
|
| 144 |
if chunk:
|
|
|
|
| 146 |
else:
|
| 147 |
yield await resp.text()
|
| 148 |
|
|
|
|
| 149 |
@app.middleware("http")
|
| 150 |
async def add_request_id(request: Request, call_next):
|
| 151 |
request.state.request_id = rid = str(uuid.uuid4())
|
|
|
|
| 155 |
logger.info("[%s] finished in %.2fs", rid, time.perf_counter() - start)
|
| 156 |
return resp
|
| 157 |
|
|
|
|
| 158 |
@app.get("/")
|
| 159 |
async def root():
|
| 160 |
return {"message": "API is running"}
|
|
|
|
| 172 |
if not messages:
|
| 173 |
raise HTTPException(status_code=400, detail="Missing 'messages'")
|
| 174 |
stream = body.get("stream", False)
|
| 175 |
+
|
| 176 |
+
# Use exact deepresearch payload if flag is passed
|
| 177 |
+
if body.get("test_payload") == "deepsearch":
|
| 178 |
+
payload = {
|
| 179 |
+
"messages": [{"id": "s2eB86t", "content": "google", "role": "user"}],
|
| 180 |
+
"agentMode": {},
|
| 181 |
+
"id": "s2eB86t",
|
| 182 |
+
"previewToken": None,
|
| 183 |
+
"userId": None,
|
| 184 |
+
"codeModelMode": True,
|
| 185 |
+
"trendingAgentMode": {},
|
| 186 |
+
"isMicMode": False,
|
| 187 |
+
"userSystemPrompt": None,
|
| 188 |
+
"maxTokens": 1024,
|
| 189 |
+
"playgroundTopP": None,
|
| 190 |
+
"playgroundTemperature": None,
|
| 191 |
+
"isChromeExt": False,
|
| 192 |
+
"githubToken": "",
|
| 193 |
+
"clickedAnswer2": False,
|
| 194 |
+
"clickedAnswer3": False,
|
| 195 |
+
"clickedForceWebSearch": False,
|
| 196 |
+
"visitFromDelta": False,
|
| 197 |
+
"isMemoryEnabled": False,
|
| 198 |
+
"mobileClient": False,
|
| 199 |
+
"userSelectedModel": None,
|
| 200 |
+
"validated": "00f37b34-a166-4efb-bce5-1312d87f2f94",
|
| 201 |
+
"imageGenerationMode": False,
|
| 202 |
+
"webSearchModePrompt": False,
|
| 203 |
+
"deepSearchMode": True,
|
| 204 |
+
"domains": None,
|
| 205 |
+
"vscodeClient": False,
|
| 206 |
+
"codeInterpreterMode": False,
|
| 207 |
+
"customProfile": {
|
| 208 |
+
"name": "",
|
| 209 |
+
"occupation": "",
|
| 210 |
+
"traits": [],
|
| 211 |
+
"additionalInfo": "",
|
| 212 |
+
"enableNewChats": False
|
| 213 |
+
},
|
| 214 |
+
"session": {
|
| 215 |
+
"user": {
|
| 216 |
+
"name": "S.C gaming",
|
| 217 |
+
"email": "simarmanbir@gmail.com",
|
| 218 |
+
"image": "https://lh3.googleusercontent.com/a/ACg8ocI-ze5Qe42S-j8xaCL6X7KSVwfiOae4fONqpTxzt0d2_a2FIld1=s96-c",
|
| 219 |
+
"id": "100846841133312010974"
|
| 220 |
+
},
|
| 221 |
+
"expires": "2025-06-09T19:36:08.220Z",
|
| 222 |
+
"isNewUser": False
|
| 223 |
+
},
|
| 224 |
+
"isPremium": True,
|
| 225 |
+
"subscriptionCache": {
|
| 226 |
+
"status": "PREMIUM",
|
| 227 |
+
"customerId": "cus_Rtiok4sPQNoo1c",
|
| 228 |
+
"expiryTimestamp": 1749108685,
|
| 229 |
+
"lastChecked": 1746822333827,
|
| 230 |
+
"isTrialSubscription": True
|
| 231 |
+
},
|
| 232 |
+
"beastMode": False,
|
| 233 |
+
"reasoningMode": False,
|
| 234 |
+
"designerMode": False
|
| 235 |
+
}
|
| 236 |
+
else:
|
| 237 |
+
payload = build_payload(messages)
|
| 238 |
|
| 239 |
if not stream:
|
| 240 |
+
chunks = []
|
| 241 |
async for part in get_blackbox_response(data=payload, stream=False, request_id=rid):
|
| 242 |
if part.startswith("Error:"):
|
| 243 |
raise HTTPException(status_code=502, detail=part)
|
| 244 |
chunks.append(part)
|
|
|
|
| 245 |
return {
|
| 246 |
"id": str(uuid.uuid4()),
|
| 247 |
"object": "chat.completion",
|
|
|
|
| 249 |
"model": "DeepResearch",
|
| 250 |
"choices": [{
|
| 251 |
"index": 0,
|
| 252 |
+
"message": {"role": "assistant", "content": "".join(chunks)},
|
| 253 |
+
"finish_reason": "stop"
|
| 254 |
+
}]
|
| 255 |
}
|
| 256 |
|
| 257 |
async def event_stream():
|