webscout 7.0__py3-none-any.whl → 7.2__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 (147) hide show
  1. webscout/AIauto.py +191 -191
  2. webscout/AIbase.py +122 -122
  3. webscout/AIutel.py +440 -440
  4. webscout/Bard.py +343 -161
  5. webscout/DWEBS.py +489 -492
  6. webscout/Extra/YTToolkit/YTdownloader.py +995 -995
  7. webscout/Extra/YTToolkit/__init__.py +2 -2
  8. webscout/Extra/YTToolkit/transcriber.py +476 -479
  9. webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
  10. webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
  11. webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
  12. webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
  13. webscout/Extra/YTToolkit/ytapi/video.py +103 -103
  14. webscout/Extra/autocoder/__init__.py +9 -9
  15. webscout/Extra/autocoder/autocoder_utiles.py +199 -199
  16. webscout/Extra/autocoder/rawdog.py +5 -7
  17. webscout/Extra/autollama.py +230 -230
  18. webscout/Extra/gguf.py +3 -3
  19. webscout/Extra/weather.py +171 -171
  20. webscout/LLM.py +442 -442
  21. webscout/Litlogger/__init__.py +67 -681
  22. webscout/Litlogger/core/__init__.py +6 -0
  23. webscout/Litlogger/core/level.py +20 -0
  24. webscout/Litlogger/core/logger.py +123 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +50 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +174 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +231 -0
  31. webscout/Litlogger/styles/formats.py +377 -0
  32. webscout/Litlogger/styles/text.py +87 -0
  33. webscout/Litlogger/utils/__init__.py +6 -0
  34. webscout/Litlogger/utils/detectors.py +154 -0
  35. webscout/Litlogger/utils/formatters.py +200 -0
  36. webscout/Provider/AISEARCH/DeepFind.py +250 -250
  37. webscout/Provider/Blackboxai.py +136 -137
  38. webscout/Provider/ChatGPTGratis.py +226 -0
  39. webscout/Provider/Cloudflare.py +91 -78
  40. webscout/Provider/DeepSeek.py +218 -0
  41. webscout/Provider/Deepinfra.py +59 -35
  42. webscout/Provider/Free2GPT.py +131 -124
  43. webscout/Provider/Gemini.py +100 -115
  44. webscout/Provider/Glider.py +74 -59
  45. webscout/Provider/Groq.py +30 -18
  46. webscout/Provider/Jadve.py +108 -77
  47. webscout/Provider/Llama3.py +117 -94
  48. webscout/Provider/Marcus.py +191 -137
  49. webscout/Provider/Netwrck.py +62 -50
  50. webscout/Provider/PI.py +79 -124
  51. webscout/Provider/PizzaGPT.py +129 -83
  52. webscout/Provider/QwenLM.py +311 -0
  53. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  54. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  55. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  56. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  57. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  58. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  59. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  60. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  61. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  62. webscout/Provider/TTI/artbit/__init__.py +22 -22
  63. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  64. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  65. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  66. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  67. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  68. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  69. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  70. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  71. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  72. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  73. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  74. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  75. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  76. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  77. webscout/Provider/TTI/talkai/__init__.py +4 -4
  78. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  79. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  80. webscout/Provider/TTS/deepgram.py +182 -182
  81. webscout/Provider/TTS/elevenlabs.py +136 -136
  82. webscout/Provider/TTS/gesserit.py +150 -150
  83. webscout/Provider/TTS/murfai.py +138 -138
  84. webscout/Provider/TTS/parler.py +133 -134
  85. webscout/Provider/TTS/streamElements.py +360 -360
  86. webscout/Provider/TTS/utils.py +280 -280
  87. webscout/Provider/TTS/voicepod.py +116 -116
  88. webscout/Provider/TextPollinationsAI.py +74 -47
  89. webscout/Provider/WiseCat.py +193 -0
  90. webscout/Provider/__init__.py +144 -136
  91. webscout/Provider/cerebras.py +242 -227
  92. webscout/Provider/chatglm.py +204 -204
  93. webscout/Provider/dgaf.py +67 -39
  94. webscout/Provider/gaurish.py +105 -66
  95. webscout/Provider/geminiapi.py +208 -208
  96. webscout/Provider/granite.py +223 -0
  97. webscout/Provider/hermes.py +218 -218
  98. webscout/Provider/llama3mitril.py +179 -179
  99. webscout/Provider/llamatutor.py +72 -62
  100. webscout/Provider/llmchat.py +60 -35
  101. webscout/Provider/meta.py +794 -794
  102. webscout/Provider/multichat.py +331 -230
  103. webscout/Provider/typegpt.py +359 -356
  104. webscout/Provider/yep.py +5 -5
  105. webscout/__main__.py +5 -5
  106. webscout/cli.py +319 -319
  107. webscout/conversation.py +241 -242
  108. webscout/exceptions.py +328 -328
  109. webscout/litagent/__init__.py +28 -28
  110. webscout/litagent/agent.py +2 -3
  111. webscout/litprinter/__init__.py +0 -58
  112. webscout/scout/__init__.py +8 -8
  113. webscout/scout/core.py +884 -884
  114. webscout/scout/element.py +459 -459
  115. webscout/scout/parsers/__init__.py +69 -69
  116. webscout/scout/parsers/html5lib_parser.py +172 -172
  117. webscout/scout/parsers/html_parser.py +236 -236
  118. webscout/scout/parsers/lxml_parser.py +178 -178
  119. webscout/scout/utils.py +38 -38
  120. webscout/swiftcli/__init__.py +811 -811
  121. webscout/update_checker.py +2 -12
  122. webscout/version.py +1 -1
  123. webscout/webscout_search.py +1142 -1140
  124. webscout/webscout_search_async.py +635 -635
  125. webscout/zeroart/__init__.py +54 -54
  126. webscout/zeroart/base.py +60 -60
  127. webscout/zeroart/effects.py +99 -99
  128. webscout/zeroart/fonts.py +816 -816
  129. {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
  130. webscout-7.2.dist-info/RECORD +217 -0
  131. webstoken/__init__.py +30 -30
  132. webstoken/classifier.py +189 -189
  133. webstoken/keywords.py +216 -216
  134. webstoken/language.py +128 -128
  135. webstoken/ner.py +164 -164
  136. webstoken/normalizer.py +35 -35
  137. webstoken/processor.py +77 -77
  138. webstoken/sentiment.py +206 -206
  139. webstoken/stemmer.py +73 -73
  140. webstoken/tagger.py +60 -60
  141. webstoken/tokenizer.py +158 -158
  142. webscout/Provider/RUBIKSAI.py +0 -272
  143. webscout-7.0.dist-info/RECORD +0 -199
  144. {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
  145. {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
  146. {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
  147. {webscout-7.0.dist-info → webscout-7.2.dist-info}/top_level.txt +0 -0
webscout/Provider/PI.py CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  import cloudscraper
2
3
  import json
3
4
  import re
@@ -7,9 +8,9 @@ from webscout.AIutel import Optimizers
7
8
  from webscout.AIutel import Conversation
8
9
  from webscout.AIutel import AwesomePrompts
9
10
  from webscout.AIbase import Provider
10
- from typing import Dict, Union, Any
11
+ from typing import Dict, Union, Any, Optional
11
12
  from webscout import LitAgent
12
- from webscout.Litlogger import LitLogger, LogFormat, ColorScheme
13
+ from webscout.Litlogger import Logger, LogFormat
13
14
 
14
15
  class PiAI(Provider):
15
16
  """
@@ -35,26 +36,6 @@ class PiAI(Provider):
35
36
  ):
36
37
  """
37
38
  Initializes the PiAI provider with specified parameters.
38
-
39
- Args:
40
- is_conversation (bool): Whether to maintain conversation history
41
- max_tokens (int): Maximum number of tokens in response
42
- timeout (int): Request timeout in seconds
43
- intro (str): Custom introduction message
44
- filepath (str): Path to save conversation history
45
- update_file (bool): Whether to update conversation history file
46
- proxies (dict): Proxy configuration
47
- history_offset (int): Conversation history limit
48
- act (str): Custom personality/act for the AI
49
- logging (bool): Enable debug logging
50
-
51
- Examples:
52
- >>> ai = PiAI(logging=True)
53
- >>> ai.ask("What's the weather today?", "Alice")
54
- Sends a prompt to Pi.ai and returns the response.
55
-
56
- >>> ai.chat("Tell me a joke", voice_name="William")
57
- Initiates a chat with Pi.ai using the provided prompt.
58
39
  """
59
40
  self.scraper = cloudscraper.create_scraper()
60
41
  self.url = 'https://pi.ai/api/chat'
@@ -115,30 +96,19 @@ class PiAI(Provider):
115
96
  self.conversation.history_offset = history_offset
116
97
  self.session.proxies = proxies
117
98
 
118
- # Initialize logger
119
- self.logger = LitLogger(name="PiAI", format=LogFormat.MODERN_EMOJI, color_scheme=ColorScheme.CYBERPUNK) if logging else None
99
+ self.logger = Logger(name="PiAI", format=LogFormat.MODERN_EMOJI) if logging else None
120
100
 
121
101
  self.knowledge_cutoff = "December 2023"
122
102
 
123
- # Initialize conversation ID
124
103
  if self.is_conversation:
125
104
  self.start_conversation()
126
105
 
127
106
  if self.logger:
128
- self.logger.debug("PiAI instance initialized")
107
+ self.logger.info("PiAI instance initialized successfully")
129
108
 
130
109
  def start_conversation(self) -> str:
131
110
  """
132
111
  Initializes a new conversation and returns the conversation ID.
133
-
134
- Returns:
135
- str: The conversation ID from Pi.ai
136
-
137
- Examples:
138
- >>> ai = PiAI()
139
- >>> conversation_id = ai.start_conversation()
140
- >>> print(conversation_id)
141
- 'abc123xyz'
142
112
  """
143
113
  if self.logger:
144
114
  self.logger.debug("Starting new conversation")
@@ -151,52 +121,34 @@ class PiAI(Provider):
151
121
  timeout=self.timeout
152
122
  )
153
123
 
154
- if not response.ok and self.logger:
155
- self.logger.error(f"Failed to start conversation: {response.status_code}")
124
+ if not response.ok:
125
+ if self.logger:
126
+ self.logger.error(f"Failed to start conversation. Status code: {response.status_code}")
127
+ raise Exception(f"Failed to start conversation: {response.status_code}")
156
128
 
157
129
  data = response.json()
158
130
  self.conversation_id = data['conversations'][0]['sid']
159
131
 
160
132
  if self.logger:
161
- self.logger.debug(f"Conversation started with ID: {self.conversation_id}")
133
+ self.logger.info(f"Conversation started successfully with ID: {self.conversation_id}")
162
134
 
163
135
  return self.conversation_id
164
136
 
165
137
  def ask(
166
138
  self,
167
139
  prompt: str,
168
- voice_name:str,
140
+ voice_name: Optional[str] = None,
169
141
  stream: bool = False,
170
142
  raw: bool = False,
171
143
  optimizer: str = None,
172
144
  conversationally: bool = False,
173
- verbose:bool = None,
174
- output_file:str = None
145
+ output_file: str = None
175
146
  ) -> dict:
176
147
  """
177
148
  Interact with Pi.ai by sending a prompt and receiving a response.
178
-
179
- Args:
180
- prompt (str): The prompt to be sent to Pi.ai
181
- voice_name (str): The name of the voice to use for audio responses
182
- stream (bool): Flag for streaming response
183
- raw (bool): If True, returns the raw response as received
184
- optimizer (str): Name of the prompt optimizer to use
185
- conversationally (bool): If True, chat conversationally when using optimizer
186
- verbose (bool): If True, provides detailed output
187
- output_file (str): File path to save the output
188
-
189
- Returns:
190
- dict: A dictionary containing the AI's response
191
-
192
- Examples:
193
- >>> ai = PiAI(logging=True)
194
- >>> response = ai.ask("Hello!", "Alice", verbose=True)
195
- >>> print(response['text'])
196
- 'Hi! How can I help you today?'
197
149
  """
198
150
  if self.logger:
199
- self.logger.debug(f"ask() called with prompt: {prompt}, voice: {voice_name}")
151
+ self.logger.debug(f"Processing request - Prompt: {prompt[:50]}... Voice: {voice_name}")
200
152
 
201
153
  conversation_prompt = self.conversation.gen_complete_prompt(prompt)
202
154
  if optimizer:
@@ -206,7 +158,7 @@ class PiAI(Provider):
206
158
  )
207
159
  else:
208
160
  if self.logger:
209
- self.logger.error(f"Invalid optimizer: {optimizer}")
161
+ self.logger.error(f"Invalid optimizer requested: {optimizer}")
210
162
  raise Exception(
211
163
  f"Optimizer is not one of {self.__available_optimizers}"
212
164
  )
@@ -217,25 +169,44 @@ class PiAI(Provider):
217
169
  }
218
170
 
219
171
  def for_stream():
220
- response = self.scraper.post(self.url, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout)
172
+ response = self.scraper.post(
173
+ self.url,
174
+ headers=self.headers,
175
+ cookies=self.cookies,
176
+ json=data,
177
+ stream=True,
178
+ timeout=self.timeout
179
+ )
180
+
181
+ if not response.ok:
182
+ if self.logger:
183
+ self.logger.error(f"API request failed. Status code: {response.status_code}")
184
+ raise Exception(f"API request failed: {response.status_code}")
185
+
221
186
  output_str = response.content.decode('utf-8')
222
187
  sids = re.findall(r'"sid":"(.*?)"', output_str)
223
188
  second_sid = sids[1] if len(sids) >= 2 else None
224
- #Start the audio download in a separate thread
225
- threading.Thread(target=self.download_audio_threaded, args=(voice_name, second_sid, verbose, output_file)).start()
189
+
190
+ if voice_name and second_sid:
191
+ threading.Thread(
192
+ target=self.download_audio_threaded,
193
+ args=(voice_name, second_sid, output_file)
194
+ ).start()
226
195
 
227
196
  streaming_text = ""
228
197
  for line in response.iter_lines(decode_unicode=True):
229
198
  if line.startswith("data: "):
230
- json_data = line[6:]
231
199
  try:
232
- parsed_data = json.loads(json_data)
200
+ parsed_data = json.loads(line[6:])
233
201
  if 'text' in parsed_data:
234
202
  streaming_text += parsed_data['text']
235
203
  resp = dict(text=streaming_text)
236
204
  self.last_response.update(resp)
237
205
  yield parsed_data if raw else resp
238
- except:continue
206
+ except json.JSONDecodeError:
207
+ if self.logger:
208
+ self.logger.warning("Failed to parse JSON from stream")
209
+ continue
239
210
 
240
211
  self.conversation.update_chat_history(
241
212
  prompt, self.get_message(self.last_response)
@@ -251,44 +222,30 @@ class PiAI(Provider):
251
222
  def chat(
252
223
  self,
253
224
  prompt: str,
254
- voice_name: str = "Alice",
225
+ voice_name: Optional[str] = None,
255
226
  stream: bool = False,
256
227
  optimizer: str = None,
257
228
  conversationally: bool = False,
258
- verbose:bool = True,
259
- output_file:str = "PiAi.mp3"
229
+ output_file: str = "PiAi.mp3"
260
230
  ) -> str:
261
231
  """
262
232
  Generates a response based on the provided prompt.
263
-
264
- Args:
265
- prompt (str): Input prompt for generating response
266
- voice_name (str): Voice to use for audio response
267
- stream (bool): Enable response streaming
268
- optimizer (str): Prompt optimizer to use
269
- conversationally (bool): Enable conversational mode with optimizer
270
- verbose (bool): Enable verbose output
271
- output_file (str): Audio output file path
272
-
273
- Returns:
274
- str: The generated response
275
-
276
- Examples:
277
- >>> ai = PiAI(logging=True)
278
- >>> response = ai.chat("Tell me a joke", voice_name="William")
279
- >>> print(response)
280
- 'Why did the scarecrow win an award? Because he was outstanding in his field!'
281
233
  """
282
234
  if self.logger:
283
- self.logger.debug(f"chat() called with prompt: {prompt}, voice: {voice_name}")
235
+ self.logger.debug(f"Chat request initiated - Prompt: {prompt[:50]}...")
236
+
237
+ if voice_name and voice_name not in self.AVAILABLE_VOICES:
238
+ if self.logger:
239
+ self.logger.error(f"Invalid voice requested: {voice_name}")
240
+ raise ValueError(f"Voice '{voice_name}' not one of [{', '.join(self.AVAILABLE_VOICES.keys())}]")
284
241
 
285
- assert (
286
- voice_name in self.AVAILABLE_VOICES
287
- ), f"Voice '{voice_name}' not one of [{', '.join(self.AVAILABLE_VOICES.keys())}]"
288
242
  def for_stream():
289
243
  for response in self.ask(
290
- prompt, voice_name, True, optimizer=optimizer, conversationally=conversationally,
291
- verbose=verbose,
244
+ prompt,
245
+ voice_name,
246
+ True,
247
+ optimizer=optimizer,
248
+ conversationally=conversationally,
292
249
  output_file=output_file
293
250
  ):
294
251
  yield self.get_message(response).encode('utf-8').decode('utf-8')
@@ -301,7 +258,6 @@ class PiAI(Provider):
301
258
  False,
302
259
  optimizer=optimizer,
303
260
  conversationally=conversationally,
304
- verbose=verbose,
305
261
  output_file=output_file
306
262
  )
307
263
  ).encode('utf-8').decode('utf-8')
@@ -309,51 +265,50 @@ class PiAI(Provider):
309
265
  return for_stream() if stream else for_non_stream()
310
266
 
311
267
  def get_message(self, response: dict) -> str:
312
- """Retrieves message only from response
313
-
314
- Args:
315
- response (dict): Response generated by `self.ask`
316
-
317
- Returns:
318
- str: Message extracted
319
- """
268
+ """Retrieves message only from response"""
320
269
  assert isinstance(response, dict), "Response should be of dict data-type only"
321
270
  return response["text"]
322
271
 
323
- def download_audio_threaded(self, voice_name: str, second_sid: str, verbose:bool, output_file:str) -> None:
324
- """Downloads audio in a separate thread.
325
-
326
- Args:
327
- voice_name (str): The name of the desired voice.
328
- second_sid (str): The message SID for the audio request.
329
- verbose (bool): Flag to indicate if verbose output is desired.
330
- output_file (str): The file path where the audio will be saved.
331
- """
272
+ def download_audio_threaded(self, voice_name: str, second_sid: str, output_file: str) -> None:
273
+ """Downloads audio in a separate thread."""
332
274
  if self.logger:
333
- self.logger.debug(f"Downloading audio with voice: {voice_name}")
275
+ self.logger.debug(f"Starting audio download - Voice: {voice_name}, SID: {second_sid}")
334
276
 
335
277
  params = {
336
278
  'mode': 'eager',
337
279
  'voice': f'voice{self.AVAILABLE_VOICES[voice_name]}',
338
280
  'messageSid': second_sid,
339
281
  }
282
+
340
283
  try:
341
- audio_response = self.scraper.get('https://pi.ai/api/chat/voice', params=params, cookies=self.cookies, headers=self.headers, timeout=self.timeout)
342
- if not audio_response.ok and self.logger:
343
- self.logger.error(f"Audio download failed: {audio_response.status_code}")
284
+ audio_response = self.scraper.get(
285
+ 'https://pi.ai/api/chat/voice',
286
+ params=params,
287
+ cookies=self.cookies,
288
+ headers=self.headers,
289
+ timeout=self.timeout
290
+ )
291
+
292
+ if not audio_response.ok:
293
+ if self.logger:
294
+ self.logger.error(f"Audio download failed. Status code: {audio_response.status_code}")
295
+ return
344
296
 
345
- audio_response.raise_for_status() # Raise an exception for bad status codes
297
+ audio_response.raise_for_status()
298
+
346
299
  with open(output_file, "wb") as file:
347
300
  file.write(audio_response.content)
348
- if verbose:print("\nAudio file downloaded successfully.")
301
+
302
+ if self.logger:
303
+ self.logger.info(f"Audio file successfully downloaded to: {output_file}")
304
+
349
305
  except requests.exceptions.RequestException as e:
350
306
  if self.logger:
351
- self.logger.error(f"Audio download failed: {e}")
352
- if verbose:print(f"\nFailed to download audio file. Error: {e}")
307
+ self.logger.error(f"Audio download failed: {str(e)}")
353
308
 
354
309
  if __name__ == '__main__':
355
310
  from rich import print
356
- ai = PiAI()
357
- response = ai.chat(input(">>> "), stream=True, verbose=False)
311
+ ai = PiAI(logging=True)
312
+ response = ai.chat(input(">>> "), stream=True)
358
313
  for chunk in response:
359
- print(chunk, end="", flush=True)
314
+ print(chunk, end="", flush=True)
@@ -1,21 +1,17 @@
1
1
  import requests
2
- from typing import Any, AsyncGenerator, Dict, Optional, Union, Generator
3
2
  import json
4
-
5
- from webscout.AIutel import Optimizers
6
- from webscout.AIutel import Conversation
7
- from webscout.AIutel import AwesomePrompts, sanitize_stream
8
- from webscout.AIbase import Provider, AsyncProvider
3
+ import re
4
+ from typing import Any, Dict, Optional, Union, Generator
5
+ from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
6
+ from webscout.AIbase import Provider
9
7
  from webscout import exceptions
10
8
  from webscout import LitAgent as Lit
11
- from webscout.Litlogger import LitLogger, LogFormat, ColorScheme
9
+ from webscout.Litlogger import Logger, LogFormat
12
10
 
13
11
  class PIZZAGPT(Provider):
14
12
  """
15
13
  PIZZAGPT is a provider class for interacting with the PizzaGPT API.
16
-
17
- Attributes:
18
- knowledge_cutoff (str): The knowledge cutoff date for the model
14
+ Supports web search integration and handles responses using regex.
19
15
  """
20
16
 
21
17
  def __init__(
@@ -30,18 +26,9 @@ class PIZZAGPT(Provider):
30
26
  history_offset: int = 10250,
31
27
  act: str = None,
32
28
  logging: bool = False,
29
+ model: str = "gpt-4o-mini"
33
30
  ) -> None:
34
- """
35
- Initializes the PizzaGPT provider with the specified parameters.
36
-
37
- Examples:
38
- >>> ai = PIZZAGPT(logging=True)
39
- >>> ai.ask("What's the weather today?")
40
- Sends a prompt to the PizzaGPT API and returns the response.
41
-
42
- >>> ai.chat("Tell me a joke")
43
- Initiates a chat with the PizzaGPT API using the provided prompt.
44
- """
31
+ """Initialize PizzaGPT with enhanced configuration options."""
45
32
  self.session = requests.Session()
46
33
  self.is_conversation = is_conversation
47
34
  self.max_tokens_to_sample = max_tokens
@@ -49,31 +36,23 @@ class PIZZAGPT(Provider):
49
36
  self.stream_chunk_size = 64
50
37
  self.timeout = timeout
51
38
  self.last_response = {}
39
+ self.model = model
40
+
52
41
  self.headers = {
53
42
  "accept": "application/json",
54
- "accept-encoding": "gzip, deflate, br, zstd",
55
- "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
56
- "content-length": "17",
43
+ "accept-language": "en-US,en;q=0.9",
57
44
  "content-type": "application/json",
58
- "dnt": "1",
59
45
  "origin": "https://www.pizzagpt.it",
60
- "priority": "u=1, i",
61
46
  "referer": "https://www.pizzagpt.it/en",
62
- "sec-ch-ua": '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
63
- "sec-ch-ua-mobile": "?0",
64
- "sec-ch-ua-platform": '"Windows"',
65
- "sec-fetch-dest": "empty",
66
- "sec-fetch-mode": "cors",
67
- "sec-fetch-site": "same-origin",
68
47
  "user-agent": Lit().random(),
69
48
  "x-secret": "Marinara"
70
49
  }
71
50
 
72
51
  self.__available_optimizers = (
73
- method
74
- for method in dir(Optimizers)
52
+ method for method in dir(Optimizers)
75
53
  if callable(getattr(Optimizers, method)) and not method.startswith("__")
76
54
  )
55
+
77
56
  self.session.headers.update(self.headers)
78
57
  Conversation.intro = (
79
58
  AwesomePrompts().get_act(
@@ -82,15 +61,56 @@ class PIZZAGPT(Provider):
82
61
  if act
83
62
  else intro or Conversation.intro
84
63
  )
64
+
85
65
  self.conversation = Conversation(
86
66
  is_conversation, self.max_tokens_to_sample, filepath, update_file
87
67
  )
88
68
  self.conversation.history_offset = history_offset
89
69
  self.session.proxies = proxies
90
70
 
91
- # Initialize logger
92
- self.logger = LitLogger(name="PIZZAGPT", format=LogFormat.MODERN_EMOJI, color_scheme=ColorScheme.CYBERPUNK) if logging else None
71
+ self.logger = Logger(
72
+ name="PIZZAGPT",
73
+ format=LogFormat.MODERN_EMOJI,
74
+ ) if logging else None
93
75
 
76
+ if self.logger:
77
+ self.logger.info(f"PIZZAGPT initialized with model: {self.model}")
78
+
79
+ def _extract_content(self, text: str) -> Dict[str, Any]:
80
+ """
81
+ Extract content from response text using regex.
82
+ """
83
+ if self.logger:
84
+ self.logger.debug("Extracting content from response text")
85
+
86
+ try:
87
+ # Look for content pattern
88
+ content_match = re.search(r'"content"\s*:\s*"(.*?)"(?=\s*[,}])', text, re.DOTALL)
89
+ if not content_match:
90
+ if self.logger:
91
+ self.logger.error("Content pattern not found in response")
92
+ raise exceptions.FailedToGenerateResponseError("Content not found in response")
93
+
94
+ content = content_match.group(1)
95
+ # Unescape special characters
96
+ content = content.encode().decode('unicode_escape')
97
+
98
+ # Look for citations if present
99
+ citations = []
100
+ citations_match = re.search(r'"citations"\s*:\s*\[(.*?)\]', text, re.DOTALL)
101
+ if citations_match:
102
+ citations_text = citations_match.group(1)
103
+ citations = re.findall(r'"(.*?)"', citations_text)
104
+
105
+ return {
106
+ "content": content,
107
+ "citations": citations
108
+ }
109
+
110
+ except Exception as e:
111
+ if self.logger:
112
+ self.logger.error(f"Failed to extract content: {str(e)}")
113
+ raise exceptions.FailedToGenerateResponseError(f"Failed to extract content: {str(e)}")
94
114
 
95
115
  def ask(
96
116
  self,
@@ -99,15 +119,14 @@ class PIZZAGPT(Provider):
99
119
  raw: bool = False,
100
120
  optimizer: str = None,
101
121
  conversationally: bool = False,
122
+ web_search: bool = False,
102
123
  ) -> Dict[str, Any]:
103
124
  """
104
- Sends a prompt to the PizzaGPT API and returns the response.
105
-
106
- Examples:
107
- >>> ai = PIZZAGPT()
108
- >>> ai.ask("What's the weather today?")
109
- Returns the response from the PizzaGPT API.
125
+ Send a prompt to PizzaGPT API with optional web search capability.
110
126
  """
127
+ if self.logger:
128
+ self.logger.debug(f"Processing request - Prompt: {prompt[:50]}...")
129
+ self.logger.debug(f"Web search enabled: {web_search}")
111
130
 
112
131
  conversation_prompt = self.conversation.gen_complete_prompt(prompt)
113
132
  if optimizer:
@@ -115,38 +134,66 @@ class PIZZAGPT(Provider):
115
134
  conversation_prompt = getattr(Optimizers, optimizer)(
116
135
  conversation_prompt if conversationally else prompt
117
136
  )
137
+ if self.logger:
138
+ self.logger.debug(f"Applied optimizer: {optimizer}")
118
139
  else:
119
140
  if self.logger:
120
141
  self.logger.error(f"Invalid optimizer: {optimizer}")
121
142
  raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
122
143
 
123
- self.session.headers.update(self.headers)
124
- payload = {"question": conversation_prompt}
144
+ payload = {
145
+ "question": conversation_prompt,
146
+ "model": self.model,
147
+ "searchEnabled": web_search
148
+ }
149
+
150
+ if self.logger:
151
+ self.logger.debug(f"Sending payload: {json.dumps(payload, indent=2)}")
125
152
 
126
153
  try:
127
154
  response = self.session.post(
128
- self.api_endpoint, json=payload, timeout=self.timeout
155
+ self.api_endpoint,
156
+ json=payload,
157
+ timeout=self.timeout
129
158
  )
159
+
130
160
  if self.logger:
131
- self.logger.debug(response)
161
+ self.logger.debug(f"Response status: {response.status_code}")
162
+
132
163
  if not response.ok:
133
164
  if self.logger:
134
- self.logger.error(f"Failed to generate response: {response.status_code} {response.reason}")
165
+ self.logger.error(f"API request failed: {response.status_code} - {response.reason}")
135
166
  raise exceptions.FailedToGenerateResponseError(
136
- f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
167
+ f"Failed to generate response - ({response.status_code}, {response.reason})"
137
168
  )
138
169
 
139
- resp = response.json()
140
- if self.logger:
141
- self.logger.debug(resp)
142
- self.last_response.update(dict(text=resp['content']))
143
- self.conversation.update_chat_history(
144
- prompt, self.get_message(self.last_response)
145
- )
146
- return self.last_response
170
+ response_text = response.text
171
+ if not response_text:
172
+ if self.logger:
173
+ self.logger.error("Empty response received from API")
174
+ raise exceptions.FailedToGenerateResponseError("Empty response received from API")
147
175
 
148
- except Exception as e:
149
- raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
176
+ try:
177
+ resp = self._extract_content(response_text)
178
+ if self.logger:
179
+ self.logger.debug("Response parsed successfully")
180
+
181
+ self.last_response.update(dict(text=resp['content']))
182
+ self.conversation.update_chat_history(
183
+ prompt, self.get_message(self.last_response)
184
+ )
185
+ return self.last_response
186
+
187
+ except Exception as e:
188
+ if self.logger:
189
+ self.logger.error(f"Failed to parse response: {str(e)}")
190
+ self.logger.debug(f"Raw response text: {response_text[:500]}")
191
+ raise exceptions.FailedToGenerateResponseError(f"Failed to parse response: {str(e)}")
192
+
193
+ except requests.exceptions.RequestException as e:
194
+ if self.logger:
195
+ self.logger.error(f"Request failed: {str(e)}")
196
+ raise exceptions.FailedToGenerateResponseError(f"Request failed: {str(e)}")
150
197
 
151
198
  def chat(
152
199
  self,
@@ -154,40 +201,39 @@ class PIZZAGPT(Provider):
154
201
  stream: bool = False,
155
202
  optimizer: str = None,
156
203
  conversationally: bool = False,
204
+ web_search: bool = False,
157
205
  ) -> str:
158
206
  """
159
- Initiates a chat with the PizzaGPT API using the provided prompt.
160
-
161
- Examples:
162
- >>> ai = PIZZAGPT()
163
- >>> ai.chat("Tell me a joke")
164
- Returns the chat response from the PizzaGPT API.
207
+ Chat with PizzaGPT with optional web search capability.
165
208
  """
166
-
167
- return self.get_message(
168
- self.ask(
209
+ if self.logger:
210
+ self.logger.debug(f"Chat request initiated with web_search={web_search}")
211
+
212
+ try:
213
+ response = self.ask(
169
214
  prompt,
170
215
  optimizer=optimizer,
171
216
  conversationally=conversationally,
217
+ web_search=web_search
172
218
  )
173
- )
219
+ return self.get_message(response)
220
+ except Exception as e:
221
+ if self.logger:
222
+ self.logger.error(f"Chat failed: {str(e)}")
223
+ raise
174
224
 
175
225
  def get_message(self, response: dict) -> str:
176
- """Retrieves message only from response
177
-
178
- Args:
179
- response (dict): Response generated by `self.ask`
180
-
181
- Returns:
182
- str: Message extracted
183
- """
226
+ """Extract message from response dictionary."""
184
227
  assert isinstance(response, dict), "Response should be of dict data-type only"
185
- return response["text"]
228
+ return response.get("text", "")
229
+
186
230
  if __name__ == "__main__":
187
231
  from rich import print
188
-
189
- ai = PIZZAGPT(logging=True)
190
- # Stream the response
191
- response = ai.chat("hi")
192
- for chunk in response:
193
- print(chunk, end="", flush=True)
232
+
233
+ # Example usage with web search enabled
234
+ ai = PIZZAGPT(logging=True)
235
+ try:
236
+ response = ai.chat("Who is Founder and CEO of HelpingAI??", web_search=True)
237
+ print(response)
238
+ except Exception as e:
239
+ print(f"Error: {str(e)}")