webscout 8.2.7__py3-none-any.whl → 8.2.9__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.
Files changed (281) hide show
  1. webscout/AIauto.py +33 -15
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +703 -250
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/Act.md +309 -0
  6. webscout/Extra/GitToolkit/__init__.py +10 -0
  7. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  8. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  10. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  11. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  12. webscout/Extra/YTToolkit/README.md +375 -0
  13. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  14. webscout/Extra/YTToolkit/__init__.py +3 -0
  15. webscout/Extra/YTToolkit/transcriber.py +476 -0
  16. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  17. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  18. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  19. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  20. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  21. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  22. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  23. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  24. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  25. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  26. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  27. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  28. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  29. webscout/Extra/__init__.py +7 -0
  30. webscout/Extra/autocoder/__init__.py +9 -0
  31. webscout/Extra/autocoder/autocoder.py +1105 -0
  32. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  33. webscout/Extra/gguf.md +430 -0
  34. webscout/Extra/gguf.py +684 -0
  35. webscout/Extra/tempmail/README.md +488 -0
  36. webscout/Extra/tempmail/__init__.py +28 -0
  37. webscout/Extra/tempmail/async_utils.py +141 -0
  38. webscout/Extra/tempmail/base.py +161 -0
  39. webscout/Extra/tempmail/cli.py +187 -0
  40. webscout/Extra/tempmail/emailnator.py +84 -0
  41. webscout/Extra/tempmail/mail_tm.py +361 -0
  42. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  43. webscout/Extra/weather.md +281 -0
  44. webscout/Extra/weather.py +194 -0
  45. webscout/Extra/weather_ascii.py +76 -0
  46. webscout/Litlogger/README.md +10 -0
  47. webscout/Litlogger/__init__.py +15 -0
  48. webscout/Litlogger/formats.py +4 -0
  49. webscout/Litlogger/handlers.py +103 -0
  50. webscout/Litlogger/levels.py +13 -0
  51. webscout/Litlogger/logger.py +92 -0
  52. webscout/Provider/AI21.py +177 -0
  53. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  54. webscout/Provider/AISEARCH/Perplexity.py +333 -0
  55. webscout/Provider/AISEARCH/README.md +279 -0
  56. webscout/Provider/AISEARCH/__init__.py +9 -0
  57. webscout/Provider/AISEARCH/felo_search.py +202 -0
  58. webscout/Provider/AISEARCH/genspark_search.py +324 -0
  59. webscout/Provider/AISEARCH/hika_search.py +186 -0
  60. webscout/Provider/AISEARCH/iask_search.py +410 -0
  61. webscout/Provider/AISEARCH/monica_search.py +220 -0
  62. webscout/Provider/AISEARCH/scira_search.py +298 -0
  63. webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
  64. webscout/Provider/Aitopia.py +316 -0
  65. webscout/Provider/AllenAI.py +440 -0
  66. webscout/Provider/Andi.py +228 -0
  67. webscout/Provider/Blackboxai.py +791 -0
  68. webscout/Provider/ChatGPTClone.py +237 -0
  69. webscout/Provider/ChatGPTGratis.py +194 -0
  70. webscout/Provider/ChatSandbox.py +342 -0
  71. webscout/Provider/Cloudflare.py +324 -0
  72. webscout/Provider/Cohere.py +208 -0
  73. webscout/Provider/Deepinfra.py +340 -0
  74. webscout/Provider/ExaAI.py +261 -0
  75. webscout/Provider/ExaChat.py +358 -0
  76. webscout/Provider/Flowith.py +217 -0
  77. webscout/Provider/FreeGemini.py +250 -0
  78. webscout/Provider/Gemini.py +169 -0
  79. webscout/Provider/GithubChat.py +369 -0
  80. webscout/Provider/GizAI.py +295 -0
  81. webscout/Provider/Glider.py +225 -0
  82. webscout/Provider/Groq.py +801 -0
  83. webscout/Provider/HF_space/__init__.py +0 -0
  84. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  85. webscout/Provider/HeckAI.py +375 -0
  86. webscout/Provider/HuggingFaceChat.py +469 -0
  87. webscout/Provider/Hunyuan.py +283 -0
  88. webscout/Provider/Jadve.py +291 -0
  89. webscout/Provider/Koboldai.py +384 -0
  90. webscout/Provider/LambdaChat.py +411 -0
  91. webscout/Provider/Llama3.py +259 -0
  92. webscout/Provider/MCPCore.py +315 -0
  93. webscout/Provider/Marcus.py +198 -0
  94. webscout/Provider/Nemotron.py +218 -0
  95. webscout/Provider/Netwrck.py +270 -0
  96. webscout/Provider/OLLAMA.py +396 -0
  97. webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
  98. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  99. webscout/Provider/OPENAI/FreeGemini.py +283 -0
  100. webscout/Provider/OPENAI/NEMOTRON.py +232 -0
  101. webscout/Provider/OPENAI/Qwen3.py +283 -0
  102. webscout/Provider/OPENAI/README.md +952 -0
  103. webscout/Provider/OPENAI/TwoAI.py +357 -0
  104. webscout/Provider/OPENAI/__init__.py +40 -0
  105. webscout/Provider/OPENAI/ai4chat.py +293 -0
  106. webscout/Provider/OPENAI/api.py +969 -0
  107. webscout/Provider/OPENAI/base.py +249 -0
  108. webscout/Provider/OPENAI/c4ai.py +373 -0
  109. webscout/Provider/OPENAI/chatgpt.py +556 -0
  110. webscout/Provider/OPENAI/chatgptclone.py +494 -0
  111. webscout/Provider/OPENAI/chatsandbox.py +173 -0
  112. webscout/Provider/OPENAI/copilot.py +242 -0
  113. webscout/Provider/OPENAI/deepinfra.py +322 -0
  114. webscout/Provider/OPENAI/e2b.py +1414 -0
  115. webscout/Provider/OPENAI/exaai.py +417 -0
  116. webscout/Provider/OPENAI/exachat.py +444 -0
  117. webscout/Provider/OPENAI/flowith.py +162 -0
  118. webscout/Provider/OPENAI/freeaichat.py +359 -0
  119. webscout/Provider/OPENAI/glider.py +326 -0
  120. webscout/Provider/OPENAI/groq.py +364 -0
  121. webscout/Provider/OPENAI/heckai.py +308 -0
  122. webscout/Provider/OPENAI/llmchatco.py +335 -0
  123. webscout/Provider/OPENAI/mcpcore.py +389 -0
  124. webscout/Provider/OPENAI/multichat.py +376 -0
  125. webscout/Provider/OPENAI/netwrck.py +357 -0
  126. webscout/Provider/OPENAI/oivscode.py +287 -0
  127. webscout/Provider/OPENAI/opkfc.py +496 -0
  128. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  129. webscout/Provider/OPENAI/scirachat.py +477 -0
  130. webscout/Provider/OPENAI/sonus.py +304 -0
  131. webscout/Provider/OPENAI/standardinput.py +433 -0
  132. webscout/Provider/OPENAI/textpollinations.py +339 -0
  133. webscout/Provider/OPENAI/toolbaz.py +413 -0
  134. webscout/Provider/OPENAI/typefully.py +355 -0
  135. webscout/Provider/OPENAI/typegpt.py +364 -0
  136. webscout/Provider/OPENAI/uncovrAI.py +463 -0
  137. webscout/Provider/OPENAI/utils.py +318 -0
  138. webscout/Provider/OPENAI/venice.py +431 -0
  139. webscout/Provider/OPENAI/wisecat.py +387 -0
  140. webscout/Provider/OPENAI/writecream.py +163 -0
  141. webscout/Provider/OPENAI/x0gpt.py +365 -0
  142. webscout/Provider/OPENAI/yep.py +382 -0
  143. webscout/Provider/OpenGPT.py +209 -0
  144. webscout/Provider/Openai.py +496 -0
  145. webscout/Provider/PI.py +429 -0
  146. webscout/Provider/Perplexitylabs.py +415 -0
  147. webscout/Provider/QwenLM.py +254 -0
  148. webscout/Provider/Reka.py +214 -0
  149. webscout/Provider/StandardInput.py +290 -0
  150. webscout/Provider/TTI/README.md +82 -0
  151. webscout/Provider/TTI/__init__.py +7 -0
  152. webscout/Provider/TTI/aiarta.py +365 -0
  153. webscout/Provider/TTI/artbit.py +0 -0
  154. webscout/Provider/TTI/base.py +64 -0
  155. webscout/Provider/TTI/fastflux.py +200 -0
  156. webscout/Provider/TTI/magicstudio.py +201 -0
  157. webscout/Provider/TTI/piclumen.py +203 -0
  158. webscout/Provider/TTI/pixelmuse.py +225 -0
  159. webscout/Provider/TTI/pollinations.py +221 -0
  160. webscout/Provider/TTI/utils.py +11 -0
  161. webscout/Provider/TTS/README.md +192 -0
  162. webscout/Provider/TTS/__init__.py +10 -0
  163. webscout/Provider/TTS/base.py +159 -0
  164. webscout/Provider/TTS/deepgram.py +156 -0
  165. webscout/Provider/TTS/elevenlabs.py +111 -0
  166. webscout/Provider/TTS/gesserit.py +128 -0
  167. webscout/Provider/TTS/murfai.py +113 -0
  168. webscout/Provider/TTS/openai_fm.py +129 -0
  169. webscout/Provider/TTS/parler.py +111 -0
  170. webscout/Provider/TTS/speechma.py +580 -0
  171. webscout/Provider/TTS/sthir.py +94 -0
  172. webscout/Provider/TTS/streamElements.py +333 -0
  173. webscout/Provider/TTS/utils.py +280 -0
  174. webscout/Provider/TeachAnything.py +229 -0
  175. webscout/Provider/TextPollinationsAI.py +308 -0
  176. webscout/Provider/TwoAI.py +475 -0
  177. webscout/Provider/TypliAI.py +305 -0
  178. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  179. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  180. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  181. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  182. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  183. webscout/Provider/Venice.py +258 -0
  184. webscout/Provider/VercelAI.py +253 -0
  185. webscout/Provider/WiseCat.py +233 -0
  186. webscout/Provider/WrDoChat.py +370 -0
  187. webscout/Provider/Writecream.py +246 -0
  188. webscout/Provider/WritingMate.py +269 -0
  189. webscout/Provider/__init__.py +174 -0
  190. webscout/Provider/ai4chat.py +174 -0
  191. webscout/Provider/akashgpt.py +335 -0
  192. webscout/Provider/asksteve.py +220 -0
  193. webscout/Provider/cerebras.py +290 -0
  194. webscout/Provider/chatglm.py +215 -0
  195. webscout/Provider/cleeai.py +213 -0
  196. webscout/Provider/copilot.py +425 -0
  197. webscout/Provider/elmo.py +283 -0
  198. webscout/Provider/freeaichat.py +285 -0
  199. webscout/Provider/geminiapi.py +208 -0
  200. webscout/Provider/granite.py +235 -0
  201. webscout/Provider/hermes.py +266 -0
  202. webscout/Provider/julius.py +223 -0
  203. webscout/Provider/koala.py +170 -0
  204. webscout/Provider/learnfastai.py +325 -0
  205. webscout/Provider/llama3mitril.py +215 -0
  206. webscout/Provider/llmchat.py +258 -0
  207. webscout/Provider/llmchatco.py +306 -0
  208. webscout/Provider/lmarena.py +198 -0
  209. webscout/Provider/meta.py +801 -0
  210. webscout/Provider/multichat.py +364 -0
  211. webscout/Provider/oivscode.py +309 -0
  212. webscout/Provider/samurai.py +224 -0
  213. webscout/Provider/scira_chat.py +299 -0
  214. webscout/Provider/scnet.py +243 -0
  215. webscout/Provider/searchchat.py +292 -0
  216. webscout/Provider/sonus.py +258 -0
  217. webscout/Provider/talkai.py +194 -0
  218. webscout/Provider/toolbaz.py +353 -0
  219. webscout/Provider/turboseek.py +266 -0
  220. webscout/Provider/typefully.py +202 -0
  221. webscout/Provider/typegpt.py +289 -0
  222. webscout/Provider/uncovr.py +368 -0
  223. webscout/Provider/x0gpt.py +299 -0
  224. webscout/Provider/yep.py +389 -0
  225. webscout/__init__.py +4 -2
  226. webscout/cli.py +3 -28
  227. webscout/client.py +70 -0
  228. webscout/conversation.py +35 -35
  229. webscout/litagent/Readme.md +276 -0
  230. webscout/litagent/__init__.py +29 -0
  231. webscout/litagent/agent.py +455 -0
  232. webscout/litagent/constants.py +60 -0
  233. webscout/litprinter/__init__.py +59 -0
  234. webscout/optimizers.py +419 -419
  235. webscout/scout/README.md +404 -0
  236. webscout/scout/__init__.py +8 -0
  237. webscout/scout/core/__init__.py +7 -0
  238. webscout/scout/core/crawler.py +210 -0
  239. webscout/scout/core/scout.py +607 -0
  240. webscout/scout/core/search_result.py +96 -0
  241. webscout/scout/core/text_analyzer.py +63 -0
  242. webscout/scout/core/text_utils.py +277 -0
  243. webscout/scout/core/web_analyzer.py +52 -0
  244. webscout/scout/element.py +478 -0
  245. webscout/scout/parsers/__init__.py +69 -0
  246. webscout/scout/parsers/html5lib_parser.py +172 -0
  247. webscout/scout/parsers/html_parser.py +236 -0
  248. webscout/scout/parsers/lxml_parser.py +178 -0
  249. webscout/scout/utils.py +37 -0
  250. webscout/swiftcli/Readme.md +323 -0
  251. webscout/swiftcli/__init__.py +95 -0
  252. webscout/swiftcli/core/__init__.py +7 -0
  253. webscout/swiftcli/core/cli.py +297 -0
  254. webscout/swiftcli/core/context.py +104 -0
  255. webscout/swiftcli/core/group.py +241 -0
  256. webscout/swiftcli/decorators/__init__.py +28 -0
  257. webscout/swiftcli/decorators/command.py +221 -0
  258. webscout/swiftcli/decorators/options.py +220 -0
  259. webscout/swiftcli/decorators/output.py +252 -0
  260. webscout/swiftcli/exceptions.py +21 -0
  261. webscout/swiftcli/plugins/__init__.py +9 -0
  262. webscout/swiftcli/plugins/base.py +135 -0
  263. webscout/swiftcli/plugins/manager.py +269 -0
  264. webscout/swiftcli/utils/__init__.py +59 -0
  265. webscout/swiftcli/utils/formatting.py +252 -0
  266. webscout/swiftcli/utils/parsing.py +267 -0
  267. webscout/version.py +1 -1
  268. webscout/webscout_search.py +2 -182
  269. webscout/webscout_search_async.py +1 -179
  270. webscout/zeroart/README.md +89 -0
  271. webscout/zeroart/__init__.py +135 -0
  272. webscout/zeroart/base.py +66 -0
  273. webscout/zeroart/effects.py +101 -0
  274. webscout/zeroart/fonts.py +1239 -0
  275. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
  276. webscout-8.2.9.dist-info/RECORD +289 -0
  277. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
  278. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
  279. webscout-8.2.7.dist-info/RECORD +0 -26
  280. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
  281. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1105 @@
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)