test24 / api /utils.py
Niansuh's picture
Update api/utils.py
7a158c1 verified
raw
history blame
9.12 kB
from datetime import datetime
import json
from typing import Any, Dict, Optional
import httpx
import aiohttp
from api.config import (
DDG_API_ENDPOINT,
DDG_STATUS_URL,
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__)
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:
vqd = response.headers.get("x-vqd-4")
if not vqd:
logger.error("VQD token not found in response headers.")
else:
logger.debug(f"VQD token retrieved: {vqd}")
return vqd
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 message_to_dict_ddg(message):
if isinstance(message.content, str):
return {"role": message.role, "content": message.content}
else:
# If DDG API does not support non-string content, raise an error
raise ValueError("Message content must be a string.")
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
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,
}
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 and referer_url are not required unless specified by DDG API
# Obtain VQD token
vqd = await get_ddg_vqd()
if not vqd:
raise HTTPException(status_code=500, detail="Failed to obtain VQD token")
# Build headers
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': vqd
}
# Construct message history
try:
message_history = [message_to_dict_ddg(msg) for msg in request.messages]
except ValueError as ve:
logger.error(f"Invalid message format: {ve}")
raise HTTPException(status_code=400, detail=str(ve))
json_data = {
"model": MODEL_MAPPING.get(request.model, request.model),
"messages": message_history,
"previewToken": None,
"userId": None,
"codeModelMode": True,
"agentMode": agent_mode,
"trendingAgentMode": trending_agent_mode,
"isMicMode": False,
"userSystemPrompt": None,
"maxTokens": request.max_tokens,
"playgroundTopP": request.top_p,
"playgroundTemperature": request.temperature,
"isChromeExt": False,
"githubToken": None,
"clickedAnswer2": False,
"clickedAnswer3": False,
"clickedForceWebSearch": False,
"visitFromDelta": False,
"mobileClient": False,
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
}
logger.debug(f"Sending JSON payload to DDG API: {json.dumps(json_data)}")
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, "")
# Obtain VQD token
vqd = await get_ddg_vqd()
if not vqd:
raise HTTPException(status_code=500, detail="Failed to obtain VQD token")
# Build headers
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': vqd
}
# Construct message history
try:
message_history = [message_to_dict_ddg(msg) for msg in request.messages]
except ValueError as ve:
logger.error(f"Invalid message format: {ve}")
raise HTTPException(status_code=400, detail=str(ve))
json_data = {
"model": MODEL_MAPPING.get(request.model, request.model),
"messages": message_history,
"previewToken": None,
"userId": None,
"codeModelMode": True,
"agentMode": agent_mode,
"trendingAgentMode": trending_agent_mode,
"isMicMode": False,
"userSystemPrompt": None,
"maxTokens": request.max_tokens,
"playgroundTopP": request.top_p,
"playgroundTemperature": request.temperature,
"isChromeExt": False,
"githubToken": None,
"clickedAnswer2": False,
"clickedAnswer3": False,
"clickedForceWebSearch": False,
"visitFromDelta": False,
"mobileClient": False,
"userSelectedModel": MODEL_MAPPING.get(request.model, request.model),
}
logger.debug(f"Sending JSON payload to DDG API: {json.dumps(json_data)}")
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:]
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,
}