import gradio as gr import requests import re import os import subprocess import tempfile import sys import json import time import threading import queue import atexit # Print Python version and gradio version for debugging print(f"Python version: {sys.version}") print(f"Gradio version: {gr.__version__}") # Track running processes and resources for cleanup running_process = None temp_files = [] # Clean up resources on exit def cleanup_resources(): global running_process, temp_files # Stop any running process if running_process and running_process.poll() is None: try: running_process.terminate() running_process.wait(timeout=5) print(f"Terminated process {running_process.pid}") except Exception as e: print(f"Error terminating process: {e}") # Remove temporary files for file_path in temp_files: if os.path.exists(file_path): try: os.unlink(file_path) print(f"Removed temporary file: {file_path}") except Exception as e: print(f"Error removing file {file_path}: {e}") atexit.register(cleanup_resources) # Queue for capturing subprocess output output_queue = queue.Queue() def read_output(process, output_queue): """Read output from a process and put it in a queue""" for line in iter(process.stdout.readline, ''): if line: output_queue.put(line) process.stdout.close() def read_error(process, output_queue): """Read error output from a process and put it in a queue""" for line in iter(process.stderr.readline, ''): if line: output_queue.put(f"ERROR: {line}") process.stderr.close() 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. Create a complete, standalone Gradio application based on the user's prompt. Provide your response in the following JSON format: { "app_code": "# Python code here...", "requirements": ["gradio==3.32.0", "other_packages"], "description": "Brief description of what the app does" } Important guidelines: 1. The app_code should be a complete Gradio application 2. Use ONLY gr.Interface (NOT gr.Blocks) 3. Always include server_name="0.0.0.0" and server_port=7861 in the launch parameters 4. First requirement must be exactly "gradio==3.32.0" 5. Keep the app simple and focused on the user's request 6. DO NOT use any flagging callbacks or flagging_dir parameters in launch 7. DO NOT create or use any directories or file paths 8. Do not use features from newer Gradio versions Example: ```python import gradio as gr import numpy as np def process(input_value): return input_value * 2 demo = gr.Interface( fn=process, inputs=gr.Number(label="Input"), outputs=gr.Number(label="Output"), title="Number Doubler" ) demo.launch(server_name="0.0.0.0", server_port=7861) ``` """ 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: # Look for JSON object json_pattern = r'({[\s\S]*})' json_match = re.search(json_pattern, content) if json_match: json_str = json_match.group(1) app_info = json.loads(json_str) else: # Try to extract code and requirements manually code_pattern = r'```python\s*([\s\S]*?)```' code_match = re.search(code_pattern, content) if not code_match: return None, "No code found in the response" code = code_match.group(1) # Extract requirements from imports req_pattern = r'import\s+([a-zA-Z0-9_]+)' req_matches = re.findall(req_pattern, code) requirements = ["gradio==3.32.0"] for module in req_matches: if module != "gradio" and module not in requirements and module not in ["os", "sys"]: requirements.append(module) app_info = { "app_code": code, "requirements": requirements, "description": "Generated Gradio application" } # Clean up the code if needed 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 Exception as e: return None, f"Failed to parse API response: {str(e)}" except Exception as e: return None, f"API call failed: {str(e)}" def install_package(package, output_queue): """Install a package and send output to the queue""" cmd = [sys.executable, "-m", "pip", "install", "--upgrade", package] print(f"Installing: {package}") output_queue.put(f"Installing: {package}\n") process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1 ) # Start threads to read output stdout_thread = threading.Thread(target=read_output, args=(process, output_queue)) stderr_thread = threading.Thread(target=read_error, args=(process, output_queue)) stdout_thread.daemon = True stderr_thread.daemon = True stdout_thread.start() stderr_thread.start() # Wait for the process to complete process.wait() # Wait for threads to finish stdout_thread.join() stderr_thread.join() if process.returncode == 0: output_queue.put(f"Successfully installed {package}\n") return True else: output_queue.put(f"Failed to install {package} (error code: {process.returncode})\n") return False def run_app(app_code, output_queue): """Run the app in a separate process""" global running_process, temp_files # Stop any existing process if running_process and running_process.poll() is None: running_process.terminate() try: running_process.wait(timeout=5) except subprocess.TimeoutExpired: running_process.kill() # Create a temporary file for the app fd, app_file = tempfile.mkstemp(suffix='.py') os.close(fd) with open(app_file, 'w') as f: f.write(app_code) temp_files.append(app_file) # Make sure the code has the correct launch parameters with open(app_file, 'r') as f: app_code = f.read() # Remove flagging_callback if present app_code = re.sub(r'flagging_callback\s*=\s*[^,)]+', '', app_code) # Remove flagging_dir if present app_code = re.sub(r'flagging_dir\s*=\s*[^,)]+', '', app_code) # Make sure it has the correct launch parameters if "demo.launch" not in app_code: app_code += "\n\ndemo.launch(server_name='0.0.0.0', server_port=7861)\n" elif "server_name" not in app_code or "server_port" not in app_code: app_code = re.sub(r'demo\.launch\s*\(', r'demo.launch(server_name="0.0.0.0", server_port=7861, ', app_code) # Write the updated code back to the file with open(app_file, 'w') as f: f.write(app_code) # Run the app output_queue.put(f"Starting the app...\n") running_process = subprocess.Popen( [sys.executable, app_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1 ) # Start threads to read output stdout_thread = threading.Thread(target=read_output, args=(running_process, output_queue)) stderr_thread = threading.Thread(target=read_error, args=(running_process, output_queue)) stdout_thread.daemon = True stderr_thread.daemon = True stdout_thread.start() stderr_thread.start() # Wait a bit for the app to start time.sleep(5) # Check if the process is still running if running_process.poll() is not None: output_queue.put(f"App failed to start (error code: {running_process.returncode})\n") return False, app_file output_queue.put(f"App is running on port 7861\n") return True, app_file # 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, installs required packages, and runs it directly within this container. The generated app will be displayed below. """) 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 ) 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") progress_output = gr.Textbox( label="Progress Log", lines=10, max_lines=20, interactive=False ) status_output = gr.Markdown("") with gr.Column(scale=2): # Frame to display the running app app_frame = gr.HTML("