Spaces:
Running
Running
File size: 9,047 Bytes
dfc5e93 5f4f3c1 cd13883 83207ef cd13883 83207ef cd13883 83207ef cd13883 83207ef cd13883 83207ef cd13883 83207ef 853d569 cd13883 5f4f3c1 853d569 cd13883 853d569 cd13883 853d569 dfc5e93 b752712 cd13883 b752712 dfc5e93 cd13883 83207ef cd13883 83207ef cd13883 83207ef cd13883 0950920 83207ef cd13883 f4eb547 0950920 cd13883 83207ef cd13883 83207ef cd13883 f4eb547 cd13883 83207ef f4eb547 83207ef cd13883 83207ef dfc5e93 cd13883 |
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 |
import gradio as gr
from huggingface_hub import InferenceClient
import re
import os # Good practice to import os if needed, though not strictly used here yet
# --- Hugging Face Token (Optional but Recommended) ---
# It's better to use a token, especially for private models or higher rate limits
# from huggingface_hub import login
# login("YOUR_HUGGINGFACE_TOKEN") # Replace with your actual token or set HF_TOKEN env var
# --- Inference Client ---
# Consider adding error handling for client initialization if needed
try:
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
except Exception as e:
print(f"Error initializing InferenceClient: {e}")
# Optionally, raise the exception or handle it gracefully in the UI
# For now, we'll let it proceed and potentially fail later if client is None
client = None
# --- Parsing Function ---
def parse_files(raw_response):
"""
Parses filenames and code blocks from the raw AI output.
Assumes format:
filename1.ext
```lang # Optional code block marker
code for file1
``` # Optional code block marker
filename2.ext
code for file2
...
"""
if not raw_response:
return []
# Improved pattern to handle optional code blocks and leading/trailing whitespace
# It looks for a filename line followed by content until the next filename line or end of string.
pattern = re.compile(
r"^\s*([\w\-.\/\\]+\.\w+)\s*\n" # Filename line (must have an extension)
r"(.*?)" # Capture content (non-greedy)
r"(?=\n\s*[\w\-.\/\\]+\.\w+\s*\n|\Z)", # Lookahead for next filename or end of string
re.DOTALL | re.MULTILINE
)
files = pattern.findall(raw_response)
# Clean up content: remove potential code block markers and extra whitespace
cleaned_files = []
for name, content in files:
# Remove common code block markers (``` optionally followed by lang)
content_cleaned = re.sub(r"^\s*```[a-zA-Z]*\n?", "", content, flags=re.MULTILINE)
content_cleaned = re.sub(r"\n?```\s*$", "", content_cleaned, flags=re.MULTILINE)
cleaned_files.append((name.strip(), content_cleaned.strip()))
# Handle case where the AI might just output code without filenames
if not cleaned_files and raw_response.strip():
# Basic check if it looks like code (e.g., contains common HTML/CSS/JS chars)
if any(c in raw_response for c in ['<','>','{','}',';','(',')']):
# Default to index.html if no files parsed but content exists
print("Warning: No filenames found, defaulting to index.html")
lang = "html" # Guess language, default to html
if "{" in raw_response and "}" in raw_response and ":" in raw_response:
lang = "css"
elif "function" in raw_response or "const" in raw_response or "let" in raw_response:
lang = "javascript"
# Determine a default filename based on guessed language
default_filename = "index.html"
if lang == "css": default_filename = "style.css"
elif lang == "javascript": default_filename = "script.js"
cleaned_files.append((default_filename, raw_response.strip()))
return cleaned_files
# --- Code Generation Function ---
def generate_code(prompt, backend, system_message, max_tokens, temperature, top_p):
"""Generates code using the InferenceClient."""
if not client:
# Return an error structure if client failed to initialize
return "Error: Inference Client not available.", []
full_sys_msg = f"""
You are a code generation AI. Given a prompt, generate the necessary files for a website using the {backend} backend.
Always include an index.html file.
Respond ONLY with filenames and the raw code for each file.
Each file must start with its filename on a new line. Example:
index.html
<!DOCTYPE html>
<html>
<head><title>My Site</title></head>
<body><h1>Hello</h1></body>
</html>
style.css
body {{
font-family: sans-serif;
}}
script.js
console.log('Hello World!');
Ensure the code is complete and functional for each file. NO commentary, NO explanations, NO markdown formatting like backticks (```).
Start generating the files now.
""".strip()
messages = [
{"role": "system", "content": full_sys_msg + ("\n" + system_message if system_message else "")},
{"role": "user", "content": prompt}
]
try:
response = client.chat_completion(
messages=messages,
max_tokens=int(max_tokens), # Ensure max_tokens is int
temperature=temperature,
top_p=top_p,
stream=False # Ensure streaming is off for this logic
)
raw = response.choices[0].message.content
print("\n--- Raw AI Response ---")
print(raw)
print("----------------------\n")
files = parse_files(raw)
return None, files # Return None for error, and the list of files
except Exception as e:
print(f"Error during AI generation: {e}")
return f"Error during AI generation: {e}", [] # Return error message
# --- Gradio Event Handler ---
def on_generate(prompt, backend, system_message, max_tokens, temperature, top_p):
"""Callback function for the generate button."""
error_msg, files = generate_code(prompt, backend, system_message, max_tokens, temperature, top_p)
if error_msg:
# Display error in a single tab if generation failed
error_tab = gr.TabItem(label="Error", children=[gr.Textbox(value=error_msg, label="Generation Error")])
return gr.Tabs(tabs=[error_tab]) # Return a Tabs component with the error tab
if not files:
# Display message if no files were parsed
no_files_tab = gr.TabItem(label="Output", children=[gr.Textbox(value="AI did not return recognizable file content. Check raw output in console.", label="Result")])
return gr.Tabs(tabs=[no_files_tab]) # Return a Tabs component with this message
tabs = []
for name, content in files:
name = name.strip()
content = content.strip()
if not name or not content: # Skip empty names or content
print(f"Skipping file with empty name or content: Name='{name}'")
continue
# Determine language for syntax highlighting
lang = "text" # Default
if name.endswith(".html") or name.endswith(".htm"):
lang = "html"
elif name.endswith(".css"):
lang = "css"
elif name.endswith(".js"):
lang = "javascript"
elif name.endswith(".py"):
lang = "python"
elif name.endswith(".json"):
lang = "json"
elif name.endswith(".md"):
lang = "markdown"
elif name.endswith(".sh") or name.endswith(".bash"):
lang = "bash"
tab_item = gr.TabItem(label=name, elem_id=f"tab_{name.replace('.', '_')}", children=[ # Ensure unique elem_id
gr.Code(value=content, language=lang, label=name) # Add label to Code block
])
tabs.append(tab_item)
# *** The Key Fix ***
# Return a new gr.Tabs component instance containing the generated TabItems
return gr.Tabs(tabs=tabs)
# --- Gradio UI Definition ---
with gr.Blocks() as demo:
gr.Markdown("### Website Generator (Static / Flask / Node.js)")
gr.Markdown("Describe the website you want to create. The AI will generate the necessary files.")
with gr.Row():
prompt = gr.Textbox(label="Describe your website", placeholder="E.g., a simple portfolio site with a contact form", scale=3)
backend = gr.Dropdown(["Static", "Flask", "Node.js"], value="Static", label="Backend Technology", scale=1)
with gr.Accordion("Advanced Options", open=False):
system_message = gr.Textbox(label="Extra instructions for the AI (System Message)", placeholder="Optional: e.g., 'Use Bootstrap 5', 'Prefer functional components in React'", value="")
max_tokens = gr.Slider(minimum=256, maximum=4096, value=1536, step=64, label="Max Tokens (Length)") # Increased max
temperature = gr.Slider(minimum=0.1, maximum=1.5, value=0.7, step=0.1, label="Temperature (Creativity)")
top_p = gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p (Sampling)")
generate_button = gr.Button("✨ Generate Code ✨", variant="primary")
gr.Markdown("#### Generated Files")
# Define the Tabs component placeholder. It will be replaced by the output of on_generate.
out_tabs = gr.Tabs(elem_id="output_tabs")
# Button click action
generate_button.click(
on_generate,
inputs=[prompt, backend, system_message, max_tokens, temperature, top_p],
outputs=[out_tabs], # Output the new Tabs component to replace the placeholder
show_progress="full" # Show progress during generation
)
if __name__ == "__main__":
demo.launch(debug=True) # Use debug=True for more detailed error messages in console |