webscout 8.3.7__py3-none-any.whl → 2025.10.11__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/AIauto.py +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +236 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +30 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +664 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/webscout_search.py +1183 -1183
- webscout/webscout_search_async.py +649 -649
- webscout/yep_search.py +346 -346
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/METADATA +937 -937
- webscout-2025.10.11.dist-info/RECORD +300 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.11.dist-info}/top_level.txt +0 -0
webscout/Provider/QwenLM.py
CHANGED
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Union, Any, Dict, Generator, Optional
|
|
3
|
-
import uuid
|
|
4
|
-
import time
|
|
5
|
-
|
|
6
|
-
from curl_cffi import Session
|
|
7
|
-
|
|
8
|
-
from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
|
|
9
|
-
from webscout.AIbase import Provider
|
|
10
|
-
from webscout import exceptions
|
|
11
|
-
|
|
12
|
-
class QwenLM(Provider):
|
|
13
|
-
"""
|
|
14
|
-
A class to interact with the QwenLM API
|
|
15
|
-
"""
|
|
16
|
-
required_auth = True
|
|
17
|
-
AVAILABLE_MODELS = [
|
|
18
|
-
"qwen-plus-2025-09-11",
|
|
19
|
-
"qwen3-max-preview",
|
|
20
|
-
"qwen3-235b-a22b",
|
|
21
|
-
"qwen3-coder-plus",
|
|
22
|
-
"qwen3-30b-a3b",
|
|
23
|
-
"qwen3-coder-30b-a3b-instruct",
|
|
24
|
-
"qwen-max-latest",
|
|
25
|
-
"qwen-plus-2025-01-25",
|
|
26
|
-
"qwq-32b",
|
|
27
|
-
"qwen-turbo-2025-02-11",
|
|
28
|
-
"qwen2.5-omni-7b",
|
|
29
|
-
"qvq-72b-preview-0310",
|
|
30
|
-
"qwen2.5-vl-32b-instruct",
|
|
31
|
-
"qwen2.5-14b-instruct-1m",
|
|
32
|
-
"qwen2.5-coder-32b-instruct",
|
|
33
|
-
"qwen2.5-72b-instruct"
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
def __init__(
|
|
37
|
-
self,
|
|
38
|
-
cookies_path: str,
|
|
39
|
-
is_conversation: bool = True,
|
|
40
|
-
max_tokens: int = 600,
|
|
41
|
-
timeout: int = 30,
|
|
42
|
-
intro: Optional[str] = None,
|
|
43
|
-
filepath: Optional[str] = None,
|
|
44
|
-
update_file: bool = True,
|
|
45
|
-
proxies: dict = {},
|
|
46
|
-
history_offset: int = 10250,
|
|
47
|
-
act: Optional[str] = None,
|
|
48
|
-
model: str = "qwen-plus-2025-09-11",
|
|
49
|
-
system_prompt: str = "You are a helpful AI assistant."
|
|
50
|
-
):
|
|
51
|
-
"""Initializes the QwenLM API client."""
|
|
52
|
-
if model not in self.AVAILABLE_MODELS:
|
|
53
|
-
raise ValueError(
|
|
54
|
-
f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
self.session = Session(impersonate="chrome")
|
|
58
|
-
self.is_conversation = is_conversation
|
|
59
|
-
self.max_tokens_to_sample = max_tokens
|
|
60
|
-
self.api_endpoint = "https://chat.qwen.ai/api/chat/completions"
|
|
61
|
-
self.stream_chunk_size = 64
|
|
62
|
-
self.timeout = timeout
|
|
63
|
-
self.last_response = {}
|
|
64
|
-
self.model = model
|
|
65
|
-
self.system_prompt = system_prompt
|
|
66
|
-
self.cookies_path = cookies_path
|
|
67
|
-
self.cookies_dict, self.token = self._load_cookies()
|
|
68
|
-
self.chat_id = str(uuid.uuid4())
|
|
69
|
-
|
|
70
|
-
self.headers = {
|
|
71
|
-
"Accept": "*/*",
|
|
72
|
-
"Accept-Language": "en-US,en;q=0.9",
|
|
73
|
-
"Cache-Control": "no-cache",
|
|
74
|
-
"Connection": "keep-alive",
|
|
75
|
-
"DNT": "1",
|
|
76
|
-
"Origin": "https://chat.qwen.ai",
|
|
77
|
-
"Pragma": "no-cache",
|
|
78
|
-
"Referer": f"https://chat.qwen.ai/c/{self.chat_id}",
|
|
79
|
-
"Sec-Fetch-Dest": "empty",
|
|
80
|
-
"Sec-Fetch-Mode": "cors",
|
|
81
|
-
"Sec-Fetch-Site": "same-origin",
|
|
82
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
83
|
-
"authorization": f"Bearer {self.token}" if self.token else '',
|
|
84
|
-
}
|
|
85
|
-
self.session.headers.update(self.headers)
|
|
86
|
-
self.session.cookies.update(self.cookies_dict)
|
|
87
|
-
self.session.proxies = proxies
|
|
88
|
-
self.chat_type = "t2t" # search - used WEB, t2t - chatbot, t2i - image_gen
|
|
89
|
-
|
|
90
|
-
self.__available_optimizers = (
|
|
91
|
-
method
|
|
92
|
-
for method in dir(Optimizers)
|
|
93
|
-
if callable(getattr(Optimizers, method))
|
|
94
|
-
and not method.startswith("__")
|
|
95
|
-
)
|
|
96
|
-
Conversation.intro = (
|
|
97
|
-
AwesomePrompts().get_act(
|
|
98
|
-
act, raise_not_found=True, default=None, case_insensitive=True
|
|
99
|
-
)
|
|
100
|
-
if act
|
|
101
|
-
else intro or Conversation.intro
|
|
102
|
-
)
|
|
103
|
-
self.conversation = Conversation(
|
|
104
|
-
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
105
|
-
)
|
|
106
|
-
self.conversation.history_offset = history_offset
|
|
107
|
-
|
|
108
|
-
def _load_cookies(self) -> tuple[dict, str]:
|
|
109
|
-
"""Load cookies from a JSON file and build a cookie dict."""
|
|
110
|
-
try:
|
|
111
|
-
with open(self.cookies_path, "r") as f:
|
|
112
|
-
cookies = json.load(f)
|
|
113
|
-
cookies_dict = {cookie['name']: cookie['value'] for cookie in cookies}
|
|
114
|
-
token = cookies_dict.get("token", "")
|
|
115
|
-
return cookies_dict, token
|
|
116
|
-
except FileNotFoundError:
|
|
117
|
-
raise exceptions.InvalidAuthenticationError(
|
|
118
|
-
"Error: cookies.json file not found!"
|
|
119
|
-
)
|
|
120
|
-
except json.JSONDecodeError:
|
|
121
|
-
raise exceptions.InvalidAuthenticationError(
|
|
122
|
-
"Error: Invalid JSON format in cookies.json!"
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
def ask(
|
|
126
|
-
self,
|
|
127
|
-
prompt: str,
|
|
128
|
-
stream: bool = False,
|
|
129
|
-
raw: bool = False,
|
|
130
|
-
optimizer: Optional[str] = None,
|
|
131
|
-
conversationally: bool = False,
|
|
132
|
-
) -> Union[Dict[str, Any], Generator[Any, None, None]]:
|
|
133
|
-
"""Chat with AI."""
|
|
134
|
-
|
|
135
|
-
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
136
|
-
if optimizer:
|
|
137
|
-
if optimizer in self.__available_optimizers:
|
|
138
|
-
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
139
|
-
conversation_prompt if conversationally else prompt
|
|
140
|
-
)
|
|
141
|
-
else:
|
|
142
|
-
raise Exception(
|
|
143
|
-
f"Optimizer is not one of {list(self.__available_optimizers)}"
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
payload = {
|
|
147
|
-
'stream': stream,
|
|
148
|
-
'incremental_output': False,
|
|
149
|
-
"chat_type": "t2t",
|
|
150
|
-
"model": self.model,
|
|
151
|
-
"messages": [
|
|
152
|
-
{
|
|
153
|
-
"role": "user",
|
|
154
|
-
"content": conversation_prompt,
|
|
155
|
-
"chat_type": "t2t",
|
|
156
|
-
"extra": {},
|
|
157
|
-
"feature_config": {"thinking_enabled": False},
|
|
158
|
-
}
|
|
159
|
-
],
|
|
160
|
-
"session_id": str(uuid.uuid4()),
|
|
161
|
-
"chat_id": str(uuid.uuid4()),
|
|
162
|
-
"id": str(uuid.uuid4()),
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
def for_stream() -> Generator[Dict[str, Any], None, None]:
|
|
166
|
-
response = self.session.post(
|
|
167
|
-
self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
|
|
168
|
-
)
|
|
169
|
-
if not response.ok:
|
|
170
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
171
|
-
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
cumulative_text = ""
|
|
175
|
-
for line in response.iter_lines(decode_unicode=False):
|
|
176
|
-
if line:
|
|
177
|
-
line = line.decode('utf-8') if isinstance(line, bytes) else line
|
|
178
|
-
if line.startswith("data: "):
|
|
179
|
-
data = line[6:]
|
|
180
|
-
if data == "[DONE]":
|
|
181
|
-
break
|
|
182
|
-
try:
|
|
183
|
-
json_data = json.loads(data)
|
|
184
|
-
if "response.created" in json_data:
|
|
185
|
-
# Initial response, can ignore or use for chat_id etc.
|
|
186
|
-
continue
|
|
187
|
-
if "choices" in json_data:
|
|
188
|
-
delta = json_data["choices"][0]["delta"]
|
|
189
|
-
new_content = delta.get("content", "")
|
|
190
|
-
status = delta.get("status", "")
|
|
191
|
-
if status == "finished":
|
|
192
|
-
break
|
|
193
|
-
cumulative_text += new_content
|
|
194
|
-
if new_content:
|
|
195
|
-
yield delta if raw else {"text": new_content}
|
|
196
|
-
except json.JSONDecodeError:
|
|
197
|
-
continue
|
|
198
|
-
self.last_response.update(dict(text=cumulative_text))
|
|
199
|
-
self.conversation.update_chat_history(
|
|
200
|
-
prompt, self.get_message(self.last_response)
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
def for_non_stream() -> Dict[str, Any]:
|
|
204
|
-
"""
|
|
205
|
-
Handles non-streaming responses by making a non-streaming request.
|
|
206
|
-
"""
|
|
207
|
-
|
|
208
|
-
# Create a non-streaming payload
|
|
209
|
-
non_stream_payload = payload.copy()
|
|
210
|
-
non_stream_payload['stream'] = False
|
|
211
|
-
non_stream_payload['incremental_output'] = False
|
|
212
|
-
|
|
213
|
-
response = self.session.post(
|
|
214
|
-
self.api_endpoint, json=non_stream_payload, headers=self.headers, stream=False, timeout=self.timeout
|
|
215
|
-
)
|
|
216
|
-
if not response.ok:
|
|
217
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
218
|
-
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
result = response.json()
|
|
222
|
-
assistant_reply = (
|
|
223
|
-
result.get("choices", [{}])[0]
|
|
224
|
-
.get("message", {})
|
|
225
|
-
.get("content", "")
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
self.last_response.update({"text": assistant_reply})
|
|
229
|
-
self.conversation.update_chat_history(
|
|
230
|
-
prompt, self.get_message(self.last_response)
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
return {"text": assistant_reply}
|
|
234
|
-
|
|
235
|
-
return for_stream() if stream else for_non_stream()
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
def chat(
|
|
239
|
-
self,
|
|
240
|
-
prompt: str,
|
|
241
|
-
stream: bool = False,
|
|
242
|
-
optimizer: Optional[str] = None,
|
|
243
|
-
conversationally: bool = False,
|
|
244
|
-
) -> Union[str, Generator[str, None, None]]:
|
|
245
|
-
"""Generate response string from chat."""
|
|
246
|
-
|
|
247
|
-
def for_stream() -> Generator[str, None, None]:
|
|
248
|
-
for response in self.ask(prompt, True, optimizer=optimizer, conversationally=conversationally):
|
|
249
|
-
yield response if isinstance(response, str) else response["text"]
|
|
250
|
-
|
|
251
|
-
def for_non_stream() -> str:
|
|
252
|
-
result = self.ask(prompt, False, optimizer=optimizer, conversationally=conversationally)
|
|
253
|
-
return self.get_message(result)
|
|
254
|
-
|
|
255
|
-
return for_stream() if stream else for_non_stream()
|
|
256
|
-
|
|
257
|
-
def get_message(self, response: dict) -> str:
|
|
258
|
-
"""Extracts the message content from a response dict."""
|
|
259
|
-
assert isinstance(response, dict), "Response should be a dict"
|
|
260
|
-
return response.get("text", "")
|
|
261
|
-
|
|
262
|
-
if __name__ == "__main__":
|
|
263
|
-
from rich import print
|
|
264
|
-
cookies_path = r"C:\Users\koula\Desktop\Webscout\cookies.json"
|
|
265
|
-
for model in QwenLM.AVAILABLE_MODELS:
|
|
266
|
-
ai = QwenLM(cookies_path=cookies_path, model=model)
|
|
267
|
-
response = ai.chat("hi")
|
|
268
|
-
print(f"Model: {model}")
|
|
269
|
-
print(response)
|
|
270
|
-
print("-" * 50)
|
|
271
|
-
# for chunk in response:
|
|
272
|
-
# print(chunk, end="", flush=True)
|
|
1
|
+
import json
|
|
2
|
+
from typing import Union, Any, Dict, Generator, Optional
|
|
3
|
+
import uuid
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from curl_cffi import Session
|
|
7
|
+
|
|
8
|
+
from webscout.AIutel import Optimizers, Conversation, AwesomePrompts
|
|
9
|
+
from webscout.AIbase import Provider
|
|
10
|
+
from webscout import exceptions
|
|
11
|
+
|
|
12
|
+
class QwenLM(Provider):
|
|
13
|
+
"""
|
|
14
|
+
A class to interact with the QwenLM API
|
|
15
|
+
"""
|
|
16
|
+
required_auth = True
|
|
17
|
+
AVAILABLE_MODELS = [
|
|
18
|
+
"qwen-plus-2025-09-11",
|
|
19
|
+
"qwen3-max-preview",
|
|
20
|
+
"qwen3-235b-a22b",
|
|
21
|
+
"qwen3-coder-plus",
|
|
22
|
+
"qwen3-30b-a3b",
|
|
23
|
+
"qwen3-coder-30b-a3b-instruct",
|
|
24
|
+
"qwen-max-latest",
|
|
25
|
+
"qwen-plus-2025-01-25",
|
|
26
|
+
"qwq-32b",
|
|
27
|
+
"qwen-turbo-2025-02-11",
|
|
28
|
+
"qwen2.5-omni-7b",
|
|
29
|
+
"qvq-72b-preview-0310",
|
|
30
|
+
"qwen2.5-vl-32b-instruct",
|
|
31
|
+
"qwen2.5-14b-instruct-1m",
|
|
32
|
+
"qwen2.5-coder-32b-instruct",
|
|
33
|
+
"qwen2.5-72b-instruct"
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
cookies_path: str,
|
|
39
|
+
is_conversation: bool = True,
|
|
40
|
+
max_tokens: int = 600,
|
|
41
|
+
timeout: int = 30,
|
|
42
|
+
intro: Optional[str] = None,
|
|
43
|
+
filepath: Optional[str] = None,
|
|
44
|
+
update_file: bool = True,
|
|
45
|
+
proxies: dict = {},
|
|
46
|
+
history_offset: int = 10250,
|
|
47
|
+
act: Optional[str] = None,
|
|
48
|
+
model: str = "qwen-plus-2025-09-11",
|
|
49
|
+
system_prompt: str = "You are a helpful AI assistant."
|
|
50
|
+
):
|
|
51
|
+
"""Initializes the QwenLM API client."""
|
|
52
|
+
if model not in self.AVAILABLE_MODELS:
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
self.session = Session(impersonate="chrome")
|
|
58
|
+
self.is_conversation = is_conversation
|
|
59
|
+
self.max_tokens_to_sample = max_tokens
|
|
60
|
+
self.api_endpoint = "https://chat.qwen.ai/api/chat/completions"
|
|
61
|
+
self.stream_chunk_size = 64
|
|
62
|
+
self.timeout = timeout
|
|
63
|
+
self.last_response = {}
|
|
64
|
+
self.model = model
|
|
65
|
+
self.system_prompt = system_prompt
|
|
66
|
+
self.cookies_path = cookies_path
|
|
67
|
+
self.cookies_dict, self.token = self._load_cookies()
|
|
68
|
+
self.chat_id = str(uuid.uuid4())
|
|
69
|
+
|
|
70
|
+
self.headers = {
|
|
71
|
+
"Accept": "*/*",
|
|
72
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
73
|
+
"Cache-Control": "no-cache",
|
|
74
|
+
"Connection": "keep-alive",
|
|
75
|
+
"DNT": "1",
|
|
76
|
+
"Origin": "https://chat.qwen.ai",
|
|
77
|
+
"Pragma": "no-cache",
|
|
78
|
+
"Referer": f"https://chat.qwen.ai/c/{self.chat_id}",
|
|
79
|
+
"Sec-Fetch-Dest": "empty",
|
|
80
|
+
"Sec-Fetch-Mode": "cors",
|
|
81
|
+
"Sec-Fetch-Site": "same-origin",
|
|
82
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
|
83
|
+
"authorization": f"Bearer {self.token}" if self.token else '',
|
|
84
|
+
}
|
|
85
|
+
self.session.headers.update(self.headers)
|
|
86
|
+
self.session.cookies.update(self.cookies_dict)
|
|
87
|
+
self.session.proxies = proxies
|
|
88
|
+
self.chat_type = "t2t" # search - used WEB, t2t - chatbot, t2i - image_gen
|
|
89
|
+
|
|
90
|
+
self.__available_optimizers = (
|
|
91
|
+
method
|
|
92
|
+
for method in dir(Optimizers)
|
|
93
|
+
if callable(getattr(Optimizers, method))
|
|
94
|
+
and not method.startswith("__")
|
|
95
|
+
)
|
|
96
|
+
Conversation.intro = (
|
|
97
|
+
AwesomePrompts().get_act(
|
|
98
|
+
act, raise_not_found=True, default=None, case_insensitive=True
|
|
99
|
+
)
|
|
100
|
+
if act
|
|
101
|
+
else intro or Conversation.intro
|
|
102
|
+
)
|
|
103
|
+
self.conversation = Conversation(
|
|
104
|
+
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
105
|
+
)
|
|
106
|
+
self.conversation.history_offset = history_offset
|
|
107
|
+
|
|
108
|
+
def _load_cookies(self) -> tuple[dict, str]:
|
|
109
|
+
"""Load cookies from a JSON file and build a cookie dict."""
|
|
110
|
+
try:
|
|
111
|
+
with open(self.cookies_path, "r") as f:
|
|
112
|
+
cookies = json.load(f)
|
|
113
|
+
cookies_dict = {cookie['name']: cookie['value'] for cookie in cookies}
|
|
114
|
+
token = cookies_dict.get("token", "")
|
|
115
|
+
return cookies_dict, token
|
|
116
|
+
except FileNotFoundError:
|
|
117
|
+
raise exceptions.InvalidAuthenticationError(
|
|
118
|
+
"Error: cookies.json file not found!"
|
|
119
|
+
)
|
|
120
|
+
except json.JSONDecodeError:
|
|
121
|
+
raise exceptions.InvalidAuthenticationError(
|
|
122
|
+
"Error: Invalid JSON format in cookies.json!"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def ask(
|
|
126
|
+
self,
|
|
127
|
+
prompt: str,
|
|
128
|
+
stream: bool = False,
|
|
129
|
+
raw: bool = False,
|
|
130
|
+
optimizer: Optional[str] = None,
|
|
131
|
+
conversationally: bool = False,
|
|
132
|
+
) -> Union[Dict[str, Any], Generator[Any, None, None]]:
|
|
133
|
+
"""Chat with AI."""
|
|
134
|
+
|
|
135
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
136
|
+
if optimizer:
|
|
137
|
+
if optimizer in self.__available_optimizers:
|
|
138
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
139
|
+
conversation_prompt if conversationally else prompt
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
raise Exception(
|
|
143
|
+
f"Optimizer is not one of {list(self.__available_optimizers)}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
payload = {
|
|
147
|
+
'stream': stream,
|
|
148
|
+
'incremental_output': False,
|
|
149
|
+
"chat_type": "t2t",
|
|
150
|
+
"model": self.model,
|
|
151
|
+
"messages": [
|
|
152
|
+
{
|
|
153
|
+
"role": "user",
|
|
154
|
+
"content": conversation_prompt,
|
|
155
|
+
"chat_type": "t2t",
|
|
156
|
+
"extra": {},
|
|
157
|
+
"feature_config": {"thinking_enabled": False},
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"session_id": str(uuid.uuid4()),
|
|
161
|
+
"chat_id": str(uuid.uuid4()),
|
|
162
|
+
"id": str(uuid.uuid4()),
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
def for_stream() -> Generator[Dict[str, Any], None, None]:
|
|
166
|
+
response = self.session.post(
|
|
167
|
+
self.api_endpoint, json=payload, headers=self.headers, stream=True, timeout=self.timeout
|
|
168
|
+
)
|
|
169
|
+
if not response.ok:
|
|
170
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
171
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
cumulative_text = ""
|
|
175
|
+
for line in response.iter_lines(decode_unicode=False):
|
|
176
|
+
if line:
|
|
177
|
+
line = line.decode('utf-8') if isinstance(line, bytes) else line
|
|
178
|
+
if line.startswith("data: "):
|
|
179
|
+
data = line[6:]
|
|
180
|
+
if data == "[DONE]":
|
|
181
|
+
break
|
|
182
|
+
try:
|
|
183
|
+
json_data = json.loads(data)
|
|
184
|
+
if "response.created" in json_data:
|
|
185
|
+
# Initial response, can ignore or use for chat_id etc.
|
|
186
|
+
continue
|
|
187
|
+
if "choices" in json_data:
|
|
188
|
+
delta = json_data["choices"][0]["delta"]
|
|
189
|
+
new_content = delta.get("content", "")
|
|
190
|
+
status = delta.get("status", "")
|
|
191
|
+
if status == "finished":
|
|
192
|
+
break
|
|
193
|
+
cumulative_text += new_content
|
|
194
|
+
if new_content:
|
|
195
|
+
yield delta if raw else {"text": new_content}
|
|
196
|
+
except json.JSONDecodeError:
|
|
197
|
+
continue
|
|
198
|
+
self.last_response.update(dict(text=cumulative_text))
|
|
199
|
+
self.conversation.update_chat_history(
|
|
200
|
+
prompt, self.get_message(self.last_response)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def for_non_stream() -> Dict[str, Any]:
|
|
204
|
+
"""
|
|
205
|
+
Handles non-streaming responses by making a non-streaming request.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
# Create a non-streaming payload
|
|
209
|
+
non_stream_payload = payload.copy()
|
|
210
|
+
non_stream_payload['stream'] = False
|
|
211
|
+
non_stream_payload['incremental_output'] = False
|
|
212
|
+
|
|
213
|
+
response = self.session.post(
|
|
214
|
+
self.api_endpoint, json=non_stream_payload, headers=self.headers, stream=False, timeout=self.timeout
|
|
215
|
+
)
|
|
216
|
+
if not response.ok:
|
|
217
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
218
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
result = response.json()
|
|
222
|
+
assistant_reply = (
|
|
223
|
+
result.get("choices", [{}])[0]
|
|
224
|
+
.get("message", {})
|
|
225
|
+
.get("content", "")
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
self.last_response.update({"text": assistant_reply})
|
|
229
|
+
self.conversation.update_chat_history(
|
|
230
|
+
prompt, self.get_message(self.last_response)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return {"text": assistant_reply}
|
|
234
|
+
|
|
235
|
+
return for_stream() if stream else for_non_stream()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def chat(
|
|
239
|
+
self,
|
|
240
|
+
prompt: str,
|
|
241
|
+
stream: bool = False,
|
|
242
|
+
optimizer: Optional[str] = None,
|
|
243
|
+
conversationally: bool = False,
|
|
244
|
+
) -> Union[str, Generator[str, None, None]]:
|
|
245
|
+
"""Generate response string from chat."""
|
|
246
|
+
|
|
247
|
+
def for_stream() -> Generator[str, None, None]:
|
|
248
|
+
for response in self.ask(prompt, True, optimizer=optimizer, conversationally=conversationally):
|
|
249
|
+
yield response if isinstance(response, str) else response["text"]
|
|
250
|
+
|
|
251
|
+
def for_non_stream() -> str:
|
|
252
|
+
result = self.ask(prompt, False, optimizer=optimizer, conversationally=conversationally)
|
|
253
|
+
return self.get_message(result)
|
|
254
|
+
|
|
255
|
+
return for_stream() if stream else for_non_stream()
|
|
256
|
+
|
|
257
|
+
def get_message(self, response: dict) -> str:
|
|
258
|
+
"""Extracts the message content from a response dict."""
|
|
259
|
+
assert isinstance(response, dict), "Response should be a dict"
|
|
260
|
+
return response.get("text", "")
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
from rich import print
|
|
264
|
+
cookies_path = r"C:\Users\koula\Desktop\Webscout\cookies.json"
|
|
265
|
+
for model in QwenLM.AVAILABLE_MODELS:
|
|
266
|
+
ai = QwenLM(cookies_path=cookies_path, model=model)
|
|
267
|
+
response = ai.chat("hi")
|
|
268
|
+
print(f"Model: {model}")
|
|
269
|
+
print(response)
|
|
270
|
+
print("-" * 50)
|
|
271
|
+
# for chunk in response:
|
|
272
|
+
# print(chunk, end="", flush=True)
|
|
@@ -7,12 +7,27 @@ from pathlib import Path
|
|
|
7
7
|
# Get current directory
|
|
8
8
|
current_dir = Path(__file__).parent
|
|
9
9
|
|
|
10
|
+
# List to store all exported names
|
|
11
|
+
__all__ = []
|
|
12
|
+
|
|
10
13
|
# Auto-import all .py files (except __init__.py)
|
|
11
14
|
for file_path in current_dir.glob("*.py"):
|
|
12
15
|
if file_path.name != "__init__.py":
|
|
13
16
|
module_name = file_path.stem
|
|
14
17
|
try:
|
|
15
18
|
module = importlib.import_module(f".{module_name}", package=__name__)
|
|
16
|
-
|
|
19
|
+
|
|
20
|
+
# Import the main class (assumes class name matches filename)
|
|
21
|
+
class_name = module_name
|
|
22
|
+
if hasattr(module, class_name):
|
|
23
|
+
globals()[class_name] = getattr(module, class_name)
|
|
24
|
+
__all__.append(class_name)
|
|
25
|
+
else:
|
|
26
|
+
# If no matching class, import all public attributes
|
|
27
|
+
for attr_name in dir(module):
|
|
28
|
+
if not attr_name.startswith('_'):
|
|
29
|
+
globals()[attr_name] = getattr(module, attr_name)
|
|
30
|
+
if attr_name not in __all__:
|
|
31
|
+
__all__.append(attr_name)
|
|
17
32
|
except ImportError:
|
|
18
33
|
pass # Skip files that can't be imported
|