Spaces:
Sleeping
Sleeping
import gradio as gr | |
import requests | |
import re | |
import os | |
import subprocess | |
import uuid | |
import time | |
import json | |
import sys | |
import importlib.util | |
import tempfile | |
import signal | |
import atexit | |
from pathlib import Path | |
# Print Python version info for debugging | |
print(f"Python version: {sys.version}") | |
print(f"Python executable: {sys.executable}") | |
# Application directories | |
APP_DIR = Path(os.getcwd()) | |
VENV_DIR = APP_DIR / "venvs" | |
os.makedirs(VENV_DIR, exist_ok=True) | |
# Make sure we have virtualenv installed | |
try: | |
import venv | |
print("venv module available") | |
except ImportError: | |
subprocess.run([sys.executable, "-m", "pip", "install", "virtualenv"], check=True) | |
print("Installed virtualenv") | |
# Track running processes | |
running_processes = [] | |
# Clean up any running processes on exit | |
def cleanup_processes(): | |
for process in running_processes: | |
try: | |
if process.poll() is None: # If process is still running | |
process.terminate() | |
process.wait(timeout=5) | |
print(f"Terminated process {process.pid}") | |
except Exception as e: | |
print(f"Error terminating process: {e}") | |
atexit.register(cleanup_processes) | |
def call_openai_api(api_key, prompt): | |
"""Call OpenAI API to generate Gradio app code and requirements""" | |
headers = { | |
"Content-Type": "application/json", | |
"Authorization": f"Bearer {api_key}" | |
} | |
system_prompt = """You are an expert at creating Python applications with Gradio. | |
Your task is to create a complete, standalone Gradio application based on the user's prompt. | |
Provide your response in the following JSON format: | |
{ | |
"app_code": "# Your Python code here...", | |
"requirements": ["gradio==3.50.2", "numpy", "pandas", ...], | |
"app_name": "descriptive-name-of-app", | |
"description": "Brief description of what the app does" | |
} | |
Important guidelines: | |
1. The app_code should be a complete Gradio application. | |
2. Include gradio.launch(server_name="0.0.0.0", server_port=8000) at the end of the app. | |
3. Don't use any resource that requires internet access (no API calls). | |
4. Only use libraries that can be installed via pip. | |
5. First requirement should be gradio==3.50.2. | |
6. Make sure your code only uses the import statements for packages in the requirements list. | |
7. Make the app functionality self-contained and robust. | |
8. Don't use any buttons' .click() methods or event handlers - use gr.Interface() instead. | |
9. Don't create directories or write to any file paths. | |
10. Don't use flagging callbacks or features. | |
11. Don't use __name__ == "__main__" checks, just have the launch call at the end. | |
Here's a simple template to follow: | |
```python | |
import gradio as gr | |
import numpy as np | |
# Define your functions here | |
def process_data(input_data): | |
result = input_data * 2 # Simple example | |
return f"Processed: {result}" | |
# Create the Gradio interface | |
demo = gr.Interface( | |
fn=process_data, | |
inputs=gr.Number(label="Input Data"), | |
outputs=gr.Textbox(label="Result"), | |
title="Data Processor" | |
) | |
# Launch the app | |
demo.launch(server_name="0.0.0.0", server_port=8000) | |
``` | |
""" | |
data = { | |
"model": "gpt-4o", | |
"messages": [ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": prompt} | |
], | |
"temperature": 0.2, | |
"max_tokens": 4000 | |
} | |
try: | |
response = requests.post( | |
"https://api.openai.com/v1/chat/completions", | |
headers=headers, | |
json=data | |
) | |
if response.status_code != 200: | |
return None, f"API Error: {response.status_code} - {response.text}" | |
result = response.json() | |
content = result["choices"][0]["message"]["content"] | |
# Try to parse the JSON response | |
try: | |
# Extract JSON from response | |
json_pattern = r'```json\s*([\s\S]*?)```|({[\s\S]*})' | |
json_matches = re.findall(json_pattern, content) | |
json_str = "" | |
for match in json_matches: | |
if match[0]: # From code block | |
json_str = match[0] | |
break | |
elif match[1]: # Direct JSON | |
json_str = match[1] | |
break | |
if not json_str: | |
json_str = content # Try the whole content | |
app_info = json.loads(json_str) | |
# Extract Python code if it's wrapped in code blocks | |
if "```python" in app_info["app_code"]: | |
code_pattern = r'```python\s*([\s\S]*?)```' | |
code_match = re.search(code_pattern, app_info["app_code"]) | |
if code_match: | |
app_info["app_code"] = code_match.group(1) | |
return app_info, None | |
except json.JSONDecodeError: | |
# Fallback pattern matching if JSON parsing fails | |
app_code_pattern = r'```python\s*([\s\S]*?)```' | |
app_code_matches = re.findall(app_code_pattern, content) | |
app_code = app_code_matches[0] if app_code_matches else "" | |
if not app_code: | |
return None, "Could not extract app code from response" | |
# Try to extract requirements | |
req_pattern = r'```(?:python)?\s*.*?import\s+([a-zA-Z0-9_]+)' | |
req_matches = re.findall(req_pattern, content) | |
requirements = ["gradio==3.50.2"] | |
if req_matches: | |
for module in req_matches: | |
if module != "gradio" and module not in requirements: | |
requirements.append(module) | |
# Construct a partial app_info | |
app_info = { | |
"app_code": app_code, | |
"requirements": requirements, | |
"app_name": f"gradio-app-{int(time.time())}", | |
"description": "Generated Gradio application" | |
} | |
return app_info, None | |
except Exception as e: | |
return None, f"Error: {str(e)}" | |
def create_virtual_env(app_info): | |
"""Create a virtual environment with the required packages""" | |
try: | |
# Create a unique ID for this app | |
app_id = str(uuid.uuid4())[:8] | |
app_name = f"{app_info['app_name']}-{app_id}" | |
venv_path = VENV_DIR / app_name | |
# Create a virtual environment | |
print(f"Creating virtual environment at {venv_path}") | |
subprocess.run([sys.executable, "-m", "venv", str(venv_path)], check=True) | |
# Determine the pip executable path | |
if os.name == 'nt': # Windows | |
pip_path = venv_path / "Scripts" / "pip" | |
else: # Unix/Linux | |
pip_path = venv_path / "bin" / "pip" | |
# Install required packages | |
print(f"Installing packages: {', '.join(app_info['requirements'])}") | |
subprocess.run([str(pip_path), "install", "--upgrade", "pip"], check=True) | |
for req in app_info["requirements"]: | |
subprocess.run([str(pip_path), "install", req], check=True) | |
return { | |
"app_id": app_id, | |
"app_name": app_name, | |
"venv_path": str(venv_path), | |
"pip_path": str(pip_path), | |
"description": app_info["description"], | |
"code": app_info["app_code"], | |
"requirements": app_info["requirements"] | |
} | |
except Exception as e: | |
return None, f"Error creating virtual environment: {str(e)}" | |
def run_app_in_venv(app_details, app_code): | |
"""Run the Gradio app in a virtual environment""" | |
try: | |
# Determine the Python executable path | |
if os.name == 'nt': # Windows | |
python_path = Path(app_details["venv_path"]) / "Scripts" / "python" | |
else: # Unix/Linux | |
python_path = Path(app_details["venv_path"]) / "bin" / "python" | |
# Create a temporary file for the app code | |
with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as f: | |
app_file = f.name | |
f.write(app_code.encode('utf-8')) | |
# Change the port to 8000 if not already set | |
if "server_port" not in app_code: | |
with open(app_file, 'a') as f: | |
f.write("\n\n# Ensure the app is running on port 8000\nif 'demo' in locals():\n demo.launch(server_name='0.0.0.0', server_port=8000)\n") | |
# Run the app | |
print(f"Running app with Python: {python_path}") | |
process = subprocess.Popen( | |
[str(python_path), app_file], | |
stdout=subprocess.PIPE, | |
stderr=subprocess.PIPE, | |
text=True | |
) | |
running_processes.append(process) | |
# Wait a bit for the app to start | |
time.sleep(5) | |
# Check if the process is still running | |
if process.poll() is not None: | |
stdout, stderr = process.communicate() | |
return None, f"App failed to start: {stderr}" | |
return { | |
"process": process, | |
"app_file": app_file, | |
"url": "http://localhost:8000", | |
"process_id": process.pid | |
}, None | |
except Exception as e: | |
return None, f"Error running app: {str(e)}" | |
def stop_running_app(app_process): | |
"""Stop a running Gradio app process""" | |
if app_process and app_process.poll() is None: | |
app_process.terminate() | |
try: | |
app_process.wait(timeout=5) | |
except subprocess.TimeoutExpired: | |
app_process.kill() | |
if app_process in running_processes: | |
running_processes.remove(app_process) | |
return True | |
return False | |
# Create the Gradio interface | |
with gr.Blocks(title="Gradio App Generator") as demo: | |
gr.Markdown("# 🤖 Gradio App Generator") | |
gr.Markdown(""" | |
This app generates a Gradio application based on your description and runs it in a virtual environment | |
with the required dependencies. The generated app will be displayed below. | |
""") | |
# State variables to track the running app | |
running_app_process = gr.State(None) | |
running_app_file = gr.State(None) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
api_key = gr.Textbox( | |
label="OpenAI API Key", | |
placeholder="sk-...", | |
type="password", | |
info="Your key is used only for this session" | |
) | |
prompt = gr.Textbox( | |
label="App Description", | |
placeholder="Describe the Gradio app you want to create...", | |
lines=5 | |
) | |
install_bar = gr.HTML( | |
"<div id='install-status'></div>", | |
visible=False, | |
label="Installation Status" | |
) | |
with gr.Row(): | |
generate_btn = gr.Button("Generate & Run App", variant="primary") | |
stop_btn = gr.Button("Stop Running App", variant="stop", visible=False) | |
with gr.Accordion("Generated Code", open=False): | |
code_output = gr.Code(language="python", label="App Code") | |
with gr.Accordion("Requirements", open=False): | |
req_output = gr.Textbox(label="Required Packages") | |
status_output = gr.Markdown("") | |
with gr.Column(scale=2): | |
# Frame to display the running app | |
app_frame = gr.HTML("<div style='text-align:center; padding:50px;'><h3>Your generated app will appear here</h3></div>") | |
def on_generate(api_key_val, prompt_val, running_process, app_file): | |
# Stop any previously running app | |
if running_process: | |
stop_running_app(running_process) | |
# Clean up previous app file | |
if app_file and os.path.exists(app_file): | |
try: | |
os.unlink(app_file) | |
except: | |
pass | |
# Validate API key | |
if not api_key_val or len(api_key_val) < 20 or not api_key_val.startswith("sk-"): | |
return ( | |
None, None, None, "⚠️ Please provide a valid OpenAI API key", | |
"<div style='text-align:center; padding:50px;'><h3>Invalid API key</h3></div>", | |
gr.update(visible=False), gr.update(visible=False), running_process, app_file | |
) | |
try: | |
# Generate code via API | |
app_info, api_error = call_openai_api(api_key_val, prompt_val) | |
if api_error or not app_info: | |
return ( | |
None, None, None, f"⚠️ {api_error or 'Failed to generate app'}", | |
"<div style='text-align:center; padding:50px;'><h3>Error generating app</h3></div>", | |
gr.update(visible=False), gr.update(visible=False), running_process, app_file | |
) | |
# Create virtual environment with required packages | |
install_status = f"<div style='padding:10px; background-color:#f0f0f0; border-radius:5px;'>" | |
install_status += f"<p>Creating virtual environment for '{app_info['app_name']}'...</p>" | |
install_status += f"<p>Installing packages: {', '.join(app_info['requirements'])}</p>" | |
install_status += "</div>" | |
yield ( | |
app_info["app_code"], "\n".join(app_info["requirements"]), None, | |
"⏳ Setting up virtual environment and installing packages...", | |
"<div style='text-align:center; padding:50px;'><h3>Preparing your app...</h3></div>", | |
gr.update(visible=True, value=install_status), gr.update(visible=False), running_process, app_file | |
) | |
venv_details, venv_error = create_virtual_env(app_info) | |
if venv_error or not venv_details: | |
return ( | |
app_info["app_code"], "\n".join(app_info["requirements"]), None, | |
f"⚠️ {venv_error or 'Failed to create virtual environment'}", | |
"<div style='text-align:center; padding:50px;'><h3>Error setting up environment</h3></div>", | |
gr.update(visible=False), gr.update(visible=False), running_process, app_file | |
) | |
# Run the app | |
app_result, app_error = run_app_in_venv(venv_details, app_info["app_code"]) | |
if app_error or not app_result: | |
return ( | |
app_info["app_code"], "\n".join(app_info["requirements"]), None, | |
f"⚠️ {app_error or 'Failed to run app'}", | |
"<div style='text-align:center; padding:50px;'><h3>Error running app</h3></div>", | |
gr.update(visible=False), gr.update(visible=False), running_process, app_file | |
) | |
# Display the running app in an iframe | |
iframe_html = f""" | |
<div style="height:600px; border:1px solid #ddd; border-radius:5px; overflow:hidden;"> | |
<iframe src="http://localhost:8000" width="100%" height="100%" frameborder="0"></iframe> | |
</div> | |
""" | |
return ( | |
app_info["app_code"], "\n".join(app_info["requirements"]), None, | |
f"✅ App '{venv_details['app_name']}' is running! View it below.", | |
iframe_html, | |
gr.update(visible=False), gr.update(visible=True), app_result["process"], app_result["app_file"] | |
) | |
except Exception as e: | |
import traceback | |
error_details = traceback.format_exc() | |
return ( | |
None, None, None, f"⚠️ Error: {str(e)}\n\n{error_details}", | |
"<div style='text-align:center; padding:50px;'><h3>An error occurred</h3></div>", | |
gr.update(visible=False), gr.update(visible=False), running_process, app_file | |
) | |
def on_stop(running_process, app_file): | |
if running_process: | |
stopped = stop_running_app(running_process) | |
# Clean up app file | |
if app_file and os.path.exists(app_file): | |
try: | |
os.unlink(app_file) | |
except: | |
pass | |
if stopped: | |
return ( | |
"✅ App stopped successfully", | |
"<div style='text-align:center; padding:50px;'><h3>App stopped</h3></div>", | |
gr.update(visible=False), None, None | |
) | |
return ( | |
"⚠️ No app was running", | |
"<div style='text-align:center; padding:50px;'><h3>No app was running</h3></div>", | |
gr.update(visible=False), None, None | |
) | |
generate_btn.click( | |
on_generate, | |
inputs=[api_key, prompt, running_app_process, running_app_file], | |
outputs=[ | |
code_output, req_output, install_bar, status_output, app_frame, | |
install_bar, stop_btn, running_app_process, running_app_file | |
] | |
) | |
stop_btn.click( | |
on_stop, | |
inputs=[running_app_process, running_app_file], | |
outputs=[status_output, app_frame, stop_btn, running_app_process, running_app_file] | |
) | |
if __name__ == "__main__": | |
demo.queue().launch(server_name="0.0.0.0", server_port=7860) |