wuhp commited on
Commit
d4653f4
·
verified ·
1 Parent(s): 934dfd0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -146
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
- # Moved OUTSIDE the gr.Blocks context
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
- # Moved 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,29 +275,30 @@ def ai_workflow_chat(
275
  repo_id_state: str | None,
276
  workflow_state: str,
277
  space_sdk: str,
278
- preview_html: str, # Passed in to maintain its value in State
279
- container_logs: str, # Passed in to maintain its value in State
280
- build_logs: str, # Passed in to maintain its value in State
 
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, # New input for grounding state
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 temporary storage)
300
- bool, # 10: Updated use_grounding_state (must return all state vars passed in)
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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include use_grounding
605
  # No return needed
606
 
607
  else:
608
  history = add_bot_message(history, "☁️ Uploading `requirements.txt`...")
609
- # Yield to show message before upload
610
  yield (history, repo_id, state, updated_preview, updated_run, updated_build,
611
- attempts, app_desc, repo_name, generated_code, use_grounding) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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) # Include 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
- # Textbox for Gemini API key. Read from environment variable if available.
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
- # Radio buttons to select the Gemini model
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
- # Gemini Key Input change: update key state -> configure Gemini -> update send button
936
- # This uses gemini_input value as input to update gemini_key state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # New checkbox for optional grounding
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
- gr.Markdown("## Space SDK")
972
- # Radio buttons to select the Space SDK (Gradio or Streamlit)
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
- iframe, run_txt, build_txt, # UI outputs whose current values are needed by the generator
 
 
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 # Add the new grounding state output (generators must yield/return all state they modify/pass through)
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, # This trigger doesn't need inputs from components
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)