theguywhosucks commited on
Commit
c8b55ab
Β·
verified Β·
1 Parent(s): e430628

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +514 -120
app.py CHANGED
@@ -1,7 +1,10 @@
1
  import gradio as gr
2
  from gradio_client import Client
 
3
  import random
 
4
  from datetime import datetime, timedelta
 
5
 
6
  # Backend engines
7
  BACKEND_ENGINES = [
@@ -9,144 +12,403 @@ BACKEND_ENGINES = [
9
  "ChocoLaboratory/SANDBOX_BACKEND"
10
  ]
11
 
12
- # Sandbox Manager
13
  class SandboxManager:
14
  def __init__(self):
15
  self.client = None
16
  self.current_engine = None
17
  self.is_connected = False
18
- self.sandbox_start_time = None
19
- self.sandbox_duration = timedelta(hours=2) # 2-hour limit
 
 
 
20
 
21
  def select_random_engine(self):
22
- if not self.current_engine:
23
- self.current_engine = random.choice(BACKEND_ENGINES)
24
  return self.current_engine
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  def connect(self):
 
 
 
 
27
  engine = self.select_random_engine()
28
  try:
29
  self.client = Client(engine)
30
  self.is_connected = True
31
- return f"βœ“ Connected to: {engine}", engine
32
  except Exception as e:
33
  self.is_connected = False
34
  self.current_engine = None
35
- return f"βœ— Connection failed to {engine}: {str(e)}", "Not connected"
36
-
37
- def check_sandbox_active(self):
38
- if self.sandbox_start_time:
39
- if datetime.now() - self.sandbox_start_time > self.sandbox_duration:
40
- self.sandbox_start_time = None
41
- return False
42
- return True
43
- return False
44
 
45
  def launch_sandbox(self, main_py_code, requirements_txt):
 
 
 
 
 
 
 
46
  if not self.is_connected:
47
- return "βœ— Not connected to backend. Please connect first.", ""
48
- if self.check_sandbox_active():
49
- return "⚠ Sandbox is still active. You cannot deploy another until 2 hours have passed.", self.get_status()
50
  try:
51
  result = self.client.predict(
52
  code=main_py_code,
53
  requirements=requirements_txt,
54
  api_name="/launch_sandbox"
55
  )
56
- self.sandbox_start_time = datetime.now()
57
- return f"βœ“ Sandbox deployed successfully on {self.current_engine}\n\nResponse: {result}", self.get_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  except Exception as e:
59
- return f"βœ— Deployment failed: {str(e)}", ""
60
 
61
  def kill_sandbox(self):
 
62
  if not self.is_connected:
63
- return "βœ— Not connected to backend", ""
64
- if not self.check_sandbox_active():
65
- return "⚠ No active sandbox to terminate.", self.get_status()
66
  try:
67
  result = self.client.predict(api_name="/kill_sandbox")
68
- self.sandbox_start_time = None
69
- return f"βœ“ Sandbox terminated on {self.current_engine}\n\nResponse: {result}", self.get_status()
 
 
70
  except Exception as e:
71
- return f"βœ— Termination failed: {str(e)}", ""
72
-
73
  def get_status(self):
 
74
  if not self.is_connected:
75
  return "OFFLINE"
76
- if self.check_sandbox_active():
77
- remaining = self.sandbox_duration - (datetime.now() - self.sandbox_start_time)
78
- hrs, rem = divmod(remaining.seconds, 3600)
79
- mins, sec = divmod(rem, 60)
80
- return f"RUNNING - {hrs}h {mins}m remaining"
81
- else:
82
- return "IDLE"
83
-
84
- def fetch_logs(self):
85
- if not self.is_connected:
86
- return "βœ— Not connected to backend"
87
  try:
88
- result = self.client.predict(api_name="/fetch_logs")
89
- timestamp = datetime.now().strftime("%H:%M:%S")
90
- return f"[{timestamp}] Logs from {self.current_engine}:\n\n{result}"
 
 
 
 
91
  except Exception as e:
92
- return f"[ERROR] Failed to fetch logs: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  # Initialize sandbox manager
95
  sandbox = SandboxManager()
96
 
97
- # Sample templates
98
- SAMPLE_MAIN_PY = "# Enterprise Python Application\nprint('Sandbox Compute Platform - Ready')"
99
- SAMPLE_REQUIREMENTS_TXT = "# Production dependencies\n# Specify exact versions for reproducibility"
 
 
100
 
101
- # Readable and professional CSS
102
- professional_css = """
103
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
  :root {
106
- --primary-color: #1e40af;
107
- --primary-hover: #2563eb;
108
- --secondary-color: #374151;
109
- --success-color: #059669;
110
- --warning-color: #d97706;
111
- --danger-color: #b91c1c;
112
- --background: #f9fafb;
113
  --surface: #ffffff;
114
- --border: #d1d5db;
115
- --text-primary: #111827;
116
- --text-secondary: #4b5563;
117
- --text-muted: #6b7280;
 
 
 
 
 
118
  }
119
 
120
  .gradio-container {
121
- font-family: 'Inter', sans-serif !important;
122
- background-color: var(--background) !important;
123
- color: var(--text-primary) !important;
124
  min-height: 100vh;
125
  }
126
 
 
127
  .platform-header {
128
- background: #1e40af !important;
129
  color: white !important;
130
- padding: 16px 24px !important;
131
- }
132
-
133
- .platform-title { font-size: 24px; font-weight: 700; }
134
- .platform-subtitle { font-size: 14px; opacity: 0.85; margin-top: 4px; }
135
-
136
- .enterprise-container { background-color: var(--surface); border: 1px solid var(--border); border-radius: 12px; margin: 20px; padding: 16px; }
137
- .container-header { font-weight: 600; font-size: 16px; margin-bottom: 12px; }
138
- .code-editor-professional { font-family: 'JetBrains Mono', monospace; background: #1e1e1e; color: #f9fafb; border-radius: 8px; padding: 8px; }
139
- .requirements-editor { font-family: 'JetBrains Mono', monospace; background: #2d2d2d; color: #f9fafb; border-radius: 8px; padding: 8px; }
140
- .terminal-logs { font-family: 'JetBrains Mono', monospace; background: #0f172a; color: #e2e8f0; border-radius: 8px; padding: 16px; }
141
- .btn-primary { background: var(--primary-color); color: white; border-radius: 8px; padding: 12px; }
142
- .btn-primary:hover { background: var(--primary-hover); }
143
- .btn-secondary { background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); border-radius: 8px; padding: 12px; }
144
- .btn-secondary:hover { background: #f3f4f6; }
145
- .btn-danger { background: var(--danger-color); color: white; border-radius: 8px; padding: 12px; }
146
- .status-online { background: #d1fae5; color: var(--success-color); padding: 16px; border-radius: 8px; text-align: center; }
147
- .status-offline { background: #fee2e2; color: var(--danger-color); padding: 16px; border-radius: 8px; text-align: center; }
148
- .status-warning { background: #fef3c7; color: var(--warning-color); padding: 16px; border-radius: 8px; text-align: center; }
149
- .section-divider { height: 1px; background: var(--border); margin: 24px 0; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  """
151
 
152
  def auto_refresh_status():
@@ -158,48 +420,180 @@ def auto_refresh_logs():
158
  def load_sample_files():
159
  return SAMPLE_MAIN_PY, SAMPLE_REQUIREMENTS_TXT
160
 
161
- # Build Gradio interface
162
- with gr.Blocks(css=professional_css, title="Sandbox Compute Platform") as demo:
163
-
 
 
 
 
 
 
164
  gr.HTML("""
165
  <div class="platform-header">
166
- <div class="platform-title">Sandbox Compute Platform</div>
167
- <div class="platform-subtitle">Enterprise-grade cloud computing infrastructure</div>
 
 
 
168
  </div>
169
  """)
170
-
 
171
  with gr.Row():
172
- with gr.Column(scale=3):
173
- with gr.Group(elem_classes=["enterprise-container"]):
174
- gr.HTML('<div class="container-header">πŸ”§ Infrastructure Management</div>')
175
- connect_btn = gr.Button("Initialize Connection", elem_classes=["btn-primary"])
176
- load_samples_btn = gr.Button("Load Templates", elem_classes=["btn-secondary"])
177
- current_engine_display = gr.Textbox(value="No engine selected", interactive=False, elem_classes=["status-warning"], show_label=False)
178
- connection_status = gr.Textbox(value="DISCONNECTED", interactive=False, elem_classes=["status-offline"], show_label=False)
179
-
180
- with gr.Group(elem_classes=["enterprise-container"]):
181
- gr.HTML('<div class="container-header">πŸ’» Development Environment</div>')
182
- main_py_editor = gr.Code(value=SAMPLE_MAIN_PY, language="python", lines=20, elem_classes=["code-editor-professional"], show_label=False)
183
- requirements_editor = gr.Textbox(value=SAMPLE_REQUIREMENTS_TXT, lines=20, elem_classes=["requirements-editor"], show_label=False)
184
- gr.HTML('<div class="section-divider"></div>')
185
- launch_btn = gr.Button("Deploy Application", elem_classes=["btn-primary"])
186
- kill_btn = gr.Button("Terminate Instance", elem_classes=["btn-danger"])
187
- launch_output = gr.Textbox(lines=6, interactive=False, elem_classes=["terminal-logs"], show_label=False)
188
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  with gr.Column(scale=1):
190
- gr.HTML('<div class="container-header">πŸ“Š System Monitor</div>')
191
- status_display = gr.Textbox(value="OFFLINE", interactive=False, elem_classes=["status-offline"], show_label=False)
192
- status_refresh_btn = gr.Button("Refresh Status", elem_classes=["btn-secondary"])
193
- fetch_logs_btn = gr.Button("Fetch Application Logs", elem_classes=["btn-secondary"])
194
- logs_display = gr.Textbox(lines=14, interactive=False, elem_classes=["terminal-logs"], show_label=False)
195
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  # Event Handlers
197
- connect_btn.click(fn=sandbox.connect, outputs=[connection_status, current_engine_display])
198
- load_samples_btn.click(fn=load_sample_files, outputs=[main_py_editor, requirements_editor])
199
- launch_btn.click(fn=sandbox.launch_sandbox, inputs=[main_py_editor, requirements_editor], outputs=[launch_output, status_display])
200
- kill_btn.click(fn=sandbox.kill_sandbox, outputs=[launch_output, status_display])
201
- status_refresh_btn.click(fn=auto_refresh_status, outputs=status_display)
202
- fetch_logs_btn.click(fn=auto_refresh_logs, outputs=logs_display)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
 
204
  if __name__ == "__main__":
205
- demo.launch(share=True, server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
1
  import gradio as gr
2
  from gradio_client import Client
3
+ import json
4
  import random
5
+ import time
6
  from datetime import datetime, timedelta
7
+ import threading
8
 
9
  # Backend engines
10
  BACKEND_ENGINES = [
 
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"
41
+
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():
 
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
+ )