webscout 1.4.3__py3-none-any.whl → 1.4.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/AI.py +253 -3
- webscout/AIutel.py +4 -2
- webscout/__init__.py +2 -1
- webscout/utils.py +0 -1
- webscout/version.py +1 -1
- webscout/webai.py +15 -1
- webscout/webscout_search.py +13 -10
- webscout/webscout_search_async.py +132 -76
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/METADATA +70 -5
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/RECORD +14 -14
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/LICENSE.md +0 -0
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/WHEEL +0 -0
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/entry_points.txt +0 -0
- {webscout-1.4.3.dist-info → webscout-1.4.5.dist-info}/top_level.txt +0 -0
webscout/AI.py
CHANGED
|
@@ -25,9 +25,259 @@ from webscout.AIutel import AwesomePrompts, sanitize_stream
|
|
|
25
25
|
from webscout.AIbase import Provider, AsyncProvider
|
|
26
26
|
from Helpingai_T2 import Perplexity
|
|
27
27
|
from webscout import exceptions
|
|
28
|
-
from typing import Any, AsyncGenerator
|
|
28
|
+
from typing import Any, AsyncGenerator, Dict
|
|
29
29
|
import logging
|
|
30
30
|
import httpx
|
|
31
|
+
#------------------------------------ThinkAnyAI------------
|
|
32
|
+
class ThinkAnyAI(Provider):
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
model: str = "claude-3-haiku",
|
|
36
|
+
locale: str = "en",
|
|
37
|
+
web_search: bool = False,
|
|
38
|
+
chunk_size: int = 1,
|
|
39
|
+
streaming: bool = True,
|
|
40
|
+
is_conversation: bool = True,
|
|
41
|
+
max_tokens: int = 600,
|
|
42
|
+
timeout: int = 30,
|
|
43
|
+
intro: str = None,
|
|
44
|
+
filepath: str = None,
|
|
45
|
+
update_file: bool = True,
|
|
46
|
+
proxies: dict = {},
|
|
47
|
+
history_offset: int = 10250,
|
|
48
|
+
act: str = None,
|
|
49
|
+
):
|
|
50
|
+
"""Initializes ThinkAnyAI
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
model (str): The AI model to be used for generating responses. Defaults to "claude-3-haiku".
|
|
54
|
+
locale (str): The language locale. Defaults to "en" (English).
|
|
55
|
+
web_search (bool): Whether to include web search results in the response. Defaults to False.
|
|
56
|
+
chunk_size (int): The size of data chunks when streaming responses. Defaults to 1.
|
|
57
|
+
streaming (bool): Whether to stream response data. Defaults to True.
|
|
58
|
+
is_conversation (bool, optional): Flag for chatting conversationally. Defaults to True.
|
|
59
|
+
max_tokens (int, optional): Maximum number of tokens to be generated upon completion. Defaults to 600.
|
|
60
|
+
timeout (int, optional): Http request timeout. Defaults to 30.
|
|
61
|
+
intro (str, optional): Conversation introductory prompt. Defaults to None.
|
|
62
|
+
filepath (str, optional): Path to file containing conversation history. Defaults to None.
|
|
63
|
+
update_file (bool, optional): Add new prompts and responses to the file. Defaults to True.
|
|
64
|
+
proxies (dict, optional): Http request proxies. Defaults to {}.
|
|
65
|
+
history_offset (int, optional): Limit conversation history to this number of last texts. Defaults to 10250.
|
|
66
|
+
act (str|int, optional): Awesome prompt key or index. (Used as intro). Defaults to None.
|
|
67
|
+
"""
|
|
68
|
+
self.base_url = "https://thinkany.ai/api"
|
|
69
|
+
self.model = model
|
|
70
|
+
self.locale = locale
|
|
71
|
+
self.web_search = web_search
|
|
72
|
+
self.chunk_size = chunk_size
|
|
73
|
+
self.streaming = streaming
|
|
74
|
+
self.last_response = {}
|
|
75
|
+
self.session = requests.Session()
|
|
76
|
+
self.session.proxies = proxies
|
|
77
|
+
|
|
78
|
+
self.__available_optimizers = (
|
|
79
|
+
method
|
|
80
|
+
for method in dir(Optimizers)
|
|
81
|
+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
Conversation.intro = (
|
|
85
|
+
AwesomePrompts().get_act(
|
|
86
|
+
act, raise_not_found=True, default=None, case_insensitive=True
|
|
87
|
+
)
|
|
88
|
+
if act
|
|
89
|
+
else intro or Conversation.intro
|
|
90
|
+
)
|
|
91
|
+
self.conversation = Conversation(
|
|
92
|
+
is_conversation, max_tokens, filepath, update_file
|
|
93
|
+
)
|
|
94
|
+
self.conversation.history_offset = history_offset
|
|
95
|
+
|
|
96
|
+
def ask(
|
|
97
|
+
self,
|
|
98
|
+
prompt: str,
|
|
99
|
+
stream: bool = False,
|
|
100
|
+
raw: bool = False,
|
|
101
|
+
optimizer: str = None,
|
|
102
|
+
conversationally: bool = False,
|
|
103
|
+
) -> dict | AsyncGenerator:
|
|
104
|
+
"""Chat with AI asynchronously.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
prompt (str): Prompt to be send.
|
|
108
|
+
stream (bool, optional): Flag for streaming response. Defaults to False.
|
|
109
|
+
raw (bool, optional): Stream back raw response as received. Defaults to False.
|
|
110
|
+
optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defeaults to None
|
|
111
|
+
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
|
|
112
|
+
Returns:
|
|
113
|
+
dict : {}
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"content": "General Kenobi! \n\n(I couldn't help but respond with the iconic Star Wars greeting since you used it first. )\n\nIs there anything I can help you with today?\n[Image of Hello there General Kenobi]",
|
|
117
|
+
"conversation_id": "c_f13f6217f9a997aa",
|
|
118
|
+
"response_id": "r_d3665f95975c368f",
|
|
119
|
+
"factualityQueries": null,
|
|
120
|
+
"textQuery": [
|
|
121
|
+
"hello there",
|
|
122
|
+
1
|
|
123
|
+
],
|
|
124
|
+
"choices": [
|
|
125
|
+
{
|
|
126
|
+
"id": "rc_ea075c9671bfd8cb",
|
|
127
|
+
"content": [
|
|
128
|
+
"General Kenobi! \n\n(I couldn't help but respond with the iconic Star Wars greeting since you used it first. )\n\nIs there anything I can help you with today?\n[Image of Hello there General Kenobi]"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "rc_de6dd3fb793a5402",
|
|
133
|
+
"content": [
|
|
134
|
+
"General Kenobi! (or just a friendly hello, whichever you prefer!). \n\nI see you're a person of culture as well. *Star Wars* references are always appreciated. \n\nHow can I help you today?\n"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "rc_a672ac089caf32db",
|
|
139
|
+
"content": [
|
|
140
|
+
"General Kenobi! (or just a friendly hello if you're not a Star Wars fan!). \n\nHow can I help you today? Feel free to ask me anything, or tell me what you'd like to chat about. I'm here to assist in any way I can.\n[Image of Obi-Wan Kenobi saying hello there]"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
|
|
145
|
+
"images": [
|
|
146
|
+
"https://i.pinimg.com/originals/40/74/60/407460925c9e419d82b93313f0b42f71.jpg"
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
"""
|
|
152
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
153
|
+
if optimizer:
|
|
154
|
+
if optimizer in self.__available_optimizers:
|
|
155
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
156
|
+
conversation_prompt if conversationally else prompt
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
raise Exception(
|
|
160
|
+
f"Optimizer is not one of {self.__available_optimizers}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def initiate_conversation(query: str) -> str:
|
|
164
|
+
"""
|
|
165
|
+
Initiates a new conversation with the ThinkAny AI API.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
query (str): The initial query to start the conversation.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
str: The UUID (Unique Identifier) of the conversation.
|
|
172
|
+
"""
|
|
173
|
+
url = f"{self.base_url}/new-conversation"
|
|
174
|
+
payload = {
|
|
175
|
+
"content": query,
|
|
176
|
+
"locale": self.locale,
|
|
177
|
+
"mode": "search" if self.web_search else "chat",
|
|
178
|
+
"model": self.model,
|
|
179
|
+
"source": "all",
|
|
180
|
+
}
|
|
181
|
+
response = self.session.post(url, json=payload)
|
|
182
|
+
return response.json().get("data", {}).get("uuid", "DevsDoCode")
|
|
183
|
+
|
|
184
|
+
def RAG_search(uuid: str) -> tuple[bool, list]:
|
|
185
|
+
"""
|
|
186
|
+
Performs a web search using the Retrieve And Generate (RAG) model.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
uuid (str): The UUID of the conversation.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
tuple: A tuple containing a boolean indicating the success of the search
|
|
193
|
+
and a list of search result links.
|
|
194
|
+
"""
|
|
195
|
+
if not self.web_search:
|
|
196
|
+
return True, []
|
|
197
|
+
url = f"{self.base_url}/rag-search"
|
|
198
|
+
payload = {"conv_uuid": uuid}
|
|
199
|
+
response = self.session.post(url, json=payload)
|
|
200
|
+
links = [source["link"] for source in response.json().get("data", [])]
|
|
201
|
+
return response.json().get("message", "").strip(), links
|
|
202
|
+
|
|
203
|
+
def for_stream():
|
|
204
|
+
conversation_uuid = initiate_conversation(conversation_prompt)
|
|
205
|
+
web_search_result, links = RAG_search(conversation_uuid)
|
|
206
|
+
if not web_search_result:
|
|
207
|
+
print("Failed to generate WEB response. Making normal Query...")
|
|
208
|
+
|
|
209
|
+
url = f"{self.base_url}/chat"
|
|
210
|
+
payload = {
|
|
211
|
+
"role": "user",
|
|
212
|
+
"content": prompt,
|
|
213
|
+
"conv_uuid": conversation_uuid,
|
|
214
|
+
"model": self.model,
|
|
215
|
+
}
|
|
216
|
+
response = self.session.post(url, json=payload, stream=True)
|
|
217
|
+
complete_content = ""
|
|
218
|
+
for content in response.iter_content(
|
|
219
|
+
decode_unicode=True, chunk_size=self.chunk_size
|
|
220
|
+
):
|
|
221
|
+
complete_content += content
|
|
222
|
+
yield content if raw else dict(text=complete_content)
|
|
223
|
+
self.last_response.update(dict(text=complete_content, links=links))
|
|
224
|
+
self.conversation.update_chat_history(
|
|
225
|
+
prompt, self.get_message(self.last_response)
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def for_non_stream():
|
|
229
|
+
for _ in for_stream():
|
|
230
|
+
pass
|
|
231
|
+
return self.last_response
|
|
232
|
+
|
|
233
|
+
return for_stream() if stream else for_non_stream()
|
|
234
|
+
|
|
235
|
+
def chat(
|
|
236
|
+
self,
|
|
237
|
+
prompt: str,
|
|
238
|
+
stream: bool = False,
|
|
239
|
+
optimizer: str = None,
|
|
240
|
+
conversationally: bool = False,
|
|
241
|
+
) -> str:
|
|
242
|
+
"""Generate response `str`
|
|
243
|
+
Args:
|
|
244
|
+
prompt (str): Prompt to be send.
|
|
245
|
+
stream (bool, optional): Flag for streaming response. Defaults to False.
|
|
246
|
+
optimizer (str, optional): Prompt optimizer name - `[code, shell_command]`. Defaults to None.
|
|
247
|
+
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
|
|
248
|
+
Returns:
|
|
249
|
+
str: Response generated
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
def for_stream():
|
|
253
|
+
for response in self.ask(
|
|
254
|
+
prompt, True, optimizer=optimizer, conversationally=conversationally
|
|
255
|
+
):
|
|
256
|
+
yield self.get_message(response)
|
|
257
|
+
|
|
258
|
+
def for_non_stream():
|
|
259
|
+
return self.get_message(
|
|
260
|
+
self.ask(
|
|
261
|
+
prompt,
|
|
262
|
+
False,
|
|
263
|
+
optimizer=optimizer,
|
|
264
|
+
conversationally=conversationally,
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return for_stream() if stream else for_non_stream()
|
|
269
|
+
|
|
270
|
+
def get_message(self, response: Dict[str, Any]) -> str:
|
|
271
|
+
"""Retrieves message only from response
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
response (dict): Response generated by `self.ask`
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
str: Message extracted
|
|
278
|
+
"""
|
|
279
|
+
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
280
|
+
return response["text"]
|
|
31
281
|
#-----------------------------------------------xjai-------------------------------------------
|
|
32
282
|
class Xjai(Provider):
|
|
33
283
|
def __init__(
|
|
@@ -2802,6 +3052,7 @@ class AsyncKOBOLDAI(AsyncProvider):
|
|
|
2802
3052
|
class OPENGPT:
|
|
2803
3053
|
def __init__(
|
|
2804
3054
|
self,
|
|
3055
|
+
assistant_id,
|
|
2805
3056
|
is_conversation: bool = True,
|
|
2806
3057
|
max_tokens: int = 600,
|
|
2807
3058
|
timeout: int = 30,
|
|
@@ -2834,7 +3085,7 @@ class OPENGPT:
|
|
|
2834
3085
|
self.stream_chunk_size = 64
|
|
2835
3086
|
self.timeout = timeout
|
|
2836
3087
|
self.last_response = {}
|
|
2837
|
-
self.assistant_id =
|
|
3088
|
+
self.assistant_id = assistant_id
|
|
2838
3089
|
self.authority = "opengpts-example-vz4y4ooboq-uc.a.run.app"
|
|
2839
3090
|
|
|
2840
3091
|
self.headers = {
|
|
@@ -3026,7 +3277,6 @@ class OPENGPT:
|
|
|
3026
3277
|
str: Message extracted
|
|
3027
3278
|
"""
|
|
3028
3279
|
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
3029
|
-
return response["content"]
|
|
3030
3280
|
class AsyncOPENGPT(AsyncProvider):
|
|
3031
3281
|
def __init__(
|
|
3032
3282
|
self,
|
webscout/AIutel.py
CHANGED
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from playsound import playsound
|
|
19
19
|
from time import sleep as wait
|
|
20
20
|
import pathlib
|
|
21
|
+
import urllib.parse
|
|
21
22
|
appdir = appdirs.AppDirs("AIWEBS", "vortex")
|
|
22
23
|
|
|
23
24
|
default_path = appdir.user_cache_dir
|
|
@@ -39,7 +40,8 @@ webai = [
|
|
|
39
40
|
"cohere",
|
|
40
41
|
"yepchat",
|
|
41
42
|
"you",
|
|
42
|
-
"xjai"
|
|
43
|
+
"xjai",
|
|
44
|
+
"thinkany"
|
|
43
45
|
]
|
|
44
46
|
|
|
45
47
|
gpt4free_providers = [
|
|
@@ -938,7 +940,7 @@ class Audio:
|
|
|
938
940
|
), f"Voice '{voice}' not one of [{', '.join(cls.all_voices)}]"
|
|
939
941
|
# Base URL for provider API
|
|
940
942
|
url: str = (
|
|
941
|
-
f"https://api.streamelements.com/kappa/v2/speech?voice={voice}&text={{{message}}}"
|
|
943
|
+
f"https://api.streamelements.com/kappa/v2/speech?voice={voice}&text={{{urllib.parse.quote(message)}}}"
|
|
942
944
|
)
|
|
943
945
|
resp = requests.get(url=url, headers=cls.headers, stream=True)
|
|
944
946
|
if not resp.ok:
|
webscout/__init__.py
CHANGED
webscout/utils.py
CHANGED
webscout/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.4.
|
|
1
|
+
__version__ = "1.4.5"
|
|
2
2
|
|
webscout/webai.py
CHANGED
|
@@ -471,6 +471,20 @@ class Main(cmd.Cmd):
|
|
|
471
471
|
history_offset=history_offset,
|
|
472
472
|
act=awesome_prompt,
|
|
473
473
|
)
|
|
474
|
+
elif provider == "thinkany":
|
|
475
|
+
from webscout.AI import ThinkAnyAI
|
|
476
|
+
|
|
477
|
+
self.bot = ThinkAnyAI(
|
|
478
|
+
is_conversation=disable_conversation,
|
|
479
|
+
max_tokens=max_tokens,
|
|
480
|
+
timeout=timeout,
|
|
481
|
+
intro=intro,
|
|
482
|
+
filepath=filepath,
|
|
483
|
+
update_file=update_file,
|
|
484
|
+
proxies=proxies,
|
|
485
|
+
history_offset=history_offset,
|
|
486
|
+
act=awesome_prompt,
|
|
487
|
+
)
|
|
474
488
|
elif provider == "yepchat":
|
|
475
489
|
from webscout.AI import YEPCHAT
|
|
476
490
|
|
|
@@ -981,7 +995,7 @@ class Main(cmd.Cmd):
|
|
|
981
995
|
self.output_bond("Chat History", formatted_history, self.color)
|
|
982
996
|
if click.confirm("Do you wish to save this chat"):
|
|
983
997
|
save_to = click.prompt(
|
|
984
|
-
"Enter path/file-name", default="
|
|
998
|
+
"Enter path/file-name", default=f"{self.provider}-chat.txt"
|
|
985
999
|
)
|
|
986
1000
|
with open(save_to, "a") as fh:
|
|
987
1001
|
fh.write(history)
|
webscout/webscout_search.py
CHANGED
|
@@ -20,11 +20,19 @@ class WEBS(AsyncWEBS):
|
|
|
20
20
|
def __init__(
|
|
21
21
|
self,
|
|
22
22
|
headers: Optional[Dict[str, str]] = None,
|
|
23
|
-
|
|
23
|
+
proxy: Optional[str] = None,
|
|
24
|
+
proxies: Union[Dict[str, str], str, None] = None, # deprecated
|
|
24
25
|
timeout: Optional[int] = 10,
|
|
25
26
|
) -> None:
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
"""Initialize the DDGS object.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
headers (dict, optional): Dictionary of headers for the HTTP client. Defaults to None.
|
|
31
|
+
proxy (str, optional): proxy for the HTTP client, supports http/https/socks5 protocols.
|
|
32
|
+
example: "http://user:pass@example.com:3128". Defaults to None.
|
|
33
|
+
timeout (int, optional): Timeout value for the HTTP client. Defaults to 10.
|
|
34
|
+
"""
|
|
35
|
+
super().__init__(headers=headers, proxy=proxy, proxies=proxies, timeout=timeout)
|
|
28
36
|
|
|
29
37
|
def __enter__(self) -> "WEBS":
|
|
30
38
|
return self
|
|
@@ -42,13 +50,8 @@ class WEBS(AsyncWEBS):
|
|
|
42
50
|
|
|
43
51
|
def _close_session(self) -> None:
|
|
44
52
|
"""Close the curl-cffi async session."""
|
|
45
|
-
if self.
|
|
46
|
-
|
|
47
|
-
coro = self._asession.close()
|
|
48
|
-
# Check if coro is a coroutine object
|
|
49
|
-
if asyncio.iscoroutine(coro):
|
|
50
|
-
self._run_async_in_thread(coro)
|
|
51
|
-
self._exit_done = True
|
|
53
|
+
if hasattr(self, "_asession") and self._asession._closed is False:
|
|
54
|
+
self._run_async_in_thread(self._asession.close()) # type: ignore
|
|
52
55
|
|
|
53
56
|
def _run_async_in_thread(self, coro: Awaitable[Any]) -> Any:
|
|
54
57
|
"""Runs an async coroutine in a separate thread."""
|
|
@@ -5,10 +5,10 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from datetime import datetime, timezone
|
|
7
7
|
from decimal import Decimal
|
|
8
|
-
from functools import partial
|
|
8
|
+
from functools import cached_property, partial
|
|
9
9
|
from itertools import cycle, islice
|
|
10
10
|
from types import TracebackType
|
|
11
|
-
from typing import Dict, List, Optional, Tuple, Union
|
|
11
|
+
from typing import Dict, List, Optional, Tuple, Type, Union, cast
|
|
12
12
|
|
|
13
13
|
from curl_cffi import requests
|
|
14
14
|
|
|
@@ -34,68 +34,71 @@ logger = logging.getLogger("webscout_search.AsyncWEBS")
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class AsyncWEBS:
|
|
37
|
-
"""
|
|
37
|
+
"""webscout_search async class to get search results from duckduckgo.com."""
|
|
38
38
|
|
|
39
39
|
_executor: Optional[ThreadPoolExecutor] = None
|
|
40
40
|
|
|
41
41
|
def __init__(
|
|
42
42
|
self,
|
|
43
43
|
headers: Optional[Dict[str, str]] = None,
|
|
44
|
-
|
|
44
|
+
proxy: Optional[str] = None,
|
|
45
|
+
proxies: Union[Dict[str, str], str, None] = None, # deprecated
|
|
45
46
|
timeout: Optional[int] = 10,
|
|
46
47
|
) -> None:
|
|
47
48
|
"""Initialize the AsyncWEBS object.
|
|
48
49
|
|
|
49
50
|
Args:
|
|
50
51
|
headers (dict, optional): Dictionary of headers for the HTTP client. Defaults to None.
|
|
51
|
-
|
|
52
|
+
proxy (str, optional): proxy for the HTTP client, supports http/https/socks5 protocols.
|
|
53
|
+
example: "http://user:pass@example.com:3128". Defaults to None.
|
|
52
54
|
timeout (int, optional): Timeout value for the HTTP client. Defaults to 10.
|
|
53
55
|
"""
|
|
54
|
-
self.
|
|
56
|
+
self.proxy: Optional[str] = proxy
|
|
57
|
+
assert self.proxy is None or isinstance(self.proxy, str), "proxy must be a str"
|
|
58
|
+
if not proxy and proxies:
|
|
59
|
+
warnings.warn("'proxies' is deprecated, use 'proxy' instead.", stacklevel=1)
|
|
60
|
+
self.proxy = proxies.get("http") or proxies.get("https") if isinstance(proxies, dict) else proxies
|
|
55
61
|
self._asession = requests.AsyncSession(
|
|
56
62
|
headers=headers,
|
|
57
|
-
|
|
63
|
+
proxy=self.proxy,
|
|
58
64
|
timeout=timeout,
|
|
59
65
|
impersonate="chrome",
|
|
60
66
|
allow_redirects=False,
|
|
61
67
|
)
|
|
62
68
|
self._asession.headers["Referer"] = "https://duckduckgo.com/"
|
|
63
|
-
self._parser: Optional[LHTMLParser] = None
|
|
64
69
|
self._exception_event = asyncio.Event()
|
|
65
|
-
self._exit_done = False
|
|
66
70
|
|
|
67
71
|
async def __aenter__(self) -> "AsyncWEBS":
|
|
68
72
|
return self
|
|
69
73
|
|
|
70
74
|
async def __aexit__(
|
|
71
75
|
self,
|
|
72
|
-
exc_type: Optional[BaseException] = None,
|
|
76
|
+
exc_type: Optional[Type[BaseException]] = None,
|
|
73
77
|
exc_val: Optional[BaseException] = None,
|
|
74
78
|
exc_tb: Optional[TracebackType] = None,
|
|
75
79
|
) -> None:
|
|
76
|
-
await self.
|
|
80
|
+
await self._asession.__aexit__(exc_type, exc_val, exc_tb) # type: ignore
|
|
77
81
|
|
|
78
82
|
def __del__(self) -> None:
|
|
79
|
-
if self.
|
|
80
|
-
|
|
83
|
+
if hasattr(self, "_asession") and self._asession._closed is False:
|
|
84
|
+
with suppress(RuntimeError, RuntimeWarning):
|
|
85
|
+
asyncio.create_task(self._asession.close()) # type: ignore
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if self._exit_done is False:
|
|
85
|
-
await self._asession.close()
|
|
86
|
-
self._exit_done = True
|
|
87
|
-
|
|
88
|
-
def _get_parser(self) -> "LHTMLParser":
|
|
87
|
+
@cached_property
|
|
88
|
+
def parser(self) -> Optional["LHTMLParser"]:
|
|
89
89
|
"""Get HTML parser."""
|
|
90
|
-
|
|
91
|
-
self._parser = LHTMLParser(remove_blank_text=True, remove_comments=True, remove_pis=True, collect_ids=False)
|
|
92
|
-
return self._parser
|
|
90
|
+
return LHTMLParser(remove_blank_text=True, remove_comments=True, remove_pis=True, collect_ids=False)
|
|
93
91
|
|
|
94
|
-
|
|
92
|
+
@classmethod
|
|
93
|
+
def _get_executor(cls, max_workers: int = 1) -> ThreadPoolExecutor:
|
|
95
94
|
"""Get ThreadPoolExecutor. Default max_workers=1, because >=2 leads to a big overhead"""
|
|
96
|
-
if
|
|
97
|
-
|
|
98
|
-
return
|
|
95
|
+
if cls._executor is None:
|
|
96
|
+
cls._executor = ThreadPoolExecutor(max_workers=max_workers)
|
|
97
|
+
return cls._executor
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def executor(cls) -> Optional[ThreadPoolExecutor]:
|
|
101
|
+
return cls._get_executor()
|
|
99
102
|
|
|
100
103
|
async def _aget_url(
|
|
101
104
|
self,
|
|
@@ -107,19 +110,18 @@ class AsyncWEBS:
|
|
|
107
110
|
if self._exception_event.is_set():
|
|
108
111
|
raise WebscoutE("Exception occurred in previous call.")
|
|
109
112
|
try:
|
|
110
|
-
resp = await self._asession.request(method, url, data=data, params=params
|
|
111
|
-
resp_content: bytes = await resp.acontent()
|
|
113
|
+
resp = await self._asession.request(method, url, data=data, params=params)
|
|
112
114
|
except Exception as ex:
|
|
113
115
|
self._exception_event.set()
|
|
114
116
|
if "time" in str(ex).lower():
|
|
115
117
|
raise TimeoutE(f"{url} {type(ex).__name__}: {ex}") from ex
|
|
116
118
|
raise WebscoutE(f"{url} {type(ex).__name__}: {ex}") from ex
|
|
117
|
-
logger.debug(f"_aget_url() {resp.url} {resp.status_code} {resp.elapsed:.2f} {len(
|
|
119
|
+
logger.debug(f"_aget_url() {resp.url} {resp.status_code} {resp.elapsed:.2f} {len(resp.content)}")
|
|
118
120
|
if resp.status_code == 200:
|
|
119
|
-
return
|
|
121
|
+
return cast(bytes, resp.content)
|
|
120
122
|
self._exception_event.set()
|
|
121
123
|
if resp.status_code in (202, 301, 403):
|
|
122
|
-
raise RatelimitE(f"{resp.url} {resp.status_code}")
|
|
124
|
+
raise RatelimitE(f"{resp.url} {resp.status_code} Ratelimit")
|
|
123
125
|
raise WebscoutE(f"{resp.url} return None. {params=} {data=}")
|
|
124
126
|
|
|
125
127
|
async def _aget_vqd(self, keywords: str) -> str:
|
|
@@ -136,7 +138,7 @@ class AsyncWEBS:
|
|
|
136
138
|
backend: str = "api",
|
|
137
139
|
max_results: Optional[int] = None,
|
|
138
140
|
) -> List[Dict[str, str]]:
|
|
139
|
-
"""
|
|
141
|
+
"""webscout text search generator. Query params: https://duckduckgo.com/params.
|
|
140
142
|
|
|
141
143
|
Args:
|
|
142
144
|
keywords: keywords for query.
|
|
@@ -153,7 +155,7 @@ class AsyncWEBS:
|
|
|
153
155
|
List of dictionaries with search results, or None if there was an error.
|
|
154
156
|
|
|
155
157
|
Raises:
|
|
156
|
-
WebscoutE: Base exception for
|
|
158
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
157
159
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
158
160
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
159
161
|
"""
|
|
@@ -177,7 +179,7 @@ class AsyncWEBS:
|
|
|
177
179
|
timelimit: Optional[str] = None,
|
|
178
180
|
max_results: Optional[int] = None,
|
|
179
181
|
) -> List[Dict[str, str]]:
|
|
180
|
-
"""
|
|
182
|
+
"""webscout text search generator. Query params: https://duckduckgo.com/params.
|
|
181
183
|
|
|
182
184
|
Args:
|
|
183
185
|
keywords: keywords for query.
|
|
@@ -190,7 +192,7 @@ class AsyncWEBS:
|
|
|
190
192
|
List of dictionaries with search results.
|
|
191
193
|
|
|
192
194
|
Raises:
|
|
193
|
-
WebscoutE: Base exception for
|
|
195
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
194
196
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
195
197
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
196
198
|
"""
|
|
@@ -241,11 +243,19 @@ class AsyncWEBS:
|
|
|
241
243
|
}
|
|
242
244
|
results[priority] = result
|
|
243
245
|
|
|
244
|
-
tasks = [_text_api_page(0, 0)]
|
|
246
|
+
tasks = [asyncio.create_task(_text_api_page(0, 0))]
|
|
245
247
|
if max_results:
|
|
246
248
|
max_results = min(max_results, 500)
|
|
247
|
-
tasks.extend(
|
|
248
|
-
|
|
249
|
+
tasks.extend(
|
|
250
|
+
asyncio.create_task(_text_api_page(s, i)) for i, s in enumerate(range(23, max_results, 50), start=1)
|
|
251
|
+
)
|
|
252
|
+
try:
|
|
253
|
+
await asyncio.gather(*tasks)
|
|
254
|
+
except Exception as e:
|
|
255
|
+
for task in tasks:
|
|
256
|
+
task.cancel()
|
|
257
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
258
|
+
raise e
|
|
249
259
|
|
|
250
260
|
return list(islice(filter(None, results), max_results))
|
|
251
261
|
|
|
@@ -257,7 +267,7 @@ class AsyncWEBS:
|
|
|
257
267
|
timelimit: Optional[str] = None,
|
|
258
268
|
max_results: Optional[int] = None,
|
|
259
269
|
) -> List[Dict[str, str]]:
|
|
260
|
-
"""
|
|
270
|
+
"""webscout text search generator. Query params: https://duckduckgo.com/params.
|
|
261
271
|
|
|
262
272
|
Args:
|
|
263
273
|
keywords: keywords for query.
|
|
@@ -270,7 +280,7 @@ class AsyncWEBS:
|
|
|
270
280
|
List of dictionaries with search results.
|
|
271
281
|
|
|
272
282
|
Raises:
|
|
273
|
-
WebscoutE: Base exception for
|
|
283
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
274
284
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
275
285
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
276
286
|
"""
|
|
@@ -302,7 +312,7 @@ class AsyncWEBS:
|
|
|
302
312
|
return
|
|
303
313
|
|
|
304
314
|
tree = await self._asession.loop.run_in_executor(
|
|
305
|
-
self.
|
|
315
|
+
self.executor, partial(document_fromstring, resp_content, self.parser)
|
|
306
316
|
)
|
|
307
317
|
|
|
308
318
|
for e in tree.xpath("//div[h2]"):
|
|
@@ -327,11 +337,19 @@ class AsyncWEBS:
|
|
|
327
337
|
}
|
|
328
338
|
results[priority] = result
|
|
329
339
|
|
|
330
|
-
tasks = [_text_html_page(0, 0)]
|
|
340
|
+
tasks = [asyncio.create_task(_text_html_page(0, 0))]
|
|
331
341
|
if max_results:
|
|
332
342
|
max_results = min(max_results, 500)
|
|
333
|
-
tasks.extend(
|
|
334
|
-
|
|
343
|
+
tasks.extend(
|
|
344
|
+
asyncio.create_task(_text_html_page(s, i)) for i, s in enumerate(range(23, max_results, 50), start=1)
|
|
345
|
+
)
|
|
346
|
+
try:
|
|
347
|
+
await asyncio.gather(*tasks)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
for task in tasks:
|
|
350
|
+
task.cancel()
|
|
351
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
352
|
+
raise e
|
|
335
353
|
|
|
336
354
|
return list(islice(filter(None, results), max_results))
|
|
337
355
|
|
|
@@ -342,7 +360,7 @@ class AsyncWEBS:
|
|
|
342
360
|
timelimit: Optional[str] = None,
|
|
343
361
|
max_results: Optional[int] = None,
|
|
344
362
|
) -> List[Dict[str, str]]:
|
|
345
|
-
"""
|
|
363
|
+
"""webscout text search generator. Query params: https://duckduckgo.com/params.
|
|
346
364
|
|
|
347
365
|
Args:
|
|
348
366
|
keywords: keywords for query.
|
|
@@ -354,7 +372,7 @@ class AsyncWEBS:
|
|
|
354
372
|
List of dictionaries with search results.
|
|
355
373
|
|
|
356
374
|
Raises:
|
|
357
|
-
WebscoutE: Base exception for
|
|
375
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
358
376
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
359
377
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
360
378
|
"""
|
|
@@ -381,7 +399,7 @@ class AsyncWEBS:
|
|
|
381
399
|
return
|
|
382
400
|
|
|
383
401
|
tree = await self._asession.loop.run_in_executor(
|
|
384
|
-
self.
|
|
402
|
+
self.executor, partial(document_fromstring, resp_content, self.parser)
|
|
385
403
|
)
|
|
386
404
|
|
|
387
405
|
data = zip(cycle(range(1, 5)), tree.xpath("//table[last()]//tr"))
|
|
@@ -410,11 +428,19 @@ class AsyncWEBS:
|
|
|
410
428
|
}
|
|
411
429
|
results[priority] = result
|
|
412
430
|
|
|
413
|
-
tasks = [_text_lite_page(0, 0)]
|
|
431
|
+
tasks = [asyncio.create_task(_text_lite_page(0, 0))]
|
|
414
432
|
if max_results:
|
|
415
433
|
max_results = min(max_results, 500)
|
|
416
|
-
tasks.extend(
|
|
417
|
-
|
|
434
|
+
tasks.extend(
|
|
435
|
+
asyncio.create_task(_text_lite_page(s, i)) for i, s in enumerate(range(23, max_results, 50), start=1)
|
|
436
|
+
)
|
|
437
|
+
try:
|
|
438
|
+
await asyncio.gather(*tasks)
|
|
439
|
+
except Exception as e:
|
|
440
|
+
for task in tasks:
|
|
441
|
+
task.cancel()
|
|
442
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
443
|
+
raise e
|
|
418
444
|
|
|
419
445
|
return list(islice(filter(None, results), max_results))
|
|
420
446
|
|
|
@@ -431,7 +457,7 @@ class AsyncWEBS:
|
|
|
431
457
|
license_image: Optional[str] = None,
|
|
432
458
|
max_results: Optional[int] = None,
|
|
433
459
|
) -> List[Dict[str, str]]:
|
|
434
|
-
"""
|
|
460
|
+
"""webscout images search. Query params: https://duckduckgo.com/params.
|
|
435
461
|
|
|
436
462
|
Args:
|
|
437
463
|
keywords: keywords for query.
|
|
@@ -454,7 +480,7 @@ class AsyncWEBS:
|
|
|
454
480
|
List of dictionaries with images search results.
|
|
455
481
|
|
|
456
482
|
Raises:
|
|
457
|
-
WebscoutE: Base exception for
|
|
483
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
458
484
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
459
485
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
460
486
|
"""
|
|
@@ -505,11 +531,19 @@ class AsyncWEBS:
|
|
|
505
531
|
}
|
|
506
532
|
results[priority] = result
|
|
507
533
|
|
|
508
|
-
tasks = [_images_page(0, page=0)]
|
|
534
|
+
tasks = [asyncio.create_task(_images_page(0, page=0))]
|
|
509
535
|
if max_results:
|
|
510
536
|
max_results = min(max_results, 500)
|
|
511
|
-
tasks.extend(
|
|
512
|
-
|
|
537
|
+
tasks.extend(
|
|
538
|
+
asyncio.create_task(_images_page(s, i)) for i, s in enumerate(range(100, max_results, 100), start=1)
|
|
539
|
+
)
|
|
540
|
+
try:
|
|
541
|
+
await asyncio.gather(*tasks)
|
|
542
|
+
except Exception as e:
|
|
543
|
+
for task in tasks:
|
|
544
|
+
task.cancel()
|
|
545
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
546
|
+
raise e
|
|
513
547
|
|
|
514
548
|
return list(islice(filter(None, results), max_results))
|
|
515
549
|
|
|
@@ -524,7 +558,7 @@ class AsyncWEBS:
|
|
|
524
558
|
license_videos: Optional[str] = None,
|
|
525
559
|
max_results: Optional[int] = None,
|
|
526
560
|
) -> List[Dict[str, str]]:
|
|
527
|
-
"""
|
|
561
|
+
"""webscout videos search. Query params: https://duckduckgo.com/params.
|
|
528
562
|
|
|
529
563
|
Args:
|
|
530
564
|
keywords: keywords for query.
|
|
@@ -540,7 +574,7 @@ class AsyncWEBS:
|
|
|
540
574
|
List of dictionaries with videos search results.
|
|
541
575
|
|
|
542
576
|
Raises:
|
|
543
|
-
WebscoutE: Base exception for
|
|
577
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
544
578
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
545
579
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
546
580
|
"""
|
|
@@ -579,11 +613,19 @@ class AsyncWEBS:
|
|
|
579
613
|
priority += 1
|
|
580
614
|
results[priority] = row
|
|
581
615
|
|
|
582
|
-
tasks = [_videos_page(0, 0)]
|
|
616
|
+
tasks = [asyncio.create_task(_videos_page(0, 0))]
|
|
583
617
|
if max_results:
|
|
584
618
|
max_results = min(max_results, 400)
|
|
585
|
-
tasks.extend(
|
|
586
|
-
|
|
619
|
+
tasks.extend(
|
|
620
|
+
asyncio.create_task(_videos_page(s, i)) for i, s in enumerate(range(59, max_results, 59), start=1)
|
|
621
|
+
)
|
|
622
|
+
try:
|
|
623
|
+
await asyncio.gather(*tasks)
|
|
624
|
+
except Exception as e:
|
|
625
|
+
for task in tasks:
|
|
626
|
+
task.cancel()
|
|
627
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
628
|
+
raise e
|
|
587
629
|
|
|
588
630
|
return list(islice(filter(None, results), max_results))
|
|
589
631
|
|
|
@@ -595,7 +637,7 @@ class AsyncWEBS:
|
|
|
595
637
|
timelimit: Optional[str] = None,
|
|
596
638
|
max_results: Optional[int] = None,
|
|
597
639
|
) -> List[Dict[str, str]]:
|
|
598
|
-
"""
|
|
640
|
+
"""webscout news search. Query params: https://duckduckgo.com/params.
|
|
599
641
|
|
|
600
642
|
Args:
|
|
601
643
|
keywords: keywords for query.
|
|
@@ -608,7 +650,7 @@ class AsyncWEBS:
|
|
|
608
650
|
List of dictionaries with news search results.
|
|
609
651
|
|
|
610
652
|
Raises:
|
|
611
|
-
WebscoutE: Base exception for
|
|
653
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
612
654
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
613
655
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
614
656
|
"""
|
|
@@ -653,16 +695,24 @@ class AsyncWEBS:
|
|
|
653
695
|
}
|
|
654
696
|
results[priority] = result
|
|
655
697
|
|
|
656
|
-
tasks = [_news_page(0, 0)]
|
|
698
|
+
tasks = [asyncio.create_task(_news_page(0, 0))]
|
|
657
699
|
if max_results:
|
|
658
700
|
max_results = min(max_results, 200)
|
|
659
|
-
tasks.extend(
|
|
660
|
-
|
|
701
|
+
tasks.extend(
|
|
702
|
+
asyncio.create_task(_news_page(s, i)) for i, s in enumerate(range(29, max_results, 29), start=1)
|
|
703
|
+
)
|
|
704
|
+
try:
|
|
705
|
+
await asyncio.gather(*tasks)
|
|
706
|
+
except Exception as e:
|
|
707
|
+
for task in tasks:
|
|
708
|
+
task.cancel()
|
|
709
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
710
|
+
raise e
|
|
661
711
|
|
|
662
712
|
return list(islice(filter(None, results), max_results))
|
|
663
713
|
|
|
664
714
|
async def answers(self, keywords: str) -> List[Dict[str, str]]:
|
|
665
|
-
"""
|
|
715
|
+
"""webscout instant answers. Query params: https://duckduckgo.com/params.
|
|
666
716
|
|
|
667
717
|
Args:
|
|
668
718
|
keywords: keywords for query,
|
|
@@ -671,7 +721,7 @@ class AsyncWEBS:
|
|
|
671
721
|
List of dictionaries with instant answers results.
|
|
672
722
|
|
|
673
723
|
Raises:
|
|
674
|
-
WebscoutE: Base exception for
|
|
724
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
675
725
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
676
726
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
677
727
|
"""
|
|
@@ -733,7 +783,7 @@ class AsyncWEBS:
|
|
|
733
783
|
return results
|
|
734
784
|
|
|
735
785
|
async def suggestions(self, keywords: str, region: str = "wt-wt") -> List[Dict[str, str]]:
|
|
736
|
-
"""
|
|
786
|
+
"""webscout suggestions. Query params: https://duckduckgo.com/params.
|
|
737
787
|
|
|
738
788
|
Args:
|
|
739
789
|
keywords: keywords for query.
|
|
@@ -743,7 +793,7 @@ class AsyncWEBS:
|
|
|
743
793
|
List of dictionaries with suggestions results.
|
|
744
794
|
|
|
745
795
|
Raises:
|
|
746
|
-
WebscoutE: Base exception for
|
|
796
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
747
797
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
748
798
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
749
799
|
"""
|
|
@@ -772,7 +822,7 @@ class AsyncWEBS:
|
|
|
772
822
|
radius: int = 0,
|
|
773
823
|
max_results: Optional[int] = None,
|
|
774
824
|
) -> List[Dict[str, str]]:
|
|
775
|
-
"""
|
|
825
|
+
"""webscout maps search. Query params: https://duckduckgo.com/params.
|
|
776
826
|
|
|
777
827
|
Args:
|
|
778
828
|
keywords: keywords for query
|
|
@@ -793,7 +843,7 @@ class AsyncWEBS:
|
|
|
793
843
|
List of dictionaries with maps search results, or None if there was an error.
|
|
794
844
|
|
|
795
845
|
Raises:
|
|
796
|
-
WebscoutE: Base exception for
|
|
846
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
797
847
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
798
848
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
799
849
|
"""
|
|
@@ -945,7 +995,7 @@ class AsyncWEBS:
|
|
|
945
995
|
async def translate(
|
|
946
996
|
self, keywords: Union[List[str], str], from_: Optional[str] = None, to: str = "en"
|
|
947
997
|
) -> List[Dict[str, str]]:
|
|
948
|
-
"""
|
|
998
|
+
"""webscout translate.
|
|
949
999
|
|
|
950
1000
|
Args:
|
|
951
1001
|
keywords: string or list of strings to translate.
|
|
@@ -956,7 +1006,7 @@ class AsyncWEBS:
|
|
|
956
1006
|
List od dictionaries with translated keywords.
|
|
957
1007
|
|
|
958
1008
|
Raises:
|
|
959
|
-
WebscoutE: Base exception for
|
|
1009
|
+
WebscoutE: Base exception for webscout_search errors.
|
|
960
1010
|
RatelimitE: Inherits from WebscoutE, raised for exceeding API request rate limits.
|
|
961
1011
|
TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
|
|
962
1012
|
"""
|
|
@@ -987,7 +1037,13 @@ class AsyncWEBS:
|
|
|
987
1037
|
|
|
988
1038
|
if isinstance(keywords, str):
|
|
989
1039
|
keywords = [keywords]
|
|
990
|
-
tasks = [_translate_keyword(keyword) for keyword in keywords]
|
|
991
|
-
|
|
1040
|
+
tasks = [asyncio.create_task(_translate_keyword(keyword)) for keyword in keywords]
|
|
1041
|
+
try:
|
|
1042
|
+
await asyncio.gather(*tasks)
|
|
1043
|
+
except Exception as e:
|
|
1044
|
+
for task in tasks:
|
|
1045
|
+
task.cancel()
|
|
1046
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
1047
|
+
raise e
|
|
992
1048
|
|
|
993
1049
|
return results
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: webscout
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.5
|
|
4
4
|
Summary: Search for anything using the Google, DuckDuckGo, phind.com. Also containes AI models, can transcribe yt videos, temporary email and phone number generation, have TTS support and webai(terminal gpt and open interpeter)
|
|
5
5
|
Author: OEvortex
|
|
6
6
|
Author-email: helpingai5@gmail.com
|
|
@@ -54,9 +54,31 @@ Provides-Extra: dev
|
|
|
54
54
|
Requires-Dist: ruff >=0.1.6 ; extra == 'dev'
|
|
55
55
|
Requires-Dist: pytest >=7.4.2 ; extra == 'dev'
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
<div align="center">
|
|
58
|
+
<!-- Replace `#` with your actual links -->
|
|
59
|
+
<a href="https://t.me/devsdocode"><img alt="Telegram" src="https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white"></a>
|
|
60
|
+
<a href="https://www.instagram.com/sree.shades_/"><img alt="Instagram" src="https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white"></a>
|
|
61
|
+
<a href="https://www.linkedin.com/in/developer-sreejan/"><img alt="LinkedIn" src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white"></a>
|
|
62
|
+
<a href="https://buymeacoffee.com/devsdocode"><img alt="Buy Me A Coffee" src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-FFDD00?style=for-the-badge&logo=buymeacoffee&logoColor=black"></a>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div align="center">
|
|
66
|
+
<!-- Replace `#` with your actual links -->
|
|
67
|
+
<a href="https://youtube.com/@@OEvortex">➤ Vortex's YouTube Channel</a>
|
|
68
|
+
</div>
|
|
69
|
+
<div align="center">
|
|
70
|
+
<a href="https://youtube.com/@devsdocode">➤ Devs Do Code's YouTube Channel</a>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
|
|
59
74
|
|
|
75
|
+
|
|
76
|
+
# WEBSCOUT
|
|
77
|
+
</div>
|
|
78
|
+
<p align="center">
|
|
79
|
+
<div align="center">
|
|
80
|
+
<img src="https://img.shields.io/badge/WebScout-API-blue?style=for-the-badge&logo=WebScout" alt="WebScout API Badge">
|
|
81
|
+
</div>
|
|
60
82
|
<a href="#"><img alt="Python version" src="https://img.shields.io/pypi/pyversions/webscout"/></a>
|
|
61
83
|
<a href="https://pepy.tech/project/webscout"><img alt="Downloads" src="https://static.pepy.tech/badge/webscout"></a>
|
|
62
84
|
|
|
@@ -64,7 +86,7 @@ Search for anything using the Google, DuckDuckGo, phind.com. Also containes AI m
|
|
|
64
86
|
|
|
65
87
|
|
|
66
88
|
## Table of Contents
|
|
67
|
-
- [
|
|
89
|
+
- [WEBSCOUT](#webscout)
|
|
68
90
|
- [Table of Contents](#table-of-contents)
|
|
69
91
|
- [Install](#install)
|
|
70
92
|
- [CLI version](#cli-version)
|
|
@@ -105,6 +127,7 @@ Search for anything using the Google, DuckDuckGo, phind.com. Also containes AI m
|
|
|
105
127
|
- [10. `Reka` - chat with reka](#10-reka---chat-with-reka)
|
|
106
128
|
- [11. `Cohere` - chat with cohere](#11-cohere---chat-with-cohere)
|
|
107
129
|
- [12. `Xjai` - chat with free gpt 3.5](#12-xjai---chat-with-free-gpt-35)
|
|
130
|
+
- [`ThinkAny` - AI search engine](#thinkany---ai-search-engine)
|
|
108
131
|
- [`LLM`](#llm)
|
|
109
132
|
- [`LLM` with internet](#llm-with-internet)
|
|
110
133
|
- [LLM with deepwebs](#llm-with-deepwebs)
|
|
@@ -790,7 +813,7 @@ print(response)
|
|
|
790
813
|
```python
|
|
791
814
|
from webscout.AI import OPENGPT
|
|
792
815
|
|
|
793
|
-
opengpt = OPENGPT(is_conversation=True, max_tokens=8000, timeout=30)
|
|
816
|
+
opengpt = OPENGPT(is_conversation=True, max_tokens=8000, timeout=30, assistant_id="bca37014-6f97-4f2b-8928-81ea8d478d88")
|
|
794
817
|
while True:
|
|
795
818
|
# Prompt the user for input
|
|
796
819
|
prompt = input("Enter your prompt: ")
|
|
@@ -862,7 +885,31 @@ prompt = "Tell me about india"
|
|
|
862
885
|
response = ai.chat(prompt)
|
|
863
886
|
print(response)
|
|
864
887
|
```
|
|
888
|
+
### `ThinkAny` - AI search engine
|
|
889
|
+
```python
|
|
890
|
+
from webscout.AI import ThinkAnyAI
|
|
865
891
|
|
|
892
|
+
ai = ThinkAnyAI(
|
|
893
|
+
is_conversation=True,
|
|
894
|
+
max_tokens=800,
|
|
895
|
+
timeout=30,
|
|
896
|
+
intro=None,
|
|
897
|
+
filepath=None,
|
|
898
|
+
update_file=True,
|
|
899
|
+
proxies={},
|
|
900
|
+
history_offset=10250,
|
|
901
|
+
act=None,
|
|
902
|
+
web_search=False,
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
prompt = "what is meaning of life"
|
|
906
|
+
|
|
907
|
+
response = ai.ask(prompt)
|
|
908
|
+
|
|
909
|
+
# Extract and print the message from the response
|
|
910
|
+
message = ai.get_message(response)
|
|
911
|
+
print(message)
|
|
912
|
+
```
|
|
866
913
|
### `LLM`
|
|
867
914
|
```python
|
|
868
915
|
from webscout.LLM import LLM
|
|
@@ -1093,3 +1140,21 @@ if __name__ == "__main__":
|
|
|
1093
1140
|
```shell
|
|
1094
1141
|
python -m webscout.webai webai --provider "phind" --rawdog
|
|
1095
1142
|
```
|
|
1143
|
+
|
|
1144
|
+
<div align="center">
|
|
1145
|
+
<!-- Replace `#` with your actual links -->
|
|
1146
|
+
<a href="https://t.me/devsdocode"><img alt="Telegram" src="https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white"></a>
|
|
1147
|
+
<a href="https://www.instagram.com/sree.shades_/"><img alt="Instagram" src="https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white"></a>
|
|
1148
|
+
<a href="https://www.linkedin.com/in/developer-sreejan/"><img alt="LinkedIn" src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white"></a>
|
|
1149
|
+
<a href="https://buymeacoffee.com/devsdocode"><img alt="Buy Me A Coffee" src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-FFDD00?style=for-the-badge&logo=buymeacoffee&logoColor=black"></a>
|
|
1150
|
+
</div>
|
|
1151
|
+
|
|
1152
|
+
<div align="center">
|
|
1153
|
+
<!-- Replace `#` with your actual links -->
|
|
1154
|
+
<a href="https://youtube.com/@@OEvortex">➤ Vortex's YouTube Channel</a>
|
|
1155
|
+
</div>
|
|
1156
|
+
<div align="center">
|
|
1157
|
+
<a href="https://youtube.com/@devsdocode">➤ Devs Do Code's YouTube Channel</a>
|
|
1158
|
+
</div>
|
|
1159
|
+
|
|
1160
|
+
|
|
@@ -10,12 +10,12 @@ DeepWEBS/networks/webpage_fetcher.py,sha256=vRB9T3o-nMgrMkG2NPHTDctNeXaPSKCmBXqu
|
|
|
10
10
|
DeepWEBS/utilsdw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
DeepWEBS/utilsdw/enver.py,sha256=vpI7s4_o_VL9govSryOv-z1zYK3pTEW3-H9QNN8JYtc,2472
|
|
12
12
|
DeepWEBS/utilsdw/logger.py,sha256=Z0nFUcEGyU8r28yKiIyvEtO26xxpmJgbvNToTfwZecc,8174
|
|
13
|
-
webscout/AI.py,sha256=
|
|
13
|
+
webscout/AI.py,sha256=sQGpHRwikBBXcxWsB-nRCSBG3oYhSVBHUFr3C2dHrv8,226145
|
|
14
14
|
webscout/AIbase.py,sha256=GoHbN8r0gq2saYRZv6LA-Fr9Jlcjv80STKFXUq2ZeGU,4710
|
|
15
|
-
webscout/AIutel.py,sha256=
|
|
15
|
+
webscout/AIutel.py,sha256=WJXAUaNK4IQ-txweZhm3scE11b-pK_tlIjS5VWJN8_E,33217
|
|
16
16
|
webscout/DWEBS.py,sha256=QT-7-dUgWhQ_H7EVZD53AVyXxyskoPMKCkFIpzkN56Q,7332
|
|
17
17
|
webscout/LLM.py,sha256=CiDz0okZNEoXuxMwadZnwRGSLpqk2zg0vzvXSxQZjcE,1910
|
|
18
|
-
webscout/__init__.py,sha256=
|
|
18
|
+
webscout/__init__.py,sha256=WcRrw6-MIpt_lHtA498MaO5oWuMRkEk5qYH0mVt4_Nc,1090
|
|
19
19
|
webscout/__main__.py,sha256=ZtTRgsRjUi2JOvYFLF1ZCh55Sdoz94I-BS-TlJC7WDU,126
|
|
20
20
|
webscout/async_providers.py,sha256=pPoSdfB_4SlOYcpAtkKIyDtl7sZ9DGgWy5aIBOjBO9Q,971
|
|
21
21
|
webscout/cli.py,sha256=F888fdrFUQgczMBN4yMOSf6Nh-IbvkqpPhDsbnA2FtQ,17059
|
|
@@ -24,15 +24,15 @@ webscout/g4f.py,sha256=F7POjR03ek7eZvcTX-p7gMe1b0nLNoIqF-L_vZwos0c,24489
|
|
|
24
24
|
webscout/models.py,sha256=5iQIdtedT18YuTZ3npoG7kLMwcrKwhQ7928dl_7qZW0,692
|
|
25
25
|
webscout/tempid.py,sha256=5oc3UbXhPGKxrMRTfRABT-V-dNzH_hOKWtLYM6iCWd4,5896
|
|
26
26
|
webscout/transcriber.py,sha256=EddvTSq7dPJ42V3pQVnGuEiYQ7WjJ9uyeR9kMSxN7uY,20622
|
|
27
|
-
webscout/utils.py,sha256=
|
|
28
|
-
webscout/version.py,sha256=
|
|
27
|
+
webscout/utils.py,sha256=CxeXvp0rWIulUrEaPZMaNfg_tSuQLRSV8uuHA2chyKE,2603
|
|
28
|
+
webscout/version.py,sha256=2IusSRAul_UY0-wnbdAHj0XD7AIfWOrO2BBUSV-Sep0,25
|
|
29
29
|
webscout/voice.py,sha256=0QjXTHAQmCK07IDZXRc7JXem47cnPJH7u3X0sVP1-UQ,967
|
|
30
|
-
webscout/webai.py,sha256=
|
|
31
|
-
webscout/webscout_search.py,sha256=
|
|
32
|
-
webscout/webscout_search_async.py,sha256=
|
|
33
|
-
webscout-1.4.
|
|
34
|
-
webscout-1.4.
|
|
35
|
-
webscout-1.4.
|
|
36
|
-
webscout-1.4.
|
|
37
|
-
webscout-1.4.
|
|
38
|
-
webscout-1.4.
|
|
30
|
+
webscout/webai.py,sha256=Ijnkr8b0mO2I8-mdCZggoIZ5cqMfxaVFpKpeJf7xtTw,82831
|
|
31
|
+
webscout/webscout_search.py,sha256=bBod97PffHDhS1AiyzMJJN74PXWK3A5OFCcEoHYWtcw,3393
|
|
32
|
+
webscout/webscout_search_async.py,sha256=4_L_t_I9WlvpPEI3FI0K3v6Aayr0pNvD3chYOp7JR8o,42902
|
|
33
|
+
webscout-1.4.5.dist-info/LICENSE.md,sha256=mRVwJuT4SXC5O93BFdsfWBjlXjGn2Np90Zm5SocUzM0,3150
|
|
34
|
+
webscout-1.4.5.dist-info/METADATA,sha256=rLuICqX1CjT-NAz4IxoEvn2k78rmNyCLL-ecsHWr3wU,43430
|
|
35
|
+
webscout-1.4.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
36
|
+
webscout-1.4.5.dist-info/entry_points.txt,sha256=8-93eRslYrzTHs5E-6yFRJrve00C9q-SkXJD113jzRY,197
|
|
37
|
+
webscout-1.4.5.dist-info/top_level.txt,sha256=OD5YKy6Y3hldL7SmuxsiEDxAG4LgdSSWwzYk22MF9fk,18
|
|
38
|
+
webscout-1.4.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|