summaryrefslogblamecommitdiffstats
path: root/g4f/Provider/DDG.py
blob: 91ccde32d5c173f8b4f381035c2d9e6b2748c945 (plain) (tree)



















                                                                                      

                                          
                                                                                                                                 
                     

                                










                                                                                                             
                                                                                                                                        
















































































                                                                                                                    
from __future__ import annotations

import json
import aiohttp
import asyncio
from typing import Optional
import base64

from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
from .helper import get_connector
from ..typing import AsyncResult, Messages
from ..requests.raise_for_status import raise_for_status
from ..providers.conversation import BaseConversation

class DDG(AsyncGeneratorProvider, ProviderModelMixin):
    url = base64.b64decode("aHR0cHM6Ly9kdWNrZHVja2dvLmNvbS9haWNoYXQ=").decode("utf-8")
    working = True
    supports_gpt_35_turbo = True
    supports_message_history = True

    # default_model = "gpt-3.5-turbo-0125"
    default_model = "gpt-4o-mini"
    models = ["gpt-4o-mini", "claude-3-haiku-20240307", "meta-llama/Llama-3-70b-chat-hf", "mistralai/Mixtral-8x7B-Instruct-v0.1"]
    model_aliases = {
        "gpt-4": "gpt-4o-mini",
        "gpt-4o": "gpt-4o-mini",
        "claude-3-haiku": "claude-3-haiku-20240307",
        "llama-3-70b": "meta-llama/Llama-3-70b-chat-hf",
        "mixtral-8x7B": "mistralai/Mixtral-8x7B-Instruct-v0.1"
    }

    # Obfuscated URLs and headers
    status_url = base64.b64decode("aHR0cHM6Ly9kdWNrZHVja2dvLmNvbS9kdWNrY2hhdC92MS9zdGF0dXM=").decode("utf-8")
    chat_url = base64.b64decode("aHR0cHM6Ly9kdWNrZHVja2dvLmNvbS9kdWNrY2hhdC92MS9jaGF0").decode("utf-8")
    referer = base64.b64decode("aHR0cHM6Ly9kdWNrZHVja2dvLmNvbS8=").decode("utf-8")
    origin = base64.b64decode("aHR0cHM6Ly9kdWNrZHVja2dvLmNvbQ==").decode("utf-8")
    
    user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
    headers = {
        'User-Agent': user_agent,
        'Accept': 'text/event-stream',
        'Accept-Language': 'en-US,en;q=0.5',
        'Accept-Encoding': 'gzip, deflate, br, zstd',
        'Referer': referer,
        'Content-Type': 'application/json',
        'Origin': origin,
        'Connection': 'keep-alive',
        'Cookie': 'dcm=3',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-origin',
        'Pragma': 'no-cache',
        'TE': 'trailers'
    }

    @classmethod
    async def get_vqd(cls, session: aiohttp.ClientSession) -> Optional[str]:
        try:
            async with session.get(cls.status_url, headers={"x-vqd-accept": "1"}) as response:
                await raise_for_status(response)
                return response.headers.get("x-vqd-4")
        except Exception as e:
            print(f"Error getting VQD: {e}")
            return None

    @classmethod
    async def create_async_generator(
        cls,
        model: str,
        messages: Messages,
        proxy: str = None,
        connector: aiohttp.BaseConnector = None,
        conversation: Conversation = None,
        return_conversation: bool = False,
        **kwargs
    ) -> AsyncResult:
        async with aiohttp.ClientSession(headers=cls.headers, connector=get_connector(connector, proxy)) as session:
            vqd_4 = None
            if conversation is not None and len(messages) > 1:
                vqd_4 = conversation.vqd_4
                messages = [*conversation.messages, messages[-2], messages[-1]]
            else:
                for _ in range(3):  # Try up to 3 times to get a valid VQD
                    vqd_4 = await cls.get_vqd(session)
                    if vqd_4:
                        break
                    await asyncio.sleep(1)  # Wait a bit before retrying
                
                if not vqd_4:
                    raise Exception("Failed to obtain a valid VQD token")
                
                messages = [messages[-1]]  # Only use the last message for new conversations
            
            payload = {
                'model': cls.get_model(model),
                'messages': [{'role': m['role'], 'content': m['content']} for m in messages]
            }
            
            async with session.post(cls.chat_url, json=payload, headers={"x-vqd-4": vqd_4}) as response:
                await raise_for_status(response)
                if return_conversation:
                    yield Conversation(vqd_4, messages)
                
                async for line in response.content:
                    if line.startswith(b"data: "):
                        chunk = line[6:]
                        if chunk.startswith(b"[DONE]"):
                            break
                        try:
                            data = json.loads(chunk)
                            if "message" in data and data["message"]:
                                yield data["message"]
                        except json.JSONDecodeError:
                            print(f"Failed to decode JSON: {chunk}")

class Conversation(BaseConversation):
    def __init__(self, vqd_4: str, messages: Messages) -> None:
        self.vqd_4 = vqd_4
        self.messages = messages