webscout 8.2.9__py3-none-any.whl → 2026.1.19__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.
- webscout/AIauto.py +524 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -375
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -129
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -478
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
webscout/AIauto.py
CHANGED
|
@@ -1,251 +1,524 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module provides the AUTO provider, which automatically selects and uses
|
|
3
|
-
an available LLM provider from the webscout library that doesn't require
|
|
4
|
-
API keys or cookies.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
self
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
1
|
+
"""
|
|
2
|
+
This module provides the AUTO provider, which automatically selects and uses
|
|
3
|
+
an available LLM provider from the webscout library that doesn't require
|
|
4
|
+
API keys or cookies.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import difflib
|
|
8
|
+
import importlib
|
|
9
|
+
import inspect
|
|
10
|
+
import pkgutil
|
|
11
|
+
import random
|
|
12
|
+
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
|
|
13
|
+
|
|
14
|
+
from webscout.AIbase import Provider, Response
|
|
15
|
+
from webscout.exceptions import AllProvidersFailure
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_providers() -> Tuple[Dict[str, Type[Provider]], set]:
|
|
19
|
+
"""
|
|
20
|
+
Dynamically loads all Provider classes from the `webscout.Provider` package.
|
|
21
|
+
|
|
22
|
+
This function iterates through the modules in the `webscout.Provider` package,
|
|
23
|
+
imports each module, and inspects its attributes to identify classes that
|
|
24
|
+
inherit from the `Provider` base class. It also identifies providers that
|
|
25
|
+
require special authentication parameters.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Tuple[Dict[str, Type[Provider]], set]: A tuple containing two elements:
|
|
29
|
+
- provider_map (Dict[str, Type[Provider]]): A dictionary mapping uppercase provider names to their classes.
|
|
30
|
+
- api_key_providers (set): A set of uppercase provider names requiring special authentication.
|
|
31
|
+
"""
|
|
32
|
+
provider_map: Dict[str, Type[Provider]] = {}
|
|
33
|
+
api_key_providers: set = set()
|
|
34
|
+
provider_package = importlib.import_module("webscout.Provider")
|
|
35
|
+
|
|
36
|
+
for _, module_name, _ in pkgutil.iter_modules(provider_package.__path__):
|
|
37
|
+
try:
|
|
38
|
+
module = importlib.import_module(f"webscout.Provider.{module_name}")
|
|
39
|
+
for attr_name in dir(module):
|
|
40
|
+
attr = getattr(module, attr_name)
|
|
41
|
+
if isinstance(attr, type) and issubclass(attr, Provider) and attr != Provider:
|
|
42
|
+
p_name = attr_name.upper()
|
|
43
|
+
provider_map[p_name] = attr
|
|
44
|
+
|
|
45
|
+
if hasattr(attr, "required_auth") and attr.required_auth:
|
|
46
|
+
api_key_providers.add(p_name)
|
|
47
|
+
else:
|
|
48
|
+
try:
|
|
49
|
+
sig = inspect.signature(attr.__init__).parameters
|
|
50
|
+
if any(k in sig for k in ('api_key', 'cookie_file', 'cookie_path', 'access_token')):
|
|
51
|
+
api_key_providers.add(p_name)
|
|
52
|
+
except (ValueError, TypeError):
|
|
53
|
+
pass
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
return provider_map, api_key_providers
|
|
57
|
+
|
|
58
|
+
def _get_models_safely(provider_cls: type) -> List[str]:
|
|
59
|
+
"""
|
|
60
|
+
Safely retrieves the list of available models from a provider class.
|
|
61
|
+
|
|
62
|
+
This function attempts to find model information through the `AVAILABLE_MODELS`
|
|
63
|
+
attribute or by calling the `get_models` class method. It handles potential
|
|
64
|
+
errors gracefully to ensure the loading process is not interrupted.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
provider_cls (type): The provider class to inspect.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List[str]: A list of unique model names supported by the provider.
|
|
71
|
+
"""
|
|
72
|
+
models = []
|
|
73
|
+
try:
|
|
74
|
+
if hasattr(provider_cls, "AVAILABLE_MODELS"):
|
|
75
|
+
val = getattr(provider_cls, "AVAILABLE_MODELS")
|
|
76
|
+
if isinstance(val, list):
|
|
77
|
+
models.extend(val)
|
|
78
|
+
|
|
79
|
+
if hasattr(provider_cls, "get_models"):
|
|
80
|
+
try:
|
|
81
|
+
# Use getattr to call the class method safely
|
|
82
|
+
get_models_method = getattr(provider_cls, "get_models")
|
|
83
|
+
if callable(get_models_method):
|
|
84
|
+
res = get_models_method()
|
|
85
|
+
if isinstance(res, list):
|
|
86
|
+
models.extend(res)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
return list(set(models))
|
|
92
|
+
|
|
93
|
+
provider_map, api_key_providers = load_providers()
|
|
94
|
+
|
|
95
|
+
class AUTO(Provider):
|
|
96
|
+
"""
|
|
97
|
+
An automatic provider that intelligently selects and utilizes an available
|
|
98
|
+
LLM provider from the webscout library.
|
|
99
|
+
|
|
100
|
+
It cycles through available free providers
|
|
101
|
+
until one successfully processes the request. Excludes providers
|
|
102
|
+
requiring API keys or cookies by default.
|
|
103
|
+
"""
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
model: str = "auto",
|
|
107
|
+
api_key: Optional[str] = None,
|
|
108
|
+
is_conversation: bool = True,
|
|
109
|
+
max_tokens: int = 600,
|
|
110
|
+
timeout: int = 30,
|
|
111
|
+
intro: Optional[str] = None,
|
|
112
|
+
filepath: Optional[str] = None,
|
|
113
|
+
update_file: bool = True,
|
|
114
|
+
proxies: dict = {},
|
|
115
|
+
history_offset: int = 10250,
|
|
116
|
+
act: Optional[str] = None,
|
|
117
|
+
exclude: Optional[List[str]] = None,
|
|
118
|
+
print_provider_info: bool = False,
|
|
119
|
+
**kwargs: Any,
|
|
120
|
+
):
|
|
121
|
+
"""
|
|
122
|
+
Initializes the AUTO provider, setting up the parameters for provider selection and request handling.
|
|
123
|
+
|
|
124
|
+
This constructor initializes the AUTO provider with various configuration options,
|
|
125
|
+
including conversation settings, request limits, and provider exclusions.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
model (str): The model to use. Defaults to "auto".
|
|
129
|
+
api_key (str, optional): API key for providers that require it. Defaults to None.
|
|
130
|
+
is_conversation (bool): Flag for conversational mode. Defaults to True.
|
|
131
|
+
max_tokens (int): Maximum tokens for the response. Defaults to 600.
|
|
132
|
+
timeout (int): Request timeout in seconds. Defaults to 30.
|
|
133
|
+
intro (str, optional): Introductory prompt. Defaults to None.
|
|
134
|
+
filepath (str, optional): Path for conversation history. Defaults to None.
|
|
135
|
+
update_file (bool): Whether to update the history file. Defaults to True.
|
|
136
|
+
proxies (dict): Proxies for requests. Defaults to {}.
|
|
137
|
+
history_offset (int): History character offset limit. Defaults to 10250.
|
|
138
|
+
act (str, optional): Awesome prompt key. Defaults to None.
|
|
139
|
+
exclude (Optional[list[str]]): List of provider names (uppercase) to exclude. Defaults to None.
|
|
140
|
+
print_provider_info (bool): Whether to print the name of the successful provider. Defaults to False.
|
|
141
|
+
**kwargs: Additional keyword arguments for providers.
|
|
142
|
+
"""
|
|
143
|
+
self.provider: Optional[Provider] = None
|
|
144
|
+
self.provider_name: Optional[str] = None
|
|
145
|
+
self.model: str = model
|
|
146
|
+
self.api_key: Optional[str] = api_key
|
|
147
|
+
self.is_conversation: bool = is_conversation
|
|
148
|
+
self.max_tokens: int = max_tokens
|
|
149
|
+
self.timeout: int = timeout
|
|
150
|
+
self.intro: Optional[str] = intro
|
|
151
|
+
self.filepath: Optional[str] = filepath
|
|
152
|
+
self.update_file: bool = update_file
|
|
153
|
+
self.proxies: dict = proxies
|
|
154
|
+
self.history_offset: int = history_offset
|
|
155
|
+
self.act: Optional[str] = act
|
|
156
|
+
self.exclude: list[str] = [e.upper() for e in exclude] if exclude else []
|
|
157
|
+
self.print_provider_info: bool = print_provider_info
|
|
158
|
+
self.kwargs: dict = kwargs
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def last_response(self) -> Dict[str, Any]:
|
|
163
|
+
"""
|
|
164
|
+
Retrieves the last response dictionary from the successfully used provider.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
dict[str, Any]: The last response dictionary, or an empty dictionary if no provider has been used yet.
|
|
168
|
+
"""
|
|
169
|
+
return self.provider.last_response if self.provider else {}
|
|
170
|
+
|
|
171
|
+
@last_response.setter
|
|
172
|
+
def last_response(self, value: Dict[str, Any]):
|
|
173
|
+
if self.provider:
|
|
174
|
+
self.provider.last_response = value
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def conversation(self) -> object:
|
|
178
|
+
"""
|
|
179
|
+
Retrieves the conversation object from the successfully used provider.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
object: The conversation object, or None if no provider has been used yet.
|
|
183
|
+
"""
|
|
184
|
+
return self.provider.conversation if self.provider else None
|
|
185
|
+
|
|
186
|
+
def _fuzzy_resolve_provider_and_model(
|
|
187
|
+
self, model: str
|
|
188
|
+
) -> Optional[Tuple[Type[Provider], str]]:
|
|
189
|
+
"""
|
|
190
|
+
Performs an enhanced search to find the closest provider and model match.
|
|
191
|
+
|
|
192
|
+
The search follows a three-step priority:
|
|
193
|
+
1. Exact case-insensitive match.
|
|
194
|
+
2. Substring match (e.g., 'gpt4' matching 'gpt-4o').
|
|
195
|
+
3. Fuzzy match using difflib for close string similarity.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
model (str): The model name to search for.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Optional[Tuple[Type[Provider], str]]: A tuple containing the provider class
|
|
202
|
+
and the resolved model name, or None if no match is found.
|
|
203
|
+
"""
|
|
204
|
+
available = [
|
|
205
|
+
(name, cls) for name, cls in provider_map.items()
|
|
206
|
+
if name not in self.exclude
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
if not self.api_key:
|
|
210
|
+
available = [p for p in available if p[0] not in api_key_providers]
|
|
211
|
+
|
|
212
|
+
model_to_provider = {}
|
|
213
|
+
for p_name, p_cls in available:
|
|
214
|
+
p_models = _get_models_safely(p_cls)
|
|
215
|
+
for m in p_models:
|
|
216
|
+
if m not in model_to_provider:
|
|
217
|
+
model_to_provider[m] = p_cls
|
|
218
|
+
|
|
219
|
+
if not model_to_provider:
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
for m_name in model_to_provider:
|
|
223
|
+
if m_name.lower() == model.lower():
|
|
224
|
+
return model_to_provider[m_name], m_name
|
|
225
|
+
|
|
226
|
+
for m_name in model_to_provider:
|
|
227
|
+
if model.lower() in m_name.lower() or m_name.lower() in model.lower():
|
|
228
|
+
if self.print_provider_info:
|
|
229
|
+
print(f"\033[1;33mSubstring match: '{model}' -> '{m_name}'\033[0m")
|
|
230
|
+
return model_to_provider[m_name], m_name
|
|
231
|
+
|
|
232
|
+
matches = difflib.get_close_matches(model, model_to_provider.keys(), n=1, cutoff=0.5)
|
|
233
|
+
if matches:
|
|
234
|
+
matched_model = matches[0]
|
|
235
|
+
if self.print_provider_info:
|
|
236
|
+
print(f"\033[1;33mFuzzy match: '{model}' -> '{matched_model}'\033[0m")
|
|
237
|
+
return model_to_provider[matched_model], matched_model
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
def _resolve_provider_and_model(
|
|
241
|
+
self, model: str
|
|
242
|
+
) -> Tuple[Optional[Type[Provider]], str]:
|
|
243
|
+
"""
|
|
244
|
+
Resolves the appropriate provider and model name based on the input string.
|
|
245
|
+
|
|
246
|
+
The resolution logic handles:
|
|
247
|
+
- 'Provider/Model' format for direct targeting.
|
|
248
|
+
- 'auto' for automatic random selection.
|
|
249
|
+
- Exact model name matches across all available providers.
|
|
250
|
+
- Fuzzy resolution as a fallback.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
model (str): The model specification string.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Tuple[Optional[Type[Provider]], str]: A tuple of (ProviderClass, ResolvedModelName).
|
|
257
|
+
"""
|
|
258
|
+
if "/" in model:
|
|
259
|
+
p_name, m_name = model.split("/", 1)
|
|
260
|
+
found_p = next(
|
|
261
|
+
(cls for name, cls in provider_map.items() if name.lower() == p_name.lower()),
|
|
262
|
+
None,
|
|
263
|
+
)
|
|
264
|
+
if found_p:
|
|
265
|
+
return found_p, m_name
|
|
266
|
+
|
|
267
|
+
if model == "auto":
|
|
268
|
+
return None, "auto"
|
|
269
|
+
|
|
270
|
+
available = [
|
|
271
|
+
(name, cls) for name, cls in provider_map.items()
|
|
272
|
+
if name not in self.exclude
|
|
273
|
+
]
|
|
274
|
+
|
|
275
|
+
if not self.api_key:
|
|
276
|
+
available = [p for p in available if p[0] not in api_key_providers]
|
|
277
|
+
|
|
278
|
+
for p_name, p_cls in available:
|
|
279
|
+
p_models = _get_models_safely(p_cls)
|
|
280
|
+
if model in p_models:
|
|
281
|
+
return p_cls, model
|
|
282
|
+
|
|
283
|
+
fuzzy_result = self._fuzzy_resolve_provider_and_model(model)
|
|
284
|
+
if fuzzy_result:
|
|
285
|
+
return fuzzy_result
|
|
286
|
+
|
|
287
|
+
return None, model
|
|
288
|
+
|
|
289
|
+
def ask(
|
|
290
|
+
self,
|
|
291
|
+
prompt: str,
|
|
292
|
+
stream: bool = False,
|
|
293
|
+
raw: bool = False,
|
|
294
|
+
optimizer: Optional[str] = None,
|
|
295
|
+
conversationally: bool = False,
|
|
296
|
+
**kwargs: Any,
|
|
297
|
+
) -> Response:
|
|
298
|
+
"""
|
|
299
|
+
Sends the prompt to available providers, attempting to get a response from each until one succeeds.
|
|
300
|
+
|
|
301
|
+
This method iterates through a prioritized list of available providers based on the requested model
|
|
302
|
+
and attempts to send the prompt to each provider until a successful response is received.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
prompt (str): The user's prompt.
|
|
306
|
+
stream (bool): Whether to stream the response. Defaults to False.
|
|
307
|
+
raw (bool): Whether to return the raw response format. Defaults to False.
|
|
308
|
+
optimizer (str, optional): Name of the optimizer to use. Defaults to None.
|
|
309
|
+
conversationally (bool): Whether to apply optimizer conversationally. Defaults to False.
|
|
310
|
+
**kwargs: Additional keyword arguments for the provider's ask method.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Union[Dict, Generator]: The response dictionary or generator from the successful provider.
|
|
314
|
+
"""
|
|
315
|
+
ask_kwargs = {
|
|
316
|
+
"prompt": prompt,
|
|
317
|
+
"stream": stream,
|
|
318
|
+
"raw": raw,
|
|
319
|
+
"optimizer": optimizer,
|
|
320
|
+
"conversationally": conversationally,
|
|
321
|
+
}
|
|
322
|
+
ask_kwargs.update(kwargs)
|
|
323
|
+
|
|
324
|
+
resolved_provider, resolved_model = self._resolve_provider_and_model(self.model)
|
|
325
|
+
|
|
326
|
+
queue = []
|
|
327
|
+
if resolved_provider:
|
|
328
|
+
queue.append((resolved_provider.__name__.upper(), resolved_provider, resolved_model))
|
|
329
|
+
|
|
330
|
+
all_available = [
|
|
331
|
+
(name, cls) for name, cls in provider_map.items()
|
|
332
|
+
if name not in self.exclude and (resolved_provider is None or cls != resolved_provider)
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
if not self.api_key:
|
|
336
|
+
all_available = [p for p in all_available if p[0] not in api_key_providers]
|
|
337
|
+
|
|
338
|
+
random.shuffle(all_available)
|
|
339
|
+
|
|
340
|
+
model_prio = []
|
|
341
|
+
others = []
|
|
342
|
+
|
|
343
|
+
for name, cls in all_available:
|
|
344
|
+
p_models = _get_models_safely(cls)
|
|
345
|
+
if resolved_model != "auto" and resolved_model in p_models:
|
|
346
|
+
model_prio.append((name, cls, resolved_model))
|
|
347
|
+
else:
|
|
348
|
+
m = resolved_model
|
|
349
|
+
if resolved_model != "auto" and p_models:
|
|
350
|
+
m = random.choice(p_models)
|
|
351
|
+
elif resolved_model == "auto" and p_models:
|
|
352
|
+
m = random.choice(p_models)
|
|
353
|
+
queue_model = m if m else "auto"
|
|
354
|
+
others.append((name, cls, queue_model))
|
|
355
|
+
|
|
356
|
+
queue.extend(model_prio)
|
|
357
|
+
queue.extend(others)
|
|
358
|
+
|
|
359
|
+
for provider_name, provider_class, model_to_use in queue:
|
|
360
|
+
try:
|
|
361
|
+
sig = inspect.signature(provider_class.__init__).parameters
|
|
362
|
+
init_kwargs = {
|
|
363
|
+
"is_conversation": self.is_conversation,
|
|
364
|
+
"max_tokens": self.max_tokens,
|
|
365
|
+
"timeout": self.timeout,
|
|
366
|
+
"intro": self.intro,
|
|
367
|
+
"filepath": self.filepath,
|
|
368
|
+
"update_file": self.update_file,
|
|
369
|
+
"proxies": self.proxies,
|
|
370
|
+
"history_offset": self.history_offset,
|
|
371
|
+
"act": self.act,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if 'model' in sig:
|
|
375
|
+
init_kwargs['model'] = model_to_use
|
|
376
|
+
if 'api_key' in sig and self.api_key:
|
|
377
|
+
init_kwargs['api_key'] = self.api_key
|
|
378
|
+
|
|
379
|
+
for k, v in self.kwargs.items():
|
|
380
|
+
if k in sig or any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.values()):
|
|
381
|
+
init_kwargs[k] = v
|
|
382
|
+
|
|
383
|
+
if not any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.values()):
|
|
384
|
+
init_kwargs = {k: v for k, v in init_kwargs.items() if k in sig}
|
|
385
|
+
|
|
386
|
+
provider_instance = provider_class(**init_kwargs)
|
|
387
|
+
self.provider = provider_instance
|
|
388
|
+
self.provider_name = provider_name
|
|
389
|
+
response = provider_instance.ask(**ask_kwargs)
|
|
390
|
+
|
|
391
|
+
if stream and inspect.isgenerator(response):
|
|
392
|
+
try:
|
|
393
|
+
first_chunk = next(response)
|
|
394
|
+
except StopIteration:
|
|
395
|
+
continue
|
|
396
|
+
except Exception:
|
|
397
|
+
continue
|
|
398
|
+
|
|
399
|
+
def chained_gen() -> Any:
|
|
400
|
+
if self.print_provider_info:
|
|
401
|
+
model = getattr(self.provider, "model", None)
|
|
402
|
+
provider_class_name = self.provider.__class__.__name__
|
|
403
|
+
if model:
|
|
404
|
+
print(f"\033[1;34m{provider_class_name}:{model}\033[0m\n")
|
|
405
|
+
else:
|
|
406
|
+
print(f"\033[1;34m{provider_class_name}\033[0m\n")
|
|
407
|
+
yield first_chunk
|
|
408
|
+
yield from response
|
|
409
|
+
return chained_gen()
|
|
410
|
+
|
|
411
|
+
if not stream and inspect.isgenerator(response):
|
|
412
|
+
try:
|
|
413
|
+
while True:
|
|
414
|
+
next(response)
|
|
415
|
+
except StopIteration as e:
|
|
416
|
+
response = e.value
|
|
417
|
+
except Exception:
|
|
418
|
+
continue
|
|
419
|
+
|
|
420
|
+
if self.print_provider_info:
|
|
421
|
+
model = getattr(self.provider, "model", None)
|
|
422
|
+
provider_class_name = self.provider.__class__.__name__
|
|
423
|
+
if model:
|
|
424
|
+
print(f"\033[1;34m{provider_class_name}:{model}\033[0m\n")
|
|
425
|
+
else:
|
|
426
|
+
print(f"\033[1;34m{provider_class_name}\033[0m\n")
|
|
427
|
+
return response
|
|
428
|
+
except Exception:
|
|
429
|
+
continue
|
|
430
|
+
|
|
431
|
+
raise AllProvidersFailure("All providers failed to process the request")
|
|
432
|
+
|
|
433
|
+
def chat(
|
|
434
|
+
self,
|
|
435
|
+
prompt: str,
|
|
436
|
+
stream: bool = False,
|
|
437
|
+
optimizer: Optional[str] = None,
|
|
438
|
+
conversationally: bool = False,
|
|
439
|
+
**kwargs: Any,
|
|
440
|
+
) -> Union[str, Generator[str, None, None]]:
|
|
441
|
+
"""
|
|
442
|
+
Provides a simplified chat interface, returning the message string or a generator of message strings.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
prompt (str): The user's prompt.
|
|
446
|
+
stream (bool): Whether to stream the response. Defaults to False.
|
|
447
|
+
optimizer (str, optional): Name of the optimizer to use. Defaults to None.
|
|
448
|
+
conversationally (bool): Whether to apply optimizer conversationally. Defaults to False.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
Union[str, Generator[str, None, None]]: The response string or a generator yielding
|
|
452
|
+
response chunks.
|
|
453
|
+
"""
|
|
454
|
+
if stream:
|
|
455
|
+
return self._chat_stream(prompt, optimizer, conversationally)
|
|
456
|
+
else:
|
|
457
|
+
return self._chat_non_stream(prompt, optimizer, conversationally)
|
|
458
|
+
|
|
459
|
+
def _chat_stream(self, prompt: str, optimizer: Optional[str], conversationally: bool) -> Generator[str, None, None]:
|
|
460
|
+
"""
|
|
461
|
+
Internal helper for streaming chat responses.
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
prompt (str): The user's prompt.
|
|
465
|
+
optimizer (Optional[str]): Name of the optimizer.
|
|
466
|
+
conversationally (bool): Whether to apply optimizer conversationally.
|
|
467
|
+
|
|
468
|
+
Yields:
|
|
469
|
+
str: Message chunks extracted from the provider's stream.
|
|
470
|
+
"""
|
|
471
|
+
response = self.ask(
|
|
472
|
+
prompt,
|
|
473
|
+
stream=True,
|
|
474
|
+
optimizer=optimizer,
|
|
475
|
+
conversationally=conversationally,
|
|
476
|
+
)
|
|
477
|
+
if hasattr(response, "__iter__") and not isinstance(response, (str, bytes, dict)):
|
|
478
|
+
for chunk in response:
|
|
479
|
+
yield self.get_message(chunk)
|
|
480
|
+
elif isinstance(response, dict):
|
|
481
|
+
yield self.get_message(response)
|
|
482
|
+
|
|
483
|
+
def _chat_non_stream(self, prompt: str, optimizer: Optional[str], conversationally: bool) -> str:
|
|
484
|
+
"""
|
|
485
|
+
Internal helper for non-streaming chat responses.
|
|
486
|
+
|
|
487
|
+
Args:
|
|
488
|
+
prompt (str): The user's prompt.
|
|
489
|
+
optimizer (Optional[str]): Name of the optimizer.
|
|
490
|
+
conversationally (bool): Whether to apply optimizer conversationally.
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
str: The full message text extracted from the provider's response.
|
|
494
|
+
"""
|
|
495
|
+
response = self.ask(
|
|
496
|
+
prompt,
|
|
497
|
+
stream=False,
|
|
498
|
+
optimizer=optimizer,
|
|
499
|
+
conversationally=conversationally,
|
|
500
|
+
)
|
|
501
|
+
if isinstance(response, dict):
|
|
502
|
+
return self.get_message(response)
|
|
503
|
+
return str(response)
|
|
504
|
+
|
|
505
|
+
def get_message(self, response: Response) -> str:
|
|
506
|
+
"""
|
|
507
|
+
Extracts the message text from the provider's response dictionary.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
response (Response): The response obtained from the `ask` method.
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
str: The extracted message string.
|
|
514
|
+
"""
|
|
515
|
+
assert self.provider is not None, "Chat with AI first"
|
|
516
|
+
if not isinstance(response, dict):
|
|
517
|
+
return str(response)
|
|
518
|
+
return self.provider.get_message(response)
|
|
519
|
+
|
|
520
|
+
if __name__ == "__main__":
|
|
521
|
+
auto = AUTO(print_provider_info=True)
|
|
522
|
+
response = auto.chat("Hello, how are you?", stream=True)
|
|
523
|
+
for chunk in response:
|
|
524
|
+
print(chunk, end="", flush=True)
|