Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -35,6 +35,7 @@ print("model_logic.py loaded successfully.")
|
|
35 |
|
36 |
|
37 |
bbb = chr(96) * 3
|
|
|
38 |
parsed_code_blocks_state_cache = []
|
39 |
BOT_ROLE_NAME = "assistant"
|
40 |
# Removed GROQ_API_ENDPOINT as it's now in model_logic
|
@@ -133,6 +134,7 @@ def _parse_chat_stream_logic(latest_bot_message_content, existing_files_state=No
|
|
133 |
Parses a single bot message content string to find file blocks and updates the state.
|
134 |
Assumes existing_files_state is the current state *before* this message.
|
135 |
"""
|
|
|
136 |
latest_blocks_dict = {}
|
137 |
if existing_files_state:
|
138 |
# Copy existing blocks, except for potential structure blocks that might be overwritten
|
@@ -192,6 +194,7 @@ def _parse_chat_stream_logic(latest_bot_message_content, existing_files_state=No
|
|
192 |
|
193 |
def _export_selected_logic(selected_filenames, space_line_name_for_md, parsed_blocks_for_export):
|
194 |
# This function remains largely the same, using the provided parsed_blocks_for_export
|
|
|
195 |
results = {"output_str": "", "error_message": None, "download_filepath": None}
|
196 |
# Filter out structure blocks for file listing/export content
|
197 |
exportable_blocks_content = [b for b in parsed_blocks_for_export if not b.get("is_structure_block") and not b.get("is_binary") and not (b.get("code", "").startswith("[Error loading content:") or b.get("code", "").startswith("[Binary or Skipped file]"))]
|
@@ -276,14 +279,11 @@ def _convert_gr_history_to_api_messages(system_prompt, gr_history, current_user_
|
|
276 |
if current_user_message: messages.append({"role": "user", "content": current_user_message})
|
277 |
return messages
|
278 |
|
279 |
-
# This function is no longer needed as we process the *latest* message content directly from the stream handler
|
280 |
-
# def get_latest_bot_message_as_json(gr_history):
|
281 |
-
# ...
|
282 |
-
|
283 |
|
284 |
def _generate_ui_outputs_from_cache(owner, space_name):
|
285 |
-
#
|
286 |
global parsed_code_blocks_state_cache
|
|
|
287 |
preview_md_val = "*No files in cache to display.*"
|
288 |
formatted_md_val = f"# Space: {owner}/{space_name}\n## File Structure\n{bbb}\n📁 Root\n{bbb}\n\n*No files in cache.*" if owner or space_name else "*Load or define a Space to see its Markdown structure.*"
|
289 |
download_file = None
|
@@ -321,6 +321,7 @@ def _generate_ui_outputs_from_cache(owner, space_name):
|
|
321 |
|
322 |
# --- Refactored Chat Submit Handler ---
|
323 |
def handle_chat_submit(user_message, chat_history, api_key_input, provider_select, model_select, system_prompt, hf_owner_name, hf_repo_name, _current_formatted_markdown):
|
|
|
324 |
global parsed_code_blocks_state_cache
|
325 |
_chat_msg_in = ""
|
326 |
_chat_hist = list(chat_history)
|
@@ -346,7 +347,7 @@ def handle_chat_submit(user_message, chat_history, api_key_input, provider_selec
|
|
346 |
|
347 |
for f_info in parsed_from_md.get("files", []):
|
348 |
# Only add if it has a path and isn't the structure block representation placeholder
|
349 |
-
if f_info.get("path") and f_info["path"] != "File Structure (original)":
|
350 |
# Check if it's a binary representation string
|
351 |
is_binary_repr = isinstance(f_info.get("content"), str) and (f_info["content"].startswith("[Binary file") or f_info["content"].startswith("[Error loading content:") or f_info["content"].startswith("[Binary or Skipped file]"))
|
352 |
# Check if a block with this filename already exists in new_cache_state and replace it
|
@@ -434,14 +435,14 @@ def handle_chat_submit(user_message, chat_history, api_key_input, provider_selec
|
|
434 |
# Generate stream from model_logic
|
435 |
for chunk in generate_stream(provider_select, model_select, api_key_override, api_msgs):
|
436 |
if chunk is None: continue # Skip None chunks if any
|
437 |
-
if chunk.startswith("Error: ") or chunk.startswith("API HTTP Error"):
|
438 |
# If an error chunk is received, treat it as the final output and stop
|
439 |
full_bot_response_content = chunk
|
440 |
error_during_stream = chunk
|
441 |
break # Stop processing stream
|
442 |
else:
|
443 |
# Accumulate response and update the last message in chat_hist
|
444 |
-
full_bot_response_content += chunk
|
445 |
_chat_hist[-1] = (user_message, full_bot_response_content)
|
446 |
_status = f"Streaming from {model_select}..."
|
447 |
# Yield update immediately after receiving chunk
|
@@ -450,7 +451,7 @@ def handle_chat_submit(user_message, chat_history, api_key_input, provider_selec
|
|
450 |
# After the stream finishes or breaks
|
451 |
if error_during_stream:
|
452 |
_status = error_during_stream # Set status to the error message
|
453 |
-
elif full_bot_response_content:
|
454 |
_status = f"Streaming complete. Processing files from {model_select} response..."
|
455 |
|
456 |
# Pass the *current state* (updated from markdown at the start)
|
@@ -472,9 +473,12 @@ def handle_chat_submit(user_message, chat_history, api_key_input, provider_selec
|
|
472 |
_formatted_output_update, _detected_files_update, _download_btn_update = _generate_ui_outputs_from_cache(hf_owner_name, hf_repo_name)
|
473 |
_status = "Processing complete. Previews updated."
|
474 |
else:
|
475 |
-
# Handle cases where the stream finished but yielded no content (e.g., filter)
|
476 |
-
|
477 |
-
|
|
|
|
|
|
|
478 |
|
479 |
except Exception as e:
|
480 |
# Catch any errors that occurred *before* or *during* the stream setup/iteration
|
@@ -487,6 +491,8 @@ def handle_chat_submit(user_message, chat_history, api_key_input, provider_selec
|
|
487 |
_chat_hist.append((user_message, error_msg)) # Append as a new user/bot turn if structure unexpected
|
488 |
_status = error_msg
|
489 |
# Previews and markdown might not be affected by a generation error, keep existing state
|
|
|
|
|
490 |
|
491 |
# Final yield to update UI after all processing
|
492 |
yield (_chat_msg_in, _chat_hist, _status, _detected_files_update, _formatted_output_update, _download_btn_update)
|
@@ -499,14 +505,21 @@ def update_models_dropdown(provider_select):
|
|
499 |
return gr.update(choices=[], value=None)
|
500 |
models = get_models_for_provider(provider_select)
|
501 |
default_model = get_default_model_for_provider(provider_select)
|
502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
503 |
|
|
|
504 |
|
505 |
-
|
506 |
-
#
|
507 |
|
508 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
509 |
-
#
|
510 |
global parsed_code_blocks_state_cache
|
511 |
_formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
|
512 |
_file_browser_update, _iframe_html_update, _download_btn_update = gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False), gr.update(interactive=False, value=None)
|
@@ -621,14 +634,17 @@ def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
621 |
|
622 |
|
623 |
def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, formatted_markdown_content):
|
|
|
|
|
624 |
# ... (this function calls build_logic_create_space and refreshes file list)
|
625 |
_build_status, _iframe_html, _file_browser_update = "Starting space build process...", gr.update(value=None, visible=False), gr.update(visible=False, choices=[], value=None)
|
626 |
-
|
627 |
-
|
|
|
628 |
final_owner_for_build = ui_owner_name_part
|
629 |
if not final_owner_for_build:
|
630 |
token_for_whoami, token_err = build_logic_get_api_token(hf_api_key_ui)
|
631 |
-
if token_err: _build_status = f"Build Error: {token_err}"; yield _build_status, _iframe_html, _file_browser_update; return
|
632 |
if token_for_whoami:
|
633 |
try:
|
634 |
user_info = build_logic_whoami(token=token_for_whoami)
|
@@ -637,19 +653,18 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
637 |
except Exception as e: _build_status += f"\n(Warning: Could not auto-detect owner for build: {e}. Please specify.)"
|
638 |
else: _build_status += "\n(Warning: Owner not specified and no token to auto-detect for build. Please specify owner or provide a token.)"
|
639 |
|
640 |
-
if not final_owner_for_build: _build_status = "Build Error: HF Owner Name could not be determined. Please specify it."; yield _build_status, _iframe_html, _file_browser_update; return
|
641 |
|
642 |
# Before building, parse the markdown to ensure the cache reflects exactly what's being built
|
643 |
# This prevents inconsistencies if the user manually edited the markdown output
|
644 |
try:
|
645 |
parsed_from_md_for_build = build_logic_parse_markdown(formatted_markdown_content)
|
646 |
# Replace the global cache state with the state derived from the markdown being built
|
647 |
-
global parsed_code_blocks_state_cache
|
648 |
parsed_code_blocks_state_cache = []
|
649 |
if parsed_from_md_for_build.get("owner_md"): # Update UI owner/space name if present in MD
|
650 |
-
ui_owner_name_part = parsed_from_md_for_build["owner_md"]
|
651 |
if parsed_from_md_for_build.get("repo_name_md"):
|
652 |
-
ui_space_name_part = parsed_from_md_for_build["repo_name_md"]
|
653 |
|
654 |
# Rebuild cache from parsed markdown files + structure block
|
655 |
structure_block_md = next((f for f in parsed_from_md_for_build.get("files", []) if f.get("path") == "File Structure (original)"), None)
|
@@ -663,7 +678,7 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
663 |
})
|
664 |
|
665 |
for f_info in parsed_from_md_for_build.get("files", []):
|
666 |
-
if f_info.get("path") and f_info["path"] != "File Structure (original)":
|
667 |
is_binary_repr = isinstance(f_info.get("content"), str) and (f_info["content"].startswith("[Binary file") or f_info["content"].startswith("[Error loading content:") or f_info["content"].startswith("[Binary or Skipped file]"))
|
668 |
parsed_code_blocks_state_cache.append({
|
669 |
"filename": f_info["path"],
|
@@ -676,12 +691,17 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
676 |
|
677 |
except Exception as e:
|
678 |
_build_status = f"Build Error: Failed to parse Markdown structure before building: {e}";
|
679 |
-
|
|
|
680 |
|
681 |
|
682 |
result_message = build_logic_create_space(hf_api_key_ui, ui_space_name_part, final_owner_for_build, space_sdk_ui, formatted_markdown_content)
|
683 |
_build_status = f"Build Process: {result_message}"
|
684 |
|
|
|
|
|
|
|
|
|
685 |
if "Successfully" in result_message:
|
686 |
# Use potentially updated owner/space name from markdown parsing
|
687 |
sub_owner = re.sub(r'[^a-z0-9\-]+', '-', ui_owner_name_part.lower()).strip('-') or 'owner'
|
@@ -695,17 +715,17 @@ def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_p
|
|
695 |
if err_list: _build_status += f"\nFile list refresh error after build: {err_list}"; _file_browser_update = gr.update(visible=True, choices=sorted(file_list or []), value="Error refreshing files")
|
696 |
else: _file_browser_update = gr.update(visible=True, choices=sorted(file_list or []), value=None if file_list else "No files found")
|
697 |
|
698 |
-
#
|
699 |
-
owner_name_output = gr.update(value=ui_owner_name_part)
|
700 |
-
space_name_output = gr.update(value=ui_space_name_part)
|
701 |
-
|
702 |
-
|
703 |
yield _build_status, _iframe_html, _file_browser_update, owner_name_output, space_name_output
|
704 |
|
705 |
|
706 |
# File editing handlers are okay, just need to ensure they update the cache properly after commit/delete
|
707 |
def handle_load_file_for_editing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path):
|
708 |
-
#
|
|
|
|
|
|
|
|
|
709 |
_file_content_val, _edit_status_val, _commit_msg_val, _lang_update = "", "Error: No file selected.", gr.update(value=""), gr.update(language="python") # Reset values
|
710 |
if not selected_file_path or selected_file_path in ["No files found", "Error loading files", "Error refreshing files"]:
|
711 |
yield _file_content_val, "Select a file from the dropdown.", _commit_msg_val, _lang_update # Clear editor and status
|
@@ -745,6 +765,7 @@ def handle_load_file_for_editing(hf_api_key_ui, ui_space_name_part, ui_owner_nam
|
|
745 |
yield _file_content_val, _edit_status_val, _commit_msg_val, _lang_update
|
746 |
|
747 |
def handle_commit_file_changes(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_edit_path, edited_content, commit_message):
|
|
|
748 |
global parsed_code_blocks_state_cache
|
749 |
_edit_status_val = "Processing commit..."
|
750 |
# Initialize updates for components that might change
|
@@ -817,6 +838,7 @@ def handle_commit_file_changes(hf_api_key_ui, ui_space_name_part, ui_owner_name_
|
|
817 |
|
818 |
|
819 |
def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path):
|
|
|
820 |
global parsed_code_blocks_state_cache
|
821 |
_edit_status_val = "Processing deletion..."
|
822 |
# Initialize updates for components that might change/clear
|
@@ -885,6 +907,8 @@ def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, fi
|
|
885 |
_file_browser_value_update = file_to_delete_path # Keep the file selected visually
|
886 |
|
887 |
# Markdown and preview are not changed if deletion failed, keep current updates as gr.update()
|
|
|
|
|
888 |
|
889 |
|
890 |
yield (_edit_status_val, _file_browser_choices_update, _file_browser_value_update, _file_content_editor_update, _commit_msg_update, _lang_update, _formatted_md_out, _detected_preview_out, _download_btn_out)
|
@@ -892,6 +916,7 @@ def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, fi
|
|
892 |
|
893 |
# Space status handler is okay
|
894 |
def handle_refresh_space_status(hf_api_key_ui, ui_owner_name, ui_space_name):
|
|
|
895 |
# ... (rest of this function is the same)
|
896 |
yield "*Fetching space status...*" # Initial feedback
|
897 |
owner_to_use = ui_owner_name
|
@@ -1098,5 +1123,6 @@ with gr.Blocks(theme=custom_theme, css=custom_css) as demo:
|
|
1098 |
# Refresh status handler is okay
|
1099 |
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])
|
1100 |
|
|
|
1101 |
if __name__ == "__main__":
|
1102 |
demo.launch(debug=True, share=False)
|
|
|
35 |
|
36 |
|
37 |
bbb = chr(96) * 3
|
38 |
+
# Declare the global variable at the module level where it's initialized
|
39 |
parsed_code_blocks_state_cache = []
|
40 |
BOT_ROLE_NAME = "assistant"
|
41 |
# Removed GROQ_API_ENDPOINT as it's now in model_logic
|
|
|
134 |
Parses a single bot message content string to find file blocks and updates the state.
|
135 |
Assumes existing_files_state is the current state *before* this message.
|
136 |
"""
|
137 |
+
# This function takes state as an argument and returns new state, it doesn't need 'global'
|
138 |
latest_blocks_dict = {}
|
139 |
if existing_files_state:
|
140 |
# Copy existing blocks, except for potential structure blocks that might be overwritten
|
|
|
194 |
|
195 |
def _export_selected_logic(selected_filenames, space_line_name_for_md, parsed_blocks_for_export):
|
196 |
# This function remains largely the same, using the provided parsed_blocks_for_export
|
197 |
+
# It takes state as an argument and doesn't need 'global'
|
198 |
results = {"output_str": "", "error_message": None, "download_filepath": None}
|
199 |
# Filter out structure blocks for file listing/export content
|
200 |
exportable_blocks_content = [b for b in parsed_blocks_for_export if not b.get("is_structure_block") and not b.get("is_binary") and not (b.get("code", "").startswith("[Error loading content:") or b.get("code", "").startswith("[Binary or Skipped file]"))]
|
|
|
279 |
if current_user_message: messages.append({"role": "user", "content": current_user_message})
|
280 |
return messages
|
281 |
|
|
|
|
|
|
|
|
|
282 |
|
283 |
def _generate_ui_outputs_from_cache(owner, space_name):
|
284 |
+
# Declare global at the top
|
285 |
global parsed_code_blocks_state_cache
|
286 |
+
# This function remains largely the same, generating UI previews and the export MD
|
287 |
preview_md_val = "*No files in cache to display.*"
|
288 |
formatted_md_val = f"# Space: {owner}/{space_name}\n## File Structure\n{bbb}\n📁 Root\n{bbb}\n\n*No files in cache.*" if owner or space_name else "*Load or define a Space to see its Markdown structure.*"
|
289 |
download_file = None
|
|
|
321 |
|
322 |
# --- Refactored Chat Submit Handler ---
|
323 |
def handle_chat_submit(user_message, chat_history, api_key_input, provider_select, model_select, system_prompt, hf_owner_name, hf_repo_name, _current_formatted_markdown):
|
324 |
+
# Declare global at the top
|
325 |
global parsed_code_blocks_state_cache
|
326 |
_chat_msg_in = ""
|
327 |
_chat_hist = list(chat_history)
|
|
|
347 |
|
348 |
for f_info in parsed_from_md.get("files", []):
|
349 |
# Only add if it has a path and isn't the structure block representation placeholder
|
350 |
+
if f_info.get("path") and f_info["path"] != "File Structure (original)"):
|
351 |
# Check if it's a binary representation string
|
352 |
is_binary_repr = isinstance(f_info.get("content"), str) and (f_info["content"].startswith("[Binary file") or f_info["content"].startswith("[Error loading content:") or f_info["content"].startswith("[Binary or Skipped file]"))
|
353 |
# Check if a block with this filename already exists in new_cache_state and replace it
|
|
|
435 |
# Generate stream from model_logic
|
436 |
for chunk in generate_stream(provider_select, model_select, api_key_override, api_msgs):
|
437 |
if chunk is None: continue # Skip None chunks if any
|
438 |
+
if isinstance(chunk, str) and (chunk.startswith("Error: ") or chunk.startswith("API HTTP Error")):
|
439 |
# If an error chunk is received, treat it as the final output and stop
|
440 |
full_bot_response_content = chunk
|
441 |
error_during_stream = chunk
|
442 |
break # Stop processing stream
|
443 |
else:
|
444 |
# Accumulate response and update the last message in chat_hist
|
445 |
+
full_bot_response_content += str(chunk) # Ensure chunk is string
|
446 |
_chat_hist[-1] = (user_message, full_bot_response_content)
|
447 |
_status = f"Streaming from {model_select}..."
|
448 |
# Yield update immediately after receiving chunk
|
|
|
451 |
# After the stream finishes or breaks
|
452 |
if error_during_stream:
|
453 |
_status = error_during_stream # Set status to the error message
|
454 |
+
elif full_bot_response_content and not full_bot_response_content.startswith("Error: "): # Only parse if it's not an error message
|
455 |
_status = f"Streaming complete. Processing files from {model_select} response..."
|
456 |
|
457 |
# Pass the *current state* (updated from markdown at the start)
|
|
|
473 |
_formatted_output_update, _detected_files_update, _download_btn_update = _generate_ui_outputs_from_cache(hf_owner_name, hf_repo_name)
|
474 |
_status = "Processing complete. Previews updated."
|
475 |
else:
|
476 |
+
# Handle cases where the stream finished but yielded no content (e.g., filter) or only an error was yielded
|
477 |
+
if not error_during_stream:
|
478 |
+
_status = "AI response complete, but returned no content."
|
479 |
+
# Keep existing previews/markdown if no content was generated to parse or if it was an error message
|
480 |
+
_formatted_output_update, _detected_files_update, _download_btn_update = _generate_ui_outputs_from_cache(hf_owner_name, hf_repo_name)
|
481 |
+
|
482 |
|
483 |
except Exception as e:
|
484 |
# Catch any errors that occurred *before* or *during* the stream setup/iteration
|
|
|
491 |
_chat_hist.append((user_message, error_msg)) # Append as a new user/bot turn if structure unexpected
|
492 |
_status = error_msg
|
493 |
# Previews and markdown might not be affected by a generation error, keep existing state
|
494 |
+
_formatted_output_update, _detected_files_update, _download_btn_update = _generate_ui_outputs_from_cache(hf_owner_name, hf_repo_name)
|
495 |
+
|
496 |
|
497 |
# Final yield to update UI after all processing
|
498 |
yield (_chat_msg_in, _chat_hist, _status, _detected_files_update, _formatted_output_update, _download_btn_update)
|
|
|
505 |
return gr.update(choices=[], value=None)
|
506 |
models = get_models_for_provider(provider_select)
|
507 |
default_model = get_default_model_for_provider(provider_select)
|
508 |
+
# Ensure default is in choices, or pick first, or None
|
509 |
+
if default_model and default_model in models:
|
510 |
+
selected_value = default_model
|
511 |
+
elif models:
|
512 |
+
selected_value = models[0]
|
513 |
+
else:
|
514 |
+
selected_value = None
|
515 |
|
516 |
+
return gr.update(choices=models, value=selected_value)
|
517 |
|
518 |
+
|
519 |
+
# --- Existing handlers for Load, Build, Edit, Delete, Status (Mostly unchanged, just global placement) ---
|
520 |
|
521 |
def handle_load_existing_space(hf_api_key_ui, ui_owner_name, ui_space_name):
|
522 |
+
# Declare global at the top
|
523 |
global parsed_code_blocks_state_cache
|
524 |
_formatted_md_val, _detected_preview_val, _status_val = "*Loading files...*", "*Loading files...*", f"Loading Space: {ui_owner_name}/{ui_space_name}..."
|
525 |
_file_browser_update, _iframe_html_update, _download_btn_update = gr.update(visible=False, choices=[], value=None), gr.update(value=None, visible=False), gr.update(interactive=False, value=None)
|
|
|
634 |
|
635 |
|
636 |
def handle_build_space_button(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, space_sdk_ui, formatted_markdown_content):
|
637 |
+
# Declare global at the top
|
638 |
+
global parsed_code_blocks_state_cache
|
639 |
# ... (this function calls build_logic_create_space and refreshes file list)
|
640 |
_build_status, _iframe_html, _file_browser_update = "Starting space build process...", gr.update(value=None, visible=False), gr.update(visible=False, choices=[], value=None)
|
641 |
+
# Include outputs for owner/space name textboxes in the initial yield
|
642 |
+
yield _build_status, _iframe_html, _file_browser_update, gr.update(value=ui_owner_name_part), gr.update(value=ui_space_name_part) # Yield initial status
|
643 |
+
if not ui_space_name_part or "/" in ui_space_name_part: _build_status = f"Build Error: HF Space Name '{ui_space_name_part}' must be repo name only (no '/')."; yield _build_status, _iframe_html, _file_browser_update, gr.update(), gr.update(); return
|
644 |
final_owner_for_build = ui_owner_name_part
|
645 |
if not final_owner_for_build:
|
646 |
token_for_whoami, token_err = build_logic_get_api_token(hf_api_key_ui)
|
647 |
+
if token_err: _build_status = f"Build Error: {token_err}"; yield _build_status, _iframe_html, _file_browser_update, gr.update(), gr.update(); return
|
648 |
if token_for_whoami:
|
649 |
try:
|
650 |
user_info = build_logic_whoami(token=token_for_whoami)
|
|
|
653 |
except Exception as e: _build_status += f"\n(Warning: Could not auto-detect owner for build: {e}. Please specify.)"
|
654 |
else: _build_status += "\n(Warning: Owner not specified and no token to auto-detect for build. Please specify owner or provide a token.)"
|
655 |
|
656 |
+
if not final_owner_for_build: _build_status = "Build Error: HF Owner Name could not be determined. Please specify it."; yield _build_status, _iframe_html, _file_browser_update, gr.update(), gr.update(); return
|
657 |
|
658 |
# Before building, parse the markdown to ensure the cache reflects exactly what's being built
|
659 |
# This prevents inconsistencies if the user manually edited the markdown output
|
660 |
try:
|
661 |
parsed_from_md_for_build = build_logic_parse_markdown(formatted_markdown_content)
|
662 |
# Replace the global cache state with the state derived from the markdown being built
|
|
|
663 |
parsed_code_blocks_state_cache = []
|
664 |
if parsed_from_md_for_build.get("owner_md"): # Update UI owner/space name if present in MD
|
665 |
+
ui_owner_name_part = parsed_from_md_for_build["owner_md"] # Use this updated value later
|
666 |
if parsed_from_md_for_build.get("repo_name_md"):
|
667 |
+
ui_space_name_part = parsed_from_md_for_build["repo_name_md"] # Use this updated value later
|
668 |
|
669 |
# Rebuild cache from parsed markdown files + structure block
|
670 |
structure_block_md = next((f for f in parsed_from_md_for_build.get("files", []) if f.get("path") == "File Structure (original)"), None)
|
|
|
678 |
})
|
679 |
|
680 |
for f_info in parsed_from_md_for_build.get("files", []):
|
681 |
+
if f_info.get("path") and f_info["path"] != "File Structure (original)"):
|
682 |
is_binary_repr = isinstance(f_info.get("content"), str) and (f_info["content"].startswith("[Binary file") or f_info["content"].startswith("[Error loading content:") or f_info["content"].startswith("[Binary or Skipped file]"))
|
683 |
parsed_code_blocks_state_cache.append({
|
684 |
"filename": f_info["path"],
|
|
|
691 |
|
692 |
except Exception as e:
|
693 |
_build_status = f"Build Error: Failed to parse Markdown structure before building: {e}";
|
694 |
+
# Yield error status, including keeping current owner/space name in textboxes
|
695 |
+
yield _build_status, _iframe_html, _file_browser_update, gr.update(value=ui_owner_name_part), gr.update(value=ui_space_name_part); return # Stop build on parse error
|
696 |
|
697 |
|
698 |
result_message = build_logic_create_space(hf_api_key_ui, ui_space_name_part, final_owner_for_build, space_sdk_ui, formatted_markdown_content)
|
699 |
_build_status = f"Build Process: {result_message}"
|
700 |
|
701 |
+
# Update UI with owner/space names extracted from markdown if present
|
702 |
+
owner_name_output = gr.update(value=ui_owner_name_part)
|
703 |
+
space_name_output = gr.update(value=ui_space_name_part)
|
704 |
+
|
705 |
if "Successfully" in result_message:
|
706 |
# Use potentially updated owner/space name from markdown parsing
|
707 |
sub_owner = re.sub(r'[^a-z0-9\-]+', '-', ui_owner_name_part.lower()).strip('-') or 'owner'
|
|
|
715 |
if err_list: _build_status += f"\nFile list refresh error after build: {err_list}"; _file_browser_update = gr.update(visible=True, choices=sorted(file_list or []), value="Error refreshing files")
|
716 |
else: _file_browser_update = gr.update(visible=True, choices=sorted(file_list or []), value=None if file_list else "No files found")
|
717 |
|
718 |
+
# Final yield including potential updates to owner/space name textboxes
|
|
|
|
|
|
|
|
|
719 |
yield _build_status, _iframe_html, _file_browser_update, owner_name_output, space_name_output
|
720 |
|
721 |
|
722 |
# File editing handlers are okay, just need to ensure they update the cache properly after commit/delete
|
723 |
def handle_load_file_for_editing(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, selected_file_path):
|
724 |
+
# Declare global at the top (even though it's not modified here, it's good practice if the function *might* interact with it in the future, or just for consistency)
|
725 |
+
# In this specific function, `global` isn't strictly needed as it only *reads* indirectly via _generate_ui_outputs_from_cache which handles its own global
|
726 |
+
# Keeping it here for consistency as per the error symptoms observed in similar functions.
|
727 |
+
global parsed_code_blocks_state_cache # Added global here
|
728 |
+
|
729 |
_file_content_val, _edit_status_val, _commit_msg_val, _lang_update = "", "Error: No file selected.", gr.update(value=""), gr.update(language="python") # Reset values
|
730 |
if not selected_file_path or selected_file_path in ["No files found", "Error loading files", "Error refreshing files"]:
|
731 |
yield _file_content_val, "Select a file from the dropdown.", _commit_msg_val, _lang_update # Clear editor and status
|
|
|
765 |
yield _file_content_val, _edit_status_val, _commit_msg_val, _lang_update
|
766 |
|
767 |
def handle_commit_file_changes(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_edit_path, edited_content, commit_message):
|
768 |
+
# Declare global at the top
|
769 |
global parsed_code_blocks_state_cache
|
770 |
_edit_status_val = "Processing commit..."
|
771 |
# Initialize updates for components that might change
|
|
|
838 |
|
839 |
|
840 |
def handle_delete_file(hf_api_key_ui, ui_space_name_part, ui_owner_name_part, file_to_delete_path):
|
841 |
+
# Declare global at the top
|
842 |
global parsed_code_blocks_state_cache
|
843 |
_edit_status_val = "Processing deletion..."
|
844 |
# Initialize updates for components that might change/clear
|
|
|
907 |
_file_browser_value_update = file_to_delete_path # Keep the file selected visually
|
908 |
|
909 |
# Markdown and preview are not changed if deletion failed, keep current updates as gr.update()
|
910 |
+
# Regenerate previews to show they are unchanged
|
911 |
+
_formatted_md_out, _detected_preview_out, _download_btn_out = _generate_ui_outputs_from_cache(owner_to_use, ui_space_name_part)
|
912 |
|
913 |
|
914 |
yield (_edit_status_val, _file_browser_choices_update, _file_browser_value_update, _file_content_editor_update, _commit_msg_update, _lang_update, _formatted_md_out, _detected_preview_out, _download_btn_out)
|
|
|
916 |
|
917 |
# Space status handler is okay
|
918 |
def handle_refresh_space_status(hf_api_key_ui, ui_owner_name, ui_space_name):
|
919 |
+
# This function doesn't modify the global cache, so no global declaration needed.
|
920 |
# ... (rest of this function is the same)
|
921 |
yield "*Fetching space status...*" # Initial feedback
|
922 |
owner_to_use = ui_owner_name
|
|
|
1123 |
# Refresh status handler is okay
|
1124 |
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])
|
1125 |
|
1126 |
+
|
1127 |
if __name__ == "__main__":
|
1128 |
demo.launch(debug=True, share=False)
|