Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -112,7 +112,7 @@ def get_container_logs_action(repo_id, profile, token):
|
|
112 |
if not (repo_id and profile and token):
|
113 |
return "⚠️ Please log in and create a Space first."
|
114 |
# Add a short delay before fetching run logs, build might just finish
|
115 |
-
time.sleep(5)
|
116 |
return _fetch_space_logs_level(repo_id, "run", token.token)
|
117 |
|
118 |
|
@@ -159,18 +159,32 @@ STATE_CHECKING_LOGS_BUILD = "checking_logs_build"
|
|
159 |
STATE_CHECKING_LOGS_RUN = "checking_logs_run"
|
160 |
STATE_DEBUGGING_CODE = "debugging_code"
|
161 |
STATE_UPLOADING_FIXED_APP_PY = "uploading_fixed_app_py"
|
162 |
-
STATE_COMPLETE = "complete"
|
163 |
|
164 |
MAX_DEBUG_ATTEMPTS = 3
|
165 |
|
166 |
-
def update_chat(history, bot_message):
|
167 |
-
"""Helper to
|
168 |
-
#
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
|
172 |
-
history.append([None, bot_message])
|
173 |
-
return history
|
174 |
|
175 |
def ai_workflow_chat(
|
176 |
message: str,
|
@@ -188,7 +202,7 @@ def ai_workflow_chat(
|
|
188 |
debug_attempts_state: int,
|
189 |
app_description_state: str | None, # Persist initial request
|
190 |
repo_name_state: str | None, # Persist chosen name
|
191 |
-
generated_code_state: str | None, #
|
192 |
) -> tuple[
|
193 |
list[list[str | None]], # history
|
194 |
str | None, # repo_id
|
@@ -207,15 +221,16 @@ def ai_workflow_chat(
|
|
207 |
attempts = debug_attempts_state
|
208 |
app_desc = app_description_state
|
209 |
repo_name = repo_name_state
|
210 |
-
generated_code = generated_code_state
|
|
|
211 |
|
212 |
updated_preview = preview_html
|
213 |
updated_build = build_logs
|
214 |
updated_run = container_logs
|
215 |
|
216 |
-
# Add user message to history for
|
|
|
217 |
history.append([message, None])
|
218 |
-
# Yield immediately to show user message
|
219 |
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
220 |
|
221 |
|
@@ -242,43 +257,50 @@ def ai_workflow_chat(
|
|
242 |
history = update_chat(history, "Workflow reset.")
|
243 |
# Reset all state variables
|
244 |
yield history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0, None, None, None
|
245 |
-
return # End workflow
|
246 |
|
247 |
elif generate_match:
|
248 |
new_repo_name = generate_match.group(1)
|
249 |
new_app_desc = message # Store the full request
|
250 |
history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
|
251 |
# Transition to creating space state, passing name and description
|
252 |
-
|
|
|
|
|
|
|
|
|
|
|
253 |
|
254 |
elif create_match:
|
255 |
new_repo_name = create_match.group(1)
|
256 |
history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
|
257 |
# Transition to creating space state, just passing the name (desc will be default)
|
258 |
-
|
|
|
|
|
|
|
|
|
259 |
|
260 |
elif "create" in message.lower() and not repo_id: # Generic create trigger
|
261 |
history = update_chat(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
|
262 |
# Transition to awaiting name state
|
263 |
-
|
|
|
|
|
264 |
|
265 |
else:
|
266 |
# Handle other chat messages if needed, or just respond unknown
|
267 |
history = update_chat(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
|
268 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
269 |
# Stay in IDLE state
|
|
|
|
|
270 |
|
271 |
-
elif state == STATE_AWAITING_REPO_NAME:
|
272 |
-
new_repo_name = message.strip()
|
273 |
-
if not new_repo_name or re.search(r'\s', new_repo_name): # Basic validation for repo name
|
274 |
-
history = update_chat(history, "Invalid name. Please provide a single word/slug for the Space name.")
|
275 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Stay in this state
|
276 |
-
else:
|
277 |
-
history = update_chat(history, f"Using Space name `{new_repo_name}`. Creating Space `{hf_profile.username}/{new_repo_name}`...")
|
278 |
-
# Transition to creating space state, pass the received name
|
279 |
-
yield history, repo_id, STATE_CREATING_SPACE, updated_preview, updated_run, updated_build, attempts, app_desc, new_repo_name, generated_code
|
280 |
|
281 |
-
|
|
|
|
|
|
|
|
|
282 |
# This state is triggered when we *already have* the repo_name in state
|
283 |
if not repo_name: # Safety check
|
284 |
history = update_chat(history, "Internal error: Repo name missing for creation. Resetting.")
|
@@ -288,15 +310,19 @@ def ai_workflow_chat(
|
|
288 |
try:
|
289 |
new_repo_id, iframe_html = create_space_action(repo_name, space_sdk, hf_profile, hf_token)
|
290 |
updated_preview = iframe_html
|
291 |
-
|
292 |
-
|
293 |
-
|
|
|
|
|
|
|
294 |
|
295 |
except Exception as e:
|
296 |
-
history = update_chat(history, f"❌ Error creating space: {e}")
|
297 |
# Reset state on failure
|
298 |
yield history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0, None, None, None
|
299 |
|
|
|
300 |
elif state == STATE_GENERATING_CODE:
|
301 |
# Use the stored app description or a default
|
302 |
prompt_desc = app_desc if app_desc else 'a Gradio image-blur test app with upload and slider controls'
|
@@ -308,7 +334,7 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
308 |
"""
|
309 |
try:
|
310 |
history = update_chat(history, "🧠 Generating `app.py` code with Gemini...")
|
311 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code #
|
312 |
|
313 |
code = call_gemini(prompt, gemini_api_key, gemini_model)
|
314 |
# Clean markdown and whitespace
|
@@ -323,13 +349,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
323 |
|
324 |
history = update_chat(history, "✅ `app.py` code generated. Click 'Send' to upload.")
|
325 |
# Transition to uploading state, store the generated code
|
326 |
-
|
|
|
|
|
|
|
327 |
|
328 |
except Exception as e:
|
329 |
history = update_chat(history, f"❌ Error generating code: {e}. Click 'reset'.")
|
330 |
# Reset state on failure
|
331 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
332 |
|
|
|
333 |
elif state == STATE_UPLOADING_APP_PY:
|
334 |
# Use the generated_code stored in state
|
335 |
if not generated_code:
|
@@ -338,18 +368,22 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
338 |
return
|
339 |
|
340 |
history = update_chat(history, "☁️ Uploading `app.py`...")
|
341 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name,
|
342 |
|
343 |
try:
|
344 |
upload_file_to_space_action(io.StringIO(generated_code), "app.py", repo_id, hf_profile, hf_token)
|
345 |
history = update_chat(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
|
346 |
-
# Transition to generating requirements
|
347 |
-
|
|
|
|
|
|
|
348 |
|
349 |
except Exception as e:
|
350 |
history = update_chat(history, f"❌ Error uploading app.py: {e}. Click 'reset'.")
|
351 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
352 |
|
|
|
353 |
elif state == STATE_GENERATING_REQUIREMENTS:
|
354 |
history = update_chat(history, "📄 Generating `requirements.txt`...")
|
355 |
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
|
@@ -357,21 +391,28 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
357 |
# Simple heuristic for requirements based on SDK and common needs
|
358 |
reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
|
359 |
# Add common deps if likely used (could parse code, but simpler heuristic for demo)
|
360 |
-
if "google.generativeai" in (
|
361 |
reqs_list.append("google-generativeai")
|
362 |
-
if "requests" in (
|
363 |
reqs_list.append("requests")
|
364 |
-
reqs_list.append("huggingface_hub") # Needed for log fetching etc if done inside the space itself (though not
|
|
|
|
|
|
|
|
|
365 |
|
366 |
reqs_content = "\n".join(reqs_list) + "\n"
|
367 |
|
368 |
history = update_chat(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
|
369 |
# Transition to uploading requirements, store content temporarily
|
370 |
-
|
|
|
|
|
|
|
371 |
|
372 |
|
373 |
elif state == STATE_UPLOADING_REQUIREMENTS:
|
374 |
-
# Use content stored in state (
|
375 |
reqs_content_to_upload = generated_code # Get content from state
|
376 |
if not reqs_content_to_upload:
|
377 |
history = update_chat(history, "Internal error: No requirements content to upload. Resetting.")
|
@@ -379,13 +420,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
379 |
return
|
380 |
|
381 |
history = update_chat(history, "☁️ Uploading `requirements.txt`...")
|
382 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name,
|
383 |
|
384 |
try:
|
385 |
upload_file_to_space_action(io.StringIO(reqs_content_to_upload), "requirements.txt", repo_id, hf_profile, hf_token)
|
386 |
history = update_chat(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
|
387 |
-
# Transition to generating README
|
388 |
-
|
|
|
|
|
|
|
|
|
389 |
|
390 |
except Exception as e:
|
391 |
history = update_chat(history, f"❌ Error uploading requirements.txt: {e}. Click 'reset'.")
|
@@ -397,18 +442,23 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
397 |
|
398 |
# Generate a simple README based on app_desc or repo_name
|
399 |
readme_title = repo_name if repo_name else "My Awesome Space"
|
400 |
-
|
|
|
401 |
|
402 |
readme_content = f"# {readme_title}\n\n{readme_description}\n\n" \
|
403 |
-
"This Space was automatically generated by an AI workflow.\n"
|
|
|
404 |
|
405 |
history = update_chat(history, "✅ `README.md` generated. Click 'Send' to upload.")
|
406 |
# Transition to uploading README, store content temporarily
|
407 |
-
|
|
|
|
|
|
|
408 |
|
409 |
|
410 |
elif state == STATE_UPLOADING_README:
|
411 |
-
# Use content stored in state (
|
412 |
readme_content_to_upload = generated_code # Get content from state
|
413 |
if not readme_content_to_upload:
|
414 |
history = update_chat(history, "Internal error: No README content to upload. Resetting.")
|
@@ -416,13 +466,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
416 |
return
|
417 |
|
418 |
history = update_chat(history, "☁️ Uploading `README.md`...")
|
419 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name,
|
420 |
|
421 |
try:
|
422 |
upload_file_to_space_action(io.StringIO(readme_content_to_upload), "README.md", repo_id, hf_profile, hf_token)
|
423 |
-
history = update_chat(history, "✅ Uploaded `README.md`.
|
424 |
-
# Transition to checking build logs
|
425 |
-
|
|
|
|
|
|
|
|
|
426 |
|
427 |
except Exception as e:
|
428 |
history = update_chat(history, f"❌ Error uploading README.md: {e}. Click 'reset'.")
|
@@ -440,13 +494,20 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
440 |
# Simple check: if build logs contain "Error" or "Exception", might indicate build issue.
|
441 |
# More robust would involve checking build status via API, but logs are simpler for demo.
|
442 |
# Assuming successful build leads to container logs check.
|
|
|
443 |
if "Error" in updated_build or "Exception" in updated_build:
|
444 |
-
history = update_chat(history, "⚠️ Build logs may contain errors. Please inspect. Click 'Send' to check container logs (app might still start).")
|
445 |
-
|
|
|
|
|
|
|
446 |
|
447 |
else:
|
448 |
history = update_chat(history, "✅ Build logs fetched. Click 'Send' to check container logs.")
|
449 |
-
|
|
|
|
|
|
|
450 |
|
451 |
|
452 |
elif state == STATE_CHECKING_LOGS_RUN:
|
@@ -461,17 +522,24 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
461 |
attempts += 1
|
462 |
history = update_chat(history, f"❌ Errors detected in container logs. Attempting debug fix #{attempts}/{MAX_DEBUG_ATTEMPTS}. Click 'Send' to proceed.")
|
463 |
# Transition to debugging state, increment attempts
|
464 |
-
|
|
|
|
|
465 |
|
466 |
elif ("Error" in updated_run or "Exception" in updated_run) and attempts >= MAX_DEBUG_ATTEMPTS:
|
467 |
history = update_chat(history, f"❌ Errors detected in container logs. Max debug attempts ({MAX_DEBUG_ATTEMPTS}) reached. Please inspect logs manually or click 'reset'.")
|
468 |
-
# Transition to complete
|
469 |
-
|
|
|
|
|
470 |
|
471 |
else:
|
472 |
history = update_chat(history, "✅ App appears to be running successfully! Check the iframe above. Click 'reset' to start a new project.")
|
473 |
-
# Transition to complete
|
474 |
-
|
|
|
|
|
|
|
475 |
|
476 |
elif state == STATE_DEBUGGING_CODE:
|
477 |
history = update_chat(history, f"🧠 Calling Gemini to generate fix based on logs...")
|
@@ -501,7 +569,10 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
501 |
|
502 |
history = update_chat(history, "✅ Fix code generated. Click 'Send' to upload.")
|
503 |
# Transition to uploading fixed code, pass the fixed code
|
504 |
-
|
|
|
|
|
|
|
505 |
|
506 |
|
507 |
except Exception as e:
|
@@ -510,7 +581,7 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
510 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
511 |
|
512 |
elif state == STATE_UPLOADING_FIXED_APP_PY:
|
513 |
-
# Use the fixed code stored in state (
|
514 |
fixed_code_to_upload = generated_code # Get code from state
|
515 |
if not fixed_code_to_upload:
|
516 |
history = update_chat(history, "Internal error: No fixed code available to upload. Resetting.")
|
@@ -518,13 +589,17 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
518 |
return
|
519 |
|
520 |
history = update_chat(history, "☁️ Uploading fixed `app.py`...")
|
521 |
-
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name,
|
522 |
|
523 |
try:
|
524 |
upload_file_to_space_action(io.StringIO(fixed_code_to_upload), "app.py", repo_id, hf_profile, hf_token)
|
525 |
history = update_chat(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
|
526 |
-
# Transition back to checking run logs (after rebuild)
|
527 |
-
|
|
|
|
|
|
|
|
|
528 |
|
529 |
except Exception as e:
|
530 |
history = update_chat(history, f"❌ Error uploading fixed app.py: {e}. Click 'reset'.")
|
@@ -533,22 +608,33 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
533 |
elif state == STATE_COMPLETE:
|
534 |
# App is successfully deployed or failed after attempts.
|
535 |
# User should click reset or start a new command.
|
536 |
-
# Just yield the current state.
|
537 |
-
|
|
|
|
|
538 |
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
|
543 |
|
544 |
except Exception as e:
|
545 |
# Catch-all for unexpected exceptions in any state
|
546 |
-
|
547 |
-
|
548 |
-
|
|
|
|
|
549 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
550 |
|
551 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
552 |
# --- Build the Gradio UI ---
|
553 |
|
554 |
with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
@@ -563,7 +649,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
563 |
debug_attempts = gr.State(0) # Counter for debug attempts
|
564 |
app_description = gr.State(None) # Stores the user's original request string
|
565 |
repo_name_state = gr.State(None) # Stores the parsed repo name
|
566 |
-
generated_code_state = gr.State(None) # Temporarily stores generated code or file content
|
567 |
|
568 |
with gr.Row():
|
569 |
# Sidebar
|
@@ -574,10 +660,9 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
574 |
|
575 |
# Initial load to check login status
|
576 |
ai_builder_tab.load(show_profile, outputs=login_status)
|
577 |
-
# Update status on login click
|
578 |
login_btn.click(show_profile, outputs=login_status)
|
579 |
-
|
580 |
-
login_btn.click(lambda p, t: (p, t), outputs=[hf_profile, hf_token])
|
581 |
|
582 |
gr.Markdown("## Google AI Studio API Key")
|
583 |
gemini_input = gr.Textbox(label="API Key", type="password", interactive=True) # Ensure interactive
|
@@ -591,9 +676,9 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
591 |
choices=[
|
592 |
("Gemini 2.5 Flash Preview 04-17", "gemini-2.5-flash-preview-04-17"),
|
593 |
("Gemini 2.5 Pro Preview 03-25", "gemini-2.5-pro-preview-03-25"),
|
594 |
-
("Gemini
|
595 |
-
("Gemini
|
596 |
-
("Gemini 1.
|
597 |
],
|
598 |
value="gemini-2.5-flash-preview-04-17",
|
599 |
label="Select model",
|
@@ -603,6 +688,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
603 |
model_selector.change(lambda m: m, inputs=model_selector, outputs=gemini_model)
|
604 |
|
605 |
# Configure Gemini status on load and when key/model changes
|
|
|
606 |
ai_builder_tab.load(
|
607 |
configure_gemini,
|
608 |
inputs=[gemini_key, gemini_model],
|
@@ -630,19 +716,20 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
630 |
|
631 |
# Main content
|
632 |
with gr.Column(scale=3):
|
633 |
-
|
|
|
634 |
user_input = gr.Textbox(placeholder="Type your message…", interactive=True) # Ensure interactive
|
635 |
send_btn = gr.Button("Send", interactive=False)
|
636 |
|
637 |
# Logic to enable send button only when logged in and API key is set
|
638 |
-
# This
|
639 |
-
def update_send_button_state(profile, token, key, model):
|
640 |
is_logged_in = profile is not None and token is not None
|
641 |
-
is_gemini_ready = key is not None and model is not None
|
642 |
-
#
|
643 |
-
# but for UI responsiveness, this is okay.
|
644 |
return gr.update(interactive=is_logged_in and is_gemini_ready)
|
645 |
|
|
|
646 |
ai_builder_tab.load(
|
647 |
update_send_button_state,
|
648 |
inputs=[hf_profile, hf_token, gemini_key, gemini_model],
|
@@ -664,12 +751,12 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
664 |
outputs=[send_btn]
|
665 |
)
|
666 |
|
667 |
-
|
668 |
iframe = gr.HTML("<p>No Space created yet.</p>")
|
669 |
build_txt = gr.Textbox(label="Build Logs", lines=10, interactive=False, value="") # Initialize empty
|
670 |
run_txt = gr.Textbox(label="Container Logs", lines=10, interactive=False, value="") # Initialize empty
|
671 |
|
672 |
# The main event handler for the Send button
|
|
|
673 |
send_btn.click(
|
674 |
ai_workflow_chat,
|
675 |
inputs=[
|
@@ -692,20 +779,30 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
692 |
outputs=user_input
|
693 |
)
|
694 |
|
695 |
-
# Link state variables to UI status displays
|
|
|
696 |
workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
|
697 |
repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
|
698 |
-
#
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
#
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
709 |
|
710 |
|
711 |
if __name__ == "__main__":
|
|
|
112 |
if not (repo_id and profile and token):
|
113 |
return "⚠️ Please log in and create a Space first."
|
114 |
# Add a short delay before fetching run logs, build might just finish
|
115 |
+
time.sleep(5) # Added delay for robustness
|
116 |
return _fetch_space_logs_level(repo_id, "run", token.token)
|
117 |
|
118 |
|
|
|
159 |
STATE_CHECKING_LOGS_RUN = "checking_logs_run"
|
160 |
STATE_DEBUGGING_CODE = "debugging_code"
|
161 |
STATE_UPLOADING_FIXED_APP_PY = "uploading_fixed_app_py"
|
162 |
+
STATE_COMPLETE = "complete" # Added a final state
|
163 |
|
164 |
MAX_DEBUG_ATTEMPTS = 3
|
165 |
|
166 |
+
def update_chat(history: list[list[str | None]], bot_message: str) -> list[list[str | None]]:
|
167 |
+
"""Helper to set the bot's response for the last user message."""
|
168 |
+
# Assume the last entry was just added with history.append([message, None])
|
169 |
+
# If history is empty, this is an error in logic flow, but add safety.
|
170 |
+
if history:
|
171 |
+
# Ensure the last message is indeed a user message awaiting response
|
172 |
+
if history[-1][1] is None:
|
173 |
+
history[-1][1] = bot_message
|
174 |
+
else:
|
175 |
+
# This case means we're trying to add a bot response but the last message
|
176 |
+
# already has one. This might happen if a step is re-triggered.
|
177 |
+
# Append as a new bot-only message as a fallback.
|
178 |
+
# In a strict state machine, this might indicate a flow error.
|
179 |
+
# But for robustness, let's add it.
|
180 |
+
history.append([None, bot_message])
|
181 |
+
else:
|
182 |
+
# This shouldn't happen - update_chat should always be called after a user message is added.
|
183 |
+
print("Warning: update_chat called with empty history.")
|
184 |
+
history.append([None, bot_message]) # As a fallback, add a bot-only message
|
185 |
+
|
186 |
+
return history # Return the modified history list
|
187 |
|
|
|
|
|
188 |
|
189 |
def ai_workflow_chat(
|
190 |
message: str,
|
|
|
202 |
debug_attempts_state: int,
|
203 |
app_description_state: str | None, # Persist initial request
|
204 |
repo_name_state: str | None, # Persist chosen name
|
205 |
+
generated_code_state: str | None, # Temporarily stores generated code or file content
|
206 |
) -> tuple[
|
207 |
list[list[str | None]], # history
|
208 |
str | None, # repo_id
|
|
|
221 |
attempts = debug_attempts_state
|
222 |
app_desc = app_description_state
|
223 |
repo_name = repo_name_state
|
224 |
+
generated_code = generated_code_state # This slot is reused for different file contents
|
225 |
+
|
226 |
|
227 |
updated_preview = preview_html
|
228 |
updated_build = build_logs
|
229 |
updated_run = container_logs
|
230 |
|
231 |
+
# Add user message to history with a placeholder for the bot's response
|
232 |
+
# We yield immediately after this to show the user's message
|
233 |
history.append([message, None])
|
|
|
234 |
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
235 |
|
236 |
|
|
|
257 |
history = update_chat(history, "Workflow reset.")
|
258 |
# Reset all state variables
|
259 |
yield history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0, None, None, None
|
260 |
+
return # End workflow for this trigger
|
261 |
|
262 |
elif generate_match:
|
263 |
new_repo_name = generate_match.group(1)
|
264 |
new_app_desc = message # Store the full request
|
265 |
history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
|
266 |
# Transition to creating space state, passing name and description
|
267 |
+
state = STATE_CREATING_SPACE
|
268 |
+
repo_name = new_repo_name
|
269 |
+
app_desc = new_app_desc
|
270 |
+
# Yield state change and bot message
|
271 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
272 |
+
|
273 |
|
274 |
elif create_match:
|
275 |
new_repo_name = create_match.group(1)
|
276 |
history = update_chat(history, f"Acknowledged: '{message}'. Starting workflow to create Space `{hf_profile.username}/{new_repo_name}`.")
|
277 |
# Transition to creating space state, just passing the name (desc will be default)
|
278 |
+
state = STATE_CREATING_SPACE
|
279 |
+
repo_name = new_repo_name
|
280 |
+
# app_desc remains None or existing
|
281 |
+
# Yield state change and bot message
|
282 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
283 |
|
284 |
elif "create" in message.lower() and not repo_id: # Generic create trigger
|
285 |
history = update_chat(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
|
286 |
# Transition to awaiting name state
|
287 |
+
state = STATE_AWAITING_REPO_NAME
|
288 |
+
# Yield state change and bot message
|
289 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
290 |
|
291 |
else:
|
292 |
# Handle other chat messages if needed, or just respond unknown
|
293 |
history = update_chat(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
|
|
|
294 |
# Stay in IDLE state
|
295 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
296 |
+
return # End workflow for this trigger
|
297 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
|
299 |
+
# --- Workflow steps triggered by state ---
|
300 |
+
# Note: These steps assume the state was transitioned into in the previous yield.
|
301 |
+
# A new click triggers the function again, and the state machine picks up here.
|
302 |
+
|
303 |
+
if state == STATE_CREATING_SPACE:
|
304 |
# This state is triggered when we *already have* the repo_name in state
|
305 |
if not repo_name: # Safety check
|
306 |
history = update_chat(history, "Internal error: Repo name missing for creation. Resetting.")
|
|
|
310 |
try:
|
311 |
new_repo_id, iframe_html = create_space_action(repo_name, space_sdk, hf_profile, hf_token)
|
312 |
updated_preview = iframe_html
|
313 |
+
repo_id = new_repo_id # Update repo_id state variable
|
314 |
+
history = update_chat(history, f"✅ Space `{repo_id}` created. Click 'Send' to generate and upload code.")
|
315 |
+
# Transition to generating code state
|
316 |
+
state = STATE_GENERATING_CODE
|
317 |
+
# Yield state change and bot message
|
318 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
319 |
|
320 |
except Exception as e:
|
321 |
+
history = update_chat(history, f"❌ Error creating space: {e}. Click 'reset'.")
|
322 |
# Reset state on failure
|
323 |
yield history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0, None, None, None
|
324 |
|
325 |
+
|
326 |
elif state == STATE_GENERATING_CODE:
|
327 |
# Use the stored app description or a default
|
328 |
prompt_desc = app_desc if app_desc else 'a Gradio image-blur test app with upload and slider controls'
|
|
|
334 |
"""
|
335 |
try:
|
336 |
history = update_chat(history, "🧠 Generating `app.py` code with Gemini...")
|
337 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show "Generating..." message
|
338 |
|
339 |
code = call_gemini(prompt, gemini_api_key, gemini_model)
|
340 |
# Clean markdown and whitespace
|
|
|
349 |
|
350 |
history = update_chat(history, "✅ `app.py` code generated. Click 'Send' to upload.")
|
351 |
# Transition to uploading state, store the generated code
|
352 |
+
state = STATE_UPLOADING_APP_PY
|
353 |
+
generated_code = code # Store code in state
|
354 |
+
# Yield state change and bot message
|
355 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
356 |
|
357 |
except Exception as e:
|
358 |
history = update_chat(history, f"❌ Error generating code: {e}. Click 'reset'.")
|
359 |
# Reset state on failure
|
360 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
361 |
|
362 |
+
|
363 |
elif state == STATE_UPLOADING_APP_PY:
|
364 |
# Use the generated_code stored in state
|
365 |
if not generated_code:
|
|
|
368 |
return
|
369 |
|
370 |
history = update_chat(history, "☁️ Uploading `app.py`...")
|
371 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
|
372 |
|
373 |
try:
|
374 |
upload_file_to_space_action(io.StringIO(generated_code), "app.py", repo_id, hf_profile, hf_token)
|
375 |
history = update_chat(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
|
376 |
+
# Transition to generating requirements, clear the temporary code storage
|
377 |
+
state = STATE_GENERATING_REQUIREMENTS
|
378 |
+
generated_code = None # Clear temporary storage
|
379 |
+
# Yield state change and bot message
|
380 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
381 |
|
382 |
except Exception as e:
|
383 |
history = update_chat(history, f"❌ Error uploading app.py: {e}. Click 'reset'.")
|
384 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
385 |
|
386 |
+
|
387 |
elif state == STATE_GENERATING_REQUIREMENTS:
|
388 |
history = update_chat(history, "📄 Generating `requirements.txt`...")
|
389 |
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield to show message
|
|
|
391 |
# Simple heuristic for requirements based on SDK and common needs
|
392 |
reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
|
393 |
# Add common deps if likely used (could parse code, but simpler heuristic for demo)
|
394 |
+
if "google.generativeai" in str(generated_code_state) or gemini_api_key: # Check if Gemini was used for code generation OR if key is set
|
395 |
reqs_list.append("google-generativeai")
|
396 |
+
if "requests" in str(generated_code_state):
|
397 |
reqs_list.append("requests")
|
398 |
+
reqs_list.append("huggingface_hub") # Needed for log fetching etc if done inside the space itself (though not currently)
|
399 |
+
# Add Pillow for image processing if it's an image app (common requirement)
|
400 |
+
if "image" in str(app_desc).lower() or "upload" in str(app_desc).lower():
|
401 |
+
reqs_list.append("Pillow")
|
402 |
+
|
403 |
|
404 |
reqs_content = "\n".join(reqs_list) + "\n"
|
405 |
|
406 |
history = update_chat(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
|
407 |
# Transition to uploading requirements, store content temporarily
|
408 |
+
state = STATE_UPLOADING_REQUIREMENTS
|
409 |
+
generated_code = reqs_content # Pass content in state (reusing generated_code slot)
|
410 |
+
# Yield state change and bot message
|
411 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
412 |
|
413 |
|
414 |
elif state == STATE_UPLOADING_REQUIREMENTS:
|
415 |
+
# Use content stored in state (reusing generated_code slot)
|
416 |
reqs_content_to_upload = generated_code # Get content from state
|
417 |
if not reqs_content_to_upload:
|
418 |
history = update_chat(history, "Internal error: No requirements content to upload. Resetting.")
|
|
|
420 |
return
|
421 |
|
422 |
history = update_chat(history, "☁️ Uploading `requirements.txt`...")
|
423 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
|
424 |
|
425 |
try:
|
426 |
upload_file_to_space_action(io.StringIO(reqs_content_to_upload), "requirements.txt", repo_id, hf_profile, hf_token)
|
427 |
history = update_chat(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
|
428 |
+
# Transition to generating README, clear the temporary storage
|
429 |
+
state = STATE_GENERATING_README
|
430 |
+
generated_code = None # Clear temporary storage
|
431 |
+
# Yield state change and bot message
|
432 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
433 |
+
|
434 |
|
435 |
except Exception as e:
|
436 |
history = update_chat(history, f"❌ Error uploading requirements.txt: {e}. Click 'reset'.")
|
|
|
442 |
|
443 |
# Generate a simple README based on app_desc or repo_name
|
444 |
readme_title = repo_name if repo_name else "My Awesome Space"
|
445 |
+
# Use app_desc if available, otherwise a generic description
|
446 |
+
readme_description = app_desc if app_desc else f"This Hugging Face Space hosts an AI-generated {space_sdk} application."
|
447 |
|
448 |
readme_content = f"# {readme_title}\n\n{readme_description}\n\n" \
|
449 |
+
"This Space was automatically generated by an AI workflow.\n\n" \
|
450 |
+
f"Built with the {space_sdk} SDK.\n"
|
451 |
|
452 |
history = update_chat(history, "✅ `README.md` generated. Click 'Send' to upload.")
|
453 |
# Transition to uploading README, store content temporarily
|
454 |
+
state = STATE_UPLOADING_README
|
455 |
+
generated_code = readme_content # Pass content in state
|
456 |
+
# Yield state change and bot message
|
457 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
458 |
|
459 |
|
460 |
elif state == STATE_UPLOADING_README:
|
461 |
+
# Use content stored in state (reusing generated_code slot)
|
462 |
readme_content_to_upload = generated_code # Get content from state
|
463 |
if not readme_content_to_upload:
|
464 |
history = update_chat(history, "Internal error: No README content to upload. Resetting.")
|
|
|
466 |
return
|
467 |
|
468 |
history = update_chat(history, "☁️ Uploading `README.md`...")
|
469 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
|
470 |
|
471 |
try:
|
472 |
upload_file_to_space_action(io.StringIO(readme_content_to_upload), "README.md", repo_id, hf_profile, hf_token)
|
473 |
+
history = update_chat(history, "✅ Uploaded `README.md`. All files uploaded. Space is now building. Click 'Send' to check build logs.")
|
474 |
+
# Transition to checking build logs, clear the temporary storage
|
475 |
+
state = STATE_CHECKING_LOGS_BUILD
|
476 |
+
generated_code = None # Clear temporary storage
|
477 |
+
# Yield state change and bot message
|
478 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
479 |
+
|
480 |
|
481 |
except Exception as e:
|
482 |
history = update_chat(history, f"❌ Error uploading README.md: {e}. Click 'reset'.")
|
|
|
494 |
# Simple check: if build logs contain "Error" or "Exception", might indicate build issue.
|
495 |
# More robust would involve checking build status via API, but logs are simpler for demo.
|
496 |
# Assuming successful build leads to container logs check.
|
497 |
+
# Check updated_build content for errors
|
498 |
if "Error" in updated_build or "Exception" in updated_build:
|
499 |
+
history = update_chat(history, "⚠️ Build logs may contain errors. Please inspect above. Click 'Send' to check container logs (app might still start).")
|
500 |
+
# Transition to run logs check
|
501 |
+
state = STATE_CHECKING_LOGS_RUN
|
502 |
+
# Yield state change and message, include updated build logs
|
503 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
504 |
|
505 |
else:
|
506 |
history = update_chat(history, "✅ Build logs fetched. Click 'Send' to check container logs.")
|
507 |
+
# Transition to run logs check
|
508 |
+
state = STATE_CHECKING_LOGS_RUN
|
509 |
+
# Yield state change and message, include updated build logs
|
510 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
511 |
|
512 |
|
513 |
elif state == STATE_CHECKING_LOGS_RUN:
|
|
|
522 |
attempts += 1
|
523 |
history = update_chat(history, f"❌ Errors detected in container logs. Attempting debug fix #{attempts}/{MAX_DEBUG_ATTEMPTS}. Click 'Send' to proceed.")
|
524 |
# Transition to debugging state, increment attempts
|
525 |
+
state = STATE_DEBUGGING_CODE
|
526 |
+
# Yield state change and message, include updated run logs
|
527 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
528 |
|
529 |
elif ("Error" in updated_run or "Exception" in updated_run) and attempts >= MAX_DEBUG_ATTEMPTS:
|
530 |
history = update_chat(history, f"❌ Errors detected in container logs. Max debug attempts ({MAX_DEBUG_ATTEMPTS}) reached. Please inspect logs manually or click 'reset'.")
|
531 |
+
# Transition to complete state after failed attempts
|
532 |
+
state = STATE_COMPLETE # Indicate workflow finished with errors
|
533 |
+
# Yield state change and message
|
534 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
535 |
|
536 |
else:
|
537 |
history = update_chat(history, "✅ App appears to be running successfully! Check the iframe above. Click 'reset' to start a new project.")
|
538 |
+
# Transition to complete state on success
|
539 |
+
state = STATE_COMPLETE # Indicate workflow finished successfully
|
540 |
+
# Yield state change and message
|
541 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
542 |
+
|
543 |
|
544 |
elif state == STATE_DEBUGGING_CODE:
|
545 |
history = update_chat(history, f"🧠 Calling Gemini to generate fix based on logs...")
|
|
|
569 |
|
570 |
history = update_chat(history, "✅ Fix code generated. Click 'Send' to upload.")
|
571 |
# Transition to uploading fixed code, pass the fixed code
|
572 |
+
state = STATE_UPLOADING_FIXED_APP_PY
|
573 |
+
generated_code = fix_code # Store fix_code in state
|
574 |
+
# Yield state change and message
|
575 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
576 |
|
577 |
|
578 |
except Exception as e:
|
|
|
581 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
582 |
|
583 |
elif state == STATE_UPLOADING_FIXED_APP_PY:
|
584 |
+
# Use the fixed code stored in state (reusing generated_code slot)
|
585 |
fixed_code_to_upload = generated_code # Get code from state
|
586 |
if not fixed_code_to_upload:
|
587 |
history = update_chat(history, "Internal error: No fixed code available to upload. Resetting.")
|
|
|
589 |
return
|
590 |
|
591 |
history = update_chat(history, "☁️ Uploading fixed `app.py`...")
|
592 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code # Yield, keep temp state
|
593 |
|
594 |
try:
|
595 |
upload_file_to_space_action(io.StringIO(fixed_code_to_upload), "app.py", repo_id, hf_profile, hf_token)
|
596 |
history = update_chat(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
|
597 |
+
# Transition back to checking run logs (after rebuild), clear temporary storage
|
598 |
+
state = STATE_CHECKING_LOGS_RUN
|
599 |
+
generated_code = None # Clear temporary storage
|
600 |
+
# Yield state change and message
|
601 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
602 |
+
|
603 |
|
604 |
except Exception as e:
|
605 |
history = update_chat(history, f"❌ Error uploading fixed app.py: {e}. Click 'reset'.")
|
|
|
608 |
elif state == STATE_COMPLETE:
|
609 |
# App is successfully deployed or failed after attempts.
|
610 |
# User should click reset or start a new command.
|
611 |
+
# Just yield the current state to update UI if needed.
|
612 |
+
# The message for STATE_COMPLETE is set in the state it transitions from.
|
613 |
+
pass # No state change needed, just yield current state at the end of try block
|
614 |
+
|
615 |
|
616 |
+
# If we reached here and the state wasn't handled (e.g., from a previous error state)
|
617 |
+
# or if a yield didn't happen in the previous block (logic error)
|
618 |
+
# The initial yield after adding the user message ensures something is always sent back.
|
619 |
|
620 |
|
621 |
except Exception as e:
|
622 |
# Catch-all for unexpected exceptions in any state
|
623 |
+
# This might mean the state machine logic itself failed or a core function raised unexpectedly.
|
624 |
+
error_message = f"Workflow step failed unexpectedly ({state}): {e}. Click 'Send' to re-attempt this step or 'reset'."
|
625 |
+
history = update_chat(history, error_message)
|
626 |
+
print(f"Critical Error in state {state}: {e}") # Print to server logs
|
627 |
+
# Transition to idle state on unexpected errors to prevent getting stuck.
|
628 |
yield history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0, None, None, None
|
629 |
|
630 |
|
631 |
+
# Final yield after the try block ensures the final state is returned
|
632 |
+
# after a successful step completes or after an error is caught.
|
633 |
+
# This is particularly important for states like STATE_COMPLETE.
|
634 |
+
yield history, repo_id, state, updated_preview, updated_run, updated_build, attempts, app_desc, repo_name, generated_code
|
635 |
+
|
636 |
+
|
637 |
+
|
638 |
# --- Build the Gradio UI ---
|
639 |
|
640 |
with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
|
649 |
debug_attempts = gr.State(0) # Counter for debug attempts
|
650 |
app_description = gr.State(None) # Stores the user's original request string
|
651 |
repo_name_state = gr.State(None) # Stores the parsed repo name
|
652 |
+
generated_code_state = gr.State(None) # Temporarily stores generated code or file content (reused)
|
653 |
|
654 |
with gr.Row():
|
655 |
# Sidebar
|
|
|
660 |
|
661 |
# Initial load to check login status
|
662 |
ai_builder_tab.load(show_profile, outputs=login_status)
|
663 |
+
# Update status and state on login click
|
664 |
login_btn.click(show_profile, outputs=login_status)
|
665 |
+
login_btn.click(lambda p, t: (p, t), inputs=[login_btn], outputs=[hf_profile, hf_token]) # Use login_btn output directly
|
|
|
666 |
|
667 |
gr.Markdown("## Google AI Studio API Key")
|
668 |
gemini_input = gr.Textbox(label="API Key", type="password", interactive=True) # Ensure interactive
|
|
|
676 |
choices=[
|
677 |
("Gemini 2.5 Flash Preview 04-17", "gemini-2.5-flash-preview-04-17"),
|
678 |
("Gemini 2.5 Pro Preview 03-25", "gemini-2.5-pro-preview-03-25"),
|
679 |
+
("Gemini 1.5 Flash", "gemini-1.5-flash"), # Keep relevant models
|
680 |
+
("Gemini 1.5 Pro", "gemini-1.5-pro"),
|
681 |
+
("Gemini 1.0 Pro", "gemini-1.0-pro"),
|
682 |
],
|
683 |
value="gemini-2.5-flash-preview-04-17",
|
684 |
label="Select model",
|
|
|
688 |
model_selector.change(lambda m: m, inputs=model_selector, outputs=gemini_model)
|
689 |
|
690 |
# Configure Gemini status on load and when key/model changes
|
691 |
+
# Note: These handlers *update the status text*, they don't block the workflow.
|
692 |
ai_builder_tab.load(
|
693 |
configure_gemini,
|
694 |
inputs=[gemini_key, gemini_model],
|
|
|
716 |
|
717 |
# Main content
|
718 |
with gr.Column(scale=3):
|
719 |
+
# Corrected Chatbot initialization
|
720 |
+
chatbot = gr.Chatbot(type='messages') # Added type='messages'
|
721 |
user_input = gr.Textbox(placeholder="Type your message…", interactive=True) # Ensure interactive
|
722 |
send_btn = gr.Button("Send", interactive=False)
|
723 |
|
724 |
# Logic to enable send button only when logged in and API key is set
|
725 |
+
# This function determines the interactive state of the button
|
726 |
+
def update_send_button_state(profile: gr.OAuthProfile | None, token: gr.OAuthToken | None, key: str | None, model: str | None):
|
727 |
is_logged_in = profile is not None and token is not None
|
728 |
+
is_gemini_ready = key is not None and model is not None # Basic check
|
729 |
+
# Could add a check if configure_gemini returned success last time
|
|
|
730 |
return gr.update(interactive=is_logged_in and is_gemini_ready)
|
731 |
|
732 |
+
# Update button state on load and whenever relevant inputs change
|
733 |
ai_builder_tab.load(
|
734 |
update_send_button_state,
|
735 |
inputs=[hf_profile, hf_token, gemini_key, gemini_model],
|
|
|
751 |
outputs=[send_btn]
|
752 |
)
|
753 |
|
|
|
754 |
iframe = gr.HTML("<p>No Space created yet.</p>")
|
755 |
build_txt = gr.Textbox(label="Build Logs", lines=10, interactive=False, value="") # Initialize empty
|
756 |
run_txt = gr.Textbox(label="Container Logs", lines=10, interactive=False, value="") # Initialize empty
|
757 |
|
758 |
# The main event handler for the Send button
|
759 |
+
# It maps inputs/outputs to the ai_workflow_chat generator function
|
760 |
send_btn.click(
|
761 |
ai_workflow_chat,
|
762 |
inputs=[
|
|
|
779 |
outputs=user_input
|
780 |
)
|
781 |
|
782 |
+
# Link state variables to UI status displays (reactive updates)
|
783 |
+
# These update the UI components whenever the State variables they listen to change.
|
784 |
workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
|
785 |
repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
|
786 |
+
# The logs and iframe are updated directly by the `send_btn.click` output,
|
787 |
+
# but adding reactive updates from the state variables can sometimes help
|
788 |
+
# ensure consistency if state changes are yielded before the UI components are
|
789 |
+
# explicitly updated in the same yield tuple.
|
790 |
+
# iframe.change(lambda h: h, inputs=iframe, outputs=iframe) # Already linked via click outputs
|
791 |
+
# build_txt.change(lambda t: t, inputs=build_txt, outputs=build_txt) # Already linked
|
792 |
+
# run_txt.change(lambda t: t, inputs=run_txt, outputs=run_txt) # Already linked
|
793 |
+
|
794 |
+
|
795 |
+
# Add an initial message to the chatbot on load
|
796 |
+
# THIS CALL MUST BE INSIDE the with gr.Blocks() block
|
797 |
+
def greet():
|
798 |
+
# Returning a list of lists in the format [user_msg, bot_msg] for Chatbot
|
799 |
+
# An initial message is often just a bot message, format can be [None, bot_msg]
|
800 |
+
# but Chatbot(type='messages') expects {'role': ..., 'content': ...}
|
801 |
+
# For the initial message, the tuple format might still work, or we structure it.
|
802 |
+
# Let's try the tuple format first as it was used before.
|
803 |
+
return [[None, "Welcome! Please log in to Hugging Face and provide your Google AI Studio API key to start building Spaces. Once ready, type 'generate me a gradio app called myapp' or 'create' to begin."]]
|
804 |
+
|
805 |
+
ai_builder_tab.load(greet, outputs=chatbot)
|
806 |
|
807 |
|
808 |
if __name__ == "__main__":
|