webscout 6.4__py3-none-any.whl → 6.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (116) hide show
  1. webscout/AIutel.py +7 -54
  2. webscout/DWEBS.py +48 -26
  3. webscout/{YTdownloader.py → Extra/YTToolkit/YTdownloader.py} +990 -1103
  4. webscout/Extra/YTToolkit/__init__.py +3 -0
  5. webscout/{transcriber.py → Extra/YTToolkit/transcriber.py} +1 -1
  6. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  7. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  8. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  9. webscout/Extra/YTToolkit/ytapi/extras.py +45 -0
  10. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  11. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  12. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  13. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  14. webscout/Extra/YTToolkit/ytapi/query.py +37 -0
  15. webscout/Extra/YTToolkit/ytapi/stream.py +60 -0
  16. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  17. webscout/Extra/YTToolkit/ytapi/video.py +102 -0
  18. webscout/Extra/__init__.py +2 -1
  19. webscout/Extra/autocoder/autocoder_utiles.py +119 -101
  20. webscout/Extra/autocoder/rawdog.py +679 -680
  21. webscout/Extra/gguf.py +441 -441
  22. webscout/Extra/markdownlite/__init__.py +862 -0
  23. webscout/Extra/weather_ascii.py +2 -2
  24. webscout/Provider/AISEARCH/__init__.py +2 -0
  25. webscout/Provider/AISEARCH/ooai.py +155 -0
  26. webscout/Provider/Amigo.py +70 -85
  27. webscout/Provider/{prefind.py → Jadve.py} +72 -70
  28. webscout/Provider/Netwrck.py +235 -0
  29. webscout/Provider/Openai.py +4 -3
  30. webscout/Provider/PI.py +292 -221
  31. webscout/Provider/PizzaGPT.py +3 -3
  32. webscout/Provider/Reka.py +0 -1
  33. webscout/Provider/TTS/__init__.py +5 -1
  34. webscout/Provider/TTS/deepgram.py +183 -0
  35. webscout/Provider/TTS/elevenlabs.py +137 -0
  36. webscout/Provider/TTS/gesserit.py +151 -0
  37. webscout/Provider/TTS/murfai.py +139 -0
  38. webscout/Provider/TTS/parler.py +134 -107
  39. webscout/Provider/TTS/streamElements.py +360 -275
  40. webscout/Provider/TTS/utils.py +280 -0
  41. webscout/Provider/TTS/voicepod.py +116 -116
  42. webscout/Provider/TeachAnything.py +15 -2
  43. webscout/Provider/Youchat.py +42 -8
  44. webscout/Provider/__init__.py +8 -21
  45. webscout/Provider/meta.py +794 -779
  46. webscout/Provider/multichat.py +230 -0
  47. webscout/Provider/promptrefine.py +2 -2
  48. webscout/Provider/talkai.py +10 -13
  49. webscout/Provider/turboseek.py +5 -4
  50. webscout/Provider/tutorai.py +8 -112
  51. webscout/Provider/typegpt.py +5 -7
  52. webscout/Provider/x0gpt.py +81 -9
  53. webscout/Provider/yep.py +123 -361
  54. webscout/__init__.py +33 -28
  55. webscout/conversation.py +24 -9
  56. webscout/exceptions.py +188 -20
  57. webscout/litprinter/__init__.py +719 -831
  58. webscout/litprinter/colors.py +54 -0
  59. webscout/optimizers.py +420 -270
  60. webscout/prompt_manager.py +279 -279
  61. webscout/scout/__init__.py +8 -0
  62. webscout/scout/core/__init__.py +7 -0
  63. webscout/scout/core/crawler.py +140 -0
  64. webscout/scout/core/scout.py +571 -0
  65. webscout/scout/core/search_result.py +96 -0
  66. webscout/scout/core/text_analyzer.py +63 -0
  67. webscout/scout/core/text_utils.py +277 -0
  68. webscout/scout/core/web_analyzer.py +52 -0
  69. webscout/scout/core.py +884 -0
  70. webscout/scout/element.py +460 -0
  71. webscout/scout/parsers/__init__.py +69 -0
  72. webscout/scout/parsers/html5lib_parser.py +172 -0
  73. webscout/scout/parsers/html_parser.py +236 -0
  74. webscout/scout/parsers/lxml_parser.py +178 -0
  75. webscout/scout/utils.py +38 -0
  76. webscout/update_checker.py +184 -125
  77. webscout/version.py +1 -1
  78. webscout/zeroart/__init__.py +55 -0
  79. webscout/zeroart/base.py +60 -0
  80. webscout/zeroart/effects.py +99 -0
  81. webscout/zeroart/fonts.py +816 -0
  82. webscout/zerodir/__init__.py +225 -0
  83. {webscout-6.4.dist-info → webscout-6.6.dist-info}/METADATA +18 -231
  84. webscout-6.6.dist-info/RECORD +197 -0
  85. webscout-6.6.dist-info/top_level.txt +2 -0
  86. webstoken/__init__.py +30 -0
  87. webstoken/classifier.py +189 -0
  88. webstoken/keywords.py +216 -0
  89. webstoken/language.py +128 -0
  90. webstoken/ner.py +164 -0
  91. webstoken/normalizer.py +35 -0
  92. webstoken/processor.py +77 -0
  93. webstoken/sentiment.py +206 -0
  94. webstoken/stemmer.py +73 -0
  95. webstoken/t.py +75 -0
  96. webstoken/tagger.py +60 -0
  97. webstoken/tokenizer.py +158 -0
  98. webscout/Agents/Onlinesearcher.py +0 -182
  99. webscout/Agents/__init__.py +0 -2
  100. webscout/Agents/functioncall.py +0 -248
  101. webscout/Bing_search.py +0 -251
  102. webscout/Provider/Perplexity.py +0 -599
  103. webscout/Provider/RoboCoders.py +0 -206
  104. webscout/Provider/genspark.py +0 -225
  105. webscout/Provider/perplexitylabs.py +0 -265
  106. webscout/Provider/twitterclone.py +0 -251
  107. webscout/Provider/upstage.py +0 -230
  108. webscout/gpt4free.py +0 -666
  109. webscout/requestsHTMLfix.py +0 -775
  110. webscout/webai.py +0 -2590
  111. webscout-6.4.dist-info/RECORD +0 -154
  112. webscout-6.4.dist-info/top_level.txt +0 -1
  113. /webscout/Provider/{felo_search.py → AISEARCH/felo_search.py} +0 -0
  114. {webscout-6.4.dist-info → webscout-6.6.dist-info}/LICENSE.md +0 -0
  115. {webscout-6.4.dist-info → webscout-6.6.dist-info}/WHEEL +0 -0
  116. {webscout-6.4.dist-info → webscout-6.6.dist-info}/entry_points.txt +0 -0
@@ -1,206 +0,0 @@
1
- import requests
2
- import json
3
- from typing import Any, Dict, Optional, Generator, Union
4
-
5
- from webscout.AIutel import Optimizers
6
- from webscout.AIutel import Conversation
7
- from webscout.AIutel import AwesomePrompts
8
- from webscout.AIbase import Provider
9
- from webscout import exceptions
10
-
11
-
12
- class RoboCoders(Provider):
13
- """
14
- A class to interact with the RoboCoders API.
15
- """
16
-
17
- api_endpoint = "https://api.robocoders.ai/chat"
18
- working = True
19
- supports_message_history = True
20
- default_model = "GeneralCodingAgent"
21
- agent = [default_model, "RepoAgent", "FrontEndAgent"]
22
- models = [*agent]
23
-
24
- def __init__(
25
- self,
26
- is_conversation: bool = True,
27
- max_tokens: int = 600,
28
- timeout: int = 30,
29
- intro: str = None,
30
- filepath: str = None,
31
- update_file: bool = True,
32
- proxies: dict = {},
33
- history_offset: int = 10250,
34
- act: str = None,
35
- model: str = default_model,
36
- system_prompt: str = "You are a helpful coding assistant.",
37
- ):
38
- """Initializes the RoboCoders API client."""
39
- if model not in self.models:
40
- raise ValueError(f"Invalid model: {model}. Choose from: {', '.join(self.models)}")
41
-
42
- self.session = requests.Session()
43
- self.is_conversation = is_conversation
44
- self.max_tokens_to_sample = max_tokens
45
- self.timeout = timeout
46
- self.last_response = {}
47
- self.model = model
48
- self.system_prompt = system_prompt
49
-
50
- self.headers = {"Content-Type": "application/json"}
51
- self.access_token = self._get_access_token() # Get token on initialization
52
- if not self.access_token:
53
- raise exceptions.AuthenticationError("Failed to get access token")
54
-
55
- self.session_id = self._create_session() # Create session on initialization
56
- if not self.session_id:
57
- raise exceptions.SessionCreationError("Failed to create session")
58
-
59
- self.headers["Authorization"] = f"Bearer {self.access_token}"
60
-
61
- self.__available_optimizers = (
62
- method
63
- for method in dir(Optimizers)
64
- if callable(getattr(Optimizers, method)) and not method.startswith("__")
65
- )
66
- Conversation.intro = (
67
- AwesomePrompts().get_act(
68
- act, raise_not_found=True, default=None, case_insensitive=True
69
- )
70
- if act
71
- else intro or Conversation.intro
72
- )
73
- self.conversation = Conversation(
74
- is_conversation, self.max_tokens_to_sample, filepath, update_file
75
- )
76
- self.conversation.history_offset = history_offset
77
- self.session.proxies = proxies
78
-
79
- def _get_access_token(self) -> Optional[str]:
80
- """Get access token for authentication."""
81
- url_auth = "https://api.robocoders.ai/auth"
82
- headers_auth = {
83
- "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
84
- "accept-language": "en-US,en;q=0.9",
85
- "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
86
- }
87
- try:
88
- response = self.session.get(url_auth, headers=headers_auth, timeout=self.timeout)
89
- response.raise_for_status() # Raise exception for HTTP errors
90
- text = response.text
91
- return text.split('id="token">')[1].split("</pre>")[0].strip()
92
- except (requests.exceptions.RequestException, IndexError) as e:
93
- raise exceptions.APIConnectionError(f"Failed to get access token: {e}")
94
-
95
-
96
- def _create_session(self) -> Optional[str]:
97
- """Create a new chat session."""
98
- url_create_session = "https://api.robocoders.ai/create-session"
99
- headers_create_session = {"Authorization": f"Bearer {self.access_token}"}
100
- try:
101
- response = self.session.get(url_create_session, headers=headers_create_session, timeout=self.timeout)
102
- response.raise_for_status()
103
- data = response.json()
104
- return data.get("sid")
105
- except requests.exceptions.RequestException as e:
106
- raise exceptions.APIConnectionError(f"Failed to create session: {e}")
107
-
108
-
109
-
110
- def ask(
111
- self,
112
- prompt: str,
113
- stream: bool = False,
114
- raw: bool = False,
115
- optimizer: str = None,
116
- conversationally: bool = False,
117
- ) -> Union[Dict, Generator[str, None, None]]:
118
- """
119
- Sends a prompt to the RoboCoders API and returns the response.
120
- """
121
- conversation_prompt = self.conversation.gen_complete_prompt(prompt)
122
- if optimizer:
123
- if optimizer in self.__available_optimizers:
124
- conversation_prompt = getattr(Optimizers, optimizer)(
125
- conversation_prompt if conversationally else prompt
126
- )
127
- else:
128
- raise exceptions.FailedToGenerateResponseError(
129
- f"Optimizer is not one of {self.__available_optimizers}"
130
- )
131
-
132
- data = {
133
- "sid": self.session_id,
134
- "prompt": conversation_prompt,
135
- "agent": self.model,
136
- }
137
-
138
- def generate_chunks():
139
- response = self._make_request(
140
- "POST", self.api_endpoint, headers=self.headers, json=data, stream=True
141
- )
142
- for line in response.iter_lines():
143
- if line:
144
- try:
145
- response_data = json.loads(line)
146
- message = response_data.get("message", "")
147
- if message:
148
- yield message
149
- except json.JSONDecodeError:
150
- pass # Handle or log the error as needed
151
-
152
- if stream:
153
- streaming_text = ""
154
- for chunk in generate_chunks():
155
- streaming_text += chunk
156
- yield chunk if raw else {"text": chunk}
157
- self.last_response.update({"text": streaming_text}) #Update last_response
158
- self.conversation.update_chat_history(prompt, streaming_text)
159
- else:
160
- full_response = "".join(generate_chunks())
161
- self.last_response.update({"text": full_response})
162
- self.conversation.update_chat_history(prompt, full_response)
163
- return self.last_response
164
-
165
- def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
166
- try:
167
- response = self.session.request(
168
- method, url, timeout=self.timeout, **kwargs
169
- )
170
- response.raise_for_status()
171
- return response
172
- except requests.exceptions.RequestException as e:
173
- raise exceptions.APIConnectionError(f"Failed to make request: {e}") from e
174
-
175
- def chat(
176
- self,
177
- prompt: str,
178
- stream: bool = False,
179
- optimizer: str = None,
180
- conversationally: bool = False,
181
- ) -> Union[str, Generator[str, None, None]]:
182
- """Generate response string or stream."""
183
-
184
- if stream:
185
- gen = self.ask(
186
- prompt, stream=True, optimizer=optimizer, conversationally=conversationally
187
- )
188
- for chunk in gen:
189
- yield self.get_message(chunk)
190
- else:
191
- return self.get_message(
192
- self.ask(prompt, stream=False, optimizer=optimizer, conversationally=conversationally)
193
- )
194
-
195
- def get_message(self, response: Dict[str, Any]) -> str:
196
- """Retrieves message from response."""
197
- assert isinstance(response, dict), "Response should be of dict data-type only"
198
- return response["text"]
199
-
200
-
201
- if __name__ == "__main__":
202
- from rich import print
203
- ai = RoboCoders(model="GeneralCodingAgent")
204
- response = ai.chat("Can you help me with Python programming?", stream=True)
205
- for chunk in response:
206
- print(chunk, end='', flush=True)
@@ -1,225 +0,0 @@
1
- import cloudscraper
2
- from uuid import uuid4
3
- import json
4
- import re
5
- from webscout.AIutel import Optimizers
6
- from webscout.AIutel import Conversation
7
- from webscout.AIutel import AwesomePrompts
8
- from webscout.AIbase import Provider
9
-
10
- class Genspark(Provider):
11
- """
12
- A class to interact with the Genspark.ai API.
13
- """
14
-
15
- def __init__(
16
- self,
17
- is_conversation: bool = True,
18
- max_tokens: int = 600,
19
- timeout: int = 30,
20
- intro: str = None,
21
- filepath: str = None,
22
- update_file: bool = True,
23
- proxies: dict = {},
24
- history_offset: int = 10250,
25
- act: str = None,
26
- ) -> None:
27
- """
28
- Instantiates Genspark
29
-
30
- Args:
31
- is_conversation (bool, optional): Flag for chatting conversationally. Defaults to True.
32
- max_tokens (int, optional): Maximum number of tokens to be generated upon completion. Defaults to 600.
33
- timeout (int, optional): Http request timeout. Defaults to 30.
34
- intro (str, optional): Conversation introductory prompt. Defaults to None.
35
- filepath (str, optional): Path to file containing conversation history. Defaults to None.
36
- update_file (bool, optional): Add new prompts and responses to the file. Defaults to True.
37
- proxies (dict, optional): Http request proxies. Defaults to {}.
38
- history_offset (int, optional): Limit conversation history to this number of last texts. Defaults to 10250.
39
- act (str|int, optional): Awesome prompt key or index. (Used as intro). Defaults to None.
40
- """
41
- self.session = cloudscraper.create_scraper()
42
- self.is_conversation = is_conversation
43
- self.max_tokens_to_sample = max_tokens
44
- self.chat_endpoint = "https://www.genspark.ai/api/search/stream"
45
- self.stream_chunk_size = 64
46
- self.timeout = timeout
47
- self.last_response = {}
48
- self.headers = {
49
- "Accept": "*/*",
50
- "Accept-Encoding": "gzip, deflate, br, zstd",
51
- "Accept-Language": "en-US,en;q=0.9,en-IN;q=0.8",
52
- "Content-Type": "application/json",
53
- "DNT": "1",
54
- "Origin": "https://www.genspark.ai",
55
- "Priority": "u=1, i",
56
- "Sec-CH-UA": '"Chromium";v="128", "Not;A=Brand";v="24", "Microsoft Edge";v="128"',
57
- "Sec-CH-UA-Mobile": "?0",
58
- "Sec-CH-UA-Platform": '"Windows"',
59
- "Sec-Fetch-Dest": "empty",
60
- "Sec-Fetch-Mode": "cors",
61
- "Sec-Fetch-Site": "same-origin",
62
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0",
63
- }
64
- self.cookies = {
65
- "i18n_redirected": "en-US",
66
- "agree_terms": "0",
67
- "session_id": uuid4().hex,
68
- }
69
-
70
- self.__available_optimizers = [
71
- method
72
- for method in dir(Optimizers)
73
- if callable(getattr(Optimizers, method)) and not method.startswith("__")
74
- ]
75
- self.session.headers.update(self.headers)
76
- Conversation.intro = (
77
- AwesomePrompts().get_act(
78
- act, raise_not_found=True, default=None, case_insensitive=True
79
- )
80
- if act
81
- else intro or Conversation.intro
82
- )
83
- self.conversation = Conversation(
84
- is_conversation, self.max_tokens_to_sample, filepath, update_file
85
- )
86
- self.conversation.history_offset = history_offset
87
- self.session.proxies = proxies
88
-
89
- def ask(
90
- self,
91
- prompt: str,
92
- stream: bool = False,
93
- raw: bool = False,
94
- optimizer: str = None,
95
- conversationally: bool = False,
96
- ) -> dict:
97
- """
98
- Chat with AI
99
-
100
- Args:
101
- prompt (str): Prompt to be send.
102
- stream (bool, optional): Flag for streaming response. Defaults to False.
103
- raw (bool, optional): Stream back raw response as received. Defaults to False.
104
- optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
105
- conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
106
-
107
- Returns:
108
- dict : {}
109
- ```json
110
- {
111
- "text" : "How may I assist you today?"
112
- }
113
- ```
114
- """
115
- conversation_prompt = self.conversation.gen_complete_prompt(prompt)
116
- if optimizer:
117
- if optimizer in self.__available_optimizers:
118
- conversation_prompt = getattr(Optimizers, optimizer)(
119
- conversation_prompt if conversationally else prompt
120
- )
121
- else:
122
- raise Exception(
123
- f"Optimizer is not one of {self.__available_optimizers}"
124
- )
125
-
126
- self.url = f"https://www.genspark.ai/api/search/stream?query={conversation_prompt}"
127
-
128
- payload = {}
129
-
130
- def for_stream():
131
- response = self.session.post(
132
- self.url,
133
- headers=self.headers,
134
- cookies=self.cookies,
135
- json=payload,
136
- stream=True,
137
- timeout=self.timeout,
138
- )
139
- if not response.ok:
140
- raise Exception(
141
- f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
142
- )
143
-
144
- full_response = ""
145
- for line in response.iter_lines(decode_unicode=True):
146
- if line:
147
- if line.startswith("data: "):
148
- try:
149
- data = json.loads(line[6:])
150
- if data.get("type") == "result_field" and data["field_name"] == "streaming_summary":
151
- full_response = data.get("field_value", "")
152
- yield full_response if raw else {"text": full_response}
153
- except json.JSONDecodeError as e:
154
- print(f"Error decoding JSON: {line} - {e}")
155
-
156
- self.last_response.update({"text": full_response})
157
- self.conversation.update_chat_history(
158
- prompt, self.get_message(self.last_response)
159
- )
160
-
161
- def for_non_stream():
162
- for _ in for_stream():
163
- pass
164
- return self.last_response
165
-
166
- return for_stream() if stream else for_non_stream()
167
-
168
- def chat(
169
- self,
170
- prompt: str,
171
- stream: bool = False,
172
- optimizer: str = None,
173
- conversationally: bool = False,
174
- ) -> str:
175
- """
176
- Generate response `str`
177
- Args:
178
- prompt (str): Prompt to be send.
179
- stream (bool, optional): Flag for streaming response. Defaults to False.
180
- optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
181
- conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
182
- Returns:
183
- str: Response generated
184
- """
185
-
186
- def for_stream():
187
- for response in self.ask(
188
- prompt, True, optimizer=optimizer, conversationally=conversationally
189
- ):
190
- yield self.get_message(response)
191
-
192
- def for_non_stream():
193
- response = self.ask(prompt, False, optimizer=optimizer, conversationally=conversationally)
194
- return self.get_message(response)
195
-
196
- return for_stream() if stream else for_non_stream()
197
-
198
- def get_message(self, response: dict) -> str:
199
- """
200
- Retrieves message only from response
201
-
202
- Args:
203
- response (dict): Response generated by `self.ask`
204
-
205
- Returns:
206
- str: Message extracted
207
- """
208
- assert isinstance(response, dict), "Response should be of dict data-type only"
209
- text = response.get('text', '')
210
- # Remove footnote references from the text
211
- text = re.sub(r"\[.*?\]\(.*?\)", "", text)
212
- try:
213
- # Attempt to parse the text as JSON
214
- text_json = json.loads(text)
215
- return text_json.get('detailAnswer', text)
216
- except json.JSONDecodeError:
217
- # If text is not JSON, return it as is
218
- return text
219
-
220
- if __name__ == '__main__':
221
- from rich import print
222
- ai = Genspark()
223
- response = ai.chat("tell me about Abhay koul, HelpingAI", stream=True)
224
- for chunk in response:
225
- print(chunk, end="", flush=True)
@@ -1,265 +0,0 @@
1
- import json
2
- import random
3
- import requests
4
- import websocket
5
- from typing import Generator, Union, Optional, Dict, Any
6
-
7
- from webscout.AIutel import Optimizers
8
- from webscout.AIutel import Conversation
9
- from webscout.AIutel import AwesomePrompts
10
- from webscout.AIbase import Provider
11
- from webscout import exceptions
12
-
13
- API_URL = "https://www.perplexity.ai/socket.io/"
14
- WS_URL = "wss://www.perplexity.ai/socket.io/"
15
-
16
- class PerplexityLabs(Provider):
17
- """
18
- A class to interact with the Perplexity Labs API
19
- """
20
- url = "https://labs.perplexity.ai"
21
-
22
- # Models with web search capability
23
- online_models = [
24
- "llama-3.1-sonar-large-128k-online",
25
- "llama-3.1-sonar-small-128k-online",
26
- ]
27
-
28
- # Models for chat/instruct without web search
29
- chat_models = [
30
- "llama-3.1-sonar-large-128k-chat",
31
- "llama-3.1-sonar-small-128k-chat",
32
- "llama-3.1-8b-instruct",
33
- "llama-3.1-70b-instruct",
34
- ]
35
-
36
- def __init__(
37
- self,
38
- is_conversation: bool = True,
39
- max_tokens: int = 600,
40
- timeout: int = 30,
41
- intro: str = None,
42
- filepath: str = None,
43
- update_file: bool = True,
44
- proxies: dict = {},
45
- history_offset: int = 10250,
46
- act: str = None,
47
- model: str = "llama-3.1-sonar-large-128k-online",
48
- system_prompt: str = "You are a helpful AI assistant.",
49
- ):
50
- """Initializes the PerplexityLabs API client."""
51
- self.session = requests.Session()
52
- self.is_conversation = is_conversation
53
- self.max_tokens_to_sample = max_tokens
54
- self.timeout = timeout
55
- self.last_response = {}
56
- self.system_prompt = system_prompt
57
- self.headers = {
58
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
59
- "Accept": "*/*",
60
- "Accept-Language": "en-US,en;q=0.9",
61
- "Accept-Encoding": "gzip, deflate, br",
62
- "Origin": self.url,
63
- "Connection": "keep-alive",
64
- "Referer": f"{self.url}/",
65
- "Sec-Fetch-Dest": "empty",
66
- "Sec-Fetch-Mode": "cors",
67
- "Sec-Fetch-Site": "same-site",
68
- "Pragma": "no-cache",
69
- "Cache-Control": "no-cache",
70
- }
71
- self.session.headers.update(self.headers)
72
- self.proxies = proxies
73
- self.model = model
74
- self.conversation = Conversation(is_conversation, max_tokens, filepath, update_file)
75
- self.conversation.history_offset = history_offset
76
- self.__available_optimizers = ["gpt4", "claude", "gemini", "simple"]
77
-
78
- try:
79
- self.session.get(self.url)
80
- except requests.exceptions.RequestException as e:
81
- raise exceptions.ProviderConnectionError(f"Failed to initialize session: {e}")
82
-
83
-
84
- def _get_session_id(self) -> str:
85
- t = format(random.getrandbits(32), "08x")
86
- try:
87
- response = self.session.get(
88
- f"{API_URL}?EIO=4&transport=polling&t={t}",
89
- proxies=self.proxies
90
- )
91
- response.raise_for_status()
92
- text = response.text
93
-
94
- if not text.startswith("0"):
95
- raise exceptions.InvalidResponseError("Invalid response format")
96
-
97
- return json.loads(text[1:])["sid"]
98
- except requests.exceptions.RequestException as e:
99
- raise exceptions.ProviderConnectionError(f"Failed to get session ID: {e}")
100
- except (json.JSONDecodeError, KeyError) as e:
101
- raise exceptions.InvalidResponseError(f"Failed to parse session ID: {e}")
102
-
103
-
104
- def _authenticate_session(self, sid: str, t: str) -> None:
105
- try:
106
- response = self.session.post(
107
- f"{API_URL}?EIO=4&transport=polling&t={t}&sid={sid}",
108
- data='40{"jwt":"anonymous-ask-user"}',
109
- proxies=self.proxies
110
- )
111
- response.raise_for_status()
112
- if response.text != "OK":
113
- raise exceptions.AuthenticationError("Authentication failed")
114
- except requests.exceptions.RequestException as e:
115
- raise exceptions.ProviderConnectionError(f"Authentication failed: {e}")
116
-
117
-
118
- def _websocket_interaction(
119
- self,
120
- sid: str,
121
- t: str,
122
- prompt: str,
123
- model: str
124
- ) -> Generator[str, None, None]:
125
- ws = websocket.create_connection(
126
- f"{WS_URL}?EIO=4&transport=websocket&sid={sid}",
127
- header=[f"{k}: {v}" for k, v in self.headers.items()],
128
- cookie="; ".join([f"{k}={v}" for k, v in self.session.cookies.items()]),
129
- proxy=self.proxies.get("http") or self.proxies.get("https") if self.proxies else None,
130
- )
131
-
132
- try:
133
- ws.send("2probe")
134
- if ws.recv() != "3probe":
135
- raise exceptions.ProviderConnectionError("WebSocket handshake failed")
136
- ws.send("5")
137
- ws.recv()
138
- ws.recv()
139
-
140
- message_data = {
141
- "version": "2.5",
142
- "source": "default",
143
- "model": model,
144
- "messages": [{"role": "user", "content": prompt}]
145
- }
146
- ws.send("42" + json.dumps(["perplexity_labs", message_data]))
147
-
148
- last_message = 0
149
- while True:
150
- message = ws.recv()
151
- if message == "2":
152
- if last_message == 0:
153
- raise exceptions.InvalidResponseError("No response received")
154
- ws.send("3")
155
- continue
156
-
157
- try:
158
- data = json.loads(message[2:])[1]
159
- new_content = data["output"][last_message:]
160
- if new_content:
161
- yield new_content
162
- last_message = len(data["output"])
163
- if data.get("final", False):
164
- break
165
- except Exception as e:
166
- raise exceptions.InvalidResponseError(f"Failed to parse message: {e}")
167
-
168
- finally:
169
- ws.close()
170
-
171
- def ask(
172
- self,
173
- prompt: str,
174
- stream: bool = False,
175
- raw: bool = False,
176
- optimizer: str = None,
177
- conversationally: bool = False,
178
- ) -> Union[str, Dict[str, Any]]:
179
- """Ask a question and get a response."""
180
- conversation_prompt = self.conversation.gen_complete_prompt(prompt)
181
- if optimizer:
182
- if optimizer in self.__available_optimizers:
183
- conversation_prompt = getattr(Optimizers, optimizer)(
184
- conversation_prompt if conversationally else prompt
185
- )
186
- else:
187
- raise exceptions.FailedToGenerateResponseError(
188
- f"Optimizer is not one of {self.__available_optimizers}"
189
- )
190
-
191
- t = format(random.getrandbits(32), "08x")
192
- sid = self._get_session_id()
193
- self._authenticate_session(sid, t)
194
-
195
- def for_stream():
196
- gen = self._websocket_interaction(sid, t, conversation_prompt, self.model)
197
- full_response = ""
198
- for chunk in gen:
199
- full_response += chunk
200
- response_dict = dict(text=chunk)
201
- self.last_response.update(response_dict)
202
- yield response_dict if not raw else chunk
203
- self.conversation.update_chat_history(prompt, full_response)
204
-
205
- def for_non_stream():
206
- full_response = "".join(self._websocket_interaction(sid, t, conversation_prompt, self.model))
207
- self.last_response.update({"text": full_response})
208
- self.conversation.update_chat_history(prompt, full_response)
209
- return self.last_response
210
-
211
- return for_stream() if stream else for_non_stream()
212
-
213
- def chat(
214
- self,
215
- prompt: str,
216
- stream: bool = False,
217
- optimizer: str = None,
218
- conversationally: bool = False,
219
- ) -> Union[str, Generator[str, None, None]]:
220
- """Generate response
221
- Args:
222
- prompt (str): Prompt to be send.
223
- stream (bool, optional): Flag for streaming response. Defaults to False.
224
- optimizer (str, optional): Prompt optimizer name. Defaults to None.
225
- conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
226
- Returns:
227
- Union[str, Generator[str, None, None]]: Response generated
228
- """
229
- def for_stream():
230
- for response in self.ask(
231
- prompt, True, optimizer=optimizer, conversationally=conversationally
232
- ):
233
- yield self.get_message(response)
234
-
235
- def for_non_stream():
236
- return self.get_message(
237
- self.ask(
238
- prompt,
239
- False,
240
- optimizer=optimizer,
241
- conversationally=conversationally,
242
- )
243
- )
244
-
245
- return for_stream() if stream else for_non_stream()
246
-
247
- def get_message(self, response: Dict[str, Any]) -> str:
248
- """Retrieves message only from response
249
-
250
- Args:
251
- response (dict): Response generated by `self.ask`
252
-
253
- Returns:
254
- str: Message extracted
255
- """
256
- assert isinstance(response, dict), "Response should be of dict data-type only"
257
- return response["text"]
258
-
259
- if __name__ == "__main__":
260
- from rich import print
261
-
262
- ai = PerplexityLabs()
263
- response = ai.chat("Tell me about Abhay Koul (HelpingAI)", stream=True)
264
- for chunk in response:
265
- print(chunk, end="", flush=True)