MINEOGO commited on
Commit
aa6f369
·
verified ·
1 Parent(s): b865b71

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from huggingface_hub import InferenceClient
3
+ import os
4
+ import re # For post-processing fallback
5
+
6
+ # --- Configuration ---
7
+ API_TOKEN = os.getenv("HF_TOKEN", None)
8
+ MODEL = "HuggingFaceH4/zephyr-7b-beta" # Or choose another suitable model
9
+
10
+ # --- Initialize Inference Client ---
11
+ try:
12
+ print(f"Attempting to initialize Inference Client for model: {MODEL}")
13
+ if API_TOKEN:
14
+ print("Using HF Token found in environment.")
15
+ client = InferenceClient(model=MODEL, token=API_TOKEN)
16
+ else:
17
+ print("HF Token not found. Running without token (may lead to rate limits).")
18
+ client = InferenceClient(model=MODEL)
19
+ print("Inference Client initialized successfully.")
20
+ except Exception as e:
21
+ print(f"Error initializing Inference Client: {e}")
22
+ raise gr.Error(f"Failed to initialize the AI model client for '{MODEL}'. Check model name, network, and HF_TOKEN secret if applicable. Error: {e}")
23
+
24
+ # --- Core Code Generation Function ---
25
+ def generate_code(
26
+ prompt: str,
27
+ backend_choice: str,
28
+ file_structure: str,
29
+ max_tokens: int,
30
+ temperature: float,
31
+ top_p: float,
32
+ ):
33
+ """
34
+ Generates website code based on user prompt and choices.
35
+ Aims for richer CSS, emphasizes completeness, and strictly outputs ONLY raw code.
36
+ Yields the code token by token for live updates.
37
+ """
38
+ print(f"--- Generating Code ---")
39
+ print(f"Prompt: {prompt[:100]}...")
40
+ print(f"Backend Context: {backend_choice}")
41
+ print(f"File Structure: {file_structure}")
42
+ # Log the max_tokens value being used for this request
43
+ print(f"Settings: Max Tokens={max_tokens}, Temp={temperature}, Top-P={top_p}")
44
+
45
+ # --- Dynamically Build System Message ---
46
+ if file_structure == "Single File":
47
+ file_structure_instruction = (
48
+ "- **File Structure is 'Single File':** Generate ONLY a single, complete `index.html` file. "
49
+ "Embed ALL CSS directly within `<style>` tags inside the `<head>`. "
50
+ "Embed ALL necessary JavaScript directly within `<script>` tags just before the closing `</body>` tag. "
51
+ "Do NOT use file separation markers."
52
+ )
53
+ else: # Multiple Files
54
+ file_structure_instruction = (
55
+ "- **File Structure is 'Multiple Files':** Generate code for `index.html`, `style.css`, and `script.js` (if JS is needed). "
56
+ "Use these EXACT markers: `<!-- index.html -->`, `/* style.css */`, `// script.js` (only if JS is needed).\n"
57
+ "- Place the corresponding code directly after each marker.\n"
58
+ "- Inside `index.html`, link `style.css` in the `<head>` and include `script.js` before `</body>` if generated."
59
+ )
60
+
61
+ # Assemble the full system message - Emphasizing completeness and NO premature stopping
62
+ system_message = (
63
+ "You are an expert frontend web developer AI. Your primary goal is to generate **complete, visually appealing, modern, and well-styled** frontend code (HTML, CSS, client-side JS) based *only* on the user's description and selected options. "
64
+ "Follow ALL these rules with EXTREME STRICTNESS:\n"
65
+ "1. **STYLE & DETAIL:** Generate rich, detailed code. Use **plenty of CSS** for layout, spacing, typography, colors, and effects. Aim for a high-quality visual result.\n"
66
+ "2. **COMPLETENESS:** Generate the *entire* requested code structure. Ensure all files/sections are fully generated and properly closed (e.g., closing HTML tags `</html>`, CSS braces `}`, script tags `</script>`). **DO NOT STOP GENERATING PREMATURELY.** Finish the whole task.\n"
67
+ "3. **RAW CODE ONLY:** Your *entire* response MUST consist *only* of the requested source code. NO extra text, NO explanations, NO apologies, NO introductions, NO summaries, NO comments about the code (except standard code comments), NO MARKDOWN formatting (like ```html), and ***ABSOLUTELY NO CONVERSATIONAL TEXT OR TAGS*** like `<|user|>` or `<|assistant|>`.\n"
68
+ "4. **IMMEDIATE CODE START:** The response MUST begin *directly* with the first character of the code (`<!DOCTYPE html>` or `<!-- index.html -->`).\n"
69
+ "5. **IMMEDIATE CODE END:** The response MUST end *immediately* after the very last character of the generated code (`</html>`, `}`, `;`, etc.). DO NOT add *any* text, spaces, or newlines after the code concludes.\n"
70
+ "6. **MANDATORY `index.html`:** Always generate the content for `index.html`.\n"
71
+ f"7. **FILE STRUCTURE ({file_structure}):** Strictly follow ONLY the instructions for the *selected* file structure:\n"
72
+ f" {file_structure_instruction}\n"
73
+ "8. **BACKEND CONTEXT ({backend_choice}):** Use as a hint for frontend structure only. Generate ONLY frontend code.\n"
74
+ "9. **FRONTEND ONLY:** Do NOT generate server-side code.\n"
75
+ "10. **ACCURACY:** Generate functional code addressing the user's prompt.\n\n"
76
+ "REMEMBER: Create COMPLETE, visually appealing code. Output ONLY raw code. START immediately with code. FINISH the entire code generation. END immediately with code. NO extra text/tags."
77
+ )
78
+
79
+ # --- Construct the messages for the API ---
80
+ messages = [
81
+ {"role": "system", "content": system_message},
82
+ {"role": "user", "content": f"Generate the complete website frontend code for: {prompt}"}
83
+ ]
84
+
85
+ # --- Stream the response from the API ---
86
+ response_stream = ""
87
+ full_response_for_cleaning = ""
88
+ token_count = 0 # Add a simple counter for debugging
89
+ try:
90
+ print("Sending request to Hugging Face Inference API...")
91
+ stream = client.chat_completion(
92
+ messages=messages,
93
+ max_tokens=max_tokens, # Use the value from the slider
94
+ stream=True,
95
+ temperature=temperature,
96
+ top_p=top_p,
97
+ )
98
+ for message in stream:
99
+ token = message.choices[0].delta.content
100
+ if isinstance(token, str):
101
+ token_count += 1 # Crude approximation of tokens received
102
+ response_stream += token
103
+ full_response_for_cleaning += token
104
+ # Log progress occasionally for debugging if needed
105
+ # if token_count % 100 == 0:
106
+ # print(f"Stream progress: Received ~{token_count} tokens...")
107
+ yield response_stream # Yield cumulative response for live update
108
+
109
+ print(f"API stream finished. Received ~{token_count} tokens. Raw length: {len(full_response_for_cleaning)}")
110
+ # Check if received tokens are close to max_tokens, indicating potential cutoff
111
+ if token_count >= max_tokens - 10: # Check if close to the limit (allowing for slight variations)
112
+ print(f"WARNING: Generation might have been cut short due to reaching max_tokens limit ({max_tokens}).")
113
+ # Optionally, append a warning to the output itself, though it violates the "code only" rule
114
+ # full_response_for_cleaning += "\n\n<!-- WARNING: Output may be incomplete due to max_tokens limit. -->"
115
+
116
+
117
+ # --- Post-Processing (Fallback Safety Net) ---
118
+ cleaned_response = full_response_for_cleaning.strip()
119
+ cleaned_response = re.sub(r"^\s*```[a-z]*\s*\n?", "", cleaned_response)
120
+ cleaned_response = re.sub(r"\n?\s*```\s*$", "", cleaned_response)
121
+ cleaned_response = re.sub(r"<\s*\|?\s*(user|assistant)\s*\|?\s*>", "", cleaned_response, flags=re.IGNORECASE)
122
+ common_phrases = [
123
+ "Here is the code:", "Okay, here is the code:", "Here's the code:",
124
+ "Sure, here is the code you requested:", "Let me know if you need anything else."
125
+ ]
126
+ # Simple check, might need more robust cleaning if issues persist
127
+ for phrase in common_phrases:
128
+ # Check start
129
+ if cleaned_response.lower().startswith(phrase.lower()):
130
+ cleaned_response = cleaned_response[len(phrase):].lstrip()
131
+ # Check end - be careful not to remove parts of valid code
132
+ # This end check is risky, might remove valid closing comments or similar.
133
+ # Consider removing if it causes issues.
134
+ # if cleaned_response.lower().endswith(phrase.lower()):
135
+ # cleaned_response = cleaned_response[:-len(phrase)].rstrip()
136
+
137
+
138
+ yield cleaned_response.strip() # Yield final cleaned response
139
+
140
+ except Exception as e:
141
+ error_message = f"An error occurred during the API call: {e}"
142
+ print(error_message)
143
+ yield f"## Error\n\nFailed to generate code.\n**Reason:** {e}\n\nPlease check the model status, your connection, and API token (if applicable)."
144
+
145
+
146
+ # --- Build Gradio Interface using Blocks ---
147
+ with gr.Blocks(css=".gradio-container { max-width: 90% !important; }") as demo:
148
+ gr.Markdown("# ✨ Website Code Generator ✨")
149
+ gr.Markdown(
150
+ "Describe the website you want. The AI will generate **visually styled** frontend code (HTML, CSS, JS) using **plenty of CSS**. "
151
+ "The code appears live below.\n"
152
+ "**Important:**\n"
153
+ "1. This generator creates code based *only* on your initial description. To refine, modify your description and generate again.\n"
154
+ "2. **If the code output stops abruptly**, it likely hit the 'Max New Tokens' limit. **Increase the slider value below** and try again!" # Added explanation
155
+ )
156
+
157
+ with gr.Row():
158
+ with gr.Column(scale=2):
159
+ prompt_input = gr.Textbox(
160
+ label="Website Description",
161
+ placeholder="e.g., A modern portfolio landing page with smooth scroll nav, stylish hero, project cards with hover effects, contact form.",
162
+ lines=6,
163
+ )
164
+ backend_radio = gr.Radio(
165
+ ["Static", "Flask", "Node.js"], label="Backend Context Hint", value="Static",
166
+ info="Hint for AI (e.g., {{var}}) - generates ONLY frontend code."
167
+ )
168
+ file_structure_radio = gr.Radio(
169
+ ["Multiple Files", "Single File"], label="Output File Structure", value="Multiple Files",
170
+ info="Choose 'Single File' (all in index.html) or 'Multiple Files' (separate css/js)."
171
+ )
172
+ generate_button = gr.Button("🎨 Generate Stylish Website Code", variant="primary")
173
+
174
+ with gr.Column(scale=3):
175
+ code_output = gr.Code(
176
+ label="Generated Code (Raw Output - Aiming for Style!)",
177
+ language="html",
178
+ lines=30,
179
+ interactive=False,
180
+ )
181
+
182
+ with gr.Accordion("Advanced Generation Settings", open=False):
183
+ # INCREASED max_tokens range and default value
184
+ max_tokens_slider = gr.Slider(
185
+ minimum=512,
186
+ maximum=4096, # Set maximum to model's limit (Zephyr 7B can handle this)
187
+ value=3072, # Increased default significantly
188
+ step=256, # Larger steps might be practical
189
+ label="Max New Tokens",
190
+ info="Max length of generated code. Increase if output is cut off!" # Updated info
191
+ )
192
+ temperature_slider = gr.Slider(
193
+ minimum=0.1, maximum=1.2, value=0.7, step=0.1, label="Temperature",
194
+ info="Controls randomness. Lower=focused, Higher=creative."
195
+ )
196
+ top_p_slider = gr.Slider(
197
+ minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-P",
198
+ info="Alternative randomness control."
199
+ )
200
+
201
+ # --- Connect Inputs/Outputs ---
202
+ generate_button.click(
203
+ fn=generate_code,
204
+ inputs=[
205
+ prompt_input,
206
+ backend_radio,
207
+ file_structure_radio,
208
+ max_tokens_slider,
209
+ temperature_slider,
210
+ top_p_slider,
211
+ ],
212
+ outputs=code_output,
213
+ )
214
+
215
+ # --- Examples ---
216
+ gr.Examples(
217
+ examples=[
218
+ ["A simple counter page with a number display, an increment button, and a decrement button. Style the buttons nicely and center everything.", "Static", "Single File"],
219
+ ["A responsive product grid for an e-commerce site. Each card needs an image, title, price, and 'Add to Cart' button with a hover effect. Use modern CSS.", "Static", "Multiple Files"],
220
+ ["A personal blog homepage featuring a clean header with navigation, a main content area for post summaries (placeholders ok), and a simple footer. Use a nice font.", "Flask", "Multiple Files"],
221
+ ["A 'Coming Soon' page with a large countdown timer (use JS), a background image, and an email signup form. Make it look sleek.", "Static", "Multiple Files"]
222
+ ],
223
+ inputs=[prompt_input, backend_radio, file_structure_radio],
224
+ label="Example Prompts (Aiming for Style)"
225
+ )
226
+
227
+ # --- Launch ---
228
+ if __name__ == "__main__":
229
+ print("Starting Gradio app...")
230
+ # Ensure queue is enabled for Spaces
231
+ demo.queue(max_size=10).launch()
232
+ print("Gradio app launched.")