mic3333 commited on
Commit
8ec449d
Β·
verified Β·
1 Parent(s): 81b031b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -95
app.py CHANGED
@@ -8,25 +8,55 @@ import gradio as gr
8
  def create_pyodide_interface():
9
  """Create a Gradio interface that uses Pyodide for client-side Python execution"""
10
 
11
- # Custom HTML/JS that loads and uses Pyodide
12
  pyodide_html = """
13
- <div id="pyodide-container">
14
- <div id="pyodide-status">Loading Pyodide...</div>
15
- <div id="pyodide-output" style="display:none;">
16
- <pre id="output-text"></pre>
17
- <div id="plot-container"></div>
 
 
 
18
  </div>
19
  </div>
20
 
21
  <script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>
22
  <script>
23
  let pyodide = null;
24
- let plotCounter = 0;
 
 
 
 
 
 
 
 
25
 
26
  async function initPyodide() {
 
 
 
27
  try {
28
- pyodide = await loadPyodide();
29
- await pyodide.loadPackage(["numpy", "matplotlib", "pandas"]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  // Set up matplotlib backend for web
32
  pyodide.runPython(`
@@ -39,35 +69,55 @@ def create_pyodide_interface():
39
  import base64
40
 
41
  def show_plot():
42
- buffer = io.BytesIO()
43
- plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
44
- buffer.seek(0)
45
- plot_data = buffer.getvalue()
46
- buffer.close()
47
- plt.close()
48
- return base64.b64encode(plot_data).decode()
 
 
 
49
 
50
  # Override plt.show() to capture plots
51
  original_show = plt.show
52
  def custom_show(*args, **kwargs):
53
  return show_plot()
54
  plt.show = custom_show
 
 
55
  `);
56
 
57
- document.getElementById('pyodide-status').textContent = 'Pyodide ready!';
58
- document.getElementById('pyodide-status').style.color = 'green';
 
 
 
 
 
59
  } catch (error) {
60
- document.getElementById('pyodide-status').textContent = 'Error loading Pyodide: ' + error;
61
- document.getElementById('pyodide-status').style.color = 'red';
 
62
  }
63
  }
64
 
65
  async function executePyodideCode(code) {
 
 
 
 
 
 
 
66
  if (!pyodide) {
67
- return "Pyodide not ready yet. Please wait...";
68
  }
69
 
70
  try {
 
 
71
  // Capture stdout
72
  pyodide.runPython(`
73
  import sys
@@ -95,7 +145,7 @@ def create_pyodide_interface():
95
  None
96
  `);
97
  } catch (e) {
98
- // No plot generated
99
  }
100
 
101
  // Display results
@@ -110,38 +160,55 @@ def create_pyodide_interface():
110
 
111
  // Display plot if generated
112
  if (plotData) {
113
- plotContainer.innerHTML = `<img src="data:image/png;base64,${plotData}" style="max-width: 100%; height: auto;" alt="Generated Plot">`;
114
- outputText.textContent += '\n\nπŸ“Š Plot generated successfully!';
 
115
  } else {
116
  plotContainer.innerHTML = '';
 
117
  }
118
 
119
- return output + (plotData ? '\nπŸ“Š Plot displayed below' : '');
120
 
121
  } catch (error) {
 
122
  document.getElementById('output-text').textContent = 'Error: ' + error.toString();
 
123
  return 'Error: ' + error.toString();
124
  }
125
  }
126
 
127
- // Initialize Pyodide when page loads
128
- initPyodide();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  // Make function globally available
131
  window.executePyodideCode = executePyodideCode;
 
132
  </script>
133
  """
134
 
135
  return pyodide_html
136
 
137
- def python_executor_fallback(code):
138
- """Fallback Python executor for server-side processing"""
139
- return "Pyodide is client-side only. Use the interface above to execute Python code in your browser."
140
-
141
  # Create the Gradio interface
142
  with gr.Blocks(title="LibreChat Pyodide Code Interpreter") as demo:
143
  gr.Markdown("# LibreChat Pyodide Code Interpreter")
144
  gr.Markdown("**Client-side Python execution** using Pyodide - Python runs directly in your browser!")
 
145
 
146
  # Pyodide interface
147
  pyodide_interface = gr.HTML(create_pyodide_interface())
@@ -149,39 +216,35 @@ with gr.Blocks(title="LibreChat Pyodide Code Interpreter") as demo:
149
  with gr.Row():
150
  with gr.Column():
151
  code_input = gr.Textbox(
152
- placeholder="""# Try this example:
153
- import matplotlib.pyplot as plt
154
- import numpy as np
155
-
156
- x = np.linspace(0, 10, 100)
157
- y = np.sin(x)
158
-
159
- plt.figure(figsize=(10, 6))
160
- plt.plot(x, y, 'b-', linewidth=2)
161
- plt.title('Sine Wave - Generated with Pyodide!')
162
- plt.xlabel('X values')
163
- plt.ylabel('Y values')
164
- plt.grid(True, alpha=0.3)
165
- plt.show()
166
 
167
- print("Plot created with client-side Python!")""",
 
 
 
 
 
 
168
  lines=15,
169
  label="Python Code (executes in your browser)"
170
  )
171
 
172
  execute_btn = gr.Button("Execute with Pyodide", variant="primary")
 
173
 
174
  with gr.Column():
175
- gr.Markdown("### Results will appear above in the Pyodide container")
176
  status_display = gr.Textbox(
177
- label="Execution Status",
178
  interactive=False,
179
  lines=3
180
  )
181
 
182
  # JavaScript execution
183
  execute_btn.click(
184
- fn=None, # No Python function needed
185
  inputs=[code_input],
186
  outputs=[status_display],
187
  js="""
@@ -189,57 +252,27 @@ print("Plot created with client-side Python!")""",
189
  if (window.executePyodideCode) {
190
  return window.executePyodideCode(code);
191
  } else {
192
- return "Pyodide not ready yet. Please wait for initialization.";
193
  }
194
  }
195
  """
196
  )
197
 
198
- # Examples
199
- gr.Markdown("## Examples")
200
-
201
- gr.Code("""
202
- # Data Analysis Example
203
- import pandas as pd
204
- import numpy as np
205
-
206
- # Create sample data
207
- data = {
208
- 'Name': ['Alice', 'Bob', 'Charlie', 'Diana'],
209
- 'Age': [25, 30, 35, 28],
210
- 'Score': [85, 92, 78, 96]
211
- }
212
-
213
- df = pd.DataFrame(data)
214
- print("Sample DataFrame:")
215
- print(df)
216
- print(f"\\nAverage Score: {df['Score'].mean():.1f}")
217
- """, language="python", label="Data Analysis with Pandas")
218
-
219
- gr.Code("""
220
- # Visualization Example
221
- import matplotlib.pyplot as plt
222
- import numpy as np
223
-
224
- # Generate data
225
- categories = ['A', 'B', 'C', 'D', 'E']
226
- values = np.random.randint(10, 50, 5)
227
-
228
- # Create bar chart
229
- plt.figure(figsize=(10, 6))
230
- bars = plt.bar(categories, values, color=['red', 'blue', 'green', 'orange', 'purple'])
231
- plt.title('Random Bar Chart - Pyodide Demo')
232
- plt.xlabel('Categories')
233
- plt.ylabel('Values')
234
-
235
- # Add value labels on bars
236
- for bar, value in zip(bars, values):
237
- plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
238
- str(value), ha='center', va='bottom')
239
-
240
- plt.show()
241
- print(f"Generated chart with values: {values}")
242
- """, language="python", label="Matplotlib Visualization")
243
 
244
  if __name__ == "__main__":
245
  demo.launch(
 
8
  def create_pyodide_interface():
9
  """Create a Gradio interface that uses Pyodide for client-side Python execution"""
10
 
11
+ # Enhanced HTML/JS with better error handling and status updates
12
  pyodide_html = """
13
+ <div id="pyodide-container" style="border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 10px 0;">
14
+ <div id="pyodide-status" style="font-weight: bold; padding: 10px; background: #f0f0f0; border-radius: 3px;">
15
+ πŸ”„ Loading Pyodide... This may take 10-30 seconds on first load.
16
+ </div>
17
+ <div id="pyodide-output" style="display:none; margin-top: 10px;">
18
+ <h4>Execution Results:</h4>
19
+ <pre id="output-text" style="background: #f8f8f8; padding: 10px; border-radius: 3px; max-height: 300px; overflow-y: auto;"></pre>
20
+ <div id="plot-container" style="text-align: center; margin-top: 10px;"></div>
21
  </div>
22
  </div>
23
 
24
  <script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>
25
  <script>
26
  let pyodide = null;
27
+ let pyodideReady = false;
28
+ let initializationStarted = false;
29
+
30
+ function updateStatus(message, color = 'black') {
31
+ const statusDiv = document.getElementById('pyodide-status');
32
+ statusDiv.innerHTML = message;
33
+ statusDiv.style.color = color;
34
+ console.log('Pyodide Status:', message);
35
+ }
36
 
37
  async function initPyodide() {
38
+ if (initializationStarted) return;
39
+ initializationStarted = true;
40
+
41
  try {
42
+ updateStatus('πŸ”„ Loading Pyodide core...', 'blue');
43
+
44
+ // Load Pyodide with error handling
45
+ pyodide = await loadPyodide({
46
+ indexURL: "https://cdn.jsdelivr.net/pyodide/v0.24.1/full/"
47
+ });
48
+
49
+ updateStatus('πŸ“¦ Loading Python packages (numpy, matplotlib, pandas)...', 'blue');
50
+
51
+ // Load packages one by one with progress
52
+ await pyodide.loadPackage("numpy");
53
+ updateStatus('πŸ“¦ numpy loaded, loading matplotlib...', 'blue');
54
+
55
+ await pyodide.loadPackage("matplotlib");
56
+ updateStatus('πŸ“¦ matplotlib loaded, loading pandas...', 'blue');
57
+
58
+ await pyodide.loadPackage("pandas");
59
+ updateStatus('βš™οΈ Setting up matplotlib backend...', 'blue');
60
 
61
  // Set up matplotlib backend for web
62
  pyodide.runPython(`
 
69
  import base64
70
 
71
  def show_plot():
72
+ try:
73
+ buffer = io.BytesIO()
74
+ plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
75
+ buffer.seek(0)
76
+ plot_data = buffer.getvalue()
77
+ buffer.close()
78
+ plt.close()
79
+ return base64.b64encode(plot_data).decode()
80
+ except Exception as e:
81
+ return None
82
 
83
  # Override plt.show() to capture plots
84
  original_show = plt.show
85
  def custom_show(*args, **kwargs):
86
  return show_plot()
87
  plt.show = custom_show
88
+
89
+ print("Pyodide Python environment ready!")
90
  `);
91
 
92
+ pyodideReady = true;
93
+ updateStatus('βœ… Pyodide ready! You can now execute Python code.', 'green');
94
+
95
+ // Show a test message
96
+ document.getElementById('pyodide-output').style.display = 'block';
97
+ document.getElementById('output-text').textContent = 'Pyodide initialization complete! Ready to execute Python code.';
98
+
99
  } catch (error) {
100
+ console.error('Pyodide initialization error:', error);
101
+ updateStatus('❌ Failed to initialize Pyodide: ' + error.toString(), 'red');
102
+ pyodideReady = false;
103
  }
104
  }
105
 
106
  async function executePyodideCode(code) {
107
+ console.log('Execute called, pyodideReady:', pyodideReady);
108
+
109
+ if (!pyodideReady) {
110
+ updateStatus('⏳ Pyodide is still initializing. Please wait...', 'orange');
111
+ return "Pyodide is still loading. Please wait for the green 'ready' status above.";
112
+ }
113
+
114
  if (!pyodide) {
115
+ return "Error: Pyodide not available.";
116
  }
117
 
118
  try {
119
+ updateStatus('▢️ Executing Python code...', 'blue');
120
+
121
  // Capture stdout
122
  pyodide.runPython(`
123
  import sys
 
145
  None
146
  `);
147
  } catch (e) {
148
+ console.log('No plot generated or plot error:', e);
149
  }
150
 
151
  // Display results
 
160
 
161
  // Display plot if generated
162
  if (plotData) {
163
+ plotContainer.innerHTML = `<img src="data:image/png;base64,${plotData}" style="max-width: 100%; height: auto; border: 1px solid #ddd;" alt="Generated Plot">`;
164
+ outputText.textContent += '\\n\\nπŸ“Š Plot generated successfully!';
165
+ updateStatus('βœ… Code executed with plot generated!', 'green');
166
  } else {
167
  plotContainer.innerHTML = '';
168
+ updateStatus('βœ… Code executed successfully!', 'green');
169
  }
170
 
171
+ return output + (plotData ? '\\nπŸ“Š Plot displayed above' : '');
172
 
173
  } catch (error) {
174
+ console.error('Execution error:', error);
175
  document.getElementById('output-text').textContent = 'Error: ' + error.toString();
176
+ updateStatus('❌ Execution error: ' + error.toString(), 'red');
177
  return 'Error: ' + error.toString();
178
  }
179
  }
180
 
181
+ // Auto-retry initialization if it fails
182
+ async function safeInitPyodide() {
183
+ try {
184
+ await initPyodide();
185
+ } catch (error) {
186
+ console.error('Init failed, retrying in 3 seconds:', error);
187
+ updateStatus('πŸ”„ Initialization failed, retrying in 3 seconds...', 'orange');
188
+ setTimeout(safeInitPyodide, 3000);
189
+ }
190
+ }
191
+
192
+ // Initialize when page loads
193
+ if (document.readyState === 'loading') {
194
+ document.addEventListener('DOMContentLoaded', safeInitPyodide);
195
+ } else {
196
+ safeInitPyodide();
197
+ }
198
 
199
  // Make function globally available
200
  window.executePyodideCode = executePyodideCode;
201
+ window.checkPyodideStatus = () => pyodideReady;
202
  </script>
203
  """
204
 
205
  return pyodide_html
206
 
 
 
 
 
207
  # Create the Gradio interface
208
  with gr.Blocks(title="LibreChat Pyodide Code Interpreter") as demo:
209
  gr.Markdown("# LibreChat Pyodide Code Interpreter")
210
  gr.Markdown("**Client-side Python execution** using Pyodide - Python runs directly in your browser!")
211
+ gr.Markdown("⏳ **Please wait for Pyodide to fully initialize before executing code (watch the status above).**")
212
 
213
  # Pyodide interface
214
  pyodide_interface = gr.HTML(create_pyodide_interface())
 
216
  with gr.Row():
217
  with gr.Column():
218
  code_input = gr.Textbox(
219
+ value="""# Simple test - try this first
220
+ print("Hello from browser-based Python!")
221
+ print("2 + 2 =", 2 + 2)
 
 
 
 
 
 
 
 
 
 
 
222
 
223
+ # Then try this matplotlib example:
224
+ # import matplotlib.pyplot as plt
225
+ # import numpy as np
226
+ # x = np.linspace(0, 10, 50)
227
+ # plt.plot(x, np.sin(x))
228
+ # plt.title('Sine Wave')
229
+ # plt.show()""",
230
  lines=15,
231
  label="Python Code (executes in your browser)"
232
  )
233
 
234
  execute_btn = gr.Button("Execute with Pyodide", variant="primary")
235
+ check_status_btn = gr.Button("Check Pyodide Status", variant="secondary")
236
 
237
  with gr.Column():
238
+ gr.Markdown("### Execution Status")
239
  status_display = gr.Textbox(
240
+ label="Last Execution Result",
241
  interactive=False,
242
  lines=3
243
  )
244
 
245
  # JavaScript execution
246
  execute_btn.click(
247
+ fn=None,
248
  inputs=[code_input],
249
  outputs=[status_display],
250
  js="""
 
252
  if (window.executePyodideCode) {
253
  return window.executePyodideCode(code);
254
  } else {
255
+ return "Pyodide functions not available. Check browser console for errors.";
256
  }
257
  }
258
  """
259
  )
260
 
261
+ # Status check button
262
+ check_status_btn.click(
263
+ fn=None,
264
+ inputs=[],
265
+ outputs=[status_display],
266
+ js="""
267
+ function() {
268
+ if (window.checkPyodideStatus) {
269
+ return window.checkPyodideStatus() ? "βœ… Pyodide is ready!" : "⏳ Pyodide is still loading...";
270
+ } else {
271
+ return "❌ Pyodide not available.";
272
+ }
273
+ }
274
+ """
275
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
  if __name__ == "__main__":
278
  demo.launch(