webscout 8.3.7__py3-none-any.whl → 2025.10.13__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/Andi.py +1 -1
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +237 -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 +27 -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 +663 -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/search/__init__.py +51 -0
- webscout/search/base.py +195 -0
- webscout/search/duckduckgo_main.py +54 -0
- webscout/search/engines/__init__.py +48 -0
- webscout/search/engines/bing.py +84 -0
- webscout/search/engines/bing_news.py +52 -0
- webscout/search/engines/brave.py +43 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +78 -0
- webscout/search/engines/duckduckgo/base.py +187 -0
- webscout/search/engines/duckduckgo/images.py +97 -0
- webscout/search/engines/duckduckgo/maps.py +168 -0
- webscout/search/engines/duckduckgo/news.py +68 -0
- webscout/search/engines/duckduckgo/suggestions.py +21 -0
- webscout/search/engines/duckduckgo/text.py +211 -0
- webscout/search/engines/duckduckgo/translate.py +47 -0
- webscout/search/engines/duckduckgo/videos.py +63 -0
- webscout/search/engines/duckduckgo/weather.py +74 -0
- webscout/search/engines/mojeek.py +37 -0
- webscout/search/engines/wikipedia.py +56 -0
- webscout/search/engines/yahoo.py +65 -0
- webscout/search/engines/yahoo_news.py +64 -0
- webscout/search/engines/yandex.py +43 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +32 -0
- webscout/search/engines/yep/images.py +99 -0
- webscout/search/engines/yep/suggestions.py +35 -0
- webscout/search/engines/yep/text.py +114 -0
- webscout/search/http_client.py +156 -0
- webscout/search/results.py +137 -0
- webscout/search/yep_main.py +44 -0
- 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/version.py.bak +2 -0
- 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.13.dist-info}/METADATA +936 -937
- webscout-2025.10.13.dist-info/RECORD +329 -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/webscout_search.py +0 -1183
- webscout/webscout_search_async.py +0 -649
- webscout/yep_search.py +0 -346
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/top_level.txt +0 -0
webscout/Provider/chatglm.py
CHANGED
|
@@ -1,302 +1,395 @@
|
|
|
1
|
-
from curl_cffi import CurlError
|
|
2
|
-
from curl_cffi.requests import Session
|
|
3
|
-
from typing import Any, Dict, Optional, Generator, List, Union
|
|
4
|
-
import uuid
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from webscout.AIutel import
|
|
8
|
-
from webscout.
|
|
9
|
-
from webscout import
|
|
10
|
-
from webscout.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"glm-4.
|
|
24
|
-
"glm-4
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if model in
|
|
42
|
-
return model
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
self.
|
|
62
|
-
self.
|
|
63
|
-
self.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
self.conversation
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
self.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
1
|
+
from curl_cffi import CurlError
|
|
2
|
+
from curl_cffi.requests import Session
|
|
3
|
+
from typing import Any, Dict, Optional, Generator, List, Union
|
|
4
|
+
import uuid
|
|
5
|
+
import time
|
|
6
|
+
import urllib.parse
|
|
7
|
+
from webscout.AIutel import Optimizers
|
|
8
|
+
from webscout.AIutel import Conversation
|
|
9
|
+
from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
|
|
10
|
+
from webscout.AIbase import Provider
|
|
11
|
+
from webscout import exceptions
|
|
12
|
+
from webscout.litagent import LitAgent
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ChatGLM(Provider):
|
|
16
|
+
"""
|
|
17
|
+
A class to interact with the Z.AI Chat API (GLM-4.5).
|
|
18
|
+
"""
|
|
19
|
+
required_auth = False
|
|
20
|
+
url = "https://chat.z.ai"
|
|
21
|
+
# Model nickname mapping system
|
|
22
|
+
MODEL_MAPPING = {
|
|
23
|
+
"glm-4.5V": "glm-4.5v",
|
|
24
|
+
"glm-4-32B": "main_chat",
|
|
25
|
+
"glm-4.5-Air": "0727-106B-API",
|
|
26
|
+
"glm-4.5": "0727-360B-API",
|
|
27
|
+
"glm-4.6": "GLM-4-6-API-V1",
|
|
28
|
+
# Add more nicknames as needed
|
|
29
|
+
}
|
|
30
|
+
# Reverse mapping: API format to nickname
|
|
31
|
+
GLM_TO_MODEL = {v: k for k, v in MODEL_MAPPING.items()}
|
|
32
|
+
AVAILABLE_MODELS = list(MODEL_MAPPING.keys()) + list(GLM_TO_MODEL.keys()) + ["0727-106B-API", "0727-360B-API", "glm-4.5v", "main_chat"]
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def _resolve_model(cls, model: str) -> str:
|
|
36
|
+
"""
|
|
37
|
+
Resolve a model nickname or API name to the API format.
|
|
38
|
+
"""
|
|
39
|
+
if model in cls.GLM_TO_MODEL:
|
|
40
|
+
return model
|
|
41
|
+
if model in cls.MODEL_MAPPING:
|
|
42
|
+
return cls.MODEL_MAPPING[model]
|
|
43
|
+
# fallback to direct API name if present
|
|
44
|
+
if model in ["0727-106B-API", "0727-360B-API", "glm-4.5v", "main_chat"]:
|
|
45
|
+
return model
|
|
46
|
+
raise ValueError(f"Invalid model: {model}. Choose from: {cls.AVAILABLE_MODELS}")
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
is_conversation: bool = True,
|
|
50
|
+
max_tokens: int = 600,
|
|
51
|
+
timeout: int = 30,
|
|
52
|
+
intro: str = None,
|
|
53
|
+
filepath: str = None,
|
|
54
|
+
update_file: bool = True,
|
|
55
|
+
proxies: dict = {},
|
|
56
|
+
history_offset: int = 10250,
|
|
57
|
+
act: str = None,
|
|
58
|
+
model: str = "0727-106B-API",
|
|
59
|
+
):
|
|
60
|
+
"""Initializes the Z.AI Chat API client."""
|
|
61
|
+
self.session = Session()
|
|
62
|
+
self.is_conversation = is_conversation
|
|
63
|
+
self.max_tokens_to_sample = max_tokens
|
|
64
|
+
self.timeout = timeout
|
|
65
|
+
self.last_response = {}
|
|
66
|
+
self.headers = {
|
|
67
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
68
|
+
'App-Name': 'chatglm',
|
|
69
|
+
'Content-Type': 'application/json',
|
|
70
|
+
'Origin': self.url,
|
|
71
|
+
'User-Agent': LitAgent().random(),
|
|
72
|
+
'X-App-Platform': 'pc',
|
|
73
|
+
'X-App-Version': '0.0.1',
|
|
74
|
+
'Accept': 'text/event-stream',
|
|
75
|
+
}
|
|
76
|
+
self.api_endpoint = f"{self.url}/api/chat/completions"
|
|
77
|
+
self.__available_optimizers = (
|
|
78
|
+
method
|
|
79
|
+
for method in dir(Optimizers)
|
|
80
|
+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
81
|
+
)
|
|
82
|
+
self.session.headers.update(self.headers)
|
|
83
|
+
Conversation.intro = (
|
|
84
|
+
AwesomePrompts().get_act(
|
|
85
|
+
act, raise_not_found=True, default=None, case_insensitive=True
|
|
86
|
+
)
|
|
87
|
+
if act
|
|
88
|
+
else intro or Conversation.intro
|
|
89
|
+
)
|
|
90
|
+
self.conversation = Conversation(
|
|
91
|
+
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
92
|
+
)
|
|
93
|
+
self.conversation.history_offset = history_offset
|
|
94
|
+
self.session.proxies = proxies
|
|
95
|
+
# Use nickname resolution for model
|
|
96
|
+
self.model = self._resolve_model(model)
|
|
97
|
+
|
|
98
|
+
def _get_api_key(self):
|
|
99
|
+
if not hasattr(self, 'api_key') or not self.api_key:
|
|
100
|
+
response = self.session.get(f"{self.url}/api/v1/auths/")
|
|
101
|
+
self.api_key = response.json().get("token")
|
|
102
|
+
return self.api_key
|
|
103
|
+
|
|
104
|
+
def _get_user_id(self):
|
|
105
|
+
"""Get user_id from the auth response"""
|
|
106
|
+
if not hasattr(self, 'user_id') or not self.user_id:
|
|
107
|
+
response = self.session.get(f"{self.url}/api/v1/auths/")
|
|
108
|
+
data = response.json()
|
|
109
|
+
self.user_id = data.get("id", str(uuid.uuid4()))
|
|
110
|
+
return self.user_id
|
|
111
|
+
|
|
112
|
+
def _get_cookie(self):
|
|
113
|
+
"""Get authentication cookie from the site"""
|
|
114
|
+
if not hasattr(self, 'cookie') or not self.cookie:
|
|
115
|
+
response = self.session.get(f"{self.url}/")
|
|
116
|
+
self.cookie = response.headers.get('Set-Cookie', '')
|
|
117
|
+
return self.cookie
|
|
118
|
+
|
|
119
|
+
def _build_api_url(self, chat_id: str = "local"):
|
|
120
|
+
"""Build the API URL with all required query parameters"""
|
|
121
|
+
api_key = self._get_api_key()
|
|
122
|
+
user_id = self._get_user_id()
|
|
123
|
+
timestamp = str(int(time.time() * 1000))
|
|
124
|
+
request_id = str(uuid.uuid4())
|
|
125
|
+
user_agent = self.session.headers.get('User-Agent', 'Mozilla/5.0')
|
|
126
|
+
|
|
127
|
+
params = {
|
|
128
|
+
'timestamp': timestamp,
|
|
129
|
+
'requestId': request_id,
|
|
130
|
+
'user_id': user_id,
|
|
131
|
+
'version': '0.0.1',
|
|
132
|
+
'platform': 'web',
|
|
133
|
+
'token': api_key,
|
|
134
|
+
'user_agent': user_agent,
|
|
135
|
+
'language': 'en-US',
|
|
136
|
+
'languages': 'en-US,en',
|
|
137
|
+
'timezone': 'UTC',
|
|
138
|
+
'cookie_enabled': 'true',
|
|
139
|
+
'screen_width': '1920',
|
|
140
|
+
'screen_height': '1080',
|
|
141
|
+
'screen_resolution': '1920x1080',
|
|
142
|
+
'viewport_height': '1080',
|
|
143
|
+
'viewport_width': '1920',
|
|
144
|
+
'viewport_size': '1920x1080',
|
|
145
|
+
'color_depth': '24',
|
|
146
|
+
'pixel_ratio': '1',
|
|
147
|
+
'current_url': f'https://chat.z.ai/c/{chat_id}',
|
|
148
|
+
'pathname': f'/c/{chat_id}',
|
|
149
|
+
'search': '',
|
|
150
|
+
'hash': '',
|
|
151
|
+
'host': 'chat.z.ai',
|
|
152
|
+
'hostname': 'chat.z.ai',
|
|
153
|
+
'protocol': 'https:',
|
|
154
|
+
'referrer': '',
|
|
155
|
+
'title': 'Z.ai Chat - Free AI powered by GLM-4.6',
|
|
156
|
+
'timezone_offset': '0',
|
|
157
|
+
'is_mobile': 'false',
|
|
158
|
+
'is_touch': 'false',
|
|
159
|
+
'max_touch_points': '0',
|
|
160
|
+
'browser_name': 'Chrome',
|
|
161
|
+
'os_name': 'Windows',
|
|
162
|
+
'signature_timestamp': timestamp
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
query_string = urllib.parse.urlencode(params)
|
|
166
|
+
return f"{self.api_endpoint}?{query_string}"
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# _zai_extractor removed; use extract_regexes in sanitize_stream
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def ask(
|
|
173
|
+
self,
|
|
174
|
+
prompt: str,
|
|
175
|
+
stream: bool = False,
|
|
176
|
+
raw: bool = False,
|
|
177
|
+
optimizer: str = None,
|
|
178
|
+
conversationally: bool = False,
|
|
179
|
+
) -> Union[Dict[str, Any], Generator[Any, None, None]]:
|
|
180
|
+
"""Chat with Z.AI API
|
|
181
|
+
Args:
|
|
182
|
+
prompt (str): Prompt to be sent.
|
|
183
|
+
stream (bool, optional): Flag for streaming response. Defaults to False.
|
|
184
|
+
raw (bool, optional): Stream back raw response as received. Defaults to False.
|
|
185
|
+
optimizer (str, optional): Prompt optimizer name. Defaults to None.
|
|
186
|
+
conversationally (bool, optional): Chat conversationally when using optimizer. Defaults to False.
|
|
187
|
+
model (str, optional): Model name. Defaults to None.
|
|
188
|
+
Returns:
|
|
189
|
+
Union[Dict, Generator[Dict, None, None]]: Response generated
|
|
190
|
+
"""
|
|
191
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
192
|
+
if optimizer:
|
|
193
|
+
if optimizer in self.__available_optimizers:
|
|
194
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
195
|
+
conversation_prompt if conversationally else prompt
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
199
|
+
f"Optimizer is not one of {self.__available_optimizers}"
|
|
200
|
+
)
|
|
201
|
+
api_key = self._get_api_key()
|
|
202
|
+
chat_id = str(uuid.uuid4())
|
|
203
|
+
payload = {
|
|
204
|
+
"stream": True,
|
|
205
|
+
"model": self.model, # Already resolved to API format
|
|
206
|
+
"messages": [
|
|
207
|
+
{"role": "user", "content": conversation_prompt}
|
|
208
|
+
],
|
|
209
|
+
"params": {},
|
|
210
|
+
"features": {
|
|
211
|
+
"image_generation": False,
|
|
212
|
+
"web_search": False,
|
|
213
|
+
"auto_web_search": False,
|
|
214
|
+
"preview_mode": True,
|
|
215
|
+
"flags": [],
|
|
216
|
+
"features": [
|
|
217
|
+
{"type": "mcp", "server": "vibe-coding", "status": "hidden"},
|
|
218
|
+
{"type": "mcp", "server": "ppt-maker", "status": "hidden"},
|
|
219
|
+
{"type": "mcp", "server": "image-search", "status": "hidden"},
|
|
220
|
+
{"type": "mcp", "server": "deep-research", "status": "hidden"},
|
|
221
|
+
{"type": "tool_selector", "server": "tool_selector", "status": "hidden"},
|
|
222
|
+
{"type": "mcp", "server": "advanced-search", "status": "hidden"}
|
|
223
|
+
],
|
|
224
|
+
"enable_thinking": True
|
|
225
|
+
},
|
|
226
|
+
"actions": [],
|
|
227
|
+
"tags": [],
|
|
228
|
+
"chat_id": chat_id,
|
|
229
|
+
"id": str(uuid.uuid4())
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
def for_stream():
|
|
233
|
+
streaming_text = ""
|
|
234
|
+
try:
|
|
235
|
+
cookie = self._get_cookie()
|
|
236
|
+
api_url = self._build_api_url(chat_id)
|
|
237
|
+
response = self.session.post(
|
|
238
|
+
api_url,
|
|
239
|
+
json=payload,
|
|
240
|
+
stream=True,
|
|
241
|
+
timeout=self.timeout,
|
|
242
|
+
impersonate="chrome120",
|
|
243
|
+
headers={
|
|
244
|
+
"Authorization": f"Bearer {api_key}",
|
|
245
|
+
"x-fe-version": "prod-fe-1.0.95",
|
|
246
|
+
"accept": "*/*",
|
|
247
|
+
"accept-language": "en-US",
|
|
248
|
+
"content-type": "application/json",
|
|
249
|
+
"dnt": "1",
|
|
250
|
+
"origin": self.url,
|
|
251
|
+
"referer": f"{self.url}/c/{chat_id}",
|
|
252
|
+
"sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
|
|
253
|
+
"sec-ch-ua-mobile": "?0",
|
|
254
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
255
|
+
"sec-fetch-dest": "empty",
|
|
256
|
+
"sec-fetch-mode": "cors",
|
|
257
|
+
"sec-fetch-site": "same-origin",
|
|
258
|
+
"sec-gpc": "1",
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
response.raise_for_status()
|
|
262
|
+
|
|
263
|
+
def glm_content_extractor(chunk):
|
|
264
|
+
if not isinstance(chunk, dict) or chunk.get("type") != "chat:completion":
|
|
265
|
+
return None
|
|
266
|
+
data = chunk.get("data", {})
|
|
267
|
+
phase = data.get("phase")
|
|
268
|
+
usage = data.get("usage")
|
|
269
|
+
if usage:
|
|
270
|
+
return None
|
|
271
|
+
delta_content = data.get("delta_content")
|
|
272
|
+
if delta_content:
|
|
273
|
+
if phase == "thinking":
|
|
274
|
+
# Remove details/summary tags if present
|
|
275
|
+
split_text = delta_content.split("</summary>\n>")[-1]
|
|
276
|
+
return {"reasoning_content": split_text}
|
|
277
|
+
elif phase == "answer":
|
|
278
|
+
return {"content": delta_content}
|
|
279
|
+
else:
|
|
280
|
+
return {"content": delta_content}
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
processed_stream = sanitize_stream(
|
|
284
|
+
data=response.iter_content(chunk_size=None),
|
|
285
|
+
intro_value="data:",
|
|
286
|
+
to_json=True,
|
|
287
|
+
content_extractor=glm_content_extractor,
|
|
288
|
+
yield_raw_on_error=False,
|
|
289
|
+
raw=False
|
|
290
|
+
)
|
|
291
|
+
last_content = ""
|
|
292
|
+
last_reasoning = ""
|
|
293
|
+
in_think = False
|
|
294
|
+
for chunk in processed_stream:
|
|
295
|
+
if not chunk:
|
|
296
|
+
continue
|
|
297
|
+
content = chunk.get('content') if isinstance(chunk, dict) else None
|
|
298
|
+
reasoning = chunk.get('reasoning_content') if isinstance(chunk, dict) else None
|
|
299
|
+
# Handle reasoning_content with <think> tags
|
|
300
|
+
if reasoning and reasoning != last_reasoning:
|
|
301
|
+
if not in_think:
|
|
302
|
+
yield "<think>\n\n"
|
|
303
|
+
in_think = True
|
|
304
|
+
yield reasoning
|
|
305
|
+
last_reasoning = reasoning
|
|
306
|
+
# If we were in <think> and now have new content, close <think>
|
|
307
|
+
if in_think and content and content != last_content:
|
|
308
|
+
yield "\n</think>\n\n"
|
|
309
|
+
in_think = False
|
|
310
|
+
# Handle normal content
|
|
311
|
+
if content and content != last_content:
|
|
312
|
+
yield content
|
|
313
|
+
streaming_text += content
|
|
314
|
+
last_content = content
|
|
315
|
+
# Close <think> tag if still open at end
|
|
316
|
+
if in_think:
|
|
317
|
+
yield "\n</think>\n\n"
|
|
318
|
+
if not raw:
|
|
319
|
+
self.last_response = {"text": streaming_text}
|
|
320
|
+
self.conversation.update_chat_history(prompt, streaming_text)
|
|
321
|
+
except CurlError as e:
|
|
322
|
+
raise exceptions.APIConnectionError(f"Request failed (CurlError): {e}") from e
|
|
323
|
+
except Exception as e:
|
|
324
|
+
raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}") from e
|
|
325
|
+
|
|
326
|
+
def for_non_stream():
|
|
327
|
+
full_text = ""
|
|
328
|
+
try:
|
|
329
|
+
for chunk_data in for_stream():
|
|
330
|
+
if raw:
|
|
331
|
+
if isinstance(chunk_data, str):
|
|
332
|
+
full_text += chunk_data
|
|
333
|
+
else:
|
|
334
|
+
if isinstance(chunk_data, dict) and "text" in chunk_data:
|
|
335
|
+
full_text += chunk_data["text"]
|
|
336
|
+
except Exception as e:
|
|
337
|
+
if not full_text:
|
|
338
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to get non-stream response: {str(e)}") from e
|
|
339
|
+
self.last_response = {"text": full_text}
|
|
340
|
+
return full_text if raw else self.last_response
|
|
341
|
+
return for_stream() if stream else for_non_stream()
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def chat(
|
|
345
|
+
self,
|
|
346
|
+
prompt: str,
|
|
347
|
+
stream: bool = False,
|
|
348
|
+
optimizer: str = None,
|
|
349
|
+
conversationally: bool = False,
|
|
350
|
+
raw: bool = False,
|
|
351
|
+
) -> Union[str, Generator[str, None, None]]:
|
|
352
|
+
"""Generate response `str`"""
|
|
353
|
+
|
|
354
|
+
def for_stream():
|
|
355
|
+
for response in self.ask(
|
|
356
|
+
prompt, True, raw=raw, optimizer=optimizer, conversationally=conversationally
|
|
357
|
+
):
|
|
358
|
+
if raw:
|
|
359
|
+
yield response
|
|
360
|
+
else:
|
|
361
|
+
# Only call get_message on dicts, yield str directly
|
|
362
|
+
if isinstance(response, dict):
|
|
363
|
+
yield self.get_message(response)
|
|
364
|
+
elif isinstance(response, str):
|
|
365
|
+
yield response
|
|
366
|
+
|
|
367
|
+
def for_non_stream():
|
|
368
|
+
result = self.ask(
|
|
369
|
+
prompt,
|
|
370
|
+
False,
|
|
371
|
+
raw=raw,
|
|
372
|
+
optimizer=optimizer,
|
|
373
|
+
conversationally=conversationally,
|
|
374
|
+
)
|
|
375
|
+
if raw:
|
|
376
|
+
return result if isinstance(result, str) else self.get_message(result)
|
|
377
|
+
else:
|
|
378
|
+
return self.get_message(result)
|
|
379
|
+
|
|
380
|
+
return for_stream() if stream else for_non_stream()
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def get_message(self, response: dict) -> str:
|
|
384
|
+
"""Retrieves message only from response"""
|
|
385
|
+
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
386
|
+
return response.get("text", "")
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
if __name__ == "__main__":
|
|
391
|
+
from rich import print
|
|
392
|
+
ai = ChatGLM(model="glm-4.6")
|
|
393
|
+
response = ai.chat("hi", stream=True, raw=True)
|
|
394
|
+
for chunk in response:
|
|
302
395
|
print(chunk, end="", flush=True)
|