MINEOGO commited on
Commit
338b431
·
verified ·
1 Parent(s): 4dfc8f9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -192
app.py CHANGED
@@ -2,7 +2,6 @@ import gradio as gr
2
  from huggingface_hub import InferenceClient
3
  import os
4
  import re
5
- # import traceback # Optional for debugging
6
 
7
  API_TOKEN = os.getenv("HF_TOKEN", None)
8
  MODEL = "Qwen/Qwen3-32B"
@@ -13,79 +12,41 @@ try:
13
  except Exception as e:
14
  raise gr.Error(f"Failed to initialize model client for {MODEL}. Error: {e}. Check HF_TOKEN and model availability.")
15
 
16
- # --- Helper Function to Parse Code during Streaming ---
17
- def parse_streaming_code(current_response: str) -> dict:
18
- """
19
- Parses potentially incomplete AI output stream.
20
- Identifies if .TAB separator is present and splits code accordingly.
21
- Returns dict with html_code, backend_code, filename, language, and visibility flag.
22
- """
23
- files = {
24
- 'html_code': '',
25
- 'backend_code': '',
26
- 'backend_filename': 'Backend', # Default label
27
- 'backend_language': None,
28
- 'backend_visible': False # Default visibility
29
- }
30
- separator_pattern = r'\.TAB\[NAME=([^\]]+)\]\n?'
31
- match = re.search(separator_pattern, current_response)
32
-
33
- if match:
34
- # Separator found in the stream so far
35
- html_part = current_response[:match.start()].strip()
36
- backend_part = current_response[match.end():].strip() # Code after separator
37
- backend_filename = match.group(1).strip()
38
-
39
- files['html_code'] = html_part
40
- files['backend_code'] = backend_part
41
- files['backend_filename'] = backend_filename
42
- files['backend_visible'] = True # Make backend visible
43
-
44
- # Determine language
45
- if backend_filename.endswith(".py"): files['backend_language'] = 'python'
46
- elif backend_filename.endswith(".js"): files['backend_language'] = 'javascript'
47
- elif backend_filename.endswith(".css"): files['backend_language'] = 'css'
48
- else: files['backend_language'] = None
49
- else:
50
- # No separator found yet, assume all content is HTML
51
- files['html_code'] = current_response.strip()
52
- # Keep backend_visible as False
53
-
54
  return files
55
 
56
- # --- Minimal Cleaning for Intermediate Stream Chunks ---
57
- def clean_intermediate_stream(text: str) -> str:
58
- """ Basic cleaning for streaming chunks (e.g., remove chat markers). """
59
- cleaned = re.sub(r"<\s*\|?\s*(user|system|assistant)\s*\|?\s*>", "", text, flags=re.IGNORECASE)
60
- # Avoid stripping aggressively during stream as it might remove partial code
61
- return cleaned
62
 
63
- # --- Core Code Generation Function - Modified for Streaming UI Updates ---
64
- def generate_code(
65
- prompt: str,
66
- backend_choice: str,
67
- max_tokens: int,
68
- temperature: float,
69
- top_p: float,
70
- ):
71
- print(f"Streaming code generation for: {prompt[:100]}... | Backend: {backend_choice}")
72
 
73
- system_message = ( # System message remains the same
74
- "You are an AI that generates website code. You MUST ONLY output the raw code, without any conversational text like 'Here is the code' or explanations before or after the code blocks. "
75
- "You MUST NOT wrap the code in markdown fences like ```html, ```python, or ```js. "
76
- "If the user requests 'Static' or the prompt clearly implies only frontend code, generate ONLY the content for the `index.html` file. "
77
- "If the user requests 'Flask' or 'Node.js' and the prompt requires backend logic, you MUST generate both the `index.html` content AND the corresponding main backend file content (e.g., `app.py` for Flask, `server.js` or `app.js` for Node.js). "
78
- "When generating multiple files, you MUST separate them EXACTLY as follows: "
79
- "1. Output the complete code for the first file (e.g., `index.html`). "
80
- "2. On a new line immediately after the first file's code, add the separator '.TAB[NAME=filename.ext]' (e.g., '.TAB[NAME=app.py]' or '.TAB[NAME=server.js]'). "
81
- "3. On the next line, immediately start the code for the second file. "
82
- "Generate only the necessary files (usually index.html and potentially one backend file). "
83
- "The generated website code must be SFW and have minimal errors. "
84
- "Only include comments where user modification is strictly required. Avoid explanatory comments. "
85
- "If the user asks you to create code that is NOT for a website, you MUST respond ONLY with the exact phrase: "
86
- "'hey there! am here to create websites for you unfortunately am programmed to not create codes! otherwise I would go on the naughty list :-('"
87
- )
88
 
 
89
  user_prompt = f"USER_PROMPT = {prompt}\nUSER_BACKEND = {backend_choice}"
90
 
91
  messages = [
@@ -94,15 +55,10 @@ def generate_code(
94
  ]
95
 
96
  full_response = ""
97
- # Initialize state for UI updates
98
- current_html = ""
99
- current_backend = ""
100
- current_backend_label = "Backend"
101
- current_backend_lang = None
102
- is_backend_visible = False
103
 
104
- # Initial clear of outputs
105
- yield gr.update(value="", visible=True), gr.update(visible=False), gr.update(value="", visible=False)
106
 
107
  try:
108
  stream = client.chat_completion(
@@ -117,142 +73,56 @@ def generate_code(
117
  token = message.choices[0].delta.content
118
  if isinstance(token, str):
119
  full_response += token
120
- # Clean intermediate response minimally
121
- cleaned_response_chunk = clean_intermediate_stream(full_response)
122
-
123
- # Parse the *entire accumulated* cleaned response on each iteration
124
- parsed_state = parse_streaming_code(cleaned_response_chunk)
125
-
126
- # Update state variables
127
- current_html = parsed_state['html_code']
128
- current_backend = parsed_state['backend_code']
129
- current_backend_label = parsed_state['backend_filename']
130
- current_backend_lang = parsed_state['backend_language']
131
- is_backend_visible = parsed_state['backend_visible'] # This determines visibility
132
-
133
- # Prepare Gradio updates based on the *current* parsed state
134
- html_update = gr.update(value=current_html)
135
- # Update the backend tab's visibility
136
- tab_update = gr.update(visible=is_backend_visible)
137
- # Update the backend code block's content, label, language, and visibility
138
- backend_code_update = gr.update(
139
- value=current_backend,
140
- label=current_backend_label,
141
- language=current_backend_lang,
142
- visible=is_backend_visible # Make code block visible *if* tab is visible
143
- )
144
-
145
- # Yield updates for html_code, backend_tab, backend_code
146
- yield html_update, tab_update, backend_code_update
147
-
148
- # --- Final Clean and Update after Stream ---
149
- # Ensure the final state is clean and fully parsed
150
- final_cleaned_response = full_response.strip()
151
- # Remove fences/phrases missed during stream (optional but good practice)
152
- final_cleaned_response = re.sub(r"^\s*```[a-z]*\s*\n?", "", final_cleaned_response)
153
- final_cleaned_response = re.sub(r"\n?\s*```\s*$", "", final_cleaned_response)
154
- common_phrases = ["Here is the code:", "Okay, here is the code:", "Here's the code:", "Sure, here is the code you requested:"]
155
- temp_lower = final_cleaned_response.lower()
156
- for phrase in common_phrases:
157
- if temp_lower.startswith(phrase.lower()):
158
- final_cleaned_response = final_cleaned_response[len(phrase):].lstrip()
159
- temp_lower = final_cleaned_response.lower()
160
-
161
- # Check for refusal message in the final response
162
- refusal_message = "hey there! am here to create websites for you unfortunately am programmed to not create codes! otherwise I would go on the naughty list :-("
163
- if refusal_message in final_cleaned_response:
164
- yield gr.update(value=refusal_message), gr.update(visible=False), gr.update(value="", visible=False)
165
- return # Stop processing
166
-
167
- # Final parse
168
- final_parsed_state = parse_streaming_code(final_cleaned_response)
169
-
170
- # Final updates to ensure everything is correct
171
- final_html_update = gr.update(value=final_parsed_state['html_code'])
172
- final_tab_update = gr.update(visible=final_parsed_state['backend_visible'])
173
- final_backend_code_update = gr.update(
174
- value=final_parsed_state['backend_code'],
175
- label=final_parsed_state['backend_filename'],
176
- language=final_parsed_state['backend_language'],
177
- visible=final_parsed_state['backend_visible']
178
- )
179
- yield final_html_update, final_tab_update, final_backend_code_update
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  except Exception as e:
183
  print(f"ERROR during code generation stream: {e}")
184
- # traceback.print_exc() # Uncomment for detailed traceback
185
- error_message = f"## Error\n\nFailed during streaming.\n**Reason:** {e}"
186
- # Show error in HTML block, hide backend tab and code
187
- yield gr.update(value=error_message), gr.update(visible=False), gr.update(value="", visible=False)
188
-
189
 
190
- # --- Build Gradio Interface ---
191
  with gr.Blocks(css=".gradio-container { max-width: 90% !important; }") as demo:
192
  gr.Markdown("# ✨ Website Code Generator ✨")
193
- gr.Markdown(
194
- "Describe the website you want. See code generated live.\n"
195
- "If backend code is generated, a second tab will appear."
196
- )
197
 
198
  with gr.Row():
199
  with gr.Column(scale=2):
200
- prompt_input = gr.Textbox(
201
- label="Website Description",
202
- placeholder="e.g., A Flask app with a simple chat using Socket.IO",
203
- lines=6,
204
- )
205
- backend_radio = gr.Radio(
206
- ["Static", "Flask", "Node.js"],
207
- label="Backend Context",
208
- value="Static",
209
- info="Guides AI if backend code (like Python/JS) is needed alongside HTML."
210
- )
211
  generate_button = gr.Button("✨ Generate Website Code", variant="primary")
212
 
213
  with gr.Column(scale=3):
214
- # Define Tabs structure
215
- with gr.Tabs(elem_id="code-tabs"):
216
- # Tab 1: Always present for HTML
217
- with gr.Tab("index.html", elem_id="html-tab") as html_tab:
218
- html_code_output = gr.Code(
219
- label="index.html",
220
- language="html",
221
- lines=25,
222
- interactive=False,
223
- elem_id="html_code",
224
- )
225
- # Tab 2: Backend - defined but starts hidden
226
- with gr.Tab("Backend", elem_id="backend-tab", visible=False) as backend_tab:
227
- backend_code_output = gr.Code(
228
- label="Backend", # Label updated dynamically if tab becomes visible
229
- language=None, # Language updated dynamically
230
- lines=25,
231
- interactive=False,
232
- elem_id="backend_code",
233
- visible=False # Code block also starts hidden
234
- )
235
 
236
  with gr.Accordion("Advanced Settings", open=False):
237
- max_tokens_slider = gr.Slider(
238
- minimum=512, maximum=4096, value=3072, step=256, label="Max New Tokens"
239
- )
240
- temperature_slider = gr.Slider(
241
- minimum=0.1, maximum=1.2, value=0.7, step=0.1, label="Temperature"
242
- )
243
- top_p_slider = gr.Slider(
244
- minimum=0.1, maximum=1.0, value=0.9, step=0.05, label="Top-P"
245
- )
246
 
247
- # Click function now targets html_code_output, backend_tab, and backend_code_output
248
  generate_button.click(
249
  fn=generate_code,
250
  inputs=[prompt_input, backend_radio, max_tokens_slider, temperature_slider, top_p_slider],
251
- # Outputs MUST match the number and order of updates yielded by the function
252
- outputs=[html_code_output, backend_tab, backend_code_output],
253
  )
254
 
255
  if __name__ == "__main__":
256
  if not API_TOKEN:
257
  print("Warning: HF_TOKEN environment variable not set. Using anonymous access.")
258
- demo.queue(max_size=10).launch()
 
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"
 
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:
17
+ pattern = r"```([^\n]+)\n(.*?)```"
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"
24
+ elif filename.endswith(".js"):
25
+ lang = "javascript"
26
+ elif filename.endswith(".html"):
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 = [
 
55
  ]
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(
 
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()