Update app.py
Browse files
app.py
CHANGED
@@ -24,7 +24,7 @@ from build_logic import (
|
|
24 |
build_logic_create_pull_request,
|
25 |
build_logic_add_comment,
|
26 |
build_logic_like_space,
|
27 |
-
build_logic_create_space
|
28 |
)
|
29 |
|
30 |
from model_logic import (
|
@@ -95,10 +95,10 @@ If no code or actions are requested, respond conversationally and help the user
|
|
95 |
|
96 |
def escape_html_for_markdown(text):
|
97 |
if not isinstance(text, str): return ""
|
98 |
-
return text.replace("&", "&
|
99 |
|
100 |
def _infer_lang_from_filename(filename):
|
101 |
-
if not filename: return "plaintext"
|
102 |
if '.' in filename:
|
103 |
ext = filename.split('.')[-1].lower()
|
104 |
mapping = {
|
@@ -110,17 +110,17 @@ def _infer_lang_from_filename(filename):
|
|
110 |
'c': 'c', 'h': 'c', 'cpp': 'cpp', 'hpp': 'cpp', 'cs': 'csharp', 'java': 'java',
|
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': '
|
114 |
-
'csv': '
|
115 |
-
'.env': '
|
116 |
'makefile': 'makefile',
|
117 |
}
|
118 |
-
return mapping.get(ext, "
|
119 |
base_filename = os.path.basename(filename)
|
120 |
if base_filename == 'Dockerfile': return 'docker'
|
121 |
if base_filename == 'Makefile': return 'makefile'
|
122 |
-
if base_filename.startswith('.'): return 'plaintext
|
123 |
-
return "
|
124 |
|
125 |
def _clean_filename(filename_line_content):
|
126 |
text = filename_line_content.strip()
|
@@ -141,7 +141,7 @@ def _parse_and_update_state_cache(latest_bot_message_content, current_files_stat
|
|
141 |
|
142 |
structure_match = structure_pattern.search(content)
|
143 |
if structure_match:
|
144 |
-
structure_block_state = {"filename": "File Structure (from AI)", "language": structure_match.group("struct_lang") or "
|
145 |
|
146 |
current_message_proposed_filenames = []
|
147 |
for match in file_pattern.finditer(content):
|
@@ -200,7 +200,7 @@ def _export_selected_logic(selected_filenames, space_line_name_for_md, parsed_bl
|
|
200 |
if block.get('is_binary') or content.startswith(("[Binary file", "[Error loading content:", "[Binary or Skipped file]")):
|
201 |
output_lines.append(content)
|
202 |
else:
|
203 |
-
lang = block.get('language', '
|
204 |
output_lines.extend([f"{bbb}{lang}", content, bbb])
|
205 |
output_lines.append("")
|
206 |
exported_content_count += 1
|
@@ -257,7 +257,7 @@ def _generate_ui_outputs_from_cache(owner, space_name):
|
|
257 |
if block.get('is_binary') or content.startswith(("[Binary file", "[Error loading content:", "[Binary or Skipped file]")):
|
258 |
preview_md_lines.append(f"\n`{escape_html_for_markdown(content.strip())}`\n")
|
259 |
else:
|
260 |
-
lang = block.get('language', '
|
261 |
preview_md_lines.append(f"\n{bbb}{lang}\n{content.strip()}\n{bbb}\n")
|
262 |
preview_md_val = "\n".join(preview_md_lines)
|
263 |
|
@@ -265,7 +265,7 @@ def _generate_ui_outputs_from_cache(owner, space_name):
|
|
265 |
|
266 |
def generate_and_stage_changes(ai_response_content, current_files_state, hf_owner_name, hf_repo_name):
|
267 |
changeset = []
|
268 |
-
current_files_dict = {f["filename"]: f for f in current_files_state if not f.get("is_structure_block")}
|
269 |
ai_parsed_md = build_logic_parse_markdown(ai_response_content)
|
270 |
ai_proposed_files_list = ai_parsed_md.get("files", [])
|
271 |
ai_proposed_files_dict = {f["path"]: f for f in ai_proposed_files_list}
|
@@ -411,6 +411,7 @@ def generate_and_stage_changes(ai_response_content, current_files_state, hf_owne
|
|
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"]
|
@@ -593,8 +594,8 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
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 |
-
#
|
597 |
-
yield
|
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.")
|
@@ -614,7 +615,7 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
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)
|
@@ -683,6 +684,7 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
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)
|
@@ -707,6 +709,7 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
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 |
parsed_code_blocks_state_cache = []
|
711 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
712 |
owner_update = gr.update(value="")
|
@@ -741,10 +744,8 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
741 |
if change['type'] == 'CREATE_SPACE':
|
742 |
repo_id_to_create = change.get('repo_id')
|
743 |
if repo_id_to_create:
|
744 |
-
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))
|
745 |
action_status_messages.append(f"CREATE_SPACE: {msg}")
|
746 |
-
# If creation was successful, potentially update the UI fields? No, AI might create a *different* space.
|
747 |
-
# Just report success/failure. The AI can propose loading it next.
|
748 |
else:
|
749 |
action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
|
750 |
elif change['type'] == 'SET_PRIVACY':
|
@@ -805,7 +806,7 @@ def handle_confirm_changes(hf_api_key, owner_name, space_name, changeset):
|
|
805 |
if not final_overall_status: final_overall_status = "No operations were applied."
|
806 |
|
807 |
_status_reload = f"{final_overall_status} | Reloading Space state..."
|
808 |
-
yield gr.update(value=_status_reload)
|
809 |
|
810 |
refreshed_file_list = []
|
811 |
reload_error = None
|
@@ -880,15 +881,17 @@ def update_models_dropdown(provider_select):
|
|
880 |
selected_value = default_model if default_model in models else (models[0] if models else None)
|
881 |
return gr.update(choices=models, value=selected_value)
|
882 |
|
883 |
-
def
|
884 |
_status = "Detecting user from token..."
|
885 |
-
# Yield initial state
|
886 |
-
yield gr.update(value=_status), gr.update(value=""), gr.update(value="*Listing spaces...*")
|
|
|
887 |
|
888 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
889 |
if token_err:
|
890 |
_status = f"Detection Error: {token_err}"
|
891 |
-
|
|
|
892 |
return
|
893 |
|
894 |
try:
|
@@ -898,7 +901,6 @@ def handle_detect_user(hf_api_key_ui, owner_name_ui):
|
|
898 |
_status = f"User detected: {owner_name}"
|
899 |
owner_update = gr.update(value=owner_name)
|
900 |
|
901 |
-
# Immediately trigger listing spaces using the detected owner
|
902 |
list_spaces_md, list_err = build_logic_list_user_spaces(hf_api_key=token, owner=owner_name)
|
903 |
if list_err:
|
904 |
list_spaces_update = gr.update(value=f"List Spaces Error: {list_err}")
|
@@ -906,14 +908,15 @@ def handle_detect_user(hf_api_key_ui, owner_name_ui):
|
|
906 |
else:
|
907 |
list_spaces_update = gr.update(value=list_spaces_md)
|
908 |
|
909 |
-
yield gr.update(value=_status), owner_update, list_spaces_update
|
|
|
910 |
|
911 |
except Exception as e:
|
912 |
_status = f"Detection Error: Could not detect user from token: {e}"
|
913 |
print(f"Error in handle_detect_user: {e}")
|
914 |
import traceback
|
915 |
traceback.print_exc()
|
916 |
-
yield gr.update(value=_status), gr.update(), gr.update(value="*Could not list spaces due to user detection error.*")
|
917 |
|
918 |
|
919 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
@@ -924,7 +927,7 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
924 |
_changeset_clear = []
|
925 |
_changeset_summary_clear = "*No changes proposed.*"
|
926 |
_confirm_ui_hidden = gr.update(visible=False)
|
927 |
-
|
928 |
_target_owner_clear, _target_space_clear, _target_private_clear = gr.update(value=""), gr.update(value=""), gr.update(value=False)
|
929 |
|
930 |
|
@@ -934,34 +937,34 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
934 |
_iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
|
935 |
gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
|
936 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
937 |
-
|
938 |
)
|
939 |
|
940 |
owner_to_use = ui_owner_name
|
941 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
942 |
if token_err:
|
943 |
_status_val = f"Load Error: {token_err}"
|
944 |
-
yield gr.update(value=_status_val),
|
945 |
return
|
946 |
if not owner_to_use:
|
947 |
try:
|
948 |
user_info = build_logic_whoami(token=token)
|
949 |
owner_to_use = user_info.get('name')
|
950 |
if not owner_to_use: raise Exception("Could not find user name from token.")
|
951 |
-
yield gr.update(value=owner_to_use), gr.update(value=f"Loading Space: {owner_to_use}/{ui_space_name} (Auto-detected owner)...")
|
952 |
except Exception as e:
|
953 |
_status_val = f"Load Error: Error auto-detecting owner: {e}"
|
954 |
-
yield gr.update(value=_status_val),
|
955 |
return
|
956 |
|
957 |
if not owner_to_use or not ui_space_name:
|
958 |
_status_val = "Load Error: Owner and Space Name are required."
|
959 |
-
yield gr.update(value=_status_val),
|
960 |
return
|
961 |
|
962 |
sdk, file_list, err = get_space_repository_info(hf_api_key_ui, ui_space_name, owner_to_use)
|
963 |
|
964 |
-
yield gr.update(value=owner_to_use), gr.update(value=ui_space_name),
|
965 |
|
966 |
if err:
|
967 |
_status_val = f"Load Error: {err}"
|
@@ -972,7 +975,8 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
972 |
gr.update(visible=False, choices=[], value=None),
|
973 |
gr.update(), gr.update(),
|
974 |
gr.update(value=None, visible=False),
|
975 |
-
_download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
|
|
|
976 |
)
|
977 |
return
|
978 |
|
@@ -1006,7 +1010,8 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
1006 |
file_browser_update,
|
1007 |
gr.update(), gr.update(),
|
1008 |
iframe_update,
|
1009 |
-
_download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
|
|
|
1010 |
)
|
1011 |
|
1012 |
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):
|
@@ -1024,7 +1029,7 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1024 |
|
1025 |
if not ui_space_name_part or "/" in ui_space_name_part:
|
1026 |
_build_status = f"Build Error: Invalid Space Name '{ui_space_name_part}'."
|
1027 |
-
yield gr.update(value=_build_status),
|
1028 |
return
|
1029 |
|
1030 |
parsed_content = build_logic_parse_markdown(formatted_markdown_content)
|
@@ -1038,13 +1043,13 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1038 |
|
1039 |
if not manual_changeset:
|
1040 |
_build_status = "Build Error: No target space specified or no files parsed from markdown."
|
1041 |
-
yield gr.update(value=_build_status),
|
1042 |
return
|
1043 |
|
1044 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
1045 |
if token_err:
|
1046 |
_build_status = f"Build Error: API Token Error: {token_err}"
|
1047 |
-
yield gr.update(value=_build_status),
|
1048 |
return
|
1049 |
api = HfApi(token=token)
|
1050 |
repo_id_target = f"{ui_owner_name_part}/{ui_space_name_part}"
|
@@ -1056,10 +1061,10 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1056 |
except HfHubHTTPError as e_http:
|
1057 |
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.")
|
1058 |
if e_http.response and e_http.response.status_code in (401, 403):
|
1059 |
-
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status); return
|
1060 |
except Exception as e:
|
1061 |
build_status_messages.append(f"CREATE_SPACE Error: {e}")
|
1062 |
-
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status); return
|
1063 |
|
1064 |
|
1065 |
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]
|
@@ -1125,11 +1130,11 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
1125 |
|
1126 |
def handle_load_file_for_editing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path):
|
1127 |
if not selected_file_path:
|
1128 |
-
return "", "Select a file.", "", gr.update(language="
|
1129 |
|
1130 |
content, err = get_space_file_content(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path)
|
1131 |
if err:
|
1132 |
-
return "", f"Load Error: {err}", "", gr.update(language="
|
1133 |
|
1134 |
lang = _infer_lang_from_filename(selected_file_path)
|
1135 |
commit_msg = f"Update {selected_file_path}"
|
@@ -1172,9 +1177,9 @@ def handle_commit_file_changes(hf_api_key_ui, ui_space_name_part, ui_owner_name_
|
|
1172 |
|
1173 |
def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path):
|
1174 |
if not ui_owner_name_part or not ui_space_name_part:
|
1175 |
-
return "Delete Error: Cannot delete file. Please load a space first.", gr.update(), "", "", "
|
1176 |
if not file_to_delete_path:
|
1177 |
-
return "Delete Error: No file selected to delete.", gr.update(), "", "", "
|
1178 |
|
1179 |
status_msg = build_logic_delete_space_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path)
|
1180 |
|
@@ -1188,7 +1193,7 @@ def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, fi
|
|
1188 |
parsed_code_blocks_state_cache = [b for b in parsed_code_blocks_state_cache if b["filename"] != file_to_delete_path]
|
1189 |
file_content_editor_update = gr.update(value="")
|
1190 |
commit_message_update = gr.update(value="")
|
1191 |
-
editor_lang_update = gr.update(language="
|
1192 |
file_list, _ = list_space_files_for_browsing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part)
|
1193 |
file_browser_update = gr.update(choices=sorted(file_list or []), value=None)
|
1194 |
|
@@ -1286,7 +1291,6 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1286 |
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()
|
1287 |
if "/" in target_space_name:
|
1288 |
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()
|
1289 |
-
global parsed_code_blocks_state_cache
|
1290 |
|
1291 |
|
1292 |
source_repo_id = f"{source_owner}/{source_space_name}"
|
@@ -1300,7 +1304,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
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,6 +1313,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1309 |
|
1310 |
if err_list:
|
1311 |
reload_error = f"Error reloading file list after duplication: {err_list}"
|
|
|
1312 |
parsed_code_blocks_state_cache = []
|
1313 |
_file_browser_update = gr.update(visible=False, choices=[], value=None)
|
1314 |
_iframe_html_update = gr.update(value=None, visible=False)
|
@@ -1320,6 +1325,7 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1320 |
is_binary = lang == "binary" or (err_get is not None)
|
1321 |
code = f"[Error loading content: {err_get}]" if err_get else (content or "")
|
1322 |
loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
|
|
|
1323 |
parsed_code_blocks_state_cache = loaded_files
|
1324 |
|
1325 |
_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)
|
@@ -1345,224 +1351,4 @@ def handle_manual_duplicate_space(hf_api_key_ui, source_owner, source_space_name
|
|
1345 |
owner_update, space_update,
|
1346 |
gr.update(value=_formatted), gr.update(value=_detected), _download,
|
1347 |
_file_browser_update, _iframe_html_update, gr.update(value=runtime_status_md)
|
1348 |
-
)
|
1349 |
-
|
1350 |
-
|
1351 |
-
custom_theme = gr.themes.Base(primary_hue="teal", secondary_hue="purple", neutral_hue="zinc", text_size="sm", spacing_size="md", radius_size="sm", font=["System UI", "sans-serif"])
|
1352 |
-
custom_css = """
|
1353 |
-
body { background: linear-gradient(to bottom right, #2c3e50, #34495e); color: #ecf0f1; }
|
1354 |
-
.gradio-container { background: transparent !important; }
|
1355 |
-
.gr-box, .gr-panel, .gr-pill { background-color: rgba(44, 62, 80, 0.8) !important; border-color: rgba(189, 195, 199, 0.2) !important; }
|
1356 |
-
.gr-textbox, .gr-dropdown, .gr-button, .gr-code, .gr-chat-message { border-color: rgba(189, 195, 199, 0.3) !important; background-color: rgba(52, 73, 94, 0.9) !important; color: #ecf0f1 !important; }
|
1357 |
-
.gr-button.gr-button-primary { background-color: #1abc9c !important; color: white !important; border-color: #16a085 !important; }
|
1358 |
-
.gr-button.gr-button-secondary { background-color: #9b59b6 !important; color: white !important; border-color: #8e44ad !important; }
|
1359 |
-
.gr-button.gr-button-stop { background-color: #e74c3c !important; color: white !important; border-color: #c0392b !important; }
|
1360 |
-
.gr-markdown { background-color: rgba(44, 62, 80, 0.7) !important; padding: 10px; border-radius: 5px; overflow-x: auto; }
|
1361 |
-
.gr-markdown h1, .gr-markdown h2, .gr-markdown h3, .gr-markdown h4, .gr-markdown h5, .gr-markdown h6 { color: #ecf0f1 !important; border-bottom-color: rgba(189, 195, 199, 0.3) !important; }
|
1362 |
-
.gr-markdown pre code { background-color: rgba(52, 73, 94, 0.95) !important; border-color: rgba(189, 195, 199, 0.3) !important; }
|
1363 |
-
.gr-chatbot { background-color: rgba(44, 62, 80, 0.7) !important; border-color: rgba(189, 195, 199, 0.2) !important; }
|
1364 |
-
.gr-chatbot .message { background-color: rgba(52, 73, 94, 0.9) !important; color: #ecf0f1 !important; border-color: rgba(189, 195, 199, 0.3) !important; }
|
1365 |
-
.gr-chatbot .message.user { background-color: rgba(46, 204, 113, 0.9) !important; color: black !important; }
|
1366 |
-
.gradio-container .gr-accordion { border-color: rgba(189, 195, 199, 0.3) !important; }
|
1367 |
-
.gradio-container .gr-accordion.closed { background-color: rgba(52, 73, 94, 0.9) !important; }
|
1368 |
-
.gradio-container .gr-accordion.open { background-color: rgba(44, 62, 80, 0.8) !important; }
|
1369 |
-
|
1370 |
-
"""
|
1371 |
-
|
1372 |
-
with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
1373 |
-
changeset_state = gr.State([])
|
1374 |
-
|
1375 |
-
gr.Markdown("# 🤖 AI-Powered Hugging Face Space Commander")
|
1376 |
-
gr.Markdown("Use an AI assistant to create, modify, build, and manage your Hugging Face Spaces directly from this interface.")
|
1377 |
-
gr.Markdown("## ❗ This will cause changes to your huggingface spaces if you give it your Huggingface Key")
|
1378 |
-
gr.Markdown("⚒ Under Development - Use with Caution")
|
1379 |
-
|
1380 |
-
|
1381 |
-
with gr.Sidebar():
|
1382 |
-
with gr.Column(scale=1):
|
1383 |
-
with gr.Accordion("⚙️ Configuration", open=True):
|
1384 |
-
hf_api_key_input = gr.Textbox(label="Hugging Face Token", type="password", placeholder="hf_... (uses env var HF_TOKEN if empty)")
|
1385 |
-
owner_name_input = gr.Textbox(label="HF Owner Name", placeholder="e.g., your-username")
|
1386 |
-
space_name_input = gr.Textbox(label="HF Space Name", value="")
|
1387 |
-
load_space_button = gr.Button("🔄 Load Existing Space", variant="secondary")
|
1388 |
-
detect_user_button = gr.Button("👤 Detect User from Token", variant="secondary")
|
1389 |
-
|
1390 |
-
|
1391 |
-
with gr.Accordion("🤖 AI Model Settings", open=True):
|
1392 |
-
available_providers = get_available_providers()
|
1393 |
-
default_provider = 'Groq'
|
1394 |
-
if default_provider not in available_providers:
|
1395 |
-
default_provider = available_providers[0] if available_providers else None
|
1396 |
-
elif len(available_providers) < 3:
|
1397 |
-
default_provider = available_providers[0] if available_providers else None
|
1398 |
-
|
1399 |
-
initial_models = get_models_for_provider(default_provider) if default_provider else []
|
1400 |
-
initial_model = get_default_model_for_provider(default_provider) if default_provider else None
|
1401 |
-
if initial_model not in initial_models:
|
1402 |
-
initial_model = initial_models[0] if initial_models else None
|
1403 |
-
|
1404 |
-
provider_select = gr.Dropdown(
|
1405 |
-
label="AI Provider",
|
1406 |
-
choices=available_providers,
|
1407 |
-
value=default_provider,
|
1408 |
-
allow_custom_value=False
|
1409 |
-
)
|
1410 |
-
model_select = gr.Dropdown(
|
1411 |
-
label="AI Model",
|
1412 |
-
choices=initial_models,
|
1413 |
-
value=initial_model,
|
1414 |
-
allow_custom_value=False
|
1415 |
-
)
|
1416 |
-
provider_api_key_input = gr.Textbox(label="Model Provider API Key (Optional)", type="password", placeholder="sk_... (overrides backend settings)")
|
1417 |
-
system_prompt_input = gr.Textbox(label="System Prompt", lines=10, value=DEFAULT_SYSTEM_PROMPT, elem_id="system-prompt")
|
1418 |
-
|
1419 |
-
with gr.Accordion("🗄️ Space Management", open=True):
|
1420 |
-
gr.Markdown("### Manual Actions")
|
1421 |
-
with gr.Group():
|
1422 |
-
gr.Markdown("Duplicate Current Space To:")
|
1423 |
-
target_owner_input = gr.Textbox(label="Target Owner Name", placeholder="e.g., new-username")
|
1424 |
-
target_space_name_input = gr.Textbox(label="Target Space Name", placeholder="e.g., new-space")
|
1425 |
-
target_private_checkbox = gr.Checkbox(label="Make Target Private", value=False)
|
1426 |
-
duplicate_space_button = gr.Button("📂 Duplicate Space", variant="secondary")
|
1427 |
-
|
1428 |
-
gr.Markdown("---")
|
1429 |
-
gr.Markdown("### List Spaces")
|
1430 |
-
list_spaces_button = gr.Button("📄 List My Spaces", variant="secondary")
|
1431 |
-
list_spaces_display = gr.Markdown("*List of spaces will appear here.*")
|
1432 |
-
|
1433 |
-
|
1434 |
-
with gr.Column(scale=2):
|
1435 |
-
gr.Markdown("## 💬 AI Assistant Chat")
|
1436 |
-
chatbot_display = gr.Chatbot(label="AI Chat", height=500, bubble_full_width=False, avatar_images=(None))
|
1437 |
-
with gr.Row():
|
1438 |
-
chat_message_input = gr.Textbox(show_label=False, placeholder="Your Message...", scale=7)
|
1439 |
-
send_chat_button = gr.Button("Send", variant="primary", scale=1)
|
1440 |
-
status_output = gr.Textbox(label="Last Action Status", interactive=False, value="Ready.")
|
1441 |
-
|
1442 |
-
with gr.Accordion("📝 Proposed Changes (Pending Confirmation)", open=False, visible=False) as confirm_accordion:
|
1443 |
-
changeset_display = gr.Markdown("*No changes proposed.*")
|
1444 |
-
with gr.Row():
|
1445 |
-
confirm_button = gr.Button("✅ Confirm & Apply Changes", variant="primary", visible=False)
|
1446 |
-
cancel_button = gr.Button("❌ Cancel", variant="stop", visible=False)
|
1447 |
-
|
1448 |
-
with gr.Tabs():
|
1449 |
-
with gr.TabItem("📝 Generated Markdown & Build"):
|
1450 |
-
with gr.Row():
|
1451 |
-
with gr.Column(scale=2):
|
1452 |
-
formatted_space_output_display = gr.Textbox(label="Current Space Definition (Generated Markdown)", lines=20, interactive=True, value="*Load or create a space to see its definition.*")
|
1453 |
-
download_button = gr.DownloadButton(label="Download .md", interactive=False)
|
1454 |
-
with gr.Column(scale=1):
|
1455 |
-
gr.Markdown("### Manual Build & Status")
|
1456 |
-
space_sdk_select = gr.Dropdown(label="Space SDK", choices=["gradio", "streamlit", "docker", "static"], value="gradio", interactive=True)
|
1457 |
-
space_private_checkbox = gr.Checkbox(label="Make Space Private", value=False, interactive=True)
|
1458 |
-
build_space_button = gr.Button("🚀 Build / Update Space from Markdown", variant="primary")
|
1459 |
-
build_status_display = gr.Textbox(label="Manual Build/Update Status", interactive=False, value="*Manual build status...*")
|
1460 |
-
gr.Markdown("---")
|
1461 |
-
refresh_status_button = gr.Button("🔄 Refresh Runtime/Repo Status")
|
1462 |
-
space_runtime_status_display = gr.Markdown("*Runtime/Repo status will appear here.*")
|
1463 |
-
|
1464 |
-
with gr.TabItem("🔍 Files Preview"):
|
1465 |
-
detected_files_preview = gr.Markdown(value="*A preview of the latest file versions will appear here.*")
|
1466 |
-
|
1467 |
-
with gr.TabItem("✏️ Live File Editor & Preview"):
|
1468 |
-
with gr.Row():
|
1469 |
-
with gr.Column(scale=1):
|
1470 |
-
gr.Markdown("### Live Editor")
|
1471 |
-
file_browser_dropdown = gr.Dropdown(label="Select File in Space", choices=[], interactive=True)
|
1472 |
-
file_content_editor = gr.Code(label="File Content Editor", language="python", lines=15, interactive=True, value="")
|
1473 |
-
commit_message_input = gr.Textbox(label="Commit Message", placeholder="e.g., Updated app.py", interactive=True, value="")
|
1474 |
-
with gr.Row():
|
1475 |
-
update_file_button = gr.Button("Commit Changes", variant="primary", interactive=True)
|
1476 |
-
delete_file_button = gr.Button("🗑️ Delete Selected File", variant="stop", interactive=True)
|
1477 |
-
edit_status_display = gr.Textbox(label="File Edit/Delete Status", interactive=False, value="")
|
1478 |
-
with gr.Column(scale=1):
|
1479 |
-
gr.Markdown("### Live Space Preview")
|
1480 |
-
space_iframe_display = gr.HTML(value="", visible=True)
|
1481 |
-
|
1482 |
-
|
1483 |
-
provider_select.change(update_models_dropdown, inputs=provider_select, outputs=model_select)
|
1484 |
-
|
1485 |
-
chat_inputs = [
|
1486 |
-
chat_message_input, chatbot_display, hf_api_key_input,
|
1487 |
-
provider_api_key_input, provider_select, model_select, system_prompt_input,
|
1488 |
-
owner_name_input, space_name_input
|
1489 |
-
]
|
1490 |
-
chat_outputs = [
|
1491 |
-
chat_message_input, chatbot_display, status_output,
|
1492 |
-
detected_files_preview, formatted_space_output_display, download_button,
|
1493 |
-
changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button
|
1494 |
-
]
|
1495 |
-
send_chat_button.click(handle_chat_submit, inputs=chat_inputs, outputs=chat_outputs)
|
1496 |
-
chat_message_input.submit(handle_chat_submit, inputs=chat_inputs, outputs=chat_outputs)
|
1497 |
-
|
1498 |
-
confirm_inputs = [hf_api_key_input, owner_name_input, space_name_input, changeset_state]
|
1499 |
-
confirm_outputs = [
|
1500 |
-
status_output, formatted_space_output_display, detected_files_preview, download_button,
|
1501 |
-
confirm_accordion, confirm_button, cancel_button, changeset_state, changeset_display,
|
1502 |
-
owner_name_input, space_name_input, file_browser_dropdown, space_iframe_display, space_runtime_status_display
|
1503 |
-
]
|
1504 |
-
confirm_button.click(handle_confirm_changes, inputs=confirm_inputs, outputs=confirm_outputs)
|
1505 |
-
|
1506 |
-
cancel_outputs = [
|
1507 |
-
status_output, changeset_state, changeset_display,
|
1508 |
-
confirm_accordion, confirm_button, cancel_button
|
1509 |
-
]
|
1510 |
-
cancel_button.click(handle_cancel_changes, inputs=None, outputs=cancel_outputs)
|
1511 |
-
|
1512 |
-
load_space_outputs = [
|
1513 |
-
formatted_space_output_display, detected_files_preview, status_output,
|
1514 |
-
file_browser_dropdown, owner_name_input, space_name_input,
|
1515 |
-
space_iframe_display, download_button, build_status_display,
|
1516 |
-
edit_status_display, space_runtime_status_display,
|
1517 |
-
changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
|
1518 |
-
list_spaces_display, target_owner_input, target_space_name_input, target_private_checkbox
|
1519 |
-
]
|
1520 |
-
load_space_button.click(
|
1521 |
-
fn=handle_load_existing_space,
|
1522 |
-
inputs=[hf_api_key_input, owner_name_input, space_name_input],
|
1523 |
-
outputs=load_space_outputs
|
1524 |
-
)
|
1525 |
-
|
1526 |
-
detect_user_outputs = [status_output, owner_name_input, list_spaces_display]
|
1527 |
-
detect_user_button.click(fn=handle_detect_user, inputs=[hf_api_key_input, owner_name_input], outputs=detect_user_outputs)
|
1528 |
-
|
1529 |
-
build_outputs = [
|
1530 |
-
build_status_display, space_iframe_display, file_browser_dropdown,
|
1531 |
-
owner_name_input, space_name_input,
|
1532 |
-
changeset_state, changeset_display, confirm_accordion, confirm_button, cancel_button,
|
1533 |
-
formatted_space_output_display, detected_files_preview, download_button, space_runtime_status_display
|
1534 |
-
]
|
1535 |
-
build_inputs = [
|
1536 |
-
hf_api_key_input, space_name_input, owner_name_input, space_sdk_select,
|
1537 |
-
space_private_checkbox, formatted_space_output_display
|
1538 |
-
]
|
1539 |
-
build_space_button.click(fn=handle_build_space_button, inputs=build_inputs, outputs=build_outputs)
|
1540 |
-
|
1541 |
-
file_edit_load_outputs = [file_content_editor, edit_status_display, commit_message_input, file_content_editor]
|
1542 |
-
file_browser_dropdown.change(fn=handle_load_file_for_editing, inputs=[hf_api_key_input, space_name_input, owner_name_input, file_browser_dropdown], outputs=file_edit_load_outputs)
|
1543 |
-
|
1544 |
-
commit_file_outputs = [edit_status_display, file_browser_dropdown, formatted_space_output_display, detected_files_preview, download_button]
|
1545 |
-
update_file_button.click(fn=handle_commit_file_changes, inputs=[hf_api_key_input, space_name_input, owner_name_input, file_browser_dropdown, file_content_editor, commit_message_input], outputs=commit_file_outputs)
|
1546 |
-
|
1547 |
-
delete_file_outputs = [
|
1548 |
-
edit_status_display, file_browser_dropdown,
|
1549 |
-
file_content_editor, commit_message_input, file_content_editor,
|
1550 |
-
formatted_space_output_display, detected_files_preview, download_button
|
1551 |
-
]
|
1552 |
-
delete_file_button.click(fn=handle_delete_file, inputs=[hf_api_key_input, space_name_input, owner_name_input, file_browser_dropdown], outputs=delete_file_outputs)
|
1553 |
-
|
1554 |
-
refresh_status_button.click(fn=handle_refresh_space_status, inputs=[hf_api_key_input, owner_name_input, space_name_input], outputs=[space_runtime_status_display])
|
1555 |
-
|
1556 |
-
manual_duplicate_inputs = [hf_api_key_input, owner_name_input, space_name_input, target_owner_input, target_space_name_input, target_private_checkbox]
|
1557 |
-
manual_duplicate_outputs = [
|
1558 |
-
status_output, owner_name_input, space_name_input,
|
1559 |
-
formatted_space_output_display, detected_files_preview, download_button,
|
1560 |
-
file_browser_dropdown, space_iframe_display, space_runtime_status_display
|
1561 |
-
]
|
1562 |
-
duplicate_space_button.click(fn=handle_manual_duplicate_space, inputs=manual_duplicate_inputs, outputs=manual_duplicate_outputs)
|
1563 |
-
|
1564 |
-
list_spaces_button.click(fn=handle_list_spaces, inputs=[hf_api_key_input, owner_name_input], outputs=[list_spaces_display])
|
1565 |
-
|
1566 |
-
|
1567 |
-
if __name__ == "__main__":
|
1568 |
-
demo.launch(debug=False, mcp_server=True)
|
|
|
24 |
build_logic_create_pull_request,
|
25 |
build_logic_add_comment,
|
26 |
build_logic_like_space,
|
27 |
+
build_logic_create_space
|
28 |
)
|
29 |
|
30 |
from model_logic import (
|
|
|
95 |
|
96 |
def escape_html_for_markdown(text):
|
97 |
if not isinstance(text, str): return ""
|
98 |
+
return text.replace("&", "&").replace("<", "<").replace(">", ">")
|
99 |
|
100 |
def _infer_lang_from_filename(filename):
|
101 |
+
if not filename: return "text" # Changed from "plaintext" to "text" for gr.Code compatibility
|
102 |
if '.' in filename:
|
103 |
ext = filename.split('.')[-1].lower()
|
104 |
mapping = {
|
|
|
110 |
'c': 'c', 'h': 'c', 'cpp': 'cpp', 'hpp': 'cpp', 'cs': 'csharp', 'java': 'java',
|
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': 'text', 'log': 'text', 'ini': 'ini', 'conf': 'text', 'cfg': 'text', # Changed plaintext to text
|
114 |
+
'csv': 'text', 'tsv': 'text', 'err': 'text', # Changed plaintext to text
|
115 |
+
'.env': 'text', '.gitignore': 'text', '.npmrc': 'text', '.gitattributes': 'text', # Changed plaintext to text
|
116 |
'makefile': 'makefile',
|
117 |
}
|
118 |
+
return mapping.get(ext, "text") # Changed default to text
|
119 |
base_filename = os.path.basename(filename)
|
120 |
if base_filename == 'Dockerfile': return 'docker'
|
121 |
if base_filename == 'Makefile': return 'makefile'
|
122 |
+
if base_filename.startswith('.'): return 'text' # Changed plaintext to text
|
123 |
+
return "text" # Changed default to text
|
124 |
|
125 |
def _clean_filename(filename_line_content):
|
126 |
text = filename_line_content.strip()
|
|
|
141 |
|
142 |
structure_match = structure_pattern.search(content)
|
143 |
if structure_match:
|
144 |
+
structure_block_state = {"filename": "File Structure (from AI)", "language": structure_match.group("struct_lang") or "text", "code": structure_match.group("structure_code").strip(), "is_binary": False, "is_structure_block": True} # Changed plaintext to text
|
145 |
|
146 |
current_message_proposed_filenames = []
|
147 |
for match in file_pattern.finditer(content):
|
|
|
200 |
if block.get('is_binary') or content.startswith(("[Binary file", "[Error loading content:", "[Binary or Skipped file]")):
|
201 |
output_lines.append(content)
|
202 |
else:
|
203 |
+
lang = block.get('language', 'text') or 'text' # Changed plaintext to text
|
204 |
output_lines.extend([f"{bbb}{lang}", content, bbb])
|
205 |
output_lines.append("")
|
206 |
exported_content_count += 1
|
|
|
257 |
if block.get('is_binary') or content.startswith(("[Binary file", "[Error loading content:", "[Binary or Skipped file]")):
|
258 |
preview_md_lines.append(f"\n`{escape_html_for_markdown(content.strip())}`\n")
|
259 |
else:
|
260 |
+
lang = block.get('language', 'text') or 'text' # Changed plaintext to text
|
261 |
preview_md_lines.append(f"\n{bbb}{lang}\n{content.strip()}\n{bbb}\n")
|
262 |
preview_md_val = "\n".join(preview_md_lines)
|
263 |
|
|
|
265 |
|
266 |
def generate_and_stage_changes(ai_response_content, current_files_state, hf_owner_name, hf_repo_name):
|
267 |
changeset = []
|
268 |
+
current_files_dict = {f["filename"]: f.copy() for f in current_files_state if not f.get("is_structure_block")}
|
269 |
ai_parsed_md = build_logic_parse_markdown(ai_response_content)
|
270 |
ai_proposed_files_list = ai_parsed_md.get("files", [])
|
271 |
ai_proposed_files_dict = {f["path"]: f for f in ai_proposed_files_list}
|
|
|
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 |
+
|
415 |
for file_info in ai_proposed_files_list:
|
416 |
filename = file_info["path"]
|
417 |
proposed_content = file_info["content"]
|
|
|
594 |
|
595 |
_status = "Applying changes..."
|
596 |
yield _status, gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="*Applying changes...*")
|
597 |
+
# Explicitly yield None for the other 15 outputs initially
|
598 |
+
yield None, None, None, None, None, None, None, None, None, None, None, None, None, None, None # Ensure 16 None values
|
599 |
|
600 |
if not changeset:
|
601 |
return "No changes to apply.", gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value="No changes were staged.")
|
|
|
615 |
private=change.get("private", False)
|
616 |
)
|
617 |
_status_reload = f"{status_message} | Attempting to load the new Space [{change['target_repo_id']}]..."
|
618 |
+
yield gr.update(value=_status_reload), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
619 |
|
620 |
target_repo_id = change['target_repo_id']
|
621 |
new_owner, new_space_name = target_repo_id.split('/', 1) if '/' in target_repo_id else (None, target_repo_id)
|
|
|
684 |
gr.update(value="*Runtime status unavailable for new space.*")
|
685 |
)
|
686 |
|
687 |
+
|
688 |
else:
|
689 |
final_overall_status = "Duplicate Action Error: Invalid duplicate changeset format."
|
690 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(owner_name, space_name)
|
|
|
709 |
status_message = build_logic_delete_space(hf_api_key, delete_owner, delete_space)
|
710 |
final_overall_status = status_message
|
711 |
if "Successfully" in status_message:
|
712 |
+
global parsed_code_blocks_state_cache
|
713 |
parsed_code_blocks_state_cache = []
|
714 |
_formatted, _detected, _download = _generate_ui_outputs_from_cache(None, None)
|
715 |
owner_update = gr.update(value="")
|
|
|
744 |
if change['type'] == 'CREATE_SPACE':
|
745 |
repo_id_to_create = change.get('repo_id')
|
746 |
if repo_id_to_create:
|
747 |
+
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))
|
748 |
action_status_messages.append(f"CREATE_SPACE: {msg}")
|
|
|
|
|
749 |
else:
|
750 |
action_status_messages.append("CREATE_SPACE Error: Target repo_id not specified.")
|
751 |
elif change['type'] == 'SET_PRIVACY':
|
|
|
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), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # Yield status
|
810 |
|
811 |
refreshed_file_list = []
|
812 |
reload_error = None
|
|
|
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_and_list_spaces(hf_api_key_ui):
|
885 |
_status = "Detecting user from token..."
|
886 |
+
# Yield initial state: update status, clear owner/space fields, clear list display, clear duplicate fields
|
887 |
+
yield (gr.update(value=_status), gr.update(value=""), gr.update(value=""), gr.update(value="*Listing spaces...*"), gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False), gr.update(value=""), gr.update(value=""), gr.update(value="*Manual build status...*"), gr.update(value="*Select a file...*"), gr.update(value="*Runtime/Repo status will appear here.*"), [], gr.update(value="*No changes proposed.*"), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value=""), gr.update(value=""), gr.update(value=False))
|
888 |
+
|
889 |
|
890 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
891 |
if token_err:
|
892 |
_status = f"Detection Error: {token_err}"
|
893 |
+
# Yield error status, keep fields cleared, update list display error
|
894 |
+
yield (gr.update(value=_status), gr.update(), gr.update(), gr.update(value="*Could not list spaces due to token error.*"), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
|
895 |
return
|
896 |
|
897 |
try:
|
|
|
901 |
_status = f"User detected: {owner_name}"
|
902 |
owner_update = gr.update(value=owner_name)
|
903 |
|
|
|
904 |
list_spaces_md, list_err = build_logic_list_user_spaces(hf_api_key=token, owner=owner_name)
|
905 |
if list_err:
|
906 |
list_spaces_update = gr.update(value=f"List Spaces Error: {list_err}")
|
|
|
908 |
else:
|
909 |
list_spaces_update = gr.update(value=list_spaces_md)
|
910 |
|
911 |
+
yield (gr.update(value=_status), owner_update, gr.update(value=""), list_spaces_update, gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False), gr.update(value=""), gr.update(value=""), gr.update(value="*Manual build status...*"), gr.update(value="*Select a file...*"), gr.update(value="*Runtime/Repo status will appear here.*"), [], gr.update(value="*No changes proposed.*"), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value=""), gr.update(value=""), gr.update(value=False))
|
912 |
+
|
913 |
|
914 |
except Exception as e:
|
915 |
_status = f"Detection Error: Could not detect user from token: {e}"
|
916 |
print(f"Error in handle_detect_user: {e}")
|
917 |
import traceback
|
918 |
traceback.print_exc()
|
919 |
+
yield (gr.update(value=_status), gr.update(), gr.update(), gr.update(value="*Could not list spaces due to user detection error.*"), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
|
920 |
|
921 |
|
922 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
|
927 |
_changeset_clear = []
|
928 |
_changeset_summary_clear = "*No changes proposed.*"
|
929 |
_confirm_ui_hidden = gr.update(visible=False)
|
930 |
+
_list_spaces_display_placeholder = "*List of spaces will appear here.*"
|
931 |
_target_owner_clear, _target_space_clear, _target_private_clear = gr.update(value=""), gr.update(value=""), gr.update(value=False)
|
932 |
|
933 |
|
|
|
937 |
_iframe_html_update, _download_btn_update, gr.update(value=_build_status_clear),
|
938 |
gr.update(value=_edit_status_clear), gr.update(value=_runtime_status_clear),
|
939 |
_changeset_clear, gr.update(value=_changeset_summary_clear), _confirm_ui_hidden, _confirm_ui_hidden, _confirm_ui_hidden,
|
940 |
+
gr.update(value=_list_spaces_display_placeholder), _target_owner_clear, _target_space_clear, _target_private_clear
|
941 |
)
|
942 |
|
943 |
owner_to_use = ui_owner_name
|
944 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
945 |
if token_err:
|
946 |
_status_val = f"Load Error: {token_err}"
|
947 |
+
yield (gr.update(value=_status_val), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
|
948 |
return
|
949 |
if not owner_to_use:
|
950 |
try:
|
951 |
user_info = build_logic_whoami(token=token)
|
952 |
owner_to_use = user_info.get('name')
|
953 |
if not owner_to_use: raise Exception("Could not find user name from token.")
|
954 |
+
yield gr.update(value=owner_to_use), gr.update(value=f"Loading Space: {owner_to_use}/{ui_space_name} (Auto-detected owner)..."), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
955 |
except Exception as e:
|
956 |
_status_val = f"Load Error: Error auto-detecting owner: {e}"
|
957 |
+
yield (gr.update(value=_status_val), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
|
958 |
return
|
959 |
|
960 |
if not owner_to_use or not ui_space_name:
|
961 |
_status_val = "Load Error: Owner and Space Name are required."
|
962 |
+
yield (gr.update(value=_status_val), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update())
|
963 |
return
|
964 |
|
965 |
sdk, file_list, err = get_space_repository_info(hf_api_key_ui, ui_space_name, owner_to_use)
|
966 |
|
967 |
+
yield gr.update(value=owner_to_use), gr.update(value=ui_space_name), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
968 |
|
969 |
if err:
|
970 |
_status_val = f"Load Error: {err}"
|
|
|
975 |
gr.update(visible=False, choices=[], value=None),
|
976 |
gr.update(), gr.update(),
|
977 |
gr.update(value=None, visible=False),
|
978 |
+
_download, gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
|
979 |
+
gr.update(value=_list_spaces_display_placeholder), gr.update(value=""), gr.update(value=""), gr.update(value=False)
|
980 |
)
|
981 |
return
|
982 |
|
|
|
1010 |
file_browser_update,
|
1011 |
gr.update(), gr.update(),
|
1012 |
iframe_update,
|
1013 |
+
_download, gr.update(), gr.update(), gr.update(value=runtime_status_md), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
|
1014 |
+
gr.update(value=_list_spaces_display_placeholder), gr.update(value=""), gr.update(value=""), gr.update(value=False)
|
1015 |
)
|
1016 |
|
1017 |
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):
|
|
|
1029 |
|
1030 |
if not ui_space_name_part or "/" in ui_space_name_part:
|
1031 |
_build_status = f"Build Error: Invalid Space Name '{ui_space_name_part}'."
|
1032 |
+
yield gr.update(value=_build_status), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1033 |
return
|
1034 |
|
1035 |
parsed_content = build_logic_parse_markdown(formatted_markdown_content)
|
|
|
1043 |
|
1044 |
if not manual_changeset:
|
1045 |
_build_status = "Build Error: No target space specified or no files parsed from markdown."
|
1046 |
+
yield gr.update(value=_build_status), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1047 |
return
|
1048 |
|
1049 |
token, token_err = build_logic_get_api_token(hf_api_key_ui)
|
1050 |
if token_err:
|
1051 |
_build_status = f"Build Error: API Token Error: {token_err}"
|
1052 |
+
yield gr.update(value=_build_status), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1053 |
return
|
1054 |
api = HfApi(token=token)
|
1055 |
repo_id_target = f"{ui_owner_name_part}/{ui_space_name_part}"
|
|
|
1061 |
except HfHubHTTPError as e_http:
|
1062 |
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.")
|
1063 |
if e_http.response and e_http.response.status_code in (401, 403):
|
1064 |
+
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(); return
|
1065 |
except Exception as e:
|
1066 |
build_status_messages.append(f"CREATE_SPACE Error: {e}")
|
1067 |
+
_build_status = f"Build Error: {build_status_messages[-1]}. Cannot proceed with file upload."; yield gr.update(value=_build_status), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(); return
|
1068 |
|
1069 |
|
1070 |
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]
|
|
|
1130 |
|
1131 |
def handle_load_file_for_editing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path):
|
1132 |
if not selected_file_path:
|
1133 |
+
return "", "Select a file.", "", gr.update(language="text") # Changed plaintext to text
|
1134 |
|
1135 |
content, err = get_space_file_content(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path)
|
1136 |
if err:
|
1137 |
+
return "", f"Load Error: {err}", "", gr.update(language="text") # Changed plaintext to text
|
1138 |
|
1139 |
lang = _infer_lang_from_filename(selected_file_path)
|
1140 |
commit_msg = f"Update {selected_file_path}"
|
|
|
1177 |
|
1178 |
def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path):
|
1179 |
if not ui_owner_name_part or not ui_space_name_part:
|
1180 |
+
return "Delete Error: Cannot delete file. Please load a space first.", gr.update(), "", "", "text", gr.update(), gr.update(), gr.update() # Changed plaintext to text
|
1181 |
if not file_to_delete_path:
|
1182 |
+
return "Delete Error: No file selected to delete.", gr.update(), "", "", "text", gr.update(), gr.update(), gr.update() # Changed plaintext to text
|
1183 |
|
1184 |
status_msg = build_logic_delete_space_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path)
|
1185 |
|
|
|
1193 |
parsed_code_blocks_state_cache = [b for b in parsed_code_blocks_state_cache if b["filename"] != file_to_delete_path]
|
1194 |
file_content_editor_update = gr.update(value="")
|
1195 |
commit_message_update = gr.update(value="")
|
1196 |
+
editor_lang_update = gr.update(language="text") # Changed plaintext to text
|
1197 |
file_list, _ = list_space_files_for_browsing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part)
|
1198 |
file_browser_update = gr.update(choices=sorted(file_list or []), value=None)
|
1199 |
|
|
|
1291 |
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()
|
1292 |
if "/" in target_space_name:
|
1293 |
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()
|
|
|
1294 |
|
1295 |
|
1296 |
source_repo_id = f"{source_owner}/{source_space_name}"
|
|
|
1304 |
status_msg = f"Duplication Result: {result_message}"
|
1305 |
|
1306 |
_status_reload = f"{status_msg} | Attempting to load the new Space [{target_repo_id}]..."
|
1307 |
+
yield gr.update(value=_status_reload), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
1308 |
|
1309 |
new_owner = target_owner
|
1310 |
new_space_name = target_space_name
|
|
|
1313 |
|
1314 |
if err_list:
|
1315 |
reload_error = f"Error reloading file list after duplication: {err_list}"
|
1316 |
+
global parsed_code_blocks_state_cache
|
1317 |
parsed_code_blocks_state_cache = []
|
1318 |
_file_browser_update = gr.update(visible=False, choices=[], value=None)
|
1319 |
_iframe_html_update = gr.update(value=None, visible=False)
|
|
|
1325 |
is_binary = lang == "binary" or (err_get is not None)
|
1326 |
code = f"[Error loading content: {err_get}]" if err_get else (content or "")
|
1327 |
loaded_files.append({"filename": file_path, "code": code, "language": lang, "is_binary": is_binary, "is_structure_block": False})
|
1328 |
+
global parsed_code_blocks_state_cache
|
1329 |
parsed_code_blocks_state_cache = loaded_files
|
1330 |
|
1331 |
_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)
|
|
|
1351 |
owner_update, space_update,
|
1352 |
gr.update(value=_formatted), gr.update(value=_detected), _download,
|
1353 |
_file_browser_update, _iframe_html_update, gr.update(value=runtime_status_md)
|
1354 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|