webscout 8.2.3__py3-none-any.whl → 8.2.5__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.
- webscout/AIutel.py +226 -14
- webscout/Bard.py +579 -206
- webscout/DWEBS.py +78 -35
- webscout/Extra/gguf.py +2 -0
- webscout/Extra/tempmail/base.py +1 -1
- webscout/Provider/AISEARCH/hika_search.py +4 -0
- webscout/Provider/AISEARCH/scira_search.py +2 -5
- webscout/Provider/Aitopia.py +75 -51
- webscout/Provider/AllenAI.py +181 -147
- webscout/Provider/ChatGPTClone.py +97 -86
- webscout/Provider/ChatSandbox.py +342 -0
- webscout/Provider/Cloudflare.py +79 -32
- webscout/Provider/Deepinfra.py +135 -94
- webscout/Provider/ElectronHub.py +103 -39
- webscout/Provider/ExaChat.py +36 -20
- webscout/Provider/GPTWeb.py +103 -47
- webscout/Provider/GithubChat.py +52 -49
- webscout/Provider/GizAI.py +283 -0
- webscout/Provider/Glider.py +39 -28
- webscout/Provider/Groq.py +222 -91
- webscout/Provider/HeckAI.py +93 -69
- webscout/Provider/HuggingFaceChat.py +113 -106
- webscout/Provider/Hunyuan.py +94 -83
- webscout/Provider/Jadve.py +104 -79
- webscout/Provider/LambdaChat.py +142 -123
- webscout/Provider/Llama3.py +94 -39
- webscout/Provider/MCPCore.py +315 -0
- webscout/Provider/Marcus.py +95 -37
- webscout/Provider/Netwrck.py +94 -52
- 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/exachat.py +4 -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/OPENAI/scirachat.py +2 -4
- webscout/Provider/OPENAI/textpollinations.py +20 -22
- webscout/Provider/OPENAI/toolbaz.py +1 -0
- webscout/Provider/OpenGPT.py +48 -38
- webscout/Provider/PI.py +178 -93
- webscout/Provider/PizzaGPT.py +66 -36
- webscout/Provider/StandardInput.py +42 -30
- webscout/Provider/TeachAnything.py +95 -52
- webscout/Provider/TextPollinationsAI.py +138 -78
- webscout/Provider/TwoAI.py +162 -81
- webscout/Provider/TypliAI.py +305 -0
- webscout/Provider/Venice.py +97 -58
- webscout/Provider/VercelAI.py +33 -14
- webscout/Provider/WiseCat.py +65 -28
- webscout/Provider/Writecream.py +37 -11
- webscout/Provider/WritingMate.py +135 -63
- webscout/Provider/__init__.py +9 -27
- webscout/Provider/ai4chat.py +6 -7
- webscout/Provider/asksteve.py +53 -44
- webscout/Provider/cerebras.py +77 -31
- webscout/Provider/chatglm.py +47 -37
- webscout/Provider/copilot.py +0 -3
- webscout/Provider/elmo.py +109 -60
- webscout/Provider/granite.py +102 -54
- webscout/Provider/hermes.py +95 -48
- webscout/Provider/koala.py +1 -1
- webscout/Provider/learnfastai.py +113 -54
- webscout/Provider/llama3mitril.py +86 -51
- webscout/Provider/llmchat.py +88 -46
- webscout/Provider/llmchatco.py +110 -115
- webscout/Provider/meta.py +41 -37
- webscout/Provider/multichat.py +67 -28
- webscout/Provider/scira_chat.py +49 -30
- webscout/Provider/scnet.py +106 -53
- webscout/Provider/searchchat.py +87 -88
- webscout/Provider/sonus.py +113 -63
- webscout/Provider/toolbaz.py +115 -82
- webscout/Provider/turboseek.py +90 -43
- webscout/Provider/tutorai.py +82 -64
- webscout/Provider/typefully.py +85 -35
- webscout/Provider/typegpt.py +118 -61
- webscout/Provider/uncovr.py +132 -76
- webscout/Provider/x0gpt.py +69 -26
- webscout/Provider/yep.py +79 -66
- webscout/cli.py +256 -0
- webscout/conversation.py +34 -22
- webscout/exceptions.py +23 -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.5.dist-info}/METADATA +183 -50
- {webscout-8.2.3.dist-info → webscout-8.2.5.dist-info}/RECORD +97 -113
- {webscout-8.2.3.dist-info → webscout-8.2.5.dist-info}/WHEEL +1 -1
- webscout-8.2.5.dist-info/entry_points.txt +3 -0
- {webscout-8.2.3.dist-info → webscout-8.2.5.dist-info}/top_level.txt +0 -1
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/Chatify.py +0 -175
- 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/askmyai.py +0 -158
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llamatutor.py +0 -192
- webscout-8.2.3.dist-info/entry_points.txt +0 -5
- {webscout-8.2.3.dist-info → webscout-8.2.5.dist-info/licenses}/LICENSE.md +0 -0
webscout/Provider/x0gpt.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
from typing import Union, Any, Dict
|
|
1
|
+
from typing import Optional, Union, Any, Dict
|
|
2
2
|
from uuid import uuid4
|
|
3
|
-
import
|
|
3
|
+
from curl_cffi import CurlError
|
|
4
|
+
from curl_cffi.requests import Session
|
|
4
5
|
import re
|
|
5
6
|
|
|
6
7
|
from webscout.AIutel import Optimizers
|
|
7
8
|
from webscout.AIutel import Conversation
|
|
8
|
-
from webscout.AIutel import AwesomePrompts
|
|
9
|
+
from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
|
|
9
10
|
from webscout.AIbase import Provider
|
|
10
11
|
from webscout import exceptions
|
|
11
12
|
from webscout.litagent import LitAgent
|
|
13
|
+
# Import HTTPVersion enum
|
|
14
|
+
from curl_cffi.const import CurlHttpVersion
|
|
12
15
|
|
|
13
16
|
class X0GPT(Provider):
|
|
14
17
|
"""
|
|
@@ -60,7 +63,8 @@ class X0GPT(Provider):
|
|
|
60
63
|
>>> print(ai.system_prompt)
|
|
61
64
|
'You are a friendly assistant.'
|
|
62
65
|
"""
|
|
63
|
-
|
|
66
|
+
# Initialize curl_cffi Session instead of requests.Session
|
|
67
|
+
self.session = Session()
|
|
64
68
|
self.is_conversation = is_conversation
|
|
65
69
|
self.max_tokens_to_sample = max_tokens
|
|
66
70
|
self.api_endpoint = "https://x0-gpt.devwtf.in/api/stream/reply"
|
|
@@ -77,18 +81,18 @@ class X0GPT(Provider):
|
|
|
77
81
|
"path": "/api/stream/reply",
|
|
78
82
|
"scheme": "https",
|
|
79
83
|
"accept": "*/*",
|
|
80
|
-
"accept-encoding": "gzip, deflate, br, zstd",
|
|
84
|
+
"accept-encoding": "gzip, deflate, br, zstd", # Keep zstd for now
|
|
81
85
|
"accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
|
|
82
|
-
"content-length": "114",
|
|
86
|
+
# "content-length": "114", # Let curl_cffi handle content-length
|
|
83
87
|
"content-type": "application/json",
|
|
84
88
|
"dnt": "1",
|
|
85
89
|
"origin": "https://x0-gpt.devwtf.in",
|
|
86
|
-
"priority": "u=1, i",
|
|
90
|
+
# "priority": "u=1, i", # Remove priority header
|
|
87
91
|
"referer": "https://x0-gpt.devwtf.in/chat",
|
|
88
92
|
"sec-ch-ua": '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
|
|
89
93
|
"sec-ch-ua-mobile": "?0",
|
|
90
94
|
"sec-ch-ua-platform": '"Windows"',
|
|
91
|
-
"user-agent": self.agent.random()
|
|
95
|
+
"user-agent": self.agent.random()
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
self.__available_optimizers = (
|
|
@@ -96,7 +100,10 @@ class X0GPT(Provider):
|
|
|
96
100
|
for method in dir(Optimizers)
|
|
97
101
|
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
98
102
|
)
|
|
103
|
+
# Update curl_cffi session headers and proxies
|
|
99
104
|
self.session.headers.update(self.headers)
|
|
105
|
+
self.session.proxies = proxies
|
|
106
|
+
|
|
100
107
|
Conversation.intro = (
|
|
101
108
|
AwesomePrompts().get_act(
|
|
102
109
|
act, raise_not_found=True, default=None, case_insensitive=True
|
|
@@ -108,7 +115,17 @@ class X0GPT(Provider):
|
|
|
108
115
|
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
109
116
|
)
|
|
110
117
|
self.conversation.history_offset = history_offset
|
|
111
|
-
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _x0gpt_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
121
|
+
"""Extracts content from the x0gpt stream format '0:"..."'."""
|
|
122
|
+
if isinstance(chunk, str):
|
|
123
|
+
match = re.search(r'0:"(.*?)"', chunk)
|
|
124
|
+
if match:
|
|
125
|
+
# Decode potential unicode escapes like \u00e9
|
|
126
|
+
content = match.group(1).encode().decode('unicode_escape')
|
|
127
|
+
return content.replace('\\\\', '\\').replace('\\"', '"') # Handle escaped backslashes and quotes
|
|
128
|
+
return None
|
|
112
129
|
|
|
113
130
|
def ask(
|
|
114
131
|
self,
|
|
@@ -158,25 +175,48 @@ class X0GPT(Provider):
|
|
|
158
175
|
}
|
|
159
176
|
|
|
160
177
|
def for_stream():
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
try:
|
|
179
|
+
# Use curl_cffi session post with updated impersonate and http_version
|
|
180
|
+
response = self.session.post(
|
|
181
|
+
self.api_endpoint,
|
|
182
|
+
headers=self.headers,
|
|
183
|
+
json=payload,
|
|
184
|
+
stream=True,
|
|
185
|
+
timeout=self.timeout,
|
|
186
|
+
impersonate="chrome120", # Try a different impersonation profile
|
|
187
|
+
http_version=CurlHttpVersion.V1_1 # Force HTTP/1.1
|
|
165
188
|
)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
if not response.ok:
|
|
190
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
191
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
streaming_response = ""
|
|
195
|
+
# Use sanitize_stream with the custom extractor
|
|
196
|
+
processed_stream = sanitize_stream(
|
|
197
|
+
data=response.iter_content(chunk_size=None), # Pass byte iterator
|
|
198
|
+
intro_value=None, # No simple prefix to remove here
|
|
199
|
+
to_json=False, # Content is not JSON
|
|
200
|
+
content_extractor=self._x0gpt_extractor # Use the specific extractor
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
for content_chunk in processed_stream:
|
|
204
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
205
|
+
streaming_response += content_chunk
|
|
206
|
+
yield content_chunk if raw else dict(text=content_chunk)
|
|
207
|
+
|
|
208
|
+
self.last_response.update(dict(text=streaming_response))
|
|
209
|
+
self.conversation.update_chat_history(
|
|
210
|
+
prompt, self.get_message(self.last_response)
|
|
211
|
+
)
|
|
212
|
+
except CurlError as e: # Catch CurlError
|
|
213
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
214
|
+
except Exception as e: # Catch other potential exceptions
|
|
215
|
+
# Include the original exception type in the message for clarity
|
|
216
|
+
raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}")
|
|
178
217
|
|
|
179
218
|
def for_non_stream():
|
|
219
|
+
# This function implicitly uses the updated for_stream
|
|
180
220
|
for _ in for_stream():
|
|
181
221
|
pass
|
|
182
222
|
return self.last_response
|
|
@@ -245,7 +285,10 @@ class X0GPT(Provider):
|
|
|
245
285
|
'Why did the scarecrow win an award? Because he was outstanding in his field!'
|
|
246
286
|
"""
|
|
247
287
|
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
248
|
-
|
|
288
|
+
# Ensure text exists before processing
|
|
289
|
+
text = response.get("text", "")
|
|
290
|
+
# Formatting is now mostly handled by the extractor, just return
|
|
291
|
+
formatted_text = text
|
|
249
292
|
return formatted_text
|
|
250
293
|
|
|
251
294
|
if __name__ == "__main__":
|
webscout/Provider/yep.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
import cloudscraper
|
|
3
2
|
import json
|
|
3
|
+
from curl_cffi import CurlError
|
|
4
|
+
from curl_cffi.requests import Session
|
|
4
5
|
|
|
5
6
|
from typing import Any, Dict, Optional, Generator, Union, List, TypeVar
|
|
6
7
|
|
|
7
8
|
from webscout.AIutel import Optimizers
|
|
8
|
-
from webscout.AIutel import AwesomePrompts
|
|
9
|
+
from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
|
|
9
10
|
from webscout.AIbase import Provider
|
|
10
11
|
from webscout import exceptions
|
|
11
12
|
from webscout.litagent import LitAgent
|
|
@@ -62,7 +63,8 @@ class YEPCHAT(Provider):
|
|
|
62
63
|
f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
|
|
63
64
|
)
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
# Initialize curl_cffi Session instead of cloudscraper
|
|
67
|
+
self.session = Session()
|
|
66
68
|
self.is_conversation = is_conversation
|
|
67
69
|
self.max_tokens_to_sample = max_tokens
|
|
68
70
|
self.chat_endpoint = "https://api.yep.com/v1/chat/completions"
|
|
@@ -111,11 +113,10 @@ class YEPCHAT(Provider):
|
|
|
111
113
|
is_conversation, self.max_tokens_to_sample, filepath, update_file, tools=tools
|
|
112
114
|
)
|
|
113
115
|
self.conversation.history_offset = history_offset
|
|
116
|
+
# Set consistent headers and proxies for the curl_cffi session
|
|
117
|
+
self.session.headers.update(self.headers)
|
|
114
118
|
self.session.proxies = proxies
|
|
115
|
-
|
|
116
|
-
# Set consistent headers for the scraper session
|
|
117
|
-
for header, value in self.headers.items():
|
|
118
|
-
self.session.headers[header] = value
|
|
119
|
+
# Note: curl_cffi handles cookies differently, passed directly in requests
|
|
119
120
|
|
|
120
121
|
def refresh_identity(self, browser: str = None):
|
|
121
122
|
"""
|
|
@@ -137,10 +138,9 @@ class YEPCHAT(Provider):
|
|
|
137
138
|
})
|
|
138
139
|
|
|
139
140
|
# Update session headers
|
|
140
|
-
|
|
141
|
-
self.session.headers[header] = value
|
|
141
|
+
self.session.headers.update(self.headers)
|
|
142
142
|
|
|
143
|
-
# Generate new cookies
|
|
143
|
+
# Generate new cookies (will be passed in requests)
|
|
144
144
|
self.cookies = {"__Host-session": uuid.uuid4().hex, '__cf_bm': uuid.uuid4().hex}
|
|
145
145
|
|
|
146
146
|
return self.fingerprint
|
|
@@ -192,73 +192,82 @@ class YEPCHAT(Provider):
|
|
|
192
192
|
|
|
193
193
|
def for_stream():
|
|
194
194
|
try:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
else:
|
|
195
|
+
# buffer = b"" # No longer needed here
|
|
196
|
+
# Use curl_cffi session post, pass cookies explicitly
|
|
197
|
+
response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
|
|
198
|
+
|
|
199
|
+
if not response.ok:
|
|
200
|
+
# If we get a non-200 response, try refreshing our identity once
|
|
201
|
+
if response.status_code in [403, 429]:
|
|
202
|
+
self.refresh_identity()
|
|
203
|
+
# Retry with new identity
|
|
204
|
+
# Use curl_cffi session post, pass cookies explicitly
|
|
205
|
+
retry_response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, stream=True, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
|
|
206
|
+
if not retry_response.ok:
|
|
208
207
|
raise exceptions.FailedToGenerateResponseError(
|
|
209
|
-
f"Failed to generate response - ({
|
|
208
|
+
f"Failed to generate response after identity refresh - ({retry_response.status_code}, {retry_response.reason}) - {retry_response.text}"
|
|
210
209
|
)
|
|
210
|
+
response = retry_response # Use the successful retry response
|
|
211
|
+
else:
|
|
212
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
213
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
214
|
+
)
|
|
211
215
|
|
|
212
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
# If tool call failed, update history with error
|
|
246
|
-
self.conversation.update_chat_history(prompt,
|
|
247
|
-
f"Error executing tool call: {response_data['result']}")
|
|
216
|
+
# --- Start of stream processing block (should be outside the 'if not response.ok' block) ---
|
|
217
|
+
streaming_text = ""
|
|
218
|
+
|
|
219
|
+
# Use sanitize_stream to process the lines
|
|
220
|
+
processed_stream = sanitize_stream(
|
|
221
|
+
data=response.iter_content(chunk_size=None), # Pass the byte iterator directly
|
|
222
|
+
intro_value="data:",
|
|
223
|
+
to_json=True, # Yep sends JSON after 'data:'
|
|
224
|
+
skip_markers=["[DONE]"], # Skip the final marker
|
|
225
|
+
yield_raw_on_error=False, # Only process valid JSON data
|
|
226
|
+
# --- Add the content extractor ---
|
|
227
|
+
content_extractor=lambda chunk: chunk.get('choices', [{}])[0].get('delta', {}).get('content') if isinstance(chunk, dict) else None
|
|
228
|
+
)
|
|
229
|
+
# The loop now yields the final extracted string content directly
|
|
230
|
+
for content_chunk in processed_stream:
|
|
231
|
+
# --- TEMPORARY DEBUG PRINT ---
|
|
232
|
+
# print(f"\nDEBUG: Received extracted content: {content_chunk!r}\n", flush=True) # Keep or remove debug print as needed
|
|
233
|
+
if content_chunk and isinstance(content_chunk, str): # Ensure it's a non-empty string
|
|
234
|
+
streaming_text += content_chunk
|
|
235
|
+
# Yield dict or raw string chunk based on 'raw' flag
|
|
236
|
+
yield dict(text=content_chunk) if not raw else content_chunk
|
|
237
|
+
# --- End of stream processing block ---
|
|
238
|
+
|
|
239
|
+
# Check if the response contains a tool call (This should happen *after* processing the stream)
|
|
240
|
+
response_data = self.conversation.handle_tool_response(streaming_text)
|
|
241
|
+
|
|
242
|
+
if response_data["is_tool_call"]:
|
|
243
|
+
# Handle tool call results
|
|
244
|
+
if response_data["success"]:
|
|
245
|
+
for tool_call in response_data.get("tool_calls", []):
|
|
246
|
+
tool_name = tool_call.get("name", "unknown_tool")
|
|
247
|
+
result = response_data["result"]
|
|
248
|
+
self.conversation.update_chat_history_with_tool(prompt, tool_name, result)
|
|
248
249
|
else:
|
|
249
|
-
#
|
|
250
|
-
self.conversation.update_chat_history(prompt,
|
|
250
|
+
# If tool call failed, update history with error
|
|
251
|
+
self.conversation.update_chat_history(prompt,
|
|
252
|
+
f"Error executing tool call: {response_data['result']}")
|
|
253
|
+
else:
|
|
254
|
+
# Normal response handling
|
|
255
|
+
self.conversation.update_chat_history(prompt, streaming_text)
|
|
251
256
|
|
|
257
|
+
except CurlError as e: # Catch CurlError
|
|
258
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
252
259
|
except Exception as e:
|
|
253
260
|
raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
|
|
254
261
|
|
|
255
262
|
def for_non_stream():
|
|
256
263
|
try:
|
|
257
|
-
|
|
264
|
+
# Use curl_cffi session post, pass cookies explicitly
|
|
265
|
+
response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
|
|
258
266
|
if not response.ok:
|
|
259
267
|
if response.status_code in [403, 429]:
|
|
260
268
|
self.refresh_identity()
|
|
261
|
-
|
|
269
|
+
# Use curl_cffi session post, pass cookies explicitly
|
|
270
|
+
response = self.session.post(self.chat_endpoint, headers=self.headers, cookies=self.cookies, json=data, timeout=self.timeout, impersonate=self.fingerprint.get("browser_type", "chrome110"))
|
|
262
271
|
if not response.ok:
|
|
263
272
|
raise exceptions.FailedToGenerateResponseError(
|
|
264
273
|
f"Failed to generate response after identity refresh - ({response.status_code}, {response.reason}) - {response.text}"
|
|
@@ -268,6 +277,7 @@ class YEPCHAT(Provider):
|
|
|
268
277
|
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
269
278
|
)
|
|
270
279
|
|
|
280
|
+
# ... existing non-stream response handling code ...
|
|
271
281
|
response_data = response.json()
|
|
272
282
|
if 'choices' in response_data and len(response_data['choices']) > 0:
|
|
273
283
|
content = response_data['choices'][0].get('message', {}).get('content', '')
|
|
@@ -298,8 +308,10 @@ class YEPCHAT(Provider):
|
|
|
298
308
|
return {"text": content}
|
|
299
309
|
else:
|
|
300
310
|
raise exceptions.FailedToGenerateResponseError("No response content found")
|
|
311
|
+
except CurlError as e: # Catch CurlError
|
|
312
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
301
313
|
except Exception as e:
|
|
302
|
-
raise exceptions.FailedToGenerateResponseError(f"Request failed: e")
|
|
314
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
|
|
303
315
|
|
|
304
316
|
return for_stream() if stream else for_non_stream()
|
|
305
317
|
|
|
@@ -373,4 +385,5 @@ if __name__ == "__main__":
|
|
|
373
385
|
display_text = "Empty or invalid response"
|
|
374
386
|
print(f"{model:<50} {status:<10} {display_text}")
|
|
375
387
|
except Exception as e:
|
|
376
|
-
print(f"{model:<50} {'✗':<10} {str(e)}")
|
|
388
|
+
print(f"{model:<50} {'✗':<10} {str(e)}")
|
|
389
|
+
|
webscout/cli.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
from .swiftcli import CLI, option
|
|
3
3
|
from .webscout_search import WEBS
|
|
4
|
+
from .DWEBS import GoogleSearch # Import GoogleSearch from DWEBS
|
|
5
|
+
from .yep_search import YepSearch # Import YepSearch from yep_search
|
|
4
6
|
from .version import __version__
|
|
5
7
|
|
|
6
8
|
|
|
@@ -282,6 +284,260 @@ def weather(location: str, language: str, proxy: str = None, timeout: int = 10):
|
|
|
282
284
|
except Exception as e:
|
|
283
285
|
raise e
|
|
284
286
|
|
|
287
|
+
@app.command()
|
|
288
|
+
@option("--keywords", "-k", help="Search keywords", required=True)
|
|
289
|
+
@option("--region", "-r", help="Region for search results (ISO country code)", default="all")
|
|
290
|
+
@option("--safesearch", "-s", help="SafeSearch setting (on, moderate, off)", default="moderate")
|
|
291
|
+
@option("--max-results", "-m", help="Maximum number of results", type=int, default=10)
|
|
292
|
+
@option("--start-num", "-start", help="Starting position for pagination", type=int, default=0)
|
|
293
|
+
@option("--unique", "-u", help="Filter duplicate results", type=bool, default=True)
|
|
294
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=10)
|
|
295
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
296
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
297
|
+
def google_text(
|
|
298
|
+
keywords: str,
|
|
299
|
+
region: str,
|
|
300
|
+
safesearch: str,
|
|
301
|
+
max_results: int,
|
|
302
|
+
start_num: int,
|
|
303
|
+
unique: bool,
|
|
304
|
+
timeout: int = 10,
|
|
305
|
+
proxy: str = None,
|
|
306
|
+
impersonate: str = "chrome110"
|
|
307
|
+
):
|
|
308
|
+
"""Perform a text search using Google Search."""
|
|
309
|
+
google = GoogleSearch(
|
|
310
|
+
timeout=timeout,
|
|
311
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
312
|
+
verify=True,
|
|
313
|
+
lang="en",
|
|
314
|
+
sleep_interval=0.0,
|
|
315
|
+
impersonate=impersonate
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
results = google.text(
|
|
320
|
+
keywords=keywords,
|
|
321
|
+
region=region,
|
|
322
|
+
safesearch=safesearch,
|
|
323
|
+
max_results=max_results,
|
|
324
|
+
start_num=start_num,
|
|
325
|
+
unique=unique
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Convert SearchResult objects to dictionaries for printing
|
|
329
|
+
formatted_results = []
|
|
330
|
+
for result in results:
|
|
331
|
+
result_dict = {
|
|
332
|
+
"title": result.title,
|
|
333
|
+
"url": result.url,
|
|
334
|
+
"description": result.description,
|
|
335
|
+
}
|
|
336
|
+
# Add any metadata to the result dictionary
|
|
337
|
+
for k, v in result.metadata.items():
|
|
338
|
+
result_dict[k] = v
|
|
339
|
+
|
|
340
|
+
formatted_results.append(result_dict)
|
|
341
|
+
|
|
342
|
+
_print_data(formatted_results)
|
|
343
|
+
except Exception as e:
|
|
344
|
+
raise e
|
|
345
|
+
|
|
346
|
+
@app.command()
|
|
347
|
+
@option("--keywords", "-k", help="Search keywords", required=True)
|
|
348
|
+
@option("--region", "-r", help="Region for search results (ISO country code)", default="all")
|
|
349
|
+
@option("--safesearch", "-s", help="SafeSearch setting (on, moderate, off)", default="moderate")
|
|
350
|
+
@option("--max-results", "-m", help="Maximum number of results", type=int, default=10)
|
|
351
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=10)
|
|
352
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
353
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
354
|
+
def google_news(
|
|
355
|
+
keywords: str,
|
|
356
|
+
region: str,
|
|
357
|
+
safesearch: str,
|
|
358
|
+
max_results: int,
|
|
359
|
+
timeout: int = 10,
|
|
360
|
+
proxy: str = None,
|
|
361
|
+
impersonate: str = "chrome110"
|
|
362
|
+
):
|
|
363
|
+
"""Perform a news search using Google Search."""
|
|
364
|
+
google = GoogleSearch(
|
|
365
|
+
timeout=timeout,
|
|
366
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
367
|
+
verify=True,
|
|
368
|
+
lang="en",
|
|
369
|
+
sleep_interval=0.0,
|
|
370
|
+
impersonate=impersonate
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
try:
|
|
374
|
+
results = google.news(
|
|
375
|
+
keywords=keywords,
|
|
376
|
+
region=region,
|
|
377
|
+
safesearch=safesearch,
|
|
378
|
+
max_results=max_results
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# Convert SearchResult objects to dictionaries for printing
|
|
382
|
+
formatted_results = []
|
|
383
|
+
for result in results:
|
|
384
|
+
result_dict = {
|
|
385
|
+
"title": result.title,
|
|
386
|
+
"url": result.url,
|
|
387
|
+
"description": result.description,
|
|
388
|
+
}
|
|
389
|
+
# Add any metadata to the result dictionary
|
|
390
|
+
for k, v in result.metadata.items():
|
|
391
|
+
result_dict[k] = v
|
|
392
|
+
|
|
393
|
+
formatted_results.append(result_dict)
|
|
394
|
+
|
|
395
|
+
_print_data(formatted_results)
|
|
396
|
+
except Exception as e:
|
|
397
|
+
raise e
|
|
398
|
+
|
|
399
|
+
@app.command()
|
|
400
|
+
@option("--query", "-q", help="Search query", required=True)
|
|
401
|
+
@option("--region", "-r", help="Region for suggestions (ISO country code)", default="all")
|
|
402
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=10)
|
|
403
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
404
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
405
|
+
def google_suggestions(
|
|
406
|
+
query: str,
|
|
407
|
+
region: str,
|
|
408
|
+
timeout: int = 10,
|
|
409
|
+
proxy: str = None,
|
|
410
|
+
impersonate: str = "chrome110"
|
|
411
|
+
):
|
|
412
|
+
"""Get search suggestions from Google Search."""
|
|
413
|
+
google = GoogleSearch(
|
|
414
|
+
timeout=timeout,
|
|
415
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
416
|
+
verify=True,
|
|
417
|
+
lang="en",
|
|
418
|
+
sleep_interval=0.0,
|
|
419
|
+
impersonate=impersonate
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
results = google.suggestions(query=query, region=region)
|
|
424
|
+
|
|
425
|
+
# Format suggestions for printing
|
|
426
|
+
formatted_results = []
|
|
427
|
+
for i, suggestion in enumerate(results, 1):
|
|
428
|
+
formatted_results.append({"position": i, "suggestion": suggestion})
|
|
429
|
+
|
|
430
|
+
_print_data(formatted_results)
|
|
431
|
+
except Exception as e:
|
|
432
|
+
raise e
|
|
433
|
+
|
|
434
|
+
@app.command()
|
|
435
|
+
@option("--keywords", "-k", help="Search keywords", required=True)
|
|
436
|
+
@option("--region", "-r", help="Region for search results", default="all")
|
|
437
|
+
@option("--safesearch", "-s", help="SafeSearch setting (on, moderate, off)", default="moderate")
|
|
438
|
+
@option("--max-results", "-m", help="Maximum number of results", type=int, default=10)
|
|
439
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=20)
|
|
440
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
441
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
442
|
+
def yep_text(
|
|
443
|
+
keywords: str,
|
|
444
|
+
region: str,
|
|
445
|
+
safesearch: str,
|
|
446
|
+
max_results: int,
|
|
447
|
+
timeout: int = 20,
|
|
448
|
+
proxy: str = None,
|
|
449
|
+
impersonate: str = "chrome110"
|
|
450
|
+
):
|
|
451
|
+
"""Perform a text search using Yep Search."""
|
|
452
|
+
yep = YepSearch(
|
|
453
|
+
timeout=timeout,
|
|
454
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
455
|
+
verify=True,
|
|
456
|
+
impersonate=impersonate
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
try:
|
|
460
|
+
results = yep.text(
|
|
461
|
+
keywords=keywords,
|
|
462
|
+
region=region,
|
|
463
|
+
safesearch=safesearch,
|
|
464
|
+
max_results=max_results
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
_print_data(results)
|
|
468
|
+
except Exception as e:
|
|
469
|
+
raise e
|
|
470
|
+
|
|
471
|
+
@app.command()
|
|
472
|
+
@option("--keywords", "-k", help="Search keywords", required=True)
|
|
473
|
+
@option("--region", "-r", help="Region for search results", default="all")
|
|
474
|
+
@option("--safesearch", "-s", help="SafeSearch setting (on, moderate, off)", default="moderate")
|
|
475
|
+
@option("--max-results", "-m", help="Maximum number of results", type=int, default=10)
|
|
476
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=20)
|
|
477
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
478
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
479
|
+
def yep_images(
|
|
480
|
+
keywords: str,
|
|
481
|
+
region: str,
|
|
482
|
+
safesearch: str,
|
|
483
|
+
max_results: int,
|
|
484
|
+
timeout: int = 20,
|
|
485
|
+
proxy: str = None,
|
|
486
|
+
impersonate: str = "chrome110"
|
|
487
|
+
):
|
|
488
|
+
"""Perform an image search using Yep Search."""
|
|
489
|
+
yep = YepSearch(
|
|
490
|
+
timeout=timeout,
|
|
491
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
492
|
+
verify=True,
|
|
493
|
+
impersonate=impersonate
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
try:
|
|
497
|
+
results = yep.images(
|
|
498
|
+
keywords=keywords,
|
|
499
|
+
region=region,
|
|
500
|
+
safesearch=safesearch,
|
|
501
|
+
max_results=max_results
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
_print_data(results)
|
|
505
|
+
except Exception as e:
|
|
506
|
+
raise e
|
|
507
|
+
|
|
508
|
+
@app.command()
|
|
509
|
+
@option("--query", "-q", help="Search query", required=True)
|
|
510
|
+
@option("--region", "-r", help="Region for suggestions", default="all")
|
|
511
|
+
@option("--timeout", "-timeout", help="Timeout value for requests", type=int, default=20)
|
|
512
|
+
@option("--proxy", "-p", help="Proxy URL to use for requests")
|
|
513
|
+
@option("--impersonate", "-i", help="Browser to impersonate", default="chrome110")
|
|
514
|
+
def yep_suggestions(
|
|
515
|
+
query: str,
|
|
516
|
+
region: str,
|
|
517
|
+
timeout: int = 20,
|
|
518
|
+
proxy: str = None,
|
|
519
|
+
impersonate: str = "chrome110"
|
|
520
|
+
):
|
|
521
|
+
"""Get search suggestions from Yep Search."""
|
|
522
|
+
yep = YepSearch(
|
|
523
|
+
timeout=timeout,
|
|
524
|
+
proxies={"https": proxy, "http": proxy} if proxy else None,
|
|
525
|
+
verify=True,
|
|
526
|
+
impersonate=impersonate
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
try:
|
|
530
|
+
results = yep.suggestions(query=query, region=region)
|
|
531
|
+
|
|
532
|
+
# Format suggestions for printing
|
|
533
|
+
formatted_results = []
|
|
534
|
+
for i, suggestion in enumerate(results, 1):
|
|
535
|
+
formatted_results.append({"position": i, "suggestion": suggestion})
|
|
536
|
+
|
|
537
|
+
_print_data(formatted_results)
|
|
538
|
+
except Exception as e:
|
|
539
|
+
raise e
|
|
540
|
+
|
|
285
541
|
def main():
|
|
286
542
|
"""Main entry point for the CLI."""
|
|
287
543
|
try:
|