DDG.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. from __future__ import annotations
  2. import json
  3. import aiohttp
  4. from aiohttp import ClientSession
  5. from ..typing import AsyncResult, Messages
  6. from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
  7. from .helper import format_prompt
  8. class DDG(AsyncGeneratorProvider, ProviderModelMixin):
  9. url = "https://duckduckgo.com"
  10. api_endpoint = "https://duckduckgo.com/duckchat/v1/chat"
  11. working = True
  12. supports_stream = True
  13. supports_system_message = True
  14. supports_message_history = True
  15. default_model = "gpt-4o-mini"
  16. models = [
  17. "gpt-4o-mini",
  18. "claude-3-haiku-20240307",
  19. "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
  20. "mistralai/Mixtral-8x7B-Instruct-v0.1"
  21. ]
  22. model_aliases = {
  23. "claude-3-haiku": "claude-3-haiku-20240307",
  24. "llama-3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
  25. "mixtral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1"
  26. }
  27. @classmethod
  28. def get_model(cls, model: str) -> str:
  29. return cls.model_aliases.get(model, model) if model in cls.model_aliases else cls.default_model
  30. @classmethod
  31. async def get_vqd(cls):
  32. status_url = "https://duckduckgo.com/duckchat/v1/status"
  33. headers = {
  34. 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
  35. 'Accept': 'text/event-stream',
  36. 'x-vqd-accept': '1'
  37. }
  38. async with aiohttp.ClientSession() as session:
  39. try:
  40. async with session.get(status_url, headers=headers) as response:
  41. if response.status == 200:
  42. return response.headers.get("x-vqd-4")
  43. else:
  44. print(f"Error: Status code {response.status}")
  45. return None
  46. except Exception as e:
  47. print(f"Error getting VQD: {e}")
  48. return None
  49. @classmethod
  50. async def create_async_generator(
  51. cls,
  52. model: str,
  53. messages: Messages,
  54. conversation: dict = None,
  55. proxy: str = None,
  56. **kwargs
  57. ) -> AsyncResult:
  58. model = cls.get_model(model)
  59. headers = {
  60. 'accept': 'text/event-stream',
  61. 'content-type': 'application/json',
  62. 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
  63. }
  64. vqd = conversation.get('vqd') if conversation else await cls.get_vqd()
  65. if not vqd:
  66. raise Exception("Failed to obtain VQD token")
  67. headers['x-vqd-4'] = vqd
  68. if conversation:
  69. message_history = conversation.get('messages', [])
  70. message_history.append({"role": "user", "content": format_prompt(messages)})
  71. else:
  72. message_history = [{"role": "user", "content": format_prompt(messages)}]
  73. async with ClientSession(headers=headers) as session:
  74. data = {
  75. "model": model,
  76. "messages": message_history
  77. }
  78. async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
  79. response.raise_for_status()
  80. async for line in response.content:
  81. if line:
  82. decoded_line = line.decode('utf-8')
  83. if decoded_line.startswith('data: '):
  84. json_str = decoded_line[6:]
  85. if json_str == '[DONE]':
  86. break
  87. try:
  88. json_data = json.loads(json_str)
  89. if 'message' in json_data:
  90. yield json_data['message']
  91. except json.JSONDecodeError:
  92. pass