Niansuh commited on
Commit
ba9aaa0
·
verified ·
1 Parent(s): eb6514b

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +81 -80
main.py CHANGED
@@ -39,6 +39,13 @@ rate_limit_store = defaultdict(lambda: {"count": 0, "timestamp": time.time()})
39
  CLEANUP_INTERVAL = 60 # seconds
40
  RATE_LIMIT_WINDOW = 60 # seconds
41
 
 
 
 
 
 
 
 
42
  class Blackbox:
43
  label = "Blackbox AI"
44
  url = "https://www.blackbox.ai"
@@ -184,20 +191,34 @@ class Blackbox:
184
 
185
  @staticmethod
186
  def clean_response(text: str) -> str:
187
- pattern = r'^\$\@\$v=undefined-rv1\$\@\$'
188
  cleaned_text = re.sub(pattern, '', text)
189
  return cleaned_text
190
 
191
  @classmethod
192
- async def generate_response(
193
  cls,
194
  model: str,
195
  messages: List[Dict[str, str]],
196
  proxy: Optional[str] = None,
197
  websearch: bool = False,
198
  **kwargs
199
- ) -> AsyncGenerator[Union[str, Dict[str, Any]], None]:
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  model = cls.get_model(model)
 
201
  chat_id = cls.generate_random_string()
202
  next_action = cls.generate_next_action()
203
  next_router_state_tree = cls.generate_next_router_state_tree()
@@ -213,7 +234,7 @@ class Blackbox:
213
  content = message.get('content', '')
214
  if role and content:
215
  formatted_prompt += f"{role}: {content}\n"
216
-
217
  if prefix:
218
  formatted_prompt = f"{prefix} {formatted_prompt}".strip()
219
 
@@ -295,42 +316,14 @@ class Blackbox:
295
  proxy=proxy
296
  ) as response_api_chat:
297
  response_api_chat.raise_for_status()
298
- text = await response_api_chat.text()
299
- cleaned_response = cls.clean_response(text)
300
-
301
- if model in cls.image_models:
302
- match = re.search(r'!\[.*?\]\((https?://[^\)]+)\)', cleaned_response)
303
- if match:
304
- image_url = match.group(1)
305
- yield {"type": "image", "url": image_url}
306
- else:
307
- yield {"type": "text", "content": cleaned_response}
308
- else:
309
- if websearch:
310
- match = re.search(r'\$~~~\$(.*?)\$~~~\$', cleaned_response, re.DOTALL)
311
- if match:
312
- source_part = match.group(1).strip()
313
- answer_part = cleaned_response[match.end():].strip()
314
- try:
315
- sources = json.loads(source_part)
316
- source_formatted = "**Source:**\n"
317
- for item in sources:
318
- title = item.get('title', 'No Title')
319
- link = item.get('link', '#')
320
- position = item.get('position', '')
321
- source_formatted += f"{position}. [{title}]({link})\n"
322
- final_response = f"{answer_part}\n\n{source_formatted}"
323
- except json.JSONDecodeError:
324
- final_response = f"{answer_part}\n\nSource information is unavailable."
325
- else:
326
- final_response = cleaned_response
327
- else:
328
- if '$~~~$' in cleaned_response:
329
- final_response = cleaned_response.split('$~~~$')[0].strip()
330
- else:
331
- final_response = cleaned_response
332
-
333
- yield {"type": "text", "content": final_response}
334
  except ClientResponseError as e:
335
  error_text = f"Error {e.status}: {e.message}"
336
  try:
@@ -339,10 +332,11 @@ class Blackbox:
339
  error_text += f" - {cleaned_error}"
340
  except Exception:
341
  pass
342
- yield {"type": "error", "content": error_text}
343
  except Exception as e:
344
- yield {"type": "error", "content": f"Unexpected error during /api/chat request: {str(e)}"}
345
 
 
346
  chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
347
 
348
  try:
@@ -362,9 +356,9 @@ class Blackbox:
362
  error_text += f" - {cleaned_error}"
363
  except Exception:
364
  pass
365
- yield {"type": "error", "content": error_text}
366
  except Exception as e:
367
- yield {"type": "error", "content": f"Unexpected error during /chat/{chat_id} request: {str(e)}"}
368
 
369
  # Custom exception for model not working
370
  class ModelNotWorkingException(Exception):
@@ -393,7 +387,7 @@ async def rate_limiter_per_ip(request: Request):
393
  current_time = time.time()
394
 
395
  # Initialize or update the count and timestamp
396
- if current_time - rate_limit_store[client_ip]["timestamp"] > RATE_LIMIT_WINDOW:
397
  rate_limit_store[client_ip] = {"count": 1, "timestamp": current_time}
398
  else:
399
  if rate_limit_store[client_ip]["count"] >= RATE_LIMIT:
@@ -455,6 +449,7 @@ class Message(BaseModel):
455
  class ChatRequest(BaseModel):
456
  model: str
457
  messages: List[Message]
 
458
  temperature: Optional[float] = 1.0
459
  top_p: Optional[float] = 1.0
460
  n: Optional[int] = 1
@@ -463,8 +458,17 @@ class ChatRequest(BaseModel):
463
  frequency_penalty: Optional[float] = 0.0
464
  logit_bias: Optional[Dict[str, float]] = None
465
  user: Optional[str] = None
466
- stream: Optional[bool] = False
467
- websearch: Optional[bool] = False
 
 
 
 
 
 
 
 
 
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)):
@@ -481,40 +485,43 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
481
  raise HTTPException(status_code=400, detail="Requested model is not available.")
482
 
483
  # Process the request with actual message content, but don't log it
484
- response_generator = Blackbox.generate_response(
485
  model=request.model,
486
  messages=[{"role": msg.role, "content": msg.content} for msg in request.messages],
487
  temperature=request.temperature,
488
- max_tokens=request.max_tokens,
489
- websearch=request.websearch
490
  )
491
 
492
  if request.stream:
493
  async def stream_response():
494
- async for chunk in response_generator:
495
- if chunk["type"] == "text":
496
- yield f"data: {json.dumps({'choices': [{'delta': {'content': chunk['content']}}]})}\n\n"
497
- elif chunk["type"] == "image":
498
- yield f"data: {json.dumps({'choices': [{'delta': {'image': chunk['url']}}]})}\n\n"
499
- elif chunk["type"] == "error":
500
- yield f"data: {json.dumps({'error': chunk['content']})}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
501
  yield "data: [DONE]\n\n"
502
-
 
 
503
  return StreamingResponse(stream_response(), media_type="text/event-stream")
504
  else:
505
- full_response = ""
506
- image_url = None
507
- async for chunk in response_generator:
508
- if chunk["type"] == "text":
509
- full_response += chunk["content"]
510
- elif chunk["type"] == "image":
511
- image_url = chunk["url"]
512
- elif chunk["type"] == "error":
513
- raise HTTPException(status_code=500, detail=chunk["content"])
514
-
515
  logger.info(f"Completed response generation for API key: {api_key} | IP: {client_ip}")
516
-
517
- response = {
518
  "id": f"chatcmpl-{uuid.uuid4()}",
519
  "object": "chat.completion",
520
  "created": int(datetime.now().timestamp()),
@@ -524,23 +531,17 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
524
  "index": 0,
525
  "message": {
526
  "role": "assistant",
527
- "content": full_response
528
  },
529
  "finish_reason": "stop"
530
  }
531
  ],
532
  "usage": {
533
  "prompt_tokens": sum(len(msg.content.split()) for msg in request.messages),
534
- "completion_tokens": len(full_response.split()),
535
- "total_tokens": sum(len(msg.content.split()) for msg in request.messages) + len(full_response.split())
536
  },
537
  }
538
-
539
- if image_url:
540
- response["choices"][0]["message"]["image"] = image_url
541
-
542
- return response
543
-
544
  except ModelNotWorkingException as e:
545
  logger.warning(f"Model not working: {e} | IP: {client_ip}")
546
  raise HTTPException(status_code=503, detail=str(e))
@@ -584,4 +585,4 @@ async def http_exception_handler(request: Request, exc: HTTPException):
584
 
585
  if __name__ == "__main__":
586
  import uvicorn
587
- uvicorn.run(app, host="0.0.0.0", port=8000)
 
39
  CLEANUP_INTERVAL = 60 # seconds
40
  RATE_LIMIT_WINDOW = 60 # seconds
41
 
42
+ class ImageResponse:
43
+ def __init__(self, images: Union[str, List[str]], alt: str = "Generated Image"):
44
+ if isinstance(images, str):
45
+ images = [images]
46
+ self.images = images
47
+ self.alt = alt
48
+
49
  class Blackbox:
50
  label = "Blackbox AI"
51
  url = "https://www.blackbox.ai"
 
191
 
192
  @staticmethod
193
  def clean_response(text: str) -> str:
194
+ pattern = r'^\$\@\$v=undefined-rv1\$\@'
195
  cleaned_text = re.sub(pattern, '', text)
196
  return cleaned_text
197
 
198
  @classmethod
199
+ async def create_async_generator(
200
  cls,
201
  model: str,
202
  messages: List[Dict[str, str]],
203
  proxy: Optional[str] = None,
204
  websearch: bool = False,
205
  **kwargs
206
+ ) -> AsyncGenerator[Union[str, ImageResponse], None]:
207
+ """
208
+ Creates an asynchronous generator for streaming responses from Blackbox AI.
209
+
210
+ Parameters:
211
+ model (str): Model to use for generating responses.
212
+ messages (List[Dict[str, str]]): Message history.
213
+ proxy (Optional[str]): Proxy URL, if needed.
214
+ websearch (bool): Enables or disables web search mode.
215
+ **kwargs: Additional keyword arguments.
216
+
217
+ Yields:
218
+ Union[str, ImageResponse]: Segments of the generated response or ImageResponse objects.
219
+ """
220
  model = cls.get_model(model)
221
+
222
  chat_id = cls.generate_random_string()
223
  next_action = cls.generate_next_action()
224
  next_router_state_tree = cls.generate_next_router_state_tree()
 
234
  content = message.get('content', '')
235
  if role and content:
236
  formatted_prompt += f"{role}: {content}\n"
237
+
238
  if prefix:
239
  formatted_prompt = f"{prefix} {formatted_prompt}".strip()
240
 
 
316
  proxy=proxy
317
  ) as response_api_chat:
318
  response_api_chat.raise_for_status()
319
+ # We update this part to stream the response incrementally
320
+ # Instead of waiting for the full response, we read the response as it arrives
321
+ async for chunk in response_api_chat.content.iter_chunked(1024):
322
+ if not chunk:
323
+ continue
324
+ text = chunk.decode('utf-8', errors='ignore')
325
+ cleaned_chunk = cls.clean_response(text)
326
+ yield cleaned_chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  except ClientResponseError as e:
328
  error_text = f"Error {e.status}: {e.message}"
329
  try:
 
332
  error_text += f" - {cleaned_error}"
333
  except Exception:
334
  pass
335
+ yield error_text
336
  except Exception as e:
337
+ yield f"Unexpected error during /api/chat request: {str(e)}"
338
 
339
+ # Not clear what to do with this second request; keeping it for compatibility
340
  chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
341
 
342
  try:
 
356
  error_text += f" - {cleaned_error}"
357
  except Exception:
358
  pass
359
+ yield error_text
360
  except Exception as e:
361
+ yield f"Unexpected error during /chat/{chat_id} request: {str(e)}"
362
 
363
  # Custom exception for model not working
364
  class ModelNotWorkingException(Exception):
 
387
  current_time = time.time()
388
 
389
  # Initialize or update the count and timestamp
390
+ if current_time - rate_limit_store[client_ip]["timest(由Dream 提供Free https://opus.gptuu.com)amp"] > RATE_LIMIT_WINDOW:
391
  rate_limit_store[client_ip] = {"count": 1, "timestamp": current_time}
392
  else:
393
  if rate_limit_store[client_ip]["count"] >= RATE_LIMIT:
 
449
  class ChatRequest(BaseModel):
450
  model: str
451
  messages: List[Message]
452
+ stream: Optional[bool] = False # Added for streaming support
453
  temperature: Optional[float] = 1.0
454
  top_p: Optional[float] = 1.0
455
  n: Optional[int] = 1
 
458
  frequency_penalty: Optional[float] = 0.0
459
  logit_bias: Optional[Dict[str, float]] = None
460
  user: Optional[str] = None
461
+
462
+ # Helper function to collect responses from async generator
463
+ async def collect_response_content(generator: AsyncGenerator[Union[str, ImageResponse], None]) -> str:
464
+ response_content = ''
465
+ async for chunk in generator:
466
+ if isinstance(chunk, str):
467
+ response_content += chunk
468
+ elif isinstance(chunk, ImageResponse):
469
+ # Handle image response if needed
470
+ response_content += f"[Image: {chunk.alt}] {', '.join(chunk.images)}\n"
471
+ return response_content
472
 
473
  @app.post("/v1/chat/completions", dependencies=[Depends(rate_limiter_per_ip)])
474
  async def chat_completions(request: ChatRequest, req: Request, api_key: str = Depends(get_api_key)):
 
485
  raise HTTPException(status_code=400, detail="Requested model is not available.")
486
 
487
  # Process the request with actual message content, but don't log it
488
+ generator = Blackbox.create_async_generator(
489
  model=request.model,
490
  messages=[{"role": msg.role, "content": msg.content} for msg in request.messages],
491
  temperature=request.temperature,
492
+ max_tokens=request.max_tokens
 
493
  )
494
 
495
  if request.stream:
496
  async def stream_response():
497
+ async for chunk in generator:
498
+ if isinstance(chunk, str):
499
+ data = json.dumps({
500
+ "choices": [{
501
+ "delta": {"content": chunk},
502
+ "index": 0,
503
+ "finish_reason": None
504
+ }],
505
+ "model": request.model,
506
+ "id": f"chatcmpl-{uuid.uuid4()}",
507
+ "object": "chat.completion.chunk",
508
+ "created": int(datetime.now().timestamp()),
509
+ })
510
+ # Ensure that each chunk is sent immediately
511
+ yield f"data: {data}\n\n"
512
+ elif isinstance(chunk, ImageResponse):
513
+ # Handle image responses here if needed
514
+ pass # For now, we skip image handling in streaming
515
+ # Send the termination message
516
  yield "data: [DONE]\n\n"
517
+
518
+ logger.info(f"Streaming response enabled for API key: {api_key} | IP: {client_ip}")
519
+
520
  return StreamingResponse(stream_response(), media_type="text/event-stream")
521
  else:
522
+ response_content = await collect_response_content(generator)
 
 
 
 
 
 
 
 
 
523
  logger.info(f"Completed response generation for API key: {api_key} | IP: {client_ip}")
524
+ return {
 
525
  "id": f"chatcmpl-{uuid.uuid4()}",
526
  "object": "chat.completion",
527
  "created": int(datetime.now().timestamp()),
 
531
  "index": 0,
532
  "message": {
533
  "role": "assistant",
534
+ "content": response_content
535
  },
536
  "finish_reason": "stop"
537
  }
538
  ],
539
  "usage": {
540
  "prompt_tokens": sum(len(msg.content.split()) for msg in request.messages),
541
+ "completion_tokens": len(response_content.split()),
542
+ "total_tokens": sum(len(msg.content.split()) for msg in request.messages) + len(response_content.split())
543
  },
544
  }
 
 
 
 
 
 
545
  except ModelNotWorkingException as e:
546
  logger.warning(f"Model not working: {e} | IP: {client_ip}")
547
  raise HTTPException(status_code=503, detail=str(e))
 
585
 
586
  if __name__ == "__main__":
587
  import uvicorn
588
+ uvicorn.run(app, host="0.0.0.0", port=8000)