test24 / api /utils.py
Niansuh's picture
Update api/utils.py
21339c1 verified
raw
history blame
8.25 kB
from datetime import datetime
import json
from typing import Any, Dict, Optional
import httpx
import aiohttp # Add this import
from api.config import (
DDG_API_ENDPOINT, # Updated
DDG_STATUS_URL, # Updated
MODEL_MAPPING,
ALLOWED_MODELS,
AGENT_MODE,
TRENDING_AGENT_MODE,
MODEL_PREFIXES,
MODEL_REFERERS
)
from fastapi import HTTPException
from api.models import ChatRequest
from api.logger import setup_logger
import uuid
logger = setup_logger(__name__)
# Remove Blackbox-specific functions and add DDG-specific functions
# New DDG Integration
async def get_ddg_vqd():
status_url = DDG_STATUS_URL
headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
'Accept': 'text/event-stream',
'x-vqd-accept': '1'
}
async with aiohttp.ClientSession() as session:
try:
async with session.get(status_url, headers=headers) as response:
if response.status == 200:
return response.headers.get("x-vqd-4")
else:
logger.error(f"Error: Status code {response.status} when fetching VQD")
return None
except Exception as e:
logger.error(f"Error getting VQD: {e}")
return None
def format_prompt(messages):
"""Helper function to format messages into a prompt string."""
return "\n".join([f"{msg.role}: {msg.content}" for msg in messages])
def message_to_dict_ddg(message, model_prefix: Optional[str] = None):
if isinstance(message.content, str):
content = message.content
if model_prefix:
content = f"{model_prefix} {content}"
return {"role": message.role, "content": content}
elif isinstance(message.content, list) and len(message.content) == 2:
content = message.content[0]["text"]
if model_prefix:
content = f"{model_prefix} {content}"
return {
"role": message.role,
"content": content,
"data": {
"imageBase64": message.content[1]["image_url"]["url"],
"fileText": "",
"title": "snapshot",
},
}
else:
return {"role": message.role, "content": message.content}
def strip_model_prefix(content: str, model_prefix: Optional[str] = None) -> str:
"""Remove the model prefix from the response content if present."""
if model_prefix and content.startswith(model_prefix):
logger.debug(f"Stripping prefix '{model_prefix}' from content.")
return content[len(model_prefix):].strip()
logger.debug("No prefix to strip from content.")
return content
async def process_ddg_streaming_response(request: ChatRequest):
agent_mode = AGENT_MODE.get(request.model, {})
trending_agent_mode = TRENDING_AGENT_MODE.get(request.model, {})
model_prefix = MODEL_PREFIXES.get(request.model, "")
referer_path = MODEL_REFERERS.get(request.model, f"/?model={request.model}")
referer_url = f"https://duckduckgo.com{referer_path}" # Updated BASE_URL
# Update headers with dynamic Referer
dynamic_headers = {
'accept': 'text/event-stream',
'content-type': 'application/json',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
'x-vqd-4': await get_ddg_vqd()
}
if not dynamic_headers['x-vqd-4']:
raise HTTPException(status_code=500, detail="Failed to obtain VQD token")
json_data = {
"model": MODEL_MAPPING.get(request.model, request.model),
"messages": [message_to_dict_ddg(msg, model_prefix=model_prefix) for msg in request.messages],
# Add additional fields if required by DDG API
}
async with httpx.AsyncClient() as client:
try:
async with client.stream(
"POST",
DDG_API_ENDPOINT,
headers=dynamic_headers,
json=json_data,
timeout=100,
) as response:
response.raise_for_status()
async for line in response.aiter_lines():
timestamp = int(datetime.now().timestamp())
if line:
content = line
if content.startswith("$@$v=undefined-rv1$@$"):
content = content[21:]
# Strip the model prefix from the response content
cleaned_content = strip_model_prefix(content, model_prefix)
yield f"data: {json.dumps(create_chat_completion_data(cleaned_content, request.model, timestamp))}\n\n"
yield f"data: {json.dumps(create_chat_completion_data('', request.model, timestamp, 'stop'))}\n\n"
yield "data: [DONE]\n\n"
except httpx.HTTPStatusError as e:
logger.error(f"HTTP error occurred: {e}")
raise HTTPException(status_code=e.response.status_code, detail=str(e))
except httpx.RequestError as e:
logger.error(f"Error occurred during request: {e}")
raise HTTPException(status_code=500, detail=str(e))
async def process_ddg_non_streaming_response(request: ChatRequest):
agent_mode = AGENT_MODE.get(request.model, {})
trending_agent_mode = TRENDING_AGENT_MODE.get(request.model, {})
model_prefix = MODEL_PREFIXES.get(request.model, "")
referer_path = MODEL_REFERERS.get(request.model, f"/?model={request.model}")
referer_url = f"https://duckduckgo.com{referer_path}" # Updated BASE_URL
# Update headers with dynamic Referer
dynamic_headers = {
'accept': 'application/json',
'content-type': 'application/json',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
'x-vqd-4': await get_ddg_vqd()
}
if not dynamic_headers['x-vqd-4']:
raise HTTPException(status_code=500, detail="Failed to obtain VQD token")
json_data = {
"model": MODEL_MAPPING.get(request.model, request.model),
"messages": [message_to_dict_ddg(msg, model_prefix=model_prefix) for msg in request.messages],
# Add additional fields if required by DDG API
}
full_response = ""
async with httpx.AsyncClient() as client:
try:
response = await client.post(
DDG_API_ENDPOINT,
headers=dynamic_headers,
json=json_data,
timeout=100
)
response.raise_for_status()
full_response = response.text
except httpx.HTTPStatusError as e:
logger.error(f"HTTP error occurred: {e}")
raise HTTPException(status_code=e.response.status_code, detail=str(e))
except httpx.RequestError as e:
logger.error(f"Error occurred during request: {e}")
raise HTTPException(status_code=500, detail=str(e))
if full_response.startswith("$@$v=undefined-rv1$@$"):
full_response = full_response[21:]
# Strip the model prefix from the full response
cleaned_full_response = strip_model_prefix(full_response, model_prefix)
return {
"id": f"chatcmpl-{uuid.uuid4()}",
"object": "chat.completion",
"created": int(datetime.now().timestamp()),
"model": request.model,
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": cleaned_full_response},
"finish_reason": "stop",
}
],
"usage": None,
}
def create_chat_completion_data(
content: str, model: str, timestamp: int, finish_reason: Optional[str] = None
) -> Dict[str, Any]:
return {
"id": f"chatcmpl-{uuid.uuid4()}",
"object": "chat.completion.chunk",
"created": timestamp,
"model": model,
"choices": [
{
"index": 0,
"delta": {"content": content, "role": "assistant"},
"finish_reason": finish_reason,
}
],
"usage": None,
}