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
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Any, Dict, Generator, Optional, Union, cast
|
|
3
|
+
|
|
4
|
+
from curl_cffi import CurlError
|
|
5
|
+
from curl_cffi.requests import Session
|
|
6
|
+
|
|
7
|
+
from webscout import exceptions
|
|
8
|
+
from webscout.AIbase import Provider, Response
|
|
9
|
+
from webscout.AIutel import AwesomePrompts, Conversation, Optimizers, sanitize_stream
|
|
10
|
+
from webscout.litagent import LitAgent
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QodoAI(Provider):
|
|
14
|
+
"""
|
|
15
|
+
A class to interact with the Qodo AI API.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
AVAILABLE_MODELS = [
|
|
19
|
+
"gpt-4.1",
|
|
20
|
+
"gpt-4o",
|
|
21
|
+
"o3",
|
|
22
|
+
"o4-mini",
|
|
23
|
+
"claude-4-sonnet",
|
|
24
|
+
"gemini-2.5-pro",
|
|
25
|
+
"grok-4"
|
|
26
|
+
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _qodo_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
31
|
+
"""Extracts content from Qodo stream JSON objects."""
|
|
32
|
+
if isinstance(chunk, dict):
|
|
33
|
+
data = chunk.get("data", {})
|
|
34
|
+
if isinstance(data, dict):
|
|
35
|
+
tool_args = data.get("tool_args", {})
|
|
36
|
+
if isinstance(tool_args, dict):
|
|
37
|
+
return tool_args.get("content")
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
api_key: Optional[str] = None,
|
|
43
|
+
is_conversation: bool = True,
|
|
44
|
+
max_tokens: int = 2049,
|
|
45
|
+
timeout: int = 30,
|
|
46
|
+
intro: Optional[str] = None,
|
|
47
|
+
filepath: Optional[str] = None,
|
|
48
|
+
update_file: bool = True,
|
|
49
|
+
proxies: dict = {},
|
|
50
|
+
history_offset: int = 10250,
|
|
51
|
+
act: Optional[str] = None,
|
|
52
|
+
model: str = "claude-4-sonnet",
|
|
53
|
+
browser: str = "chrome"
|
|
54
|
+
):
|
|
55
|
+
"""Initializes the Qodo AI API client."""
|
|
56
|
+
if model not in self.AVAILABLE_MODELS:
|
|
57
|
+
raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
|
|
58
|
+
|
|
59
|
+
self.url = "https://api.cli.qodo.ai/v2/agentic/start-task"
|
|
60
|
+
self.info_url = "https://api.cli.qodo.ai/v2/info/get-things"
|
|
61
|
+
|
|
62
|
+
# Initialize LitAgent for user agent generation
|
|
63
|
+
self.agent = LitAgent()
|
|
64
|
+
self.fingerprint = self.agent.generate_fingerprint(browser)
|
|
65
|
+
|
|
66
|
+
# Store API key
|
|
67
|
+
self.api_key = api_key or "sk-dS7U-extxMWUxc8SbYYOuncqGUIE8-y2OY8oMCpu0eI-qnSUyH9CYWO_eAMpqwfMo7pXU3QNrclfZYMO0M6BJTM"
|
|
68
|
+
|
|
69
|
+
# Generate session ID dynamically from API
|
|
70
|
+
self.session_id = self._get_session_id()
|
|
71
|
+
self.request_id = str(uuid.uuid4())
|
|
72
|
+
|
|
73
|
+
# Use the fingerprint for headers
|
|
74
|
+
self.headers = {
|
|
75
|
+
"Accept": "text/plain",
|
|
76
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
77
|
+
"Accept-Language": self.fingerprint["accept_language"],
|
|
78
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
79
|
+
"Connection": "close",
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"host": "api.cli.qodo.ai",
|
|
82
|
+
"Request-id": self.request_id,
|
|
83
|
+
"User-Agent": self.fingerprint["user_agent"],
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Initialize curl_cffi Session
|
|
87
|
+
self.session = Session()
|
|
88
|
+
# Add Session-id to headers after getting it from API
|
|
89
|
+
self.headers["Session-id"] = self.session_id
|
|
90
|
+
self.session.headers.update(self.headers)
|
|
91
|
+
self.session.proxies.update(proxies)
|
|
92
|
+
|
|
93
|
+
self.is_conversation = is_conversation
|
|
94
|
+
self.max_tokens_to_sample = max_tokens
|
|
95
|
+
self.timeout = timeout
|
|
96
|
+
self.last_response = {}
|
|
97
|
+
self.model = model
|
|
98
|
+
|
|
99
|
+
self.__available_optimizers = (
|
|
100
|
+
method
|
|
101
|
+
for method in dir(Optimizers)
|
|
102
|
+
if callable(getattr(Optimizers, method)) and not method.startswith("__")
|
|
103
|
+
)
|
|
104
|
+
self.conversation = Conversation(
|
|
105
|
+
is_conversation, self.max_tokens_to_sample, filepath, update_file
|
|
106
|
+
)
|
|
107
|
+
act_prompt = (
|
|
108
|
+
AwesomePrompts().get_act(cast(Union[str, int], act), default=None, case_insensitive=True
|
|
109
|
+
)
|
|
110
|
+
if act
|
|
111
|
+
else intro
|
|
112
|
+
)
|
|
113
|
+
if act_prompt:
|
|
114
|
+
self.conversation.intro = act_prompt
|
|
115
|
+
self.conversation.history_offset = history_offset
|
|
116
|
+
|
|
117
|
+
def refresh_identity(self, browser: Optional[str] = None):
|
|
118
|
+
"""
|
|
119
|
+
Refreshes the browser identity fingerprint.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
browser: Specific browser to use for the new fingerprint
|
|
123
|
+
"""
|
|
124
|
+
browser = browser or self.fingerprint.get("browser_type", "chrome")
|
|
125
|
+
self.fingerprint = self.agent.generate_fingerprint(browser)
|
|
126
|
+
|
|
127
|
+
# Update headers with new fingerprint
|
|
128
|
+
self.headers.update({
|
|
129
|
+
"Accept-Language": self.fingerprint["accept_language"],
|
|
130
|
+
"User-Agent": self.fingerprint["user_agent"],
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
# Update session headers
|
|
134
|
+
for header, value in self.headers.items():
|
|
135
|
+
self.session.headers[header] = value
|
|
136
|
+
|
|
137
|
+
return self.fingerprint
|
|
138
|
+
|
|
139
|
+
def _build_payload(self, prompt: str):
|
|
140
|
+
"""Build the payload for Qodo AI API."""
|
|
141
|
+
return {
|
|
142
|
+
"agent_type": "cli",
|
|
143
|
+
"session_id": self.session_id,
|
|
144
|
+
"user_data": {
|
|
145
|
+
"extension_version": "0.7.2",
|
|
146
|
+
"os_platform": "win32",
|
|
147
|
+
"os_version": "v23.9.0",
|
|
148
|
+
"editor_type": "cli"
|
|
149
|
+
},
|
|
150
|
+
"tools": {
|
|
151
|
+
"web_search": [
|
|
152
|
+
{
|
|
153
|
+
"name": "web_search",
|
|
154
|
+
"description": "Searches the web and returns results based on the user's query (Powered by Nimble).",
|
|
155
|
+
"inputSchema": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"properties": {
|
|
158
|
+
"llm_description": {
|
|
159
|
+
"default": "Searches the web and returns results based on the user's query.",
|
|
160
|
+
"title": "Llm Description",
|
|
161
|
+
"type": "string"
|
|
162
|
+
},
|
|
163
|
+
"query": {
|
|
164
|
+
"description": "The search query to execute",
|
|
165
|
+
"title": "Query",
|
|
166
|
+
"type": "string"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"required": ["query"],
|
|
170
|
+
"title": "NimbleWebSearch"
|
|
171
|
+
},
|
|
172
|
+
"be_tool": True,
|
|
173
|
+
"autoApproved": True
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "web_fetch",
|
|
177
|
+
"description": "Fetches content from a given URL (Powered by Nimble).",
|
|
178
|
+
"inputSchema": {
|
|
179
|
+
"type": "object",
|
|
180
|
+
"properties": {
|
|
181
|
+
"llm_description": {
|
|
182
|
+
"default": "Fetches content from a given URL.",
|
|
183
|
+
"title": "Llm Description",
|
|
184
|
+
"type": "string"
|
|
185
|
+
},
|
|
186
|
+
"url": {
|
|
187
|
+
"description": "The URL to fetch content from",
|
|
188
|
+
"title": "Url",
|
|
189
|
+
"type": "string"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"required": ["url"],
|
|
193
|
+
"title": "NimbleWebFetch"
|
|
194
|
+
},
|
|
195
|
+
"be_tool": True,
|
|
196
|
+
"autoApproved": True
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
# "projects_root_path": ["C:\\Users\\koula"],
|
|
201
|
+
# "cwd": "C:\\Users\\koula",
|
|
202
|
+
"user_request": prompt,
|
|
203
|
+
"execution_strategy": "act",
|
|
204
|
+
"custom_model": self.model,
|
|
205
|
+
"stream": True
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
def ask(
|
|
209
|
+
self,
|
|
210
|
+
prompt: str,
|
|
211
|
+
stream: bool = False,
|
|
212
|
+
raw: bool = False,
|
|
213
|
+
optimizer: Optional[str] = None,
|
|
214
|
+
conversationally: bool = False,
|
|
215
|
+
**kwargs: Any,
|
|
216
|
+
) -> Union[Dict[str, Any], Generator]:
|
|
217
|
+
conversation_prompt = self.conversation.gen_complete_prompt(prompt)
|
|
218
|
+
if optimizer:
|
|
219
|
+
if optimizer in self.__available_optimizers:
|
|
220
|
+
conversation_prompt = getattr(Optimizers, optimizer)(
|
|
221
|
+
conversation_prompt if conversationally else prompt
|
|
222
|
+
)
|
|
223
|
+
else:
|
|
224
|
+
raise Exception(f"Optimizer is not one of {self.__available_optimizers}")
|
|
225
|
+
|
|
226
|
+
payload = self._build_payload(conversation_prompt)
|
|
227
|
+
payload["stream"] = stream
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def for_stream():
|
|
231
|
+
try:
|
|
232
|
+
response = self.session.post(
|
|
233
|
+
self.url,
|
|
234
|
+
json=payload,
|
|
235
|
+
stream=True,
|
|
236
|
+
timeout=self.timeout,
|
|
237
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
238
|
+
)
|
|
239
|
+
# Check for internal server error with session ID in the response
|
|
240
|
+
if response.status_code == 500 and response.text and "Internal server error, session ID:" in response.text:
|
|
241
|
+
# Switch to continue-task endpoint and retry
|
|
242
|
+
self.url = "https://api.cli.qodo.ai/v2/agentic/continue-task"
|
|
243
|
+
response = self.session.post(
|
|
244
|
+
self.url,
|
|
245
|
+
json=payload,
|
|
246
|
+
stream=True,
|
|
247
|
+
timeout=self.timeout,
|
|
248
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
249
|
+
)
|
|
250
|
+
if response.status_code == 401:
|
|
251
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
252
|
+
"Invalid API key. You need to provide your own API key.\n"
|
|
253
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
254
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
255
|
+
)
|
|
256
|
+
elif response.status_code != 200:
|
|
257
|
+
raise exceptions.FailedToGenerateResponseError(f"HTTP {response.status_code}: {response.text}")
|
|
258
|
+
|
|
259
|
+
streaming_text = ""
|
|
260
|
+
processed_stream = sanitize_stream(
|
|
261
|
+
data=response.iter_content(chunk_size=None),
|
|
262
|
+
intro_value="",
|
|
263
|
+
to_json=True,
|
|
264
|
+
skip_markers=["[DONE]"],
|
|
265
|
+
content_extractor=self._qodo_extractor,
|
|
266
|
+
yield_raw_on_error=True,
|
|
267
|
+
raw=raw
|
|
268
|
+
)
|
|
269
|
+
for content_chunk in processed_stream:
|
|
270
|
+
if content_chunk:
|
|
271
|
+
yield content_chunk if raw else {"text": content_chunk}
|
|
272
|
+
if not raw:
|
|
273
|
+
streaming_text += content_chunk
|
|
274
|
+
|
|
275
|
+
self.last_response = {"text": streaming_text}
|
|
276
|
+
self.conversation.update_chat_history(prompt, streaming_text)
|
|
277
|
+
except CurlError as e:
|
|
278
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
279
|
+
except Exception as e:
|
|
280
|
+
raise exceptions.FailedToGenerateResponseError(f"An unexpected error occurred ({type(e).__name__}): {e}")
|
|
281
|
+
|
|
282
|
+
def for_non_stream():
|
|
283
|
+
try:
|
|
284
|
+
payload["stream"] = False
|
|
285
|
+
response = self.session.post(
|
|
286
|
+
self.url,
|
|
287
|
+
json=payload,
|
|
288
|
+
timeout=self.timeout,
|
|
289
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
290
|
+
)
|
|
291
|
+
# Check for internal server error with session ID in the response
|
|
292
|
+
if response.status_code == 500 and response.text and "Internal server error, session ID:" in response.text:
|
|
293
|
+
self.url = "https://api.cli.qodo.ai/v2/agentic/continue-task"
|
|
294
|
+
response = self.session.post(
|
|
295
|
+
self.url,
|
|
296
|
+
json=payload,
|
|
297
|
+
timeout=self.timeout,
|
|
298
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
299
|
+
)
|
|
300
|
+
if response.status_code == 401:
|
|
301
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
302
|
+
"Invalid API key. You need to provide your own API key.\n"
|
|
303
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
304
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
305
|
+
)
|
|
306
|
+
elif response.status_code != 200:
|
|
307
|
+
raise exceptions.FailedToGenerateResponseError(f"HTTP {response.status_code}: {response.text}")
|
|
308
|
+
|
|
309
|
+
response_text = response.text
|
|
310
|
+
processed_stream = sanitize_stream(
|
|
311
|
+
data=response_text.splitlines(),
|
|
312
|
+
intro_value=None,
|
|
313
|
+
to_json=True,
|
|
314
|
+
content_extractor=self._qodo_extractor,
|
|
315
|
+
yield_raw_on_error=True,
|
|
316
|
+
raw=raw
|
|
317
|
+
)
|
|
318
|
+
full_response = ""
|
|
319
|
+
for content in processed_stream:
|
|
320
|
+
if content:
|
|
321
|
+
full_response += content
|
|
322
|
+
|
|
323
|
+
self.last_response = {"text": full_response}
|
|
324
|
+
self.conversation.update_chat_history(prompt, full_response)
|
|
325
|
+
return {"text": full_response} if not raw else full_response
|
|
326
|
+
except CurlError as e:
|
|
327
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
328
|
+
except Exception as e:
|
|
329
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed ({type(e).__name__}): {e}")
|
|
330
|
+
|
|
331
|
+
return for_stream() if stream else for_non_stream()
|
|
332
|
+
|
|
333
|
+
def chat(
|
|
334
|
+
self,
|
|
335
|
+
prompt: str,
|
|
336
|
+
stream: bool = False,
|
|
337
|
+
optimizer: Optional[str] = None,
|
|
338
|
+
conversationally: bool = False,
|
|
339
|
+
raw: bool = False,
|
|
340
|
+
**kwargs: Any,
|
|
341
|
+
) -> Union[str, Generator[str, None, None]]:
|
|
342
|
+
def for_stream():
|
|
343
|
+
for response in self.ask(
|
|
344
|
+
prompt, True, raw=raw, optimizer=optimizer, conversationally=conversationally
|
|
345
|
+
):
|
|
346
|
+
if raw:
|
|
347
|
+
yield response
|
|
348
|
+
else:
|
|
349
|
+
yield self.get_message(response)
|
|
350
|
+
|
|
351
|
+
def for_non_stream():
|
|
352
|
+
result = self.ask(
|
|
353
|
+
prompt, False, raw=raw, optimizer=optimizer, conversationally=conversationally
|
|
354
|
+
)
|
|
355
|
+
if raw:
|
|
356
|
+
return result
|
|
357
|
+
else:
|
|
358
|
+
return self.get_message(result)
|
|
359
|
+
|
|
360
|
+
return for_stream() if stream else for_non_stream()
|
|
361
|
+
|
|
362
|
+
def get_message(self, response: Response) -> str:
|
|
363
|
+
if isinstance(response, str):
|
|
364
|
+
return response
|
|
365
|
+
if isinstance(response, dict):
|
|
366
|
+
text = dict(response).get("text", "")
|
|
367
|
+
return text.replace('\\n', '\n').replace('\\n\\n', '\n\n')
|
|
368
|
+
return str(response)
|
|
369
|
+
|
|
370
|
+
def _get_session_id(self) -> str:
|
|
371
|
+
"""Get session ID from Qodo API."""
|
|
372
|
+
try:
|
|
373
|
+
# Create temporary session for the info request
|
|
374
|
+
temp_session = Session()
|
|
375
|
+
temp_headers = {
|
|
376
|
+
"Accept": "text/plain",
|
|
377
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
378
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
379
|
+
"Connection": "close",
|
|
380
|
+
"Content-Type": "application/json",
|
|
381
|
+
"host": "api.cli.qodo.ai",
|
|
382
|
+
"Request-id": str(uuid.uuid4()),
|
|
383
|
+
"User-Agent": self.fingerprint["user_agent"] if hasattr(self, 'fingerprint') else "axios/1.10.0",
|
|
384
|
+
}
|
|
385
|
+
temp_session.headers.update(temp_headers)
|
|
386
|
+
|
|
387
|
+
response = temp_session.get(
|
|
388
|
+
self.info_url,
|
|
389
|
+
timeout=self.timeout if hasattr(self, 'timeout') else 30,
|
|
390
|
+
impersonate="chrome110"
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if response.status_code == 200:
|
|
394
|
+
data = response.json()
|
|
395
|
+
session_id = data.get("session-id")
|
|
396
|
+
if session_id:
|
|
397
|
+
return session_id
|
|
398
|
+
elif response.status_code == 401:
|
|
399
|
+
# API key is invalid
|
|
400
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
401
|
+
"Invalid API key. You need to provide your own API key.\n"
|
|
402
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
403
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
404
|
+
)
|
|
405
|
+
else:
|
|
406
|
+
# Other HTTP errors
|
|
407
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
408
|
+
f"Failed to authenticate with Qodo API (HTTP {response.status_code}). "
|
|
409
|
+
"You may need to provide your own API key.\n"
|
|
410
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
411
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Fallback to generated session ID if API call fails
|
|
415
|
+
from datetime import datetime
|
|
416
|
+
today = datetime.now().strftime("%Y%m%d")
|
|
417
|
+
return f"{today}-{str(uuid.uuid4())}"
|
|
418
|
+
|
|
419
|
+
except exceptions.FailedToGenerateResponseError:
|
|
420
|
+
# Re-raise our custom exceptions
|
|
421
|
+
raise
|
|
422
|
+
except Exception as e:
|
|
423
|
+
# For other errors, show the API key message
|
|
424
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
425
|
+
f"Failed to connect to Qodo API: {e}\n"
|
|
426
|
+
"You may need to provide your own API key.\n"
|
|
427
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
428
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
def refresh_session(self):
|
|
432
|
+
"""
|
|
433
|
+
Refreshes the session ID by calling the Qodo API.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
str: The new session ID
|
|
437
|
+
"""
|
|
438
|
+
self.session_id = self._get_session_id()
|
|
439
|
+
|
|
440
|
+
# Update headers with new session ID
|
|
441
|
+
self.headers["Session-id"] = self.session_id
|
|
442
|
+
self.session.headers["Session-id"] = self.session_id
|
|
443
|
+
|
|
444
|
+
return self.session_id
|
|
445
|
+
|
|
446
|
+
def get_available_models(self) -> Dict[str, Any]:
|
|
447
|
+
"""
|
|
448
|
+
Get available models and info from Qodo API.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
Dict containing models, default_model, version, and session info
|
|
452
|
+
"""
|
|
453
|
+
try:
|
|
454
|
+
response = self.session.get(
|
|
455
|
+
self.info_url,
|
|
456
|
+
timeout=self.timeout,
|
|
457
|
+
impersonate=self.fingerprint.get("browser_type", "chrome110")
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
if response.status_code == 200:
|
|
461
|
+
return response.json()
|
|
462
|
+
elif response.status_code == 401:
|
|
463
|
+
raise exceptions.FailedToGenerateResponseError(
|
|
464
|
+
"Invalid API key. You need to provide your own API key.\n"
|
|
465
|
+
"Usage: QodoAI(api_key='your_api_key_here')\n"
|
|
466
|
+
"To get an API key, install Qodo CLI via: https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart"
|
|
467
|
+
)
|
|
468
|
+
else:
|
|
469
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to get models: HTTP {response.status_code}")
|
|
470
|
+
|
|
471
|
+
except CurlError as e:
|
|
472
|
+
raise exceptions.FailedToGenerateResponseError(f"Request failed (CurlError): {e}")
|
|
473
|
+
except Exception as e:
|
|
474
|
+
raise exceptions.FailedToGenerateResponseError(f"Failed to get models ({type(e).__name__}): {e}")
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
if __name__ == "__main__":
|
|
478
|
+
ai = QodoAI() # u will need to give your API key here to get api install qodo cli via https://docs.qodo.ai/qodo-documentation/qodo-gen-cli/getting-started/setup-and-quickstart
|
|
479
|
+
response = ai.chat("write a poem about india", raw=False, stream=True)
|
|
480
|
+
for chunk in response:
|
|
481
|
+
print(chunk, end='', flush=True)
|