Spaces:
Running
Running
File size: 11,656 Bytes
dfc5e93 4917053 cd13883 4917053 0e4a7a5 4917053 cd13883 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 cd13883 4917053 0e4a7a5 5f4f3c1 4917053 0e4a7a5 853d569 4917053 0e4a7a5 4917053 0e4a7a5 4917053 9376840 0e4a7a5 4917053 b752712 4917053 0e4a7a5 b752712 4917053 cd13883 0e4a7a5 4917053 58a5e73 cd13883 4917053 0e4a7a5 4917053 0e4a7a5 4917053 cd13883 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 cd13883 0950920 58a5e73 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 58a5e73 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 0e4a7a5 4917053 83207ef 0e4a7a5 4917053 dfc5e93 0e4a7a5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
import gradio as gr
from huggingface_hub import InferenceClient
import os
# --- Configuration ---
# Use environment variable for token, fallback to default if not set
# For Spaces, set the HF_TOKEN secret
API_TOKEN = os.getenv("HF_TOKEN", None)
MODEL = "HuggingFaceH4/zephyr-7b-beta" # Or choose another suitable model
# --- Initialize Inference Client ---
try:
print(f"Attempting to initialize Inference Client for model: {MODEL}")
if API_TOKEN:
print("Using HF Token found in environment.")
client = InferenceClient(model=MODEL, token=API_TOKEN)
else:
print("HF Token not found. Running without token (may lead to rate limits).")
client = InferenceClient(model=MODEL)
# Optional: Add a quick health check if needed, though client init usually suffices
# client.get_model_status(MODEL) # Example check, might raise if model invalid
print("Inference Client initialized successfully.")
except Exception as e:
print(f"Error initializing Inference Client: {e}")
# Provide a more informative error in the Gradio interface
raise gr.Error(f"Failed to initialize the AI model client for '{MODEL}'. Please check the model name and ensure network connectivity. If running locally without a token, you might hit rate limits. If using HF Spaces, ensure the HF_TOKEN secret is set correctly. Original Error: {e}")
# --- Core Code Generation Function ---
def generate_code(
prompt: str,
backend_choice: str,
file_structure: str,
max_tokens: int,
temperature: float,
top_p: float,
):
"""
Generates website code based on user prompt and choices.
Yields the code token by token for live updates.
"""
print(f"--- Generating Code ---")
print(f"Prompt: {prompt[:100]}...") # Log truncated prompt
print(f"Backend Context: {backend_choice}")
print(f"File Structure: {file_structure}")
print(f"Settings: Max Tokens={max_tokens}, Temp={temperature}, Top-P={top_p}")
# --- System Message (Internal) ---
# Guides the AI's behavior. Not user-editable in the UI.
# Refined system prompt for clarity and stricter output formatting
system_message = (
"You are an expert frontend web developer AI. Your task is to generate HTML, CSS, and potentially JavaScript code "
"for a website based ONLY on the user's description. Adhere strictly to the following constraints:\n"
"1. **Output ONLY Code:** Generate only the raw code for the requested files (`index.html`, `style.css`, `script.js`). Do NOT include any introductory text, explanations, apologies, markdown formatting (like ```html), or closing remarks. Your response must start *immediately* with the code (e.g., `<!DOCTYPE html>` or `<!-- index.html -->`).\n"
"2. **index.html is Mandatory:** ALWAYS generate a complete `index.html` file.\n"
"3. **File Structure:**\n"
f" - If '{file_structure}' is 'Multiple Files':\n"
" - Use clear markers EXACTLY as follows:\n"
" `<!-- index.html -->`\n"
" `/* style.css */`\n"
" `// script.js` (only include if JavaScript is necessary for the described functionality)\n"
" - Place the corresponding code directly after each marker.\n"
" - Link the CSS (`<link rel='stylesheet' href='style.css'>`) in the `<head>` of `index.html`.\n"
" - Include the JS (`<script src='script.js'></script>`) just before the closing `</body>` tag in `index.html` if `script.js` is generated.\n"
f" - If '{file_structure}' is 'Single File':\n"
" - Embed ALL CSS within `<style>` tags inside the `<head>` of the `index.html` file.\n"
" - Embed ALL necessary JavaScript within `<script>` tags just before the closing `</body>` tag of the `index.html` file.\n"
"4. **Backend Context ({backend_choice}):** This choice provides context. For 'Flask' or 'Node.js', you might include standard template placeholders (like `{{ variable }}` for Flask/Jinja2 or similar patterns for Node templating engines if appropriate for the frontend structure), but primarily focus on generating the static frontend assets (HTML structure, CSS styling, client-side JS interactions). For 'Static', generate standard HTML/CSS/JS without backend-specific placeholders.\n"
"5. **Focus on Frontend:** Generate the client-side code. Do not generate server-side Flask or Node.js code.\n"
"6. **Completeness:** Generate functional code based on the prompt. If the prompt is vague, create a reasonable default structure."
)
# --- Construct the messages for the API ---
messages = [
{"role": "system", "content": system_message},
{"role": "user", "content": f"Create a website based on this description: {prompt}"} # Make user role explicit
]
# --- Stream the response from the API ---
response_stream = ""
try:
print("Sending request to Hugging Face Inference API...")
# CORRECTED: Removed stop_sequences
for message in client.chat_completion(
messages=messages,
max_tokens=max_tokens,
stream=True,
temperature=temperature,
top_p=top_p,
):
token = message.choices[0].delta.content
# Basic check to ensure token is a string (it should be)
if isinstance(token, str):
response_stream += token
# Clean potential unwanted prefixes sometimes added by models
# if response_stream.strip().startswith(("```html", "```")):
# response_stream = response_stream.split("\n", 1)[-1]
yield response_stream # Yield the cumulative response for live update
print(f"API stream finished. Total length: {len(response_stream)}")
# Optional: Post-process to remove leading/trailing whitespace or markdown
final_response = response_stream.strip()
# More aggressive cleaning if needed:
# if final_response.startswith("```html"):
# final_response = final_response[7:]
# if final_response.endswith("```"):
# final_response = final_response[:-3]
# yield final_response.strip() # Yield final cleaned response
except Exception as e:
error_message = f"An error occurred during the API call: {e}"
print(error_message)
# Display the error clearly in the output box
yield f"## Error\n\nFailed to generate code.\n**Reason:** {e}\n\nPlease check the model status, your connection, and API token (if applicable)."
# --- Build Gradio Interface using Blocks ---
with gr.Blocks(css=".gradio-container { max-width: 90% !important; }") as demo: # Add CSS for wider layout
gr.Markdown("# Website Code Generator π")
gr.Markdown(
"Describe the website you want, choose your options, and the AI will generate the frontend code (HTML, CSS, JS). "
"The code will appear live in the text editor below. **Note:** The AI generates only frontend code based on your description."
)
with gr.Row():
with gr.Column(scale=2):
prompt_input = gr.Textbox(
label="Website Description",
placeholder="e.g., A simple landing page with a navigation bar (Home, About, Contact), a hero section with a title and button, and a simple footer.",
lines=5, # Increased lines for better prompt visibility
)
backend_radio = gr.Radio(
["Static", "Flask", "Node.js"],
label="Backend Context Hint",
value="Static",
info="Hint for AI: influences potential template placeholders (e.g., {{var}}) but AI generates ONLY frontend code.",
)
file_structure_radio = gr.Radio(
["Multiple Files", "Single File"], # Default to multiple for clarity
label="Output File Structure",
value="Multiple Files",
info="Generate separate files (index.html, style.css, script.js) or embed all in index.html?",
)
generate_button = gr.Button("Generate Website Code", variant="primary")
with gr.Column(scale=3):
# Use Code component which is better suited for displaying code
code_output = gr.Code(
label="Generated Code",
language="html", # Base language, will contain CSS/JS markers if multiple files
lines=28, # Increased lines
interactive=False, # Read-only display
)
with gr.Accordion("Advanced Generation Settings", open=False):
max_tokens_slider = gr.Slider(
minimum=512, # Increased minimum for potentially complex sites
maximum=4096, # Match common context lengths
value=2048, # Increased default
step=128,
label="Max New Tokens",
info="Maximum number of tokens (approx. words/code elements) the AI can generate."
)
temperature_slider = gr.Slider(
minimum=0.1,
maximum=1.2, # Allow slightly higher for more creativity if needed
value=0.6, # Slightly lower default for more predictable code
step=0.1,
label="Temperature",
info="Controls randomness. Lower values (e.g., 0.2) make output more focused, higher values (e.g., 0.9) make it more creative/random."
)
top_p_slider = gr.Slider(
minimum=0.1,
maximum=1.0,
value=0.9, # Slightly lower default top-p
step=0.05,
label="Top-P (Nucleus Sampling)",
info="Alternative to temperature for controlling randomness. Considers only the most probable tokens with cumulative probability p."
)
# --- Connect Inputs/Outputs to the Function ---
generate_button.click(
fn=generate_code,
inputs=[
prompt_input,
backend_radio,
file_structure_radio,
max_tokens_slider,
temperature_slider,
top_p_slider,
],
outputs=code_output,
#api_name="generate_website_code" # Optional: for API usage
)
# Add examples for guidance
gr.Examples(
examples=[
["A simple counter page with a number display, an increment button, and a decrement button. Use Javascript for the logic.", "Static", "Single File"],
["A login form with fields for username and password, and a submit button. Basic styling.", "Static", "Multiple Files"],
["Product cards display grid. Each card should show an image, product name, price, and an 'Add to Cart' button. Make it responsive.", "Static", "Multiple Files"],
["A personal blog homepage with a header, a list of recent posts (just placeholders), and a sidebar with categories.", "Flask", "Multiple Files"],
],
inputs=[prompt_input, backend_radio, file_structure_radio],
label="Example Prompts" # Optional label for the examples section
)
# --- Launch the App ---
if __name__ == "__main__":
print("Starting Gradio app...")
# Enable queuing for handling multiple users, essential for Spaces
# Increase concurrency count if needed and if your hardware/Space plan supports it
demo.queue(max_size=10).launch(
# debug=True, # Set debug=False for production/Spaces deployment
# share=False # Set share=True to create a temporary public link (useful for local testing)
)
print("Gradio app launched.") |