Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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 |
-
|
|
|
|
|
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 |
-
|
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', '')) >
|
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 |
-
|
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 #
|
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 |
-
|
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 |
-
|
|
|
|
|
|
|
712 |
# Return current state, logs etc. + the new history
|
713 |
-
return
|
714 |
|
715 |
if not gemini_api_key:
|
716 |
-
|
717 |
-
|
|
|
|
|
718 |
|
719 |
if not user_input or user_input.strip() == "":
|
720 |
# Handle empty prompt case - the prompt is now the user_input parameter
|
721 |
-
|
722 |
-
|
|
|
|
|
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")
|
793 |
|
794 |
# When the user actually logs in:
|
|
|
795 |
login_btn.click(
|
796 |
fn=show_profile,
|
797 |
-
inputs=None,
|
798 |
outputs=status_md,
|
799 |
api_name="login_profile"
|
800 |
)
|
801 |
login_btn.click(
|
802 |
fn=list_private_models,
|
803 |
-
inputs=None,
|
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
|
|
|
|
|
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 -
|
|
|
836 |
send_btn.click(
|
837 |
fn=handle_user_message,
|
838 |
inputs=[
|
839 |
-
chatbot, # history
|
840 |
-
user_in, #
|
841 |
sdk_choice,
|
842 |
api_key,
|
843 |
grounding,
|
844 |
temp,
|
845 |
max_tokens,
|
846 |
-
login_btn,
|
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 -
|
853 |
user_in.submit(
|
854 |
fn=handle_user_message,
|
855 |
inputs=[
|
856 |
-
chatbot,
|
857 |
-
user_in,
|
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 |
-
#
|
|
|
871 |
refresh_btn.click(
|
872 |
-
fn=lambda profile, token
|
873 |
-
fetch_logs(f"{
|
874 |
-
fetch_logs(f"{
|
875 |
),
|
876 |
-
inputs=[login_btn
|
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 |
|