wuhp commited on
Commit
6f44956
·
verified ·
1 Parent(s): 32f88a6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -35
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
- # Helper function to add a new assistant message to the chatbot history.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- ) -> WorkflowHandlerReturn:
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 yield message
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 yield message
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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
- ) -> WorkflowHandlerReturn:
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.