webscout 8.2.3__py3-none-any.whl → 8.2.4__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.
- inferno/lol.py +589 -0
- webscout/AIutel.py +226 -14
- webscout/Bard.py +579 -206
- webscout/DWEBS.py +78 -35
- webscout/Extra/tempmail/base.py +1 -1
- webscout/Provider/AISEARCH/hika_search.py +4 -0
- webscout/Provider/AllenAI.py +163 -126
- webscout/Provider/ChatGPTClone.py +96 -84
- webscout/Provider/Deepinfra.py +95 -67
- webscout/Provider/ElectronHub.py +55 -0
- webscout/Provider/GPTWeb.py +96 -46
- webscout/Provider/Groq.py +194 -91
- webscout/Provider/HeckAI.py +89 -47
- webscout/Provider/HuggingFaceChat.py +113 -106
- webscout/Provider/Hunyuan.py +94 -83
- webscout/Provider/Jadve.py +107 -75
- webscout/Provider/LambdaChat.py +106 -64
- webscout/Provider/Llama3.py +94 -39
- webscout/Provider/MCPCore.py +318 -0
- webscout/Provider/Marcus.py +85 -36
- webscout/Provider/Netwrck.py +76 -43
- webscout/Provider/OPENAI/__init__.py +4 -1
- webscout/Provider/OPENAI/ai4chat.py +286 -0
- webscout/Provider/OPENAI/chatgptclone.py +35 -14
- webscout/Provider/OPENAI/deepinfra.py +37 -0
- webscout/Provider/OPENAI/groq.py +354 -0
- webscout/Provider/OPENAI/heckai.py +6 -2
- webscout/Provider/OPENAI/mcpcore.py +376 -0
- webscout/Provider/OPENAI/multichat.py +368 -0
- webscout/Provider/OPENAI/netwrck.py +3 -1
- webscout/Provider/OpenGPT.py +48 -38
- webscout/Provider/PI.py +168 -92
- webscout/Provider/PizzaGPT.py +66 -36
- webscout/Provider/TeachAnything.py +85 -51
- webscout/Provider/TextPollinationsAI.py +109 -51
- webscout/Provider/TwoAI.py +109 -60
- webscout/Provider/Venice.py +93 -56
- webscout/Provider/VercelAI.py +2 -2
- webscout/Provider/WiseCat.py +65 -28
- webscout/Provider/Writecream.py +37 -11
- webscout/Provider/WritingMate.py +135 -63
- webscout/Provider/__init__.py +3 -21
- webscout/Provider/ai4chat.py +6 -7
- webscout/Provider/copilot.py +0 -3
- webscout/Provider/elmo.py +101 -58
- webscout/Provider/granite.py +91 -46
- webscout/Provider/hermes.py +87 -47
- webscout/Provider/koala.py +1 -1
- webscout/Provider/learnfastai.py +104 -50
- webscout/Provider/llama3mitril.py +86 -51
- webscout/Provider/llmchat.py +88 -46
- webscout/Provider/llmchatco.py +74 -49
- webscout/Provider/meta.py +41 -37
- webscout/Provider/multichat.py +54 -25
- webscout/Provider/scnet.py +93 -43
- webscout/Provider/searchchat.py +82 -75
- webscout/Provider/sonus.py +103 -51
- webscout/Provider/toolbaz.py +132 -77
- webscout/Provider/turboseek.py +92 -41
- webscout/Provider/tutorai.py +82 -64
- webscout/Provider/typefully.py +75 -33
- webscout/Provider/typegpt.py +96 -35
- webscout/Provider/uncovr.py +112 -62
- webscout/Provider/x0gpt.py +69 -26
- webscout/Provider/yep.py +79 -66
- webscout/conversation.py +35 -21
- webscout/exceptions.py +20 -0
- webscout/prompt_manager.py +56 -42
- webscout/version.py +1 -1
- webscout/webscout_search.py +65 -47
- webscout/webscout_search_async.py +81 -126
- webscout/yep_search.py +93 -43
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/METADATA +22 -10
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/RECORD +78 -81
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/WHEEL +1 -1
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/DeepSeek.py +0 -196
- webscout/Provider/Llama.py +0 -200
- webscout/Provider/Phind.py +0 -535
- webscout/Provider/WebSim.py +0 -228
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llamatutor.py +0 -192
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/entry_points.txt +0 -0
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info/licenses}/LICENSE.md +0 -0
- {webscout-8.2.3.dist-info → webscout-8.2.4.dist-info}/top_level.txt +0 -0
webscout/Provider/PI.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
from uuid import uuid4
|
|
2
|
-
import
|
|
2
|
+
from curl_cffi.requests import Session
|
|
3
|
+
from curl_cffi import CurlError
|
|
3
4
|
import json
|
|
4
5
|
import re
|
|
5
6
|
import threading
|
|
6
|
-
import requests
|
|
7
7
|
from webscout.AIutel import Optimizers
|
|
8
8
|
from webscout.AIutel import Conversation
|
|
9
9
|
from webscout.AIutel import AwesomePrompts
|
|
10
10
|
from webscout.AIbase import Provider
|
|
11
11
|
from typing import Dict, Union, Any, Optional
|
|
12
12
|
from webscout.litagent import LitAgent
|
|
13
|
+
from webscout import exceptions
|
|
13
14
|
|
|
14
15
|
class PiAI(Provider):
|
|
15
16
|
"""
|
|
@@ -35,7 +36,7 @@ class PiAI(Provider):
|
|
|
35
36
|
def __init__(
|
|
36
37
|
self,
|
|
37
38
|
is_conversation: bool = True,
|
|
38
|
-
max_tokens: int = 2048,
|
|
39
|
+
max_tokens: int = 2048, # Note: max_tokens is not used by this API
|
|
39
40
|
timeout: int = 30,
|
|
40
41
|
intro: str = None,
|
|
41
42
|
filepath: str = None,
|
|
@@ -46,7 +47,7 @@ class PiAI(Provider):
|
|
|
46
47
|
voice: bool = False,
|
|
47
48
|
voice_name: str = "voice3",
|
|
48
49
|
output_file: str = "PiAI.mp3",
|
|
49
|
-
model: str = "inflection_3_pi",
|
|
50
|
+
model: str = "inflection_3_pi", # Note: model is not used by this API
|
|
50
51
|
):
|
|
51
52
|
"""
|
|
52
53
|
Initializes PiAI with voice support.
|
|
@@ -64,8 +65,8 @@ class PiAI(Provider):
|
|
|
64
65
|
if voice and voice_name and voice_name not in self.AVAILABLE_VOICES:
|
|
65
66
|
raise ValueError(f"Voice '{voice_name}' not available. Choose from: {list(self.AVAILABLE_VOICES.keys())}")
|
|
66
67
|
|
|
67
|
-
# Initialize
|
|
68
|
-
self.
|
|
68
|
+
# Initialize curl_cffi Session instead of cloudscraper/requests
|
|
69
|
+
self.session = Session()
|
|
69
70
|
self.primary_url = 'https://pi.ai/api/chat'
|
|
70
71
|
self.fallback_url = 'https://pi.ai/api/v2/chat'
|
|
71
72
|
self.url = self.primary_url
|
|
@@ -77,9 +78,6 @@ class PiAI(Provider):
|
|
|
77
78
|
'DNT': '1',
|
|
78
79
|
'Origin': 'https://pi.ai',
|
|
79
80
|
'Referer': 'https://pi.ai/talk',
|
|
80
|
-
'Sec-CH-UA': '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
|
|
81
|
-
'Sec-CH-UA-Mobile': '?0',
|
|
82
|
-
'Sec-CH-UA-Platform': '"Windows"',
|
|
83
81
|
'Sec-Fetch-Dest': 'empty',
|
|
84
82
|
'Sec-Fetch-Mode': 'cors',
|
|
85
83
|
'Sec-Fetch-Site': 'same-origin',
|
|
@@ -90,9 +88,12 @@ class PiAI(Provider):
|
|
|
90
88
|
'__cf_bm': uuid4().hex
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
|
|
91
|
+
# Update curl_cffi session headers, proxies, and cookies
|
|
94
92
|
self.session.headers.update(self.headers)
|
|
95
|
-
self.session.proxies = proxies
|
|
93
|
+
self.session.proxies = proxies # Assign proxies directly
|
|
94
|
+
# Set cookies on the session object for curl_cffi
|
|
95
|
+
for name, value in self.cookies.items():
|
|
96
|
+
self.session.cookies.set(name, value)
|
|
96
97
|
|
|
97
98
|
self.is_conversation = is_conversation
|
|
98
99
|
self.max_tokens_to_sample = max_tokens
|
|
@@ -125,21 +126,33 @@ class PiAI(Provider):
|
|
|
125
126
|
"""
|
|
126
127
|
Initializes a new conversation and returns the conversation ID.
|
|
127
128
|
"""
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
try:
|
|
130
|
+
# Use curl_cffi session post with impersonate
|
|
131
|
+
# Cookies are handled by the session
|
|
132
|
+
response = self.session.post(
|
|
133
|
+
"https://pi.ai/api/chat/start",
|
|
134
|
+
# headers are set on the session
|
|
135
|
+
# cookies=self.cookies, # Handled by session
|
|
136
|
+
json={},
|
|
137
|
+
timeout=self.timeout,
|
|
138
|
+
# proxies are set on the session
|
|
139
|
+
impersonate="chrome110" # Use a common impersonation profile
|
|
140
|
+
)
|
|
141
|
+
response.raise_for_status() # Check for HTTP errors
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
143
|
+
data = response.json()
|
|
144
|
+
# Ensure the expected structure before accessing
|
|
145
|
+
if 'conversations' in data and data['conversations'] and 'sid' in data['conversations'][0]:
|
|
146
|
+
self.conversation_id = data['conversations'][0]['sid']
|
|
147
|
+
return self.conversation_id
|
|
148
|
+
else:
|
|
149
|
+
raise exceptions.FailedToGenerateResponseError(f"Unexpected response structure from start API: {data}")
|
|
141
150
|
|
|
142
|
-
|
|
151
|
+
except CurlError as e: # Catch CurlError
|
|
152
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to start conversation (CurlError): {e}") from e
|
|
153
|
+
except Exception as e: # Catch other potential exceptions (like HTTPError, JSONDecodeError)
|
|
154
|
+
err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
|
|
155
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to start conversation ({type(e).__name__}): {e} - {err_text}") from e
|
|
143
156
|
|
|
144
157
|
def ask(
|
|
145
158
|
self,
|
|
@@ -188,65 +201,101 @@ class PiAI(Provider):
|
|
|
188
201
|
}
|
|
189
202
|
|
|
190
203
|
def process_stream():
|
|
191
|
-
#
|
|
192
|
-
|
|
193
|
-
self.url
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
timeout=self.timeout
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
# If primary URL fails, try fallback URL
|
|
202
|
-
if not response.ok and self.url == self.primary_url:
|
|
203
|
-
self.url = self.fallback_url
|
|
204
|
-
response = self.scraper.post(
|
|
205
|
-
self.url,
|
|
206
|
-
headers=self.headers,
|
|
207
|
-
cookies=self.cookies,
|
|
204
|
+
try: # Add outer try block for error handling
|
|
205
|
+
# Try primary URL first
|
|
206
|
+
current_url = self.url
|
|
207
|
+
response = self.session.post(
|
|
208
|
+
current_url,
|
|
209
|
+
# headers are set on the session
|
|
210
|
+
# cookies are handled by the session
|
|
208
211
|
json=data,
|
|
209
212
|
stream=True,
|
|
210
|
-
timeout=self.timeout
|
|
213
|
+
timeout=self.timeout,
|
|
214
|
+
# proxies are set on the session
|
|
215
|
+
impersonate="chrome110" # Use a common impersonation profile
|
|
211
216
|
)
|
|
212
217
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
218
|
+
# If primary URL fails, try fallback URL
|
|
219
|
+
if not response.ok and current_url == self.primary_url:
|
|
220
|
+
current_url = self.fallback_url
|
|
221
|
+
response = self.session.post(
|
|
222
|
+
current_url,
|
|
223
|
+
# headers are set on the session
|
|
224
|
+
# cookies are handled by the session
|
|
225
|
+
json=data,
|
|
226
|
+
stream=True,
|
|
227
|
+
timeout=self.timeout,
|
|
228
|
+
# proxies are set on the session
|
|
229
|
+
impersonate="chrome110" # Use a common impersonation profile
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
response.raise_for_status() # Check for HTTP errors after potential fallback
|
|
233
|
+
|
|
234
|
+
# --- Process response content ---
|
|
235
|
+
# Note: curl_cffi's response.content might behave differently for streams.
|
|
236
|
+
# It's often better to iterate directly.
|
|
237
|
+
# output_str = response.content.decode('utf-8') # Avoid reading full content at once for streams
|
|
238
|
+
|
|
239
|
+
sids = []
|
|
240
|
+
streaming_text = ""
|
|
241
|
+
full_raw_data_for_sids = "" # Accumulate raw data to find SIDs later
|
|
242
|
+
|
|
243
|
+
# Iterate over bytes and decode manually
|
|
244
|
+
for line_bytes in response.iter_lines():
|
|
245
|
+
if line_bytes:
|
|
246
|
+
line = line_bytes.decode('utf-8')
|
|
247
|
+
full_raw_data_for_sids += line + "\n" # Accumulate for SID extraction
|
|
248
|
+
if line.startswith("data: "):
|
|
249
|
+
try:
|
|
250
|
+
parsed_data = json.loads(line[6:])
|
|
251
|
+
if 'text' in parsed_data and parsed_data['text'] is not None:
|
|
252
|
+
chunk_text = parsed_data['text']
|
|
253
|
+
streaming_text += chunk_text
|
|
254
|
+
# Yield raw JSON object or dict with aggregated text
|
|
255
|
+
yield parsed_data if raw else dict(text=streaming_text)
|
|
256
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
# Extract SIDs after processing the stream
|
|
260
|
+
sids = re.findall(r'"sid":"(.*?)"', full_raw_data_for_sids)
|
|
261
|
+
second_sid = sids[1] if len(sids) >= 2 else None
|
|
262
|
+
|
|
263
|
+
if voice and voice_name and second_sid:
|
|
264
|
+
threading.Thread(
|
|
265
|
+
target=self.download_audio_threaded,
|
|
266
|
+
args=(voice_name, second_sid, output_file)
|
|
267
|
+
).start()
|
|
268
|
+
|
|
269
|
+
# Update history and last response after stream finishes
|
|
270
|
+
self.last_response = dict(text=streaming_text)
|
|
271
|
+
self.conversation.update_chat_history(
|
|
272
|
+
prompt, streaming_text
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
except CurlError as e: # Catch CurlError
|
|
276
|
+
raise exceptions.FailedToGenerateResponseError(f"API request failed (CurlError): {e}") from e
|
|
277
|
+
except Exception as e: # Catch other potential exceptions (like HTTPError)
|
|
278
|
+
err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
|
|
279
|
+
raise exceptions.FailedToGenerateResponseError(f"API request failed ({type(e).__name__}): {e} - {err_text}") from e
|
|
280
|
+
|
|
242
281
|
|
|
243
282
|
if stream:
|
|
244
283
|
return process_stream()
|
|
245
284
|
else:
|
|
246
285
|
# For non-stream, collect all responses and return the final one
|
|
286
|
+
final_text = ""
|
|
287
|
+
# Ensure raw=False so process_stream yields dicts
|
|
247
288
|
for res in process_stream():
|
|
248
|
-
|
|
249
|
-
|
|
289
|
+
if isinstance(res, dict) and "text" in res:
|
|
290
|
+
final_text = res["text"] # Keep updating with the latest aggregated text
|
|
291
|
+
# Handle raw JSON object case if raw=True was passed
|
|
292
|
+
elif raw and isinstance(res, dict) and 'text' in res and res['text'] is not None:
|
|
293
|
+
final_text += res['text'] # Append chunks if raw
|
|
294
|
+
|
|
295
|
+
# last_response and history are updated within process_stream
|
|
296
|
+
# Return the final aggregated response dict or raw text
|
|
297
|
+
return final_text if raw else self.last_response
|
|
298
|
+
|
|
250
299
|
|
|
251
300
|
def chat(
|
|
252
301
|
self,
|
|
@@ -280,28 +329,35 @@ class PiAI(Provider):
|
|
|
280
329
|
|
|
281
330
|
if stream:
|
|
282
331
|
def stream_generator():
|
|
283
|
-
|
|
332
|
+
# ask() yields dicts or raw JSON objects when streaming
|
|
333
|
+
gen = self.ask(
|
|
284
334
|
prompt,
|
|
285
335
|
stream=True,
|
|
336
|
+
raw=False, # Ensure ask yields dicts for get_message
|
|
286
337
|
optimizer=optimizer,
|
|
287
338
|
conversationally=conversationally,
|
|
288
339
|
voice=voice,
|
|
289
340
|
voice_name=voice_name,
|
|
290
341
|
output_file=output_file
|
|
291
|
-
)
|
|
292
|
-
|
|
342
|
+
)
|
|
343
|
+
for response_dict in gen:
|
|
344
|
+
# get_message expects dict
|
|
345
|
+
yield self.get_message(response_dict)
|
|
293
346
|
return stream_generator()
|
|
294
347
|
else:
|
|
295
|
-
|
|
348
|
+
# ask() returns dict or raw text when not streaming
|
|
349
|
+
response_data = self.ask(
|
|
296
350
|
prompt,
|
|
297
351
|
stream=False,
|
|
352
|
+
raw=False, # Ensure ask returns dict for get_message
|
|
298
353
|
optimizer=optimizer,
|
|
299
354
|
conversationally=conversationally,
|
|
300
355
|
voice=voice,
|
|
301
356
|
voice_name=voice_name,
|
|
302
357
|
output_file=output_file
|
|
303
358
|
)
|
|
304
|
-
|
|
359
|
+
# get_message expects dict
|
|
360
|
+
return self.get_message(response_data)
|
|
305
361
|
|
|
306
362
|
def get_message(self, response: dict) -> str:
|
|
307
363
|
"""Retrieves message only from response"""
|
|
@@ -317,28 +373,48 @@ class PiAI(Provider):
|
|
|
317
373
|
}
|
|
318
374
|
|
|
319
375
|
try:
|
|
320
|
-
|
|
376
|
+
# Use curl_cffi session get with impersonate
|
|
377
|
+
audio_response = self.session.get(
|
|
321
378
|
'https://pi.ai/api/chat/voice',
|
|
322
379
|
params=params,
|
|
323
|
-
cookies
|
|
324
|
-
headers
|
|
325
|
-
timeout=self.timeout
|
|
380
|
+
# cookies are handled by the session
|
|
381
|
+
# headers are set on the session
|
|
382
|
+
timeout=self.timeout,
|
|
383
|
+
# proxies are set on the session
|
|
384
|
+
impersonate="chrome110" # Use a common impersonation profile
|
|
326
385
|
)
|
|
327
|
-
|
|
328
|
-
if not audio_response.ok:
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
audio_response.raise_for_status()
|
|
386
|
+
audio_response.raise_for_status() # Check for HTTP errors
|
|
332
387
|
|
|
333
388
|
with open(output_file, "wb") as file:
|
|
334
389
|
file.write(audio_response.content)
|
|
335
390
|
|
|
336
|
-
except
|
|
391
|
+
except CurlError: # Catch CurlError
|
|
392
|
+
# Optionally log the error
|
|
393
|
+
pass
|
|
394
|
+
except Exception: # Catch other potential exceptions
|
|
395
|
+
# Optionally log the error
|
|
337
396
|
pass
|
|
338
397
|
|
|
339
398
|
if __name__ == '__main__':
|
|
399
|
+
# Ensure curl_cffi is installed
|
|
340
400
|
from rich import print
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
401
|
+
try: # Add try-except block for testing
|
|
402
|
+
ai = PiAI(timeout=60)
|
|
403
|
+
print("[bold blue]Testing Chat (Stream):[/bold blue]")
|
|
404
|
+
response = ai.chat(input(">>> "), stream=True)
|
|
405
|
+
full_response = ""
|
|
406
|
+
for chunk in response:
|
|
407
|
+
print(chunk, end="", flush=True)
|
|
408
|
+
full_response += chunk
|
|
409
|
+
print("\n[bold green]Stream Test Complete.[/bold green]")
|
|
410
|
+
|
|
411
|
+
# Optional: Test non-stream
|
|
412
|
+
# print("\n[bold blue]Testing Chat (Non-Stream):[/bold blue]")
|
|
413
|
+
# response_non_stream = ai.chat("Hello again", stream=False)
|
|
414
|
+
# print(response_non_stream)
|
|
415
|
+
# print("[bold green]Non-Stream Test Complete.[/bold green]")
|
|
416
|
+
|
|
417
|
+
except exceptions.FailedToGenerateResponseError as e:
|
|
418
|
+
print(f"\n[bold red]API Error:[/bold red] {e}")
|
|
419
|
+
except Exception as e:
|
|
420
|
+
print(f"\n[bold red]An unexpected error occurred:[/bold red] {e}")
|
webscout/Provider/PizzaGPT.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
from curl_cffi.requests import Session
|
|
2
|
+
from curl_cffi import CurlError
|
|
2
3
|
import json
|
|
3
4
|
import re
|
|
4
5
|
from typing import Any, Dict, Optional, Union, Generator
|
|
@@ -17,7 +18,7 @@ class PIZZAGPT(Provider):
|
|
|
17
18
|
def __init__(
|
|
18
19
|
self,
|
|
19
20
|
is_conversation: bool = True,
|
|
20
|
-
max_tokens: int = 600,
|
|
21
|
+
max_tokens: int = 600, # Note: max_tokens is not used by this API
|
|
21
22
|
timeout: int = 30,
|
|
22
23
|
intro: str = None,
|
|
23
24
|
filepath: str = None,
|
|
@@ -31,7 +32,8 @@ class PIZZAGPT(Provider):
|
|
|
31
32
|
if model not in self.AVAILABLE_MODELS:
|
|
32
33
|
raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
# Initialize curl_cffi Session
|
|
36
|
+
self.session = Session()
|
|
35
37
|
self.is_conversation = is_conversation
|
|
36
38
|
self.max_tokens_to_sample = max_tokens
|
|
37
39
|
self.api_endpoint = "https://www.pizzagpt.it/api/chatx-completion"
|
|
@@ -48,8 +50,6 @@ class PIZZAGPT(Provider):
|
|
|
48
50
|
"referer": "https://www.pizzagpt.it/en",
|
|
49
51
|
"user-agent": Lit().random(),
|
|
50
52
|
"x-secret": "Marinara",
|
|
51
|
-
"sec-ch-ua": '"Chromium";v="134", "Not:A-Brand";v="24"',
|
|
52
|
-
"sec-ch-ua-platform": '"Windows"'
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
self.__available_optimizers = (
|
|
@@ -57,7 +57,10 @@ class PIZZAGPT(Provider):
|
|
|
57
57
|
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
+
# Update curl_cffi session headers and proxies
|
|
60
61
|
self.session.headers.update(self.headers)
|
|
62
|
+
self.session.proxies = proxies # Assign proxies directly
|
|
63
|
+
|
|
61
64
|
Conversation.intro = (
|
|
62
65
|
AwesomePrompts().get_act(
|
|
63
66
|
act, raise_not_found=True, default=None, case_insensitive=True
|
|
@@ -70,7 +73,6 @@ class PIZZAGPT(Provider):
|
|
|
70
73
|
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
71
74
|
)
|
|
72
75
|
self.conversation.history_offset = history_offset
|
|
73
|
-
self.session.proxies = proxies
|
|
74
76
|
|
|
75
77
|
def _extract_content(self, text: str) -> Dict[str, Any]:
|
|
76
78
|
"""
|
|
@@ -104,8 +106,8 @@ class PIZZAGPT(Provider):
|
|
|
104
106
|
def ask(
|
|
105
107
|
self,
|
|
106
108
|
prompt: str,
|
|
107
|
-
stream: bool = False,
|
|
108
|
-
raw: bool = False,
|
|
109
|
+
stream: bool = False, # Note: API does not support streaming
|
|
110
|
+
raw: bool = False, # Keep raw param for interface consistency
|
|
109
111
|
optimizer: str = None,
|
|
110
112
|
conversationally: bool = False,
|
|
111
113
|
web_search: bool = False,
|
|
@@ -116,9 +118,7 @@ class PIZZAGPT(Provider):
|
|
|
116
118
|
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
117
119
|
if optimizer:
|
|
118
120
|
if optimizer in self.__available_optimizers:
|
|
119
|
-
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
120
|
-
conversation_prompt if conversationally else prompt
|
|
121
|
-
)
|
|
121
|
+
conversation_prompt = getattr(Optimizers, optimizer)(conversation_prompt if conversationally else prompt)
|
|
122
122
|
else:
|
|
123
123
|
raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
|
|
124
124
|
|
|
@@ -129,16 +129,17 @@ class PIZZAGPT(Provider):
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
try:
|
|
132
|
+
# Use curl_cffi session post with impersonate
|
|
132
133
|
response = self.session.post(
|
|
133
134
|
self.api_endpoint,
|
|
135
|
+
# headers are set on the session
|
|
134
136
|
json=payload,
|
|
135
|
-
timeout=self.timeout
|
|
137
|
+
timeout=self.timeout,
|
|
138
|
+
# proxies are set on the session
|
|
139
|
+
impersonate="chrome110" # Use a common impersonation profile
|
|
136
140
|
)
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
140
|
-
f"Failed to generate response - ({response.status_code}, {response.reason})"
|
|
141
|
-
)
|
|
142
|
+
response.raise_for_status() # Check for HTTP errors
|
|
142
143
|
|
|
143
144
|
response_text = response.text
|
|
144
145
|
if not response_text:
|
|
@@ -147,52 +148,81 @@ class PIZZAGPT(Provider):
|
|
|
147
148
|
try:
|
|
148
149
|
resp = self._extract_content(response_text)
|
|
149
150
|
|
|
150
|
-
self.last_response
|
|
151
|
+
self.last_response = {"text": resp['content']} # Store only text in last_response
|
|
151
152
|
self.conversation.update_chat_history(
|
|
152
153
|
prompt, self.get_message(self.last_response)
|
|
153
154
|
)
|
|
154
|
-
|
|
155
|
+
# Return the full extracted data (content + citations) or raw text
|
|
156
|
+
return response_text if raw else resp
|
|
155
157
|
|
|
156
158
|
except Exception as e:
|
|
157
159
|
raise exceptions.FailedToGenerateResponseError(f"Failed to parse response: {str(e)}")
|
|
158
160
|
|
|
159
|
-
except
|
|
160
|
-
raise exceptions.FailedToGenerateResponseError(f"Request failed: {str(e)}")
|
|
161
|
+
except CurlError as e: # Catch CurlError
|
|
162
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {str(e)}") from e
|
|
163
|
+
except Exception as e: # Catch other potential exceptions (like HTTPError)
|
|
164
|
+
err_text = getattr(e, 'response', None) and getattr(e.response, 'text', '')
|
|
165
|
+
raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e} - {err_text}") from e
|
|
161
166
|
|
|
162
167
|
def chat(
|
|
163
168
|
self,
|
|
164
169
|
prompt: str,
|
|
165
|
-
stream: bool = False,
|
|
170
|
+
stream: bool = False, # Keep stream param for interface consistency
|
|
166
171
|
optimizer: str = None,
|
|
167
172
|
conversationally: bool = False,
|
|
168
173
|
web_search: bool = False,
|
|
174
|
+
# Add raw parameter for consistency
|
|
175
|
+
raw: bool = False
|
|
169
176
|
) -> str:
|
|
170
177
|
"""
|
|
171
178
|
Chat with PizzaGPT with optional web search capability.
|
|
172
179
|
"""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
# API doesn't stream, call ask directly
|
|
181
|
+
response_data = self.ask(
|
|
182
|
+
prompt,
|
|
183
|
+
stream=False, # Call ask in non-stream mode
|
|
184
|
+
raw=raw, # Pass raw flag to ask
|
|
185
|
+
optimizer=optimizer,
|
|
186
|
+
conversationally=conversationally,
|
|
187
|
+
web_search=web_search
|
|
188
|
+
)
|
|
189
|
+
# If raw=True, ask returns string, otherwise dict
|
|
190
|
+
return response_data if raw else self.get_message(response_data)
|
|
191
|
+
|
|
183
192
|
|
|
184
193
|
def get_message(self, response: dict) -> str:
|
|
185
194
|
"""Extract message from response dictionary."""
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
# Handle case where raw response (string) might be passed mistakenly
|
|
196
|
+
if isinstance(response, str):
|
|
197
|
+
# Attempt to parse if it looks like the expected structure, otherwise return as is
|
|
198
|
+
try:
|
|
199
|
+
extracted = self._extract_content(response)
|
|
200
|
+
return extracted.get("content", "")
|
|
201
|
+
except:
|
|
202
|
+
return response # Return raw string if parsing fails
|
|
203
|
+
elif isinstance(response, dict):
|
|
204
|
+
# If it's already the extracted dict from ask(raw=False)
|
|
205
|
+
if "content" in response:
|
|
206
|
+
return response.get("content", "")
|
|
207
|
+
# If it's the last_response format
|
|
208
|
+
elif "text" in response:
|
|
209
|
+
return response.get("text", "")
|
|
210
|
+
return "" # Default empty string
|
|
188
211
|
|
|
189
212
|
if __name__ == "__main__":
|
|
213
|
+
# Ensure curl_cffi is installed
|
|
190
214
|
from rich import print
|
|
191
215
|
|
|
192
216
|
# Example usage with web search enabled
|
|
193
|
-
ai = PIZZAGPT()
|
|
217
|
+
ai = PIZZAGPT(timeout=60)
|
|
194
218
|
try:
|
|
195
|
-
|
|
219
|
+
print("[bold blue]Testing Chat (Web Search Disabled):[/bold blue]")
|
|
220
|
+
response = ai.chat("hi", web_search=False)
|
|
196
221
|
print(response)
|
|
222
|
+
|
|
223
|
+
# print("\n[bold blue]Testing Chat (Web Search Enabled):[/bold blue]")
|
|
224
|
+
# response_web = ai.chat("What's the weather in Rome?", web_search=True)
|
|
225
|
+
# print(response_web)
|
|
226
|
+
|
|
197
227
|
except Exception as e:
|
|
198
|
-
print(f"Error: {str(e)}")
|
|
228
|
+
print(f"[bold red]Error:[/bold red] {str(e)}")
|