Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,11 +1,24 @@
|
|
1 |
import gradio as gr
|
2 |
import os
|
3 |
-
import
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
# Set fixed MPLCONFIGDIR to avoid permission issues
|
7 |
os.environ['MPLCONFIGDIR'] = '/tmp'
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
# Sample code for different Gradio apps
|
10 |
EXAMPLE_CODES = {
|
11 |
"hello_world": """
|
@@ -21,6 +34,8 @@ demo = gr.Interface(
|
|
21 |
title="Hello World",
|
22 |
description="A simple greeting app"
|
23 |
)
|
|
|
|
|
24 |
""",
|
25 |
|
26 |
"calculator": """
|
@@ -49,6 +64,8 @@ demo = gr.Interface(
|
|
49 |
title="Calculator",
|
50 |
description="Perform basic arithmetic operations"
|
51 |
)
|
|
|
|
|
52 |
""",
|
53 |
|
54 |
"image_filter": """
|
@@ -85,8 +102,11 @@ demo = gr.Interface(
|
|
85 |
],
|
86 |
outputs=gr.Image(type="pil"),
|
87 |
title="Image Filter",
|
88 |
-
description="Apply various filters to images"
|
|
|
89 |
)
|
|
|
|
|
90 |
"""
|
91 |
}
|
92 |
|
@@ -105,40 +125,140 @@ def simulate_llm_response(prompt):
|
|
105 |
# Default to hello world
|
106 |
return EXAMPLE_CODES["hello_world"], None
|
107 |
|
108 |
-
#
|
109 |
-
def
|
110 |
-
"""
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
-
#
|
115 |
-
|
116 |
-
stderr_buffer = io.StringIO()
|
117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
try:
|
119 |
-
|
120 |
-
|
121 |
-
|
|
|
|
|
122 |
|
123 |
-
#
|
124 |
-
|
125 |
-
return None, "No 'demo' object found in the code", stdout_buffer.getvalue(), stderr_buffer.getvalue()
|
126 |
|
127 |
-
#
|
128 |
-
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
except Exception as e:
|
131 |
import traceback
|
132 |
-
return None, f"Error
|
133 |
|
134 |
-
#
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
# Header
|
137 |
gr.Markdown("# 🤖 LLM Gradio App Generator")
|
138 |
gr.Markdown("Generate and run Gradio apps dynamically!")
|
139 |
|
|
|
|
|
|
|
140 |
with gr.Row():
|
141 |
-
#
|
142 |
with gr.Column(scale=1):
|
143 |
# App description input
|
144 |
prompt = gr.Textbox(
|
@@ -148,120 +268,125 @@ with gr.Blocks(title="LLM Gradio App Generator") as app:
|
|
148 |
)
|
149 |
|
150 |
# Example buttons
|
151 |
-
gr.Markdown("###
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
155 |
|
156 |
# Generate button
|
157 |
-
|
|
|
|
|
158 |
|
159 |
-
#
|
160 |
-
gr.
|
161 |
-
|
162 |
|
163 |
-
#
|
164 |
-
|
165 |
-
log_output = gr.Textbox(label="Output Log", lines=5)
|
166 |
|
167 |
-
#
|
168 |
with gr.Column(scale=2):
|
169 |
-
#
|
170 |
-
|
171 |
-
|
172 |
-
# Container for the dynamically generated app
|
173 |
-
app_container = gr.HTML(
|
174 |
-
"""<div style="display:flex; justify-content:center; align-items:center; height:400px; border:1px dashed #ccc; border-radius:8px;">
|
175 |
<div style="text-align:center;">
|
176 |
-
<h3>
|
177 |
-
<p>
|
178 |
</div>
|
179 |
</div>"""
|
180 |
)
|
181 |
|
182 |
# Example button functions
|
183 |
-
def
|
184 |
-
return
|
185 |
|
186 |
hello_btn.click(
|
187 |
-
lambda:
|
188 |
inputs=None,
|
189 |
outputs=prompt
|
190 |
)
|
191 |
|
192 |
calc_btn.click(
|
193 |
-
lambda:
|
194 |
inputs=None,
|
195 |
outputs=prompt
|
196 |
)
|
197 |
|
198 |
-
|
199 |
-
lambda:
|
200 |
inputs=None,
|
201 |
outputs=prompt
|
202 |
)
|
203 |
|
204 |
-
#
|
205 |
-
def
|
206 |
if not prompt_text:
|
207 |
-
return
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
|
216 |
# Get code from LLM (simulated)
|
217 |
code, error = simulate_llm_response(prompt_text)
|
218 |
if error:
|
219 |
-
return
|
220 |
-
code,
|
221 |
-
f"Error generating code: {error}",
|
222 |
-
"""<div style="text-align:center; color:red; padding:20px;">
|
223 |
-
<h3>Error</h3>
|
224 |
-
<p>{0}</p>
|
225 |
-
</div>""".format(error)
|
226 |
-
)
|
227 |
|
228 |
-
#
|
229 |
-
|
230 |
-
|
|
|
231 |
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
<p>{0}</p>
|
239 |
-
</div>""".format(exec_error)
|
240 |
-
)
|
241 |
|
242 |
-
|
243 |
-
|
244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
|
246 |
-
return
|
247 |
-
|
248 |
-
|
249 |
-
return (
|
250 |
-
code,
|
251 |
-
f"Error rendering app: {str(e)}",
|
252 |
-
"""<div style="text-align:center; color:red; padding:20px;">
|
253 |
-
<h3>Rendering Error</h3>
|
254 |
-
<p>{0}</p>
|
255 |
-
</div>""".format(traceback.format_exc())
|
256 |
-
)
|
257 |
|
258 |
# Connect the generate button
|
259 |
generate_btn.click(
|
260 |
-
|
261 |
-
inputs=prompt,
|
262 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
)
|
264 |
|
265 |
-
# Launch the app
|
266 |
if __name__ == "__main__":
|
267 |
-
|
|
|
1 |
import gradio as gr
|
2 |
import os
|
3 |
+
import tempfile
|
4 |
+
import time
|
5 |
+
import subprocess
|
6 |
+
import threading
|
7 |
+
import signal
|
8 |
+
import json
|
9 |
+
import random
|
10 |
+
import string
|
11 |
|
12 |
# Set fixed MPLCONFIGDIR to avoid permission issues
|
13 |
os.environ['MPLCONFIGDIR'] = '/tmp'
|
14 |
|
15 |
+
# Create tmp directory if it doesn't exist
|
16 |
+
TEMP_DIR = os.path.join(tempfile.gettempdir(), "gradio_apps")
|
17 |
+
os.makedirs(TEMP_DIR, exist_ok=True)
|
18 |
+
|
19 |
+
# Track running processes
|
20 |
+
processes = {}
|
21 |
+
|
22 |
# Sample code for different Gradio apps
|
23 |
EXAMPLE_CODES = {
|
24 |
"hello_world": """
|
|
|
34 |
title="Hello World",
|
35 |
description="A simple greeting app"
|
36 |
)
|
37 |
+
|
38 |
+
demo.launch(server_name="0.0.0.0", server_port=PORT)
|
39 |
""",
|
40 |
|
41 |
"calculator": """
|
|
|
64 |
title="Calculator",
|
65 |
description="Perform basic arithmetic operations"
|
66 |
)
|
67 |
+
|
68 |
+
demo.launch(server_name="0.0.0.0", server_port=PORT)
|
69 |
""",
|
70 |
|
71 |
"image_filter": """
|
|
|
102 |
],
|
103 |
outputs=gr.Image(type="pil"),
|
104 |
title="Image Filter",
|
105 |
+
description="Apply various filters to images",
|
106 |
+
allow_flagging=False
|
107 |
)
|
108 |
+
|
109 |
+
demo.launch(server_name="0.0.0.0", server_port=PORT)
|
110 |
"""
|
111 |
}
|
112 |
|
|
|
125 |
# Default to hello world
|
126 |
return EXAMPLE_CODES["hello_world"], None
|
127 |
|
128 |
+
# Find an available port
|
129 |
+
def find_available_port(start_port=7870):
|
130 |
+
"""Find an available port starting from start_port"""
|
131 |
+
import socket
|
132 |
+
from contextlib import closing
|
133 |
+
|
134 |
+
def is_port_available(port):
|
135 |
+
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
|
136 |
+
return sock.connect_ex(('localhost', port)) != 0
|
137 |
+
|
138 |
+
port = start_port
|
139 |
+
while not is_port_available(port):
|
140 |
+
port += 1
|
141 |
+
|
142 |
+
return port
|
143 |
+
|
144 |
+
# Generate a random string
|
145 |
+
def random_string(length=8):
|
146 |
+
"""Generate a random string of fixed length"""
|
147 |
+
letters = string.ascii_lowercase
|
148 |
+
return ''.join(random.choice(letters) for i in range(length))
|
149 |
+
|
150 |
+
# Function to run a Gradio app as a subprocess
|
151 |
+
def run_gradio_app(code, app_id=None):
|
152 |
+
"""Run a Gradio app as a subprocess and return the port"""
|
153 |
+
global processes
|
154 |
+
|
155 |
+
# Clean up any previous process with the same ID
|
156 |
+
if app_id in processes and processes[app_id]["process"].poll() is None:
|
157 |
+
processes[app_id]["process"].terminate()
|
158 |
+
try:
|
159 |
+
processes[app_id]["process"].wait(timeout=5)
|
160 |
+
except:
|
161 |
+
processes[app_id]["process"].kill()
|
162 |
+
|
163 |
+
# Remove the file
|
164 |
+
try:
|
165 |
+
os.unlink(processes[app_id]["file"])
|
166 |
+
except:
|
167 |
+
pass
|
168 |
+
|
169 |
+
# Generate a unique ID if not provided
|
170 |
+
if app_id is None:
|
171 |
+
app_id = random_string()
|
172 |
|
173 |
+
# Find an available port
|
174 |
+
port = find_available_port()
|
|
|
175 |
|
176 |
+
# Replace PORT in the code with the actual port
|
177 |
+
code = code.replace("PORT", str(port))
|
178 |
+
|
179 |
+
# Create a temporary file
|
180 |
+
with tempfile.NamedTemporaryFile(suffix='.py', dir=TEMP_DIR, delete=False) as f:
|
181 |
+
f.write(code.encode('utf-8'))
|
182 |
+
file_path = f.name
|
183 |
+
|
184 |
+
# Run the app as a subprocess
|
185 |
try:
|
186 |
+
process = subprocess.Popen(
|
187 |
+
[sys.executable, file_path],
|
188 |
+
stdout=subprocess.PIPE,
|
189 |
+
stderr=subprocess.PIPE
|
190 |
+
)
|
191 |
|
192 |
+
# Wait a moment for the app to start
|
193 |
+
time.sleep(3)
|
|
|
194 |
|
195 |
+
# Check if the process is still running
|
196 |
+
if process.poll() is not None:
|
197 |
+
stdout, stderr = process.communicate()
|
198 |
+
return None, f"App failed to start (exit code: {process.returncode})\nStdout: {stdout.decode('utf-8')}\nStderr: {stderr.decode('utf-8')}"
|
199 |
+
|
200 |
+
# Store the process and file path
|
201 |
+
processes[app_id] = {
|
202 |
+
"process": process,
|
203 |
+
"file": file_path,
|
204 |
+
"port": port
|
205 |
+
}
|
206 |
+
|
207 |
+
return port, None
|
208 |
except Exception as e:
|
209 |
import traceback
|
210 |
+
return None, f"Error starting app: {str(e)}\n{traceback.format_exc()}"
|
211 |
|
212 |
+
# Function to stop a running app
|
213 |
+
def stop_gradio_app(app_id):
|
214 |
+
"""Stop a running Gradio app"""
|
215 |
+
global processes
|
216 |
+
|
217 |
+
if app_id in processes:
|
218 |
+
process = processes[app_id]["process"]
|
219 |
+
file_path = processes[app_id]["file"]
|
220 |
+
|
221 |
+
if process.poll() is None:
|
222 |
+
process.terminate()
|
223 |
+
try:
|
224 |
+
process.wait(timeout=5)
|
225 |
+
except:
|
226 |
+
process.kill()
|
227 |
+
|
228 |
+
# Remove the file
|
229 |
+
try:
|
230 |
+
os.unlink(file_path)
|
231 |
+
except:
|
232 |
+
pass
|
233 |
+
|
234 |
+
del processes[app_id]
|
235 |
+
return True
|
236 |
+
|
237 |
+
return False
|
238 |
+
|
239 |
+
# Clean up on exit
|
240 |
+
def cleanup():
|
241 |
+
"""Clean up all running processes and temporary files"""
|
242 |
+
for app_id in list(processes.keys()):
|
243 |
+
stop_gradio_app(app_id)
|
244 |
+
|
245 |
+
import atexit
|
246 |
+
atexit.register(cleanup)
|
247 |
+
|
248 |
+
# Import sys after we've defined all the functions
|
249 |
+
import sys
|
250 |
+
|
251 |
+
# Main Gradio interface
|
252 |
+
with gr.Blocks(title="LLM Gradio App Generator") as demo:
|
253 |
# Header
|
254 |
gr.Markdown("# 🤖 LLM Gradio App Generator")
|
255 |
gr.Markdown("Generate and run Gradio apps dynamically!")
|
256 |
|
257 |
+
# App ID for tracking the current app
|
258 |
+
app_id = gr.State("")
|
259 |
+
|
260 |
with gr.Row():
|
261 |
+
# Left column (input)
|
262 |
with gr.Column(scale=1):
|
263 |
# App description input
|
264 |
prompt = gr.Textbox(
|
|
|
268 |
)
|
269 |
|
270 |
# Example buttons
|
271 |
+
gr.Markdown("### Try These Examples:")
|
272 |
+
with gr.Row():
|
273 |
+
hello_btn = gr.Button("Hello World")
|
274 |
+
calc_btn = gr.Button("Calculator")
|
275 |
+
image_btn = gr.Button("Image Filter")
|
276 |
|
277 |
# Generate button
|
278 |
+
with gr.Row():
|
279 |
+
generate_btn = gr.Button("Generate & Run App", variant="primary")
|
280 |
+
stop_btn = gr.Button("Stop App", variant="stop")
|
281 |
|
282 |
+
# Display the generated code
|
283 |
+
with gr.Accordion("Generated Code", open=False):
|
284 |
+
code_output = gr.Code(language="python", label="Python Code")
|
285 |
|
286 |
+
# Status message
|
287 |
+
status_output = gr.Markdown("Enter a description and click 'Generate & Run App'")
|
|
|
288 |
|
289 |
+
# Right column (output)
|
290 |
with gr.Column(scale=2):
|
291 |
+
# Frame to display the running app
|
292 |
+
app_frame = gr.HTML(
|
293 |
+
"""<div style="display:flex; justify-content:center; align-items:center; height:600px; border:1px dashed #ccc; border-radius:8px;">
|
|
|
|
|
|
|
294 |
<div style="text-align:center;">
|
295 |
+
<h3>App Preview</h3>
|
296 |
+
<p>Generate an app to see it here</p>
|
297 |
</div>
|
298 |
</div>"""
|
299 |
)
|
300 |
|
301 |
# Example button functions
|
302 |
+
def use_example(example_text):
|
303 |
+
return example_text
|
304 |
|
305 |
hello_btn.click(
|
306 |
+
lambda: use_example("A simple hello world app that greets the user by name"),
|
307 |
inputs=None,
|
308 |
outputs=prompt
|
309 |
)
|
310 |
|
311 |
calc_btn.click(
|
312 |
+
lambda: use_example("A calculator app that can add, subtract, multiply and divide two numbers"),
|
313 |
inputs=None,
|
314 |
outputs=prompt
|
315 |
)
|
316 |
|
317 |
+
image_btn.click(
|
318 |
+
lambda: use_example("An image filter app that can apply grayscale, invert, and sepia filters to images"),
|
319 |
inputs=None,
|
320 |
outputs=prompt
|
321 |
)
|
322 |
|
323 |
+
# Generate and run the app
|
324 |
+
def on_generate_click(prompt_text, current_app_id):
|
325 |
if not prompt_text:
|
326 |
+
return current_app_id, "", "Please enter a description of the app you want to generate.", app_frame.value
|
327 |
+
|
328 |
+
# Stop the current app if running
|
329 |
+
if current_app_id:
|
330 |
+
stop_gradio_app(current_app_id)
|
331 |
+
|
332 |
+
# Generate a new app ID
|
333 |
+
new_app_id = random_string()
|
334 |
|
335 |
# Get code from LLM (simulated)
|
336 |
code, error = simulate_llm_response(prompt_text)
|
337 |
if error:
|
338 |
+
return current_app_id, "", f"Error generating code: {error}", app_frame.value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
|
340 |
+
# Run the app
|
341 |
+
port, run_error = run_gradio_app(code, new_app_id)
|
342 |
+
if run_error:
|
343 |
+
return current_app_id, code, f"Error running app: {run_error}", app_frame.value
|
344 |
|
345 |
+
# Create an iframe to display the app
|
346 |
+
iframe_html = f"""
|
347 |
+
<div style="height:600px; border:1px solid #ddd; border-radius:8px; overflow:hidden;">
|
348 |
+
<iframe src="http://localhost:{port}" width="100%" height="100%" frameborder="0"></iframe>
|
349 |
+
</div>
|
350 |
+
"""
|
|
|
|
|
|
|
351 |
|
352 |
+
return new_app_id, code, f"✅ App running on port {port}", iframe_html
|
353 |
+
|
354 |
+
# Stop the app
|
355 |
+
def on_stop_click(current_app_id):
|
356 |
+
if not current_app_id:
|
357 |
+
return "", "No app is currently running"
|
358 |
+
|
359 |
+
stopped = stop_gradio_app(current_app_id)
|
360 |
+
|
361 |
+
if stopped:
|
362 |
+
# Reset the frame
|
363 |
+
iframe_html = """
|
364 |
+
<div style="display:flex; justify-content:center; align-items:center; height:600px; border:1px dashed #ccc; border-radius:8px;">
|
365 |
+
<div style="text-align:center;">
|
366 |
+
<h3>App Stopped</h3>
|
367 |
+
<p>Generate a new app to see it here</p>
|
368 |
+
</div>
|
369 |
+
</div>
|
370 |
+
"""
|
371 |
|
372 |
+
return "", f"✅ App stopped successfully", iframe_html
|
373 |
+
else:
|
374 |
+
return current_app_id, "Failed to stop the app", app_frame.value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
|
376 |
# Connect the generate button
|
377 |
generate_btn.click(
|
378 |
+
on_generate_click,
|
379 |
+
inputs=[prompt, app_id],
|
380 |
+
outputs=[app_id, code_output, status_output, app_frame]
|
381 |
+
)
|
382 |
+
|
383 |
+
# Connect the stop button
|
384 |
+
stop_btn.click(
|
385 |
+
on_stop_click,
|
386 |
+
inputs=[app_id],
|
387 |
+
outputs=[app_id, status_output, app_frame]
|
388 |
)
|
389 |
|
390 |
+
# Launch the main app
|
391 |
if __name__ == "__main__":
|
392 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|