DuckDuckGo.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. from __future__ import annotations
  2. import asyncio
  3. try:
  4. from duckduckgo_search import DDGS
  5. from duckduckgo_search.exceptions import DuckDuckGoSearchException, RatelimitException, ConversationLimitException
  6. has_requirements = True
  7. except ImportError:
  8. has_requirements = False
  9. try:
  10. import nodriver
  11. has_nodriver = True
  12. except ImportError:
  13. has_nodriver = False
  14. from ..typing import AsyncResult, Messages
  15. from ..requests import get_nodriver
  16. from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
  17. from .helper import get_last_user_message
  18. class DuckDuckGo(AsyncGeneratorProvider, ProviderModelMixin):
  19. label = "Duck.ai (duckduckgo_search)"
  20. url = "https://duckduckgo.com/aichat"
  21. api_base = "https://duckduckgo.com/duckchat/v1/"
  22. working = False
  23. supports_stream = True
  24. supports_system_message = True
  25. supports_message_history = True
  26. default_model = "gpt-4o-mini"
  27. models = [default_model, "meta-llama/Llama-3.3-70B-Instruct-Turbo", "claude-3-haiku-20240307", "o3-mini", "mistralai/Mistral-Small-24B-Instruct-2501"]
  28. ddgs: DDGS = None
  29. model_aliases = {
  30. "gpt-4": "gpt-4o-mini",
  31. "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
  32. "claude-3-haiku": "claude-3-haiku-20240307",
  33. "mixtral-small-24b": "mistralai/Mistral-Small-24B-Instruct-2501",
  34. }
  35. @classmethod
  36. async def create_async_generator(
  37. cls,
  38. model: str,
  39. messages: Messages,
  40. proxy: str = None,
  41. timeout: int = 60,
  42. **kwargs
  43. ) -> AsyncResult:
  44. if not has_requirements:
  45. raise ImportError("duckduckgo_search is not installed. Install it with `pip install duckduckgo-search`.")
  46. if cls.ddgs is None:
  47. cls.ddgs = DDGS(proxy=proxy, timeout=timeout)
  48. if has_nodriver:
  49. await cls.nodriver_auth(proxy=proxy)
  50. model = cls.get_model(model)
  51. for chunk in cls.ddgs.chat_yield(get_last_user_message(messages), model, timeout):
  52. yield chunk
  53. @classmethod
  54. async def nodriver_auth(cls, proxy: str = None):
  55. browser, stop_browser = await get_nodriver(proxy=proxy)
  56. try:
  57. page = browser.main_tab
  58. def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
  59. if cls.api_base in event.request.url:
  60. if "X-Vqd-4" in event.request.headers:
  61. cls.ddgs._chat_vqd = event.request.headers["X-Vqd-4"]
  62. if "X-Vqd-Hash-1" in event.request.headers:
  63. cls.ddgs._chat_vqd_hash = event.request.headers["X-Vqd-Hash-1"]
  64. if "F-Fe-Version" in event.request.headers:
  65. cls.ddgs._chat_xfe = event.request.headers["F-Fe-Version" ]
  66. await page.send(nodriver.cdp.network.enable())
  67. page.add_handler(nodriver.cdp.network.RequestWillBeSent, on_request)
  68. page = await browser.get(cls.url)
  69. while True:
  70. if cls.ddgs._chat_vqd:
  71. break
  72. await asyncio.sleep(1)
  73. await page.close()
  74. finally:
  75. stop_browser()