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": "[email protected]",
|
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():
|