PerplexityLabs.py 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. from __future__ import annotations
  2. import random
  3. import json
  4. from ..typing import AsyncResult, Messages
  5. from ..requests import StreamSession, raise_for_status
  6. from ..providers.response import FinishReason
  7. from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
  8. API_URL = "https://www.perplexity.ai/socket.io/"
  9. WS_URL = "wss://www.perplexity.ai/socket.io/"
  10. class PerplexityLabs(AsyncGeneratorProvider, ProviderModelMixin):
  11. url = "https://labs.perplexity.ai"
  12. working = True
  13. default_model = "sonar-pro"
  14. models = [
  15. "sonar",
  16. default_model,
  17. "sonar-reasoning",
  18. "sonar-reasoning-pro",
  19. ]
  20. @classmethod
  21. async def create_async_generator(
  22. cls,
  23. model: str,
  24. messages: Messages,
  25. proxy: str = None,
  26. **kwargs
  27. ) -> AsyncResult:
  28. headers = {
  29. "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
  30. "Accept": "*/*",
  31. "Accept-Language": "de,en-US;q=0.7,en;q=0.3",
  32. "Accept-Encoding": "gzip, deflate, br",
  33. "Origin": cls.url,
  34. "Connection": "keep-alive",
  35. "Referer": f"{cls.url}/",
  36. "Sec-Fetch-Dest": "empty",
  37. "Sec-Fetch-Mode": "cors",
  38. "Sec-Fetch-Site": "same-site",
  39. "TE": "trailers",
  40. }
  41. async with StreamSession(headers=headers, proxies={"all": proxy}) as session:
  42. t = format(random.getrandbits(32), "08x")
  43. async with session.get(
  44. f"{API_URL}?EIO=4&transport=polling&t={t}"
  45. ) as response:
  46. await raise_for_status(response)
  47. text = await response.text()
  48. assert text.startswith("0")
  49. sid = json.loads(text[1:])["sid"]
  50. post_data = '40{"jwt":"anonymous-ask-user"}'
  51. async with session.post(
  52. f"{API_URL}?EIO=4&transport=polling&t={t}&sid={sid}",
  53. data=post_data
  54. ) as response:
  55. await raise_for_status(response)
  56. assert await response.text() == "OK"
  57. async with session.ws_connect(f"{WS_URL}?EIO=4&transport=websocket&sid={sid}", autoping=False) as ws:
  58. await ws.send_str("2probe")
  59. assert(await ws.receive_str() == "3probe")
  60. await ws.send_str("5")
  61. assert(await ws.receive_str())
  62. assert(await ws.receive_str() == "6")
  63. message_data = {
  64. "version": "2.16",
  65. "source": "default",
  66. "model": model,
  67. "messages": messages
  68. }
  69. await ws.send_str("42" + json.dumps(["perplexity_labs", message_data]))
  70. last_message = 0
  71. while True:
  72. message = await ws.receive_str()
  73. if message == "2":
  74. if last_message == 0:
  75. raise RuntimeError("Unknown error")
  76. await ws.send_str("3")
  77. continue
  78. try:
  79. data = json.loads(message[2:])[1]
  80. yield data["output"][last_message:]
  81. last_message = len(data["output"])
  82. if data["final"]:
  83. yield FinishReason("stop")
  84. break
  85. except Exception as e:
  86. print(f"Error processing message: {message} - {e}")
  87. raise RuntimeError(f"Message: {message}") from e