MINEOGO commited on
Commit
0e4a7a5
·
verified ·
1 Parent(s): 4917053

Update app.py

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