theguywhosucks commited on
Commit
6ea2073
Β·
verified Β·
1 Parent(s): 818f130

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +486 -266
app.py CHANGED
@@ -6,75 +6,35 @@ import time
6
  from datetime import datetime, timedelta
7
  import threading
8
 
9
- # --- Constants ---
10
  BACKEND_ENGINES = [
11
  "ChocoLaboratory/SANDBOXBACKEND2",
12
  "ChocoLaboratory/SANDBOX_BACKEND"
13
  ]
14
- SESSION_TIME_LIMIT_HOURS = 2
15
 
16
- # --- Sample Code ---
17
- SAMPLE_MAIN_PY = """# Enterprise Python Application
18
- import os
19
- import time
20
- from datetime import datetime
21
-
22
- def main():
23
- print("πŸš€ Sandbox Compute Platform - Initialized")
24
- print(f"πŸ“… Started at: {datetime.now()}")
25
- print("βœ… Environment ready for development")
26
-
27
- # Your application code here
28
- count = 0
29
- while True:
30
- print(f"[{count}] ⏱️ Heartbeat... {datetime.now().strftime('%H:%M:%S')}")
31
- time.sleep(30)
32
- count += 1
33
-
34
- if __name__ == "__main__":
35
- main()
36
- """
37
-
38
- SAMPLE_REQUIREMENTS_TXT = """# Production Dependencies
39
- # Specify exact versions for reproducibility
40
-
41
- # Web framework
42
- flask==2.3.3
43
- requests==2.31.0
44
-
45
- # Data processing
46
- pandas==2.1.1
47
- numpy==1.24.3
48
-
49
- # Utilities
50
- python-dotenv==1.0.0
51
- """
52
-
53
- # --- SandboxManager Class ---
54
  class SandboxManager:
55
- """Manages the lifecycle of a sandboxed environment."""
56
  def __init__(self):
57
  self.client = None
58
  self.current_engine = None
59
  self.is_connected = False
60
  self.sandbox_created = False
61
  self.creation_time = None
62
- self.time_limit = timedelta(hours=SESSION_TIME_LIMIT_HOURS)
63
- self.monitor_thread = None
64
- self.stop_monitor = threading.Event()
65
-
66
  def select_random_engine(self):
67
- """Selects a random backend engine from the available pool."""
68
  self.current_engine = random.choice(BACKEND_ENGINES)
69
  return self.current_engine
70
-
71
- def get_remaining_time_str(self):
72
- """Calculates and formats the remaining time for the sandbox."""
73
  if not self.creation_time:
74
  return "N/A"
75
 
76
  elapsed = datetime.now() - self.creation_time
77
- remaining = self.time_limit - elapsed
78
 
79
  if remaining.total_seconds() <= 0:
80
  return "EXPIRED"
@@ -82,298 +42,558 @@ class SandboxManager:
82
  hours, remainder = divmod(int(remaining.total_seconds()), 3600)
83
  minutes, seconds = divmod(remainder, 60)
84
  return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
85
-
86
- def is_expired(self):
87
- """Checks if the sandbox session has expired."""
88
  if not self.creation_time:
89
  return False
90
- return (datetime.now() - self.creation_time) >= self.time_limit
91
-
 
 
92
  def connect(self):
93
- """Connects to a random backend engine."""
94
- if self.is_connected:
95
- return "βœ… Already connected.", self.current_engine
96
-
97
  engine = self.select_random_engine()
98
  try:
99
  self.client = Client(engine)
100
  self.is_connected = True
101
- return f"βœ… Connection successful to: {engine}", engine
102
  except Exception as e:
103
  self.is_connected = False
104
  self.current_engine = None
105
- return f"❌ Connection failed: {str(e)}", "Connection Failed"
106
-
107
  def launch_sandbox(self, main_py_code, requirements_txt):
108
- """Launches the sandbox environment."""
109
  if self.sandbox_created:
110
- if self.is_expired():
111
- return "⏰ Sandbox expired. Please refresh the page to start a new session.", ""
112
- return f"⚠️ Sandbox already running. Time left: {self.get_remaining_time_str()}", self.get_status()
113
-
 
114
  if not self.is_connected:
115
- return "❌ Not connected to a backend. Please connect first.", ""
116
-
117
  try:
118
  result = self.client.predict(
119
  code=main_py_code,
120
  requirements=requirements_txt,
121
  api_name="/launch_sandbox"
122
  )
 
 
123
  self.sandbox_created = True
124
  self.creation_time = datetime.now()
125
- self.start_monitor_thread()
126
 
127
- remaining = self.get_remaining_time_str()
 
 
 
128
  return (f"πŸš€ Sandbox deployed successfully on {self.current_engine}\n"
129
- f"⏱️ Time Limit: {SESSION_TIME_LIMIT_HOURS} hours | Remaining: {remaining}\n"
130
- f"πŸ“ Response: {result}"), self.get_status()
131
  except Exception as e:
132
  return f"❌ Deployment failed: {str(e)}", ""
133
-
134
  def fetch_logs(self):
135
- """Fetches the latest logs from the sandbox."""
136
- if not self.sandbox_created:
137
- return "[SYSTEM] Sandbox not deployed."
138
- if self.is_expired():
 
139
  return "⏰ Sandbox expired. Logs are no longer available."
140
-
141
  try:
142
  result = self.client.predict(api_name="/fetch_logs")
143
  timestamp = datetime.now().strftime("%H:%M:%S")
144
- remaining = self.get_remaining_time_str()
145
- return f"[{timestamp}] Remaining: {remaining}\nLogs from {self.current_engine}:\n\n{result}"
146
  except Exception as e:
147
  return f"[ERROR] Failed to fetch logs: {str(e)}"
148
-
149
  def kill_sandbox(self):
150
- """Terminates the currently running sandbox."""
151
- if not self.is_connected or not self.sandbox_created:
152
- return "❌ No active sandbox to terminate.", ""
153
-
154
  try:
155
  result = self.client.predict(api_name="/kill_sandbox")
156
  self.sandbox_created = False
157
  self.creation_time = None
158
- self.stop_monitor.set()
159
- return f"πŸ›‘ Sandbox terminated on {self.current_engine}.\n\nResponse: {result}", "TERMINATED"
160
  except Exception as e:
161
- return f"❌ Termination failed: {str(e)}", self.get_status()
162
-
163
  def get_status(self):
164
- """Gets the current status of the sandbox."""
165
  if not self.is_connected:
166
  return "OFFLINE"
167
- if not self.sandbox_created:
168
- return "IDLE"
169
- if self.is_expired():
170
  return "EXPIRED"
171
 
172
  try:
173
  result = self.client.predict(api_name="/status_sandbox")
174
- remaining = self.get_remaining_time_str()
175
- status_text = str(result).lower()
176
- if "running" in status_text:
177
- return f"RUNNING ({remaining}) - {result}"
178
  else:
179
- return f"IDLE ({remaining}) - {result}"
 
180
  except Exception as e:
181
  return f"ERROR - {str(e)}"
182
-
183
- def start_monitor_thread(self):
184
- """Starts a background thread to monitor session expiration."""
185
- self.stop_monitor.clear()
186
  def monitor():
187
- while not self.stop_monitor.is_set() and self.sandbox_created:
188
- if self.is_expired():
189
- print("Sandbox expired. Auto-terminating...")
190
- self.kill_sandbox()
 
 
191
  break
192
- time.sleep(30)
193
-
194
- self.monitor_thread = threading.Thread(target=monitor, daemon=True)
195
- self.monitor_thread.start()
196
-
197
- # --- Gradio UI & Logic ---
198
 
199
- # Initialize manager
200
  sandbox = SandboxManager()
201
 
202
- # UI helper functions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  def load_sample_files():
204
  return SAMPLE_MAIN_PY, SAMPLE_REQUIREMENTS_TXT
205
 
206
  def get_session_info():
207
  if sandbox.sandbox_created:
208
- remaining = sandbox.get_remaining_time_str()
209
- created_at = sandbox.creation_time.strftime("%H:%M:%S")
210
- return f"Created: {created_at} | Remaining: {remaining}"
211
  return "No active sandbox"
212
 
213
- # Modern CSS for a professional look
214
- PROFESSIONAL_CSS = """
215
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
216
- :root {
217
- --primary-500: #3b82f6; --primary-600: #2563eb;
218
- --gray-50: #f9fafb; --gray-100: #f3f4f6; --gray-200: #e5e7eb; --gray-700: #374151; --gray-800: #1f2937; --gray-900: #111827;
219
- --red-500: #ef4444; --red-600: #dc2626;
220
- --green-500: #22c55e;
221
- --amber-500: #f59e0b;
222
- --font-sans: 'Inter', sans-serif; --font-mono: 'JetBrains Mono', monospace;
223
- --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
224
- }
225
- body, .gradio-container { background-color: var(--gray-50); font-family: var(--font-sans) !important; }
226
- .card { background-color: white; border-radius: 0.75rem; border: 1px solid var(--gray-200); box-shadow: var(--shadow-sm); margin-bottom: 1.5rem !important; }
227
- .card-header { padding: 1rem 1.5rem; border-bottom: 1px solid var(--gray-200); font-weight: 600; font-size: 1.125rem; color: var(--gray-800); }
228
- .card-content { padding: 1.5rem; }
229
- .form-label { font-weight: 500; color: var(--gray-700); margin-bottom: 0.5rem; font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; display: block; }
230
- .btn { border-radius: 0.5rem; font-weight: 500; transition: all 0.2s ease; }
231
- .btn-primary { background-color: var(--primary-500); color: white; border: none; }
232
- .btn-primary:hover { background-color: var(--primary-600); transform: translateY(-1px); box-shadow: var(--shadow-md); }
233
- .btn-secondary { background-color: white; color: var(--gray-700); border: 1px solid var(--gray-200); }
234
- .btn-secondary:hover { background-color: var(--gray-50); border-color: var(--gray-300); }
235
- .btn-danger { background-color: var(--red-500); color: white; border: none; }
236
- .btn-danger:hover { background-color: var(--red-600); transform: translateY(-1px); box-shadow: var(--shadow-md); }
237
- .status-badge { text-align: center; padding: 0.5rem 1rem; border-radius: 9999px; font-weight: 500; font-size: 0.875rem; border: 1px solid transparent; }
238
- .status-offline { background-color: #fee2e2; color: #b91c1c; border-color: #fecaca; }
239
- .status-online { background-color: #dcfce7; color: #166534; border-color: #bbf7d0; }
240
- .status-idle { background-color: #feefc3; color: #b45309; border-color: #fde68a; }
241
- .status-running { animation: pulse 2s infinite; background-color: #dbeafe; color: #1e40af; border-color: #bfdbfe; }
242
- @keyframes pulse { 50% { opacity: 0.7; } }
243
- .code-editor, .terminal { font-family: var(--font-mono) !important; background-color: var(--gray-900) !important; color: var(--gray-100) !important; border-radius: 0.5rem !important; border: 1px solid var(--gray-700) !important; }
244
- .header { text-align: center; padding: 2rem 1rem; }
245
- .header-title { font-size: 2.25rem; font-weight: 700; color: var(--gray-800); }
246
- .header-subtitle { font-size: 1rem; color: var(--gray-700); max-width: 600px; margin: 0.5rem auto 0; }
247
- """
248
-
249
- with gr.Blocks(css=PROFESSIONAL_CSS, title="Sandbox Compute Platform") as demo:
250
- # --- Header ---
251
  gr.HTML("""
252
- <div class="header">
253
- <h1 class="header-title">πŸš€ Sandbox Compute Platform</h1>
254
- <p class="header-subtitle">An enterprise-grade cloud development environment with secure, time-limited sessions for rapid prototyping and execution.</p>
 
 
 
255
  </div>
256
  """)
257
 
 
258
  with gr.Row():
259
- # --- Left Column: Configuration & Actions ---
260
- with gr.Column(scale=3):
261
- # Connection Card
262
- with gr.Group(elem_classes="card"):
263
- gr.HTML('<div class="card-header">1. Connection</div>')
264
- with gr.Group(elem_classes="card-content"):
265
- with gr.Row():
266
- connect_btn = gr.Button("πŸ”— Connect to Backend", elem_classes=["btn", "btn-primary"])
267
- load_samples_btn = gr.Button("πŸ“„ Load Templates", elem_classes=["btn", "btn-secondary"])
268
- connection_status = gr.Textbox(value="DISCONNECTED", label="Connection Status", interactive=False, elem_classes=["status-badge", "status-offline"])
269
-
270
- # Environment Card
271
- with gr.Group(elem_classes="card"):
272
- gr.HTML('<div class="card-header">2. Environment Setup</div>')
273
- with gr.Group(elem_classes="card-content"):
274
- with gr.Tabs():
275
- with gr.TabItem("main.py"):
276
- main_py_editor = gr.Code(value=SAMPLE_MAIN_PY, language="python", label="Application Source Code", lines=15, elem_classes="code-editor")
277
- with gr.TabItem("requirements.txt"):
278
- requirements_editor = gr.Code(value=SAMPLE_REQUIREMENTS_TXT, language="plaintext", label="Dependencies", lines=15, elem_classes="code-editor")
279
-
280
- # Deployment Card
281
- with gr.Group(elem_classes="card"):
282
- gr.HTML('<div class="card-header">3. Deployment</div>')
283
- with gr.Group(elem_classes="card-content"):
284
- with gr.Row():
285
- launch_btn = gr.Button("πŸš€ Deploy Sandbox", elem_classes=["btn", "btn-primary"], scale=2)
286
- kill_btn = gr.Button("πŸ›‘ Terminate", elem_classes=["btn", "btn-danger"])
287
- launch_output = gr.Textbox(label="Deployment Output", lines=4, interactive=False, placeholder="Deployment logs will appear here...", elem_classes="terminal")
288
 
289
- # --- Right Column: Monitoring ---
 
290
  with gr.Column(scale=2):
291
- # Status Card
292
- with gr.Group(elem_classes="card"):
293
- gr.HTML('<div class="card-header">System Monitor</div>')
294
- with gr.Group(elem_classes="card-content"):
295
- gr.HTML('<p class="form-label">Instance Status</p>')
296
- status_display = gr.Textbox(value="OFFLINE", interactive=False, show_label=False, elem_classes=["status-badge", "status-offline"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
- gr.HTML('<p class="form-label" style="margin-top: 1.5rem;">Active Engine</p>')
299
- current_engine_display = gr.Textbox(value="None", interactive=False, show_label=False)
 
300
 
301
- gr.HTML('<p class="form-label" style="margin-top: 1.5rem;">Session Info</p>')
302
- session_info = gr.Textbox(value="No active sandbox", interactive=False, show_label=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
- # Logs Card
305
- with gr.Group(elem_classes="card"):
306
- gr.HTML('<div class="card-header">Application Logs</div>')
307
- with gr.Group(elem_classes="card-content"):
308
- fetch_logs_btn = gr.Button("πŸ”„ Refresh Logs", elem_classes=["btn", "btn-secondary"])
309
- logs_display = gr.Textbox(lines=18, interactive=False, placeholder="[SYSTEM] Application logs will appear here...", show_label=False, elem_classes="terminal")
310
-
311
- # --- Event Handlers & Logic ---
 
 
 
 
 
 
312
 
313
- # Connection Logic
314
- def handle_connect():
315
- status_msg, engine_name = sandbox.connect()
316
- if sandbox.is_connected:
317
- return {
318
- connection_status: gr.update(value=status_msg, elem_classes=["status-badge", "status-online"]),
319
- current_engine_display: gr.update(value=engine_name)
320
- }
321
- else:
322
- return {
323
- connection_status: gr.update(value=status_msg, elem_classes=["status-badge", "status-offline"]),
324
- current_engine_display: gr.update(value=engine_name)
325
- }
326
-
327
- connect_btn.click(fn=handle_connect, outputs=[connection_status, current_engine_display])
328
 
329
- # Sample Loading
330
- load_samples_btn.click(fn=load_sample_files, outputs=[main_py_editor, requirements_editor])
331
-
332
- # Launch Logic
333
- def handle_launch(main_py, reqs_txt):
334
- output, status = sandbox.launch_sandbox(main_py, reqs_txt)
335
- return output, status, get_session_info()
336
-
337
  launch_btn.click(
338
- fn=handle_launch,
339
  inputs=[main_py_editor, requirements_editor],
340
- outputs=[launch_output, status_display, session_info]
341
  )
342
-
343
- # Kill Logic
344
- def handle_kill():
345
- output, status = sandbox.kill_sandbox()
346
- return output, status, get_session_info()
347
-
348
- kill_btn.click(fn=handle_kill, outputs=[launch_output, status_display, session_info])
349
-
350
- # Log Fetching
351
- fetch_logs_btn.click(fn=sandbox.fetch_logs, outputs=logs_display)
352
 
353
- # Automatic Refresh Logic
354
- def update_status_and_logs():
355
- status = sandbox.get_status()
356
- logs = sandbox.fetch_logs() if sandbox.sandbox_created else "[SYSTEM] Auto-refresh paused. Deploy a sandbox to see logs."
357
- s_info = get_session_info()
358
-
359
- status_class = "status-offline"
360
- if "RUNNING" in status: status_class = "status-running"
361
- elif "IDLE" in status: status_class = "status-idle"
362
- elif "EXPIRED" in status: status_class = "status-offline"
363
-
364
- return {
365
- status_display: gr.update(value=status, elem_classes=["status-badge", status_class]),
366
- logs_display: logs,
367
- session_info: s_info
368
- }
369
-
370
- demo.load(
371
- fn=update_status_and_logs,
372
- outputs=[status_display, logs_display, session_info],
373
- every=15 # Refresh every 15 seconds
374
  )
375
 
376
- # --- Launch Application ---
377
  if __name__ == "__main__":
378
- demo.launch(server_name="0.0.0.0", server_port=7860, debug=False)
379
-
 
 
 
 
 
6
  from datetime import datetime, timedelta
7
  import threading
8
 
9
+ # Backend engines
10
  BACKEND_ENGINES = [
11
  "ChocoLaboratory/SANDBOXBACKEND2",
12
  "ChocoLaboratory/SANDBOX_BACKEND"
13
  ]
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  class SandboxManager:
 
16
  def __init__(self):
17
  self.client = None
18
  self.current_engine = None
19
  self.is_connected = False
20
  self.sandbox_created = False
21
  self.creation_time = None
22
+ self.time_limit_hours = 2
23
+ self.auto_refresh_thread = None
24
+ self.stop_auto_refresh = False
25
+
26
  def select_random_engine(self):
27
+ """Select a random backend engine"""
28
  self.current_engine = random.choice(BACKEND_ENGINES)
29
  return self.current_engine
30
+
31
+ def get_remaining_time(self):
32
+ """Calculate remaining time for the sandbox"""
33
  if not self.creation_time:
34
  return "N/A"
35
 
36
  elapsed = datetime.now() - self.creation_time
37
+ remaining = timedelta(hours=self.time_limit_hours) - elapsed
38
 
39
  if remaining.total_seconds() <= 0:
40
  return "EXPIRED"
 
42
  hours, remainder = divmod(int(remaining.total_seconds()), 3600)
43
  minutes, seconds = divmod(remainder, 60)
44
  return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
45
+
46
+ def is_sandbox_expired(self):
47
+ """Check if sandbox has exceeded the 2-hour limit"""
48
  if not self.creation_time:
49
  return False
50
+
51
+ elapsed = datetime.now() - self.creation_time
52
+ return elapsed.total_seconds() > (self.time_limit_hours * 3600)
53
+
54
  def connect(self):
55
+ """Connect to a backend engine"""
56
+ if self.sandbox_created:
57
+ return "⚠️ Sandbox already created. You can only create one sandbox per session.", self.current_engine
58
+
59
  engine = self.select_random_engine()
60
  try:
61
  self.client = Client(engine)
62
  self.is_connected = True
63
+ return f"βœ… Connected to: {engine}", engine
64
  except Exception as e:
65
  self.is_connected = False
66
  self.current_engine = None
67
+ return f"❌ Connection failed to {engine}: {str(e)}", "Connection Failed"
68
+
69
  def launch_sandbox(self, main_py_code, requirements_txt):
70
+ """Launch the sandbox (only once per session)"""
71
  if self.sandbox_created:
72
+ remaining = self.get_remaining_time()
73
+ if remaining == "EXPIRED":
74
+ return "⏰ Your sandbox has expired (2-hour limit reached). Please refresh the page to start a new session.", ""
75
+ return f"⚠️ Sandbox already running. Remaining time: {remaining}", self.get_status()
76
+
77
  if not self.is_connected:
78
+ return "❌ Not connected to backend. Please connect first.", ""
79
+
80
  try:
81
  result = self.client.predict(
82
  code=main_py_code,
83
  requirements=requirements_txt,
84
  api_name="/launch_sandbox"
85
  )
86
+
87
+ # Mark sandbox as created and record creation time
88
  self.sandbox_created = True
89
  self.creation_time = datetime.now()
 
90
 
91
+ # Start auto-refresh thread for time monitoring
92
+ self.start_time_monitor()
93
+
94
+ remaining = self.get_remaining_time()
95
  return (f"πŸš€ Sandbox deployed successfully on {self.current_engine}\n"
96
+ f"⏱️ Time limit: 2 hours | Remaining: {remaining}\n\n"
97
+ f"Response: {result}"), self.get_status()
98
  except Exception as e:
99
  return f"❌ Deployment failed: {str(e)}", ""
100
+
101
  def fetch_logs(self):
102
+ """Fetch logs from the sandbox"""
103
+ if not self.is_connected:
104
+ return "❌ Not connected to backend"
105
+
106
+ if self.is_sandbox_expired():
107
  return "⏰ Sandbox expired. Logs are no longer available."
108
+
109
  try:
110
  result = self.client.predict(api_name="/fetch_logs")
111
  timestamp = datetime.now().strftime("%H:%M:%S")
112
+ remaining = self.get_remaining_time()
113
+ return f"[{timestamp}] Remaining time: {remaining}\nLogs from {self.current_engine}:\n\n{result}"
114
  except Exception as e:
115
  return f"[ERROR] Failed to fetch logs: {str(e)}"
116
+
117
  def kill_sandbox(self):
118
+ """Terminate the sandbox"""
119
+ if not self.is_connected:
120
+ return "❌ Not connected to backend", ""
121
+
122
  try:
123
  result = self.client.predict(api_name="/kill_sandbox")
124
  self.sandbox_created = False
125
  self.creation_time = None
126
+ self.stop_auto_refresh = True
127
+ return f"πŸ›‘ Sandbox terminated on {self.current_engine}\n\nResponse: {result}", "TERMINATED"
128
  except Exception as e:
129
+ return f"❌ Termination failed: {str(e)}", ""
130
+
131
  def get_status(self):
132
+ """Get current sandbox status"""
133
  if not self.is_connected:
134
  return "OFFLINE"
135
+
136
+ if self.is_sandbox_expired():
 
137
  return "EXPIRED"
138
 
139
  try:
140
  result = self.client.predict(api_name="/status_sandbox")
141
+ if "running" in str(result).lower():
142
+ remaining = self.get_remaining_time()
143
+ return f"RUNNING ({remaining} left) - {result}"
 
144
  else:
145
+ remaining = self.get_remaining_time()
146
+ return f"IDLE ({remaining} left) - {result}"
147
  except Exception as e:
148
  return f"ERROR - {str(e)}"
149
+
150
+ def start_time_monitor(self):
151
+ """Start monitoring thread for time limits"""
 
152
  def monitor():
153
+ while not self.stop_auto_refresh and self.sandbox_created:
154
+ if self.is_sandbox_expired():
155
+ try:
156
+ self.kill_sandbox()
157
+ except:
158
+ pass
159
  break
160
+ time.sleep(60) # Check every minute
161
+
162
+ self.auto_refresh_thread = threading.Thread(target=monitor, daemon=True)
163
+ self.auto_refresh_thread.start()
 
 
164
 
165
+ # Initialize sandbox manager
166
  sandbox = SandboxManager()
167
 
168
+ # Sample files
169
+ SAMPLE_MAIN_PY = """# Enterprise Python Application
170
+ import os
171
+ import time
172
+ from datetime import datetime
173
+
174
+ def main():
175
+ print("πŸš€ Sandbox Compute Platform - Initialized")
176
+ print(f"πŸ“… Started at: {datetime.now()}")
177
+ print("βœ… Environment ready for development")
178
+
179
+ # Your application code here
180
+ while True:
181
+ print(f"⏱️ Running... {datetime.now().strftime('%H:%M:%S')}")
182
+ time.sleep(30)
183
+
184
+ if __name__ == "__main__":
185
+ main()
186
+ """
187
+
188
+ SAMPLE_REQUIREMENTS_TXT = """# Production Dependencies
189
+ # Specify exact versions for reproducibility
190
+
191
+ # Web framework
192
+ flask==2.3.3
193
+ requests==2.31.0
194
+
195
+ # Data processing
196
+ pandas==2.1.1
197
+ numpy==1.24.3
198
+
199
+ # Utilities
200
+ python-dotenv==1.0.0
201
+ """
202
+
203
+ # Enhanced CSS with modern design
204
+ industrial_css = """
205
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
206
+
207
+ :root {
208
+ --primary: #2563eb;
209
+ --primary-hover: #1d4ed8;
210
+ --secondary: #64748b;
211
+ --success: #10b981;
212
+ --warning: #f59e0b;
213
+ --danger: #ef4444;
214
+ --surface: #ffffff;
215
+ --surface-elevated: #f8fafc;
216
+ --border: #e2e8f0;
217
+ --text: #0f172a;
218
+ --text-muted: #64748b;
219
+ --shadow: rgba(15, 23, 42, 0.1);
220
+ }
221
+
222
+ * {
223
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
224
+ }
225
+
226
+ .gradio-container {
227
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%) !important;
228
+ min-height: 100vh;
229
+ }
230
+
231
+ /* Header */
232
+ .platform-header {
233
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%) !important;
234
+ color: white !important;
235
+ padding: 2rem !important;
236
+ border-radius: 0 0 1rem 1rem !important;
237
+ margin-bottom: 1.5rem !important;
238
+ box-shadow: 0 10px 25px var(--shadow) !important;
239
+ }
240
+
241
+ .platform-title {
242
+ font-size: 2rem !important;
243
+ font-weight: 800 !important;
244
+ margin-bottom: 0.5rem !important;
245
+ }
246
+
247
+ .platform-subtitle {
248
+ font-size: 1rem !important;
249
+ opacity: 0.9 !important;
250
+ font-weight: 400 !important;
251
+ }
252
+
253
+ .breadcrumb {
254
+ background: var(--surface-elevated) !important;
255
+ padding: 0.75rem 1.5rem !important;
256
+ border-radius: 0.75rem !important;
257
+ font-size: 0.875rem !important;
258
+ color: var(--text-muted) !important;
259
+ border: 1px solid var(--border) !important;
260
+ margin-bottom: 1.5rem !important;
261
+ }
262
+
263
+ /* Cards */
264
+ .card {
265
+ background: var(--surface) !important;
266
+ border: 1px solid var(--border) !important;
267
+ border-radius: 1rem !important;
268
+ box-shadow: 0 4px 6px -1px var(--shadow) !important;
269
+ overflow: hidden !important;
270
+ margin-bottom: 1.5rem !important;
271
+ }
272
+
273
+ .card-header {
274
+ background: var(--surface-elevated) !important;
275
+ padding: 1rem 1.5rem !important;
276
+ border-bottom: 1px solid var(--border) !important;
277
+ font-weight: 600 !important;
278
+ font-size: 1.1rem !important;
279
+ display: flex !important;
280
+ align-items: center !important;
281
+ gap: 0.5rem !important;
282
+ }
283
+
284
+ .card-content {
285
+ padding: 1.5rem !important;
286
+ }
287
+
288
+ /* Buttons */
289
+ .btn {
290
+ padding: 0.75rem 1.5rem !important;
291
+ border-radius: 0.5rem !important;
292
+ font-weight: 500 !important;
293
+ font-size: 0.875rem !important;
294
+ transition: all 0.2s ease !important;
295
+ border: none !important;
296
+ }
297
+
298
+ .btn-primary {
299
+ background: linear-gradient(135deg, var(--primary), var(--primary-hover)) !important;
300
+ color: white !important;
301
+ box-shadow: 0 4px 14px 0 rgba(37, 99, 235, 0.25) !important;
302
+ }
303
+
304
+ .btn-primary:hover {
305
+ transform: translateY(-2px) !important;
306
+ box-shadow: 0 6px 20px 0 rgba(37, 99, 235, 0.35) !important;
307
+ }
308
+
309
+ .btn-secondary {
310
+ background: var(--surface) !important;
311
+ color: var(--text) !important;
312
+ border: 1px solid var(--border) !important;
313
+ }
314
+
315
+ .btn-secondary:hover {
316
+ background: var(--surface-elevated) !important;
317
+ border-color: var(--secondary) !important;
318
+ }
319
+
320
+ .btn-danger {
321
+ background: linear-gradient(135deg, var(--danger), #dc2626) !important;
322
+ color: white !important;
323
+ box-shadow: 0 4px 14px 0 rgba(239, 68, 68, 0.25) !important;
324
+ }
325
+
326
+ .btn-danger:hover {
327
+ transform: translateY(-2px) !important;
328
+ box-shadow: 0 6px 20px 0 rgba(239, 68, 68, 0.35) !important;
329
+ }
330
+
331
+ /* Status indicators */
332
+ .status-badge {
333
+ padding: 0.5rem 1rem !important;
334
+ border-radius: 0.5rem !important;
335
+ font-weight: 500 !important;
336
+ font-size: 0.875rem !important;
337
+ text-align: center !important;
338
+ }
339
+
340
+ .status-online {
341
+ background: #d1fae5 !important;
342
+ color: var(--success) !important;
343
+ border: 1px solid #a7f3d0 !important;
344
+ }
345
+
346
+ .status-offline {
347
+ background: #fee2e2 !important;
348
+ color: var(--danger) !important;
349
+ border: 1px solid #fecaca !important;
350
+ }
351
+
352
+ .status-warning {
353
+ background: #fef3c7 !important;
354
+ color: var(--warning) !important;
355
+ border: 1px solid #fed7aa !important;
356
+ }
357
+
358
+ /* Code editors */
359
+ .code-editor {
360
+ font-family: 'JetBrains Mono', monospace !important;
361
+ background: #1e293b !important;
362
+ color: #e2e8f0 !important;
363
+ border-radius: 0.75rem !important;
364
+ border: 1px solid #334155 !important;
365
+ }
366
+
367
+ .requirements-editor {
368
+ font-family: 'JetBrains Mono', monospace !important;
369
+ background: #27272a !important;
370
+ color: #f4f4f5 !important;
371
+ border-radius: 0.75rem !important;
372
+ border: 1px solid #3f3f46 !important;
373
+ }
374
+
375
+ .terminal {
376
+ font-family: 'JetBrains Mono', monospace !important;
377
+ background: #0f172a !important;
378
+ color: #e2e8f0 !important;
379
+ border-radius: 0.75rem !important;
380
+ padding: 1rem !important;
381
+ border: 1px solid #1e293b !important;
382
+ }
383
+
384
+ /* Form labels */
385
+ .form-label {
386
+ font-weight: 600 !important;
387
+ color: var(--text) !important;
388
+ margin-bottom: 0.5rem !important;
389
+ font-size: 0.875rem !important;
390
+ text-transform: uppercase !important;
391
+ letter-spacing: 0.025em !important;
392
+ }
393
+
394
+ /* Monitoring panel */
395
+ .monitor-panel {
396
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important;
397
+ border: 1px solid var(--border) !important;
398
+ border-radius: 1rem !important;
399
+ padding: 1.5rem !important;
400
+ box-shadow: 0 4px 6px -1px var(--shadow) !important;
401
+ }
402
+
403
+ /* Animations */
404
+ @keyframes pulse {
405
+ 0%, 100% { opacity: 1; }
406
+ 50% { opacity: 0.5; }
407
+ }
408
+
409
+ .pulse {
410
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
411
+ }
412
+ """
413
+
414
+ def auto_refresh_status():
415
+ return sandbox.get_status()
416
+
417
+ def auto_refresh_logs():
418
+ return sandbox.fetch_logs()
419
+
420
  def load_sample_files():
421
  return SAMPLE_MAIN_PY, SAMPLE_REQUIREMENTS_TXT
422
 
423
  def get_session_info():
424
  if sandbox.sandbox_created:
425
+ remaining = sandbox.get_remaining_time()
426
+ created = sandbox.creation_time.strftime("%H:%M:%S") if sandbox.creation_time else "N/A"
427
+ return f"Created: {created} | Remaining: {remaining}"
428
  return "No active sandbox"
429
 
430
+ # Build enhanced Gradio interface
431
+ with gr.Blocks(css=industrial_css, title="Sandbox Compute Platform") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  gr.HTML("""
433
+ <div class="platform-header">
434
+ <div class="platform-title">πŸš€ Sandbox Compute Platform</div>
435
+ <div class="platform-subtitle">Enterprise-grade cloud computing with 2-hour session limits</div>
436
+ </div>
437
+ <div class="breadcrumb">
438
+ 🏠 Dashboard β†’ ⚑ Compute Services β†’ πŸ› οΈ Sandbox Management Console
439
  </div>
440
  """)
441
 
442
+ # Session Info Bar
443
  with gr.Row():
444
+ with gr.Column():
445
+ session_info = gr.Textbox(
446
+ value=get_session_info(),
447
+ label="πŸ“Š Session Information",
448
+ interactive=False,
449
+ elem_classes=["status-badge"]
450
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
+ with gr.Row():
453
+ # Main Content
454
  with gr.Column(scale=2):
455
+ # Connection Management
456
+ with gr.Group(elem_classes=["card"]):
457
+ gr.HTML('<div class="card-header">πŸ”Œ Connection Management</div>')
458
+ with gr.Group(elem_classes=["card-content"]):
459
+ gr.HTML('<div class="form-label">Initialize Backend Connection</div>')
460
+ with gr.Row():
461
+ connect_btn = gr.Button("πŸš€ Connect to Backend", elem_classes=["btn-primary"])
462
+ load_samples_btn = gr.Button("πŸ“„ Load Templates", elem_classes=["btn-secondary"])
463
+
464
+ gr.HTML('<div class="form-label">Active Engine</div>')
465
+ current_engine_display = gr.Textbox(
466
+ value="No engine selected",
467
+ interactive=False,
468
+ elem_classes=["status-badge"],
469
+ show_label=False
470
+ )
471
+
472
+ gr.HTML('<div class="form-label">Connection Status</div>')
473
+ connection_status = gr.Textbox(
474
+ value="DISCONNECTED",
475
+ interactive=False,
476
+ elem_classes=["status-offline"],
477
+ show_label=False
478
+ )
479
+
480
+ # Development Environment
481
+ with gr.Group(elem_classes=["card"]):
482
+ gr.HTML('<div class="card-header">πŸ’» Development Environment</div>')
483
+ with gr.Group(elem_classes=["card-content"]):
484
+ with gr.Tab("πŸ“ main.py"):
485
+ gr.HTML('<div class="form-label">Application Source Code</div>')
486
+ main_py_editor = gr.Code(
487
+ value=SAMPLE_MAIN_PY,
488
+ language="python",
489
+ lines=15,
490
+ elem_classes=["code-editor"],
491
+ show_label=False
492
+ )
493
+
494
+ with gr.Tab("πŸ“¦ requirements.txt"):
495
+ gr.HTML('<div class="form-label">Dependency Specifications</div>')
496
+ requirements_editor = gr.Textbox(
497
+ value=SAMPLE_REQUIREMENTS_TXT,
498
+ lines=15,
499
+ elem_classes=["requirements-editor"],
500
+ show_label=False,
501
+ max_lines=20
502
+ )
503
+
504
+ gr.HTML('<hr style="margin: 1.5rem 0; border: 1px solid var(--border);">')
505
 
506
+ with gr.Row():
507
+ launch_btn = gr.Button("πŸš€ Deploy Sandbox", elem_classes=["btn-primary"], size="lg")
508
+ kill_btn = gr.Button("πŸ›‘ Terminate Sandbox", elem_classes=["btn-danger"], size="lg")
509
 
510
+ gr.HTML('<div class="form-label">Deployment Output</div>')
511
+ launch_output = gr.Textbox(
512
+ lines=6,
513
+ interactive=False,
514
+ placeholder="πŸš€ Deployment logs and status information will appear here...",
515
+ elem_classes=["terminal"],
516
+ show_label=False
517
+ )
518
+
519
+ # Monitoring Panel
520
+ with gr.Column(scale=1):
521
+ with gr.Group(elem_classes=["monitor-panel"]):
522
+ gr.HTML('<div class="form-label">πŸ“Š System Monitor</div>')
523
+
524
+ gr.HTML('<div class="form-label">Instance Status</div>')
525
+ status_display = gr.Textbox(
526
+ value="OFFLINE",
527
+ interactive=False,
528
+ elem_classes=["status-offline"],
529
+ show_label=False
530
+ )
531
+
532
+ status_refresh_btn = gr.Button("πŸ”„ Refresh Status", elem_classes=["btn-secondary"], size="sm")
533
+
534
+ gr.HTML('<div class="form-label">⚠️ Session Limits</div>')
535
+ gr.HTML("""
536
+ <div style="background: #fef3c7; padding: 1rem; border-radius: 0.5rem; border: 1px solid #fed7aa;">
537
+ <strong>Time Limit:</strong> 2 hours per sandbox<br>
538
+ <strong>Sessions:</strong> 1 sandbox per browser session<br>
539
+ <strong>Auto-cleanup:</strong> Sandbox terminates when expired
540
+ </div>
541
+ """)
542
+
543
+ # Logs Section
544
+ with gr.Group(elem_classes=["card"]):
545
+ gr.HTML('<div class="card-header">πŸ“‹ System Logs & Monitoring</div>')
546
+ with gr.Group(elem_classes=["card-content"]):
547
+ with gr.Row():
548
+ fetch_logs_btn = gr.Button("πŸ“‹ Fetch Application Logs", elem_classes=["btn-secondary"])
549
+ auto_refresh_btn = gr.Button("πŸ”„ Auto Refresh (30s)", elem_classes=["btn-secondary"])
550
 
551
+ gr.HTML('<div class="form-label">Runtime Logs</div>')
552
+ logs_display = gr.Textbox(
553
+ lines=12,
554
+ interactive=False,
555
+ elem_classes=["terminal"],
556
+ placeholder="[SYSTEM] πŸ“‹ Application logs will appear here...",
557
+ show_label=False
558
+ )
559
+
560
+ # Event Handlers
561
+ connect_btn.click(
562
+ fn=sandbox.connect,
563
+ outputs=[connection_status, current_engine_display]
564
+ )
565
 
566
+ load_samples_btn.click(
567
+ fn=load_sample_files,
568
+ outputs=[main_py_editor, requirements_editor]
569
+ )
 
 
 
 
 
 
 
 
 
 
 
570
 
 
 
 
 
 
 
 
 
571
  launch_btn.click(
572
+ fn=sandbox.launch_sandbox,
573
  inputs=[main_py_editor, requirements_editor],
574
+ outputs=[launch_output, status_display]
575
  )
 
 
 
 
 
 
 
 
 
 
576
 
577
+ kill_btn.click(
578
+ fn=sandbox.kill_sandbox,
579
+ outputs=[launch_output, status_display]
580
+ )
581
+
582
+ fetch_logs_btn.click(
583
+ fn=sandbox.fetch_logs,
584
+ outputs=logs_display
585
+ )
586
+
587
+ status_refresh_btn.click(
588
+ fn=auto_refresh_status,
589
+ outputs=status_display
 
 
 
 
 
 
 
 
590
  )
591
 
592
+ # Launch the application
593
  if __name__ == "__main__":
594
+ demo.launch(
595
+ server_name="0.0.0.0",
596
+ server_port=7860,
597
+ share=False,
598
+ debug=False
599
+ )