wuhp commited on
Commit
0e373cb
·
verified ·
1 Parent(s): a8fc748

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -40
app.py CHANGED
@@ -21,19 +21,18 @@ def list_private_models(
21
  profile: gr.OAuthProfile | None,
22
  oauth_token: gr.OAuthToken | None
23
  ) -> str:
24
- if not profile or not oauth_token:
 
 
25
  return "Please log in to see your models."
26
  try:
27
- # Check if the token is valid before listing models
28
- if not hasattr(oauth_token, 'token') or not oauth_token.token:
29
- return "Invalid or missing access token."
30
-
31
  models = [
32
  f"{m.id} ({'private' if m.private else 'public'})"
33
  for m in list_models(author=profile.username, token=oauth_token.token)
34
  ]
35
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
36
  except Exception as e:
 
37
  return f"Error listing models: {e}"
38
 
39
 
@@ -427,7 +426,7 @@ def orchestrate_development(client, project_state, config, oauth_token_token):
427
  if project_state['sdk_choice'] == 'gradio':
428
  project_state['files'][project_state['main_app_file']] += "import gradio as gr\n\n# Define a simple interface\n# For example: gr.Interface(...).launch()\n"
429
  elif project_state['sdk_choice'] == 'streamlit':
430
- project_state['files'][project_state['main_app_file']] += "import streamlit as st\n\n# Your Streamlit app starts here\n# For example: st.write('Hello, world!')\n"
431
 
432
  if 'requirements.txt' not in project_state['files']:
433
  req_content = "pandas\n" + ("streamlit\n" if project_state['sdk_choice']=="streamlit" else "gradio\n") + "google-generativeai\nhuggingface-hub\n"
@@ -524,7 +523,7 @@ This is an auto-generated HF Space.
524
 
525
  # Only check iframe once logs indicate something might be running, or after a delay
526
  # Check iframe more frequently after initial wait
527
- if elapsed_log_wait > 10 or len(run_logs) > 0:
528
  project_state['iframe_ok'] = check_iframe(project_state['iframe_url'])
529
  iframe_checked = True
530
  else:
@@ -582,15 +581,24 @@ This is an auto-generated HF Space.
582
  # Analyze feedback to decide next step
583
  feedback = project_state['feedback']
584
  iframe_ok = project_state.get('iframe_ok', False)
 
 
 
 
 
 
 
 
585
  error_types = classify_errors(project_state['logs'].get('build', '') + '\n' + project_state['logs'].get('run', ''))
586
 
 
587
  print(f"Debug Analysis - Feedback: {feedback[:100]}... | Iframe OK: {iframe_ok} | Errors: {error_types}")
588
 
589
 
590
  # Decision Logic:
591
- # 1. Success? Debugger says clear AND iframe works AND no/minor errors in logs
592
  is_complete = ("All clear. Project appears complete." in feedback) or \
593
- (iframe_ok and error_types == "none" and "ERROR" not in feedback.upper() and len(project_state['logs'].get('run', '')) > 0) # Appears to run and no errors
594
 
595
  if is_complete:
596
  project_state['status'] = 'Complete'
@@ -618,7 +626,7 @@ This is an auto-generated HF Space.
618
 
619
  elif current_task == 'FINISHED':
620
  # Exit the main loop
621
- break # Loop condition handles exit
622
 
623
  else:
624
  # Unknown task
@@ -691,8 +699,8 @@ def handle_user_message(
691
  grounding_enabled: bool,
692
  temperature: float,
693
  max_output_tokens: int,
694
- profile: gr.OAuthProfile | None,
695
- oauth_token: gr.OAuthToken | None # We need the token object
696
  ):
697
  # The user_input is already the new prompt.
698
  # We need to add it to the history list here at the beginning,
@@ -700,26 +708,33 @@ def handle_user_message(
700
  # Check if the last message is *not* a user message or is empty to avoid duplicates
701
  # (Gradio's default Chatbot adds the user message automatically on submit,
702
  # but explicit handling here is safer if using user_in separately)
 
 
703
  if not history or history[-1].get("role") != "user" or history[-1].get("content") != user_input:
704
  history.append({"role": "user", "content": user_input})
705
- # Note: If Gradio Chatbot already adds the message, this might cause duplicates.
706
- # In typical chatbot+textbox setup, the textbox value is passed explicitly,
707
- # and the handler is responsible for appending it to the history.
708
 
709
  if not profile or not oauth_token or not oauth_token.token:
710
  # Append error message to history for display
711
- error_history = history + [{"role":"assistant","content":"⚠️ Please log in first via the Hugging Face button."}]
 
 
 
712
  # Return current state, logs etc. + the new history
713
- return error_history, "", "", "<p>Please log in.</p>", "Login required."
714
 
715
  if not gemini_api_key:
716
- error_history = history + [{"role":"assistant","content":"⚠️ Please provide your Gemini API Key."}]
717
- return error_history, "", "", "<p>Please provide API Key.</p>", "API Key required."
 
 
718
 
719
  if not user_input or user_input.strip() == "":
720
  # Handle empty prompt case - the prompt is now the user_input parameter
721
- empty_prompt_history = history + [{"role":"assistant","content":"Please enter requirements for the application."}]
722
- return empty_prompt_history, "", "", "<p>Enter requirements.</p>", "Waiting for prompt."
 
 
723
 
724
 
725
  client = genai.Client(api_key=gemini_api_key)
@@ -788,19 +803,21 @@ with gr.Blocks(title="HF Space Auto‑Builder (Team AI)") as demo:
788
  models_md = gr.Markdown()
789
 
790
  # On app load, show “not logged in” and list public models (or none)
 
791
  demo.load(show_profile, inputs=None, outputs=status_md, api_name="load_profile")
792
- demo.load(list_private_models, inputs=None, outputs=models_md, api_name="load_models") # Inputs=None allows Gradio to inject profile/token (will be None initially)
793
 
794
  # When the user actually logs in:
 
795
  login_btn.click(
796
  fn=show_profile,
797
- inputs=None, # Gradio will inject OAuthProfile
798
  outputs=status_md,
799
  api_name="login_profile"
800
  )
801
  login_btn.click(
802
  fn=list_private_models,
803
- inputs=None, # Gradio will inject (OAuthProfile, OAuthToken)
804
  outputs=models_md,
805
  api_name="login_models"
806
  )
@@ -825,55 +842,57 @@ with gr.Blocks(title="HF Space Auto‑Builder (Team AI)") as demo:
825
  with gr.Accordion("Logs", open=False):
826
  build_box = gr.Textbox(label="Build logs", lines=10, interactive=False, max_lines=20)
827
  run_box = gr.Textbox(label="Run logs", lines=10, interactive=False, max_lines=20)
828
- # Need login state for refresh button, inputs=None will inject profile/token
 
 
829
  refresh_btn = gr.Button("🔄 Refresh Logs Only")
830
 
831
  with gr.Accordion("App Preview", open=True):
832
  preview = gr.HTML("<p>App preview will load here when available.</p>")
833
 
834
 
835
- # Update the button click handler - ADD user_in to inputs
 
836
  send_btn.click(
837
  fn=handle_user_message,
838
  inputs=[
839
- chatbot, # history (passed automatically by chatbot)
840
- user_in, # <-- user_input (pass the textbox component)
841
  sdk_choice,
842
  api_key,
843
  grounding,
844
  temp,
845
  max_tokens,
846
- login_btn, # profile (passed automatically by LoginButton)
847
- login_btn # oauth_token (passed automatically by LoginButton)
848
  ],
849
  outputs=[chatbot, build_box, run_box, preview, project_status_md]
850
  )
851
 
852
- # Update the submit handler - ADD user_in to inputs
853
  user_in.submit(
854
  fn=handle_user_message,
855
  inputs=[
856
- chatbot, # history
857
- user_in, # <-- user_input
858
  sdk_choice,
859
  api_key,
860
  grounding,
861
  temp,
862
  max_tokens,
863
- login_btn,
864
- login_btn
865
  ],
866
  outputs=[chatbot, build_box, run_box, preview, project_status_md]
867
  )
868
 
869
  # Handler for refreshing logs manually
870
- # Inputs=None allows Gradio to inject profile/token from login_btn
 
871
  refresh_btn.click(
872
- fn=lambda profile, token: (
873
- fetch_logs(f"{profile.username}/{profile.username}-auto-space", "build", token.token) if profile and token and token.token else "Login required to fetch logs.",
874
- fetch_logs(f"{profile.username}/{profile.username}-auto-space", "run", token.token) if profile and token and token.token else "Login required to fetch logs."
875
  ),
876
- inputs=[login_btn, login_btn], # Keep login_btn twice for profile and token objects
877
  outputs=[build_box, run_box]
878
  )
879
 
 
21
  profile: gr.OAuthProfile | None,
22
  oauth_token: gr.OAuthToken | None
23
  ) -> str:
24
+ # Gradio injects profile and oauth_token automatically when inputs=None
25
+ # and the function signature has these parameter types.
26
+ if not profile or not oauth_token or not hasattr(oauth_token, 'token') or not oauth_token.token:
27
  return "Please log in to see your models."
28
  try:
 
 
 
 
29
  models = [
30
  f"{m.id} ({'private' if m.private else 'public'})"
31
  for m in list_models(author=profile.username, token=oauth_token.token)
32
  ]
33
  return "No models found." if not models else "Models:\n\n" + "\n - ".join(models)
34
  except Exception as e:
35
+ # Catching potential API errors during model listing
36
  return f"Error listing models: {e}"
37
 
38
 
 
426
  if project_state['sdk_choice'] == 'gradio':
427
  project_state['files'][project_state['main_app_file']] += "import gradio as gr\n\n# Define a simple interface\n# For example: gr.Interface(...).launch()\n"
428
  elif project_state['sdk_choice'] == 'streamlit':
429
+ project_state['files'][project_state['main_app_file']] += "import streamlit as st\n\n# Your Streamlit app starts here\n# For example: st.write('Hello, world!')\n"
430
 
431
  if 'requirements.txt' not in project_state['files']:
432
  req_content = "pandas\n" + ("streamlit\n" if project_state['sdk_choice']=="streamlit" else "gradio\n") + "google-generativeai\nhuggingface-hub\n"
 
523
 
524
  # Only check iframe once logs indicate something might be running, or after a delay
525
  # Check iframe more frequently after initial wait
526
+ if elapsed_log_wait > 10 or len(run_logs) > 0 or len(build_logs) > 100: # Also check if build logs are substantial
527
  project_state['iframe_ok'] = check_iframe(project_state['iframe_url'])
528
  iframe_checked = True
529
  else:
 
581
  # Analyze feedback to decide next step
582
  feedback = project_state['feedback']
583
  iframe_ok = project_state.get('iframe_ok', False)
584
+ # Re-check logs just before making the decision for freshest info if possible
585
+ # This might add latency, skipping for now, rely on logs fetched in LOGGING step
586
+ # build_logs = fetch_logs(project_state['repo_id'], "build", oauth_token_token)
587
+ # run_logs = fetch_logs(project_state['repo_id'], "run", oauth_token_token)
588
+ # error_types = classify_errors(build_logs + '\n' + run_logs)
589
+ # project_state['logs']['build'] = build_logs # Update state with freshest logs
590
+ # project_state['logs']['run'] = run_logs
591
+
592
  error_types = classify_errors(project_state['logs'].get('build', '') + '\n' + project_state['logs'].get('run', ''))
593
 
594
+
595
  print(f"Debug Analysis - Feedback: {feedback[:100]}... | Iframe OK: {iframe_ok} | Errors: {error_types}")
596
 
597
 
598
  # Decision Logic:
599
+ # 1. Success? Debugger says clear AND iframe works AND no/minor errors in logs AND run logs have some content
600
  is_complete = ("All clear. Project appears complete." in feedback) or \
601
+ (iframe_ok and error_types == "none" and "ERROR" not in feedback.upper() and len(project_state['logs'].get('run', '')) > 10) # Appears to run and no errors
602
 
603
  if is_complete:
604
  project_state['status'] = 'Complete'
 
626
 
627
  elif current_task == 'FINISHED':
628
  # Exit the main loop
629
+ pass # Loop condition handles exit
630
 
631
  else:
632
  # Unknown task
 
699
  grounding_enabled: bool,
700
  temperature: float,
701
  max_output_tokens: int,
702
+ profile: gr.OAuthProfile | None, # Gradio auto-injects
703
+ oauth_token: gr.OAuthToken | None # Gradio auto-injects
704
  ):
705
  # The user_input is already the new prompt.
706
  # We need to add it to the history list here at the beginning,
 
708
  # Check if the last message is *not* a user message or is empty to avoid duplicates
709
  # (Gradio's default Chatbot adds the user message automatically on submit,
710
  # but explicit handling here is safer if using user_in separately)
711
+ # A safer check: If the last message is the exact user_input, assume Gradio added it.
712
+ # Otherwise, add it.
713
  if not history or history[-1].get("role") != "user" or history[-1].get("content") != user_input:
714
  history.append({"role": "user", "content": user_input})
715
+
 
 
716
 
717
  if not profile or not oauth_token or not oauth_token.token:
718
  # Append error message to history for display
719
+ # Check if error message is already present to avoid spamming
720
+ error_msg = "⚠️ Please log in first via the Hugging Face button."
721
+ if not history or history[-1].get("content") != error_msg:
722
+ history.append({"role":"assistant","content":error_msg})
723
  # Return current state, logs etc. + the new history
724
+ return history, "", "", "<p>Please log in.</p>", "Login required."
725
 
726
  if not gemini_api_key:
727
+ error_msg = "⚠️ Please provide your Gemini API Key."
728
+ if not history or history[-1].get("content") != error_msg:
729
+ history.append({"role":"assistant","content":error_msg})
730
+ return history, "", "", "<p>Please provide API Key.</p>", "API Key required."
731
 
732
  if not user_input or user_input.strip() == "":
733
  # Handle empty prompt case - the prompt is now the user_input parameter
734
+ error_msg = "Please enter requirements for the application."
735
+ if not history or history[-1].get("content") != error_msg:
736
+ history.append({"role":"assistant","content":error_msg})
737
+ return history, "", "", "<p>Enter requirements.</p>", "Waiting for prompt."
738
 
739
 
740
  client = genai.Client(api_key=gemini_api_key)
 
803
  models_md = gr.Markdown()
804
 
805
  # On app load, show “not logged in” and list public models (or none)
806
+ # inputs=None tells Gradio to auto-inject LoginButton state if signature matches
807
  demo.load(show_profile, inputs=None, outputs=status_md, api_name="load_profile")
808
+ demo.load(list_private_models, inputs=None, outputs=models_md, api_name="load_models")
809
 
810
  # When the user actually logs in:
811
+ # inputs=None tells Gradio to auto-inject LoginButton state
812
  login_btn.click(
813
  fn=show_profile,
814
+ inputs=None,
815
  outputs=status_md,
816
  api_name="login_profile"
817
  )
818
  login_btn.click(
819
  fn=list_private_models,
820
+ inputs=None,
821
  outputs=models_md,
822
  api_name="login_models"
823
  )
 
842
  with gr.Accordion("Logs", open=False):
843
  build_box = gr.Textbox(label="Build logs", lines=10, interactive=False, max_lines=20)
844
  run_box = gr.Textbox(label="Run logs", lines=10, interactive=False, max_lines=20)
845
+ # Need login state for refresh button. For a lambda function,
846
+ # auto-injection doesn't work based on type hints in the same way,
847
+ # so we explicitly pass the component state.
848
  refresh_btn = gr.Button("🔄 Refresh Logs Only")
849
 
850
  with gr.Accordion("App Preview", open=True):
851
  preview = gr.HTML("<p>App preview will load here when available.</p>")
852
 
853
 
854
+ # Update the button click handler - REMOVE login_btn from inputs
855
+ # Gradio will auto-inject profile and token based on the function signature
856
  send_btn.click(
857
  fn=handle_user_message,
858
  inputs=[
859
+ chatbot, # history
860
+ user_in, # user_input
861
  sdk_choice,
862
  api_key,
863
  grounding,
864
  temp,
865
  max_tokens,
866
+ # REMOVED: login_btn, login_btn - Gradio injects profile/token based on signature
 
867
  ],
868
  outputs=[chatbot, build_box, run_box, preview, project_status_md]
869
  )
870
 
871
+ # Update the submit handler - REMOVE login_btn from inputs
872
  user_in.submit(
873
  fn=handle_user_message,
874
  inputs=[
875
+ chatbot,
876
+ user_in,
877
  sdk_choice,
878
  api_key,
879
  grounding,
880
  temp,
881
  max_tokens,
882
+ # REMOVED: login_btn, login_btn - Gradio injects profile/token based on signature
 
883
  ],
884
  outputs=[chatbot, build_box, run_box, preview, project_status_md]
885
  )
886
 
887
  # Handler for refreshing logs manually
888
+ # For this lambda, we explicitly pass login_btn state as it's not a
889
+ # function with OAuth type hints for auto-injection.
890
  refresh_btn.click(
891
+ fn=lambda profile_token_state: ( # Receive the tuple (profile, token) from the login_btn state
892
+ fetch_logs(f"{profile_token_state[0].username}/{profile_token_state[0].username}-auto-space", "build", profile_token_state[1].token) if profile_token_state and profile_token_state[0] and profile_token_state[1] and profile_token_state[1].token else "Login required to fetch logs.",
893
+ fetch_logs(f"{profile_token_state[0].username}/{profile_token_state[0].username}-auto-space", "run", profile_token_state[1].token) if profile_token_state and profile_token_state[0] and profile_token_state[1] and profile_token_state[1].token else "Login required to fetch logs."
894
  ),
895
+ inputs=[login_btn], # Pass the login_btn component state
896
  outputs=[build_box, run_box]
897
  )
898