Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -219,7 +219,7 @@ def add_bot_message(history: list[dict], bot_message: str) -> list[dict]:
|
|
219 |
|
220 |
# Helper function to control send button interactivity and prerequisite status text
|
221 |
# This function is triggered by changes in login status and Gemini configuration
|
222 |
-
#
|
223 |
def update_send_button_state(
|
224 |
profile: gr.OAuthProfile | None,
|
225 |
token: gr.OAuthToken | None,
|
@@ -257,7 +257,7 @@ def update_send_button_state(
|
|
257 |
return gr.update(interactive=is_ready), status_str
|
258 |
|
259 |
# Add an initial welcome message to the chatbot (defined outside Blocks to be called by load chain)
|
260 |
-
#
|
261 |
def greet():
|
262 |
return [{"role": "assistant", "content": "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."}]
|
263 |
|
@@ -275,29 +275,30 @@ def ai_workflow_chat(
|
|
275 |
repo_id_state: str | None,
|
276 |
workflow_state: str,
|
277 |
space_sdk: str,
|
278 |
-
|
279 |
-
|
280 |
-
|
|
|
281 |
debug_attempts_state: int,
|
282 |
app_description_state: str | None,
|
283 |
repo_name_state: str | None,
|
284 |
generated_code_state: str | None,
|
285 |
-
use_grounding_state: bool, #
|
286 |
# Absorb potential extra args passed by Gradio event listeners (e.g. old value, event data)
|
287 |
*args,
|
288 |
**kwargs
|
289 |
) -> tuple[
|
290 |
-
list[dict], # 0: Updated chat history
|
291 |
-
str | None, # 1: Updated repo_id
|
292 |
-
str, # 2: Updated workflow state
|
293 |
-
str, # 3: Updated iframe HTML
|
294 |
-
str, # 4: Updated container logs
|
295 |
-
str, # 5: Updated build logs
|
296 |
-
int, # 6: Updated debug attempts count
|
297 |
-
str | None, # 7: Updated app description
|
298 |
-
str | None, # 8: Updated repo name
|
299 |
-
str | None, # 9: Updated generated code (for
|
300 |
-
bool, # 10: Updated use_grounding_state (
|
301 |
]:
|
302 |
"""
|
303 |
Generator function to handle the AI workflow state machine.
|
@@ -313,6 +314,7 @@ def ai_workflow_chat(
|
|
313 |
use_grounding = use_grounding_state # Unpack grounding state
|
314 |
|
315 |
# Keep copies of potentially updated UI elements passed as inputs to update them later
|
|
|
316 |
updated_preview = preview_html
|
317 |
updated_build = build_logs
|
318 |
updated_run = container_logs
|
@@ -326,7 +328,7 @@ def ai_workflow_chat(
|
|
326 |
|
327 |
# Yield immediately to update the chat UI with the user's message
|
328 |
# This provides immediate feedback to the user while the AI processes
|
329 |
-
# Ensure all state variables are yielded back in the correct order
|
330 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
331 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
332 |
|
@@ -361,6 +363,7 @@ def ai_workflow_chat(
|
|
361 |
# Reset the workflow state and associated variables
|
362 |
history = add_bot_message(history, "Workflow reset.")
|
363 |
# Yield updated history and reset state variables to their initial values
|
|
|
364 |
yield (history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0,
|
365 |
None, None, None, False) # Reset use_grounding to default False, and other states to None/default
|
366 |
# No return needed after yield in this generator pattern; execution for this click ends here.
|
@@ -374,7 +377,7 @@ def ai_workflow_chat(
|
|
374 |
state = STATE_CREATING_SPACE
|
375 |
repo_name = new_repo_name
|
376 |
app_desc = new_app_desc
|
377 |
-
# Yield updated history and state variables
|
378 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
379 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
380 |
# No return needed
|
@@ -386,7 +389,7 @@ def ai_workflow_chat(
|
|
386 |
# Update state variables for the next step (creation)
|
387 |
state = STATE_CREATING_SPACE
|
388 |
repo_name = new_repo_name
|
389 |
-
# Yield updated history and state variables
|
390 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
391 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
392 |
# No return needed
|
@@ -395,7 +398,7 @@ def ai_workflow_chat(
|
|
395 |
# User wants to create but didn't specify a name yet
|
396 |
history = add_bot_message(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
|
397 |
state = STATE_AWAITING_REPO_NAME # Transition to the state where we wait for the name
|
398 |
-
# Yield updated history and state
|
399 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
400 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
401 |
# No return needed
|
@@ -403,7 +406,7 @@ def ai_workflow_chat(
|
|
403 |
else:
|
404 |
# Command not recognized in IDLE state
|
405 |
history = add_bot_message(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
|
406 |
-
# Yield updated history and current state
|
407 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
408 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
409 |
# No return needed
|
@@ -416,7 +419,7 @@ def ai_workflow_chat(
|
|
416 |
# Allow letters, numbers, hyphens, underscores, max 100 chars (HF limit check)
|
417 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
418 |
history = add_bot_message(history, "Invalid name. Please provide a single word/slug for the Space name (letters, numbers, underscores, hyphens only, max 100 chars).")
|
419 |
-
# Stay in AWAITING_REPO_NAME state and yield message
|
420 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
421 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
422 |
# No return needed
|
@@ -425,7 +428,7 @@ def ai_workflow_chat(
|
|
425 |
history = add_bot_message(history, f"Using Space name `{new_repo_name}`. Creating Space `{hf_profile.username}/{new_repo_name}`...")
|
426 |
state = STATE_CREATING_SPACE # Transition state to creation
|
427 |
repo_name = new_repo_name # Store the validated repo name
|
428 |
-
# Yield updated history, state, and repo name.
|
429 |
# The next click will proceed from the STATE_CREATING_SPACE block.
|
430 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
431 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
@@ -438,8 +441,9 @@ def ai_workflow_chat(
|
|
438 |
# Ensure repo_name is available (it should have been set in the previous step)
|
439 |
if not repo_name:
|
440 |
history = add_bot_message(history, "Internal error: Repo name missing for creation. Resetting.")
|
|
|
441 |
yield (history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0,
|
442 |
-
None, None, None, use_grounding) #
|
443 |
# No return needed
|
444 |
|
445 |
else:
|
@@ -450,16 +454,16 @@ def ai_workflow_chat(
|
|
450 |
repo_id = new_repo_id # Store the official repo_id
|
451 |
history = add_bot_message(history, f"✅ Space `{repo_id}` created. Click 'Send' to generate and upload code.")
|
452 |
state = STATE_GENERATING_CODE # Transition to the next state
|
453 |
-
# Yield updated state variables and history
|
454 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
455 |
-
attempts, app_desc, repo_name, generated_code, use_grounding) #
|
456 |
# No return needed
|
457 |
|
458 |
except Exception as e:
|
459 |
history = add_bot_message(history, f"❌ Error creating space: {e}. Click 'reset'.")
|
460 |
# Yield error message and reset state on failure
|
461 |
yield (history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0,
|
462 |
-
None, None, None, use_grounding) #
|
463 |
# No return needed
|
464 |
|
465 |
|
@@ -479,7 +483,7 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
479 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
480 |
# Yield to show message before the potentially time-consuming API call
|
481 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
482 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
483 |
|
484 |
# Perform the Gemini API call to generate code, optionally using grounding
|
485 |
code = call_gemini(prompt, gemini_api_key, gemini_model, use_grounding=use_grounding)
|
@@ -498,16 +502,16 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
498 |
history = add_bot_message(history, "✅ `app.py` code generated. Click 'Send' to upload.")
|
499 |
state = STATE_UPLOADING_APP_PY # Transition to the upload state
|
500 |
generated_code = code # Store the generated code in the state variable for the next step
|
501 |
-
# Yield updated state variables and history
|
502 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
503 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
504 |
# No return needed
|
505 |
|
506 |
except Exception as e:
|
507 |
history = add_bot_message(history, f"❌ Error generating code: {e}. Click 'reset'.")
|
508 |
# Yield error message and reset state on failure
|
509 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
510 |
-
None, None, None, use_grounding)
|
511 |
# No return needed
|
512 |
|
513 |
|
@@ -517,14 +521,14 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
517 |
if not code_to_upload:
|
518 |
history = add_bot_message(history, "Internal error: No code to upload. Resetting.")
|
519 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
520 |
-
None, None, None, use_grounding)
|
521 |
# No return needed
|
522 |
|
523 |
else:
|
524 |
history = add_bot_message(history, "☁️ Uploading `app.py`...")
|
525 |
-
# Yield to show message before the upload action
|
526 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
527 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
528 |
|
529 |
try:
|
530 |
# Perform the file upload action
|
@@ -532,24 +536,24 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
532 |
history = add_bot_message(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
|
533 |
state = STATE_GENERATING_REQUIREMENTS # Transition state
|
534 |
generated_code = None # Clear the stored code after use to free memory/state space
|
535 |
-
# Yield updated state variables and history
|
536 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
537 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
538 |
# No return needed
|
539 |
|
540 |
except Exception as e:
|
541 |
history = add_bot_message(history, f"❌ Error uploading `app.py`: {e}. Click 'reset'.")
|
542 |
# Yield error message and reset state on failure
|
543 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
544 |
-
None, None, None, use_grounding)
|
545 |
# No return needed
|
546 |
|
547 |
|
548 |
elif state == STATE_GENERATING_REQUIREMENTS:
|
549 |
history = add_bot_message(history, "📄 Generating `requirements.txt`...")
|
550 |
-
# Yield to show message before generating requirements
|
551 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
552 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
553 |
|
554 |
# Logic to determine required packages based on SDK and keywords in the app description
|
555 |
reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
|
@@ -583,15 +587,14 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
583 |
# Sort alphabetically for cleaner requirements.txt
|
584 |
reqs_list.sort()
|
585 |
|
586 |
-
|
587 |
reqs_content = "\n".join(reqs_list) + "\n"
|
588 |
|
589 |
history = add_bot_message(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
|
590 |
state = STATE_UPLOADING_REQUIREMENTS # Transition state
|
591 |
generated_code = reqs_content # Store requirements content
|
592 |
-
# Yield updated state variables and history
|
593 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
594 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
595 |
# No return needed
|
596 |
|
597 |
|
@@ -601,14 +604,14 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
601 |
if not reqs_content_to_upload:
|
602 |
history = add_bot_message(history, "Internal error: No requirements content to upload. Resetting.")
|
603 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
604 |
-
None, None, None, use_grounding)
|
605 |
# No return needed
|
606 |
|
607 |
else:
|
608 |
history = add_bot_message(history, "☁️ Uploading `requirements.txt`...")
|
609 |
-
# Yield
|
610 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
611 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
612 |
|
613 |
try:
|
614 |
# Perform requirements file upload
|
@@ -616,23 +619,23 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
616 |
history = add_bot_message(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
|
617 |
state = STATE_GENERATING_README # Transition state
|
618 |
generated_code = None # Clear content after use
|
619 |
-
# Yield updated state variables and history
|
620 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
621 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
622 |
# No return needed
|
623 |
|
624 |
except Exception as e:
|
625 |
history = add_bot_message(history, f"❌ Error uploading `requirements.txt`: {e}. Click 'reset'.")
|
626 |
# Yield error message and reset state on failure
|
627 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
628 |
-
None, None, None, use_grounding)
|
629 |
# No return needed
|
630 |
|
631 |
elif state == STATE_GENERATING_README:
|
632 |
history = add_bot_message(history, "📝 Generating `README.md`...")
|
633 |
-
# Yield message before generating README
|
634 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
635 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
636 |
|
637 |
# Generate simple README content with Space metadata header
|
638 |
readme_title = repo_name if repo_name else "My Awesome Space"
|
@@ -658,9 +661,9 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
658 |
history = add_bot_message(history, "✅ `README.md` generated. Click 'Send' to upload.")
|
659 |
state = STATE_UPLOADING_README # Transition state
|
660 |
generated_code = readme_content # Store README content
|
661 |
-
# Yield updated state variables and history
|
662 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
663 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
664 |
# No return needed
|
665 |
|
666 |
|
@@ -670,14 +673,14 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
670 |
if not readme_content_to_upload:
|
671 |
history = add_bot_message(history, "Internal error: No README content to upload. Resetting.")
|
672 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
673 |
-
None, None, None, use_grounding)
|
674 |
# No return needed
|
675 |
|
676 |
else:
|
677 |
history = add_bot_message(history, "☁️ Uploading `README.md`...")
|
678 |
-
# Yield message before upload
|
679 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
680 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
681 |
|
682 |
try:
|
683 |
# Perform README file upload
|
@@ -685,23 +688,23 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
685 |
history = add_bot_message(history, "✅ Uploaded `README.md`. All files uploaded. Space is now building. Click 'Send' to check build logs.")
|
686 |
state = STATE_CHECKING_LOGS_BUILD # Transition to checking build logs
|
687 |
generated_code = None # Clear content after use
|
688 |
-
# Yield updated state variables and history
|
689 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
690 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
691 |
# No return needed
|
692 |
|
693 |
except Exception as e:
|
694 |
history = add_bot_message(history, f"❌ Error uploading `README.md`: {e}. Click 'reset'.")
|
695 |
# Yield error message and reset state on failure
|
696 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
697 |
-
None, None, None, use_grounding)
|
698 |
# No return needed
|
699 |
|
700 |
elif state == STATE_CHECKING_LOGS_BUILD:
|
701 |
history = add_bot_message(history, "🔍 Fetching build logs...")
|
702 |
-
# Yield message before fetching logs (which includes a delay)
|
703 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
704 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
705 |
|
706 |
# Fetch build logs from HF Space
|
707 |
build_logs_text = get_build_logs_action(repo_id, hf_profile, hf_token)
|
@@ -713,7 +716,7 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
713 |
state = STATE_CHECKING_LOGS_RUN # Transition even on build error, to see if container starts
|
714 |
# Yield updated state, logs, and variables
|
715 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
716 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
717 |
# No return needed
|
718 |
|
719 |
else:
|
@@ -721,15 +724,15 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
721 |
state = STATE_CHECKING_LOGS_RUN # Transition to next log check
|
722 |
# Yield updated state, logs, and variables
|
723 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
724 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
725 |
# No return needed
|
726 |
|
727 |
|
728 |
elif state == STATE_CHECKING_LOGS_RUN:
|
729 |
history = add_bot_message(history, "🔍 Fetching container logs...")
|
730 |
-
# Yield message before fetching logs (includes a delay)
|
731 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
732 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
733 |
|
734 |
# Fetch container logs from HF Space
|
735 |
container_logs_text = get_container_logs_action(repo_id, hf_profile, hf_token)
|
@@ -742,7 +745,7 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
742 |
state = STATE_DEBUGGING_CODE # Transition to the debugging state
|
743 |
# Yield updated state, logs, attempts, and variables
|
744 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
745 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
746 |
# No return needed
|
747 |
|
748 |
elif ("error" in updated_run.lower() or "exception" in updated_run.lower()) and attempts >= MAX_DEBUG_ATTEMPTS:
|
@@ -751,7 +754,7 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
751 |
state = STATE_COMPLETE # Workflow ends on failure after attempts
|
752 |
# Yield updated state, logs, attempts, and variables
|
753 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
754 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
755 |
# No return needed
|
756 |
|
757 |
else:
|
@@ -760,7 +763,7 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
760 |
state = STATE_COMPLETE # Workflow ends on success
|
761 |
# Yield updated state, logs, attempts, and variables
|
762 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
763 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
764 |
# No return needed
|
765 |
|
766 |
|
@@ -768,9 +771,9 @@ This Space was automatically generated by an AI workflow using Google Gemini and
|
|
768 |
history = add_bot_message(history, f"🧠 Calling Gemini to generate fix based on logs...")
|
769 |
if use_grounding:
|
770 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
771 |
-
# Yield message before Gemini API call
|
772 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
773 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
774 |
|
775 |
# Construct prompt for Gemini including the container logs
|
776 |
debug_prompt = f"""
|
@@ -803,16 +806,16 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
803 |
history = add_bot_message(history, "✅ Fix code generated. Click 'Send' to upload.")
|
804 |
state = STATE_UPLOADING_FIXED_APP_PY # Transition to the upload state for the fix
|
805 |
generated_code = fix_code # Store the generated fix code
|
806 |
-
# Yield updated state, code, and variables
|
807 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
808 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
809 |
# No return needed
|
810 |
|
811 |
except Exception as e:
|
812 |
history = add_bot_message(history, f"❌ Error generating debug code: {e}. Click 'reset'.")
|
813 |
# Yield error message and reset state on failure
|
814 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
815 |
-
None, None, None, use_grounding)
|
816 |
# No return needed
|
817 |
|
818 |
elif state == STATE_UPLOADING_FIXED_APP_PY:
|
@@ -821,14 +824,14 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
821 |
if not fixed_code_to_upload:
|
822 |
history = add_bot_message(history, "Internal error: No fixed code available to upload. Resetting.")
|
823 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
824 |
-
None, None, None, use_grounding)
|
825 |
# No return needed
|
826 |
|
827 |
else:
|
828 |
history = add_bot_message(history, "☁️ Uploading fixed `app.py`...")
|
829 |
-
# Yield message before upload
|
830 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
831 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
832 |
|
833 |
try:
|
834 |
# Perform the upload of the fixed app.py
|
@@ -836,23 +839,23 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
836 |
history = add_bot_message(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
|
837 |
state = STATE_CHECKING_LOGS_RUN # Go back to checking run logs after uploading the fix
|
838 |
generated_code = None # Clear code after use
|
839 |
-
# Yield updated state, code, and variables
|
840 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
841 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
842 |
# No return needed
|
843 |
|
844 |
except Exception as e:
|
845 |
history = add_bot_message(history, f"❌ Error uploading fixed `app.py`: {e}. Click 'reset'.")
|
846 |
# Yield error message and reset state on failure
|
847 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
848 |
-
None, None, None, use_grounding)
|
849 |
# No return needed
|
850 |
|
851 |
elif state == STATE_COMPLETE:
|
852 |
# If in the complete state, the workflow is finished for this project.
|
853 |
# Subsequent clicks just add user messages; we simply yield the current state.
|
854 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
855 |
-
attempts, app_desc, repo_name, generated_code, use_grounding)
|
856 |
# No return needed
|
857 |
|
858 |
|
@@ -871,6 +874,7 @@ Return **only** the python code block for app.py. Do not include any extra text,
|
|
871 |
|
872 |
with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
873 |
# Gradio State variables - these persist their values across user interactions (clicks)
|
|
|
874 |
hf_profile = gr.State(None)
|
875 |
hf_token = gr.State(None)
|
876 |
# FIX: Initialize gemini_key state from env var on load
|
@@ -890,27 +894,13 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
890 |
# Sidebar column for inputs and status displays
|
891 |
with gr.Column(scale=1, min_width=300):
|
892 |
gr.Markdown("## Hugging Face Login")
|
|
|
893 |
login_status = gr.Markdown("*Not logged in.*")
|
894 |
# Hugging Face Login Button
|
895 |
login_btn = gr.LoginButton(variant="huggingface")
|
896 |
|
897 |
-
# Update status display when login button reports success
|
898 |
-
# Chain to also update the Send button/prereq status
|
899 |
-
# This uses the hf_profile and hf_token *state* variables as inputs
|
900 |
-
login_btn.click(
|
901 |
-
# The LoginButton outputs a tuple (OAuthProfile, OAuthToken) on success
|
902 |
-
lambda x: (x[0], x[1]),
|
903 |
-
inputs=[login_btn],
|
904 |
-
outputs=[hf_profile, hf_token] # Update these State variables
|
905 |
-
).then( # Chain the next action after state is updated
|
906 |
-
# Pass all relevant state variables to re-evaluate prerequisites
|
907 |
-
update_send_button_state,
|
908 |
-
inputs=[hf_profile, hf_token, gemini_key, gemini_model],
|
909 |
-
outputs=[send_btn, prereq_status] # Update button interactivity and status text
|
910 |
-
)
|
911 |
-
|
912 |
gr.Markdown("## Google AI Studio / Gemini")
|
913 |
-
#
|
914 |
gemini_input = gr.Textbox(
|
915 |
label="API Key",
|
916 |
type="password", # Hides input for security
|
@@ -920,7 +910,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
920 |
)
|
921 |
gemini_status = gr.Markdown("") # Display Gemini configuration status
|
922 |
|
923 |
-
#
|
924 |
model_selector = gr.Radio(
|
925 |
choices=[
|
926 |
("Gemini 1.5 Flash", "gemini-1.5-flash"),
|
@@ -932,8 +922,58 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
932 |
interactive=True
|
933 |
)
|
934 |
|
935 |
-
#
|
936 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
937 |
gemini_input.change(
|
938 |
lambda k: k, inputs=[gemini_input], outputs=[gemini_key] # Update gemini_key state
|
939 |
).then(
|
@@ -943,8 +983,7 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
943 |
update_send_button_state, inputs=[hf_profile, hf_token, gemini_key, gemini_model], outputs=[send_btn, prereq_status] # Update button/prereq status
|
944 |
)
|
945 |
|
946 |
-
# Gemini Model Selector change: update model state -> configure Gemini -> update send button
|
947 |
-
# This uses model_selector value as input to update gemini_model state
|
948 |
model_selector.change(
|
949 |
lambda m: m, inputs=[model_selector], outputs=[gemini_model] # Update gemini_model state
|
950 |
).then(
|
@@ -954,66 +993,47 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
954 |
update_send_button_state, inputs=[hf_profile, hf_token, gemini_key, gemini_model], outputs=[send_btn, prereq_status] # Update button/prereq status
|
955 |
)
|
956 |
|
957 |
-
#
|
958 |
-
use_grounding_checkbox = gr.Checkbox(
|
959 |
-
label="Enable Grounding with Google Search",
|
960 |
-
value=False, # Default to off
|
961 |
-
interactive=True,
|
962 |
-
info="Use Google Search results to inform Gemini's response (may improve factuality)."
|
963 |
-
)
|
964 |
-
# Link checkbox change to update the state variable
|
965 |
-
# No need to update send button status here, as grounding is not a core prerequisite
|
966 |
use_grounding_checkbox.change(
|
967 |
lambda v: v, inputs=use_grounding_checkbox, outputs=use_grounding_state
|
968 |
)
|
969 |
|
|
|
|
|
|
|
|
|
970 |
|
971 |
-
|
972 |
-
|
973 |
-
sdk_selector = gr.Radio(choices=["gradio","streamlit"], value="gradio", label="Template SDK", interactive=True)
|
974 |
-
# Update the sdk_state state variable when the selection changes
|
975 |
-
sdk_selector.change(lambda s: s, inputs=sdk_selector, outputs=sdk_state)
|
976 |
-
|
977 |
-
gr.Markdown("## Workflow Status")
|
978 |
-
# Textboxes to display the current workflow state and Space ID
|
979 |
-
status_text = gr.Textbox(label="Current State", value=STATE_IDLE, interactive=False)
|
980 |
-
repo_id_text = gr.Textbox(label="Current Space ID", value="None", interactive=False)
|
981 |
-
|
982 |
-
# --- Prerequisite Status Indicator ---
|
983 |
-
# Markdown to show if prerequisites (HF login, Gemini key) are met
|
984 |
-
prereq_status = gr.Markdown("Checking prerequisites...")
|
985 |
|
|
|
|
|
986 |
|
987 |
-
# Main content area column
|
988 |
-
with gr.Column(scale=3):
|
989 |
-
# Chatbot to display the conversation and workflow messages
|
990 |
-
chatbot = gr.Chatbot(type='messages', label="AI Workflow Chat")
|
991 |
-
# Textbox for user input messages
|
992 |
-
user_input = gr.Textbox(placeholder="Type your message…", interactive=True)
|
993 |
-
# Button to send the user message and trigger the workflow step
|
994 |
-
send_btn = gr.Button("Send", interactive=False) # Starts disabled until prereqs are met
|
995 |
|
996 |
# The main event handler for the Send button
|
997 |
# This .click() event triggers the ai_workflow_chat generator function
|
998 |
-
# Inputs are read from UI components and State variables
|
999 |
-
# Outputs are updated by the values yielded from the generator
|
1000 |
send_btn.click(
|
1001 |
ai_workflow_chat, # The generator function to run
|
|
|
1002 |
inputs=[
|
1003 |
-
user_input, chatbot, # UI inputs (message, current chat history)
|
1004 |
hf_profile, hf_token, # HF State variables
|
1005 |
gemini_key, gemini_model, # Gemini State variables
|
1006 |
repo_id, workflow, sdk_state, # Workflow State variables
|
1007 |
-
|
|
|
|
|
1008 |
debug_attempts, app_description, repo_name_state, generated_code_state, # Other State variables
|
1009 |
use_grounding_state # Add the new grounding state input
|
1010 |
],
|
|
|
|
|
1011 |
outputs=[
|
1012 |
chatbot, # Update Chatbot with new messages
|
1013 |
repo_id, workflow, # Update workflow State variables
|
1014 |
-
iframe, run_txt, build_txt, # Update UI outputs
|
1015 |
debug_attempts, app_description, repo_name_state, generated_code_state, # Update other State variables
|
1016 |
-
use_grounding_state #
|
1017 |
]
|
1018 |
).success( # Chain a .success() event to run *after* the .click() handler completes without error
|
1019 |
# Clear the user input textbox after the message is sent and processed
|
@@ -1022,18 +1042,13 @@ with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
|
1022 |
outputs=user_input # Update the user input textbox
|
1023 |
)
|
1024 |
|
1025 |
-
# Link State variables' changes to UI status displays (reactive updates)
|
1026 |
-
# When the 'workflow' state variable changes, update the text in status_text
|
1027 |
-
workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
|
1028 |
-
# When the 'repo_id' state variable changes, update the text in repo_id_text
|
1029 |
-
repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
|
1030 |
|
1031 |
-
# --- Initial Load Event Chain (Defined INSIDE gr.Blocks) ---
|
1032 |
# This chain runs once when the app loads
|
1033 |
ai_builder_tab.load(
|
1034 |
-
# Action 1: Show profile (loads cached login if available)
|
1035 |
show_profile,
|
1036 |
-
inputs=None,
|
1037 |
outputs=login_status # Update login status markdown
|
1038 |
).then(
|
1039 |
# Action 2: Configure Gemini using the initial state values (from env var if set)
|
|
|
219 |
|
220 |
# Helper function to control send button interactivity and prerequisite status text
|
221 |
# This function is triggered by changes in login status and Gemini configuration
|
222 |
+
# Defined OUTSIDE the gr.Blocks context
|
223 |
def update_send_button_state(
|
224 |
profile: gr.OAuthProfile | None,
|
225 |
token: gr.OAuthToken | None,
|
|
|
257 |
return gr.update(interactive=is_ready), status_str
|
258 |
|
259 |
# Add an initial welcome message to the chatbot (defined outside Blocks to be called by load chain)
|
260 |
+
# Defined OUTSIDE the gr.Blocks context
|
261 |
def greet():
|
262 |
return [{"role": "assistant", "content": "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."}]
|
263 |
|
|
|
275 |
repo_id_state: str | None,
|
276 |
workflow_state: str,
|
277 |
space_sdk: str,
|
278 |
+
# NOTE: UI component values are passed *by value* to the generator
|
279 |
+
preview_html: str, # Value from iframe HTML
|
280 |
+
container_logs: str, # Value from run_txt Textbox
|
281 |
+
build_logs: str, # Value from build_txt Textbox
|
282 |
debug_attempts_state: int,
|
283 |
app_description_state: str | None,
|
284 |
repo_name_state: str | None,
|
285 |
generated_code_state: str | None,
|
286 |
+
use_grounding_state: bool, # Value from use_grounding_checkbox
|
287 |
# Absorb potential extra args passed by Gradio event listeners (e.g. old value, event data)
|
288 |
*args,
|
289 |
**kwargs
|
290 |
) -> tuple[
|
291 |
+
list[dict], # 0: Updated chat history (for chatbot)
|
292 |
+
str | None, # 1: Updated repo_id (for repo_id state)
|
293 |
+
str, # 2: Updated workflow state (for workflow state)
|
294 |
+
str, # 3: Updated iframe HTML (for iframe UI component)
|
295 |
+
str, # 4: Updated container logs (for run_txt UI component)
|
296 |
+
str, # 5: Updated build logs (for build_txt UI component)
|
297 |
+
int, # 6: Updated debug attempts count (for debug_attempts state)
|
298 |
+
str | None, # 7: Updated app description (for app_description state)
|
299 |
+
str | None, # 8: Updated repo name (for repo_name_state state)
|
300 |
+
str | None, # 9: Updated generated code (for generated_code_state state)
|
301 |
+
bool, # 10: Updated use_grounding_state (for use_grounding_state state)
|
302 |
]:
|
303 |
"""
|
304 |
Generator function to handle the AI workflow state machine.
|
|
|
314 |
use_grounding = use_grounding_state # Unpack grounding state
|
315 |
|
316 |
# Keep copies of potentially updated UI elements passed as inputs to update them later
|
317 |
+
# These are the *current values* of the UI components as of the button click
|
318 |
updated_preview = preview_html
|
319 |
updated_build = build_logs
|
320 |
updated_run = container_logs
|
|
|
328 |
|
329 |
# Yield immediately to update the chat UI with the user's message
|
330 |
# This provides immediate feedback to the user while the AI processes
|
331 |
+
# Ensure all state variables and UI outputs are yielded back in the correct order
|
332 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
333 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
334 |
|
|
|
363 |
# Reset the workflow state and associated variables
|
364 |
history = add_bot_message(history, "Workflow reset.")
|
365 |
# Yield updated history and reset state variables to their initial values
|
366 |
+
# Also reset UI outputs to their initial state
|
367 |
yield (history, None, STATE_IDLE, "<p>No Space created yet.</p>", "", "", 0,
|
368 |
None, None, None, False) # Reset use_grounding to default False, and other states to None/default
|
369 |
# No return needed after yield in this generator pattern; execution for this click ends here.
|
|
|
377 |
state = STATE_CREATING_SPACE
|
378 |
repo_name = new_repo_name
|
379 |
app_desc = new_app_desc
|
380 |
+
# Yield updated history and state variables (pass UI outputs through)
|
381 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
382 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
383 |
# No return needed
|
|
|
389 |
# Update state variables for the next step (creation)
|
390 |
state = STATE_CREATING_SPACE
|
391 |
repo_name = new_repo_name
|
392 |
+
# Yield updated history and state variables (pass UI outputs through)
|
393 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
394 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
395 |
# No return needed
|
|
|
398 |
# User wants to create but didn't specify a name yet
|
399 |
history = add_bot_message(history, "Okay, what should the Space be called? (e.g., `my-awesome-app`)")
|
400 |
state = STATE_AWAITING_REPO_NAME # Transition to the state where we wait for the name
|
401 |
+
# Yield updated history and state (pass UI outputs through)
|
402 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
403 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
404 |
# No return needed
|
|
|
406 |
else:
|
407 |
# Command not recognized in IDLE state
|
408 |
history = add_bot_message(history, "Command not recognized. Try 'generate me a gradio app called myapp', or 'reset'.")
|
409 |
+
# Yield updated history and current state (pass UI outputs through)
|
410 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
411 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
412 |
# No return needed
|
|
|
419 |
# Allow letters, numbers, hyphens, underscores, max 100 chars (HF limit check)
|
420 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
421 |
history = add_bot_message(history, "Invalid name. Please provide a single word/slug for the Space name (letters, numbers, underscores, hyphens only, max 100 chars).")
|
422 |
+
# Stay in AWAITING_REPO_NAME state and yield message (pass UI outputs through)
|
423 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
424 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
425 |
# No return needed
|
|
|
428 |
history = add_bot_message(history, f"Using Space name `{new_repo_name}`. Creating Space `{hf_profile.username}/{new_repo_name}`...")
|
429 |
state = STATE_CREATING_SPACE # Transition state to creation
|
430 |
repo_name = new_repo_name # Store the validated repo name
|
431 |
+
# Yield updated history, state, and repo name. UI outputs remain unchanged for now.
|
432 |
# The next click will proceed from the STATE_CREATING_SPACE block.
|
433 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
434 |
attempts, app_desc, repo_name, generated_code, use_grounding)
|
|
|
441 |
# Ensure repo_name is available (it should have been set in the previous step)
|
442 |
if not repo_name:
|
443 |
history = add_bot_message(history, "Internal error: Repo name missing for creation. Resetting.")
|
444 |
+
# Reset relevant states and UI outputs on critical error
|
445 |
yield (history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0,
|
446 |
+
None, None, None, use_grounding) # Pass grounding state through
|
447 |
# No return needed
|
448 |
|
449 |
else:
|
|
|
454 |
repo_id = new_repo_id # Store the official repo_id
|
455 |
history = add_bot_message(history, f"✅ Space `{repo_id}` created. Click 'Send' to generate and upload code.")
|
456 |
state = STATE_GENERATING_CODE # Transition to the next state
|
457 |
+
# Yield updated state variables and history, and the new iframe HTML
|
458 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
459 |
+
attempts, app_desc, repo_name, generated_code, use_grounding) # Pass logs and grounding through
|
460 |
# No return needed
|
461 |
|
462 |
except Exception as e:
|
463 |
history = add_bot_message(history, f"❌ Error creating space: {e}. Click 'reset'.")
|
464 |
# Yield error message and reset state on failure
|
465 |
yield (history, None, STATE_IDLE, "<p>Error creating space.</p>", "", "", 0,
|
466 |
+
None, None, None, use_grounding) # Pass logs and grounding through
|
467 |
# No return needed
|
468 |
|
469 |
|
|
|
483 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
484 |
# Yield to show message before the potentially time-consuming API call
|
485 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
486 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
487 |
|
488 |
# Perform the Gemini API call to generate code, optionally using grounding
|
489 |
code = call_gemini(prompt, gemini_api_key, gemini_model, use_grounding=use_grounding)
|
|
|
502 |
history = add_bot_message(history, "✅ `app.py` code generated. Click 'Send' to upload.")
|
503 |
state = STATE_UPLOADING_APP_PY # Transition to the upload state
|
504 |
generated_code = code # Store the generated code in the state variable for the next step
|
505 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
506 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
507 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
508 |
# No return needed
|
509 |
|
510 |
except Exception as e:
|
511 |
history = add_bot_message(history, f"❌ Error generating code: {e}. Click 'reset'.")
|
512 |
# Yield error message and reset state on failure
|
513 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
514 |
+
None, None, None, use_grounding)
|
515 |
# No return needed
|
516 |
|
517 |
|
|
|
521 |
if not code_to_upload:
|
522 |
history = add_bot_message(history, "Internal error: No code to upload. Resetting.")
|
523 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
524 |
+
None, None, None, use_grounding)
|
525 |
# No return needed
|
526 |
|
527 |
else:
|
528 |
history = add_bot_message(history, "☁️ Uploading `app.py`...")
|
529 |
+
# Yield to show message before the upload action (pass UI outputs and states through)
|
530 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
531 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
532 |
|
533 |
try:
|
534 |
# Perform the file upload action
|
|
|
536 |
history = add_bot_message(history, "✅ Uploaded `app.py`. Click 'Send' to generate requirements.")
|
537 |
state = STATE_GENERATING_REQUIREMENTS # Transition state
|
538 |
generated_code = None # Clear the stored code after use to free memory/state space
|
539 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
540 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
541 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
542 |
# No return needed
|
543 |
|
544 |
except Exception as e:
|
545 |
history = add_bot_message(history, f"❌ Error uploading `app.py`: {e}. Click 'reset'.")
|
546 |
# Yield error message and reset state on failure
|
547 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
548 |
+
None, None, None, use_grounding)
|
549 |
# No return needed
|
550 |
|
551 |
|
552 |
elif state == STATE_GENERATING_REQUIREMENTS:
|
553 |
history = add_bot_message(history, "📄 Generating `requirements.txt`...")
|
554 |
+
# Yield to show message before generating requirements (pass UI outputs and states through)
|
555 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
556 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
557 |
|
558 |
# Logic to determine required packages based on SDK and keywords in the app description
|
559 |
reqs_list = ["gradio"] if space_sdk == "gradio" else ["streamlit"]
|
|
|
587 |
# Sort alphabetically for cleaner requirements.txt
|
588 |
reqs_list.sort()
|
589 |
|
|
|
590 |
reqs_content = "\n".join(reqs_list) + "\n"
|
591 |
|
592 |
history = add_bot_message(history, "✅ `requirements.txt` generated. Click 'Send' to upload.")
|
593 |
state = STATE_UPLOADING_REQUIREMENTS # Transition state
|
594 |
generated_code = reqs_content # Store requirements content
|
595 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
596 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
597 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
598 |
# No return needed
|
599 |
|
600 |
|
|
|
604 |
if not reqs_content_to_upload:
|
605 |
history = add_bot_message(history, "Internal error: No requirements content to upload. Resetting.")
|
606 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
607 |
+
None, None, None, use_grounding)
|
608 |
# No return needed
|
609 |
|
610 |
else:
|
611 |
history = add_bot_message(history, "☁️ Uploading `requirements.txt`...")
|
612 |
+
# Yield message before upload (pass UI outputs and states through)
|
613 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
614 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
615 |
|
616 |
try:
|
617 |
# Perform requirements file upload
|
|
|
619 |
history = add_bot_message(history, "✅ Uploaded `requirements.txt`. Click 'Send' to generate README.")
|
620 |
state = STATE_GENERATING_README # Transition state
|
621 |
generated_code = None # Clear content after use
|
622 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
623 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
624 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
625 |
# No return needed
|
626 |
|
627 |
except Exception as e:
|
628 |
history = add_bot_message(history, f"❌ Error uploading `requirements.txt`: {e}. Click 'reset'.")
|
629 |
# Yield error message and reset state on failure
|
630 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
631 |
+
None, None, None, use_grounding)
|
632 |
# No return needed
|
633 |
|
634 |
elif state == STATE_GENERATING_README:
|
635 |
history = add_bot_message(history, "📝 Generating `README.md`...")
|
636 |
+
# Yield message before generating README (pass UI outputs and states through)
|
637 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
638 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
639 |
|
640 |
# Generate simple README content with Space metadata header
|
641 |
readme_title = repo_name if repo_name else "My Awesome Space"
|
|
|
661 |
history = add_bot_message(history, "✅ `README.md` generated. Click 'Send' to upload.")
|
662 |
state = STATE_UPLOADING_README # Transition state
|
663 |
generated_code = readme_content # Store README content
|
664 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
665 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
666 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
667 |
# No return needed
|
668 |
|
669 |
|
|
|
673 |
if not readme_content_to_upload:
|
674 |
history = add_bot_message(history, "Internal error: No README content to upload. Resetting.")
|
675 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
676 |
+
None, None, None, use_grounding)
|
677 |
# No return needed
|
678 |
|
679 |
else:
|
680 |
history = add_bot_message(history, "☁️ Uploading `README.md`...")
|
681 |
+
# Yield message before upload (pass UI outputs and states through)
|
682 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
683 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
684 |
|
685 |
try:
|
686 |
# Perform README file upload
|
|
|
688 |
history = add_bot_message(history, "✅ Uploaded `README.md`. All files uploaded. Space is now building. Click 'Send' to check build logs.")
|
689 |
state = STATE_CHECKING_LOGS_BUILD # Transition to checking build logs
|
690 |
generated_code = None # Clear content after use
|
691 |
+
# Yield updated state variables and history (pass UI outputs and other states through)
|
692 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
693 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
694 |
# No return needed
|
695 |
|
696 |
except Exception as e:
|
697 |
history = add_bot_message(history, f"❌ Error uploading `README.md`: {e}. Click 'reset'.")
|
698 |
# Yield error message and reset state on failure
|
699 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
700 |
+
None, None, None, use_grounding)
|
701 |
# No return needed
|
702 |
|
703 |
elif state == STATE_CHECKING_LOGS_BUILD:
|
704 |
history = add_bot_message(history, "🔍 Fetching build logs...")
|
705 |
+
# Yield message before fetching logs (which includes a delay) (pass UI outputs and states through)
|
706 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
707 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
708 |
|
709 |
# Fetch build logs from HF Space
|
710 |
build_logs_text = get_build_logs_action(repo_id, hf_profile, hf_token)
|
|
|
716 |
state = STATE_CHECKING_LOGS_RUN # Transition even on build error, to see if container starts
|
717 |
# Yield updated state, logs, and variables
|
718 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
719 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
720 |
# No return needed
|
721 |
|
722 |
else:
|
|
|
724 |
state = STATE_CHECKING_LOGS_RUN # Transition to next log check
|
725 |
# Yield updated state, logs, and variables
|
726 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
727 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
728 |
# No return needed
|
729 |
|
730 |
|
731 |
elif state == STATE_CHECKING_LOGS_RUN:
|
732 |
history = add_bot_message(history, "🔍 Fetching container logs...")
|
733 |
+
# Yield message before fetching logs (includes a delay) (pass UI outputs and states through)
|
734 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
735 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
736 |
|
737 |
# Fetch container logs from HF Space
|
738 |
container_logs_text = get_container_logs_action(repo_id, hf_profile, hf_token)
|
|
|
745 |
state = STATE_DEBUGGING_CODE # Transition to the debugging state
|
746 |
# Yield updated state, logs, attempts, and variables
|
747 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
748 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
749 |
# No return needed
|
750 |
|
751 |
elif ("error" in updated_run.lower() or "exception" in updated_run.lower()) and attempts >= MAX_DEBUG_ATTEMPTS:
|
|
|
754 |
state = STATE_COMPLETE # Workflow ends on failure after attempts
|
755 |
# Yield updated state, logs, attempts, and variables
|
756 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
757 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
758 |
# No return needed
|
759 |
|
760 |
else:
|
|
|
763 |
state = STATE_COMPLETE # Workflow ends on success
|
764 |
# Yield updated state, logs, attempts, and variables
|
765 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
766 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
767 |
# No return needed
|
768 |
|
769 |
|
|
|
771 |
history = add_bot_message(history, f"🧠 Calling Gemini to generate fix based on logs...")
|
772 |
if use_grounding:
|
773 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
774 |
+
# Yield message before Gemini API call (pass UI outputs and states through)
|
775 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
776 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
777 |
|
778 |
# Construct prompt for Gemini including the container logs
|
779 |
debug_prompt = f"""
|
|
|
806 |
history = add_bot_message(history, "✅ Fix code generated. Click 'Send' to upload.")
|
807 |
state = STATE_UPLOADING_FIXED_APP_PY # Transition to the upload state for the fix
|
808 |
generated_code = fix_code # Store the generated fix code
|
809 |
+
# Yield updated state, code, and variables (pass UI outputs and states through)
|
810 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
811 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
812 |
# No return needed
|
813 |
|
814 |
except Exception as e:
|
815 |
history = add_bot_message(history, f"❌ Error generating debug code: {e}. Click 'reset'.")
|
816 |
# Yield error message and reset state on failure
|
817 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
818 |
+
None, None, None, use_grounding)
|
819 |
# No return needed
|
820 |
|
821 |
elif state == STATE_UPLOADING_FIXED_APP_PY:
|
|
|
824 |
if not fixed_code_to_upload:
|
825 |
history = add_bot_message(history, "Internal error: No fixed code available to upload. Resetting.")
|
826 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
827 |
+
None, None, None, use_grounding)
|
828 |
# No return needed
|
829 |
|
830 |
else:
|
831 |
history = add_bot_message(history, "☁️ Uploading fixed `app.py`...")
|
832 |
+
# Yield message before upload (pass UI outputs and states through)
|
833 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
834 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
835 |
|
836 |
try:
|
837 |
# Perform the upload of the fixed app.py
|
|
|
839 |
history = add_bot_message(history, "✅ Fixed `app.py` uploaded. Space will rebuild. Click 'Send' to check logs again.")
|
840 |
state = STATE_CHECKING_LOGS_RUN # Go back to checking run logs after uploading the fix
|
841 |
generated_code = None # Clear code after use
|
842 |
+
# Yield updated state, code, and variables (pass UI outputs and states through)
|
843 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
844 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
845 |
# No return needed
|
846 |
|
847 |
except Exception as e:
|
848 |
history = add_bot_message(history, f"❌ Error uploading fixed `app.py`: {e}. Click 'reset'.")
|
849 |
# Yield error message and reset state on failure
|
850 |
yield (history, None, STATE_IDLE, updated_preview, updated_run, updated_build, 0,
|
851 |
+
None, None, None, use_grounding)
|
852 |
# No return needed
|
853 |
|
854 |
elif state == STATE_COMPLETE:
|
855 |
# If in the complete state, the workflow is finished for this project.
|
856 |
# Subsequent clicks just add user messages; we simply yield the current state.
|
857 |
yield (history, repo_id, state, updated_preview, updated_run, updated_build,
|
858 |
+
attempts, app_desc, repo_name, generated_code, use_grounding)
|
859 |
# No return needed
|
860 |
|
861 |
|
|
|
874 |
|
875 |
with gr.Blocks(title="AI-Powered HF Space App Builder") as ai_builder_tab:
|
876 |
# Gradio State variables - these persist their values across user interactions (clicks)
|
877 |
+
# Define these first as they might be used in default values for components
|
878 |
hf_profile = gr.State(None)
|
879 |
hf_token = gr.State(None)
|
880 |
# FIX: Initialize gemini_key state from env var on load
|
|
|
894 |
# Sidebar column for inputs and status displays
|
895 |
with gr.Column(scale=1, min_width=300):
|
896 |
gr.Markdown("## Hugging Face Login")
|
897 |
+
# Define login_status before it's used in login_btn.click outputs
|
898 |
login_status = gr.Markdown("*Not logged in.*")
|
899 |
# Hugging Face Login Button
|
900 |
login_btn = gr.LoginButton(variant="huggingface")
|
901 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
902 |
gr.Markdown("## Google AI Studio / Gemini")
|
903 |
+
# Define gemini_input and gemini_status before they are used in change handlers
|
904 |
gemini_input = gr.Textbox(
|
905 |
label="API Key",
|
906 |
type="password", # Hides input for security
|
|
|
910 |
)
|
911 |
gemini_status = gr.Markdown("") # Display Gemini configuration status
|
912 |
|
913 |
+
# Define model_selector before it's used in its change handler
|
914 |
model_selector = gr.Radio(
|
915 |
choices=[
|
916 |
("Gemini 1.5 Flash", "gemini-1.5-flash"),
|
|
|
922 |
interactive=True
|
923 |
)
|
924 |
|
925 |
+
# Define grounding checkbox before its change handler
|
926 |
+
use_grounding_checkbox = gr.Checkbox(
|
927 |
+
label="Enable Grounding with Google Search",
|
928 |
+
value=False, # Default to off
|
929 |
+
interactive=True,
|
930 |
+
info="Use Google Search results to inform Gemini's response (may improve factuality)."
|
931 |
+
)
|
932 |
+
|
933 |
+
gr.Markdown("## Space SDK")
|
934 |
+
# Define sdk_selector before its change handler
|
935 |
+
sdk_selector = gr.Radio(choices=["gradio","streamlit"], value="gradio", label="Template SDK", interactive=True)
|
936 |
+
|
937 |
+
gr.Markdown("## Workflow Status")
|
938 |
+
# Define status_text and repo_id_text before they are used in change handlers
|
939 |
+
status_text = gr.Textbox(label="Current State", value=STATE_IDLE, interactive=False)
|
940 |
+
repo_id_text = gr.Textbox(label="Current Space ID", value="None", interactive=False)
|
941 |
+
|
942 |
+
# Define prereq_status before it's used in handlers that update it
|
943 |
+
prereq_status = gr.Markdown("Checking prerequisites...")
|
944 |
+
|
945 |
+
|
946 |
+
# Main content area column
|
947 |
+
with gr.Column(scale=3):
|
948 |
+
# Define chatbot, user_input, send_btn before send_btn.click
|
949 |
+
chatbot = gr.Chatbot(type='messages', label="AI Workflow Chat")
|
950 |
+
user_input = gr.Textbox(placeholder="Type your message…", interactive=True)
|
951 |
+
# Define send_btn before its click handler
|
952 |
+
send_btn = gr.Button("Send", interactive=False) # Starts disabled until prereqs are met
|
953 |
+
|
954 |
+
# Define iframe, build_txt, run_txt before they are used in send_btn.click inputs/outputs
|
955 |
+
# These are UI components, NOT State variables
|
956 |
+
iframe = gr.HTML("<p>No Space created yet.</p>") # HTML element for the Space iframe
|
957 |
+
build_txt = gr.Textbox(label="Build Logs", lines=10, interactive=False, value="", max_lines=20) # Set max_lines for scrollability
|
958 |
+
run_txt = gr.Textbox(label="Container Logs", lines=10, interactive=False, value="", max_lines=20) # Set max_lines for scrollability
|
959 |
+
|
960 |
+
|
961 |
+
# --- Define Event Handlers and Chains AFTER all components are defined ---
|
962 |
+
|
963 |
+
# Handle login button click
|
964 |
+
login_btn.click(
|
965 |
+
# The LoginButton outputs a tuple (OAuthProfile, OAuthToken) on success
|
966 |
+
lambda x: (x[0], x[1]),
|
967 |
+
inputs=[login_btn],
|
968 |
+
outputs=[hf_profile, hf_token] # Update these State variables
|
969 |
+
).then( # Chain the next action after state is updated
|
970 |
+
# Pass all relevant state variables to re-evaluate prerequisites
|
971 |
+
update_send_button_state,
|
972 |
+
inputs=[hf_profile, hf_token, gemini_key, gemini_model],
|
973 |
+
outputs=[send_btn, prereq_status] # Update button interactivity and status text
|
974 |
+
)
|
975 |
+
|
976 |
+
# Handle Gemini Key Input change: update key state -> configure Gemini -> update send button
|
977 |
gemini_input.change(
|
978 |
lambda k: k, inputs=[gemini_input], outputs=[gemini_key] # Update gemini_key state
|
979 |
).then(
|
|
|
983 |
update_send_button_state, inputs=[hf_profile, hf_token, gemini_key, gemini_model], outputs=[send_btn, prereq_status] # Update button/prereq status
|
984 |
)
|
985 |
|
986 |
+
# Handle Gemini Model Selector change: update model state -> configure Gemini -> update send button
|
|
|
987 |
model_selector.change(
|
988 |
lambda m: m, inputs=[model_selector], outputs=[gemini_model] # Update gemini_model state
|
989 |
).then(
|
|
|
993 |
update_send_button_state, inputs=[hf_profile, hf_token, gemini_key, gemini_model], outputs=[send_btn, prereq_status] # Update button/prereq status
|
994 |
)
|
995 |
|
996 |
+
# Handle Grounding checkbox change: update grounding state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
997 |
use_grounding_checkbox.change(
|
998 |
lambda v: v, inputs=use_grounding_checkbox, outputs=use_grounding_state
|
999 |
)
|
1000 |
|
1001 |
+
# Handle SDK selector change: update sdk state
|
1002 |
+
sdk_selector.change(
|
1003 |
+
lambda s: s, inputs=sdk_selector, outputs=sdk_state
|
1004 |
+
)
|
1005 |
|
1006 |
+
# Link Workflow State variable change to UI status display
|
1007 |
+
workflow.change(lambda s: s, inputs=workflow, outputs=status_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1008 |
|
1009 |
+
# Link Repo ID State variable change to UI status display
|
1010 |
+
repo_id.change(lambda r: r if r else "None", inputs=repo_id, outputs=repo_id_text)
|
1011 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1012 |
|
1013 |
# The main event handler for the Send button
|
1014 |
# This .click() event triggers the ai_workflow_chat generator function
|
|
|
|
|
1015 |
send_btn.click(
|
1016 |
ai_workflow_chat, # The generator function to run
|
1017 |
+
# Inputs are read from UI components AND State variables
|
1018 |
inputs=[
|
1019 |
+
user_input, chatbot, # UI component inputs (message, current chat history)
|
1020 |
hf_profile, hf_token, # HF State variables
|
1021 |
gemini_key, gemini_model, # Gemini State variables
|
1022 |
repo_id, workflow, sdk_state, # Workflow State variables
|
1023 |
+
# UI component inputs whose *current values* are needed by the generator
|
1024 |
+
# These are NOT State variables with the same names
|
1025 |
+
iframe, run_txt, build_txt,
|
1026 |
debug_attempts, app_description, repo_name_state, generated_code_state, # Other State variables
|
1027 |
use_grounding_state # Add the new grounding state input
|
1028 |
],
|
1029 |
+
# Outputs update UI components AND State variables.
|
1030 |
+
# The order MUST match the tuple yielded by the generator function.
|
1031 |
outputs=[
|
1032 |
chatbot, # Update Chatbot with new messages
|
1033 |
repo_id, workflow, # Update workflow State variables
|
1034 |
+
iframe, run_txt, build_txt, # Update UI component outputs
|
1035 |
debug_attempts, app_description, repo_name_state, generated_code_state, # Update other State variables
|
1036 |
+
use_grounding_state # Update the grounding state output (generators must yield/return all state they modify/pass through)
|
1037 |
]
|
1038 |
).success( # Chain a .success() event to run *after* the .click() handler completes without error
|
1039 |
# Clear the user input textbox after the message is sent and processed
|
|
|
1042 |
outputs=user_input # Update the user input textbox
|
1043 |
)
|
1044 |
|
|
|
|
|
|
|
|
|
|
|
1045 |
|
1046 |
+
# --- Initial Load Event Chain (Defined INSIDE gr.Blocks, AFTER components) ---
|
1047 |
# This chain runs once when the app loads
|
1048 |
ai_builder_tab.load(
|
1049 |
+
# Action 1: Show profile (loads cached login if available), does NOT need inputs
|
1050 |
show_profile,
|
1051 |
+
inputs=None,
|
1052 |
outputs=login_status # Update login status markdown
|
1053 |
).then(
|
1054 |
# Action 2: Configure Gemini using the initial state values (from env var if set)
|