AIencoder commited on
Commit
6c90f41
·
verified ·
1 Parent(s): 46e6ea0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -25
app.py CHANGED
@@ -4,6 +4,7 @@ import gradio as gr
4
  import torch
5
  import time
6
  import sys
 
7
  from io import StringIO
8
  import contextlib
9
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
@@ -49,6 +50,115 @@ class FileSystem:
49
 
50
  fs = FileSystem()
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  # --- Model Loading ---
53
  @lru_cache(maxsize=1)
54
  def load_model():
@@ -80,24 +190,54 @@ def load_model():
80
  # --- Logic Functions ---
81
 
82
  def run_code(code: str) -> Tuple[str, str]:
83
- """Actually executes Python code and captures stdout."""
84
- start_time = time.time()
85
- output_buffer = StringIO()
86
 
87
- try:
88
- # Capture standard output
89
- with contextlib.redirect_stdout(output_buffer):
90
- # dicts for globals/locals to keep environment somewhat contained
91
- exec(code, {}, {})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- exec_time = time.time() - start_time
94
- result = output_buffer.getvalue()
95
- if not result:
96
- result = "(No output)"
97
 
98
- return f"{result}\n\n[Execution time: {exec_time:.4f}s]", ""
99
- except Exception as e:
100
- return output_buffer.getvalue(), f"Traceback (most recent call last):\n{str(e)}"
 
 
 
 
 
 
 
 
101
 
102
  def generate_completion(code: str) -> str:
103
  model = load_model()
@@ -149,7 +289,6 @@ def create_diff_view(original: str, modified: str) -> str:
149
 
150
  # --- Gradio UI ---
151
 
152
- # Theme & CSS Definition
153
  theme = gr.themes.Default(
154
  font=[gr.themes.GoogleFont('JetBrains Mono'), 'monospace'],
155
  primary_hue="blue",
@@ -162,15 +301,14 @@ theme = gr.themes.Default(
162
  )
163
 
164
  css = """
165
- /* Force square corners */
166
  * { border-radius: 0 !important; }
167
  .editor-container { height: 50vh; }
168
  .terminal { height: 15vh; background-color: #1e1e1e; color: #d4d4d4; font-family: 'JetBrains Mono', monospace; }
169
  .diff-view { height: 40vh; }
170
  .code-wrap { white-space: pre-wrap !important; }
 
171
  """
172
 
173
- # FIX: Removed theme/css from Blocks constructor
174
  with gr.Blocks(title="Axon Pro - Python IDE") as demo:
175
 
176
  gr.Markdown("# 🐍 Axon Pro — Python AI IDE")
@@ -213,7 +351,7 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
213
  with gr.Tabs():
214
  with gr.Tab("Terminal", id="terminal-tab"):
215
  terminal = gr.Textbox(
216
- value=">>> Ready to run Python code...",
217
  lines=6,
218
  interactive=False,
219
  elem_classes="terminal",
@@ -227,13 +365,42 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
227
  chat_input = gr.Textbox(placeholder="Ask to generate Python code...", scale=7, container=False)
228
  send_btn = gr.Button("Generate", variant="primary", scale=1)
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  with gr.Tab("Diff View", id="diff-tab"):
231
  diff_view = gr.Code(label="AI Changes", language="python", elem_classes="diff-view code-wrap", interactive=False)
232
  with gr.Row():
233
  apply_btn = gr.Button("Apply Changes", variant="primary")
234
  discard_btn = gr.Button("Discard Changes", variant="secondary")
235
 
236
- status_bar = gr.Markdown(f"**AXON PRO v1.0** | Python 3.x | CPU Mode | TinyLlama-1.1B", elem_classes="status-bar")
237
 
238
  # State
239
  current_file_state = gr.State(fs.current_file)
@@ -241,7 +408,7 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
241
  diff_modified_state = gr.State("")
242
  diff_mode_state = gr.State(False)
243
 
244
- # Functions
245
  def update_file(content):
246
  fs.save_file(content)
247
  return fs.get_current_file_content()
@@ -286,7 +453,26 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
286
  new_hist = history + [[prompt, "Generated code available in Diff View"]]
287
  return diff, "", generated, True, new_hist, ""
288
 
289
- # Wiring
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  editor.change(update_file, editor, None)
291
  file_list.change(load_file, file_list, [editor, current_file_state])
292
  new_file_btn.click(create_file, new_file_name, [file_list, editor])
@@ -295,7 +481,6 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
295
  complete_btn.click(complete_wrapper, editor, editor)
296
  clear_btn.click(lambda: ">>> Ready...", None, terminal)
297
 
298
- # Tabs logic
299
  explain_btn.click(
300
  explain_wrapper, editor,
301
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
@@ -318,13 +503,18 @@ with gr.Blocks(title="Axon Pro - Python IDE") as demo:
318
  discard_btn.click(
319
  lambda: (gr.update(), False), None, [editor, diff_mode_state]
320
  ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)
 
 
 
 
 
 
321
 
322
  if __name__ == "__main__":
323
- # FIX: Passed theme and css here instead of Blocks()
324
  demo.launch(
325
  server_name="0.0.0.0",
326
  server_port=7860,
327
  theme=theme,
328
  css=css,
329
- ssr_mode=False # Disables SSR to prevent experimental warnings/errors
330
  )
 
4
  import torch
5
  import time
6
  import sys
7
+ import subprocess
8
  from io import StringIO
9
  import contextlib
10
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
 
50
 
51
  fs = FileSystem()
52
 
53
+ # --- Package Manager ---
54
+ class PackageManager:
55
+ """Handles pip install/uninstall operations for the HF Space runtime."""
56
+
57
+ @staticmethod
58
+ def install_package(package_name: str) -> str:
59
+ """Install a package using pip at runtime."""
60
+ if not package_name or not package_name.strip():
61
+ return "Error: No package name provided."
62
+
63
+ package_name = package_name.strip()
64
+
65
+ # Basic safety check - block obviously dangerous inputs
66
+ dangerous = [";", "&&", "||", "|", ">", "<", "`", "$", "(", ")"]
67
+ if any(ch in package_name for ch in dangerous):
68
+ return "Error: Invalid characters in package name."
69
+
70
+ try:
71
+ result = subprocess.run(
72
+ [sys.executable, "-m", "pip", "install", package_name],
73
+ capture_output=True,
74
+ text=True,
75
+ timeout=120 # 2 min timeout for large packages
76
+ )
77
+
78
+ output = ""
79
+ if result.stdout:
80
+ # Filter to show only the important lines
81
+ lines = result.stdout.strip().split("\n")
82
+ important = [l for l in lines if any(kw in l.lower() for kw in
83
+ ["successfully", "already satisfied", "installing", "requirement", "downloading", "error", "warning"])]
84
+ output = "\n".join(important) if important else lines[-1]
85
+
86
+ if result.returncode != 0:
87
+ error_msg = result.stderr.strip() if result.stderr else "Unknown error"
88
+ # Show last few lines of error (most relevant)
89
+ error_lines = error_msg.split("\n")[-5:]
90
+ return f"pip install {package_name} failed:\n" + "\n".join(error_lines)
91
+
92
+ return f"✅ {output}"
93
+
94
+ except subprocess.TimeoutExpired:
95
+ return f"Error: Installation of '{package_name}' timed out (>120s). Package may be too large."
96
+ except Exception as e:
97
+ return f"Error: {str(e)}"
98
+
99
+ @staticmethod
100
+ def uninstall_package(package_name: str) -> str:
101
+ """Uninstall a package using pip."""
102
+ if not package_name or not package_name.strip():
103
+ return "Error: No package name provided."
104
+
105
+ package_name = package_name.strip()
106
+
107
+ # Protect critical packages from uninstall
108
+ protected = ["gradio", "torch", "transformers", "pip", "setuptools", "wheel"]
109
+ if package_name.lower() in protected:
110
+ return f"Error: '{package_name}' is a protected system package and cannot be uninstalled."
111
+
112
+ try:
113
+ result = subprocess.run(
114
+ [sys.executable, "-m", "pip", "uninstall", "-y", package_name],
115
+ capture_output=True,
116
+ text=True,
117
+ timeout=60
118
+ )
119
+
120
+ if result.returncode != 0:
121
+ return f"Error: {result.stderr.strip()}"
122
+
123
+ return f"✅ Uninstalled '{package_name}'"
124
+
125
+ except Exception as e:
126
+ return f"Error: {str(e)}"
127
+
128
+ @staticmethod
129
+ def list_packages() -> str:
130
+ """List installed packages."""
131
+ try:
132
+ result = subprocess.run(
133
+ [sys.executable, "-m", "pip", "list", "--format=columns"],
134
+ capture_output=True,
135
+ text=True,
136
+ timeout=30
137
+ )
138
+ return result.stdout if result.stdout else "No packages found."
139
+ except Exception as e:
140
+ return f"Error: {str(e)}"
141
+
142
+ @staticmethod
143
+ def search_package_info(package_name: str) -> str:
144
+ """Show info about an installed package."""
145
+ if not package_name or not package_name.strip():
146
+ return "Error: No package name provided."
147
+ try:
148
+ result = subprocess.run(
149
+ [sys.executable, "-m", "pip", "show", package_name.strip()],
150
+ capture_output=True,
151
+ text=True,
152
+ timeout=15
153
+ )
154
+ if result.returncode != 0:
155
+ return f"Package '{package_name.strip()}' is not installed."
156
+ return result.stdout
157
+ except Exception as e:
158
+ return f"Error: {str(e)}"
159
+
160
+ pkg_mgr = PackageManager()
161
+
162
  # --- Model Loading ---
163
  @lru_cache(maxsize=1)
164
  def load_model():
 
190
  # --- Logic Functions ---
191
 
192
  def run_code(code: str) -> Tuple[str, str]:
193
+ """Executes Python code and captures stdout. Supports !pip commands."""
 
 
194
 
195
+ # Check if code contains !pip install lines (Jupyter-style)
196
+ lines = code.strip().split("\n")
197
+ pip_lines = [l for l in lines if l.strip().startswith("!pip ") or l.strip().startswith("!pip3 ")]
198
+ code_lines = [l for l in lines if not l.strip().startswith("!pip ") and not l.strip().startswith("!pip3 ")]
199
+
200
+ output_parts = []
201
+
202
+ # Handle any !pip commands first
203
+ for pip_line in pip_lines:
204
+ cmd = pip_line.strip().lstrip("!") # Remove the "!" prefix
205
+ parts = cmd.split()
206
+ if len(parts) >= 3 and parts[1] == "install":
207
+ pkg = " ".join(parts[2:]) # Support version specifiers like "pygame==2.5.0"
208
+ result = pkg_mgr.install_package(pkg)
209
+ output_parts.append(f">>> {pip_line.strip()}\n{result}")
210
+ elif len(parts) >= 3 and parts[1] == "uninstall":
211
+ pkg = parts[2]
212
+ result = pkg_mgr.uninstall_package(pkg)
213
+ output_parts.append(f">>> {pip_line.strip()}\n{result}")
214
+ elif len(parts) >= 2 and parts[1] == "list":
215
+ result = pkg_mgr.list_packages()
216
+ output_parts.append(f">>> {pip_line.strip()}\n{result}")
217
+ else:
218
+ output_parts.append(f">>> {pip_line.strip()}\nUnsupported pip command. Use: !pip install <pkg>, !pip uninstall <pkg>, !pip list")
219
+
220
+ # Run remaining Python code
221
+ remaining_code = "\n".join(code_lines).strip()
222
+ if remaining_code:
223
+ start_time = time.time()
224
+ output_buffer = StringIO()
225
 
226
+ try:
227
+ with contextlib.redirect_stdout(output_buffer):
228
+ exec(remaining_code, {}, {})
 
229
 
230
+ exec_time = time.time() - start_time
231
+ result = output_buffer.getvalue()
232
+ if not result:
233
+ result = "(No output)"
234
+
235
+ output_parts.append(f"{result}\n[Execution time: {exec_time:.4f}s]")
236
+ except Exception as e:
237
+ output_parts.append(f"{output_buffer.getvalue()}\n=== ERROR ===\nTraceback (most recent call last):\n{str(e)}")
238
+
239
+ combined = "\n\n".join(output_parts)
240
+ return combined, ""
241
 
242
  def generate_completion(code: str) -> str:
243
  model = load_model()
 
289
 
290
  # --- Gradio UI ---
291
 
 
292
  theme = gr.themes.Default(
293
  font=[gr.themes.GoogleFont('JetBrains Mono'), 'monospace'],
294
  primary_hue="blue",
 
301
  )
302
 
303
  css = """
 
304
  * { border-radius: 0 !important; }
305
  .editor-container { height: 50vh; }
306
  .terminal { height: 15vh; background-color: #1e1e1e; color: #d4d4d4; font-family: 'JetBrains Mono', monospace; }
307
  .diff-view { height: 40vh; }
308
  .code-wrap { white-space: pre-wrap !important; }
309
+ .pkg-output { font-family: 'JetBrains Mono', monospace; font-size: 0.85em; }
310
  """
311
 
 
312
  with gr.Blocks(title="Axon Pro - Python IDE") as demo:
313
 
314
  gr.Markdown("# 🐍 Axon Pro — Python AI IDE")
 
351
  with gr.Tabs():
352
  with gr.Tab("Terminal", id="terminal-tab"):
353
  terminal = gr.Textbox(
354
+ value=">>> Ready to run Python code...\n>>> Tip: Use !pip install <package> in your code to install packages",
355
  lines=6,
356
  interactive=False,
357
  elem_classes="terminal",
 
365
  chat_input = gr.Textbox(placeholder="Ask to generate Python code...", scale=7, container=False)
366
  send_btn = gr.Button("Generate", variant="primary", scale=1)
367
 
368
+ # --- Package Manager Tab ---
369
+ with gr.Tab("📦 Packages", id="pkg-tab"):
370
+ gr.Markdown("### Package Manager")
371
+ gr.Markdown("Install, uninstall, and manage Python packages at runtime.")
372
+
373
+ with gr.Row():
374
+ with gr.Column(scale=3):
375
+ pkg_name_input = gr.Textbox(
376
+ placeholder="e.g. pygame, requests, numpy==1.24.0",
377
+ label="Package Name",
378
+ container=True
379
+ )
380
+ with gr.Column(scale=1):
381
+ with gr.Row():
382
+ install_btn = gr.Button("📥 Install", variant="primary")
383
+ uninstall_btn = gr.Button("🗑️ Uninstall", variant="secondary")
384
+
385
+ with gr.Row():
386
+ list_btn = gr.Button("📋 List Installed", variant="secondary")
387
+ info_btn = gr.Button("ℹ️ Package Info", variant="secondary")
388
+
389
+ pkg_output = gr.Textbox(
390
+ value="Package manager ready. Install any pip package here.\n\nYou can also use !pip install <package> directly in the editor.",
391
+ label="Output",
392
+ lines=12,
393
+ interactive=False,
394
+ elem_classes="pkg-output"
395
+ )
396
+
397
  with gr.Tab("Diff View", id="diff-tab"):
398
  diff_view = gr.Code(label="AI Changes", language="python", elem_classes="diff-view code-wrap", interactive=False)
399
  with gr.Row():
400
  apply_btn = gr.Button("Apply Changes", variant="primary")
401
  discard_btn = gr.Button("Discard Changes", variant="secondary")
402
 
403
+ status_bar = gr.Markdown(f"**AXON PRO v1.1** | Python 3.x | CPU Mode | TinyLlama-1.1B | pip enabled", elem_classes="status-bar")
404
 
405
  # State
406
  current_file_state = gr.State(fs.current_file)
 
408
  diff_modified_state = gr.State("")
409
  diff_mode_state = gr.State(False)
410
 
411
+ # --- Editor Functions ---
412
  def update_file(content):
413
  fs.save_file(content)
414
  return fs.get_current_file_content()
 
453
  new_hist = history + [[prompt, "Generated code available in Diff View"]]
454
  return diff, "", generated, True, new_hist, ""
455
 
456
+ # --- Package Manager Functions ---
457
+ def install_wrapper(pkg_name):
458
+ if not pkg_name:
459
+ return "Please enter a package name."
460
+ return pkg_mgr.install_package(pkg_name)
461
+
462
+ def uninstall_wrapper(pkg_name):
463
+ if not pkg_name:
464
+ return "Please enter a package name."
465
+ return pkg_mgr.uninstall_package(pkg_name)
466
+
467
+ def list_wrapper():
468
+ return pkg_mgr.list_packages()
469
+
470
+ def info_wrapper(pkg_name):
471
+ if not pkg_name:
472
+ return "Please enter a package name."
473
+ return pkg_mgr.search_package_info(pkg_name)
474
+
475
+ # --- Wiring: Editor ---
476
  editor.change(update_file, editor, None)
477
  file_list.change(load_file, file_list, [editor, current_file_state])
478
  new_file_btn.click(create_file, new_file_name, [file_list, editor])
 
481
  complete_btn.click(complete_wrapper, editor, editor)
482
  clear_btn.click(lambda: ">>> Ready...", None, terminal)
483
 
 
484
  explain_btn.click(
485
  explain_wrapper, editor,
486
  [diff_view, diff_original_state, diff_modified_state, diff_mode_state]
 
503
  discard_btn.click(
504
  lambda: (gr.update(), False), None, [editor, diff_mode_state]
505
  ).then(lambda: gr.Tabs(selected="editor-tab"), None, main_tabs)
506
+
507
+ # --- Wiring: Package Manager ---
508
+ install_btn.click(install_wrapper, pkg_name_input, pkg_output)
509
+ uninstall_btn.click(uninstall_wrapper, pkg_name_input, pkg_output)
510
+ list_btn.click(list_wrapper, None, pkg_output)
511
+ info_btn.click(info_wrapper, pkg_name_input, pkg_output)
512
 
513
  if __name__ == "__main__":
 
514
  demo.launch(
515
  server_name="0.0.0.0",
516
  server_port=7860,
517
  theme=theme,
518
  css=css,
519
+ ssr_mode=False
520
  )