MINEOGO commited on
Commit
351a70c
·
verified ·
1 Parent(s): 3125dbb

Upload app (3).py

Browse files
Files changed (1) hide show
  1. app (3).py +258 -0
app (3).py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 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/Qwen2.5-Coder-32B-Instruct"
9
+
10
+ try:
11
+ print(f"Initializing Inference Client for model: {MODEL}")
12
+ client = InferenceClient(model=MODEL, token=API_TOKEN) if API_TOKEN else InferenceClient(model=MODEL)
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 = [
92
+ {"role": "system", "content": system_message},
93
+ {"role": "user", "content": user_prompt}
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(
109
+ messages=messages,
110
+ max_tokens=max_tokens,
111
+ stream=True,
112
+ temperature=temperature,
113
+ top_p=top_p,
114
+ )
115
+
116
+ for message in stream:
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()