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,1105 +0,0 @@
|
|
|
1
|
-
"""RawDog module for generating and auto-executing Python scripts in the CLI."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import re
|
|
5
|
-
import sys
|
|
6
|
-
import queue
|
|
7
|
-
import tempfile
|
|
8
|
-
import threading
|
|
9
|
-
import subprocess
|
|
10
|
-
from typing import Optional, Generator, List, Tuple, Dict, Any, NamedTuple
|
|
11
|
-
from rich.panel import Panel
|
|
12
|
-
from rich.syntax import Syntax
|
|
13
|
-
from rich.console import Console
|
|
14
|
-
from rich.markdown import Markdown
|
|
15
|
-
from rich.table import Table
|
|
16
|
-
from rich.theme import Theme
|
|
17
|
-
from rich.live import Live
|
|
18
|
-
from rich.box import ROUNDED
|
|
19
|
-
from .autocoder_utiles import get_intro_prompt
|
|
20
|
-
# Initialize LitLogger with custom format and colors
|
|
21
|
-
default_path = tempfile.mkdtemp(prefix="webscout_autocoder")
|
|
22
|
-
|
|
23
|
-
# Custom theme for consistent styling
|
|
24
|
-
CUSTOM_THEME = Theme({
|
|
25
|
-
"info": "cyan",
|
|
26
|
-
"warning": "yellow",
|
|
27
|
-
"error": "red bold",
|
|
28
|
-
"success": "green",
|
|
29
|
-
"code": "blue",
|
|
30
|
-
"output": "white",
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
console = Console(theme=CUSTOM_THEME)
|
|
34
|
-
class CommandResult(NamedTuple):
|
|
35
|
-
"""Result of a system command execution."""
|
|
36
|
-
success: bool
|
|
37
|
-
stdout: str
|
|
38
|
-
stderr: str
|
|
39
|
-
|
|
40
|
-
def run_system_command(
|
|
41
|
-
command: str,
|
|
42
|
-
exit_on_error: bool = False,
|
|
43
|
-
stdout_error: bool = False,
|
|
44
|
-
help: Optional[str] = None
|
|
45
|
-
) -> Tuple[bool, CommandResult]:
|
|
46
|
-
"""Execute a system command and return the result.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
command (str): Command to execute
|
|
50
|
-
exit_on_error (bool): Whether to exit on error. Defaults to False.
|
|
51
|
-
stdout_error (bool): Whether to include stdout in error messages. Defaults to False.
|
|
52
|
-
help (str, optional): Help message for errors. Defaults to None.
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
Tuple[bool, CommandResult]: Success status and command result containing stdout/stderr
|
|
56
|
-
"""
|
|
57
|
-
try:
|
|
58
|
-
# Execute command and capture output
|
|
59
|
-
process = subprocess.Popen(
|
|
60
|
-
command,
|
|
61
|
-
stdout=subprocess.PIPE,
|
|
62
|
-
stderr=subprocess.PIPE,
|
|
63
|
-
shell=True,
|
|
64
|
-
text=True
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# Get stdout and stderr
|
|
68
|
-
stdout, stderr = process.communicate()
|
|
69
|
-
success = process.returncode == 0
|
|
70
|
-
|
|
71
|
-
# Create result object
|
|
72
|
-
result = CommandResult(
|
|
73
|
-
success=success,
|
|
74
|
-
stdout=stdout.strip() if stdout else "",
|
|
75
|
-
stderr=stderr.strip() if stderr else ""
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
# Handle errors if needed
|
|
79
|
-
if not success and exit_on_error:
|
|
80
|
-
error_msg = stderr if stderr else stdout if stdout_error else "Command failed"
|
|
81
|
-
if help:
|
|
82
|
-
error_msg += f"\n{help}"
|
|
83
|
-
sys.exit(error_msg)
|
|
84
|
-
|
|
85
|
-
return success, result
|
|
86
|
-
|
|
87
|
-
except Exception as e:
|
|
88
|
-
# Handle execution errors
|
|
89
|
-
error_msg = str(e)
|
|
90
|
-
if help:
|
|
91
|
-
error_msg += f"\n{help}"
|
|
92
|
-
|
|
93
|
-
if exit_on_error:
|
|
94
|
-
sys.exit(error_msg)
|
|
95
|
-
|
|
96
|
-
return False, CommandResult(success=False, stdout="", stderr=error_msg)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class AutoCoder:
|
|
100
|
-
"""Generate and auto-execute Python scripts in the CLI with advanced error handling and retry logic.
|
|
101
|
-
|
|
102
|
-
This class provides:
|
|
103
|
-
- Automatic code generation
|
|
104
|
-
- Script execution with safety checks
|
|
105
|
-
- Advanced error handling and retries
|
|
106
|
-
- Beautiful logging with rich console
|
|
107
|
-
- Execution result capture and display
|
|
108
|
-
|
|
109
|
-
Examples:
|
|
110
|
-
>>> coder = AutoCoder()
|
|
111
|
-
>>> coder.execute("Get system info")
|
|
112
|
-
Generating system info script...
|
|
113
|
-
Script executed successfully!
|
|
114
|
-
"""
|
|
115
|
-
|
|
116
|
-
def __init__(
|
|
117
|
-
self,
|
|
118
|
-
quiet: bool = False,
|
|
119
|
-
internal_exec: bool = False,
|
|
120
|
-
confirm_script: bool = False,
|
|
121
|
-
interpreter: str = "python",
|
|
122
|
-
prettify: bool = True,
|
|
123
|
-
path_to_script: str = "",
|
|
124
|
-
max_retries: int = 3,
|
|
125
|
-
ai_instance = None
|
|
126
|
-
):
|
|
127
|
-
"""Initialize AutoCoder instance.
|
|
128
|
-
|
|
129
|
-
Args:
|
|
130
|
-
quiet (bool): Flag to control logging. Defaults to False.
|
|
131
|
-
internal_exec (bool): Execute scripts with exec function. Defaults to False.
|
|
132
|
-
confirm_script (bool): Give consent to scripts prior to execution. Defaults to False.
|
|
133
|
-
interpreter (str): Python's interpreter name. Defaults to "python".
|
|
134
|
-
prettify (bool): Prettify the code on stdout. Defaults to True.
|
|
135
|
-
path_to_script (str): Path to save generated scripts. Defaults to "".
|
|
136
|
-
max_retries (int): Maximum number of retry attempts. Defaults to 3.
|
|
137
|
-
ai_instance: AI instance for error correction. Defaults to None.
|
|
138
|
-
"""
|
|
139
|
-
self.internal_exec = internal_exec
|
|
140
|
-
self.confirm_script = confirm_script
|
|
141
|
-
self.quiet = quiet
|
|
142
|
-
self.interpreter = interpreter
|
|
143
|
-
self.prettify = prettify
|
|
144
|
-
self.path_to_script = path_to_script or os.path.join(default_path, "execute_this.py")
|
|
145
|
-
self.max_retries = max_retries
|
|
146
|
-
self.tried_solutions = set()
|
|
147
|
-
self.ai_instance = ai_instance
|
|
148
|
-
self.last_execution_result = ""
|
|
149
|
-
|
|
150
|
-
# Get Python version with enhanced logging
|
|
151
|
-
if self.internal_exec:
|
|
152
|
-
self.python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
153
|
-
else:
|
|
154
|
-
version_output = run_system_command(
|
|
155
|
-
f"{self.interpreter} --version",
|
|
156
|
-
exit_on_error=True,
|
|
157
|
-
stdout_error=True,
|
|
158
|
-
help="If you're using Webscout-cli, use the flag '--internal-exec'"
|
|
159
|
-
)[1].stdout
|
|
160
|
-
self.python_version = version_output.split(" ")[1]
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def _extract_code_blocks(self, response: str) -> List[Tuple[str, str]]:
|
|
165
|
-
"""Extract code blocks from a response string.
|
|
166
|
-
|
|
167
|
-
Args:
|
|
168
|
-
response (str): Response string containing code blocks
|
|
169
|
-
|
|
170
|
-
Returns:
|
|
171
|
-
List[Tuple[str, str]]: List of (code_type, code) tuples
|
|
172
|
-
"""
|
|
173
|
-
blocks = []
|
|
174
|
-
|
|
175
|
-
# First try to find code blocks with explicit language tags
|
|
176
|
-
pattern = r"```(\w+)\n(.*?)```"
|
|
177
|
-
matches = re.finditer(pattern, response, re.DOTALL)
|
|
178
|
-
|
|
179
|
-
for match in matches:
|
|
180
|
-
code_type = match.group(1).lower()
|
|
181
|
-
code = match.group(2).strip()
|
|
182
|
-
|
|
183
|
-
# Check if this is a shell command (starts with !)
|
|
184
|
-
if code_type == 'bash' or code_type == 'shell' or code.startswith('!'):
|
|
185
|
-
blocks.append(('shell', code))
|
|
186
|
-
else:
|
|
187
|
-
blocks.append((code_type, code))
|
|
188
|
-
|
|
189
|
-
# If no explicit code blocks found with language tags, try generic code blocks
|
|
190
|
-
if not blocks:
|
|
191
|
-
pattern = r"```(.*?)```"
|
|
192
|
-
matches = re.finditer(pattern, response, re.DOTALL)
|
|
193
|
-
for match in matches:
|
|
194
|
-
code = match.group(1).strip()
|
|
195
|
-
|
|
196
|
-
# Check if this is a shell command (starts with !)
|
|
197
|
-
if code.startswith('!'):
|
|
198
|
-
blocks.append(('shell', code))
|
|
199
|
-
else:
|
|
200
|
-
blocks.append(('python', code))
|
|
201
|
-
|
|
202
|
-
# If still no code blocks found, treat as raw Python code
|
|
203
|
-
if not blocks:
|
|
204
|
-
lines = [line.strip() for line in response.split('\n') if line.strip()]
|
|
205
|
-
if lines:
|
|
206
|
-
# Check if this is a shell command (starts with !)
|
|
207
|
-
if lines[0].startswith('!'):
|
|
208
|
-
blocks.append(('shell', '\n'.join(lines)))
|
|
209
|
-
else:
|
|
210
|
-
blocks.append(('python', '\n'.join(lines)))
|
|
211
|
-
|
|
212
|
-
return blocks
|
|
213
|
-
|
|
214
|
-
def _execute_code_block(self, code_type: str, code: str, ai_instance=None) -> Tuple[bool, str]:
|
|
215
|
-
"""Execute a code block.
|
|
216
|
-
|
|
217
|
-
Args:
|
|
218
|
-
code_type (str): Type of code block ('python' or 'shell')
|
|
219
|
-
code (str): Code to execute
|
|
220
|
-
ai_instance: Optional AI instance for error correction
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
Tuple[bool, str]: (Success status, Error message or execution result)
|
|
224
|
-
"""
|
|
225
|
-
try:
|
|
226
|
-
# Handle shell commands (starting with !)
|
|
227
|
-
if code_type == 'shell':
|
|
228
|
-
# Remove the leading '!' from each line
|
|
229
|
-
shell_commands = []
|
|
230
|
-
for line in code.split('\n'):
|
|
231
|
-
if line.startswith('!'):
|
|
232
|
-
shell_commands.append(line[1:].strip()) # Remove the '!' and any leading whitespace
|
|
233
|
-
else:
|
|
234
|
-
shell_commands.append(line.strip())
|
|
235
|
-
|
|
236
|
-
# Execute each shell command
|
|
237
|
-
overall_success = True
|
|
238
|
-
overall_result = []
|
|
239
|
-
|
|
240
|
-
# Display the shell command in Jupyter-style UI
|
|
241
|
-
if self.prettify:
|
|
242
|
-
# Format the command for display
|
|
243
|
-
cmd_display = '\n'.join([f"!{cmd}" for cmd in shell_commands if cmd])
|
|
244
|
-
syntax = Syntax(cmd_display, "bash", theme="monokai", line_numbers=True)
|
|
245
|
-
console.print(Panel(
|
|
246
|
-
syntax,
|
|
247
|
-
title="[bold blue]In [1]:[/bold blue]",
|
|
248
|
-
border_style="blue",
|
|
249
|
-
expand=True,
|
|
250
|
-
box=ROUNDED
|
|
251
|
-
))
|
|
252
|
-
|
|
253
|
-
for cmd in shell_commands:
|
|
254
|
-
if not cmd: # Skip empty commands
|
|
255
|
-
continue
|
|
256
|
-
|
|
257
|
-
success, result = run_system_command(cmd)
|
|
258
|
-
|
|
259
|
-
if success:
|
|
260
|
-
if result.stdout:
|
|
261
|
-
overall_result.append(result.stdout)
|
|
262
|
-
|
|
263
|
-
# Display the output in Jupyter-style UI
|
|
264
|
-
if self.prettify:
|
|
265
|
-
console.print(Panel(
|
|
266
|
-
result.stdout,
|
|
267
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
268
|
-
border_style="red",
|
|
269
|
-
expand=True,
|
|
270
|
-
padding=(0, 1),
|
|
271
|
-
box=ROUNDED
|
|
272
|
-
))
|
|
273
|
-
|
|
274
|
-
self.last_execution_result = '\n'.join(overall_result)
|
|
275
|
-
else:
|
|
276
|
-
error_msg = result.stderr if result.stderr else f"Command failed: {cmd}"
|
|
277
|
-
|
|
278
|
-
# Display the error in Jupyter-style UI
|
|
279
|
-
if self.prettify:
|
|
280
|
-
console.print(Panel(
|
|
281
|
-
f"Error: {error_msg}",
|
|
282
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
283
|
-
border_style="red",
|
|
284
|
-
expand=True,
|
|
285
|
-
padding=(0, 1),
|
|
286
|
-
box=ROUNDED
|
|
287
|
-
))
|
|
288
|
-
|
|
289
|
-
return False, error_msg
|
|
290
|
-
|
|
291
|
-
return True, self.last_execution_result
|
|
292
|
-
else:
|
|
293
|
-
# Handle Python code
|
|
294
|
-
result = self._execute_with_retry(code, ai_instance)
|
|
295
|
-
if result is None:
|
|
296
|
-
return True, self.last_execution_result
|
|
297
|
-
return False, result
|
|
298
|
-
except Exception as e:
|
|
299
|
-
return False, str(e)
|
|
300
|
-
|
|
301
|
-
def _format_output_panel(self, code: str, output_lines: list) -> Panel:
|
|
302
|
-
"""Format code and output into a single panel.
|
|
303
|
-
|
|
304
|
-
Args:
|
|
305
|
-
code (str): The code that was executed
|
|
306
|
-
output_lines (list): List of output lines
|
|
307
|
-
|
|
308
|
-
Returns:
|
|
309
|
-
Panel: Formatted panel with code and output
|
|
310
|
-
"""
|
|
311
|
-
# Format output
|
|
312
|
-
output_text = "\n".join(output_lines) if output_lines else "Running..."
|
|
313
|
-
|
|
314
|
-
# Create panel with Jupyter-like styling
|
|
315
|
-
panel = Panel(
|
|
316
|
-
output_text,
|
|
317
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
318
|
-
border_style="red",
|
|
319
|
-
expand=True,
|
|
320
|
-
padding=(0, 1),
|
|
321
|
-
box=ROUNDED
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
return panel
|
|
325
|
-
|
|
326
|
-
def _format_result_panel(self, output: str) -> Panel:
|
|
327
|
-
"""Format execution result into a panel.
|
|
328
|
-
|
|
329
|
-
Args:
|
|
330
|
-
output (str): Execution output text
|
|
331
|
-
|
|
332
|
-
Returns:
|
|
333
|
-
Panel: Formatted panel with execution result
|
|
334
|
-
"""
|
|
335
|
-
# Create panel with Jupyter-like styling
|
|
336
|
-
panel = Panel(
|
|
337
|
-
output,
|
|
338
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
339
|
-
border_style="red",
|
|
340
|
-
expand=True,
|
|
341
|
-
padding=(0, 1),
|
|
342
|
-
box=ROUNDED
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
return panel
|
|
346
|
-
|
|
347
|
-
def _stream_output(self, process: subprocess.Popen) -> Generator[str, None, None]:
|
|
348
|
-
"""Stream output from a subprocess in realtime.
|
|
349
|
-
|
|
350
|
-
Args:
|
|
351
|
-
process: Subprocess to stream output from
|
|
352
|
-
|
|
353
|
-
Yields:
|
|
354
|
-
str: Lines of output
|
|
355
|
-
"""
|
|
356
|
-
# Stream stdout
|
|
357
|
-
output_lines = []
|
|
358
|
-
for line in process.stdout:
|
|
359
|
-
decoded_line = line.decode('utf-8').strip() if isinstance(line, bytes) else line.strip()
|
|
360
|
-
if decoded_line:
|
|
361
|
-
output_lines.append(decoded_line)
|
|
362
|
-
yield decoded_line
|
|
363
|
-
|
|
364
|
-
# Check stderr
|
|
365
|
-
error = process.stderr.read() if process.stderr else None
|
|
366
|
-
if error:
|
|
367
|
-
error_str = error.decode('utf-8').strip() if isinstance(error, bytes) else error.strip()
|
|
368
|
-
if error_str:
|
|
369
|
-
yield f"Error: {error_str}"
|
|
370
|
-
output_lines.append(f"Error: {error_str}")
|
|
371
|
-
|
|
372
|
-
# Store the full execution result
|
|
373
|
-
self.last_execution_result = "\n".join(output_lines)
|
|
374
|
-
|
|
375
|
-
def _execute_with_retry(self, code: str, ai_instance=None) -> Optional[str]:
|
|
376
|
-
"""Execute code with retry logic and error correction.
|
|
377
|
-
|
|
378
|
-
Args:
|
|
379
|
-
code (str): Code to execute
|
|
380
|
-
ai_instance: Optional AI instance for error correction
|
|
381
|
-
|
|
382
|
-
Returns:
|
|
383
|
-
Optional[str]: Error message if execution failed, None if successful
|
|
384
|
-
"""
|
|
385
|
-
last_error = None
|
|
386
|
-
retries = 0
|
|
387
|
-
|
|
388
|
-
# Add the solution to tried solutions
|
|
389
|
-
self.tried_solutions.add(code)
|
|
390
|
-
|
|
391
|
-
# Print the code first
|
|
392
|
-
if self.prettify:
|
|
393
|
-
syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
|
|
394
|
-
console.print(Panel(
|
|
395
|
-
syntax,
|
|
396
|
-
title="[bold blue]In [1]:[/bold blue]",
|
|
397
|
-
border_style="blue",
|
|
398
|
-
expand=True,
|
|
399
|
-
box=ROUNDED
|
|
400
|
-
))
|
|
401
|
-
|
|
402
|
-
while retries < self.max_retries:
|
|
403
|
-
try:
|
|
404
|
-
if self.path_to_script:
|
|
405
|
-
script_dir = os.path.dirname(self.path_to_script)
|
|
406
|
-
if script_dir:
|
|
407
|
-
os.makedirs(script_dir, exist_ok=True)
|
|
408
|
-
with open(self.path_to_script, "w", encoding="utf-8") as f:
|
|
409
|
-
f.write(code)
|
|
410
|
-
|
|
411
|
-
if self.internal_exec:
|
|
412
|
-
# Create StringIO for output capture
|
|
413
|
-
import io
|
|
414
|
-
import sys
|
|
415
|
-
stdout = io.StringIO()
|
|
416
|
-
stderr = io.StringIO()
|
|
417
|
-
|
|
418
|
-
# Create a queue for realtime output
|
|
419
|
-
output_queue = queue.Queue()
|
|
420
|
-
output_lines = []
|
|
421
|
-
|
|
422
|
-
def execute_code():
|
|
423
|
-
try:
|
|
424
|
-
# Create a local namespace
|
|
425
|
-
local_namespace: Dict[str, Any] = {}
|
|
426
|
-
|
|
427
|
-
# Redirect stdout/stderr
|
|
428
|
-
sys.stdout = stdout
|
|
429
|
-
sys.stderr = stderr
|
|
430
|
-
|
|
431
|
-
# Execute the code
|
|
432
|
-
exec(code, globals(), local_namespace)
|
|
433
|
-
|
|
434
|
-
# Get any output
|
|
435
|
-
output = stdout.getvalue()
|
|
436
|
-
error = stderr.getvalue()
|
|
437
|
-
|
|
438
|
-
if error:
|
|
439
|
-
output_queue.put(("error", error))
|
|
440
|
-
if output:
|
|
441
|
-
output_queue.put(("output", output))
|
|
442
|
-
|
|
443
|
-
except Exception as e:
|
|
444
|
-
output_queue.put(("error", str(e)))
|
|
445
|
-
finally:
|
|
446
|
-
# Restore stdout/stderr
|
|
447
|
-
sys.stdout = sys.__stdout__
|
|
448
|
-
sys.stderr = sys.__stderr__
|
|
449
|
-
|
|
450
|
-
# Create and start execution thread
|
|
451
|
-
thread = threading.Thread(target=execute_code)
|
|
452
|
-
thread.daemon = True # Make thread daemon to avoid hanging
|
|
453
|
-
thread.start()
|
|
454
|
-
|
|
455
|
-
# Display output in realtime
|
|
456
|
-
with Live(auto_refresh=True) as live:
|
|
457
|
-
timeout_counter = 0
|
|
458
|
-
while thread.is_alive() or not output_queue.empty():
|
|
459
|
-
try:
|
|
460
|
-
msg_type, content = output_queue.get(timeout=0.1)
|
|
461
|
-
if content:
|
|
462
|
-
new_lines = content.splitlines()
|
|
463
|
-
output_lines.extend(new_lines)
|
|
464
|
-
live.update(self._format_output_panel(code, output_lines))
|
|
465
|
-
live.refresh()
|
|
466
|
-
output_queue.task_done()
|
|
467
|
-
except queue.Empty:
|
|
468
|
-
timeout_counter += 1
|
|
469
|
-
# Refresh the display to show it's still running
|
|
470
|
-
if timeout_counter % 10 == 0: # Refresh every ~1 second
|
|
471
|
-
live.update(self._format_output_panel(code, output_lines))
|
|
472
|
-
live.refresh()
|
|
473
|
-
if timeout_counter > 100 and thread.is_alive(): # ~10 seconds
|
|
474
|
-
output_lines.append("Warning: Execution taking longer than expected...")
|
|
475
|
-
live.update(self._format_output_panel(code, output_lines))
|
|
476
|
-
live.refresh()
|
|
477
|
-
continue
|
|
478
|
-
|
|
479
|
-
# Wait for thread to complete with timeout
|
|
480
|
-
thread.join(timeout=30) # 30 second timeout
|
|
481
|
-
if thread.is_alive():
|
|
482
|
-
output_lines.append("Error: Execution timed out after 30 seconds")
|
|
483
|
-
raise TimeoutError("Execution timed out after 30 seconds")
|
|
484
|
-
|
|
485
|
-
# Check for any final errors
|
|
486
|
-
error = stderr.getvalue()
|
|
487
|
-
if error:
|
|
488
|
-
raise Exception(error)
|
|
489
|
-
|
|
490
|
-
# Store the full execution result
|
|
491
|
-
self.last_execution_result = stdout.getvalue()
|
|
492
|
-
|
|
493
|
-
else:
|
|
494
|
-
try:
|
|
495
|
-
process = subprocess.Popen(
|
|
496
|
-
[self.interpreter, self.path_to_script],
|
|
497
|
-
stdout=subprocess.PIPE,
|
|
498
|
-
stderr=subprocess.PIPE,
|
|
499
|
-
text=True, # Use text mode to avoid encoding issues
|
|
500
|
-
bufsize=1,
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
output_lines = []
|
|
504
|
-
# Stream output in realtime
|
|
505
|
-
with Live(auto_refresh=True) as live:
|
|
506
|
-
for line in self._stream_output(process):
|
|
507
|
-
output_lines.append(line)
|
|
508
|
-
live.update(self._format_output_panel(code, output_lines))
|
|
509
|
-
live.refresh()
|
|
510
|
-
|
|
511
|
-
process.wait(timeout=30) # 30 second timeout
|
|
512
|
-
|
|
513
|
-
if process.returncode != 0:
|
|
514
|
-
# Try to read more detailed error information
|
|
515
|
-
if process.stderr:
|
|
516
|
-
error = process.stderr.read()
|
|
517
|
-
error_str = error.strip() if error else ""
|
|
518
|
-
if error_str:
|
|
519
|
-
raise Exception(error_str)
|
|
520
|
-
raise Exception(f"Process exited with code {process.returncode}")
|
|
521
|
-
|
|
522
|
-
# Store the full execution result
|
|
523
|
-
self.last_execution_result = "\n".join(output_lines)
|
|
524
|
-
|
|
525
|
-
except subprocess.TimeoutExpired:
|
|
526
|
-
# Handle the case where the process times out
|
|
527
|
-
if process:
|
|
528
|
-
process.kill()
|
|
529
|
-
raise TimeoutError("Execution timed out after 30 seconds")
|
|
530
|
-
|
|
531
|
-
return None
|
|
532
|
-
|
|
533
|
-
except Exception as e:
|
|
534
|
-
last_error = e
|
|
535
|
-
if retries < self.max_retries - 1 and ai_instance:
|
|
536
|
-
try:
|
|
537
|
-
# First try to handle import errors
|
|
538
|
-
if isinstance(e, ImportError):
|
|
539
|
-
fixed_code = self._handle_import_error(e, code)
|
|
540
|
-
if fixed_code:
|
|
541
|
-
code = fixed_code
|
|
542
|
-
retries += 1
|
|
543
|
-
continue
|
|
544
|
-
|
|
545
|
-
# Get error context and try to fix the specific error
|
|
546
|
-
error_context = self._get_error_context(e, code)
|
|
547
|
-
fixed_response = ai_instance.chat(error_context)
|
|
548
|
-
fixed_code = self._extract_code_from_response(fixed_response)
|
|
549
|
-
|
|
550
|
-
if not fixed_code:
|
|
551
|
-
# If no code found, try a more general approach
|
|
552
|
-
general_context = f"""
|
|
553
|
-
The code failed with error: {str(e)}
|
|
554
|
-
|
|
555
|
-
Original Code:
|
|
556
|
-
```python
|
|
557
|
-
{code}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
Please provide a complete, corrected version of the code that handles this error. The code should:
|
|
561
|
-
1. Handle any potential encoding issues
|
|
562
|
-
2. Include proper error handling
|
|
563
|
-
3. Use appropriate libraries and imports
|
|
564
|
-
4. Be compatible with the current Python environment
|
|
565
|
-
5. Fix the specific error: {str(e)}
|
|
566
|
-
|
|
567
|
-
Provide only the corrected code without any explanation.
|
|
568
|
-
"""
|
|
569
|
-
fixed_response = ai_instance.chat(general_context)
|
|
570
|
-
fixed_code = self._extract_code_from_response(fixed_response)
|
|
571
|
-
|
|
572
|
-
if not fixed_code:
|
|
573
|
-
break
|
|
574
|
-
|
|
575
|
-
if self._is_similar_solution(fixed_code):
|
|
576
|
-
# If solution is too similar, try a different approach
|
|
577
|
-
different_context = f"""
|
|
578
|
-
Previous solutions were not successful. The code failed with error: {str(e)}
|
|
579
|
-
|
|
580
|
-
Original Code:
|
|
581
|
-
```python
|
|
582
|
-
{code}
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
Please provide a significantly different approach to solve this problem. Consider:
|
|
586
|
-
1. Using alternative libraries or methods
|
|
587
|
-
2. Implementing a different algorithm
|
|
588
|
-
3. Adding more robust error handling
|
|
589
|
-
4. Using a different encoding or data handling approach
|
|
590
|
-
5. Specifically address the error: {str(e)}
|
|
591
|
-
|
|
592
|
-
Provide only the corrected code without any explanation.
|
|
593
|
-
"""
|
|
594
|
-
fixed_response = ai_instance.chat(different_context)
|
|
595
|
-
fixed_code = self._extract_code_from_response(fixed_response)
|
|
596
|
-
|
|
597
|
-
if self._is_similar_solution(fixed_code):
|
|
598
|
-
break
|
|
599
|
-
|
|
600
|
-
# Update code and continue with retry
|
|
601
|
-
code = fixed_code
|
|
602
|
-
self.tried_solutions.add(code)
|
|
603
|
-
retries += 1
|
|
604
|
-
continue
|
|
605
|
-
|
|
606
|
-
except Exception as ai_error:
|
|
607
|
-
console.print(f"Error during AI correction: {str(ai_error)}", style="error")
|
|
608
|
-
break
|
|
609
|
-
break
|
|
610
|
-
|
|
611
|
-
return str(last_error) if last_error else "Unknown error occurred"
|
|
612
|
-
|
|
613
|
-
def execute(self, prompt: str, ai_instance=None) -> bool:
|
|
614
|
-
"""Execute the given prompt using the appropriate executor.
|
|
615
|
-
|
|
616
|
-
Args:
|
|
617
|
-
prompt (str): Prompt to execute
|
|
618
|
-
ai_instance: Optional AI instance for error correction
|
|
619
|
-
|
|
620
|
-
Returns:
|
|
621
|
-
bool: True if execution was successful, False otherwise
|
|
622
|
-
"""
|
|
623
|
-
try:
|
|
624
|
-
# Check if this is a direct shell command (starts with !)
|
|
625
|
-
if prompt.strip().startswith('!'):
|
|
626
|
-
# Handle shell command
|
|
627
|
-
cmd = prompt.strip()[1:].strip() # Remove the '!' and any leading whitespace
|
|
628
|
-
|
|
629
|
-
# Display the shell command in Jupyter-style UI
|
|
630
|
-
if self.prettify:
|
|
631
|
-
syntax = Syntax(f"!{cmd}", "bash", theme="monokai", line_numbers=True)
|
|
632
|
-
console.print(Panel(
|
|
633
|
-
syntax,
|
|
634
|
-
title="[bold blue]In [1]:[/bold blue]",
|
|
635
|
-
border_style="blue",
|
|
636
|
-
expand=True,
|
|
637
|
-
box=ROUNDED
|
|
638
|
-
))
|
|
639
|
-
|
|
640
|
-
success, result = run_system_command(cmd)
|
|
641
|
-
|
|
642
|
-
if success:
|
|
643
|
-
if result.stdout:
|
|
644
|
-
# Display the output in Jupyter-style UI
|
|
645
|
-
if self.prettify:
|
|
646
|
-
console.print(Panel(
|
|
647
|
-
result.stdout,
|
|
648
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
649
|
-
border_style="red",
|
|
650
|
-
expand=True,
|
|
651
|
-
padding=(0, 1),
|
|
652
|
-
box=ROUNDED
|
|
653
|
-
))
|
|
654
|
-
else:
|
|
655
|
-
console.print(result.stdout, style="output")
|
|
656
|
-
self.last_execution_result = result.stdout
|
|
657
|
-
return True
|
|
658
|
-
else:
|
|
659
|
-
error_msg = result.stderr if result.stderr else f"Command failed: {cmd}"
|
|
660
|
-
# Display the error in Jupyter-style UI
|
|
661
|
-
if self.prettify:
|
|
662
|
-
console.print(Panel(
|
|
663
|
-
f"Error: {error_msg}",
|
|
664
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
665
|
-
border_style="red",
|
|
666
|
-
expand=True,
|
|
667
|
-
padding=(0, 1),
|
|
668
|
-
box=ROUNDED
|
|
669
|
-
))
|
|
670
|
-
else:
|
|
671
|
-
console.print(error_msg, style="error")
|
|
672
|
-
return False
|
|
673
|
-
|
|
674
|
-
# Extract code blocks
|
|
675
|
-
code_blocks = self._extract_code_blocks(prompt)
|
|
676
|
-
if not code_blocks:
|
|
677
|
-
console.print("No executable code found in the prompt", style="warning")
|
|
678
|
-
return False
|
|
679
|
-
|
|
680
|
-
# Execute each code block
|
|
681
|
-
overall_success = True
|
|
682
|
-
for code_type, code in code_blocks:
|
|
683
|
-
success, result = self._execute_code_block(code_type, code, ai_instance)
|
|
684
|
-
|
|
685
|
-
if not success:
|
|
686
|
-
console.print(f"Execution failed: {result}", style="error")
|
|
687
|
-
overall_success = False
|
|
688
|
-
|
|
689
|
-
return overall_success
|
|
690
|
-
|
|
691
|
-
except Exception as e:
|
|
692
|
-
console.print(f"Error in execution: {str(e)}", style="error")
|
|
693
|
-
return False
|
|
694
|
-
|
|
695
|
-
def _extract_code_from_response(self, response: str) -> str:
|
|
696
|
-
"""Extract code from AI response.
|
|
697
|
-
|
|
698
|
-
Args:
|
|
699
|
-
response (str): AI response containing code blocks
|
|
700
|
-
|
|
701
|
-
Returns:
|
|
702
|
-
str: Extracted code from the first code block
|
|
703
|
-
"""
|
|
704
|
-
if not response:
|
|
705
|
-
return ""
|
|
706
|
-
|
|
707
|
-
# First try to find code blocks with explicit language tags
|
|
708
|
-
code_blocks = self._extract_code_blocks(response)
|
|
709
|
-
if code_blocks:
|
|
710
|
-
# Return the content of the first code block
|
|
711
|
-
return code_blocks[0][1]
|
|
712
|
-
|
|
713
|
-
# If no code blocks found, try to find raw Python code or shell commands
|
|
714
|
-
lines = []
|
|
715
|
-
for line in response.split('\n'):
|
|
716
|
-
line = line.strip()
|
|
717
|
-
if not line:
|
|
718
|
-
continue
|
|
719
|
-
|
|
720
|
-
# Skip markdown headers and other non-code lines
|
|
721
|
-
if line.startswith(('#', '```', '---', '===', '>>>')):
|
|
722
|
-
continue
|
|
723
|
-
|
|
724
|
-
# Skip common non-code lines
|
|
725
|
-
if any(line.startswith(prefix) for prefix in ['Please', 'Here', 'The', 'This', 'You']):
|
|
726
|
-
continue
|
|
727
|
-
|
|
728
|
-
lines.append(line)
|
|
729
|
-
|
|
730
|
-
if lines:
|
|
731
|
-
return '\n'.join(lines)
|
|
732
|
-
|
|
733
|
-
return ""
|
|
734
|
-
|
|
735
|
-
def _get_error_context(self, error: Exception, code: str) -> str:
|
|
736
|
-
"""Create context about the error for AI correction.
|
|
737
|
-
|
|
738
|
-
Args:
|
|
739
|
-
error (Exception): The caught exception
|
|
740
|
-
code (str): The code that caused the error
|
|
741
|
-
|
|
742
|
-
Returns:
|
|
743
|
-
str: Formatted error context for AI
|
|
744
|
-
"""
|
|
745
|
-
error_type = type(error).__name__
|
|
746
|
-
error_msg = str(error)
|
|
747
|
-
|
|
748
|
-
# Get Python version and environment info
|
|
749
|
-
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
750
|
-
platform = sys.platform
|
|
751
|
-
|
|
752
|
-
# Get the line number where the error occurred if available
|
|
753
|
-
import traceback
|
|
754
|
-
tb = traceback.extract_tb(error.__traceback__)
|
|
755
|
-
line_info = ""
|
|
756
|
-
if tb:
|
|
757
|
-
line_info = f"\nError occurred at line {tb[-1].lineno}"
|
|
758
|
-
|
|
759
|
-
return f"""
|
|
760
|
-
The code failed with error:
|
|
761
|
-
Error Type: {error_type}
|
|
762
|
-
Error Message: {error_msg}{line_info}
|
|
763
|
-
|
|
764
|
-
Environment:
|
|
765
|
-
Python Version: {python_version}
|
|
766
|
-
Platform: {platform}
|
|
767
|
-
|
|
768
|
-
Original Code:
|
|
769
|
-
```python
|
|
770
|
-
{code}
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
Please fix the code to handle this error. The solution should:
|
|
774
|
-
1. Address the specific error: {error_msg}
|
|
775
|
-
2. Be compatible with Python {python_version}
|
|
776
|
-
3. Work on {platform}
|
|
777
|
-
4. Include proper error handling
|
|
778
|
-
5. Use appropriate libraries and imports
|
|
779
|
-
|
|
780
|
-
Provide only the corrected code without any explanation.
|
|
781
|
-
"""
|
|
782
|
-
|
|
783
|
-
def _handle_import_error(self, error: ImportError, code: str) -> Optional[str]:
|
|
784
|
-
"""Handle missing package errors by attempting to install them.
|
|
785
|
-
|
|
786
|
-
Args:
|
|
787
|
-
error (ImportError): The import error
|
|
788
|
-
code (str): The code that caused the error
|
|
789
|
-
|
|
790
|
-
Returns:
|
|
791
|
-
Optional[str]: Fixed code or None if installation failed
|
|
792
|
-
"""
|
|
793
|
-
try:
|
|
794
|
-
missing_package = str(error).split("'")[1] if "'" in str(error) else str(error).split("No module named")[1].strip()
|
|
795
|
-
missing_package = missing_package.replace("'", "").strip()
|
|
796
|
-
|
|
797
|
-
console.print(f"Installing missing package: {missing_package}", style="info")
|
|
798
|
-
result = subprocess.run(
|
|
799
|
-
[sys.executable, "-m", "pip", "install", missing_package],
|
|
800
|
-
capture_output=True,
|
|
801
|
-
text=True
|
|
802
|
-
)
|
|
803
|
-
if result.returncode == 0:
|
|
804
|
-
console.print(f"Successfully installed {missing_package}", style="success")
|
|
805
|
-
return code # Retry with same code after installing package
|
|
806
|
-
else:
|
|
807
|
-
raise Exception(f"Failed to install {missing_package}: {result.stderr}")
|
|
808
|
-
except Exception as e:
|
|
809
|
-
console.print(f"Error installing package: {str(e)}", style="error")
|
|
810
|
-
return None
|
|
811
|
-
|
|
812
|
-
def _is_similar_solution(self, new_code: str, threshold: float = 0.8) -> bool:
|
|
813
|
-
"""Check if the new solution is too similar to previously tried ones.
|
|
814
|
-
|
|
815
|
-
Args:
|
|
816
|
-
new_code (str): New solution to check
|
|
817
|
-
threshold (float): Similarity threshold (0-1). Defaults to 0.8.
|
|
818
|
-
|
|
819
|
-
Returns:
|
|
820
|
-
bool: True if solution is too similar to previous attempts
|
|
821
|
-
"""
|
|
822
|
-
import difflib
|
|
823
|
-
|
|
824
|
-
def normalize_code(code: str) -> str:
|
|
825
|
-
lines = [line.split('#')[0].strip() for line in code.split('\n')]
|
|
826
|
-
return '\n'.join(line for line in lines if line)
|
|
827
|
-
|
|
828
|
-
new_code_norm = normalize_code(new_code)
|
|
829
|
-
|
|
830
|
-
for tried_code in self.tried_solutions:
|
|
831
|
-
tried_code_norm = normalize_code(tried_code)
|
|
832
|
-
similarity = difflib.SequenceMatcher(None, new_code_norm, tried_code_norm).ratio()
|
|
833
|
-
if similarity > threshold:
|
|
834
|
-
return True
|
|
835
|
-
return False
|
|
836
|
-
|
|
837
|
-
def main(self, response: str) -> Optional[str]:
|
|
838
|
-
"""Execute code with error correction.
|
|
839
|
-
|
|
840
|
-
Args:
|
|
841
|
-
response (str): AI response containing code
|
|
842
|
-
|
|
843
|
-
Returns:
|
|
844
|
-
Optional[str]: Error message if execution failed, None if successful
|
|
845
|
-
"""
|
|
846
|
-
if not response:
|
|
847
|
-
return "No response provided"
|
|
848
|
-
|
|
849
|
-
# Check if this is a shell command (starts with !)
|
|
850
|
-
if response.strip().startswith('!'):
|
|
851
|
-
# Handle shell command
|
|
852
|
-
cmd = response.strip()[1:].strip() # Remove the '!' and any leading whitespace
|
|
853
|
-
|
|
854
|
-
# Display the shell command in Jupyter-style UI
|
|
855
|
-
if self.prettify:
|
|
856
|
-
syntax = Syntax(f"!{cmd}", "bash", theme="monokai", line_numbers=True)
|
|
857
|
-
console.print(Panel(
|
|
858
|
-
syntax,
|
|
859
|
-
title="[bold blue]In [1]:[/bold blue]",
|
|
860
|
-
border_style="blue",
|
|
861
|
-
expand=True,
|
|
862
|
-
box=ROUNDED
|
|
863
|
-
))
|
|
864
|
-
|
|
865
|
-
success, result = run_system_command(cmd)
|
|
866
|
-
|
|
867
|
-
if success:
|
|
868
|
-
if result.stdout:
|
|
869
|
-
# Display the output in Jupyter-style UI
|
|
870
|
-
if self.prettify:
|
|
871
|
-
console.print(Panel(
|
|
872
|
-
result.stdout,
|
|
873
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
874
|
-
border_style="red",
|
|
875
|
-
expand=True,
|
|
876
|
-
padding=(0, 1),
|
|
877
|
-
box=ROUNDED
|
|
878
|
-
))
|
|
879
|
-
self.last_execution_result = result.stdout
|
|
880
|
-
return None
|
|
881
|
-
else:
|
|
882
|
-
error_msg = result.stderr if result.stderr else f"Command failed: {cmd}"
|
|
883
|
-
# Display the error in Jupyter-style UI
|
|
884
|
-
if self.prettify:
|
|
885
|
-
console.print(Panel(
|
|
886
|
-
f"Error: {error_msg}",
|
|
887
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
888
|
-
border_style="red",
|
|
889
|
-
expand=True,
|
|
890
|
-
padding=(0, 1),
|
|
891
|
-
box=ROUNDED
|
|
892
|
-
))
|
|
893
|
-
else:
|
|
894
|
-
console.print(error_msg, style="error")
|
|
895
|
-
return error_msg
|
|
896
|
-
|
|
897
|
-
# Extract code blocks
|
|
898
|
-
code_blocks = self._extract_code_blocks(response)
|
|
899
|
-
if code_blocks:
|
|
900
|
-
code_type, code = code_blocks[0]
|
|
901
|
-
|
|
902
|
-
# Handle shell commands
|
|
903
|
-
if code_type == 'shell':
|
|
904
|
-
success, result = self._execute_code_block(code_type, code)
|
|
905
|
-
if success:
|
|
906
|
-
return None
|
|
907
|
-
else:
|
|
908
|
-
# Error is already displayed in _execute_code_block
|
|
909
|
-
return result
|
|
910
|
-
|
|
911
|
-
# Handle regular Python code
|
|
912
|
-
code = self._extract_code_from_response(response)
|
|
913
|
-
if not code:
|
|
914
|
-
return "No executable code found in the response"
|
|
915
|
-
|
|
916
|
-
ai_instance = self.ai_instance or globals().get('ai')
|
|
917
|
-
|
|
918
|
-
if not ai_instance:
|
|
919
|
-
console.print("AI instance not found, error correction disabled", style="warning")
|
|
920
|
-
try:
|
|
921
|
-
if self.path_to_script:
|
|
922
|
-
script_dir = os.path.dirname(self.path_to_script)
|
|
923
|
-
if script_dir:
|
|
924
|
-
os.makedirs(script_dir, exist_ok=True)
|
|
925
|
-
with open(self.path_to_script, "w", encoding="utf-8") as f:
|
|
926
|
-
f.write(code)
|
|
927
|
-
|
|
928
|
-
if self.internal_exec:
|
|
929
|
-
console.print("[INFO] Executing code internally", style="info")
|
|
930
|
-
# Create a local namespace
|
|
931
|
-
local_namespace: Dict[str, Any] = {}
|
|
932
|
-
|
|
933
|
-
# Capture stdout
|
|
934
|
-
import io
|
|
935
|
-
old_stdout = sys.stdout
|
|
936
|
-
captured_output = io.StringIO()
|
|
937
|
-
sys.stdout = captured_output
|
|
938
|
-
|
|
939
|
-
# Execute the code
|
|
940
|
-
try:
|
|
941
|
-
exec(code, globals(), local_namespace)
|
|
942
|
-
# Capture the result
|
|
943
|
-
self.last_execution_result = captured_output.getvalue()
|
|
944
|
-
finally:
|
|
945
|
-
# Restore stdout
|
|
946
|
-
sys.stdout = old_stdout
|
|
947
|
-
else:
|
|
948
|
-
console.print("[INFO] Executing code as external process", style="info")
|
|
949
|
-
result = subprocess.run(
|
|
950
|
-
[self.interpreter, self.path_to_script],
|
|
951
|
-
capture_output=True,
|
|
952
|
-
text=True
|
|
953
|
-
)
|
|
954
|
-
self.last_execution_result = result.stdout
|
|
955
|
-
|
|
956
|
-
if result.returncode != 0:
|
|
957
|
-
raise Exception(result.stderr or result.stdout)
|
|
958
|
-
|
|
959
|
-
return None
|
|
960
|
-
except Exception as e:
|
|
961
|
-
error_msg = f"Execution error: {str(e)}"
|
|
962
|
-
console.print(error_msg, style="error")
|
|
963
|
-
return error_msg
|
|
964
|
-
|
|
965
|
-
result = self._execute_with_retry(code, ai_instance)
|
|
966
|
-
return result
|
|
967
|
-
|
|
968
|
-
@property
|
|
969
|
-
def intro_prompt(self) -> str:
|
|
970
|
-
"""Get the introduction prompt.
|
|
971
|
-
|
|
972
|
-
Returns:
|
|
973
|
-
str: Introduction prompt
|
|
974
|
-
"""
|
|
975
|
-
return get_intro_prompt()
|
|
976
|
-
|
|
977
|
-
def log(self, message: str, category: str = "info"):
|
|
978
|
-
"""RawDog logger
|
|
979
|
-
|
|
980
|
-
Args:
|
|
981
|
-
message (str): Log message
|
|
982
|
-
category (str, optional): Log level. Defaults to 'info'.
|
|
983
|
-
"""
|
|
984
|
-
if self.quiet:
|
|
985
|
-
return
|
|
986
|
-
|
|
987
|
-
message = "[Webscout] - " + message
|
|
988
|
-
if category == "error":
|
|
989
|
-
console.print(f"[ERROR] {message}", style="error")
|
|
990
|
-
else:
|
|
991
|
-
console.print(message, style=category)
|
|
992
|
-
|
|
993
|
-
def stdout(self, message: str, style: str = "info") -> None:
|
|
994
|
-
"""Enhanced stdout with Rich formatting.
|
|
995
|
-
|
|
996
|
-
Args:
|
|
997
|
-
message (str): Text to be printed
|
|
998
|
-
style (str, optional): Style to apply. Defaults to "info".
|
|
999
|
-
"""
|
|
1000
|
-
if not self.prettify:
|
|
1001
|
-
print(message)
|
|
1002
|
-
return
|
|
1003
|
-
|
|
1004
|
-
if message.startswith("```") and message.endswith("```"):
|
|
1005
|
-
# Handle code blocks
|
|
1006
|
-
code = message.strip("`").strip()
|
|
1007
|
-
syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
|
|
1008
|
-
console.print(Panel(syntax, title="Code", border_style="blue"))
|
|
1009
|
-
elif "```python" in message:
|
|
1010
|
-
# Handle markdown code blocks
|
|
1011
|
-
md = Markdown(message)
|
|
1012
|
-
console.print(md)
|
|
1013
|
-
else:
|
|
1014
|
-
# Handle regular text with optional styling
|
|
1015
|
-
console.print(message, style=style)
|
|
1016
|
-
|
|
1017
|
-
def print_code(self, code: str, title: str = "Generated Code") -> None:
|
|
1018
|
-
"""Print code with syntax highlighting and panel.
|
|
1019
|
-
|
|
1020
|
-
Args:
|
|
1021
|
-
code (str): Code to print
|
|
1022
|
-
title (str, optional): Panel title. Defaults to "Generated Code".
|
|
1023
|
-
"""
|
|
1024
|
-
if self.prettify:
|
|
1025
|
-
syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
|
|
1026
|
-
console.print(Panel(
|
|
1027
|
-
syntax,
|
|
1028
|
-
title=f"[bold blue]In [1]:[/bold blue]",
|
|
1029
|
-
border_style="blue",
|
|
1030
|
-
expand=True,
|
|
1031
|
-
box=ROUNDED
|
|
1032
|
-
))
|
|
1033
|
-
else:
|
|
1034
|
-
print(f"\n{title}:")
|
|
1035
|
-
print(code)
|
|
1036
|
-
|
|
1037
|
-
def print_output(self, output: str, style: str = "output") -> None:
|
|
1038
|
-
"""Print command output with optional styling.
|
|
1039
|
-
|
|
1040
|
-
Args:
|
|
1041
|
-
output (str): Output to print
|
|
1042
|
-
style (str, optional): Style to apply. Defaults to "output".
|
|
1043
|
-
"""
|
|
1044
|
-
if self.prettify:
|
|
1045
|
-
# Try to detect if output is Python code
|
|
1046
|
-
try:
|
|
1047
|
-
# If it looks like Python code, syntax highlight it
|
|
1048
|
-
compile(output, '<string>', 'exec')
|
|
1049
|
-
syntax = Syntax(output, "python", theme="monokai", line_numbers=False)
|
|
1050
|
-
formatted_output = syntax
|
|
1051
|
-
except SyntaxError:
|
|
1052
|
-
# If not Python code, treat as plain text
|
|
1053
|
-
formatted_output = output
|
|
1054
|
-
|
|
1055
|
-
# Use the style parameter for the panel border
|
|
1056
|
-
console.print(Panel(
|
|
1057
|
-
formatted_output,
|
|
1058
|
-
title="[bold red]Out [1]:[/bold red]",
|
|
1059
|
-
border_style=style if style != "output" else "red",
|
|
1060
|
-
expand=True,
|
|
1061
|
-
padding=(0, 1),
|
|
1062
|
-
box=ROUNDED
|
|
1063
|
-
))
|
|
1064
|
-
else:
|
|
1065
|
-
print("\nOutput:")
|
|
1066
|
-
print(output)
|
|
1067
|
-
|
|
1068
|
-
def print_error(self, error: str) -> None:
|
|
1069
|
-
"""Print error message with styling.
|
|
1070
|
-
|
|
1071
|
-
Args:
|
|
1072
|
-
error (str): Error message to print
|
|
1073
|
-
"""
|
|
1074
|
-
if self.prettify:
|
|
1075
|
-
console.print(f"\n Error:", style="error bold")
|
|
1076
|
-
console.print(error, style="error")
|
|
1077
|
-
else:
|
|
1078
|
-
print("\nError:")
|
|
1079
|
-
print(error)
|
|
1080
|
-
|
|
1081
|
-
def print_table(self, headers: list, rows: list) -> None:
|
|
1082
|
-
"""Print data in a formatted table.
|
|
1083
|
-
|
|
1084
|
-
Args:
|
|
1085
|
-
headers (list): Table headers
|
|
1086
|
-
rows (list): Table rows
|
|
1087
|
-
"""
|
|
1088
|
-
if not self.prettify:
|
|
1089
|
-
# Simple ASCII table
|
|
1090
|
-
print("\n" + "-" * 80)
|
|
1091
|
-
print("| " + " | ".join(headers) + " |")
|
|
1092
|
-
print("-" * 80)
|
|
1093
|
-
for row in rows:
|
|
1094
|
-
print("| " + " | ".join(str(cell) for cell in row) + " |")
|
|
1095
|
-
print("-" * 80)
|
|
1096
|
-
return
|
|
1097
|
-
|
|
1098
|
-
table = Table(show_header=True, header_style="bold cyan")
|
|
1099
|
-
for header in headers:
|
|
1100
|
-
table.add_column(header)
|
|
1101
|
-
|
|
1102
|
-
for row in rows:
|
|
1103
|
-
table.add_row(*[str(cell) for cell in row])
|
|
1104
|
-
|
|
1105
|
-
console.print(table)
|