Update main.py
Browse files
main.py
CHANGED
|
@@ -96,10 +96,10 @@ class ModelNotWorkingException(Exception):
|
|
| 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 |
-
# Define
|
| 100 |
class ImageResponseData:
|
| 101 |
-
def __init__(self,
|
| 102 |
-
self.
|
| 103 |
self.alt = alt
|
| 104 |
|
| 105 |
# Mock implementation of to_data_uri
|
|
@@ -140,6 +140,7 @@ class Blackbox:
|
|
| 140 |
'ReactAgent',
|
| 141 |
'XcodeAgent',
|
| 142 |
'AngularJSAgent',
|
|
|
|
| 143 |
]
|
| 144 |
|
| 145 |
# Filter models based on AVAILABLE_MODELS
|
|
@@ -265,7 +266,9 @@ class Blackbox:
|
|
| 265 |
model: str,
|
| 266 |
messages: List[Dict[str, str]],
|
| 267 |
proxy: Optional[str] = None,
|
| 268 |
-
|
|
|
|
|
|
|
| 269 |
**kwargs
|
| 270 |
) -> AsyncGenerator[Union[str, ImageResponseData], None]:
|
| 271 |
"""
|
|
@@ -275,7 +278,9 @@ class Blackbox:
|
|
| 275 |
model (str): Model to use for generating responses.
|
| 276 |
messages (List[Dict[str, str]]): Message history.
|
| 277 |
proxy (Optional[str]): Proxy URL, if needed.
|
| 278 |
-
|
|
|
|
|
|
|
| 279 |
**kwargs: Additional keyword arguments.
|
| 280 |
|
| 281 |
Yields:
|
|
@@ -345,7 +350,7 @@ class Blackbox:
|
|
| 345 |
"trendingAgentMode": trending_agent_mode,
|
| 346 |
"isMicMode": False,
|
| 347 |
"userSystemPrompt": None,
|
| 348 |
-
"maxTokens":
|
| 349 |
"playgroundTopP": 0.9,
|
| 350 |
"playgroundTemperature": 0.5,
|
| 351 |
"isChromeExt": False,
|
|
@@ -355,91 +360,96 @@ class Blackbox:
|
|
| 355 |
"clickedForceWebSearch": False,
|
| 356 |
"visitFromDelta": False,
|
| 357 |
"mobileClient": False,
|
| 358 |
-
"webSearchMode":
|
| 359 |
-
"userSelectedModel":
|
| 360 |
}
|
| 361 |
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
}
|
| 370 |
-
headers_chat_combined = {**common_headers, **headers_chat}
|
| 371 |
|
| 372 |
-
|
|
|
|
| 373 |
|
| 374 |
async with ClientSession(headers=common_headers) as session:
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
cls.api_endpoint,
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
image_url = match.group(1)
|
| 390 |
-
image_response = ImageResponseData(images=image_url, alt="Generated Image")
|
| 391 |
-
yield image_response
|
| 392 |
-
else:
|
| 393 |
-
yield cleaned_response
|
| 394 |
-
else:
|
| 395 |
-
if websearch:
|
| 396 |
-
match = re.search(r'\$~~~\$(.*?)\$~~~\$', cleaned_response, re.DOTALL)
|
| 397 |
-
if match:
|
| 398 |
-
source_part = match.group(1).strip()
|
| 399 |
-
answer_part = cleaned_response[match.end():].strip()
|
| 400 |
-
try:
|
| 401 |
-
sources = json.loads(source_part)
|
| 402 |
-
source_formatted = "**Sources:**\n"
|
| 403 |
-
for item in sources[:5]:
|
| 404 |
-
title = item.get('title', 'No Title')
|
| 405 |
-
link = item.get('link', '#')
|
| 406 |
-
position = item.get('position', '')
|
| 407 |
-
source_formatted += f"- [{title}]({link})\n"
|
| 408 |
-
final_response = f"{answer_part}\n\n{source_formatted}"
|
| 409 |
-
except json.JSONDecodeError:
|
| 410 |
-
final_response = f"{answer_part}\n\nSource information is unavailable."
|
| 411 |
else:
|
| 412 |
-
|
|
|
|
| 413 |
else:
|
| 414 |
-
|
| 415 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 416 |
else:
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
|
|
|
|
| 432 |
chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
|
| 433 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 434 |
try:
|
| 435 |
-
async with session.post(
|
| 436 |
-
chat_url,
|
| 437 |
-
headers=headers_chat_combined,
|
| 438 |
-
data=data_chat,
|
| 439 |
-
proxy=proxy
|
| 440 |
-
) as response_chat:
|
| 441 |
response_chat.raise_for_status()
|
| 442 |
-
|
| 443 |
except ClientResponseError as e:
|
| 444 |
error_text = f"Error {e.status}: {e.message}"
|
| 445 |
try:
|
|
@@ -448,9 +458,12 @@ class Blackbox:
|
|
| 448 |
error_text += f" - {cleaned_error}"
|
| 449 |
except Exception:
|
| 450 |
pass
|
|
|
|
| 451 |
yield error_text
|
| 452 |
except Exception as e:
|
| 453 |
-
|
|
|
|
|
|
|
| 454 |
|
| 455 |
# FastAPI app setup
|
| 456 |
app = FastAPI()
|
|
@@ -502,7 +515,7 @@ class ChatRequest(BaseModel):
|
|
| 502 |
frequency_penalty: Optional[float] = 0.0
|
| 503 |
logit_bias: Optional[Dict[str, float]] = None
|
| 504 |
user: Optional[str] = None
|
| 505 |
-
|
| 506 |
|
| 507 |
class TokenizerRequest(BaseModel):
|
| 508 |
text: str
|
|
@@ -553,7 +566,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
|
|
| 553 |
async_generator = Blackbox.create_async_generator(
|
| 554 |
model=request.model,
|
| 555 |
messages=[{"role": msg.role, "content": msg.content} for msg in request.messages], # Actual message content used here
|
| 556 |
-
|
| 557 |
)
|
| 558 |
|
| 559 |
if request.stream:
|
|
@@ -563,7 +576,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
|
|
| 563 |
async for chunk in async_generator:
|
| 564 |
if isinstance(chunk, ImageResponseData):
|
| 565 |
# Handle image responses if necessary
|
| 566 |
-
image_markdown = f"
|
| 569 |
else:
|
|
@@ -628,7 +641,7 @@ async def chat_completions(request: ChatRequest, req: Request, api_key: str = De
|
|
| 628 |
response_content = ""
|
| 629 |
async for chunk in async_generator:
|
| 630 |
if isinstance(chunk, ImageResponseData):
|
| 631 |
-
response_content += f".__init__(self.message)
|
| 98 |
|
| 99 |
+
# Define ImageResponseData class
|
| 100 |
class ImageResponseData:
|
| 101 |
+
def __init__(self, url: str, alt: str):
|
| 102 |
+
self.url = url
|
| 103 |
self.alt = alt
|
| 104 |
|
| 105 |
# Mock implementation of to_data_uri
|
|
|
|
| 140 |
'ReactAgent',
|
| 141 |
'XcodeAgent',
|
| 142 |
'AngularJSAgent',
|
| 143 |
+
'Niansuh',
|
| 144 |
]
|
| 145 |
|
| 146 |
# Filter models based on AVAILABLE_MODELS
|
|
|
|
| 266 |
model: str,
|
| 267 |
messages: List[Dict[str, str]],
|
| 268 |
proxy: Optional[str] = None,
|
| 269 |
+
image: Any = None,
|
| 270 |
+
image_name: Optional[str] = None,
|
| 271 |
+
webSearchMode: bool = False,
|
| 272 |
**kwargs
|
| 273 |
) -> AsyncGenerator[Union[str, ImageResponseData], None]:
|
| 274 |
"""
|
|
|
|
| 278 |
model (str): Model to use for generating responses.
|
| 279 |
messages (List[Dict[str, str]]): Message history.
|
| 280 |
proxy (Optional[str]): Proxy URL, if needed.
|
| 281 |
+
image (Any): Image data, if applicable.
|
| 282 |
+
image_name (Optional[str]): Image name, if applicable.
|
| 283 |
+
webSearchMode (bool): Enables or disables web search mode.
|
| 284 |
**kwargs: Additional keyword arguments.
|
| 285 |
|
| 286 |
Yields:
|
|
|
|
| 350 |
"trendingAgentMode": trending_agent_mode,
|
| 351 |
"isMicMode": False,
|
| 352 |
"userSystemPrompt": None,
|
| 353 |
+
"maxTokens": 99999999,
|
| 354 |
"playgroundTopP": 0.9,
|
| 355 |
"playgroundTemperature": 0.5,
|
| 356 |
"isChromeExt": False,
|
|
|
|
| 360 |
"clickedForceWebSearch": False,
|
| 361 |
"visitFromDelta": False,
|
| 362 |
"mobileClient": False,
|
| 363 |
+
"webSearchMode": webSearchMode,
|
| 364 |
+
"userSelectedModel": None,
|
| 365 |
}
|
| 366 |
|
| 367 |
+
if model in cls.agentMode:
|
| 368 |
+
payload_api_chat["agentMode"] = cls.agentMode[model]
|
| 369 |
+
elif model in cls.trendingAgentMode:
|
| 370 |
+
payload_api_chat["trendingAgentMode"] = cls.trendingAgentMode[model]
|
| 371 |
+
elif model in cls.userSelectedModel:
|
| 372 |
+
payload_api_chat["userSelectedModel"] = cls.userSelectedModel[model]
|
| 373 |
+
|
| 374 |
+
logger.info(f"Sending request to {cls.api_endpoint} with data (excluding messages).")
|
|
|
|
| 375 |
|
| 376 |
+
timeout = ClientTimeout(total=60) # Set an appropriate timeout
|
| 377 |
+
retry_attempts = 10 # Set the number of retry attempts
|
| 378 |
|
| 379 |
async with ClientSession(headers=common_headers) as session:
|
| 380 |
+
for attempt in range(retry_attempts):
|
| 381 |
+
try:
|
| 382 |
+
async with session.post(cls.api_endpoint, json=payload_api_chat, proxy=proxy) as response:
|
| 383 |
+
response.raise_for_status()
|
| 384 |
+
logger.info(f"Received response with status {response.status}")
|
| 385 |
+
response_text = await response.text()
|
| 386 |
+
cleaned_response = cls.clean_response(response_text)
|
| 387 |
+
|
| 388 |
+
if model in cls.image_models:
|
| 389 |
+
url_match = re.search(r'https://storage\.googleapis\.com/[^\s\)]+', cleaned_response)
|
| 390 |
+
if url_match:
|
| 391 |
+
image_url = url_match.group(0)
|
| 392 |
+
logger.info(f"Image URL found: {image_url}")
|
| 393 |
+
yield ImageResponseData(url=image_url, alt=messages[-1]['content'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
else:
|
| 395 |
+
logger.error("Image URL not found in the response.")
|
| 396 |
+
raise Exception("Image URL not found in the response")
|
| 397 |
else:
|
| 398 |
+
full_response = ""
|
| 399 |
+
search_results_json = ""
|
| 400 |
+
# Handle streaming-like responses if applicable
|
| 401 |
+
# Assuming the response contains '$~~~$' for search results
|
| 402 |
+
if webSearchMode:
|
| 403 |
+
match = re.search(r'\$~~~\$(.*?)\$~~~\$', cleaned_response, re.DOTALL)
|
| 404 |
+
if match:
|
| 405 |
+
search_results_json = match.group(1)
|
| 406 |
+
answer_part = cleaned_response[:match.start()].strip()
|
| 407 |
+
try:
|
| 408 |
+
search_results = json.loads(search_results_json)
|
| 409 |
+
formatted_results = "\n\n**Sources:**\n"
|
| 410 |
+
for i, result in enumerate(search_results[:5], 1):
|
| 411 |
+
formatted_results += f"{i}. [{result['title']}]({result['link']})\n"
|
| 412 |
+
final_response = f"{answer_part}\n\n{formatted_results}"
|
| 413 |
+
yield final_response
|
| 414 |
+
except json.JSONDecodeError as je:
|
| 415 |
+
logger.error("Failed to parse search results JSON.")
|
| 416 |
+
yield f"{cleaned_response}\n\n**Sources:** Information unavailable."
|
| 417 |
+
else:
|
| 418 |
+
yield cleaned_response
|
| 419 |
else:
|
| 420 |
+
yield cleaned_response
|
| 421 |
+
break # Exit the retry loop if successful
|
| 422 |
+
except ClientError as ce:
|
| 423 |
+
logger.error(f"Client error occurred: {ce}. Retrying attempt {attempt + 1}/{retry_attempts}")
|
| 424 |
+
if attempt == retry_attempts - 1:
|
| 425 |
+
raise HTTPException(status_code=502, detail="Error communicating with the external API.")
|
| 426 |
+
except asyncio.TimeoutError:
|
| 427 |
+
logger.error(f"Request timed out. Retrying attempt {attempt + 1}/{retry_attempts}")
|
| 428 |
+
if attempt == retry_attempts - 1:
|
| 429 |
+
raise HTTPException(status_code=504, detail="External API request timed out.")
|
| 430 |
+
except Exception as e:
|
| 431 |
+
logger.error(f"Unexpected error: {e}. Retrying attempt {attempt + 1}/{retry_attempts}")
|
| 432 |
+
if attempt == retry_attempts - 1:
|
| 433 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 434 |
|
| 435 |
+
# Additional request to /chat/{chat_id} endpoint if necessary
|
| 436 |
chat_url = f'{cls.url}/chat/{chat_id}?model={model}'
|
| 437 |
|
| 438 |
+
headers_chat = {
|
| 439 |
+
'Accept': 'text/x-component',
|
| 440 |
+
'Content-Type': 'text/plain;charset=UTF-8',
|
| 441 |
+
'Referer': f'{cls.url}/chat/{chat_id}?model={model}',
|
| 442 |
+
'next-action': cls.generate_next_action(),
|
| 443 |
+
'next-router-state-tree': cls.generate_next_router_state_tree(),
|
| 444 |
+
'next-url': '/'
|
| 445 |
+
}
|
| 446 |
+
headers_chat_combined = {**common_headers, **headers_chat}
|
| 447 |
+
data_chat = '[]'
|
| 448 |
+
|
| 449 |
try:
|
| 450 |
+
async with session.post(chat_url, headers=headers_chat_combined, data=data_chat, proxy=proxy) as response_chat:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
response_chat.raise_for_status()
|
| 452 |
+
logger.info(f"Successfully posted to {chat_url}")
|
| 453 |
except ClientResponseError as e:
|
| 454 |
error_text = f"Error {e.status}: {e.message}"
|
| 455 |
try:
|
|
|
|
| 458 |
error_text += f" - {cleaned_error}"
|
| 459 |
except Exception:
|
| 460 |
pass
|
| 461 |
+
logger.error(error_text)
|
| 462 |
yield error_text
|
| 463 |
except Exception as e:
|
| 464 |
+
error_text = f"Unexpected error during /chat/{chat_id} request: {str(e)}"
|
| 465 |
+
logger.error(error_text)
|
| 466 |
+
yield error_text
|
| 467 |
|
| 468 |
# FastAPI app setup
|
| 469 |
app = FastAPI()
|
|
|
|
| 515 |
frequency_penalty: Optional[float] = 0.0
|
| 516 |
logit_bias: Optional[Dict[str, float]] = None
|
| 517 |
user: Optional[str] = None
|
| 518 |
+
webSearchMode: Optional[bool] = False # Custom parameter
|
| 519 |
|
| 520 |
class TokenizerRequest(BaseModel):
|
| 521 |
text: str
|
|
|
|
| 566 |
async_generator = Blackbox.create_async_generator(
|
| 567 |
model=request.model,
|
| 568 |
messages=[{"role": msg.role, "content": msg.content} for msg in request.messages], # Actual message content used here
|
| 569 |
+
webSearchMode=request.webSearchMode
|
| 570 |
)
|
| 571 |
|
| 572 |
if request.stream:
|
|
|
|
| 576 |
async for chunk in async_generator:
|
| 577 |
if isinstance(chunk, ImageResponseData):
|
| 578 |
# Handle image responses if necessary
|
| 579 |
+
image_markdown = f"\n"
|
| 580 |
assistant_content += image_markdown
|
| 581 |
response_chunk = create_response(image_markdown, request.model, finish_reason=None)
|
| 582 |
else:
|
|
|
|
| 641 |
response_content = ""
|
| 642 |
async for chunk in async_generator:
|
| 643 |
if isinstance(chunk, ImageResponseData):
|
| 644 |
+
response_content += f"\n"
|
| 645 |
else:
|
| 646 |
response_content += chunk
|
| 647 |
|