mic3333 commited on
Commit
dcae7f9
Β·
verified Β·
1 Parent(s): 4b10064

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -171
app.py CHANGED
@@ -1,90 +1,72 @@
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
  };
@@ -92,25 +74,19 @@ def create_pyodide_interface():
92
  async function initPyodide() {
93
  try {
94
  if (typeof loadPyodide === 'undefined') {
95
- throw new Error('Pyodide CDN not loaded');
96
  }
97
  if (typeof Plotly === 'undefined') {
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')
@@ -118,118 +94,82 @@ def create_pyodide_interface():
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');
197
- if (outputDiv) outputDiv.style.display = 'block';
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(`
229
  import sys
230
  from io import StringIO
231
  old_stdout = sys.stdout
232
- sys.stdout = captured_output = StringIO()
233
  `);
234
 
235
  // Execute user code
@@ -238,56 +178,160 @@ sys.stdout = captured_output = StringIO()
238
  // Get captured output
239
  let stdout = pyodide.runPython(`
240
  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
+ Fixed Plotly + Pyodide Integration - No Syntax Errors
4
  """
5
 
6
  import gradio as gr
7
 
8
  def create_pyodide_interface():
9
+ """Create a working Pyodide + Plotly interface with proper string escaping"""
10
 
11
+ # Use proper string escaping to avoid syntax errors
12
+ pyodide_html = '''
13
  <div id="pyodide-container" style="border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 10px 0;">
14
+ <div id="status" style="font-weight: bold; padding: 10px; background: #f0f0f0; border-radius: 3px;">
15
+ πŸ”„ Initializing Pyodide + Plotly...
16
  </div>
17
+ <div id="output" style="display:none; margin-top: 10px;">
18
+ <h4>Results:</h4>
19
+ <pre id="text-output" style="background: #f8f8f8; padding: 10px; border-radius: 3px; max-height: 200px; overflow-y: auto;"></pre>
20
+ <div id="plots" style="margin-top: 15px;"></div>
21
  </div>
22
  </div>
23
 
24
+ <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
25
+ <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script>
26
 
27
  <script>
28
  let pyodide = null;
29
+ let ready = false;
30
+ let plotId = 0;
31
 
32
+ function updateStatus(msg, color = 'black') {
33
+ const statusEl = document.getElementById('status');
34
+ if (statusEl) {
35
+ statusEl.innerHTML = msg;
36
+ statusEl.style.color = color;
37
  }
38
+ console.log('Status:', msg);
39
  }
40
 
41
+ // Function to render Plotly from Python
42
+ window.renderPlot = function(plotData, plotLayout) {
43
  try {
44
+ plotId++;
45
+ const containerId = 'plot-' + plotId;
46
+ const plotsDiv = document.getElementById('plots');
47
 
48
+ if (!plotsDiv) return false;
 
 
 
49
 
50
+ // Create plot container
51
  const plotDiv = document.createElement('div');
52
+ plotDiv.innerHTML = `
53
+ <h5>πŸ“ˆ Plot ${plotId}</h5>
54
+ <div id="${containerId}" style="width: 100%; height: 500px; border: 1px solid #ccc; margin: 10px 0;"></div>
55
+ `;
56
+ plotsDiv.appendChild(plotDiv);
 
 
 
 
 
 
 
 
 
57
 
58
+ // Parse JSON if needed
59
+ if (typeof plotData === 'string') plotData = JSON.parse(plotData);
60
+ if (typeof plotLayout === 'string') plotLayout = JSON.parse(plotLayout);
 
 
 
 
 
 
 
61
 
62
+ // Render with Plotly
63
+ Plotly.newPlot(containerId, plotData, plotLayout, {responsive: true});
64
 
65
+ console.log('Plot rendered:', containerId);
66
  return true;
67
 
68
  } catch (error) {
69
+ console.error('Plot render error:', error);
70
  return false;
71
  }
72
  };
 
74
  async function initPyodide() {
75
  try {
76
  if (typeof loadPyodide === 'undefined') {
77
+ throw new Error('Pyodide CDN failed to load');
78
  }
79
  if (typeof Plotly === 'undefined') {
80
+ throw new Error('Plotly CDN failed to load');
81
  }
82
 
83
+ updateStatus('πŸ”„ Loading Pyodide core...', 'blue');
84
+ pyodide = await loadPyodide();
 
 
 
85
 
86
  updateStatus('πŸ“¦ Installing packages...', 'blue');
87
+ await pyodide.loadPackage(['numpy', 'pandas', 'micropip']);
88
 
89
+ updateStatus('πŸ“ˆ Installing Plotly...', 'blue');
 
 
 
 
90
  await pyodide.runPythonAsync(`
91
  import micropip
92
  await micropip.install('plotly')
 
94
 
95
  updateStatus('πŸ”§ Setting up Plotly integration...', 'blue');
96
 
97
+ // Setup Python environment with PROPER string escaping
98
  pyodide.runPython(`
 
99
  import json
100
+ from js import renderPlot
101
 
102
+ # Global plot counter
103
+ plot_count = 0
104
 
105
+ def show_plot(fig):
106
+ global plot_count
 
 
 
 
107
  try:
108
+ # Convert figure to dict
109
+ fig_dict = fig.to_dict()
 
110
 
111
+ # Extract data and layout
112
+ data = fig_dict.get('data', [])
113
+ layout = fig_dict.get('layout', {})
 
114
 
115
+ # Call JavaScript render function
116
+ success = renderPlot(json.dumps(data), json.dumps(layout))
117
 
118
  if success:
119
+ plot_count += 1
120
+ print(f"Plot {plot_count} displayed successfully!")
121
  return True
122
  else:
123
+ print("Plot rendering failed")
124
  return False
125
 
126
  except Exception as e:
127
+ print(f"Plot error: {e}")
128
  return False
129
 
130
+ # Patch Plotly
131
  try:
132
  import plotly.graph_objects as go
133
  import plotly.express as px
134
 
135
+ # Replace show method
136
  original_show = go.Figure.show
137
+ go.Figure.show = lambda self, *args, **kwargs: show_plot(self)
 
 
 
 
138
 
139
+ print("Plotly patched successfully!")
 
 
140
 
141
  except ImportError as e:
142
+ print(f"Plotly import failed: {e}")
 
 
 
 
 
 
 
 
 
143
 
144
+ print("Environment setup complete!")
145
  `);
146
 
147
+ ready = true;
148
+ updateStatus('βœ… Plotly + Pyodide ready!', 'green');
 
 
 
 
149
 
150
+ document.getElementById('output').style.display = 'block';
151
+ document.getElementById('text-output').textContent = 'Ready! Try executing some Plotly code.';
 
 
152
 
153
  } catch (error) {
154
+ console.error('Initialization failed:', error);
155
  updateStatus('❌ Failed: ' + error.message, 'red');
 
156
  }
157
  }
158
 
159
+ async function executeUserCode(code) {
160
+ if (!ready) {
161
+ return 'System not ready. Please wait for green status.';
 
 
 
 
162
  }
163
 
164
  try {
165
+ updateStatus('▢️ Running...', 'blue');
 
 
 
 
166
 
167
  // Capture stdout
168
  pyodide.runPython(`
169
  import sys
170
  from io import StringIO
171
  old_stdout = sys.stdout
172
+ sys.stdout = output_capture = StringIO()
173
  `);
174
 
175
  // Execute user code
 
178
  // Get captured output
179
  let stdout = pyodide.runPython(`
180
  sys.stdout = old_stdout
181
+ output_capture.getvalue()
182
  `);
183
 
184
+ // Display output
185
+ document.getElementById('text-output').textContent = stdout || 'Code executed successfully';
186
+ updateStatus('βœ… Complete!', 'green');
 
 
 
 
 
 
 
187
 
188
+ return stdout || 'Success';
 
189
 
190
  } catch (error) {
191
+ const errorMsg = 'Error: ' + error.toString();
192
+ document.getElementById('text-output').textContent = errorMsg;
193
+ updateStatus('❌ Error occurred', 'red');
194
+ return errorMsg;
 
 
 
 
195
  }
196
  }
197
 
198
+ // Initialize when both CDNs are loaded
199
+ function checkAndInit() {
200
  if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
201
+ console.log('Both CDNs loaded, starting initialization');
202
  initPyodide();
203
  } else {
204
+ console.log('Waiting for CDNs...');
205
+ setTimeout(checkAndInit, 1000);
206
  }
207
  }
208
 
209
+ // Start when DOM is ready
210
  if (document.readyState === 'loading') {
211
+ document.addEventListener('DOMContentLoaded', checkAndInit);
212
  } else {
213
+ checkAndInit();
214
  }
215
 
216
+ // Make functions globally available
217
+ window.executeUserCode = executeUserCode;
218
+ window.checkReady = () => ready;
219
 
220
  </script>
221
+ '''
222
+
223
+ return pyodide_html
224
+
225
+ # Create Gradio app
226
+ with gr.Blocks(title="Fixed Plotly Pyodide") as demo:
227
+ gr.Markdown("# πŸπŸ“ˆ Fixed Plotly + Pyodide")
228
+ gr.Markdown("**No more syntax errors!** Interactive Plotly charts in browser.")
229
+
230
+ # Pyodide interface
231
+ pyodide_interface = gr.HTML(create_pyodide_interface())
232
+
233
+ with gr.Row():
234
+ with gr.Column(scale=2):
235
+ code_input = gr.Textbox(
236
+ value="""# Working Plotly Example
237
+ import plotly.graph_objects as go
238
+ import numpy as np
239
+
240
+ print("Creating Plotly chart...")
241
+
242
+ # Simple data
243
+ x = np.array([1, 2, 3, 4, 5])
244
+ y = x ** 2
245
+
246
+ # Create figure
247
+ fig = go.Figure()
248
+ fig.add_trace(go.Scatter(
249
+ x=x,
250
+ y=y,
251
+ mode='lines+markers',
252
+ name='xΒ²',
253
+ line=dict(color='blue', width=3),
254
+ marker=dict(size=8, color='red')
255
+ ))
256
+
257
+ fig.update_layout(
258
+ title='Interactive Quadratic Function',
259
+ xaxis_title='X values',
260
+ yaxis_title='Y values',
261
+ template='plotly_white'
262
+ )
263
+
264
+ fig.show()
265
+ print("Chart should appear above! βœ…")""",
266
+ lines=18,
267
+ label="Python Code"
268
+ )
269
+
270
+ execute_btn = gr.Button("πŸš€ Execute Code", variant="primary", size="lg")
271
+
272
+ with gr.Column(scale=1):
273
+ status_display = gr.Textbox(
274
+ label="Execution Status",
275
+ interactive=False,
276
+ lines=3
277
+ )
278
+
279
+ check_btn = gr.Button("πŸ“Š Check Status")
280
+
281
+ gr.Markdown("""
282
+ ### πŸ“‹ Quick Tests:
283
+ 1. **Wait for green status**
284
+ 2. **Run the default example**
285
+ 3. **Look for interactive chart**
286
+ 4. **Check browser console** if issues
287
+ """)
288
+
289
+ # Button handlers with simple JS
290
+ execute_btn.click(
291
+ fn=None,
292
+ inputs=[code_input],
293
+ outputs=[status_display],
294
+ js="""
295
+ function(code) {
296
+ try {
297
+ if (window.executeUserCode) {
298
+ return window.executeUserCode(code);
299
+ } else {
300
+ return 'Execute function not available';
301
+ }
302
+ } catch (error) {
303
+ return 'JS Error: ' + error.message;
304
+ }
305
+ }
306
+ """
307
+ )
308
+
309
+ check_btn.click(
310
+ fn=None,
311
+ inputs=[],
312
+ outputs=[status_display],
313
+ js="""
314
+ function() {
315
+ try {
316
+ if (window.checkReady) {
317
+ return window.checkReady() ? 'βœ… System Ready' : '⏳ Still Loading';
318
+ } else {
319
+ return '❌ Check function not available';
320
+ }
321
+ } catch (error) {
322
+ return 'Status Error: ' + error.message;
323
+ }
324
+ }
325
+ """
326
+ )
327
+
328
+ if __name__ == "__main__":
329
+ print("πŸš€ Starting Fixed Plotly + Pyodide Server...")
330
+ print("πŸ“ Access at: http://localhost:7860")
331
+
332
+ demo.launch(
333
+ server_name="0.0.0.0",
334
+ server_port=7860,
335
+ share=False,
336
+ debug=True
337
+ )