Update app.py
Browse files
app.py
CHANGED
@@ -16,14 +16,15 @@ from build_logic import (
|
|
16 |
parse_markdown as build_logic_parse_markdown,
|
17 |
delete_space_file as build_logic_delete_space_file,
|
18 |
get_space_runtime_status,
|
19 |
-
apply_staged_file_changes,
|
20 |
duplicate_space as build_logic_duplicate_space,
|
21 |
list_user_spaces as build_logic_list_user_spaces,
|
22 |
build_logic_set_space_privacy,
|
23 |
build_logic_delete_space,
|
24 |
build_logic_create_pull_request,
|
25 |
build_logic_add_comment,
|
26 |
-
build_logic_like_space
|
|
|
27 |
)
|
28 |
|
29 |
from model_logic import (
|
@@ -110,7 +111,7 @@ def _infer_lang_from_filename(filename):
|
|
110 |
'rb': 'ruby', 'php': 'php', 'go': 'go', 'rs': 'rust', 'swift': 'swift', 'kt': 'kotlin', 'kts': 'kotlin',
|
111 |
'sql': 'sql', 'dockerfile': 'docker', 'tf': 'terraform', 'hcl': 'terraform',
|
112 |
'txt': 'plaintext', 'log': 'plaintext', 'ini': 'ini', 'conf': 'plaintext', 'cfg': 'plaintext',
|
113 |
-
'csv': 'plaintext', 'tsv': '
|
114 |
'.env': 'plaintext', '.gitignore': 'plaintext', '.npmrc': 'plaintext', '.gitattributes': 'plaintext',
|
115 |
'makefile': 'makefile',
|
116 |
}
|
@@ -354,13 +355,12 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
354 |
print(f"Staged DELETE_SPACE action for {hf_owner_name}/{hf_repo_name}")
|
355 |
|
356 |
elif command == "CREATE_PR" and args:
|
357 |
-
# Expecting: CREATE_PR <target_repo_id> --title "..." --body "..."
|
358 |
target_repo_id = None
|
359 |
title = ""
|
360 |
body = ""
|
361 |
try:
|
362 |
target_repo_id = args[0]
|
363 |
-
arg_string = shlex.join(args[1:])
|
364 |
|
365 |
title_match = re.search(r'--title\s+"([^"]*)"', arg_string)
|
366 |
if title_match: title = title_match.group(1)
|
@@ -370,7 +370,7 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
370 |
if body_match: body = body_match.group(1)
|
371 |
else: print("Warning: CREATE_PR missing --body argument.")
|
372 |
|
373 |
-
if target_repo_id and title:
|
374 |
changeset.append({"type": "CREATE_PR", "source_repo_id": f"{hf_owner_name}/{hf_repo_name}", "target_repo_id": target_repo_id, "title": title, "body": body})
|
375 |
print(f"Staged CREATE_PR action to {target_repo_id}")
|
376 |
else:
|
@@ -382,16 +382,12 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
382 |
|
383 |
|
384 |
elif command == "ADD_COMMENT" and args:
|
385 |
-
# Expecting: ADD_COMMENT "Your comment text"
|
386 |
comment_text = None
|
387 |
try:
|
388 |
-
# Attempt to parse the rest of the line as a single quoted string
|
389 |
-
# Rejoin args and then try to find the quoted string
|
390 |
comment_string = shlex.join(args)
|
391 |
-
comment_match = re.match(r'^"([^"]*)"$', comment_string)
|
392 |
if comment_match: comment_text = comment_match.group(1)
|
393 |
else:
|
394 |
-
# Fallback: just use the raw args joined by space if not a perfect quoted string
|
395 |
comment_text = " ".join(args)
|
396 |
print(f"Warning: ADD_COMMENT argument was not a single quoted string, using raw args: '{comment_text}'")
|
397 |
|
@@ -408,7 +404,6 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
408 |
|
409 |
|
410 |
elif command == "LIKE_SPACE":
|
411 |
-
# No args expected
|
412 |
if hf_owner_name and hf_repo_name:
|
413 |
changeset.append({"type": "LIKE_SPACE", "repo_id": f"{hf_owner_name}/{hf_repo_name}"})
|
414 |
print(f"Staged LIKE_SPACE action on {hf_owner_name}/{hf_repo_name}")
|
@@ -416,9 +411,6 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
416 |
print(f"Warning: Cannot stage LIKE_SPACE action, no Space currently loaded.")
|
417 |
changeset.append({"type": "Error", "message": "Cannot like space, no Space currently loaded."})
|
418 |
|
419 |
-
# Note: DUPLICATE_SPACE is handled before this loop
|
420 |
-
|
421 |
-
|
422 |
for file_info in ai_proposed_files_list:
|
423 |
filename = file_info["path"]
|
424 |
proposed_content = file_info["content"]
|
@@ -456,6 +448,8 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
456 |
for change in space_actions:
|
457 |
if change["type"] == "CREATE_SPACE":
|
458 |
md_summary.append(f"- **🚀 Create New Space:** `{change.get('repo_id', '...')}` (SDK: {change.get('sdk', 'gradio')}, Private: {change.get('private', False)})")
|
|
|
|
|
459 |
elif change["type"] == "SET_PRIVACY":
|
460 |
md_summary.append(f"- **🔒 Set Privacy:** Set `{change.get('repo_id', '...')}` to `private={change.get('private', False)}`")
|
461 |
elif change["type"] == "DELETE_SPACE":
|
@@ -466,7 +460,7 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
466 |
if change.get('body'): md_summary.append(f" - Body: `{change.get('body', '...')}`")
|
467 |
elif change["type"] == "ADD_COMMENT":
|
468 |
md_summary.append(f"- **💬 Add Comment:** On `{change.get('repo_id', '...')}`")
|
469 |
-
md_summary.append(f" - Comment: `{change.get('comment', '...')[:100]}{'...' if len(change.get('comment', '')) > 100 else ''}`")
|
470 |
elif change["type"] == "LIKE_SPACE":
|
471 |
md_summary.append(f"- **👍 Like Space:** `{change.get('repo_id', '...')}`")
|
472 |
elif change["type"] == "Error":
|
@@ -598,15 +592,13 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
598 |
global parsed_code_blocks_state_cache
|
599 |
|
600 |
_status = "Applying changes..."
|
601 |
-
# Yield initial status, hide confirm UI, clear summary
|
602 |
yield _status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="*Applying changes...*")
|
603 |
-
#
|
604 |
-
yield gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
605 |
|
606 |
if not changeset:
|
607 |
return "No changes to apply.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="No changes were staged.")
|
608 |
|
609 |
-
# Check if the first action is an exclusive action like DUPLICATE_SPACE
|
610 |
first_action = changeset[0] if changeset else None
|
611 |
is_exclusive_duplicate = first_action and first_action.get('type') == 'DUPLICATE_SPACE'
|
612 |
is_exclusive_delete = first_action and first_action.get('type') == 'DELETE_SPACE'
|
@@ -621,20 +613,17 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
621 |
target_repo_id=change["target_repo_id"],
|
622 |
private=change.get("private", False)
|
623 |
)
|
624 |
-
# After duplication, attempt to load the *new* space
|
625 |
_status_reload = f"{status_message} | Attempting to load the new Space [{change['target_repo_id']}]..."
|
626 |
-
yield gr.update(value=_status_reload)
|
627 |
|
628 |
-
# Extract new owner and space name from target_repo_id
|
629 |
target_repo_id = change['target_repo_id']
|
630 |
new_owner, new_space_name = target_repo_id.split('/', 1) if '/' in target_repo_id else (None, target_repo_id)
|
631 |
|
632 |
-
# If owner was not explicitly provided in target_repo_id, try to get it from the token
|
633 |
if not new_owner:
|
634 |
token, token_err = build_logic_get_api_token(hf_api_key)
|
635 |
if token_err:
|
636 |
print(f"Error getting token for new owner determination: {token_err}")
|
637 |
-
new_owner = None
|
638 |
else:
|
639 |
try: user_info = build_logic_whoami(token=token); new_owner = user_info.get('name')
|
640 |
except Exception as e:
|
@@ -642,7 +631,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
642 |
new_owner = None
|
643 |
|
644 |
|
645 |
-
# Trigger the load logic for the new space if owner and name are available
|
646 |
if new_owner and new_space_name:
|
647 |
sdk, file_list, err_list = get_space_repository_info(hf_api_key, new_space_name, new_owner)
|
648 |
if err_list:
|
@@ -673,36 +661,30 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
673 |
else:
|
674 |
iframe_update = gr.update(value=None, visible=False)
|
675 |
|
676 |
-
# Fetch runtime status for the new space
|
677 |
runtime_status_md = handle_refresh_space_status(hf_api_key, new_owner, new_space_name)
|
678 |
|
679 |
-
# Final yield after loading the new space
|
680 |
yield (
|
681 |
-
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
682 |
-
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
683 |
-
[], gr.update(value="*No changes proposed.*"),
|
684 |
-
owner_update, space_update, file_browser_update, iframe_update,
|
685 |
-
gr.update(value=runtime_status_md)
|
686 |
)
|
687 |
|
688 |
else:
|
689 |
reload_error = "Cannot load new Space state: Owner or Space Name missing after duplication."
|
690 |
-
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
691 |
final_overall_status = status_message + f" | Reload Status: {reload_error}"
|
692 |
-
# Keep old UI fields, clear file browser/iframe
|
693 |
yield (
|
694 |
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
695 |
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
696 |
[], gr.update(value="*No changes proposed.*"),
|
697 |
gr.update(), gr.update(), gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False),
|
698 |
-
gr.update(value="*Runtime status unavailable for new space.*")
|
699 |
)
|
700 |
|
701 |
-
|
702 |
else:
|
703 |
-
# Should not happen if staged_changeset exists and type is DUPLICATE_SPACE, but as safeguard
|
704 |
final_overall_status = "Duplicate Action Error: Invalid duplicate changeset format."
|
705 |
-
# No state change, just report error
|
706 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
|
707 |
yield (
|
708 |
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
@@ -713,7 +695,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
713 |
|
714 |
elif is_exclusive_delete:
|
715 |
change = first_action
|
716 |
-
# Confirm DELETE_SPACE is only for the currently loaded space before calling the backend
|
717 |
delete_owner = change.get('owner') or owner_name
|
718 |
delete_space = change.get('space_name') or space_name
|
719 |
delete_repo_id_target = f"{delete_owner}/{delete_space}" if delete_owner and delete_space else None
|
@@ -725,24 +706,21 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
725 |
else:
|
726 |
status_message = build_logic_delete_space(hf_api_key, delete_owner, delete_space)
|
727 |
final_overall_status = status_message
|
728 |
-
# Clear the UI completely after successful deletion
|
729 |
if "Successfully" in status_message:
|
|
|
730 |
parsed_code_blocks_state_cache = []
|
731 |
-
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
732 |
owner_update = gr.update(value="")
|
733 |
space_update = gr.update(value="")
|
734 |
file_browser_update = gr.update(visible=False, choices=[], value=None)
|
735 |
iframe_update = gr.update(value=None, visible=False)
|
736 |
runtime_status_update = gr.update(value="*Space deleted, runtime status unavailable.*")
|
737 |
else:
|
738 |
-
# If deletion failed, keep the current state and update status
|
739 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
|
740 |
owner_update = gr.update()
|
741 |
space_update = gr.update()
|
742 |
file_browser_update = gr.update()
|
743 |
iframe_update = gr.update()
|
744 |
-
runtime_status_update = gr.update() # Keep existing or trigger refresh?
|
745 |
-
# Let's trigger a status refresh after failed delete attempt
|
746 |
runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
|
747 |
|
748 |
|
@@ -755,36 +733,35 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
755 |
)
|
756 |
|
757 |
|
758 |
-
else:
|
759 |
-
|
760 |
action_status_messages = []
|
761 |
file_change_operations = []
|
762 |
|
763 |
-
# Process non-file actions first
|
764 |
for change in changeset:
|
765 |
try:
|
766 |
if change['type'] == 'CREATE_SPACE':
|
767 |
repo_id_to_create = change.get('repo_id')
|
768 |
if repo_id_to_create:
|
769 |
-
msg = build_logic_create_space(hf_api_key, repo_id_to_create, change.get('sdk', 'gradio'), change.get('private', False))
|
770 |
action_status_messages.append(f"CREATE_SPACE: {msg}")
|
|
|
|
|
771 |
else:
|
772 |
action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
|
773 |
elif change['type'] == 'SET_PRIVACY':
|
774 |
-
|
775 |
-
|
776 |
msg = build_logic_set_space_privacy(hf_api_key, repo_id_to_set_privacy, change['private'])
|
777 |
action_status_messages.append(f"SET_PRIVACY: {msg}")
|
778 |
-
|
779 |
action_status_messages.append("SET_PRIVACY Error: Cannot set privacy, no Space currently loaded.")
|
780 |
elif change['type'] == 'CREATE_PR':
|
781 |
-
# Ensure source is the currently loaded space
|
782 |
source_repo = f"{owner_name}/{space_name}" if owner_name and space_name else None
|
783 |
if source_repo and change.get('target_repo_id') and change.get('title'):
|
784 |
msg = build_logic_create_pull_request(hf_api_key, source_repo, change['target_repo_id'], change['title'], change.get('body', ''))
|
785 |
action_status_messages.append(f"CREATE_PR: {msg}")
|
786 |
else:
|
787 |
-
action_status_messages.append("CREATE_PR Error: Source/Target repo, title, or body missing.")
|
788 |
elif change['type'] == 'ADD_COMMENT':
|
789 |
repo_id_to_comment = change.get('repo_id') or f"{owner_name}/{space_name}"
|
790 |
if repo_id_to_comment and owner_name and space_name and change.get('comment'):
|
@@ -802,7 +779,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
802 |
elif change['type'] == 'Error':
|
803 |
action_status_messages.append(f"Staging Error: {change.get('message', 'Unknown error')}")
|
804 |
|
805 |
-
# Collect file changes for the final commit
|
806 |
elif change['type'] in ['CREATE_FILE', 'UPDATE_FILE', 'DELETE_FILE']:
|
807 |
file_change_operations.append(change)
|
808 |
|
@@ -812,8 +788,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
812 |
import traceback
|
813 |
traceback.print_exc()
|
814 |
|
815 |
-
|
816 |
-
# Apply file changes if any were staged
|
817 |
file_commit_status = ""
|
818 |
if file_change_operations:
|
819 |
if owner_name and space_name:
|
@@ -821,18 +795,18 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
821 |
else:
|
822 |
file_commit_status = "File Commit Error: Cannot commit file changes, no Space currently loaded."
|
823 |
action_status_messages.append(file_commit_status)
|
|
|
|
|
824 |
|
825 |
|
826 |
-
# Combine all status messages
|
827 |
final_overall_status = " | ".join(action_status_messages)
|
828 |
if file_commit_status and file_commit_status != "No file changes (create/update/delete) to commit.":
|
829 |
if final_overall_status: final_overall_status += " | "
|
830 |
final_overall_status += file_commit_status
|
831 |
if not final_overall_status: final_overall_status = "No operations were applied."
|
832 |
|
833 |
-
# After applying changes, reload the space state to reflect the actual state on the Hub
|
834 |
_status_reload = f"{final_overall_status} | Reloading Space state..."
|
835 |
-
yield gr.update(value=_status_reload)
|
836 |
|
837 |
refreshed_file_list = []
|
838 |
reload_error = None
|
@@ -863,7 +837,6 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
863 |
else:
|
864 |
iframe_update = gr.update(value=None, visible=False)
|
865 |
|
866 |
-
# Refresh runtime status as well
|
867 |
runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
|
868 |
|
869 |
|
@@ -908,6 +881,42 @@ def update_models_dropdown(provider_select):
|
|
908 |
selected_value = default_model if default_model in models else (models[0] if models else None)
|
909 |
return gr.update(choices=models, value=selected_value)
|
910 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
911 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
912 |
global parsed_code_blocks_state_cache
|
913 |
_formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
|
@@ -916,7 +925,8 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
916 |
_changeset_clear = []
|
917 |
_changeset_summary_clear = "*No changes proposed.*"
|
918 |
_confirm_ui_hidden = gr.update(visible=False)
|
919 |
-
_list_spaces_display_clear =
|
|
|
920 |
|
921 |
|
922 |
yield (
|
@@ -925,7 +935,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
925 |
_iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
|
926 |
gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
|
927 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
928 |
-
|
929 |
)
|
930 |
|
931 |
owner_to_use = ui_owner_name
|
@@ -963,7 +973,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
963 |
gr.update(visible=False, choices=[], value=None),
|
964 |
gr.update(), gr.update(),
|
965 |
gr.update(value=None, visible=False),
|
966 |
-
_download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
967 |
)
|
968 |
return
|
969 |
|
@@ -997,7 +1007,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
997 |
file_browser_update,
|
998 |
gr.update(), gr.update(),
|
999 |
iframe_update,
|
1000 |
-
_download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1001 |
)
|
1002 |
|
1003 |
def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, is_private_ui, formatted_markdown_content):
|
@@ -1032,9 +1042,6 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1032 |
yield gr.update(value=_build_status),
|
1033 |
return
|
1034 |
|
1035 |
-
# Use apply_staged_file_changes and CREATE_SPACE action via confirm handler logic
|
1036 |
-
# Or simulate the parts needed here directly? Direct call might be simpler for manual build.
|
1037 |
-
# Let's use apply_staged_file_changes directly, but need to handle the CREATE_SPACE part separately first.
|
1038 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
1039 |
if token_err:
|
1040 |
_build_status = f"Build Error: API Token Error: {token_err}"
|
@@ -1045,19 +1052,17 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1045 |
|
1046 |
build_status_messages = []
|
1047 |
try:
|
1048 |
-
# Attempt to create/ensure space exists first
|
1049 |
api.create_repo(repo_id=repo_id_target, repo_type="space", space_sdk=space_sdk_select, private=is_private_ui, exist_ok=True)
|
1050 |
build_status_messages.append(f"CREATE_SPACE: Successfully created or ensured space [{repo_id_target}](https://huggingface.co/spaces/{repo_id_target}) exists.")
|
1051 |
except HfHubHTTPError as e_http:
|
1052 |
build_status_messages.append(f"CREATE_SPACE HTTP Error ({e_http.response.status_code if e_http.response else 'N/A'}): {e_http.response.text if e_http.response else str(e_http)}. Check logs.")
|
1053 |
if e_http.response and e_http.response.status_code in (401, 403):
|
1054 |
-
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status)
|
1055 |
except Exception as e:
|
1056 |
build_status_messages.append(f"CREATE_SPACE Error: {e}")
|
1057 |
-
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status)
|
1058 |
|
1059 |
|
1060 |
-
# Now apply file changes from the markdown content
|
1061 |
file_changes_only_changeset = [{"type": "CREATE_FILE", "path": f["path"], "content": f["content"], "lang": _infer_lang_from_filename(f["path"])} for f in proposed_files_list]
|
1062 |
if file_changes_only_changeset:
|
1063 |
file_commit_status = apply_staged_file_changes(hf_api_key_ui, ui_owner_name_part, ui_space_name_part, file_changes_only_changeset)
|
@@ -1079,7 +1084,7 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1079 |
gr.update(value=_build_status), _iframe_html, _file_browser_update,
|
1080 |
gr.update(value=owner_to_use), gr.update(value=space_to_use),
|
1081 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
1082 |
-
gr.update(value=_formatted_md), gr.update(value=_detected_preview_val), _download_btn_update, gr.update()
|
1083 |
)
|
1084 |
|
1085 |
sdk_built, file_list, err_list = get_space_repository_info(hf_api_key_ui, space_to_use, owner_to_use)
|
@@ -1207,7 +1212,7 @@ def handle_refresh_space_status(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
1207 |
status_details, err = get_space_runtime_status(hf_api_key_ui, ui_space_name, ui_owner_name)
|
1208 |
repo_info = None
|
1209 |
repo_info_err = None
|
1210 |
-
if not err:
|
1211 |
sdk, file_list, repo_info_err = get_space_repository_info(hf_api_key_ui, ui_space_name, ui_owner_name)
|
1212 |
if not repo_info_err:
|
1213 |
repo_info = {"sdk": sdk, "file_count": len(file_list) if file_list is not None else 'N/A'}
|
@@ -1282,13 +1287,12 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1282 |
return "Duplicate Error: Target Owner and Target Space Name are required.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1283 |
if "/" in target_space_name:
|
1284 |
return "Duplicate Error: Target Space Name should not contain '/'. Use Target Owner field for the owner part.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1285 |
-
|
1286 |
|
1287 |
source_repo_id = f"{source_owner}/{source_space_name}"
|
1288 |
target_repo_id = f"{target_owner}/{target_space_name}"
|
1289 |
|
1290 |
status_msg = f"Attempting to duplicate `{source_repo_id}` to `{target_repo_id}`..."
|
1291 |
-
# Yield initial status
|
1292 |
yield status_msg, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1293 |
|
1294 |
|
@@ -1296,7 +1300,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1296 |
status_msg = f"Duplication Result: {result_message}"
|
1297 |
|
1298 |
_status_reload = f"{status_msg} | Attempting to load the new Space [{target_repo_id}]..."
|
1299 |
-
yield gr.update(value=_status_reload)
|
1300 |
|
1301 |
new_owner = target_owner
|
1302 |
new_space_name = target_space_name
|
@@ -1305,7 +1309,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1305 |
|
1306 |
if err_list:
|
1307 |
reload_error = f"Error reloading file list after duplication: {err_list}"
|
1308 |
-
|
1309 |
parsed_code_blocks_state_cache = []
|
1310 |
_file_browser_update = gr.update(visible=False, choices=[], value=None)
|
1311 |
_iframe_html_update = gr.update(value=None, visible=False)
|
@@ -1317,6 +1321,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1317 |
is_binary = lang == "binary" or (err_get is not None)
|
1318 |
code = f"[Error loading content: {err_get}]" if err_get else (content or "")
|
1319 |
loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
|
|
|
1320 |
parsed_code_blocks_state_cache = loaded_files
|
1321 |
|
1322 |
_file_browser_update = gr.update(visible=True, choices=sorted([f["filename"] for f in parsed_code_blocks_state_cache if not f.get("is_structure_block")] or []), value=None)
|
@@ -1382,6 +1387,8 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1382 |
owner_name_input = gr.Textbox(label="HF Owner Name", placeholder="e.g., your-username")
|
1383 |
space_name_input = gr.Textbox(label="HF Space Name", value="")
|
1384 |
load_space_button = gr.Button("🔄 Load Existing Space", variant="secondary")
|
|
|
|
|
1385 |
|
1386 |
with gr.Accordion("🤖 AI Model Settings", open=True):
|
1387 |
available_providers = get_available_providers()
|
@@ -1494,7 +1501,7 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1494 |
confirm_outputs = [
|
1495 |
status_output, formatted_space_output_display, detected_files_preview, download_button,
|
1496 |
confirm_accordion, confirm_button, cancel_button, changeset_state, changeset_display,
|
1497 |
-
owner_name_input, space_name_input, file_browser_dropdown, space_iframe_display, space_runtime_status_display
|
1498 |
]
|
1499 |
confirm_button.click(handle_confirm_changes, inputs=confirm_inputs, outputs=confirm_outputs)
|
1500 |
|
@@ -1510,7 +1517,7 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1510 |
space_iframe_display, download_button, build_status_display,
|
1511 |
edit_status_display, space_runtime_status_display,
|
1512 |
changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
|
1513 |
-
list_spaces_display
|
1514 |
]
|
1515 |
load_space_button.click(
|
1516 |
fn=handle_load_existing_space,
|
@@ -1518,6 +1525,9 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1518 |
outputs=load_space_outputs
|
1519 |
)
|
1520 |
|
|
|
|
|
|
|
1521 |
build_outputs = [
|
1522 |
build_status_display, space_iframe_display, file_browser_dropdown,
|
1523 |
owner_name_input, space_name_input,
|
@@ -1557,4 +1567,4 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1557 |
|
1558 |
|
1559 |
if __name__ == "__main__":
|
1560 |
-
demo.launch(debug=False, mcp_server=True)
|
|
|
16 |
parse_markdown as build_logic_parse_markdown,
|
17 |
delete_space_file as build_logic_delete_space_file,
|
18 |
get_space_runtime_status,
|
19 |
+
apply_staged_file_changes,
|
20 |
duplicate_space as build_logic_duplicate_space,
|
21 |
list_user_spaces as build_logic_list_user_spaces,
|
22 |
build_logic_set_space_privacy,
|
23 |
build_logic_delete_space,
|
24 |
build_logic_create_pull_request,
|
25 |
build_logic_add_comment,
|
26 |
+
build_logic_like_space,
|
27 |
+
build_logic_create_space # Added back for manual build flow
|
28 |
)
|
29 |
|
30 |
from model_logic import (
|
|
|
111 |
'rb': 'ruby', 'php': 'php', 'go': 'go', 'rs': 'rust', 'swift': 'swift', 'kt': 'kotlin', 'kts': 'kotlin',
|
112 |
'sql': 'sql', 'dockerfile': 'docker', 'tf': 'terraform', 'hcl': 'terraform',
|
113 |
'txt': 'plaintext', 'log': 'plaintext', 'ini': 'ini', 'conf': 'plaintext', 'cfg': 'plaintext',
|
114 |
+
'csv': 'plaintext', 'tsv': 'tsv', 'err': 'plaintext',
|
115 |
'.env': 'plaintext', '.gitignore': 'plaintext', '.npmrc': 'plaintext', '.gitattributes': 'plaintext',
|
116 |
'makefile': 'makefile',
|
117 |
}
|
|
|
355 |
print(f"Staged DELETE_SPACE action for {hf_owner_name}/{hf_repo_name}")
|
356 |
|
357 |
elif command == "CREATE_PR" and args:
|
|
|
358 |
target_repo_id = None
|
359 |
title = ""
|
360 |
body = ""
|
361 |
try:
|
362 |
target_repo_id = args[0]
|
363 |
+
arg_string = shlex.join(args[1:])
|
364 |
|
365 |
title_match = re.search(r'--title\s+"([^"]*)"', arg_string)
|
366 |
if title_match: title = title_match.group(1)
|
|
|
370 |
if body_match: body = body_match.group(1)
|
371 |
else: print("Warning: CREATE_PR missing --body argument.")
|
372 |
|
373 |
+
if target_repo_id and title:
|
374 |
changeset.append({"type": "CREATE_PR", "source_repo_id": f"{hf_owner_name}/{hf_repo_name}", "target_repo_id": target_repo_id, "title": title, "body": body})
|
375 |
print(f"Staged CREATE_PR action to {target_repo_id}")
|
376 |
else:
|
|
|
382 |
|
383 |
|
384 |
elif command == "ADD_COMMENT" and args:
|
|
|
385 |
comment_text = None
|
386 |
try:
|
|
|
|
|
387 |
comment_string = shlex.join(args)
|
388 |
+
comment_match = re.match(r'^"([^"]*)"$', comment_string)
|
389 |
if comment_match: comment_text = comment_match.group(1)
|
390 |
else:
|
|
|
391 |
comment_text = " ".join(args)
|
392 |
print(f"Warning: ADD_COMMENT argument was not a single quoted string, using raw args: '{comment_text}'")
|
393 |
|
|
|
404 |
|
405 |
|
406 |
elif command == "LIKE_SPACE":
|
|
|
407 |
if hf_owner_name and hf_repo_name:
|
408 |
changeset.append({"type": "LIKE_SPACE", "repo_id": f"{hf_owner_name}/{hf_repo_name}"})
|
409 |
print(f"Staged LIKE_SPACE action on {hf_owner_name}/{hf_repo_name}")
|
|
|
411 |
print(f"Warning: Cannot stage LIKE_SPACE action, no Space currently loaded.")
|
412 |
changeset.append({"type": "Error", "message": "Cannot like space, no Space currently loaded."})
|
413 |
|
|
|
|
|
|
|
414 |
for file_info in ai_proposed_files_list:
|
415 |
filename = file_info["path"]
|
416 |
proposed_content = file_info["content"]
|
|
|
448 |
for change in space_actions:
|
449 |
if change["type"] == "CREATE_SPACE":
|
450 |
md_summary.append(f"- **🚀 Create New Space:** `{change.get('repo_id', '...')}` (SDK: {change.get('sdk', 'gradio')}, Private: {change.get('private', False)})")
|
451 |
+
elif change["type"] == "DUPLICATE_SPACE":
|
452 |
+
md_summary.append(f"- **📂 Duplicate Space:** From `{change.get('source_repo_id', '...')}` to `{change.get('target_repo_id', '...')}` (Private: {change.get('private', False)})")
|
453 |
elif change["type"] == "SET_PRIVACY":
|
454 |
md_summary.append(f"- **🔒 Set Privacy:** Set `{change.get('repo_id', '...')}` to `private={change.get('private', False)}`")
|
455 |
elif change["type"] == "DELETE_SPACE":
|
|
|
460 |
if change.get('body'): md_summary.append(f" - Body: `{change.get('body', '...')}`")
|
461 |
elif change["type"] == "ADD_COMMENT":
|
462 |
md_summary.append(f"- **💬 Add Comment:** On `{change.get('repo_id', '...')}`")
|
463 |
+
md_summary.append(f" - Comment: `{change.get('comment', '...')[:100]}{'...' if len(change.get('comment', '')) > 100 else ''}`")
|
464 |
elif change["type"] == "LIKE_SPACE":
|
465 |
md_summary.append(f"- **👍 Like Space:** `{change.get('repo_id', '...')}`")
|
466 |
elif change["type"] == "Error":
|
|
|
592 |
global parsed_code_blocks_state_cache
|
593 |
|
594 |
_status = "Applying changes..."
|
|
|
595 |
yield _status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="*Applying changes...*")
|
596 |
+
# Dummy yields for components that might be updated later in the generator
|
597 |
+
yield gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
598 |
|
599 |
if not changeset:
|
600 |
return "No changes to apply.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="No changes were staged.")
|
601 |
|
|
|
602 |
first_action = changeset[0] if changeset else None
|
603 |
is_exclusive_duplicate = first_action and first_action.get('type') == 'DUPLICATE_SPACE'
|
604 |
is_exclusive_delete = first_action and first_action.get('type') == 'DELETE_SPACE'
|
|
|
613 |
target_repo_id=change["target_repo_id"],
|
614 |
private=change.get("private", False)
|
615 |
)
|
|
|
616 |
_status_reload = f"{status_message} | Attempting to load the new Space [{change['target_repo_id']}]..."
|
617 |
+
yield gr.update(value=_status_reload)
|
618 |
|
|
|
619 |
target_repo_id = change['target_repo_id']
|
620 |
new_owner, new_space_name = target_repo_id.split('/', 1) if '/' in target_repo_id else (None, target_repo_id)
|
621 |
|
|
|
622 |
if not new_owner:
|
623 |
token, token_err = build_logic_get_api_token(hf_api_key)
|
624 |
if token_err:
|
625 |
print(f"Error getting token for new owner determination: {token_err}")
|
626 |
+
new_owner = None
|
627 |
else:
|
628 |
try: user_info = build_logic_whoami(token=token); new_owner = user_info.get('name')
|
629 |
except Exception as e:
|
|
|
631 |
new_owner = None
|
632 |
|
633 |
|
|
|
634 |
if new_owner and new_space_name:
|
635 |
sdk, file_list, err_list = get_space_repository_info(hf_api_key, new_space_name, new_owner)
|
636 |
if err_list:
|
|
|
661 |
else:
|
662 |
iframe_update = gr.update(value=None, visible=False)
|
663 |
|
|
|
664 |
runtime_status_md = handle_refresh_space_status(hf_api_key, new_owner, new_space_name)
|
665 |
|
|
|
666 |
yield (
|
667 |
+
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
668 |
+
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
669 |
+
[], gr.update(value="*No changes proposed.*"),
|
670 |
+
owner_update, space_update, file_browser_update, iframe_update,
|
671 |
+
gr.update(value=runtime_status_md)
|
672 |
)
|
673 |
|
674 |
else:
|
675 |
reload_error = "Cannot load new Space state: Owner or Space Name missing after duplication."
|
676 |
+
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
677 |
final_overall_status = status_message + f" | Reload Status: {reload_error}"
|
|
|
678 |
yield (
|
679 |
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
680 |
gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
681 |
[], gr.update(value="*No changes proposed.*"),
|
682 |
gr.update(), gr.update(), gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False),
|
683 |
+
gr.update(value="*Runtime status unavailable for new space.*")
|
684 |
)
|
685 |
|
|
|
686 |
else:
|
|
|
687 |
final_overall_status = "Duplicate Action Error: Invalid duplicate changeset format."
|
|
|
688 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
|
689 |
yield (
|
690 |
gr.update(value=final_overall_status), gr.update(value=_formatted), gr.update(value=_detected), _download,
|
|
|
695 |
|
696 |
elif is_exclusive_delete:
|
697 |
change = first_action
|
|
|
698 |
delete_owner = change.get('owner') or owner_name
|
699 |
delete_space = change.get('space_name') or space_name
|
700 |
delete_repo_id_target = f"{delete_owner}/{delete_space}" if delete_owner and delete_space else None
|
|
|
706 |
else:
|
707 |
status_message = build_logic_delete_space(hf_api_key, delete_owner, delete_space)
|
708 |
final_overall_status = status_message
|
|
|
709 |
if "Successfully" in status_message:
|
710 |
+
global parsed_code_blocks_state_cache
|
711 |
parsed_code_blocks_state_cache = []
|
712 |
+
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
713 |
owner_update = gr.update(value="")
|
714 |
space_update = gr.update(value="")
|
715 |
file_browser_update = gr.update(visible=False, choices=[], value=None)
|
716 |
iframe_update = gr.update(value=None, visible=False)
|
717 |
runtime_status_update = gr.update(value="*Space deleted, runtime status unavailable.*")
|
718 |
else:
|
|
|
719 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
|
720 |
owner_update = gr.update()
|
721 |
space_update = gr.update()
|
722 |
file_browser_update = gr.update()
|
723 |
iframe_update = gr.update()
|
|
|
|
|
724 |
runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
|
725 |
|
726 |
|
|
|
733 |
)
|
734 |
|
735 |
|
736 |
+
else:
|
|
|
737 |
action_status_messages = []
|
738 |
file_change_operations = []
|
739 |
|
|
|
740 |
for change in changeset:
|
741 |
try:
|
742 |
if change['type'] == 'CREATE_SPACE':
|
743 |
repo_id_to_create = change.get('repo_id')
|
744 |
if repo_id_to_create:
|
745 |
+
msg = build_logic_create_space(hf_api_key, repo_id_to_create.split('/')[1], repo_id_to_create.split('/')[0] if '/' in repo_id_to_create else None, change.get('sdk', 'gradio'), "", change.get('private', False)) # Note: Manual create requires markdown, passing empty string
|
746 |
action_status_messages.append(f"CREATE_SPACE: {msg}")
|
747 |
+
# If creation was successful, potentially update the UI fields? No, AI might create a *different* space.
|
748 |
+
# Just report success/failure. The AI can propose loading it next.
|
749 |
else:
|
750 |
action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
|
751 |
elif change['type'] == 'SET_PRIVACY':
|
752 |
+
repo_id_to_set_privacy = change.get('repo_id') or f"{owner_name}/{space_name}"
|
753 |
+
if repo_id_to_set_privacy and owner_name and space_name:
|
754 |
msg = build_logic_set_space_privacy(hf_api_key, repo_id_to_set_privacy, change['private'])
|
755 |
action_status_messages.append(f"SET_PRIVACY: {msg}")
|
756 |
+
else:
|
757 |
action_status_messages.append("SET_PRIVACY Error: Cannot set privacy, no Space currently loaded.")
|
758 |
elif change['type'] == 'CREATE_PR':
|
|
|
759 |
source_repo = f"{owner_name}/{space_name}" if owner_name and space_name else None
|
760 |
if source_repo and change.get('target_repo_id') and change.get('title'):
|
761 |
msg = build_logic_create_pull_request(hf_api_key, source_repo, change['target_repo_id'], change['title'], change.get('body', ''))
|
762 |
action_status_messages.append(f"CREATE_PR: {msg}")
|
763 |
else:
|
764 |
+
action_status_messages.append("CREATE_PR Error: Source/Target repo, title, or body missing or no Space loaded.")
|
765 |
elif change['type'] == 'ADD_COMMENT':
|
766 |
repo_id_to_comment = change.get('repo_id') or f"{owner_name}/{space_name}"
|
767 |
if repo_id_to_comment and owner_name and space_name and change.get('comment'):
|
|
|
779 |
elif change['type'] == 'Error':
|
780 |
action_status_messages.append(f"Staging Error: {change.get('message', 'Unknown error')}")
|
781 |
|
|
|
782 |
elif change['type'] in ['CREATE_FILE', 'UPDATE_FILE', 'DELETE_FILE']:
|
783 |
file_change_operations.append(change)
|
784 |
|
|
|
788 |
import traceback
|
789 |
traceback.print_exc()
|
790 |
|
|
|
|
|
791 |
file_commit_status = ""
|
792 |
if file_change_operations:
|
793 |
if owner_name and space_name:
|
|
|
795 |
else:
|
796 |
file_commit_status = "File Commit Error: Cannot commit file changes, no Space currently loaded."
|
797 |
action_status_messages.append(file_commit_status)
|
798 |
+
else:
|
799 |
+
file_commit_status = "No file changes (create/update/delete) to commit."
|
800 |
|
801 |
|
|
|
802 |
final_overall_status = " | ".join(action_status_messages)
|
803 |
if file_commit_status and file_commit_status != "No file changes (create/update/delete) to commit.":
|
804 |
if final_overall_status: final_overall_status += " | "
|
805 |
final_overall_status += file_commit_status
|
806 |
if not final_overall_status: final_overall_status = "No operations were applied."
|
807 |
|
|
|
808 |
_status_reload = f"{final_overall_status} | Reloading Space state..."
|
809 |
+
yield gr.update(value=_status_reload)
|
810 |
|
811 |
refreshed_file_list = []
|
812 |
reload_error = None
|
|
|
837 |
else:
|
838 |
iframe_update = gr.update(value=None, visible=False)
|
839 |
|
|
|
840 |
runtime_status_update = handle_refresh_space_status(hf_api_key, owner_name, space_name)
|
841 |
|
842 |
|
|
|
881 |
selected_value = default_model if default_model in models else (models[0] if models else None)
|
882 |
return gr.update(choices=models, value=selected_value)
|
883 |
|
884 |
+
def handle_detect_user(hf_api_key_ui, owner_name_ui):
|
885 |
+
_status = "Detecting user from token..."
|
886 |
+
# Yield initial state to show loading status and clear potentially outdated owner/spaces list
|
887 |
+
yield gr.update(value=_status), gr.update(value=""), gr.update(value="*Listing spaces...*")
|
888 |
+
|
889 |
+
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
890 |
+
if token_err:
|
891 |
+
_status = f"Detection Error: {token_err}"
|
892 |
+
yield gr.update(value=_status), gr.update(), gr.update(value="*Could not list spaces due to token error.*")
|
893 |
+
return
|
894 |
+
|
895 |
+
try:
|
896 |
+
user_info = build_logic_whoami(token=token)
|
897 |
+
owner_name = user_info.get('name')
|
898 |
+
if not owner_name: raise Exception("Could not find user name from token.")
|
899 |
+
_status = f"User detected: {owner_name}"
|
900 |
+
owner_update = gr.update(value=owner_name)
|
901 |
+
|
902 |
+
# Immediately trigger listing spaces using the detected owner
|
903 |
+
list_spaces_md, list_err = build_logic_list_user_spaces(hf_api_key=token, owner=owner_name)
|
904 |
+
if list_err:
|
905 |
+
list_spaces_update = gr.update(value=f"List Spaces Error: {list_err}")
|
906 |
+
_status += f" | List Spaces Error: {list_err}"
|
907 |
+
else:
|
908 |
+
list_spaces_update = gr.update(value=list_spaces_md)
|
909 |
+
|
910 |
+
yield gr.update(value=_status), owner_update, list_spaces_update
|
911 |
+
|
912 |
+
except Exception as e:
|
913 |
+
_status = f"Detection Error: Could not detect user from token: {e}"
|
914 |
+
print(f"Error in handle_detect_user: {e}")
|
915 |
+
import traceback
|
916 |
+
traceback.print_exc()
|
917 |
+
yield gr.update(value=_status), gr.update(), gr.update(value="*Could not list spaces due to user detection error.*")
|
918 |
+
|
919 |
+
|
920 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
921 |
global parsed_code_blocks_state_cache
|
922 |
_formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
|
|
|
925 |
_changeset_clear = []
|
926 |
_changeset_summary_clear = "*No changes proposed.*"
|
927 |
_confirm_ui_hidden = gr.update(visible=False)
|
928 |
+
_list_spaces_display_clear = gr.update() # Don't clear list spaces display on load, it's controlled by its own button
|
929 |
+
_target_owner_clear, _target_space_clear, _target_private_clear = gr.update(value=""), gr.update(value=""), gr.update(value=False)
|
930 |
|
931 |
|
932 |
yield (
|
|
|
935 |
_iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
|
936 |
gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
|
937 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
938 |
+
_list_spaces_display_clear, _target_owner_clear, _target_space_clear, _target_private_clear
|
939 |
)
|
940 |
|
941 |
owner_to_use = ui_owner_name
|
|
|
973 |
gr.update(visible=False, choices=[], value=None),
|
974 |
gr.update(), gr.update(),
|
975 |
gr.update(value=None, visible=False),
|
976 |
+
_download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
977 |
)
|
978 |
return
|
979 |
|
|
|
1007 |
file_browser_update,
|
1008 |
gr.update(), gr.update(),
|
1009 |
iframe_update,
|
1010 |
+
_download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1011 |
)
|
1012 |
|
1013 |
def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, is_private_ui, formatted_markdown_content):
|
|
|
1042 |
yield gr.update(value=_build_status),
|
1043 |
return
|
1044 |
|
|
|
|
|
|
|
1045 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
1046 |
if token_err:
|
1047 |
_build_status = f"Build Error: API Token Error: {token_err}"
|
|
|
1052 |
|
1053 |
build_status_messages = []
|
1054 |
try:
|
|
|
1055 |
api.create_repo(repo_id=repo_id_target, repo_type="space", space_sdk=space_sdk_select, private=is_private_ui, exist_ok=True)
|
1056 |
build_status_messages.append(f"CREATE_SPACE: Successfully created or ensured space [{repo_id_target}](https://huggingface.co/spaces/{repo_id_target}) exists.")
|
1057 |
except HfHubHTTPError as e_http:
|
1058 |
build_status_messages.append(f"CREATE_SPACE HTTP Error ({e_http.response.status_code if e_http.response else 'N/A'}): {e_http.response.text if e_http.response else str(e_http)}. Check logs.")
|
1059 |
if e_http.response and e_http.response.status_code in (401, 403):
|
1060 |
+
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), return
|
1061 |
except Exception as e:
|
1062 |
build_status_messages.append(f"CREATE_SPACE Error: {e}")
|
1063 |
+
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), return
|
1064 |
|
1065 |
|
|
|
1066 |
file_changes_only_changeset = [{"type": "CREATE_FILE", "path": f["path"], "content": f["content"], "lang": _infer_lang_from_filename(f["path"])} for f in proposed_files_list]
|
1067 |
if file_changes_only_changeset:
|
1068 |
file_commit_status = apply_staged_file_changes(hf_api_key_ui, ui_owner_name_part, ui_space_name_part, file_changes_only_changeset)
|
|
|
1084 |
gr.update(value=_build_status), _iframe_html, _file_browser_update,
|
1085 |
gr.update(value=owner_to_use), gr.update(value=space_to_use),
|
1086 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
1087 |
+
gr.update(value=_formatted_md), gr.update(value=_detected_preview_val), _download_btn_update, gr.update()
|
1088 |
)
|
1089 |
|
1090 |
sdk_built, file_list, err_list = get_space_repository_info(hf_api_key_ui, space_to_use, owner_to_use)
|
|
|
1212 |
status_details, err = get_space_runtime_status(hf_api_key_ui, ui_space_name, ui_owner_name)
|
1213 |
repo_info = None
|
1214 |
repo_info_err = None
|
1215 |
+
if not err:
|
1216 |
sdk, file_list, repo_info_err = get_space_repository_info(hf_api_key_ui, ui_space_name, ui_owner_name)
|
1217 |
if not repo_info_err:
|
1218 |
repo_info = {"sdk": sdk, "file_count": len(file_list) if file_list is not None else 'N/A'}
|
|
|
1287 |
return "Duplicate Error: Target Owner and Target Space Name are required.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1288 |
if "/" in target_space_name:
|
1289 |
return "Duplicate Error: Target Space Name should not contain '/'. Use Target Owner field for the owner part.", gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1290 |
+
|
1291 |
|
1292 |
source_repo_id = f"{source_owner}/{source_space_name}"
|
1293 |
target_repo_id = f"{target_owner}/{target_space_name}"
|
1294 |
|
1295 |
status_msg = f"Attempting to duplicate `{source_repo_id}` to `{target_repo_id}`..."
|
|
|
1296 |
yield status_msg, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1297 |
|
1298 |
|
|
|
1300 |
status_msg = f"Duplication Result: {result_message}"
|
1301 |
|
1302 |
_status_reload = f"{status_msg} | Attempting to load the new Space [{target_repo_id}]..."
|
1303 |
+
yield gr.update(value=_status_reload)
|
1304 |
|
1305 |
new_owner = target_owner
|
1306 |
new_space_name = target_space_name
|
|
|
1309 |
|
1310 |
if err_list:
|
1311 |
reload_error = f"Error reloading file list after duplication: {err_list}"
|
1312 |
+
global parsed_code_blocks_state_cache
|
1313 |
parsed_code_blocks_state_cache = []
|
1314 |
_file_browser_update = gr.update(visible=False, choices=[], value=None)
|
1315 |
_iframe_html_update = gr.update(value=None, visible=False)
|
|
|
1321 |
is_binary = lang == "binary" or (err_get is not None)
|
1322 |
code = f"[Error loading content: {err_get}]" if err_get else (content or "")
|
1323 |
loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
|
1324 |
+
global parsed_code_blocks_state_cache
|
1325 |
parsed_code_blocks_state_cache = loaded_files
|
1326 |
|
1327 |
_file_browser_update = gr.update(visible=True, choices=sorted([f["filename"] for f in parsed_code_blocks_state_cache if not f.get("is_structure_block")] or []), value=None)
|
|
|
1387 |
owner_name_input = gr.Textbox(label="HF Owner Name", placeholder="e.g., your-username")
|
1388 |
space_name_input = gr.Textbox(label="HF Space Name", value="")
|
1389 |
load_space_button = gr.Button("🔄 Load Existing Space", variant="secondary")
|
1390 |
+
detect_user_button = gr.Button("👤 Detect User from Token", variant="secondary")
|
1391 |
+
|
1392 |
|
1393 |
with gr.Accordion("🤖 AI Model Settings", open=True):
|
1394 |
available_providers = get_available_providers()
|
|
|
1501 |
confirm_outputs = [
|
1502 |
status_output, formatted_space_output_display, detected_files_preview, download_button,
|
1503 |
confirm_accordion, confirm_button, cancel_button, changeset_state, changeset_display,
|
1504 |
+
owner_name_input, space_name_input, file_browser_dropdown, space_iframe_display, space_runtime_status_display
|
1505 |
]
|
1506 |
confirm_button.click(handle_confirm_changes, inputs=confirm_inputs, outputs=confirm_outputs)
|
1507 |
|
|
|
1517 |
space_iframe_display, download_button, build_status_display,
|
1518 |
edit_status_display, space_runtime_status_display,
|
1519 |
changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
|
1520 |
+
list_spaces_display, target_owner_input, target_space_name_input, target_private_checkbox
|
1521 |
]
|
1522 |
load_space_button.click(
|
1523 |
fn=handle_load_existing_space,
|
|
|
1525 |
outputs=load_space_outputs
|
1526 |
)
|
1527 |
|
1528 |
+
detect_user_outputs = [status_output, owner_name_input, list_spaces_display]
|
1529 |
+
detect_user_button.click(fn=handle_detect_user, inputs=[hf_api_key_input, owner_name_input], outputs=detect_user_outputs)
|
1530 |
+
|
1531 |
build_outputs = [
|
1532 |
build_status_display, space_iframe_display, file_browser_dropdown,
|
1533 |
owner_name_input, space_name_input,
|
|
|
1567 |
|
1568 |
|
1569 |
if __name__ == "__main__":
|
1570 |
+
demo.launch(debug=False, mcp_server=True)
|