Niansuh commited on
Commit
b432755
·
verified ·
1 Parent(s): ff76ef0

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +63 -350
main.py CHANGED
@@ -1,21 +1,21 @@
1
- import os
2
- import re
3
- import random
4
- import string
5
  import uuid
6
- import json
7
  import logging
8
- import asyncio
9
  import time
10
  from collections import defaultdict
11
- from typing import List, Dict, Any, Optional, AsyncGenerator, Union
12
-
13
  from datetime import datetime
14
-
 
 
 
 
15
  from aiohttp import ClientSession, ClientTimeout, ClientError
16
- from fastapi import FastAPI, HTTPException, Request, Depends, Header
17
- from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
18
- from pydantic import BaseModel
19
 
20
  # Configure logging
21
  logging.basicConfig(
@@ -89,333 +89,7 @@ async def get_api_key(request: Request, authorization: str = Header(None)) -> st
89
  raise HTTPException(status_code=401, detail='Invalid API key')
90
  return api_key
91
 
92
- # Custom exception for model not working
93
- class ModelNotWorkingException(Exception):
94
- def __init__(self, model: str):
95
- self.model = model
96
- self.message = f"The model '{model}' is currently not working. Please try another model or wait for it to be fixed."
97
- super().__init__(self.message)
98
-
99
- # Mock implementations for ImageResponse and to_data_uri
100
- class ImageResponse:
101
- def __init__(self, url: str, alt: str):
102
- self.url = url
103
- self.alt = alt
104
-
105
- def to_data_uri(image: Any) -> str:
106
- return "data:image/png;base64,..." # Replace with actual base64 data
107
-
108
- class Blackbox:
109
- url = "https://www.blackbox.ai"
110
- api_endpoint = "https://www.blackbox.ai/api/chat"
111
- working = True
112
- supports_stream = True
113
- supports_system_message = True
114
- supports_message_history = True
115
-
116
- default_model = 'blackboxai'
117
- image_models = ['ImageGeneration']
118
- models = [
119
- default_model,
120
- 'blackboxai-pro',
121
- "llama-3.1-8b",
122
- 'llama-3.1-70b',
123
- 'llama-3.1-405b',
124
- 'gpt-4o',
125
- 'gemini-pro',
126
- 'gemini-1.5-flash',
127
- 'claude-sonnet-3.5',
128
- 'PythonAgent',
129
- 'JavaAgent',
130
- 'JavaScriptAgent',
131
- 'HTMLAgent',
132
- 'GoogleCloudAgent',
133
- 'AndroidDeveloper',
134
- 'SwiftDeveloper',
135
- 'Next.jsAgent',
136
- 'MongoDBAgent',
137
- 'PyTorchAgent',
138
- 'ReactAgent',
139
- 'XcodeAgent',
140
- 'AngularJSAgent',
141
- *image_models,
142
- 'Niansuh',
143
- ]
144
-
145
- # Filter models based on AVAILABLE_MODELS
146
- if AVAILABLE_MODELS:
147
- models = [model for model in models if model in AVAILABLE_MODELS]
148
-
149
- agentMode = {
150
- 'ImageGeneration': {'mode': True, 'id': "ImageGenerationLV45LJp", 'name': "Image Generation"},
151
- 'Niansuh': {'mode': True, 'id': "NiansuhAIk1HgESy", 'name': "Niansuh"},
152
- }
153
- trendingAgentMode = {
154
- "blackboxai": {},
155
- "gemini-1.5-flash": {'mode': True, 'id': 'Gemini'},
156
- "llama-3.1-8b": {'mode': True, 'id': "llama-3.1-8b"},
157
- 'llama-3.1-70b': {'mode': True, 'id': "llama-3.1-70b"},
158
- 'llama-3.1-405b': {'mode': True, 'id': "llama-3.1-405b"},
159
- 'blackboxai-pro': {'mode': True, 'id': "BLACKBOXAI-PRO"},
160
- 'PythonAgent': {'mode': True, 'id': "Python Agent"},
161
- 'JavaAgent': {'mode': True, 'id': "Java Agent"},
162
- 'JavaScriptAgent': {'mode': True, 'id': "JavaScript Agent"},
163
- 'HTMLAgent': {'mode': True, 'id': "HTML Agent"},
164
- 'GoogleCloudAgent': {'mode': True, 'id': "Google Cloud Agent"},
165
- 'AndroidDeveloper': {'mode': True, 'id': "Android Developer"},
166
- 'SwiftDeveloper': {'mode': True, 'id': "Swift Developer"},
167
- 'Next.jsAgent': {'mode': True, 'id': "Next.js Agent"},
168
- 'MongoDBAgent': {'mode': True, 'id': "MongoDB Agent"},
169
- 'PyTorchAgent': {'mode': True, 'id': "PyTorch Agent"},
170
- 'ReactAgent': {'mode': True, 'id': "React Agent"},
171
- 'XcodeAgent': {'mode': True, 'id': "Xcode Agent"},
172
- 'AngularJSAgent': {'mode': True, 'id': "AngularJS Agent"},
173
- }
174
-
175
- userSelectedModel = {
176
- "gpt-4o": "gpt-4o",
177
- "gemini-pro": "gemini-pro",
178
- 'claude-sonnet-3.5': "claude-sonnet-3.5",
179
- }
180
-
181
- model_prefixes = {
182
- 'gpt-4o': '@GPT-4o',
183
- 'gemini-pro': '@Gemini-PRO',
184
- 'claude-sonnet-3.5': '@Claude-Sonnet-3.5',
185
- 'PythonAgent': '@Python Agent',
186
- 'JavaAgent': '@Java Agent',
187
- 'JavaScriptAgent': '@JavaScript Agent',
188
- 'HTMLAgent': '@HTML Agent',
189
- 'GoogleCloudAgent': '@Google Cloud Agent',
190
- 'AndroidDeveloper': '@Android Developer',
191
- 'SwiftDeveloper': '@Swift Developer',
192
- 'Next.jsAgent': '@Next.js Agent',
193
- 'MongoDBAgent': '@MongoDB Agent',
194
- 'PyTorchAgent': '@PyTorch Agent',
195
- 'ReactAgent': '@React Agent',
196
- 'XcodeAgent': '@Xcode Agent',
197
- 'AngularJSAgent': '@AngularJS Agent',
198
- 'blackboxai-pro': '@BLACKBOXAI-PRO',
199
- 'ImageGeneration': '@Image Generation',
200
- 'Niansuh': '@Niansuh',
201
- }
202
-
203
- model_referers = {
204
- "blackboxai": f"{url}/?model=blackboxai",
205
- "gpt-4o": f"{url}/?model=gpt-4o",
206
- "gemini-pro": f"{url}/?model=gemini-pro",
207
- "claude-sonnet-3.5": f"{url}/?model=claude-sonnet-3.5"
208
- }
209
-
210
- model_aliases = {
211
- "gemini-flash": "gemini-1.5-flash",
212
- "claude-3.5-sonnet": "claude-sonnet-3.5",
213
- "flux": "ImageGeneration",
214
- "niansuh": "Niansuh",
215
- }
216
-
217
- @classmethod
218
- def get_model(cls, model: str) -> Optional[str]:
219
- if model in cls.models:
220
- return model
221
- elif model in cls.userSelectedModel and cls.userSelectedModel[model] in cls.models:
222
- return cls.userSelectedModel[model]
223
- elif model in cls.model_aliases and cls.model_aliases[model] in cls.models:
224
- return cls.model_aliases[model]
225
- else:
226
- return cls.default_model if cls.default_model in cls.models else None
227
-
228
- @classmethod
229
- async def create_async_generator(
230
- cls,
231
- model: str,
232
- messages: List[Dict[str, str]],
233
- proxy: Optional[str] = None,
234
- image: Any = None,
235
- image_name: Optional[str] = None,
236
- webSearchMode: bool = False,
237
- **kwargs
238
- ) -> AsyncGenerator[Any, None]:
239
- model = cls.get_model(model)
240
- if model is None:
241
- logger.error(f"Model {model} is not available.")
242
- raise ModelNotWorkingException(model)
243
-
244
- logger.info(f"Selected model: {model}")
245
-
246
- if not cls.working or model not in cls.models:
247
- logger.error(f"Model {model} is not working or not supported.")
248
- raise ModelNotWorkingException(model)
249
-
250
- headers = {
251
- "accept": "*/*",
252
- "accept-language": "en-US,en;q=0.9",
253
- "cache-control": "no-cache",
254
- "content-type": "application/json",
255
- "origin": cls.url,
256
- "pragma": "no-cache",
257
- "priority": "u=1, i",
258
- "referer": cls.model_referers.get(model, cls.url),
259
- "sec-ch-ua": '"Chromium";v="129", "Not=A?Brand";v="8"',
260
- "sec-ch-ua-mobile": "?0",
261
- "sec-ch-ua-platform": '"Linux"',
262
- "sec-fetch-dest": "empty",
263
- "sec-fetch-mode": "cors",
264
- "sec-fetch-site": "same-origin",
265
- "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
266
- }
267
-
268
- if model in cls.model_prefixes:
269
- prefix = cls.model_prefixes[model]
270
- if not messages[0]['content'].startswith(prefix):
271
- logger.debug(f"Adding prefix '{prefix}' to the first message.")
272
- messages[0]['content'] = f"{prefix} {messages[0]['content']}"
273
-
274
- random_id = ''.join(random.choices(string.ascii_letters + string.digits, k=7))
275
- messages[-1]['id'] = random_id
276
- messages[-1]['role'] = 'user'
277
-
278
- # Don't log the full message content for privacy
279
- logger.debug(f"Generated message ID: {random_id} for model: {model}")
280
-
281
- if image is not None:
282
- messages[-1]['data'] = {
283
- 'fileText': '',
284
- 'imageBase64': to_data_uri(image),
285
- 'title': image_name
286
- }
287
- messages[-1]['content'] = 'FILE:BB\n$#$\n\n$#$\n' + messages[-1]['content']
288
- logger.debug("Image data added to the message.")
289
-
290
- data = {
291
- "messages": messages,
292
- "id": random_id,
293
- "previewToken": None,
294
- "userId": None,
295
- "codeModelMode": True,
296
- "agentMode": {},
297
- "trendingAgentMode": {},
298
- "isMicMode": False,
299
- "userSystemPrompt": None,
300
- "maxTokens": 99999999,
301
- "playgroundTopP": 0.9,
302
- "playgroundTemperature": 0.5,
303
- "isChromeExt": False,
304
- "githubToken": None,
305
- "clickedAnswer2": False,
306
- "clickedAnswer3": False,
307
- "clickedForceWebSearch": False,
308
- "visitFromDelta": False,
309
- "mobileClient": False,
310
- "userSelectedModel": None,
311
- "webSearchMode": webSearchMode,
312
- }
313
-
314
- if model in cls.agentMode:
315
- data["agentMode"] = cls.agentMode[model]
316
- elif model in cls.trendingAgentMode:
317
- data["trendingAgentMode"] = cls.trendingAgentMode[model]
318
- elif model in cls.userSelectedModel:
319
- data["userSelectedModel"] = cls.userSelectedModel[model]
320
- logger.info(f"Sending request to {cls.api_endpoint} with data (excluding messages).")
321
-
322
- timeout = ClientTimeout(total=60) # Set an appropriate timeout
323
- retry_attempts = 10 # Set the number of retry attempts
324
-
325
- for attempt in range(retry_attempts):
326
- try:
327
- async with ClientSession(headers=headers, timeout=timeout) as session:
328
- async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
329
- response.raise_for_status()
330
- logger.info(f"Received response with status {response.status}")
331
- if model == 'ImageGeneration':
332
- response_text = await response.text()
333
- url_match = re.search(r'https://storage\.googleapis\.com/[^\s\)]+', response_text)
334
- if url_match:
335
- image_url = url_match.group(0)
336
- logger.info(f"Image URL found.")
337
- yield ImageResponse(image_url, alt=messages[-1]['content'])
338
- else:
339
- logger.error("Image URL not found in the response.")
340
- raise Exception("Image URL not found in the response")
341
- else:
342
- full_response = ""
343
- search_results_json = ""
344
- try:
345
- async for chunk, _ in response.content.iter_chunks():
346
- if chunk:
347
- decoded_chunk = chunk.decode(errors='ignore')
348
- decoded_chunk = re.sub(r'\$@\$v=[^$]+\$@\$', '', decoded_chunk)
349
- if decoded_chunk.strip():
350
- if '$~~~$' in decoded_chunk:
351
- search_results_json += decoded_chunk
352
- else:
353
- full_response += decoded_chunk
354
- yield decoded_chunk
355
- logger.info("Finished streaming response chunks.")
356
- except Exception as e:
357
- logger.exception("Error while iterating over response chunks.")
358
- raise e
359
- if data["webSearchMode"] and search_results_json:
360
- match = re.search(r'\$~~~\$(.*?)\$~~~\$', search_results_json, re.DOTALL)
361
- if match:
362
- try:
363
- search_results = json.loads(match.group(1))
364
- formatted_results = "\n\n**Sources:**\n"
365
- for i, result in enumerate(search_results[:5], 1):
366
- formatted_results += f"{i}. [{result['title']}]({result['link']})\n"
367
- logger.info("Formatted search results.")
368
- yield formatted_results
369
- except json.JSONDecodeError as je:
370
- logger.error("Failed to parse search results JSON.")
371
- raise je
372
- break # Exit the retry loop if successful
373
- except ClientError as ce:
374
- logger.error(f"Client error occurred: {ce}. Retrying attempt {attempt + 1}/{retry_attempts}")
375
- if attempt == retry_attempts - 1:
376
- raise HTTPException(status_code=502, detail="Error communicating with the external API.")
377
- except asyncio.TimeoutError:
378
- logger.error(f"Request timed out. Retrying attempt {attempt + 1}/{retry_attempts}")
379
- if attempt == retry_attempts - 1:
380
- raise HTTPException(status_code=504, detail="External API request timed out.")
381
- except Exception as e:
382
- logger.error(f"Unexpected error: {e}. Retrying attempt {attempt + 1}/{retry_attempts}")
383
- if attempt == retry_attempts - 1:
384
- raise HTTPException(status_code=500, detail=str(e))
385
-
386
- # FastAPI app setup
387
- app = FastAPI()
388
-
389
- # Add the cleanup task when the app starts
390
- @app.on_event("startup")
391
- async def startup_event():
392
- asyncio.create_task(cleanup_rate_limit_stores())
393
- logger.info("Started rate limit store cleanup task.")
394
-
395
- # Middleware to enhance security and enforce Content-Type for specific endpoints
396
- @app.middleware("http")
397
- async def security_middleware(request: Request, call_next):
398
- client_ip = request.client.host
399
- # Enforce that POST requests to /v1/chat/completions must have Content-Type: application/json
400
- if request.method == "POST" and request.url.path == "/v1/chat/completions":
401
- content_type = request.headers.get("Content-Type")
402
- if content_type != "application/json":
403
- logger.warning(f"Invalid Content-Type from IP: {client_ip} for path: {request.url.path}")
404
- return JSONResponse(
405
- status_code=400,
406
- content={
407
- "error": {
408
- "message": "Content-Type must be application/json",
409
- "type": "invalid_request_error",
410
- "param": None,
411
- "code": None
412
- }
413
- },
414
- )
415
- response = await call_next(request)
416
- return response
417
-
418
- # Request Models
419
  class Message(BaseModel):
420
  role: str
421
  content: str
@@ -466,6 +140,47 @@ def create_response(content: str, model: str, finish_reason: Optional[str] = Non
466
  "usage": None, # To be filled in non-streaming responses
467
  }
468
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  @app.post("/v1/chat/completions", dependencies=[Depends(rate_limiter_per_ip)])
470
  async def chat_completions(request: ChatRequest, req: Request, api_key: str = Depends(get_api_key)):
471
  client_ip = req.client.host
@@ -486,7 +201,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
486
  messages=[{"role": msg.role, "content": msg.content} for msg in request.messages], # Actual message content used here
487
  image=None,
488
  image_name=None,
489
- webSearchMode=request.webSearchMode
490
  )
491
 
492
  if request.stream:
@@ -496,7 +211,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
496
  async for chunk in async_generator:
497
  if isinstance(chunk, ImageResponse):
498
  # Handle image responses if necessary
499
- image_markdown = f"![image]({chunk.url})\n"
500
  assistant_content += image_markdown
501
  response_chunk = create_response(image_markdown, request.model, finish_reason=None)
502
  else:
@@ -519,9 +234,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
519
  yield f"data: {json.dumps(response_chunk)}\n\n"
520
 
521
  # After all chunks are sent, send the final message with finish_reason
522
- # *** Key Correction Starts Here ***
523
  prompt_tokens = sum(len(msg.content.split()) for msg in request.messages)
524
- # *** Key Correction Ends Here ***
525
  completion_tokens = len(assistant_content.split())
526
  total_tokens = prompt_tokens + completion_tokens
527
  estimated_cost = calculate_estimated_cost(prompt_tokens, completion_tokens)
@@ -563,7 +276,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
563
  response_content = ""
564
  async for chunk in async_generator:
565
  if isinstance(chunk, ImageResponse):
566
- response_content += f"![image]({chunk.url})\n"
567
  else:
568
  response_content += chunk
569
 
@@ -606,7 +319,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
606
  logger.exception(f"An unexpected error occurred while processing the chat completions request from IP: {client_ip}.")
607
  raise HTTPException(status_code=500, detail=str(e))
608
 
609
- # Endpoint: POST /v1/tokenizer
610
  @app.post("/v1/tokenizer", dependencies=[Depends(rate_limiter_per_ip)])
611
  async def tokenizer(request: TokenizerRequest, req: Request):
612
  client_ip = req.client.host
@@ -615,14 +328,14 @@ async def tokenizer(request: TokenizerRequest, req: Request):
615
  logger.info(f"Tokenizer requested from IP: {client_ip} | Text length: {len(text)}")
616
  return {"text": text, "tokens": token_count}
617
 
618
- # Endpoint: GET /v1/models
619
  @app.get("/v1/models", dependencies=[Depends(rate_limiter_per_ip)])
620
  async def get_models(req: Request):
621
  client_ip = req.client.host
622
  logger.info(f"Fetching available models from IP: {client_ip}")
623
  return {"data": [{"id": model, "object": "model"} for model in Blackbox.models]}
624
 
625
- # Endpoint: GET /v1/models/{model}/status
626
  @app.get("/v1/models/{model}/status", dependencies=[Depends(rate_limiter_per_ip)])
627
  async def model_status(model: str, req: Request):
628
  client_ip = req.client.host
@@ -636,21 +349,21 @@ async def model_status(model: str, req: Request):
636
  logger.warning(f"Model not found: {model} from IP: {client_ip}")
637
  raise HTTPException(status_code=404, detail="Model not found")
638
 
639
- # Endpoint: GET /v1/health
640
  @app.get("/v1/health", dependencies=[Depends(rate_limiter_per_ip)])
641
  async def health_check(req: Request):
642
  client_ip = req.client.host
643
  logger.info(f"Health check requested from IP: {client_ip}")
644
  return {"status": "ok"}
645
 
646
- # Endpoint: GET /v1/chat/completions (GET method)
647
  @app.get("/v1/chat/completions")
648
  async def chat_completions_get(req: Request):
649
  client_ip = req.client.host
650
  logger.info(f"GET request made to /v1/chat/completions from IP: {client_ip}, redirecting to 'about:blank'")
651
  return RedirectResponse(url='about:blank')
652
 
653
- # Custom exception handler to match OpenAI's error format
654
  @app.exception_handler(HTTPException)
655
  async def http_exception_handler(request: Request, exc: HTTPException):
656
  client_ip = request.client.host
 
1
+ from fastapi import FastAPI, HTTPException, Request, Depends, Header
2
+ from fastapi.responses import StreamingResponse, JSONResponse, RedirectResponse
3
+ from pydantic import BaseModel
4
+ from typing import List, Dict, Any, Optional, AsyncGenerator, Union
5
  import uuid
 
6
  import logging
7
+ import os
8
  import time
9
  from collections import defaultdict
 
 
10
  from datetime import datetime
11
+ import asyncio
12
+ import json
13
+ import re
14
+ import random
15
+ import string
16
  from aiohttp import ClientSession, ClientTimeout, ClientError
17
+
18
+ # Assuming the updated Blackbox class is defined above or imported
 
19
 
20
  # Configure logging
21
  logging.basicConfig(
 
89
  raise HTTPException(status_code=401, detail='Invalid API key')
90
  return api_key
91
 
92
+ # Define request models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  class Message(BaseModel):
94
  role: str
95
  content: str
 
140
  "usage": None, # To be filled in non-streaming responses
141
  }
142
 
143
+ # Custom exception for model not working
144
+ class ModelNotWorkingException(Exception):
145
+ def __init__(self, model: str):
146
+ self.model = model
147
+ self.message = f"The model '{model}' is currently not working. Please try another model or wait for it to be fixed."
148
+ super().__init__(self.message)
149
+
150
+ # Initialize FastAPI app
151
+ app = FastAPI()
152
+
153
+ # Add the cleanup task when the app starts
154
+ @app.on_event("startup")
155
+ async def startup_event():
156
+ asyncio.create_task(cleanup_rate_limit_stores())
157
+ logger.info("Started rate limit store cleanup task.")
158
+
159
+ # Middleware to enhance security and enforce Content-Type for specific endpoints
160
+ @app.middleware("http")
161
+ async def security_middleware(request: Request, call_next):
162
+ client_ip = request.client.host
163
+ # Enforce that POST requests to /v1/chat/completions must have Content-Type: application/json
164
+ if request.method == "POST" and request.url.path == "/v1/chat/completions":
165
+ content_type = request.headers.get("Content-Type")
166
+ if content_type != "application/json":
167
+ logger.warning(f"Invalid Content-Type from IP: {client_ip} for path: {request.url.path}")
168
+ return JSONResponse(
169
+ status_code=400,
170
+ content={
171
+ "error": {
172
+ "message": "Content-Type must be application/json",
173
+ "type": "invalid_request_error",
174
+ "param": None,
175
+ "code": None
176
+ }
177
+ },
178
+ )
179
+ response = await call_next(request)
180
+ return response
181
+
182
+ # FastAPI Endpoints
183
+
184
  @app.post("/v1/chat/completions", dependencies=[Depends(rate_limiter_per_ip)])
185
  async def chat_completions(request: ChatRequest, req: Request, api_key: str = Depends(get_api_key)):
186
  client_ip = req.client.host
 
201
  messages=[{"role": msg.role, "content": msg.content} for msg in request.messages], # Actual message content used here
202
  image=None,
203
  image_name=None,
204
+ websearch=request.webSearchMode
205
  )
206
 
207
  if request.stream:
 
211
  async for chunk in async_generator:
212
  if isinstance(chunk, ImageResponse):
213
  # Handle image responses if necessary
214
+ image_markdown = f"![image]({chunk.images})\n"
215
  assistant_content += image_markdown
216
  response_chunk = create_response(image_markdown, request.model, finish_reason=None)
217
  else:
 
234
  yield f"data: {json.dumps(response_chunk)}\n\n"
235
 
236
  # After all chunks are sent, send the final message with finish_reason
 
237
  prompt_tokens = sum(len(msg.content.split()) for msg in request.messages)
 
238
  completion_tokens = len(assistant_content.split())
239
  total_tokens = prompt_tokens + completion_tokens
240
  estimated_cost = calculate_estimated_cost(prompt_tokens, completion_tokens)
 
276
  response_content = ""
277
  async for chunk in async_generator:
278
  if isinstance(chunk, ImageResponse):
279
+ response_content += f"![image]({chunk.images})\n"
280
  else:
281
  response_content += chunk
282
 
 
319
  logger.exception(f"An unexpected error occurred while processing the chat completions request from IP: {client_ip}.")
320
  raise HTTPException(status_code=500, detail=str(e))
321
 
322
+ # Tokenizer Endpoint
323
  @app.post("/v1/tokenizer", dependencies=[Depends(rate_limiter_per_ip)])
324
  async def tokenizer(request: TokenizerRequest, req: Request):
325
  client_ip = req.client.host
 
328
  logger.info(f"Tokenizer requested from IP: {client_ip} | Text length: {len(text)}")
329
  return {"text": text, "tokens": token_count}
330
 
331
+ # Get Models Endpoint
332
  @app.get("/v1/models", dependencies=[Depends(rate_limiter_per_ip)])
333
  async def get_models(req: Request):
334
  client_ip = req.client.host
335
  logger.info(f"Fetching available models from IP: {client_ip}")
336
  return {"data": [{"id": model, "object": "model"} for model in Blackbox.models]}
337
 
338
+ # Model Status Endpoint
339
  @app.get("/v1/models/{model}/status", dependencies=[Depends(rate_limiter_per_ip)])
340
  async def model_status(model: str, req: Request):
341
  client_ip = req.client.host
 
349
  logger.warning(f"Model not found: {model} from IP: {client_ip}")
350
  raise HTTPException(status_code=404, detail="Model not found")
351
 
352
+ # Health Check Endpoint
353
  @app.get("/v1/health", dependencies=[Depends(rate_limiter_per_ip)])
354
  async def health_check(req: Request):
355
  client_ip = req.client.host
356
  logger.info(f"Health check requested from IP: {client_ip}")
357
  return {"status": "ok"}
358
 
359
+ # Redirect GET requests to /v1/chat/completions to 'about:blank'
360
  @app.get("/v1/chat/completions")
361
  async def chat_completions_get(req: Request):
362
  client_ip = req.client.host
363
  logger.info(f"GET request made to /v1/chat/completions from IP: {client_ip}, redirecting to 'about:blank'")
364
  return RedirectResponse(url='about:blank')
365
 
366
+ # Custom Exception Handler
367
  @app.exception_handler(HTTPException)
368
  async def http_exception_handler(request: Request, exc: HTTPException):
369
  client_ip = request.client.host