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
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
from curl_cffi.requests import Session
|
|
2
|
-
import uuid
|
|
3
|
-
import re
|
|
4
|
-
from typing import Any, Dict, Optional, Union
|
|
5
|
-
from webscout.AIutel import Optimizers
|
|
6
|
-
from webscout.AIutel import Conversation
|
|
7
|
-
from webscout.AIutel import AwesomePrompts, sanitize_stream # Import sanitize_stream
|
|
8
|
-
from webscout.AIbase import Provider
|
|
9
|
-
from webscout import exceptions
|
|
10
|
-
from webscout.litagent import LitAgent
|
|
11
|
-
|
|
12
|
-
class StandardInputAI(Provider):
|
|
13
|
-
"""
|
|
14
|
-
A class to interact with the Standard Input chat API.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
AVAILABLE_MODELS = {
|
|
18
|
-
"standard-quick": "quick",
|
|
19
|
-
"standard-reasoning": "quick", # Same model but with reasoning enabled
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
is_conversation: bool = True,
|
|
25
|
-
max_tokens: int = 2049,
|
|
26
|
-
timeout: int = 30,
|
|
27
|
-
intro: str = None,
|
|
28
|
-
filepath: str = None,
|
|
29
|
-
update_file: bool = True,
|
|
30
|
-
proxies: dict = {},
|
|
31
|
-
history_offset: int = 10250,
|
|
32
|
-
act: str = None,
|
|
33
|
-
model: str = "standard-quick",
|
|
34
|
-
chat_id: str = None,
|
|
35
|
-
user_id: str = None,
|
|
36
|
-
browser: str = "chrome",
|
|
37
|
-
system_prompt: str = "You are a helpful assistant.",
|
|
38
|
-
enable_reasoning: bool = False,
|
|
39
|
-
):
|
|
40
|
-
"""
|
|
41
|
-
Initializes the Standard Input API client.
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
is_conversation (bool): Whether to maintain conversation history.
|
|
45
|
-
max_tokens (int): Maximum number of tokens to generate.
|
|
46
|
-
timeout (int): Request timeout in seconds.
|
|
47
|
-
intro (str): Introduction text for the conversation.
|
|
48
|
-
filepath (str): Path to save conversation history.
|
|
49
|
-
update_file (bool): Whether to update the conversation history file.
|
|
50
|
-
proxies (dict): Proxy configuration for requests.
|
|
51
|
-
history_offset (int): Maximum history length in characters.
|
|
52
|
-
act (str): Persona for the AI to adopt.
|
|
53
|
-
model (str): Model to use, must be one of AVAILABLE_MODELS.
|
|
54
|
-
chat_id (str): Unique identifier for the chat session.
|
|
55
|
-
user_id (str): Unique identifier for the user.
|
|
56
|
-
browser (str): Browser to emulate in requests.
|
|
57
|
-
system_prompt (str): System prompt for the AI.
|
|
58
|
-
enable_reasoning (bool): Whether to enable reasoning feature.
|
|
59
|
-
"""
|
|
60
|
-
if model not in self.AVAILABLE_MODELS:
|
|
61
|
-
raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
|
|
62
|
-
|
|
63
|
-
self.url = "https://chat.standard-input.com/api/chat"
|
|
64
|
-
|
|
65
|
-
# Initialize LitAgent for user agent generation
|
|
66
|
-
self.agent = LitAgent()
|
|
67
|
-
# Use fingerprinting to create a consistent browser identity
|
|
68
|
-
self.fingerprint = self.agent.generate_fingerprint(browser)
|
|
69
|
-
self.system_prompt = system_prompt
|
|
70
|
-
|
|
71
|
-
# Use the fingerprint for headers
|
|
72
|
-
self.headers = {
|
|
73
|
-
"accept": "*/*",
|
|
74
|
-
"accept-encoding": "gzip, deflate, br, zstd",
|
|
75
|
-
"accept-language": self.fingerprint["accept_language"],
|
|
76
|
-
"content-type": "application/json",
|
|
77
|
-
"dnt": "1",
|
|
78
|
-
"origin": "https://chat.standard-input.com",
|
|
79
|
-
"referer": "https://chat.standard-input.com/",
|
|
80
|
-
"sec-ch-ua": self.fingerprint["sec_ch_ua"] or '"Microsoft Edge";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
|
|
81
|
-
"sec-ch-ua-mobile": "?0",
|
|
82
|
-
"sec-ch-ua-platform": f'"{self.fingerprint["platform"]}"',
|
|
83
|
-
"sec-fetch-dest": "empty",
|
|
84
|
-
"sec-fetch-mode": "cors",
|
|
85
|
-
"sec-fetch-site": "same-origin",
|
|
86
|
-
"sec-gpc": "1",
|
|
87
|
-
"user-agent": self.fingerprint["user_agent"],
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
# Default cookies - these should be updated for production use
|
|
91
|
-
self.cookies = {
|
|
92
|
-
"auth-chat": '''%7B%22user%22%3A%7B%22id%22%3A%2243a26ebd-7691-4a5a-8321-12aff017af86%22%2C%22email%22%3A%22iu511inmev%40illubd.com%22%2C%22accountId%22%3A%22057d78c9-06db-48eb-aeaa-0efdbaeb9446%22%2C%22provider%22%3A%22password%22%7D%2C%22tokens%22%3A%7B%22access%22%3A%22eyJhbGciOiJFUzI1NiIsImtpZCI6Ijg1NDhmZWY1LTk5MjYtNDk2Yi1hMjI2LTQ5OTExYjllYzU2NSIsInR5cCI6IkpXVCJ9.eyJtb2RlIjoiYWNjZXNzIiwidHlwZSI6InVzZXIiLCJwcm9wZXJ0aWVzIjp7ImlkIjoiNDNhMjZlYmQtNzY5MS00YTVhLTgzMzEtMTJhZmYwMTdhZjg2IiwiZW1haWwiOiJpdTUxMWlubWV2QGlsbHViZC5jb20iLCJhY2NvdW50SWQiOiIwNTdkNzhjOS0wNmRiLTQ4ZWItYWVhYS0wZWZkYmFlYjk0NDYiLCJwcm92aWRlciI6InBhc3N3b3JkIn0sImF1ZCI6InN0YW5kYXJkLWlucHV0LWlvcyIsImlzcyI6Imh0dHBzOi8vYXV0aC5zdGFuZGFyZC1pbnB1dC5jb20iLCJzdWIiOiJ1c2VyOjRmYWMzMTllZjA4MDRiZmMiLCJleHAiOjE3NDU0MDU5MDN9.d3VsEq-UCNsQWkiPlTVw7caS0wTXfCYe6yeFLeb4Ce6ZYTIFFn685SF-aKvLOxaYaq7Pyk4D2qr24riPVhxUWQ%22%2C%22refresh%22%3A%22user%3A4fac319ef0804bfc%3A3a757177-5507-4a36-9356-492f5ed06105%22%7D%7D''',
|
|
93
|
-
"auth": '''%7B%22user%22%3A%7B%22id%22%3A%22c51e291f-8f44-439d-a38b-9ea147581a13%22%2C%22email%22%3A%22r6cigexlsb%40mrotzis.com%22%2C%22accountId%22%3A%22599fd4ce-04a2-40f6-a78f-d33d0059b77f%22%2C%22provider%22%3A%22password%22%7D%2C%22tokens%22%3A%7B%22access%22%3A%22eyJhbGciOiJFUzI1NiIsImtpZCI6Ijg1NDhmZWY1LTk5MjYtNDk2Yi1hMjI2LTQ5OTExYjllYzU2NSIsInR5cCI6IkpXVCJ9.eyJtb2RlIjoiYWNjZXNzIiwidHlwZSI6InVzZXIiLCJwcm9wZXJ0aWVzIjp7ImlkIjoiYzUxZTI5MWYtOGY0NC00MzlkLWEzOGItOWVhMTQ3NTgxYTEzIiwiZW1haWwiOiJyNmNpZ2V4bHNiQG1yb3R6aXMuY29tIiwiYWNjb3VudElkIjoiNTk5ZmQ0Y2UtMDRhMi00MGY2LWE3OGYtZDMzZDAwNTliNzdmIiwicHJvdmlkZXIiOiJwYXNzd29yZCJ9LCJhdWQiOiJzdGFuZGFyZC1pbnB1dC1pb3MiLCJpc3MiOiJodHRwczovL2F1dGguc3RhbmRhcmQtaW5wdXQuY29tIiwic3ViIjoidXNlcjo4Y2FmMjRkYzUxNDc4MmNkIiwiZXhwIjoxNzQ2NzI0MTU3fQ.a3970nBJkd8JoU-khRA2JlRMuYeJ7378QS4ZL446kOkDi35uTwuC4qGrWH9efk9GkFaVcWPtYeOJjRb7f2SeJA%22%2C%22refresh%22%3A%22user%3A8caf24dc514782cd%3A14e24386-8443-4df0-ae25-234ad59218ef%22%7D%7D''',
|
|
94
|
-
"sidebar:state": "true",
|
|
95
|
-
"ph_phc_f3wUUyCfmKlKtkc2pfT7OsdcW2mBEVGN2A87yEYbG3c_posthog": '''%7B%22distinct_id%22%3A%220195c7cc-ac8f-79ff-b901-e14a78fc2a67%22%2C%22%24sesid%22%3A%5B1744688627860%2C%220196377f-9f12-77e6-a9ea-0e9669423803%22%2C1744687832850%5D%2C%22%24initial_person_info%22%3A%7B%22r%22%3A%22%24direct%22%2C%22u%22%3A%22https%3A%2F%2Fstandard-input.com%2F%22%7D%7D'''
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
self.session = Session() # Use curl_cffi Session
|
|
99
|
-
self.session.headers.update(self.headers)
|
|
100
|
-
self.session.proxies = proxies # Assign proxies directly
|
|
101
|
-
|
|
102
|
-
self.is_conversation = is_conversation
|
|
103
|
-
self.max_tokens_to_sample = max_tokens
|
|
104
|
-
self.timeout = timeout
|
|
105
|
-
self.last_response = {}
|
|
106
|
-
self.model = model
|
|
107
|
-
self.chat_id = chat_id or str(uuid.uuid4())
|
|
108
|
-
self.user_id = user_id or f"user_{str(uuid.uuid4())[:8].upper()}"
|
|
109
|
-
self.enable_reasoning = enable_reasoning
|
|
110
|
-
|
|
111
|
-
self.__available_optimizers = (
|
|
112
|
-
method
|
|
113
|
-
for method in dir(Optimizers)
|
|
114
|
-
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
115
|
-
)
|
|
116
|
-
Conversation.intro = (
|
|
117
|
-
AwesomePrompts().get_act(
|
|
118
|
-
act, raise_not_found=True, default=None, case_insensitive=True
|
|
119
|
-
)
|
|
120
|
-
if act
|
|
121
|
-
else intro or Conversation.intro
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
self.conversation = Conversation(
|
|
125
|
-
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
126
|
-
)
|
|
127
|
-
self.conversation.history_offset = history_offset
|
|
128
|
-
|
|
129
|
-
def refresh_identity(self, browser: str = None):
|
|
130
|
-
"""
|
|
131
|
-
Refreshes the browser identity fingerprint.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
browser: Specific browser to use for the new fingerprint
|
|
135
|
-
"""
|
|
136
|
-
browser = browser or self.fingerprint.get("browser_type", "chrome")
|
|
137
|
-
self.fingerprint = self.agent.generate_fingerprint(browser)
|
|
138
|
-
|
|
139
|
-
# Update headers with new fingerprint
|
|
140
|
-
self.headers.update({
|
|
141
|
-
"Accept-Language": self.fingerprint["accept_language"],
|
|
142
|
-
"Sec-CH-UA": self.fingerprint["sec_ch_ua"] or self.headers["sec-ch-ua"],
|
|
143
|
-
"Sec-CH-UA-Platform": f'"{self.fingerprint["platform"]}"',
|
|
144
|
-
"User-Agent": self.fingerprint["user_agent"],
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
# Update session headers
|
|
148
|
-
for header, value in self.headers.items():
|
|
149
|
-
self.session.headers[header] = value
|
|
150
|
-
|
|
151
|
-
return self.fingerprint
|
|
152
|
-
|
|
153
|
-
@staticmethod
|
|
154
|
-
def _standardinput_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
155
|
-
"""Extracts content from the StandardInput stream format '0:"..."'."""
|
|
156
|
-
if isinstance(chunk, str):
|
|
157
|
-
match = re.search(r'0:"(.*?)"(?=,|$)', chunk) # Look for 0:"...", possibly followed by comma or end of string
|
|
158
|
-
if match:
|
|
159
|
-
# Decode potential unicode escapes like \u00e9 and handle escaped quotes/backslashes
|
|
160
|
-
content = match.group(1).encode().decode('unicode_escape')
|
|
161
|
-
return content.replace('\\\\', '\\').replace('\\"', '"')
|
|
162
|
-
return None
|
|
163
|
-
|
|
164
|
-
def ask(
|
|
165
|
-
self,
|
|
166
|
-
prompt: str,
|
|
167
|
-
optimizer: str = None,
|
|
168
|
-
conversationally: bool = False,
|
|
169
|
-
) -> Dict[str, Any]:
|
|
170
|
-
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
171
|
-
if optimizer:
|
|
172
|
-
if optimizer in self.__available_optimizers:
|
|
173
|
-
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
174
|
-
conversation_prompt if conversationally else prompt
|
|
175
|
-
)
|
|
176
|
-
else:
|
|
177
|
-
raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
|
|
178
|
-
|
|
179
|
-
# Prepare the messages
|
|
180
|
-
messages = [
|
|
181
|
-
{"role": "system", "content": self.system_prompt},
|
|
182
|
-
{"role": "user", "content": conversation_prompt, "parts": [{"type": "text", "text": conversation_prompt}]}
|
|
183
|
-
]
|
|
184
|
-
|
|
185
|
-
# Prepare the request payload
|
|
186
|
-
payload = {
|
|
187
|
-
"id": self.chat_id,
|
|
188
|
-
"messages": messages,
|
|
189
|
-
"modelId": self.AVAILABLE_MODELS[self.model],
|
|
190
|
-
"enabledFeatures": ["reasoning"] if self.enable_reasoning or self.model == "standard-reasoning" else []
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
# Use curl_cffi post with impersonate
|
|
195
|
-
response = self.session.post(
|
|
196
|
-
self.url,
|
|
197
|
-
cookies=self.cookies,
|
|
198
|
-
json=payload,
|
|
199
|
-
stream=True,
|
|
200
|
-
timeout=self.timeout,
|
|
201
|
-
impersonate="chrome120" # Add impersonate
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
if response.status_code != 200:
|
|
205
|
-
try:
|
|
206
|
-
error_content = response.text
|
|
207
|
-
except:
|
|
208
|
-
error_content = "<could not read response content>"
|
|
209
|
-
|
|
210
|
-
if response.status_code in [403, 429]:
|
|
211
|
-
self.refresh_identity()
|
|
212
|
-
response = self.session.post(
|
|
213
|
-
self.url, cookies=self.cookies, json=payload, stream=True,
|
|
214
|
-
timeout=self.timeout, impersonate="chrome120"
|
|
215
|
-
)
|
|
216
|
-
if not response.ok:
|
|
217
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
218
|
-
f"Failed to generate response after identity refresh - ({response.status_code}, {response.reason}) - {error_content}"
|
|
219
|
-
)
|
|
220
|
-
else:
|
|
221
|
-
raise exceptions.FailedToGenerateResponseError(
|
|
222
|
-
f"Request failed with status code {response.status_code}. Response: {error_content}"
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
full_response = ""
|
|
226
|
-
# Use sanitize_stream
|
|
227
|
-
processed_stream = sanitize_stream(
|
|
228
|
-
data=response.iter_content(chunk_size=None), # Pass byte iterator
|
|
229
|
-
intro_value=None, # No simple prefix
|
|
230
|
-
to_json=False, # Content is not JSON
|
|
231
|
-
content_extractor=self._standardinput_extractor # Use the specific extractor
|
|
232
|
-
)
|
|
233
|
-
for content_chunk in processed_stream:
|
|
234
|
-
if content_chunk and isinstance(content_chunk, str):
|
|
235
|
-
full_response += content_chunk
|
|
236
|
-
|
|
237
|
-
self.last_response = {"text": full_response}
|
|
238
|
-
self.conversation.update_chat_history(prompt, full_response)
|
|
239
|
-
return {"text": full_response}
|
|
240
|
-
except Exception as e:
|
|
241
|
-
raise exceptions.FailedToGenerateResponseError(f"Request failed: {e}")
|
|
242
|
-
|
|
243
|
-
def chat(
|
|
244
|
-
self,
|
|
245
|
-
prompt: str,
|
|
246
|
-
optimizer: str = None,
|
|
247
|
-
conversationally: bool = False,
|
|
248
|
-
) -> str:
|
|
249
|
-
return self.get_message(
|
|
250
|
-
self.ask(
|
|
251
|
-
prompt, optimizer=optimizer, conversationally=conversationally
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
def get_message(self, response: dict) -> str:
|
|
256
|
-
assert isinstance(response, dict), "Response should be of dict data-type only"
|
|
257
|
-
# Extractor handles formatting
|
|
258
|
-
return response.get("text", "").replace('\\n', '\n').replace('\\n\\n', '\n\n')
|
|
259
|
-
|
|
260
|
-
if __name__ == "__main__":
|
|
261
|
-
print("-" * 100)
|
|
262
|
-
print(f"{'Model':<50} {'Status':<10} {'Response'}")
|
|
263
|
-
print("-" * 100)
|
|
264
|
-
|
|
265
|
-
test_prompt = "Say 'Hello' in one word"
|
|
266
|
-
|
|
267
|
-
# Test each model
|
|
268
|
-
for model in StandardInputAI.AVAILABLE_MODELS:
|
|
269
|
-
print(f"\rTesting {model}...", end="")
|
|
270
|
-
|
|
271
|
-
try:
|
|
272
|
-
test_ai = StandardInputAI(model=model, timeout=120) # Increased timeout
|
|
273
|
-
response = test_ai.chat(test_prompt)
|
|
274
|
-
|
|
275
|
-
if response and len(response.strip()) > 0:
|
|
276
|
-
status = "✓"
|
|
277
|
-
# Clean and truncate response
|
|
278
|
-
clean_text = response.strip().encode('utf-8', errors='ignore').decode('utf-8')
|
|
279
|
-
display_text = clean_text[:50] + "..." if len(clean_text) > 50 else clean_text
|
|
280
|
-
else:
|
|
281
|
-
status = "✗"
|
|
282
|
-
display_text = "Empty or invalid response"
|
|
283
|
-
|
|
284
|
-
print(f"\r{model:<50} {status:<10} {display_text}")
|
|
285
|
-
except Exception as e:
|
|
286
|
-
error_msg = str(e)
|
|
287
|
-
# Truncate very long error messages
|
|
288
|
-
if len(error_msg) > 100:
|
|
289
|
-
error_msg = error_msg[:97] + "..."
|
|
290
|
-
print(f"\r{model:<50} {'✗':<10} Error: {error_msg}")
|
webscout/Provider/TTI/aiarta.py
DELETED
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
"""AIArtaImager TTI-Compatible Provider - Generate stunning AI art with AI Arta! 🎨
|
|
2
|
-
|
|
3
|
-
Examples:
|
|
4
|
-
>>> from webscout.Provider.TTI.aiarta import AIArta
|
|
5
|
-
>>> client = AIArta()
|
|
6
|
-
>>> response = client.images.create(
|
|
7
|
-
... model="flux",
|
|
8
|
-
... prompt="A cool cyberpunk city at night",
|
|
9
|
-
... n=1
|
|
10
|
-
... )
|
|
11
|
-
>>> print(response)
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import requests
|
|
15
|
-
from typing import Optional, List, Dict, Any
|
|
16
|
-
from webscout.Provider.TTI.utils import ImageData, ImageResponse
|
|
17
|
-
from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
|
|
18
|
-
from io import BytesIO
|
|
19
|
-
import os
|
|
20
|
-
import tempfile
|
|
21
|
-
from webscout.litagent import LitAgent
|
|
22
|
-
import time
|
|
23
|
-
import json
|
|
24
|
-
|
|
25
|
-
try:
|
|
26
|
-
from PIL import Image
|
|
27
|
-
except ImportError:
|
|
28
|
-
Image = None
|
|
29
|
-
|
|
30
|
-
class Images(BaseImages):
|
|
31
|
-
def __init__(self, client: 'AIArta'):
|
|
32
|
-
self._client = client
|
|
33
|
-
|
|
34
|
-
def create(
|
|
35
|
-
self,
|
|
36
|
-
*,
|
|
37
|
-
model: str,
|
|
38
|
-
prompt: str,
|
|
39
|
-
n: int = 1,
|
|
40
|
-
size: str = "1024x1024",
|
|
41
|
-
response_format: str = "url",
|
|
42
|
-
user: Optional[str] = None,
|
|
43
|
-
style: str = "none",
|
|
44
|
-
aspect_ratio: str = "1:1",
|
|
45
|
-
timeout: int = 60,
|
|
46
|
-
image_format: str = "png",
|
|
47
|
-
**kwargs
|
|
48
|
-
) -> ImageResponse:
|
|
49
|
-
"""
|
|
50
|
-
image_format: "png" or "jpeg"
|
|
51
|
-
"""
|
|
52
|
-
if Image is None:
|
|
53
|
-
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
54
|
-
|
|
55
|
-
images = []
|
|
56
|
-
urls = []
|
|
57
|
-
agent = LitAgent()
|
|
58
|
-
|
|
59
|
-
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
60
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
61
|
-
for attempt in range(max_retries):
|
|
62
|
-
tmp_path = None
|
|
63
|
-
try:
|
|
64
|
-
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
65
|
-
tmp.write(img_bytes)
|
|
66
|
-
tmp.flush()
|
|
67
|
-
tmp_path = tmp.name
|
|
68
|
-
with open(tmp_path, 'rb') as f:
|
|
69
|
-
files = {
|
|
70
|
-
'fileToUpload': (f'image.{ext}', f, f'image/{ext}')
|
|
71
|
-
}
|
|
72
|
-
data = {
|
|
73
|
-
'reqtype': 'fileupload',
|
|
74
|
-
'json': 'true'
|
|
75
|
-
}
|
|
76
|
-
headers = {'User-Agent': agent.random()}
|
|
77
|
-
if attempt > 0:
|
|
78
|
-
headers['Connection'] = 'close'
|
|
79
|
-
resp = requests.post("https://catbox.moe/user/api.php", files=files, data=data, headers=headers, timeout=timeout)
|
|
80
|
-
if resp.status_code == 200 and resp.text.strip():
|
|
81
|
-
text = resp.text.strip()
|
|
82
|
-
if text.startswith('http'):
|
|
83
|
-
return text
|
|
84
|
-
try:
|
|
85
|
-
result = resp.json()
|
|
86
|
-
if "url" in result:
|
|
87
|
-
return result["url"]
|
|
88
|
-
except json.JSONDecodeError:
|
|
89
|
-
if 'http' in text:
|
|
90
|
-
return text
|
|
91
|
-
except Exception:
|
|
92
|
-
if attempt < max_retries - 1:
|
|
93
|
-
time.sleep(1 * (attempt + 1))
|
|
94
|
-
finally:
|
|
95
|
-
if tmp_path and os.path.isfile(tmp_path):
|
|
96
|
-
try:
|
|
97
|
-
os.remove(tmp_path)
|
|
98
|
-
except Exception:
|
|
99
|
-
pass
|
|
100
|
-
return None
|
|
101
|
-
|
|
102
|
-
def upload_file_alternative(img_bytes, image_format):
|
|
103
|
-
try:
|
|
104
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
105
|
-
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
106
|
-
tmp.write(img_bytes)
|
|
107
|
-
tmp.flush()
|
|
108
|
-
tmp_path = tmp.name
|
|
109
|
-
try:
|
|
110
|
-
if not os.path.isfile(tmp_path):
|
|
111
|
-
return None
|
|
112
|
-
with open(tmp_path, 'rb') as img_file:
|
|
113
|
-
files = {'file': img_file}
|
|
114
|
-
response = requests.post('https://0x0.st', files=files)
|
|
115
|
-
response.raise_for_status()
|
|
116
|
-
image_url = response.text.strip()
|
|
117
|
-
if not image_url.startswith('http'):
|
|
118
|
-
return None
|
|
119
|
-
return image_url
|
|
120
|
-
except Exception:
|
|
121
|
-
return None
|
|
122
|
-
finally:
|
|
123
|
-
try:
|
|
124
|
-
os.remove(tmp_path)
|
|
125
|
-
except Exception:
|
|
126
|
-
pass
|
|
127
|
-
except Exception:
|
|
128
|
-
return None
|
|
129
|
-
|
|
130
|
-
for _ in range(n):
|
|
131
|
-
# Step 1: Get Authentication Token
|
|
132
|
-
auth_data = self._client.read_and_refresh_token()
|
|
133
|
-
gen_headers = {
|
|
134
|
-
"Authorization": auth_data.get("idToken"),
|
|
135
|
-
}
|
|
136
|
-
# Remove content-type header for form data
|
|
137
|
-
if "content-type" in self._client.session.headers:
|
|
138
|
-
del self._client.session.headers["content-type"]
|
|
139
|
-
# get_model now returns the proper style name from model_aliases
|
|
140
|
-
style_value = self._client.get_model(model)
|
|
141
|
-
image_payload = {
|
|
142
|
-
"prompt": str(prompt),
|
|
143
|
-
"negative_prompt": str(kwargs.get("negative_prompt", "blurry, deformed hands, ugly")),
|
|
144
|
-
"style": str(style_value),
|
|
145
|
-
"images_num": str(1), # Generate one image at a time in the loop
|
|
146
|
-
"cfg_scale": str(kwargs.get("guidance_scale", 7)),
|
|
147
|
-
"steps": str(kwargs.get("num_inference_steps", 30)),
|
|
148
|
-
"aspect_ratio": str(aspect_ratio),
|
|
149
|
-
}
|
|
150
|
-
# Step 2: Generate Image (send as form data, not JSON)
|
|
151
|
-
image_response = self._client.session.post(
|
|
152
|
-
self._client.image_generation_url,
|
|
153
|
-
data=image_payload, # Use form data instead of JSON
|
|
154
|
-
headers=gen_headers,
|
|
155
|
-
timeout=timeout
|
|
156
|
-
)
|
|
157
|
-
if image_response.status_code != 200:
|
|
158
|
-
raise RuntimeError(f"AIArta API error {image_response.status_code}: {image_response.text}\nPayload: {image_payload}")
|
|
159
|
-
image_data = image_response.json()
|
|
160
|
-
record_id = image_data.get("record_id")
|
|
161
|
-
if not record_id:
|
|
162
|
-
raise RuntimeError(f"Failed to initiate image generation: {image_data}")
|
|
163
|
-
# Step 3: Check Generation Status
|
|
164
|
-
status_url = self._client.status_check_url.format(record_id=record_id)
|
|
165
|
-
while True:
|
|
166
|
-
status_response = self._client.session.get(
|
|
167
|
-
status_url,
|
|
168
|
-
headers=gen_headers,
|
|
169
|
-
timeout=timeout
|
|
170
|
-
)
|
|
171
|
-
status_data = status_response.json()
|
|
172
|
-
status = status_data.get("status")
|
|
173
|
-
if status == "DONE":
|
|
174
|
-
image_urls = [image["url"] for image in status_data.get("response", [])]
|
|
175
|
-
if not image_urls:
|
|
176
|
-
raise RuntimeError("No image URLs returned from AIArta")
|
|
177
|
-
img_resp = self._client.session.get(image_urls[0], timeout=timeout)
|
|
178
|
-
img_resp.raise_for_status()
|
|
179
|
-
img_bytes = img_resp.content
|
|
180
|
-
# Convert to png or jpeg in memory
|
|
181
|
-
with BytesIO(img_bytes) as input_io:
|
|
182
|
-
with Image.open(input_io) as im:
|
|
183
|
-
out_io = BytesIO()
|
|
184
|
-
if image_format.lower() == "jpeg":
|
|
185
|
-
im = im.convert("RGB")
|
|
186
|
-
im.save(out_io, format="JPEG")
|
|
187
|
-
else:
|
|
188
|
-
im.save(out_io, format="PNG")
|
|
189
|
-
img_bytes = out_io.getvalue()
|
|
190
|
-
images.append(img_bytes)
|
|
191
|
-
if response_format == "url":
|
|
192
|
-
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
193
|
-
if not uploaded_url:
|
|
194
|
-
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
195
|
-
if uploaded_url:
|
|
196
|
-
urls.append(uploaded_url)
|
|
197
|
-
else:
|
|
198
|
-
raise RuntimeError("Failed to upload image to catbox.moe using all available methods")
|
|
199
|
-
break
|
|
200
|
-
elif status in ("IN_QUEUE", "IN_PROGRESS"):
|
|
201
|
-
time.sleep(2)
|
|
202
|
-
else:
|
|
203
|
-
raise RuntimeError(f"Image generation failed with status: {status}")
|
|
204
|
-
|
|
205
|
-
result_data = []
|
|
206
|
-
if response_format == "url":
|
|
207
|
-
for url in urls:
|
|
208
|
-
result_data.append(ImageData(url=url))
|
|
209
|
-
elif response_format == "b64_json":
|
|
210
|
-
import base64
|
|
211
|
-
for img in images:
|
|
212
|
-
b64 = base64.b64encode(img).decode("utf-8")
|
|
213
|
-
result_data.append(ImageData(b64_json=b64))
|
|
214
|
-
else:
|
|
215
|
-
raise ValueError("response_format must be 'url' or 'b64_json'")
|
|
216
|
-
|
|
217
|
-
from time import time as _time
|
|
218
|
-
return ImageResponse(
|
|
219
|
-
created=int(_time()),
|
|
220
|
-
data=result_data
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
class AIArta(TTICompatibleProvider):
|
|
224
|
-
# Model aliases mapping from lowercase keys to proper API style names
|
|
225
|
-
model_aliases = {
|
|
226
|
-
"flux": "Flux",
|
|
227
|
-
"medieval": "Medieval",
|
|
228
|
-
"vincent_van_gogh": "Vincent Van Gogh",
|
|
229
|
-
"f_dev": "F Dev",
|
|
230
|
-
"low_poly": "Low Poly",
|
|
231
|
-
"dreamshaper_xl": "Dreamshaper-xl",
|
|
232
|
-
"anima_pencil_xl": "Anima-pencil-xl",
|
|
233
|
-
"biomech": "Biomech",
|
|
234
|
-
"trash_polka": "Trash Polka",
|
|
235
|
-
"no_style": "No Style",
|
|
236
|
-
"cheyenne_xl": "Cheyenne-xl",
|
|
237
|
-
"chicano": "Chicano",
|
|
238
|
-
"embroidery_tattoo": "Embroidery tattoo",
|
|
239
|
-
"red_and_black": "Red and Black",
|
|
240
|
-
"fantasy_art": "Fantasy Art",
|
|
241
|
-
"watercolor": "Watercolor",
|
|
242
|
-
"dotwork": "Dotwork",
|
|
243
|
-
"old_school_colored": "Old school colored",
|
|
244
|
-
"realistic_tattoo": "Realistic tattoo",
|
|
245
|
-
"japanese_2": "Japanese_2",
|
|
246
|
-
"realistic_stock_xl": "Realistic-stock-xl",
|
|
247
|
-
"f_pro": "F Pro",
|
|
248
|
-
"revanimated": "RevAnimated",
|
|
249
|
-
"katayama_mix_xl": "Katayama-mix-xl",
|
|
250
|
-
"sdxl_l": "SDXL L",
|
|
251
|
-
"cor_epica_xl": "Cor-epica-xl",
|
|
252
|
-
"anime_tattoo": "Anime tattoo",
|
|
253
|
-
"new_school": "New School",
|
|
254
|
-
"death_metal": "Death metal",
|
|
255
|
-
"old_school": "Old School",
|
|
256
|
-
"juggernaut_xl": "Juggernaut-xl",
|
|
257
|
-
"photographic": "Photographic",
|
|
258
|
-
"sdxl_1_0": "SDXL 1.0",
|
|
259
|
-
"graffiti": "Graffiti",
|
|
260
|
-
"mini_tattoo": "Mini tattoo",
|
|
261
|
-
"surrealism": "Surrealism",
|
|
262
|
-
"neo_traditional": "Neo-traditional",
|
|
263
|
-
"on_limbs_black": "On limbs black",
|
|
264
|
-
"yamers_realistic_xl": "Yamers-realistic-xl",
|
|
265
|
-
"pony_xl": "Pony-xl",
|
|
266
|
-
"playground_xl": "Playground-xl",
|
|
267
|
-
"anything_xl": "Anything-xl",
|
|
268
|
-
"flame_design": "Flame design",
|
|
269
|
-
"kawaii": "Kawaii",
|
|
270
|
-
"cinematic_art": "Cinematic Art",
|
|
271
|
-
"professional": "Professional",
|
|
272
|
-
"black_ink": "Black Ink"
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
AVAILABLE_MODELS = list(model_aliases.keys())
|
|
276
|
-
default_model = "Flux"
|
|
277
|
-
default_image_model = default_model
|
|
278
|
-
|
|
279
|
-
def __init__(self):
|
|
280
|
-
self.image_generation_url = "https://img-gen-prod.ai-arta.com/api/v1/text2image"
|
|
281
|
-
self.status_check_url = "https://img-gen-prod.ai-arta.com/api/v1/text2image/{record_id}/status"
|
|
282
|
-
self.auth_url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
|
|
283
|
-
self.token_refresh_url = "https://securetoken.googleapis.com/v1/token?key=AIzaSyB3-71wG0fIt0shj0ee4fvx1shcjJHGrrQ"
|
|
284
|
-
self.session = requests.Session()
|
|
285
|
-
self.user_agent = LitAgent().random()
|
|
286
|
-
self.headers = {
|
|
287
|
-
"accept": "application/json",
|
|
288
|
-
"accept-language": "en-US,en;q=0.9",
|
|
289
|
-
"origin": "https://img-gen-prod.ai-arta.com",
|
|
290
|
-
"referer": "https://img-gen-prod.ai-arta.com/",
|
|
291
|
-
"user-agent": self.user_agent,
|
|
292
|
-
}
|
|
293
|
-
self.session.headers.update(self.headers)
|
|
294
|
-
self.images = Images(self)
|
|
295
|
-
|
|
296
|
-
def get_auth_file(self) -> str:
|
|
297
|
-
path = os.path.join(os.path.expanduser("~"), ".ai_arta_cookies")
|
|
298
|
-
if not os.path.exists(path):
|
|
299
|
-
os.makedirs(path)
|
|
300
|
-
filename = f"auth_{self.__class__.__name__}.json"
|
|
301
|
-
return os.path.join(path, filename)
|
|
302
|
-
|
|
303
|
-
def create_token(self, path: str) -> Dict[str, Any]:
|
|
304
|
-
auth_payload = {"clientType": "CLIENT_TYPE_ANDROID"}
|
|
305
|
-
proxies = self.session.proxies if self.session.proxies else None
|
|
306
|
-
auth_response = self.session.post(self.auth_url, json=auth_payload, timeout=60, proxies=proxies)
|
|
307
|
-
auth_data = auth_response.json()
|
|
308
|
-
auth_token = auth_data.get("idToken")
|
|
309
|
-
if not auth_token:
|
|
310
|
-
raise Exception("Failed to obtain authentication token.")
|
|
311
|
-
with open(path, 'w') as f:
|
|
312
|
-
json.dump(auth_data, f)
|
|
313
|
-
return auth_data
|
|
314
|
-
|
|
315
|
-
def refresh_token(self, refresh_token: str) -> tuple[str, str]:
|
|
316
|
-
payload = {
|
|
317
|
-
"grant_type": "refresh_token",
|
|
318
|
-
"refresh_token": refresh_token,
|
|
319
|
-
}
|
|
320
|
-
response = self.session.post(self.token_refresh_url, data=payload, timeout=60)
|
|
321
|
-
response_data = response.json()
|
|
322
|
-
return response_data.get("id_token"), response_data.get("refresh_token")
|
|
323
|
-
|
|
324
|
-
def read_and_refresh_token(self) -> Dict[str, Any]:
|
|
325
|
-
path = self.get_auth_file()
|
|
326
|
-
if os.path.isfile(path):
|
|
327
|
-
with open(path, 'r') as f:
|
|
328
|
-
auth_data = json.load(f)
|
|
329
|
-
diff = time.time() - os.path.getmtime(path)
|
|
330
|
-
expires_in = int(auth_data.get("expiresIn", 3600))
|
|
331
|
-
if diff < expires_in:
|
|
332
|
-
if diff > expires_in / 2:
|
|
333
|
-
auth_data["idToken"], auth_data["refreshToken"] = self.refresh_token(
|
|
334
|
-
auth_data.get("refreshToken")
|
|
335
|
-
)
|
|
336
|
-
with open(path, 'w') as f:
|
|
337
|
-
json.dump(auth_data, f)
|
|
338
|
-
return auth_data
|
|
339
|
-
return self.create_token(path)
|
|
340
|
-
|
|
341
|
-
def get_model(self, model_name: str) -> str:
|
|
342
|
-
# Convert to lowercase for lookup
|
|
343
|
-
model_key = model_name.lower()
|
|
344
|
-
# Return the proper style name from model_aliases, or the original if not found
|
|
345
|
-
return self.model_aliases.get(model_key, model_name)
|
|
346
|
-
|
|
347
|
-
@property
|
|
348
|
-
def models(self):
|
|
349
|
-
class _ModelList:
|
|
350
|
-
def list(inner_self):
|
|
351
|
-
return type(self).AVAILABLE_MODELS
|
|
352
|
-
return _ModelList()
|
|
353
|
-
|
|
354
|
-
# Example usage:
|
|
355
|
-
if __name__ == "__main__":
|
|
356
|
-
from rich import print
|
|
357
|
-
client = AIArta()
|
|
358
|
-
response = client.images.create(
|
|
359
|
-
model="flux",
|
|
360
|
-
prompt="a white siamese cat",
|
|
361
|
-
response_format="url",
|
|
362
|
-
n=2,
|
|
363
|
-
timeout=30,
|
|
364
|
-
)
|
|
365
|
-
print(response)
|
webscout/Provider/TTI/artbit.py
DELETED
|
File without changes
|