mic3333 commited on
Commit
c4a9b3e
Β·
verified Β·
1 Parent(s): 390dba3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +515 -140
app.py CHANGED
@@ -1,96 +1,69 @@
1
  #!/usr/bin/env python3
2
  """
3
- LibreChat Pyodide Code Interpreter - Working Plotly Integration
4
  """
5
 
6
  import gradio as gr
7
 
8
  def create_pyodide_interface():
9
- """Create a Gradio interface with working Plotly support"""
10
 
11
  pyodide_html = """
12
  <div id="pyodide-container" style="border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 10px 0;">
13
  <div id="pyodide-status" style="font-weight: bold; padding: 10px; background: #f0f0f0; border-radius: 3px;">
14
- πŸ”„ Loading Pyodide with Plotly...
 
 
 
 
15
  </div>
16
  <div id="pyodide-output" style="display:none; margin-top: 10px;">
17
  <h4>Execution Results:</h4>
18
- <pre id="output-text" style="background: #f8f8f8; padding: 10px; border-radius: 3px; max-height: 200px; overflow-y: auto; white-space: pre-wrap;"></pre>
19
- <div id="plot-container" style="margin-top: 15px;"></div>
20
  </div>
21
  </div>
22
 
23
- <!-- Load Plotly.js -->
24
  <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
25
 
26
  <script>
 
27
  let pyodide = null;
28
  let pyodideReady = false;
29
- let plotCounter = 0;
 
30
 
31
- function updateStatus(message, color = 'black') {
 
32
  const statusDiv = document.getElementById('pyodide-status');
33
  if (statusDiv) {
34
  statusDiv.innerHTML = message;
35
  statusDiv.style.color = color;
36
  }
37
- console.log('Status:', message);
38
  }
39
 
40
- // Custom Plotly renderer for Pyodide
41
- window.renderPlotlyFromPython = function(plotData, plotLayout, plotConfig) {
42
- try {
43
- plotCounter++;
44
- const plotId = 'pyodide-plot-' + plotCounter;
45
- const plotContainer = document.getElementById('plot-container');
46
-
47
- if (!plotContainer) {
48
- console.error('Plot container not found');
49
- return false;
50
- }
51
-
52
- // Create new plot div
53
- const plotDiv = document.createElement('div');
54
- plotDiv.id = plotId;
55
- plotDiv.style.width = '100%';
56
- plotDiv.style.height = '500px';
57
- plotDiv.style.margin = '10px 0';
58
- plotDiv.style.border = '1px solid #ddd';
59
- plotDiv.style.borderRadius = '5px';
60
-
61
- // Add title
62
- const title = document.createElement('h5');
63
- title.textContent = 'πŸ“ˆ Interactive Plotly Chart #' + plotCounter;
64
- title.style.margin = '10px 0 5px 0';
65
-
66
- plotContainer.appendChild(title);
67
- plotContainer.appendChild(plotDiv);
68
-
69
- // Parse data if it's a string
70
- if (typeof plotData === 'string') {
71
- plotData = JSON.parse(plotData);
72
- }
73
- if (typeof plotLayout === 'string') {
74
- plotLayout = JSON.parse(plotLayout);
75
- }
76
- if (typeof plotConfig === 'string') {
77
- plotConfig = JSON.parse(plotConfig);
78
- }
79
-
80
- // Create the plot
81
- Plotly.newPlot(plotId, plotData, plotLayout, plotConfig || {responsive: true});
82
-
83
- console.log('Plotly chart rendered successfully:', plotId);
84
- return true;
85
-
86
- } catch (error) {
87
- console.error('Plotly rendering error:', error);
88
- return false;
89
  }
90
- };
91
 
92
  async function initPyodide() {
 
 
 
 
 
 
93
  try {
 
94
  if (typeof loadPyodide === 'undefined') {
95
  throw new Error('Pyodide CDN not loaded');
96
  }
@@ -98,99 +71,168 @@ def create_pyodide_interface():
98
  throw new Error('Plotly CDN not loaded');
99
  }
100
 
101
- updateStatus('πŸ”„ Loading Pyodide...', 'blue');
 
102
 
103
  pyodide = await loadPyodide({
104
  indexURL: "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/"
105
  });
106
 
107
- updateStatus('πŸ“¦ Installing packages...', 'blue');
 
108
 
109
- // Install packages
110
- await pyodide.loadPackage(['numpy', 'pandas']);
111
 
112
- // Install plotly via micropip
113
- await pyodide.loadPackage(['micropip']);
114
- await pyodide.runPythonAsync(`
115
- import micropip
116
- await micropip.install('plotly')
117
- `);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- updateStatus('πŸ”§ Setting up Plotly integration...', 'blue');
120
 
121
- // Setup Python environment with proper Plotly integration
122
  pyodide.runPython(`
123
  import sys
124
- import json
125
- from js import renderPlotlyFromPython
126
-
127
- print("Setting up Plotly integration...")
128
 
129
- # Global storage
130
- _plots_created = 0
 
131
 
132
- def show_plotly_figure(fig):
133
- """Custom show function that renders plots in the browser"""
134
- global _plots_created
135
  try:
136
- # Convert figure to JSON
137
- fig_json = fig.to_json()
138
- fig_dict = json.loads(fig_json)
139
 
140
- # Extract components
141
- data = json.dumps(fig_dict.get('data', []))
142
- layout = json.dumps(fig_dict.get('layout', {}))
143
- config = json.dumps({'responsive': True, 'displayModeBar': True})
144
-
145
- # Call JavaScript renderer
146
- success = renderPlotlyFromPython(data, layout, config)
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
- if success:
149
- _plots_created += 1
150
- print(f"βœ… Plot #{_plots_created} rendered successfully!")
151
- return True
152
- else:
153
- print("❌ Plot rendering failed")
154
- return False
 
 
 
155
 
156
  except Exception as e:
157
- print(f"❌ Plot error: {e}")
158
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
- # Patch Plotly's show methods
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  try:
162
  import plotly.graph_objects as go
163
  import plotly.express as px
164
 
165
- # Override the show method for graph_objects
166
- original_show = go.Figure.show
167
- def custom_show(self, *args, **kwargs):
168
- return show_plotly_figure(self)
169
- go.Figure.show = custom_show
170
 
171
- print("βœ… Plotly graph_objects patched")
172
-
173
- # Test basic functionality
174
- print("βœ… Plotly integration ready!")
175
- print("Use fig.show() to display interactive plots")
176
 
 
 
177
  except ImportError as e:
178
- print(f"❌ Plotly import failed: {e}")
179
 
180
- # Also set up matplotlib fallback
181
  try:
182
- import matplotlib
183
- matplotlib.use('Agg')
184
- import matplotlib.pyplot as plt
185
- print("βœ… Matplotlib also available")
186
  except ImportError:
187
- print("❌ Matplotlib not available")
 
 
 
 
 
 
188
 
189
- print("πŸŽ‰ Python environment ready!")
190
  `);
191
 
192
  pyodideReady = true;
193
  updateStatus('βœ… Pyodide + Plotly ready!', 'green');
 
194
 
195
  // Show output area
196
  const outputDiv = document.getElementById('pyodide-output');
@@ -198,31 +240,34 @@ print("πŸŽ‰ Python environment ready!")
198
 
199
  const outputText = document.getElementById('output-text');
200
  if (outputText) {
201
- outputText.textContent = 'Pyodide ready with Plotly support! Try the examples.';
202
  }
203
 
204
  } catch (error) {
205
  console.error('Initialization error:', error);
 
206
  updateStatus('❌ Failed: ' + error.message, 'red');
207
  pyodideReady = false;
208
  }
209
  }
210
 
211
  async function executePyodideCode(code) {
 
 
212
  if (!pyodideReady) {
213
- return 'Pyodide not ready. Please wait for green status.';
214
  }
215
 
216
  if (!code || code.trim() === '') {
217
- return 'No code provided.';
218
  }
219
 
220
  try {
221
- updateStatus('▢️ Executing...', 'blue');
 
222
 
223
  // Clear previous plots
224
- const plotContainer = document.getElementById('plot-container');
225
- if (plotContainer) plotContainer.innerHTML = '';
226
 
227
  // Capture stdout
228
  pyodide.runPython(`
@@ -241,53 +286,383 @@ sys.stdout = old_stdout
241
  captured_output.getvalue()
242
  `);
243
 
244
- // Display text output
 
 
 
 
 
 
245
  const outputText = document.getElementById('output-text');
 
 
 
246
  if (outputText) {
247
  let textOutput = stdout || '';
248
  if (result !== undefined && result !== null && result !== '') {
249
  if (textOutput) textOutput += '\\n';
250
  textOutput += 'Return: ' + result;
251
  }
252
- outputText.textContent = textOutput || 'Code executed successfully';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
254
 
255
- updateStatus('βœ… Executed!', 'green');
256
  return stdout || 'Code executed successfully';
257
 
258
  } catch (error) {
259
  console.error('Execution error:', error);
 
260
 
261
  const outputText = document.getElementById('output-text');
262
  if (outputText) {
263
  outputText.textContent = 'Error: ' + error.toString();
264
  }
265
- updateStatus('❌ Error', 'red');
266
  return 'Error: ' + error.toString();
267
  }
268
  }
269
 
270
- // Wait for both CDNs
271
- function waitForReady() {
272
- if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
273
- console.log('Both CDNs loaded, initializing...');
274
- initPyodide();
275
- } else {
276
- console.log('Waiting for CDNs... Pyodide:', typeof loadPyodide !== 'undefined', 'Plotly:', typeof Plotly !== 'undefined');
277
- setTimeout(waitForReady, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
278
  }
 
 
279
  }
280
 
281
- // Start when ready
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  if (document.readyState === 'loading') {
283
- document.addEventListener('DOMContentLoaded', waitForReady);
284
  } else {
285
- waitForReady();
286
  }
287
 
288
  // Global functions
289
  window.executePyodideCode = executePyodideCode;
290
  window.checkPyodideStatus = () => pyodideReady;
 
 
 
 
 
 
291
 
292
  </script>
293
- <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js" async></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ LibreChat Pyodide Code Interpreter - With Plotly Support
4
  """
5
 
6
  import gradio as gr
7
 
8
  def create_pyodide_interface():
9
+ """Create a Gradio interface with Pyodide + Plotly support"""
10
 
11
  pyodide_html = """
12
  <div id="pyodide-container" style="border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 10px 0;">
13
  <div id="pyodide-status" style="font-weight: bold; padding: 10px; background: #f0f0f0; border-radius: 3px;">
14
+ πŸ”„ Loading Pyodide with Plotly... This may take 15-30 seconds.
15
+ </div>
16
+ <div id="debug-info" style="display:none; margin-top: 10px; padding: 10px; background: #fff3cd; border-radius: 3px; font-size: 12px;">
17
+ <strong>Debug Info:</strong>
18
+ <div id="debug-text"></div>
19
  </div>
20
  <div id="pyodide-output" style="display:none; margin-top: 10px;">
21
  <h4>Execution Results:</h4>
22
+ <pre id="output-text" style="background: #f8f8f8; padding: 10px; border-radius: 3px; max-height: 300px; overflow-y: auto; white-space: pre-wrap;"></pre>
23
+ <div id="plot-container" style="text-align: center; margin-top: 10px;"></div>
24
  </div>
25
  </div>
26
 
27
+ <!-- Load Plotly.js first -->
28
  <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
29
 
30
  <script>
31
+ // Global variables
32
  let pyodide = null;
33
  let pyodideReady = false;
34
+ let initializationStarted = false;
35
+ let debugMode = true;
36
 
37
+ function updateStatus(message, color) {
38
+ color = color || 'black';
39
  const statusDiv = document.getElementById('pyodide-status');
40
  if (statusDiv) {
41
  statusDiv.innerHTML = message;
42
  statusDiv.style.color = color;
43
  }
44
+ console.log('Pyodide Status:', message);
45
  }
46
 
47
+ function debugLog(message) {
48
+ if (!debugMode) return;
49
+ console.log('DEBUG:', message);
50
+ const debugDiv = document.getElementById('debug-info');
51
+ const debugText = document.getElementById('debug-text');
52
+ if (debugDiv && debugText) {
53
+ debugDiv.style.display = 'block';
54
+ debugText.innerHTML += '<br>' + new Date().toLocaleTimeString() + ': ' + message;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
+ }
57
 
58
  async function initPyodide() {
59
+ if (initializationStarted) {
60
+ debugLog('Initialization already started');
61
+ return;
62
+ }
63
+ initializationStarted = true;
64
+
65
  try {
66
+ // Check prerequisites
67
  if (typeof loadPyodide === 'undefined') {
68
  throw new Error('Pyodide CDN not loaded');
69
  }
 
71
  throw new Error('Plotly CDN not loaded');
72
  }
73
 
74
+ updateStatus('πŸ”„ Loading Pyodide core...', 'blue');
75
+ debugLog('Starting Pyodide with Plotly support...');
76
 
77
  pyodide = await loadPyodide({
78
  indexURL: "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/"
79
  });
80
 
81
+ debugLog('Pyodide core loaded');
82
+ updateStatus('πŸ“¦ Installing Python packages...', 'blue');
83
 
84
+ // Install plotly and other packages
85
+ const packages = ['numpy', 'pandas', 'matplotlib'];
86
 
87
+ for (const pkg of packages) {
88
+ try {
89
+ debugLog(`Installing ${pkg}...`);
90
+ await pyodide.loadPackage(pkg);
91
+ debugLog(`βœ“ ${pkg} installed`);
92
+ } catch (error) {
93
+ debugLog(`⚠ ${pkg} failed: ${error.message}`);
94
+ }
95
+ }
96
+
97
+ // Install plotly via pip in Pyodide
98
+ updateStatus('πŸ“¦ Installing Plotly via pip...', 'blue');
99
+ try {
100
+ await pyodide.loadPackage(['micropip']);
101
+ await pyodide.runPythonAsync(`
102
+ import micropip
103
+ await micropip.install('plotly')
104
+ `);
105
+ debugLog('βœ“ Plotly installed via micropip');
106
+ } catch (error) {
107
+ debugLog('⚠ Plotly installation failed: ' + error.message);
108
+ }
109
 
110
+ updateStatus('πŸ”§ Setting up plotting environment...', 'blue');
111
 
112
+ // Setup Python environment with Plotly support
113
  pyodide.runPython(`
114
  import sys
115
+ print("Python " + sys.version)
 
 
 
116
 
117
+ # Global storage for plots
118
+ _matplotlib_data = None
119
+ _plotly_data = None
120
 
121
+ def capture_matplotlib():
122
+ global _matplotlib_data
 
123
  try:
124
+ import matplotlib.pyplot as plt
125
+ import io
126
+ import base64
127
 
128
+ if len(plt.get_fignums()) > 0:
129
+ buffer = io.BytesIO()
130
+ plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
131
+ buffer.seek(0)
132
+ plot_data = buffer.getvalue()
133
+ buffer.close()
134
+ _matplotlib_data = base64.b64encode(plot_data).decode()
135
+ plt.close('all')
136
+ return _matplotlib_data
137
+ return None
138
+ except Exception as e:
139
+ print("Matplotlib capture error: " + str(e))
140
+ return None
141
+
142
+ def capture_plotly(fig):
143
+ global _plotly_data
144
+ try:
145
+ # Convert plotly figure to HTML div
146
+ import plotly.offline as pyo
147
+ import plotly.io as pio
148
 
149
+ # Method 1: Try offline plot
150
+ try:
151
+ _plotly_data = pyo.plot(fig, output_type='div', include_plotlyjs=False, div_id='plotly-div')
152
+ print("Plotly captured via offline.plot")
153
+ return _plotly_data
154
+ except:
155
+ # Method 2: Try to_html
156
+ _plotly_data = fig.to_html(include_plotlyjs=False, div_id='plotly-div')
157
+ print("Plotly captured via to_html")
158
+ return _plotly_data
159
 
160
  except Exception as e:
161
+ print("Plotly capture error: " + str(e))
162
+ # Method 3: Fallback to JSON
163
+ try:
164
+ import json
165
+ _plotly_data = json.dumps(fig.to_dict())
166
+ print("Plotly captured as JSON (fallback)")
167
+ return _plotly_data
168
+ except:
169
+ return None
170
+
171
+ def get_matplotlib_data():
172
+ return _matplotlib_data
173
+
174
+ def get_plotly_data():
175
+ return _plotly_data
176
 
177
+ def clear_plot_data():
178
+ global _matplotlib_data, _plotly_data
179
+ _matplotlib_data = None
180
+ _plotly_data = None
181
+
182
+ # Setup matplotlib if available
183
+ try:
184
+ import matplotlib
185
+ matplotlib.use('Agg')
186
+ import matplotlib.pyplot as plt
187
+
188
+ original_show = plt.show
189
+ def custom_show(*args, **kwargs):
190
+ return capture_matplotlib()
191
+ plt.show = custom_show
192
+
193
+ print("βœ… Matplotlib configured")
194
+ except ImportError:
195
+ print("❌ Matplotlib not available")
196
+
197
+ # Setup plotly if available
198
  try:
199
  import plotly.graph_objects as go
200
  import plotly.express as px
201
 
202
+ # Custom show function for Plotly
203
+ def show_plotly(fig):
204
+ return capture_plotly(fig)
 
 
205
 
206
+ # Monkey patch plotly's show
207
+ original_plotly_show = go.Figure.show
208
+ def custom_plotly_show(self, *args, **kwargs):
209
+ return capture_plotly(self)
210
+ go.Figure.show = custom_plotly_show
211
 
212
+ print("βœ… Plotly configured")
213
+ print("Available: plotly.graph_objects as 'go', plotly.express as 'px'")
214
  except ImportError as e:
215
+ print("❌ Plotly not available: " + str(e))
216
 
217
+ # Test basic functionality
218
  try:
219
+ import numpy as np
220
+ print("βœ… NumPy available")
 
 
221
  except ImportError:
222
+ print("❌ NumPy not available")
223
+
224
+ try:
225
+ import pandas as pd
226
+ print("βœ… Pandas available")
227
+ except ImportError:
228
+ print("❌ Pandas not available")
229
 
230
+ print("Environment setup complete!")
231
  `);
232
 
233
  pyodideReady = true;
234
  updateStatus('βœ… Pyodide + Plotly ready!', 'green');
235
+ debugLog('Full initialization complete');
236
 
237
  // Show output area
238
  const outputDiv = document.getElementById('pyodide-output');
 
240
 
241
  const outputText = document.getElementById('output-text');
242
  if (outputText) {
243
+ outputText.textContent = 'Pyodide ready with Plotly support!\\n\\nTry the examples below or write your own code.';
244
  }
245
 
246
  } catch (error) {
247
  console.error('Initialization error:', error);
248
+ debugLog('Init error: ' + error.message);
249
  updateStatus('❌ Failed: ' + error.message, 'red');
250
  pyodideReady = false;
251
  }
252
  }
253
 
254
  async function executePyodideCode(code) {
255
+ debugLog('Execute function called');
256
+
257
  if (!pyodideReady) {
258
+ return 'Pyodide is not ready. Please wait for green status.';
259
  }
260
 
261
  if (!code || code.trim() === '') {
262
+ return 'Error: No code provided.';
263
  }
264
 
265
  try {
266
+ updateStatus('▢️ Executing Python...', 'blue');
267
+ debugLog('Executing: ' + code.substring(0, 50) + '...');
268
 
269
  // Clear previous plots
270
+ pyodide.runPython('clear_plot_data()');
 
271
 
272
  // Capture stdout
273
  pyodide.runPython(`
 
286
  captured_output.getvalue()
287
  `);
288
 
289
+ // Get plot data
290
+ let matplotlibData = pyodide.runPython('get_matplotlib_data()');
291
+ let plotlyData = pyodide.runPython('get_plotly_data()');
292
+
293
+ debugLog('Execution completed');
294
+
295
+ // Display results
296
  const outputText = document.getElementById('output-text');
297
+ const plotContainer = document.getElementById('plot-container');
298
+
299
+ // Handle text output
300
  if (outputText) {
301
  let textOutput = stdout || '';
302
  if (result !== undefined && result !== null && result !== '') {
303
  if (textOutput) textOutput += '\\n';
304
  textOutput += 'Return: ' + result;
305
  }
306
+ outputText.textContent = textOutput || 'Code executed successfully (no text output)';
307
+ }
308
+
309
+ // Handle plots
310
+ let plotHTML = '';
311
+
312
+ if (matplotlibData && matplotlibData.length > 100) {
313
+ plotHTML += `
314
+ <div style="margin: 10px 0;">
315
+ <h5>πŸ“Š Matplotlib Plot:</h5>
316
+ <img src="data:image/png;base64,${matplotlibData}"
317
+ style="max-width: 100%; height: auto; border: 1px solid #ddd;"
318
+ alt="Matplotlib Plot">
319
+ </div>
320
+ `;
321
+ }
322
+
323
+ if (plotlyData && plotlyData.length > 100) {
324
+ // Check if it's JSON (fallback method)
325
+ if (plotlyData.startsWith('{') || plotlyData.startsWith('[')) {
326
+ try {
327
+ const plotData = JSON.parse(plotlyData);
328
+ plotHTML += `
329
+ <div style="margin: 10px 0;">
330
+ <h5>πŸ“ˆ Interactive Plotly Chart:</h5>
331
+ <div id="plotly-chart-${Date.now()}" style="width: 100%; height: 500px; border: 1px solid #ddd;"></div>
332
+ </div>
333
+ `;
334
+ // Render after DOM update
335
+ setTimeout(() => {
336
+ const chartId = document.querySelector('[id^="plotly-chart-"]').id;
337
+ Plotly.newPlot(chartId, plotData.data, plotData.layout);
338
+ }, 100);
339
+ } catch (e) {
340
+ debugLog('JSON plot rendering failed: ' + e.message);
341
+ }
342
+ } else {
343
+ // HTML method
344
+ plotHTML += `
345
+ <div style="margin: 10px 0;">
346
+ <h5>πŸ“ˆ Interactive Plotly Chart:</h5>
347
+ <div style="border: 1px solid #ddd; border-radius: 5px; padding: 10px;">
348
+ ${plotlyData}
349
+ </div>
350
+ </div>
351
+ `;
352
+ }
353
+ }
354
+
355
+ if (plotContainer) {
356
+ plotContainer.innerHTML = plotHTML;
357
+ }
358
+
359
+ if (plotHTML) {
360
+ updateStatus('βœ… Executed with plot(s)!', 'green');
361
+ } else {
362
+ updateStatus('βœ… Executed successfully!', 'green');
363
  }
364
 
 
365
  return stdout || 'Code executed successfully';
366
 
367
  } catch (error) {
368
  console.error('Execution error:', error);
369
+ debugLog('Error: ' + error.message);
370
 
371
  const outputText = document.getElementById('output-text');
372
  if (outputText) {
373
  outputText.textContent = 'Error: ' + error.toString();
374
  }
375
+ updateStatus('❌ Execution failed', 'red');
376
  return 'Error: ' + error.toString();
377
  }
378
  }
379
 
380
+ // Safe initialization with retry
381
+ async function safeInit() {
382
+ let retries = 0;
383
+ const maxRetries = 3;
384
+
385
+ while (retries < maxRetries) {
386
+ try {
387
+ if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
388
+ await initPyodide();
389
+ return;
390
+ }
391
+ } catch (error) {
392
+ debugLog(`Init attempt ${retries + 1} failed: ${error.message}`);
393
+ }
394
+
395
+ retries++;
396
+ if (retries < maxRetries) {
397
+ debugLog(`Retrying in ${retries * 2} seconds...`);
398
+ await new Promise(resolve => setTimeout(resolve, retries * 2000));
399
+ }
400
  }
401
+
402
+ updateStatus('❌ Initialization failed after ' + maxRetries + ' attempts', 'red');
403
  }
404
 
405
+ // Wait for both CDNs to load
406
+ function waitForCDNs() {
407
+ const checkInterval = setInterval(() => {
408
+ if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
409
+ clearInterval(checkInterval);
410
+ debugLog('Both CDNs loaded, starting init');
411
+ safeInit();
412
+ } else {
413
+ debugLog('Waiting for CDNs... Pyodide: ' + (typeof loadPyodide !== 'undefined') + ', Plotly: ' + (typeof Plotly !== 'undefined'));
414
+ }
415
+ }, 1000);
416
+
417
+ // Timeout after 30 seconds
418
+ setTimeout(() => {
419
+ clearInterval(checkInterval);
420
+ if (!pyodideReady) {
421
+ updateStatus('❌ CDN loading timeout', 'red');
422
+ }
423
+ }, 30000);
424
+ }
425
+
426
+ // Start when DOM is ready
427
  if (document.readyState === 'loading') {
428
+ document.addEventListener('DOMContentLoaded', waitForCDNs);
429
  } else {
430
+ waitForCDNs();
431
  }
432
 
433
  // Global functions
434
  window.executePyodideCode = executePyodideCode;
435
  window.checkPyodideStatus = () => pyodideReady;
436
+ window.toggleDebugMode = function() {
437
+ debugMode = !debugMode;
438
+ const debugDiv = document.getElementById('debug-info');
439
+ if (debugDiv) debugDiv.style.display = debugMode ? 'block' : 'none';
440
+ return debugMode;
441
+ };
442
 
443
  </script>
444
+ <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js" async></script>
445
+ """
446
+
447
+ return pyodide_html
448
+
449
+ # Create the Gradio interface
450
+ with gr.Blocks(title="Pyodide + Plotly Code Interpreter") as demo:
451
+ gr.Markdown("# πŸπŸ“ˆ Pyodide + Plotly Code Interpreter")
452
+ gr.Markdown("**Interactive Python with Plotly charts** - runs entirely in your browser!")
453
+
454
+ # Pyodide interface
455
+ pyodide_interface = gr.HTML(create_pyodide_interface())
456
+
457
+ with gr.Row():
458
+ with gr.Column(scale=2):
459
+ code_input = gr.Textbox(
460
+ value="""# Plotly Example 1: Simple Line Chart
461
+ import plotly.graph_objects as go
462
+ import numpy as np
463
+
464
+ # Generate data
465
+ x = np.linspace(0, 10, 100)
466
+ y1 = np.sin(x)
467
+ y2 = np.cos(x)
468
+
469
+ # Create figure
470
+ fig = go.Figure()
471
+ fig.add_trace(go.Scatter(x=x, y=y1, name='sin(x)', line=dict(color='blue')))
472
+ fig.add_trace(go.Scatter(x=x, y=y2, name='cos(x)', line=dict(color='red')))
473
+
474
+ fig.update_layout(
475
+ title='Interactive Sine and Cosine Waves',
476
+ xaxis_title='X values',
477
+ yaxis_title='Y values',
478
+ hovermode='x unified'
479
+ )
480
+
481
+ fig.show()
482
+ print("Interactive Plotly chart created! πŸŽ‰")""",
483
+ lines=18,
484
+ label="Python Code with Plotly"
485
+ )
486
+
487
+ with gr.Row():
488
+ execute_btn = gr.Button("πŸš€ Execute", variant="primary", size="lg")
489
+ examples_btn = gr.Button("πŸ“‹ Load Examples", variant="secondary")
490
+
491
+ with gr.Column(scale=1):
492
+ gr.Markdown("### πŸŽ›οΈ Controls")
493
+
494
+ status_display = gr.Textbox(
495
+ label="Status",
496
+ interactive=False,
497
+ lines=4
498
+ )
499
+
500
+ with gr.Row():
501
+ check_btn = gr.Button("πŸ“Š Status", size="sm")
502
+ debug_btn = gr.Button("πŸ› Debug", size="sm")
503
+
504
+ # Example code snippets
505
+ examples = {
506
+ "Plotly Bar Chart": """import plotly.express as px
507
+ import pandas as pd
508
+
509
+ # Sample data
510
+ data = {
511
+ 'Category': ['A', 'B', 'C', 'D', 'E'],
512
+ 'Values': [23, 45, 56, 78, 32],
513
+ 'Colors': ['red', 'blue', 'green', 'orange', 'purple']
514
+ }
515
+ df = pd.DataFrame(data)
516
+
517
+ # Create bar chart
518
+ fig = px.bar(df, x='Category', y='Values', color='Colors',
519
+ title='Interactive Bar Chart',
520
+ labels={'Values': 'Count'})
521
+
522
+ fig.show()
523
+ print("Bar chart created!")""",
524
+
525
+ "Plotly 3D Scatter": """import plotly.graph_objects as go
526
+ import numpy as np
527
+
528
+ # Generate 3D data
529
+ n = 100
530
+ x = np.random.randn(n)
531
+ y = np.random.randn(n)
532
+ z = np.random.randn(n)
533
+ colors = np.random.randn(n)
534
+
535
+ # Create 3D scatter plot
536
+ fig = go.Figure(data=go.Scatter3d(
537
+ x=x, y=y, z=z,
538
+ mode='markers',
539
+ marker=dict(
540
+ size=8,
541
+ color=colors,
542
+ colorscale='Viridis',
543
+ showscale=True
544
+ )
545
+ ))
546
+
547
+ fig.update_layout(
548
+ title='Interactive 3D Scatter Plot',
549
+ scene=dict(
550
+ xaxis_title='X Axis',
551
+ yaxis_title='Y Axis',
552
+ zaxis_title='Z Axis'
553
+ )
554
+ )
555
+
556
+ fig.show()
557
+ print("3D scatter plot created!")""",
558
+
559
+ "Plotly Dashboard": """import plotly.graph_objects as go
560
+ from plotly.subplots import make_subplots
561
+ import numpy as np
562
+
563
+ # Generate sample data
564
+ x = np.linspace(0, 10, 50)
565
+ y1 = np.sin(x)
566
+ y2 = np.cos(x)
567
+ y3 = np.random.normal(0, 0.1, len(x))
568
+
569
+ # Create subplots
570
+ fig = make_subplots(
571
+ rows=2, cols=2,
572
+ subplot_titles=('Line Plot', 'Histogram', 'Box Plot', 'Heatmap'),
573
+ specs=[[{"secondary_y": True}, {}],
574
+ [{}, {}]]
575
+ )
576
+
577
+ # Add line plot
578
+ fig.add_trace(go.Scatter(x=x, y=y1, name='sin(x)'), row=1, col=1)
579
+ fig.add_trace(go.Scatter(x=x, y=y2, name='cos(x)', yaxis='y2'), row=1, col=1, secondary_y=True)
580
+
581
+ # Add histogram
582
+ fig.add_trace(go.Histogram(x=np.random.normal(0, 1, 1000), name='Normal Dist'), row=1, col=2)
583
+
584
+ # Add box plot
585
+ categories = ['A', 'B', 'C']
586
+ values = [np.random.normal(i, 0.5, 100) for i in range(len(categories))]
587
+ for i, (cat, vals) in enumerate(zip(categories, values)):
588
+ fig.add_trace(go.Box(y=vals, name=cat), row=2, col=1)
589
+
590
+ # Add heatmap
591
+ z = np.random.randn(10, 10)
592
+ fig.add_trace(go.Heatmap(z=z, colorscale='RdBu'), row=2, col=2)
593
+
594
+ fig.update_layout(height=600, title_text="Multi-Plot Dashboard")
595
+ fig.show()
596
+ print("Dashboard created with multiple charts!")"""
597
+ }
598
+
599
+ def load_example():
600
+ return examples["Plotly Bar Chart"]
601
+
602
+ examples_btn.click(
603
+ fn=load_example,
604
+ inputs=[],
605
+ outputs=[code_input]
606
+ )
607
+
608
+ # Event handlers
609
+ execute_btn.click(
610
+ fn=None,
611
+ inputs=[code_input],
612
+ outputs=[status_display],
613
+ js="""
614
+ function(code) {
615
+ try {
616
+ if (window.executePyodideCode) {
617
+ return window.executePyodideCode(code);
618
+ } else {
619
+ return 'Execution function not available';
620
+ }
621
+ } catch (error) {
622
+ return 'Error: ' + error.message;
623
+ }
624
+ }
625
+ """
626
+ )
627
+
628
+ check_btn.click(
629
+ fn=None,
630
+ inputs=[],
631
+ outputs=[status_display],
632
+ js="""
633
+ function() {
634
+ try {
635
+ const ready = window.checkPyodideStatus ? window.checkPyodideStatus() : false;
636
+ return ready ? 'βœ… Ready for Plotly!' : '⏳ Still loading...';
637
+ } catch (error) {
638
+ return 'Status error: ' + error.message;
639
+ }
640
+ }
641
+ """
642
+ )
643
+
644
+ debug_btn.click(
645
+ fn=None,
646
+ inputs=[],
647
+ outputs=[status_display],
648
+ js="""
649
+ function() {
650
+ try {
651
+ if (window.toggleDebugMode) {
652
+ return window.toggleDebugMode() ? 'πŸ› Debug ON' : 'πŸ› Debug OFF';
653
+ }
654
+ return 'Debug toggle unavailable';
655
+ } catch (error) {
656
+ return 'Debug error: ' + error.message;
657
+ }
658
+ }
659
+ """
660
+ )
661
+
662
+ if __name__ == "__main__":
663
+ print("πŸš€ Starting Pyodide + Plotly Interpreter...")
664
+ demo.launch(
665
+ server_name="0.0.0.0",
666
+ server_port=7860,
667
+ share=False
668
+ )