Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -5,16 +5,7 @@ import json
|
|
5 |
import io
|
6 |
import requests
|
7 |
import logging
|
8 |
-
from typing import List, Dict, Any, Tuple, Optional, Literal, Generator
|
9 |
-
|
10 |
-
import gradio as gr
|
11 |
-
import google.generativeai as genai
|
12 |
-
from google.generativeai import types # Import types for configuration and tools
|
13 |
-
|
14 |
-
from huggingface_hub import create_repo, list_models, upload_file, constants
|
15 |
-
from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status
|
16 |
-
from requests.adapters import HTTPAdapter
|
17 |
-
from urllib3.util.retry import Retry
|
18 |
|
19 |
|
20 |
# --- Configure Logging ---
|
@@ -297,8 +288,29 @@ STATE_COMPLETE: WorkflowState = "complete"
|
|
297 |
|
298 |
MAX_DEBUG_ATTEMPTS = 3 # Limit the number of automatic debug attempts
|
299 |
|
300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
301 |
def add_bot_message(history: list[dict], bot_message: str) -> list[dict]:
|
|
|
302 |
# Make a copy to avoid modifying history in place if needed later, though generator pattern usually handles this
|
303 |
new_history = list(history)
|
304 |
new_history.append({"role": "assistant", "content": bot_message})
|
@@ -342,12 +354,6 @@ def check_send_button_ready(
|
|
342 |
# They take all necessary inputs from the main generator's arguments
|
343 |
# and return the full tuple of outputs required by the generator's yield signature.
|
344 |
|
345 |
-
# Using Any for handler return type simplifies type hints since some yield and some return
|
346 |
-
# A more precise type would be Union[WorkflowOutputs, Generator[WorkflowOutputs, None, WorkflowOutputs]]
|
347 |
-
# But Gradio's type checking for generators is often loose anyway.
|
348 |
-
WorkflowHandlerReturn = Any
|
349 |
-
|
350 |
-
|
351 |
def package_workflow_outputs(
|
352 |
history: List[Dict[str, str]],
|
353 |
repo_id: Optional[str],
|
@@ -369,6 +375,7 @@ def package_workflow_outputs(
|
|
369 |
current_gemini_key, current_gemini_model)
|
370 |
|
371 |
|
|
|
372 |
def handle_idle(
|
373 |
message: str,
|
374 |
history: List[Dict[str, str]],
|
@@ -389,7 +396,7 @@ def handle_idle(
|
|
389 |
use_grounding: bool,
|
390 |
*args, # Catch potential extra args
|
391 |
**kwargs # Catch potential extra kwargs
|
392 |
-
) ->
|
393 |
"""Handles logic when in the IDLE state."""
|
394 |
logging.info(f"Handling STATE_IDLE with message: {message[:50]}...")
|
395 |
|
@@ -419,7 +426,7 @@ def handle_idle(
|
|
419 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
420 |
logging.warning(f"Invalid repo name format received: {new_repo_name}")
|
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 IDLE and
|
423 |
return package_workflow_outputs(
|
424 |
history=history, repo_id=repo_id, state=STATE_IDLE,
|
425 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
@@ -444,7 +451,7 @@ def handle_idle(
|
|
444 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
445 |
logging.warning(f"Invalid repo name format received: {new_repo_name}")
|
446 |
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).")
|
447 |
-
# Stay in IDLE and
|
448 |
return package_workflow_outputs(
|
449 |
history=history, repo_id=repo_id, state=STATE_IDLE,
|
450 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
@@ -504,7 +511,7 @@ def handle_awaiting_repo_name(
|
|
504 |
use_grounding: bool,
|
505 |
*args,
|
506 |
**kwargs
|
507 |
-
) ->
|
508 |
"""Handles logic when in the AWAITING_REPO_NAME state."""
|
509 |
logging.info(f"Handling STATE_AWAITING_REPO_NAME with message: {message[:50]}...")
|
510 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -555,7 +562,7 @@ def handle_creating_space(
|
|
555 |
use_grounding: bool,
|
556 |
*args,
|
557 |
**kwargs
|
558 |
-
) ->
|
559 |
"""Handles logic when in the CREATING_SPACE state."""
|
560 |
logging.info(f"Handling STATE_CREATING_SPACE for repo '{repo_name}'")
|
561 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -595,6 +602,7 @@ def handle_creating_space(
|
|
595 |
use_grounding=use_grounding, current_gemini_key=current_gemini_key, current_gemini_model=current_gemini_model
|
596 |
)
|
597 |
|
|
|
598 |
def handle_generating_code(
|
599 |
message: str,
|
600 |
history: List[Dict[str, str]],
|
@@ -615,7 +623,7 @@ def handle_generating_code(
|
|
615 |
use_grounding: bool,
|
616 |
*args,
|
617 |
**kwargs
|
618 |
-
) ->
|
619 |
"""Handles logic when in the GENERATING_CODE state."""
|
620 |
logging.info("Handling STATE_GENERATING_CODE")
|
621 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -635,7 +643,6 @@ Return **only** the python code block for `app.py`. Do not include any extra tex
|
|
635 |
if use_grounding:
|
636 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
637 |
# Yield message before API call to show immediate feedback
|
638 |
-
# Use package_workflow_outputs to construct the tuple
|
639 |
yield package_workflow_outputs(
|
640 |
history=history, repo_id=repo_id, state=state,
|
641 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
@@ -697,7 +704,7 @@ def handle_uploading_app_py(
|
|
697 |
use_grounding: bool,
|
698 |
*args,
|
699 |
**kwargs
|
700 |
-
) ->
|
701 |
"""Handles logic when in the UPLOADING_APP_PY state."""
|
702 |
logging.info("Handling STATE_UPLOADING_APP_PY")
|
703 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -766,7 +773,7 @@ def handle_generating_requirements(
|
|
766 |
use_grounding: bool,
|
767 |
*args,
|
768 |
**kwargs
|
769 |
-
) ->
|
770 |
"""Handles logic when in the GENERATING_REQUIREMENTS state."""
|
771 |
logging.info("Handling STATE_GENERATING_REQUIREMENTS")
|
772 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -841,7 +848,7 @@ def handle_uploading_requirements(
|
|
841 |
use_grounding: bool,
|
842 |
*args,
|
843 |
**kwargs
|
844 |
-
) ->
|
845 |
"""Handles logic when in the UPLOADING_REQUIREMENTS state."""
|
846 |
logging.info("Handling STATE_UPLOADING_REQUIREMENTS")
|
847 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -910,7 +917,7 @@ def handle_generating_readme(
|
|
910 |
use_grounding: bool,
|
911 |
*args,
|
912 |
**kwargs
|
913 |
-
) ->
|
914 |
"""Handles logic when in the GENERATING_README state."""
|
915 |
logging.info("Handling STATE_GENERATING_README")
|
916 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -976,7 +983,7 @@ def handle_uploading_readme(
|
|
976 |
use_grounding: bool,
|
977 |
*args,
|
978 |
**kwargs
|
979 |
-
) ->
|
980 |
"""Handles logic when in the UPLOADING_README state."""
|
981 |
logging.info("Handling STATE_UPLOADING_README")
|
982 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1045,7 +1052,7 @@ def handle_checking_logs_build(
|
|
1045 |
use_grounding: bool,
|
1046 |
*args,
|
1047 |
**kwargs
|
1048 |
-
) ->
|
1049 |
"""Handles logic when in the CHECKING_LOGS_BUILD state."""
|
1050 |
logging.info(f"Handling STATE_CHECKING_LOGS_BUILD for repo '{repo_id}'")
|
1051 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1112,7 +1119,7 @@ def handle_checking_logs_run(
|
|
1112 |
use_grounding: bool,
|
1113 |
*args,
|
1114 |
**kwargs
|
1115 |
-
) ->
|
1116 |
"""Handles logic when in the CHECKING_LOGS_RUN state."""
|
1117 |
logging.info(f"Handling STATE_CHECKING_LOGS_RUN for repo '{repo_id}'")
|
1118 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1195,7 +1202,7 @@ def handle_debugging_code(
|
|
1195 |
use_grounding: bool,
|
1196 |
*args,
|
1197 |
**kwargs
|
1198 |
-
) ->
|
1199 |
"""Handles logic when in the DEBUGGING_CODE state."""
|
1200 |
logging.info(f"Handling STATE_DEBUGGING_CODE (attempt #{debug_attempts}) for repo '{repo_id}'")
|
1201 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1282,7 +1289,7 @@ def handle_uploading_fixed_app_py(
|
|
1282 |
use_grounding: bool,
|
1283 |
*args,
|
1284 |
**kwargs
|
1285 |
-
) ->
|
1286 |
"""Handles logic when in the UPLOADING_FIXED_APP_PY state."""
|
1287 |
logging.info(f"Handling STATE_UPLOADING_FIXED_APP_PY for repo '{repo_id}'")
|
1288 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1353,7 +1360,7 @@ def handle_complete(
|
|
1353 |
use_grounding: bool,
|
1354 |
*args,
|
1355 |
**kwargs
|
1356 |
-
) ->
|
1357 |
"""Handles logic when in the COMPLETE state."""
|
1358 |
logging.info("Handling STATE_COMPLETE")
|
1359 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
@@ -1471,7 +1478,6 @@ def ai_workflow_chat(
|
|
1471 |
if handler:
|
1472 |
logging.debug(f"Invoking handler for state: {state}")
|
1473 |
# Call the state handler function, passing all necessary data
|
1474 |
-
# Need to pass *all* inputs to the handler function
|
1475 |
# Note: The inputs passed here are the *current* values received by the generator,
|
1476 |
# which are the values of the UI components and State variables
|
1477 |
# at the moment the button was clicked, plus any previous yielded state.
|
|
|
5 |
import io
|
6 |
import requests
|
7 |
import logging
|
8 |
+
from typing import List, Dict, Any, Tuple, Optional, Literal, Generator, Union # Import Union for clarity
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
|
11 |
# --- Configure Logging ---
|
|
|
288 |
|
289 |
MAX_DEBUG_ATTEMPTS = 3 # Limit the number of automatic debug attempts
|
290 |
|
291 |
+
|
292 |
+
# Define type aliases for workflow inputs and outputs BEFORE they are used in function signatures
|
293 |
+
WorkflowInputs = Tuple[
|
294 |
+
str, List[Dict[str, str]], Optional[gr.OAuthProfile], Optional[gr.OAuthToken],
|
295 |
+
Optional[str], Optional[str], Optional[str], WorkflowState, str, str, str, str,
|
296 |
+
int, Optional[str], Optional[str], Optional[str], bool
|
297 |
+
]
|
298 |
+
|
299 |
+
WorkflowOutputs = Tuple[
|
300 |
+
List[Dict[str, str]], Optional[str], WorkflowState, str, str, str,
|
301 |
+
int, Optional[str], Optional[str], Optional[str], bool, Optional[str], Optional[str]
|
302 |
+
]
|
303 |
+
|
304 |
+
# A handler function can either return the final output tuple or yield intermediate ones before returning the final one
|
305 |
+
# If a handler *only* returns the final tuple (no intermediate yields), its type hint should be WorkflowOutputs
|
306 |
+
# If a handler *yields* intermediate tuples before returning the final tuple, its type hint is a Generator
|
307 |
+
# The main generator function handles both cases.
|
308 |
+
# WorkflowHandlerReturn = Any # Using Any is technically correct but less specific.
|
309 |
+
# Let's be more precise where we know the return type.
|
310 |
+
|
311 |
+
|
312 |
def add_bot_message(history: list[dict], bot_message: str) -> list[dict]:
|
313 |
+
"""Helper to add a new assistant message to the chatbot history."""
|
314 |
# Make a copy to avoid modifying history in place if needed later, though generator pattern usually handles this
|
315 |
new_history = list(history)
|
316 |
new_history.append({"role": "assistant", "content": bot_message})
|
|
|
354 |
# They take all necessary inputs from the main generator's arguments
|
355 |
# and return the full tuple of outputs required by the generator's yield signature.
|
356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
def package_workflow_outputs(
|
358 |
history: List[Dict[str, str]],
|
359 |
repo_id: Optional[str],
|
|
|
375 |
current_gemini_key, current_gemini_model)
|
376 |
|
377 |
|
378 |
+
# Handlers that only RETURN a WorkflowOutputs tuple
|
379 |
def handle_idle(
|
380 |
message: str,
|
381 |
history: List[Dict[str, str]],
|
|
|
396 |
use_grounding: bool,
|
397 |
*args, # Catch potential extra args
|
398 |
**kwargs # Catch potential extra kwargs
|
399 |
+
) -> WorkflowOutputs: # Corrected return type
|
400 |
"""Handles logic when in the IDLE state."""
|
401 |
logging.info(f"Handling STATE_IDLE with message: {message[:50]}...")
|
402 |
|
|
|
426 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
427 |
logging.warning(f"Invalid repo name format received: {new_repo_name}")
|
428 |
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).")
|
429 |
+
# Stay in IDLE and return message
|
430 |
return package_workflow_outputs(
|
431 |
history=history, repo_id=repo_id, state=STATE_IDLE,
|
432 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
|
|
451 |
if not new_repo_name or re.search(r'[^a-zA-Z0-9_-]', new_repo_name) or len(new_repo_name) > 100:
|
452 |
logging.warning(f"Invalid repo name format received: {new_repo_name}")
|
453 |
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).")
|
454 |
+
# Stay in IDLE and return message
|
455 |
return package_workflow_outputs(
|
456 |
history=history, repo_id=repo_id, state=STATE_IDLE,
|
457 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
|
|
511 |
use_grounding: bool,
|
512 |
*args,
|
513 |
**kwargs
|
514 |
+
) -> WorkflowOutputs: # Corrected return type
|
515 |
"""Handles logic when in the AWAITING_REPO_NAME state."""
|
516 |
logging.info(f"Handling STATE_AWAITING_REPO_NAME with message: {message[:50]}...")
|
517 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
562 |
use_grounding: bool,
|
563 |
*args,
|
564 |
**kwargs
|
565 |
+
) -> WorkflowOutputs: # Corrected return type
|
566 |
"""Handles logic when in the CREATING_SPACE state."""
|
567 |
logging.info(f"Handling STATE_CREATING_SPACE for repo '{repo_name}'")
|
568 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
602 |
use_grounding=use_grounding, current_gemini_key=current_gemini_key, current_gemini_model=current_gemini_model
|
603 |
)
|
604 |
|
605 |
+
# Handlers that YIELD intermediate steps before returning the final tuple
|
606 |
def handle_generating_code(
|
607 |
message: str,
|
608 |
history: List[Dict[str, str]],
|
|
|
623 |
use_grounding: bool,
|
624 |
*args,
|
625 |
**kwargs
|
626 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
627 |
"""Handles logic when in the GENERATING_CODE state."""
|
628 |
logging.info("Handling STATE_GENERATING_CODE")
|
629 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
643 |
if use_grounding:
|
644 |
history = add_bot_message(history, "(Using Grounding with Google Search)")
|
645 |
# Yield message before API call to show immediate feedback
|
|
|
646 |
yield package_workflow_outputs(
|
647 |
history=history, repo_id=repo_id, state=state,
|
648 |
updated_preview=preview_html, updated_run=container_logs, updated_build=build_logs,
|
|
|
704 |
use_grounding: bool,
|
705 |
*args,
|
706 |
**kwargs
|
707 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
708 |
"""Handles logic when in the UPLOADING_APP_PY state."""
|
709 |
logging.info("Handling STATE_UPLOADING_APP_PY")
|
710 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
773 |
use_grounding: bool,
|
774 |
*args,
|
775 |
**kwargs
|
776 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
777 |
"""Handles logic when in the GENERATING_REQUIREMENTS state."""
|
778 |
logging.info("Handling STATE_GENERATING_REQUIREMENTS")
|
779 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
848 |
use_grounding: bool,
|
849 |
*args,
|
850 |
**kwargs
|
851 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
852 |
"""Handles logic when in the UPLOADING_REQUIREMENTS state."""
|
853 |
logging.info("Handling STATE_UPLOADING_REQUIREMENTS")
|
854 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
917 |
use_grounding: bool,
|
918 |
*args,
|
919 |
**kwargs
|
920 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
921 |
"""Handles logic when in the GENERATING_README state."""
|
922 |
logging.info("Handling STATE_GENERATING_README")
|
923 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
983 |
use_grounding: bool,
|
984 |
*args,
|
985 |
**kwargs
|
986 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
987 |
"""Handles logic when in the UPLOADING_README state."""
|
988 |
logging.info("Handling STATE_UPLOADING_README")
|
989 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1052 |
use_grounding: bool,
|
1053 |
*args,
|
1054 |
**kwargs
|
1055 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
1056 |
"""Handles logic when in the CHECKING_LOGS_BUILD state."""
|
1057 |
logging.info(f"Handling STATE_CHECKING_LOGS_BUILD for repo '{repo_id}'")
|
1058 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1119 |
use_grounding: bool,
|
1120 |
*args,
|
1121 |
**kwargs
|
1122 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
1123 |
"""Handles logic when in the CHECKING_LOGS_RUN state."""
|
1124 |
logging.info(f"Handling STATE_CHECKING_LOGS_RUN for repo '{repo_id}'")
|
1125 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1202 |
use_grounding: bool,
|
1203 |
*args,
|
1204 |
**kwargs
|
1205 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
1206 |
"""Handles logic when in the DEBUGGING_CODE state."""
|
1207 |
logging.info(f"Handling STATE_DEBUGGING_CODE (attempt #{debug_attempts}) for repo '{repo_id}'")
|
1208 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1289 |
use_grounding: bool,
|
1290 |
*args,
|
1291 |
**kwargs
|
1292 |
+
) -> Generator[WorkflowOutputs, None, WorkflowOutputs]: # Correct return type for generator
|
1293 |
"""Handles logic when in the UPLOADING_FIXED_APP_PY state."""
|
1294 |
logging.info(f"Handling STATE_UPLOADING_FIXED_APP_PY for repo '{repo_id}'")
|
1295 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1360 |
use_grounding: bool,
|
1361 |
*args,
|
1362 |
**kwargs
|
1363 |
+
) -> WorkflowOutputs: # Corrected return type
|
1364 |
"""Handles logic when in the COMPLETE state."""
|
1365 |
logging.info("Handling STATE_COMPLETE")
|
1366 |
current_gemini_key = gemini_api_key # Use the input vars directly
|
|
|
1478 |
if handler:
|
1479 |
logging.debug(f"Invoking handler for state: {state}")
|
1480 |
# Call the state handler function, passing all necessary data
|
|
|
1481 |
# Note: The inputs passed here are the *current* values received by the generator,
|
1482 |
# which are the values of the UI components and State variables
|
1483 |
# at the moment the button was clicked, plus any previous yielded state.
|