MINEOGO commited on
Commit
86d3caa
·
verified ·
1 Parent(s): 338b431

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +253 -47
app.py CHANGED
@@ -1,16 +1,66 @@
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
  import os
4
  import re
 
5
 
 
6
  API_TOKEN = os.getenv("HF_TOKEN", None)
 
 
 
 
 
7
  MODEL = "Qwen/Qwen3-32B"
 
 
 
 
 
 
 
 
 
 
8
 
9
  try:
10
  print(f"Initializing Inference Client for model: {MODEL}")
11
- client = InferenceClient(model=MODEL, token=API_TOKEN) if API_TOKEN else InferenceClient(model=MODEL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  except Exception as e:
13
- raise gr.Error(f"Failed to initialize model client for {MODEL}. Error: {e}. Check HF_TOKEN and model availability.")
 
 
 
 
 
 
 
 
 
 
14
 
15
  # Parse all ```filename.ext\n<code>``` blocks
16
  def parse_code_blocks(response: str) -> list:
@@ -18,6 +68,9 @@ def parse_code_blocks(response: str) -> list:
18
  blocks = re.findall(pattern, response, re.DOTALL)
19
  files = []
20
  for filename, code in blocks:
 
 
 
21
  lang = None
22
  if filename.endswith(".py"):
23
  lang = "python"
@@ -27,27 +80,56 @@ def parse_code_blocks(response: str) -> list:
27
  lang = "html"
28
  elif filename.endswith(".css"):
29
  lang = "css"
 
 
 
 
 
 
 
 
 
 
30
  files.append({
31
- "filename": filename.strip(),
32
  "language": lang,
33
- "code": code.strip()
34
  })
 
 
 
 
35
  return files
36
 
37
  def strip_think_tags(text: str) -> str:
38
- return re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
39
 
40
  def extract_thoughts(text: str) -> str:
41
  matches = re.findall(r"<think>(.*?)</think>", text, flags=re.DOTALL)
42
- return "\n".join(matches).strip()
 
43
 
 
44
  system_message = (
45
- "You are an AI that generates website code using markdown-style code blocks. "
46
- "Each file should be output as: ```filename.ext\\n<code>\\n``` with no explanation."
 
 
 
 
 
 
47
  )
48
 
 
49
  def generate_code(prompt, backend_choice, max_tokens, temperature, top_p):
50
- user_prompt = f"USER_PROMPT = {prompt}\nUSER_BACKEND = {backend_choice}"
 
 
 
 
 
 
51
 
52
  messages = [
53
  {"role": "system", "content": system_message},
@@ -56,73 +138,197 @@ def generate_code(prompt, backend_choice, max_tokens, temperature, top_p):
56
 
57
  full_response = ""
58
  current_thoughts = ""
 
 
 
 
 
 
59
 
60
- # Reset outputs: code file tabs and thinking box
61
- yield [], gr.update(visible=True, value="")
 
62
 
63
  try:
64
  stream = client.chat_completion(
65
  messages=messages,
66
  max_tokens=max_tokens,
67
  stream=True,
68
- temperature=temperature,
69
  top_p=top_p,
 
 
70
  )
71
 
72
- for message in stream:
73
- token = message.choices[0].delta.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  if isinstance(token, str):
75
  full_response += token
76
 
77
- # Extract thinking chunks and show them
78
- thoughts = extract_thoughts(full_response)
79
- if thoughts != current_thoughts:
80
- current_thoughts = thoughts
81
- yield gr.update(visible=True, value=current_thoughts)
82
-
83
- cleaned = strip_think_tags(full_response)
84
- files = parse_code_blocks(cleaned)
85
- updates = []
86
- for f in files:
87
- updates.append(gr.Code(value=f["code"], label=f["filename"], language=f["language"]))
88
- yield updates, gr.update(visible=True, value=current_thoughts)
89
-
90
- # Final clean state
91
- cleaned = strip_think_tags(full_response)
92
- files = parse_code_blocks(cleaned)
93
- final_updates = [gr.Code(value=f["code"], label=f["filename"], language=f["language"]) for f in files]
94
- yield final_updates, gr.update(visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  except Exception as e:
97
- print(f"ERROR during code generation stream: {e}")
98
- yield [gr.Markdown(f"**Error:** {e}")], gr.update(visible=False)
 
 
 
 
 
 
 
 
 
99
 
 
100
  with gr.Blocks(css=".gradio-container { max-width: 90% !important; }") as demo:
101
  gr.Markdown("# ✨ Website Code Generator ✨")
102
- gr.Markdown("Describe the website you want. Multiple file tabs will be created automatically.")
103
 
104
  with gr.Row():
105
  with gr.Column(scale=2):
106
- prompt_input = gr.Textbox(label="Website Description", lines=6)
107
- backend_radio = gr.Radio(["Static", "Flask", "Node.js"], label="Backend Context", value="Static")
108
  generate_button = gr.Button("✨ Generate Website Code", variant="primary")
109
 
 
 
 
 
 
110
  with gr.Column(scale=3):
111
- file_outputs = gr.Group(update=lambda: [])
112
- thinking_box = gr.Textbox(label="Model is thinking...", visible=False, interactive=False)
 
 
113
 
114
- with gr.Accordion("Advanced Settings", open=False):
115
- max_tokens_slider = gr.Slider(512, 4096, value=3072, step=256, label="Max New Tokens")
116
- temperature_slider = gr.Slider(0.1, 1.2, value=0.7, step=0.1, label="Temperature")
117
- top_p_slider = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-P")
118
 
119
  generate_button.click(
120
  fn=generate_code,
121
  inputs=[prompt_input, backend_radio, max_tokens_slider, temperature_slider, top_p_slider],
 
122
  outputs=[file_outputs, thinking_box],
 
123
  )
124
 
 
125
  if __name__ == "__main__":
126
- if not API_TOKEN:
127
- print("Warning: HF_TOKEN environment variable not set. Using anonymous access.")
128
- demo.queue().launch()
 
 
 
 
1
  import gradio as gr
2
+ from huggingface_hub import InferenceClient, HfHubHTTPError
3
  import os
4
  import re
5
+ import traceback
6
 
7
+ # --- Configuration ---
8
  API_TOKEN = os.getenv("HF_TOKEN", None)
9
+ # MODEL = "Qwen/Qwen3-32B" # This is a very large model, might require specific inference endpoint/hardware
10
+ # Let's try a smaller, generally available model for testing first, e.g., Mixtral
11
+ # You can change this back if you are sure Qwen3-32B is available and configured for your space/token
12
+ # MODEL = "mistralai/Mixtral-8x7B-Instruct-v0.1"
13
+ # Or uncomment the Qwen model if you are certain it's correctly set up for inference:
14
  MODEL = "Qwen/Qwen3-32B"
15
+ # i have used Qwen3 because its quiet compatible
16
+
17
+
18
+ # --- Hugging Face Client Initialization ---
19
+ print("--- App Start ---")
20
+ if not API_TOKEN:
21
+ print("Warning: HF_TOKEN environment variable not set. Using anonymous access.")
22
+ print("Certain models might require a token for access.")
23
+ else:
24
+ print(f"HF_TOKEN found (length={len(API_TOKEN)}).") # Don't print the token itself
25
 
26
  try:
27
  print(f"Initializing Inference Client for model: {MODEL}")
28
+ # Explicitly pass token=None if not found, though InferenceClient handles it.
29
+ client = InferenceClient(model=MODEL, token=API_TOKEN if API_TOKEN else None)
30
+ print("Inference Client Initialized Successfully.")
31
+ # Optional: Add a quick test call if feasible, but be mindful of potential costs/rate limits
32
+ # try:
33
+ # client.text_generation("test", max_new_tokens=1)
34
+ # print("Test generation successful.")
35
+ # except Exception as test_e:
36
+ # print(f"Warning: Test generation failed. Client might be initialized but model access could be problematic. Error: {test_e}")
37
+
38
+ except HfHubHTTPError as http_err:
39
+ # More specific error handling for HTTP errors (like 401 Unauthorized, 403 Forbidden, 404 Not Found)
40
+ error_message = (
41
+ f"Failed to initialize model client for {MODEL} due to an HTTP error.\n"
42
+ f"Status Code: {http_err.response.status_code}\n"
43
+ f"Error: {http_err}\n"
44
+ f"Check:\n"
45
+ f"1. If '{MODEL}' is a valid model ID on Hugging Face Hub.\n"
46
+ f"2. If the model requires gating or specific permissions.\n"
47
+ f"3. If your HF_TOKEN is correct and has the necessary permissions (set as a Secret in your Space).\n"
48
+ f"4. If the default Inference API supports this model or if a dedicated Inference Endpoint is needed."
49
+ )
50
+ print(f"ERROR: {error_message}")
51
+ raise gr.Error(error_message)
52
  except Exception as e:
53
+ error_message = (
54
+ f"An unexpected error occurred while initializing the model client for {MODEL}.\n"
55
+ f"Error Type: {type(e).__name__}\n"
56
+ f"Error: {e}\n"
57
+ f"Traceback:\n{traceback.format_exc()}\n" # Add traceback
58
+ f"Check HF_TOKEN, model availability, network connection, and Space resources."
59
+ )
60
+ print(f"ERROR: {error_message}")
61
+ raise gr.Error(error_message)
62
+
63
+ # --- Helper Functions ---
64
 
65
  # Parse all ```filename.ext\n<code>``` blocks
66
  def parse_code_blocks(response: str) -> list:
 
68
  blocks = re.findall(pattern, response, re.DOTALL)
69
  files = []
70
  for filename, code in blocks:
71
+ filename = filename.strip()
72
+ code = code.strip()
73
+ # Basic language detection (can be expanded)
74
  lang = None
75
  if filename.endswith(".py"):
76
  lang = "python"
 
80
  lang = "html"
81
  elif filename.endswith(".css"):
82
  lang = "css"
83
+ elif filename.endswith(".json"):
84
+ lang = "json"
85
+ elif filename.endswith(".md"):
86
+ lang = "markdown"
87
+ elif filename.endswith(".sh") or filename.endswith(".bash"):
88
+ lang = "bash"
89
+ elif filename.endswith(".java"):
90
+ lang = "java"
91
+ # Add more extensions as needed
92
+
93
  files.append({
94
+ "filename": filename,
95
  "language": lang,
96
+ "code": code
97
  })
98
+ # Add logging to see what's parsed
99
+ # print(f"Parsed {len(files)} code blocks.")
100
+ # for i, f in enumerate(files):
101
+ # print(f" Block {i}: filename='{f['filename']}', lang='{f['language']}', code_len={len(f['code'])}")
102
  return files
103
 
104
  def strip_think_tags(text: str) -> str:
105
+ return re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL).strip()
106
 
107
  def extract_thoughts(text: str) -> str:
108
  matches = re.findall(r"<think>(.*?)</think>", text, flags=re.DOTALL)
109
+ # Only return the last thought block for cleaner display? Or join all? Let's join.
110
+ return "\n---\n".join(match.strip() for match in matches).strip()
111
 
112
+ # --- System Message ---
113
  system_message = (
114
+ "You are a helpful AI assistant specialized in generating website code. "
115
+ "Generate all the necessary files based on the user's request. "
116
+ "Output each file within a separate markdown code block formatted exactly like this:\n"
117
+ "```filename.ext\n"
118
+ "<code>\n"
119
+ "```\n"
120
+ "Do not add any explanatory text outside the code blocks. Ensure the filenames have appropriate extensions. "
121
+ "If you need to think step-by-step, use <think>...</think> tags. These tags will be hidden from the final user output but help guide your generation process."
122
  )
123
 
124
+ # --- Code Generation Function ---
125
  def generate_code(prompt, backend_choice, max_tokens, temperature, top_p):
126
+ if not prompt:
127
+ # Handle empty prompt case
128
+ yield [], gr.update(value="Please enter a description for the website.", visible=True)
129
+ return
130
+
131
+ # Use f-string formatting for clarity
132
+ user_prompt = f"USER_PROMPT: {prompt}\nUSER_BACKEND_PREFERENCE: {backend_choice}"
133
 
134
  messages = [
135
  {"role": "system", "content": system_message},
 
138
 
139
  full_response = ""
140
  current_thoughts = ""
141
+ accumulated_error = "" # Accumulate errors during stream
142
+
143
+ # Reset outputs: Clear previous code blocks and show/clear thinking box
144
+ # Yield an empty list to the gr.Column to clear it.
145
+ # Make thinking box visible but empty.
146
+ yield [], gr.update(visible=True, value="Generating code...")
147
 
148
+ print(f"\n--- Generating Code ---")
149
+ print(f"Prompt: {prompt[:100]}...") # Log truncated prompt
150
+ print(f"Backend: {backend_choice}, Max Tokens: {max_tokens}, Temp: {temperature}, Top-P: {top_p}")
151
 
152
  try:
153
  stream = client.chat_completion(
154
  messages=messages,
155
  max_tokens=max_tokens,
156
  stream=True,
157
+ temperature=temperature if temperature > 0 else 0.01, # Ensure temp is positive
158
  top_p=top_p,
159
+ # Consider adding stop sequences if the model tends to run on
160
+ # stop=["```\n\n", "\n\nHuman:", "\n\nUSER:"] # Example stop sequences
161
  )
162
 
163
+ code_updates = [] # Store the gr.Code components to yield
164
+
165
+ for i, message in enumerate(stream):
166
+ # Check for errors in the stream message (some providers might include error info)
167
+ if hasattr(message, 'error') and message.error:
168
+ accumulated_error += f"Error in stream chunk {i}: {message.error}\n"
169
+ print(f"ERROR in stream chunk {i}: {message.error}")
170
+ continue # Skip this chunk if it's an error indicator
171
+
172
+ # Ensure the path to content is correct
173
+ try:
174
+ # Common path: message.choices[0].delta.content
175
+ token = message.choices[0].delta.content
176
+ # Handle potential None token at the end of the stream or in error cases
177
+ if token is None:
178
+ token = ""
179
+ # print(f"Token {i}: '{token}'") # DEBUG: print each token
180
+ except (AttributeError, IndexError, TypeError) as e:
181
+ # Handle unexpected message structure
182
+ print(f"Warning: Could not extract token from stream message {i}. Structure: {message}. Error: {e}")
183
+ token = "" # Assign empty string to avoid breaking accumulation
184
+
185
  if isinstance(token, str):
186
  full_response += token
187
 
188
+ # Update thinking box periodically (e.g., every 10 tokens or if thoughts change)
189
+ if i % 10 == 0 or "<think>" in token or "</think>" in token:
190
+ thoughts = extract_thoughts(full_response)
191
+ if thoughts != current_thoughts:
192
+ current_thoughts = thoughts
193
+ # Don't yield code_updates here yet, only update thoughts
194
+ yield code_updates, gr.update(value=current_thoughts if current_thoughts else "Thinking...", visible=True)
195
+
196
+
197
+ # Update code blocks less frequently or when a block seems complete
198
+ # Heuristic: update if the response ends with ```
199
+ if token.strip().endswith("```") or i % 20 == 0: # Adjust frequency as needed
200
+ cleaned_response = strip_think_tags(full_response)
201
+ parsed_files = parse_code_blocks(cleaned_response)
202
+
203
+ # Create gr.Code components for the parsed files
204
+ # Compare with existing code_updates to avoid redundant updates if content hasn't changed significantly
205
+ new_code_updates = []
206
+ changed = False
207
+ if len(parsed_files) != len(code_updates):
208
+ changed = True
209
+ else:
210
+ # Quick check if filenames/code lengths differ significantly
211
+ for idx, f in enumerate(parsed_files):
212
+ if (idx >= len(code_updates) or
213
+ f["filename"] != code_updates[idx].label or
214
+ len(f["code"]) != len(code_updates[idx].value)): # Simple length check
215
+ changed = True
216
+ break
217
+
218
+ if changed or not code_updates: # Update if changed or first time
219
+ code_updates = []
220
+ for f in parsed_files:
221
+ code_updates.append(
222
+ gr.Code(
223
+ value=f["code"],
224
+ label=f["filename"],
225
+ language=f["language"]
226
+ )
227
+ )
228
+ # Yield the list of gr.Code components to the gr.Column
229
+ # Also update thoughts (might be slightly out of sync, but acceptable)
230
+ yield code_updates, gr.update(value=current_thoughts if current_thoughts else "Thinking...", visible=True)
231
+
232
+
233
+ # --- Final Update after Stream Ends ---
234
+ print("Stream finished.")
235
+ if accumulated_error:
236
+ print(f"Errors occurred during stream:\n{accumulated_error}")
237
+ # Decide how to show this to the user, e.g., append to thoughts or show separately
238
+ current_thoughts += f"\n\n**Streaming Errors:**\n{accumulated_error}"
239
+
240
+ cleaned_response = strip_think_tags(full_response)
241
+ final_files = parse_code_blocks(cleaned_response)
242
+ print(f"Final parsed files: {len(final_files)}")
243
+
244
+ final_code_updates = []
245
+ if not final_files and not accumulated_error:
246
+ # Handle case where no code blocks were generated
247
+ final_code_updates.append(gr.Markdown("No code blocks were generated. The model might have responded with text instead, or the format was incorrect."))
248
+ print("Warning: No code blocks found in the final response.")
249
+ # Optionally show the raw response for debugging
250
+ # final_code_updates.append(gr.Code(label="Raw Response", value=cleaned_response, language="text"))
251
+
252
+ elif not final_files and accumulated_error:
253
+ final_code_updates.append(gr.Markdown(f"**Error during generation:**\n{accumulated_error}"))
254
+
255
+ else:
256
+ for f in final_files:
257
+ final_code_updates.append(
258
+ gr.Code(
259
+ value=f["code"],
260
+ label=f["filename"],
261
+ language=f["language"]
262
+ )
263
+ )
264
+
265
+ # Yield final code blocks and hide thinking box (or show final thoughts/errors)
266
+ final_thought_update = gr.update(visible=True if current_thoughts else False, value=current_thoughts)
267
+ yield final_code_updates, final_thought_update
268
+
269
+ except HfHubHTTPError as http_err:
270
+ # Handle errors during the streaming call itself
271
+ error_message = (
272
+ f"**Error during code generation (HTTP Error):**\n"
273
+ f"Status Code: {http_err.response.status_code}\n"
274
+ f"Error: {http_err}\n"
275
+ f"This could be due to rate limits, invalid input, model errors, or token issues.\n"
276
+ f"Check the Hugging Face Space logs for more details."
277
+ )
278
+ print(f"ERROR: {error_message}")
279
+ print(traceback.format_exc())
280
+ # Yield error message in the output area
281
+ yield [gr.Markdown(error_message)], gr.update(visible=False) # Hide thinking box on error
282
 
283
  except Exception as e:
284
+ error_message = (
285
+ f"**An unexpected error occurred during code generation:**\n"
286
+ f"Error Type: {type(e).__name__}\n"
287
+ f"Error: {e}\n\n"
288
+ f"**Traceback:**\n```\n{traceback.format_exc()}\n```\n"
289
+ f"Check the Hugging Face Space logs for more details."
290
+ )
291
+ print(f"ERROR: {error_message}")
292
+ # Yield error message in the output area
293
+ yield [gr.Markdown(error_message)], gr.update(visible=False) # Hide thinking box on error
294
+
295
 
296
+ # --- Gradio Interface ---
297
  with gr.Blocks(css=".gradio-container { max-width: 90% !important; }") as demo:
298
  gr.Markdown("# ✨ Website Code Generator ✨")
299
+ gr.Markdown("Describe the website you want. Code files will appear below. Uses `mistralai/Mixtral-8x7B-Instruct-v0.1` by default (check code to change).") # Update description
300
 
301
  with gr.Row():
302
  with gr.Column(scale=2):
303
+ prompt_input = gr.Textbox(label="Website Description", lines=6, placeholder="e.g., A simple landing page with a title, a paragraph, and a button linking to example.com")
304
+ backend_radio = gr.Radio(["Static (HTML/CSS/JS)", "Flask", "Node.js"], label="Backend Preference (Influences AI)", value="Static (HTML/CSS/JS)")
305
  generate_button = gr.Button("✨ Generate Website Code", variant="primary")
306
 
307
+ with gr.Accordion("Advanced Settings", open=False):
308
+ max_tokens_slider = gr.Slider(512, 8192, value=4096, step=256, label="Max New Tokens") # Increased max potential tokens
309
+ temperature_slider = gr.Slider(0.0, 1.2, value=0.6, step=0.05, label="Temperature (0=deterministic, >1=more creative)") # Allow 0
310
+ top_p_slider = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-P (Nucleus Sampling)")
311
+
312
  with gr.Column(scale=3):
313
+ thinking_box = gr.Textbox(label="Model Activity / Thoughts", visible=False, interactive=False, lines=2)
314
+ # Use gr.Column to hold the dynamic code blocks
315
+ # Remove the update lambda, it's not needed for Column
316
+ file_outputs = gr.Column(elem_id="code-output-area")
317
 
 
 
 
 
318
 
319
  generate_button.click(
320
  fn=generate_code,
321
  inputs=[prompt_input, backend_radio, max_tokens_slider, temperature_slider, top_p_slider],
322
+ # Output to the Column and the Textbox
323
  outputs=[file_outputs, thinking_box],
324
+ # api_name="generate_code" # Optional: for API access
325
  )
326
 
327
+ # --- Launch ---
328
  if __name__ == "__main__":
329
+ print("Starting Gradio App...")
330
+ # Use queue() for handling multiple users and streaming
331
+ # Set share=False unless you specifically want a public link from local execution
332
+ # Set debug=True for more detailed Gradio errors locally (remove/set False for production)
333
+ demo.queue().launch(debug=False, share=False)
334
+ print("Gradio App Launched.")