import gradio as gr
import re
import os
import sys
import tempfile
import time
import uuid
import logging
import subprocess
from models import generate_code_for_tab
from utils import find_free_port
# Global variable to track the Gradio subprocess
gradio_process = None
# Configure logging
logger = logging.getLogger(__name__)
def get_gradio_sys_prompt():
"""Get the System Prompt for generating Gradio applications"""
return """
You are an expert Gradio developer. Create a complete, runnable, single-file Gradio application based on the user's request.
The code must be self-contained in a single Python script.
IMPORTANT: The main Gradio app instance MUST be assigned to a variable named `demo`.
The script must end with the app launch command: `demo.launch()`.
Do not include any explanations, just the raw Python code inside a ```python block.
"""
def run_gradio_app(code, port):
"""
Write the Gradio code to a temporary file and run it in a subprocess on the specified port.
"""
temp_dir = tempfile.mkdtemp()
file_path = os.path.join(temp_dir, "app_to_run.py")
with open(file_path, "w") as f:
f.write(code)
env = os.environ.copy()
env["GRADIO_SERVER_PORT"] = str(port)
# Use subprocess.Popen to run the script in a completely isolated process
process = subprocess.Popen([sys.executable, file_path], env=env)
# Return the process object for management
return process, temp_dir, file_path
def get_html_sys_prompt():
"""Get the System Prompt for generating static pages"""
return """
You are an expert front-end developer. Create a complete, modern, and responsive single HTML file based on the user's request.
The file must be self-contained, including all necessary HTML, CSS, and JavaScript.
Do not include any explanations, just the raw HTML code.
"""
def run_gradio_in_thread(code, url_queue, session_id):
"""Run the Gradio application in a separate thread to avoid blocking the main application"""
temp_dir = tempfile.mkdtemp()
file_path = os.path.join(temp_dir, "app.py")
with open(file_path, "w") as f:
f.write(code)
python_executable = sys.executable
process = subprocess.Popen(
[python_executable, file_path],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True
)
running_processes[session_id] = process
for line in process.stdout:
print(f"Gradio App stdout: {line.strip()}")
if "Running on local URL:" in line:
url = line.split("Running on local URL:")[1].strip()
url_queue.put(url)
break
process.wait()
try:
os.remove(file_path)
os.rmdir(temp_dir)
except OSError as e:
print(f"Error cleaning up temp files: {e}")
def get_spinner_html():
"""Return HTML with a CSS spinner animation"""
return """
"""
def generate_code(code_type, model_choice, user_prompt):
"""Generate code and provide a preview based on the user's selected type (static page or Gradio application)."""
global gradio_process
logger.info(f"--- [Code Generation] Start ---")
logger.info(f"Code Type: {code_type}, Model: {model_choice}, Prompt: '{user_prompt}'")
# If a Gradio subprocess is running, terminate it first
if gradio_process and gradio_process.poll() is None:
gradio_process.terminate()
gradio_process.wait(timeout=5)
logger.info("Terminated existing Gradio process.")
if not user_prompt:
yield "Please enter a prompt.", gr.HTML("Preview will appear here."), "Status: Error", gr.update()
return
yield "", gr.HTML(get_spinner_html()), "Status: Initializing...", gr.update()
start_time = time.time()
if code_type == "Gradio App":
port = find_free_port()
system_prompt = get_gradio_sys_prompt()
full_code = ""
for chunk in generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
full_code += chunk
elapsed_time = time.time() - start_time
generated_length = len(full_code)
speed = generated_length / elapsed_time if elapsed_time > 0 else 0
status = f"""
**Status:** Generating...
- **Generation Time:** {elapsed_time:.2f}s
- **Generated Length:** {generated_length} chars
- **Average Speed:** {speed:.2f} char/s
"""
# Display the generated raw code in real-time
yield full_code, gr.update(), status, gr.update()
# Extract the Python code block from the model output
python_code_match = re.search(r"```python\n(.*?)```", full_code, re.DOTALL)
if python_code_match:
python_code = python_code_match.group(1).strip()
else:
# If no code block is found, assume the entire output is code
logger.warning("Could not find Python code block, assuming the entire output is code.")
python_code = full_code.strip()
try:
# Verify that the code can at least be compiled
compile(python_code, '', 'exec')
# Use subprocess to launch the Gradio application
process, temp_dir, temp_file_path = run_gradio_app(python_code, port)
gradio_process = process
# Give the application some time to start
time.sleep(10)
preview_html = f''
status = "**Status:** Generation Complete"
yield python_code, gr.HTML(preview_html), status, gr.Tabs(selected=0)
logger.info(f"Gradio app started on port {port}.")
except SyntaxError as e:
error_message = f"Generated code is not valid Python: {e}"
logger.error(f"Raw output from the model:\n{full_code}")
status = f"**Status:** Error - {error_message}"
yield full_code, gr.HTML(f"{error_message}
"), status, gr.update()
logger.error(error_message)
elif code_type == "Static Page":
system_prompt = get_html_sys_prompt()
full_code_with_think = ""
full_code_for_preview = ""
buffer = ""
is_thinking = False
# The model's raw output is streamed here
for code_chunk in generate_code_for_tab(system_prompt, user_prompt, code_type, model_choice):
full_code_with_think += code_chunk
buffer += code_chunk
# Process the buffer to filter out think tags for the preview
while True:
if is_thinking:
end_index = buffer.find("")
if end_index != -1:
is_thinking = False
buffer = buffer[end_index + len(""):]
else:
break
else:
start_index = buffer.find("")
if start_index != -1:
part_to_add = buffer[:start_index]
full_code_for_preview += part_to_add
is_thinking = True
buffer = buffer[start_index:]
else:
full_code_for_preview += buffer
buffer = ""
break
elapsed_time = time.time() - start_time
generated_length = len(full_code_with_think)
speed = generated_length / elapsed_time if elapsed_time > 0 else 0
status = f"""
**Status:** Generating...
- **Generation Time:** {elapsed_time:.2f}s
- **Generated Length:** {generated_length} chars
- **Average Speed:** {speed:.2f} char/s
"""
# In the generation process, we only update the code and status, not the preview.
# The preview will be updated once at the very end.
yield full_code_with_think, gr.update(), status, gr.update()
# Final update for the preview without the spinner
escaped_code = full_code_for_preview.replace("'", "'").replace('"', '"')
final_preview_html = f"""
"""
status = "**Status:** Generation Complete"
yield full_code_with_think, gr.HTML(final_preview_html), status, gr.Tabs(selected=0)
logger.info("Static page streaming finished.")
def toggle_fullscreen(is_fullscreen):
"""Toggle fullscreen mode visibility"""
is_fullscreen = not is_fullscreen
new_button_text = "Exit Fullscreen" if is_fullscreen else "Fullscreen Preview"
panel_visibility = not is_fullscreen
return is_fullscreen, gr.update(value=new_button_text), gr.update(visible=panel_visibility)
def cleanup_process():
"""Terminate the subprocess when the application exits"""
global gradio_process
if gradio_process and gradio_process.poll() is None:
gradio_process.terminate()
gradio_process.wait()
logger.info("Cleaned up Gradio process on exit.")
import atexit
atexit.register(cleanup_process)
def create_code_tab():
"""Create the UI Tab for the code generation feature"""
fullscreen_state = gr.State(False)
html_examples = [
"Write a hello world alert",
"Create a Canvas animation of continuous colorful fireworks blooming on a black background.",
"Generate a Canvas special effect with iridescent light streams.",
"Design a particle system Canvas animation that interacts with the mouse.",
"Implement a classic snake game using HTML Canvas.",
"Create a Canvas animation simulating the 'Elephant\\\'s Toothpaste' chemical experiment: in a container, colored foam continuously and rapidly gushes out, expands, and overflows, filling the entire screen. Use a suitable drawing js library, such as three.js.",
"Create a dreamy low-poly floating island scene with dynamic lighting and soft animations in a single HTML file. Use a suitable drawing js library, such as three.js.",
]
gradio_examples = [
"Write a hello world alert",
"Create a simple calculator that supports addition, subtraction, multiplication, and division",
"Create a simple Gradio application that displays 'Hello, Gradio!'",
"Create an image classification application that can classify uploaded images",
"Create a text summarization application that can summarize long input texts",
"Create a simple chatbot",
]
def update_examples(code_type):
if code_type == "Static Page":
return gr.update(visible=True), gr.update(visible=False)
else:
return gr.update(visible=False), gr.update(visible=True)
with gr.Blocks() as demo:
with gr.Row():
with gr.Column(scale=1) as left_panel:
gr.Markdown("### 1. Select Code Type")
code_type_radio = gr.Radio(["Static Page", "Gradio App"], value="Static Page", label="Code Type")
gr.Markdown("### 2. Select Model")
model_choice_radio = gr.Radio(
["Better (using Ling-1T)", "Faster (using Ring-flash-2.0)"],
value="Faster (using Ring-flash-2.0)",
label="Model Selection"
)
gr.Markdown("### 3. Enter Your Requirements")
prompt_input = gr.Textbox(lines=5, placeholder="e.g., Create a simple page with a title and a button", label="Prompt")
with gr.Column() as html_examples_column:
html_examples_component = gr.Examples(
examples=html_examples,
inputs=prompt_input,
label="✨ Why not try these cool examples",
examples_per_page=12,
)
with gr.Column(visible=False) as gradio_examples_column:
gradio_examples_component = gr.Examples(
examples=gradio_examples,
inputs=prompt_input,
label="✨ Why not try these cool examples",
examples_per_page=12,
)
generate_button = gr.Button("Generate Code", variant="primary")
status_output = gr.Markdown(value="Status: Waiting", visible=True)
with gr.Column(scale=2):
with gr.Tabs(elem_id="result_tabs") as result_tabs:
with gr.TabItem("Live Preview", id=0):
with gr.Row():
gr.Markdown("### 3. Live Preview")
fullscreen_button = gr.Button("Fullscreen Preview", scale=0)
preview_output = gr.HTML(value="Preview will appear here.
")
with gr.TabItem("Generated Source Code", id=1):
gr.Markdown("### 4. Generated Source Code")
code_output = gr.Code(language="html", label="Generated Code")
code_type_radio.change(fn=update_examples, inputs=code_type_radio, outputs=[html_examples_column, gradio_examples_column])
generate_button.click(
fn=generate_code,
inputs=[code_type_radio, model_choice_radio, prompt_input],
outputs=[code_output, preview_output, status_output, result_tabs]
)
fullscreen_button.click(
fn=toggle_fullscreen,
inputs=[fullscreen_state],
outputs=[fullscreen_state, fullscreen_button, left_panel]
)
return demo