Spaces:
Running
Running
update
Browse files
app.py
CHANGED
|
@@ -473,8 +473,18 @@ When generating multi-file applications, use this exact format:
|
|
| 473 |
[dependencies]
|
| 474 |
```
|
| 475 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
**Single vs Multi-File Decision:**
|
| 477 |
-
- Use single file for simple applications (< 100 lines)
|
| 478 |
- Use multi-file structure for complex applications with:
|
| 479 |
- Multiple models or processing pipelines
|
| 480 |
- Extensive utility functions
|
|
@@ -778,7 +788,7 @@ When generating multi-file applications, use this exact format:
|
|
| 778 |
```
|
| 779 |
|
| 780 |
**Single vs Multi-File Decision:**
|
| 781 |
-
- Use single file for simple applications (< 100 lines)
|
| 782 |
- Use multi-file structure for complex applications with:
|
| 783 |
- Multiple models or processing pipelines
|
| 784 |
- Extensive utility functions
|
|
@@ -1456,7 +1466,7 @@ When generating multi-file applications, use this exact format:
|
|
| 1456 |
```
|
| 1457 |
|
| 1458 |
**Single vs Multi-File Decision:**
|
| 1459 |
-
- Use single file for simple applications (< 100 lines)
|
| 1460 |
- Use multi-file structure for complex applications with:
|
| 1461 |
- Multiple pages or sections
|
| 1462 |
- Extensive data processing
|
|
@@ -1726,22 +1736,31 @@ CRITICAL: For imported spaces that lack anycoder attribution, you MUST add it as
|
|
| 1726 |
GradioFollowUpSystemPrompt = """You are an expert Gradio developer modifying an existing Gradio application.
|
| 1727 |
The user wants to apply changes based on their request.
|
| 1728 |
|
| 1729 |
-
CRITICAL: You MUST maintain the original multi-file structure when making modifications.
|
| 1730 |
-
Do NOT use SEARCH/REPLACE blocks.
|
|
|
|
|
|
|
| 1731 |
|
| 1732 |
-
**Output Format for Modified Gradio Apps:**
|
| 1733 |
-
|
| 1734 |
|
| 1735 |
-
```
|
| 1736 |
=== app.py ===
|
| 1737 |
[complete modified app.py content]
|
| 1738 |
|
| 1739 |
-
|
| 1740 |
-
|
| 1741 |
-
|
| 1742 |
-
|
| 1743 |
-
|
| 1744 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1745 |
|
| 1746 |
**File Modification Guidelines:**
|
| 1747 |
- Only output files that actually need changes
|
|
@@ -5848,7 +5867,9 @@ def generation_code(query: str | None, vlm_image: Optional[gr.Image], _setting:
|
|
| 5848 |
'=== index.html ===' in last_assistant_msg or
|
| 5849 |
'=== index.js ===' in last_assistant_msg or
|
| 5850 |
'=== style.css ===' in last_assistant_msg or
|
| 5851 |
-
'=== src/App.svelte ===' in last_assistant_msg
|
|
|
|
|
|
|
| 5852 |
has_existing_content = True
|
| 5853 |
|
| 5854 |
# If this is a modification request, try to apply search/replace first
|
|
@@ -6524,7 +6545,18 @@ Generate the exact search/replace blocks needed to make these changes."""
|
|
| 6524 |
elif language == "gradio":
|
| 6525 |
# Handle Gradio output - check if it's multi-file format or single file
|
| 6526 |
if ('=== app.py ===' in content or '=== requirements.txt ===' in content):
|
| 6527 |
-
# Model returned
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6528 |
_history.append([query, content])
|
| 6529 |
yield {
|
| 6530 |
code_output: content,
|
|
@@ -6532,15 +6564,61 @@ Generate the exact search/replace blocks needed to make these changes."""
|
|
| 6532 |
history_output: history_to_chatbot_messages(_history),
|
| 6533 |
}
|
| 6534 |
elif has_existing_content:
|
| 6535 |
-
#
|
| 6536 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 6537 |
-
|
| 6538 |
-
|
| 6539 |
-
|
| 6540 |
-
|
| 6541 |
-
|
| 6542 |
-
|
| 6543 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6544 |
else:
|
| 6545 |
# Fallback - treat as single file Gradio app
|
| 6546 |
_history.append([query, content])
|
|
@@ -9437,34 +9515,78 @@ with gr.Blocks(
|
|
| 9437 |
# File doesn't exist or can't be accessed, so we should upload
|
| 9438 |
should_upload_requirements = True
|
| 9439 |
|
| 9440 |
-
#
|
| 9441 |
-
|
| 9442 |
-
try:
|
| 9443 |
-
with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as f:
|
| 9444 |
-
f.write(requirements_content)
|
| 9445 |
-
requirements_temp_path = f.name
|
| 9446 |
-
|
| 9447 |
-
api.upload_file(
|
| 9448 |
-
path_or_fileobj=requirements_temp_path,
|
| 9449 |
-
path_in_repo="requirements.txt",
|
| 9450 |
-
repo_id=repo_id,
|
| 9451 |
-
repo_type="space"
|
| 9452 |
-
)
|
| 9453 |
-
except Exception as e:
|
| 9454 |
-
error_msg = str(e)
|
| 9455 |
-
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 9456 |
-
return gr.update(value=f"Error uploading requirements.txt: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 9457 |
-
else:
|
| 9458 |
-
return gr.update(value=f"Error uploading requirements.txt: {e}", visible=True)
|
| 9459 |
-
finally:
|
| 9460 |
-
import os
|
| 9461 |
-
if 'requirements_temp_path' in locals():
|
| 9462 |
-
os.unlink(requirements_temp_path)
|
| 9463 |
|
| 9464 |
# Add anycoder tag to existing README
|
| 9465 |
add_anycoder_tag_to_readme(api, repo_id)
|
| 9466 |
|
| 9467 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9468 |
file_name = "app.py"
|
| 9469 |
with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
|
| 9470 |
f.write(code)
|
|
|
|
| 473 |
[dependencies]
|
| 474 |
```
|
| 475 |
|
| 476 |
+
**🚨 CRITICAL: Always Generate requirements.txt for New Applications**
|
| 477 |
+
- ALWAYS include requirements.txt when creating new Gradio applications
|
| 478 |
+
- Generate comprehensive, production-ready dependencies based on your code
|
| 479 |
+
- Include not just direct imports but also commonly needed companion packages
|
| 480 |
+
- Use correct PyPI package names (e.g., PIL → Pillow, sklearn → scikit-learn)
|
| 481 |
+
- For diffusers: use `git+https://github.com/huggingface/diffusers`
|
| 482 |
+
- For transformers: use `git+https://github.com/huggingface/transformers`
|
| 483 |
+
- Include supporting packages (accelerate, torch, tokenizers, etc.) when using ML libraries
|
| 484 |
+
- Your requirements.txt should ensure the application works smoothly in production
|
| 485 |
+
|
| 486 |
**Single vs Multi-File Decision:**
|
| 487 |
+
- Use single file for simple applications (< 100 lines) - but still generate requirements.txt if dependencies exist
|
| 488 |
- Use multi-file structure for complex applications with:
|
| 489 |
- Multiple models or processing pipelines
|
| 490 |
- Extensive utility functions
|
|
|
|
| 788 |
```
|
| 789 |
|
| 790 |
**Single vs Multi-File Decision:**
|
| 791 |
+
- Use single file for simple applications (< 100 lines) - but still generate requirements.txt if dependencies exist
|
| 792 |
- Use multi-file structure for complex applications with:
|
| 793 |
- Multiple models or processing pipelines
|
| 794 |
- Extensive utility functions
|
|
|
|
| 1466 |
```
|
| 1467 |
|
| 1468 |
**Single vs Multi-File Decision:**
|
| 1469 |
+
- Use single file for simple applications (< 100 lines) - but still generate requirements.txt if dependencies exist
|
| 1470 |
- Use multi-file structure for complex applications with:
|
| 1471 |
- Multiple pages or sections
|
| 1472 |
- Extensive data processing
|
|
|
|
| 1736 |
GradioFollowUpSystemPrompt = """You are an expert Gradio developer modifying an existing Gradio application.
|
| 1737 |
The user wants to apply changes based on their request.
|
| 1738 |
|
| 1739 |
+
🚨 CRITICAL INSTRUCTION: You MUST maintain the original multi-file structure when making modifications.
|
| 1740 |
+
❌ Do NOT use SEARCH/REPLACE blocks.
|
| 1741 |
+
❌ Do NOT output everything in one combined block.
|
| 1742 |
+
✅ Instead, output the complete modified files using the EXACT same multi-file format as the original generation.
|
| 1743 |
|
| 1744 |
+
**MANDATORY Output Format for Modified Gradio Apps:**
|
| 1745 |
+
You MUST use this exact format with file separators. DO NOT deviate from this format:
|
| 1746 |
|
|
|
|
| 1747 |
=== app.py ===
|
| 1748 |
[complete modified app.py content]
|
| 1749 |
|
| 1750 |
+
**CRITICAL FORMATTING RULES:**
|
| 1751 |
+
- ALWAYS start each file with exactly "=== filename ===" (three equals signs before and after)
|
| 1752 |
+
- NEVER combine files into one block
|
| 1753 |
+
- NEVER use SEARCH/REPLACE blocks like <<<<<<< SEARCH
|
| 1754 |
+
- ALWAYS include app.py if it needs changes
|
| 1755 |
+
- Only include other files (utils.py, models.py, etc.) if they exist and need changes
|
| 1756 |
+
- Each file section must be complete and standalone
|
| 1757 |
+
- The format MUST match the original multi-file structure exactly
|
| 1758 |
+
|
| 1759 |
+
**🚨 CRITICAL: DO NOT GENERATE requirements.txt**
|
| 1760 |
+
- requirements.txt is automatically generated from your app.py imports
|
| 1761 |
+
- Do NOT include requirements.txt in your output unless the user specifically asks to modify dependencies
|
| 1762 |
+
- The system will automatically extract imports from app.py and generate requirements.txt
|
| 1763 |
+
- This prevents unnecessary changes to dependencies
|
| 1764 |
|
| 1765 |
**File Modification Guidelines:**
|
| 1766 |
- Only output files that actually need changes
|
|
|
|
| 5867 |
'=== index.html ===' in last_assistant_msg or
|
| 5868 |
'=== index.js ===' in last_assistant_msg or
|
| 5869 |
'=== style.css ===' in last_assistant_msg or
|
| 5870 |
+
'=== src/App.svelte ===' in last_assistant_msg or
|
| 5871 |
+
'=== app.py ===' in last_assistant_msg or
|
| 5872 |
+
'=== requirements.txt ===' in last_assistant_msg):
|
| 5873 |
has_existing_content = True
|
| 5874 |
|
| 5875 |
# If this is a modification request, try to apply search/replace first
|
|
|
|
| 6545 |
elif language == "gradio":
|
| 6546 |
# Handle Gradio output - check if it's multi-file format or single file
|
| 6547 |
if ('=== app.py ===' in content or '=== requirements.txt ===' in content):
|
| 6548 |
+
# Model returned multi-file Gradio output - ensure requirements.txt is present
|
| 6549 |
+
files = parse_multi_file_python_output(content)
|
| 6550 |
+
if files and 'app.py' in files:
|
| 6551 |
+
# Check if requirements.txt is missing and auto-generate it
|
| 6552 |
+
if 'requirements.txt' not in files:
|
| 6553 |
+
import_statements = extract_import_statements(files['app.py'])
|
| 6554 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 6555 |
+
files['requirements.txt'] = requirements_content
|
| 6556 |
+
|
| 6557 |
+
# Reformat with the auto-generated requirements.txt
|
| 6558 |
+
content = format_multi_file_python_output(files)
|
| 6559 |
+
|
| 6560 |
_history.append([query, content])
|
| 6561 |
yield {
|
| 6562 |
code_output: content,
|
|
|
|
| 6564 |
history_output: history_to_chatbot_messages(_history),
|
| 6565 |
}
|
| 6566 |
elif has_existing_content:
|
| 6567 |
+
# Check if this is a followup that should maintain multi-file structure
|
| 6568 |
last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
|
| 6569 |
+
|
| 6570 |
+
# If the original was multi-file but the response isn't, try to convert it
|
| 6571 |
+
if ('=== app.py ===' in last_content or '=== requirements.txt ===' in last_content):
|
| 6572 |
+
# Original was multi-file, but response is single block - need to convert
|
| 6573 |
+
if not ('=== app.py ===' in content or '=== requirements.txt ===' in content):
|
| 6574 |
+
# Try to parse as single-block Gradio code and convert to multi-file format
|
| 6575 |
+
clean_content = remove_code_block(content)
|
| 6576 |
+
if 'import gradio' in clean_content or 'from gradio' in clean_content:
|
| 6577 |
+
# This looks like Gradio code, convert to multi-file format
|
| 6578 |
+
files = parse_multi_file_python_output(clean_content)
|
| 6579 |
+
if not files:
|
| 6580 |
+
# Single file - create multi-file structure
|
| 6581 |
+
files = {'app.py': clean_content}
|
| 6582 |
+
|
| 6583 |
+
# Extract requirements from imports
|
| 6584 |
+
import_statements = extract_import_statements(clean_content)
|
| 6585 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 6586 |
+
files['requirements.txt'] = requirements_content
|
| 6587 |
+
|
| 6588 |
+
# Format as multi-file output
|
| 6589 |
+
formatted_content = format_multi_file_python_output(files)
|
| 6590 |
+
_history.append([query, formatted_content])
|
| 6591 |
+
yield {
|
| 6592 |
+
code_output: formatted_content,
|
| 6593 |
+
history: _history,
|
| 6594 |
+
history_output: history_to_chatbot_messages(_history),
|
| 6595 |
+
}
|
| 6596 |
+
else:
|
| 6597 |
+
# Not Gradio code, apply search/replace
|
| 6598 |
+
modified_content = apply_search_replace_changes(last_content, content)
|
| 6599 |
+
_history.append([query, modified_content])
|
| 6600 |
+
yield {
|
| 6601 |
+
code_output: modified_content,
|
| 6602 |
+
history: _history,
|
| 6603 |
+
history_output: history_to_chatbot_messages(_history),
|
| 6604 |
+
}
|
| 6605 |
+
else:
|
| 6606 |
+
# Response is already multi-file format
|
| 6607 |
+
_history.append([query, content])
|
| 6608 |
+
yield {
|
| 6609 |
+
code_output: content,
|
| 6610 |
+
history: _history,
|
| 6611 |
+
history_output: history_to_chatbot_messages(_history),
|
| 6612 |
+
}
|
| 6613 |
+
else:
|
| 6614 |
+
# Original was single file, apply search/replace
|
| 6615 |
+
modified_content = apply_search_replace_changes(last_content, content)
|
| 6616 |
+
_history.append([query, modified_content])
|
| 6617 |
+
yield {
|
| 6618 |
+
code_output: modified_content,
|
| 6619 |
+
history: _history,
|
| 6620 |
+
history_output: history_to_chatbot_messages(_history),
|
| 6621 |
+
}
|
| 6622 |
else:
|
| 6623 |
# Fallback - treat as single file Gradio app
|
| 6624 |
_history.append([query, content])
|
|
|
|
| 9515 |
# File doesn't exist or can't be accessed, so we should upload
|
| 9516 |
should_upload_requirements = True
|
| 9517 |
|
| 9518 |
+
# Note: requirements.txt upload is now handled by the multi-file commit logic below
|
| 9519 |
+
# This ensures all files are committed atomically in a single operation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9520 |
|
| 9521 |
# Add anycoder tag to existing README
|
| 9522 |
add_anycoder_tag_to_readme(api, repo_id)
|
| 9523 |
|
| 9524 |
+
# Check if code contains multi-file format
|
| 9525 |
+
if ('=== app.py ===' in code or '=== requirements.txt ===' in code):
|
| 9526 |
+
# Parse multi-file format and upload each file separately
|
| 9527 |
+
files = parse_multi_file_python_output(code)
|
| 9528 |
+
if files:
|
| 9529 |
+
# Ensure requirements.txt is present - auto-generate if missing
|
| 9530 |
+
if 'app.py' in files and 'requirements.txt' not in files:
|
| 9531 |
+
import_statements = extract_import_statements(files['app.py'])
|
| 9532 |
+
requirements_content = generate_requirements_txt_with_llm(import_statements)
|
| 9533 |
+
files['requirements.txt'] = requirements_content
|
| 9534 |
+
try:
|
| 9535 |
+
from huggingface_hub import CommitOperationAdd
|
| 9536 |
+
operations = []
|
| 9537 |
+
temp_files = []
|
| 9538 |
+
|
| 9539 |
+
# Create CommitOperation for each file
|
| 9540 |
+
for filename, content in files.items():
|
| 9541 |
+
# Create temporary file
|
| 9542 |
+
with tempfile.NamedTemporaryFile("w", suffix=f".{filename.split('.')[-1]}", delete=False) as f:
|
| 9543 |
+
f.write(content)
|
| 9544 |
+
temp_path = f.name
|
| 9545 |
+
temp_files.append(temp_path)
|
| 9546 |
+
|
| 9547 |
+
# Add to operations
|
| 9548 |
+
operations.append(CommitOperationAdd(
|
| 9549 |
+
path_in_repo=filename,
|
| 9550 |
+
path_or_fileobj=temp_path
|
| 9551 |
+
))
|
| 9552 |
+
|
| 9553 |
+
# Commit all files at once
|
| 9554 |
+
api.create_commit(
|
| 9555 |
+
repo_id=repo_id,
|
| 9556 |
+
operations=operations,
|
| 9557 |
+
commit_message=f"{'Update' if is_update else 'Deploy'} Gradio app with multiple files",
|
| 9558 |
+
repo_type="space"
|
| 9559 |
+
)
|
| 9560 |
+
|
| 9561 |
+
# Clean up temp files
|
| 9562 |
+
for temp_path in temp_files:
|
| 9563 |
+
try:
|
| 9564 |
+
os.unlink(temp_path)
|
| 9565 |
+
except Exception:
|
| 9566 |
+
pass
|
| 9567 |
+
|
| 9568 |
+
space_url = f"https://huggingface.co/spaces/{repo_id}"
|
| 9569 |
+
action_text = "Updated" if is_update else "Deployed"
|
| 9570 |
+
return gr.update(value=f"✅ {action_text}! [Open your Space here]({space_url})", visible=True)
|
| 9571 |
+
|
| 9572 |
+
except Exception as e:
|
| 9573 |
+
# Clean up temp files on error
|
| 9574 |
+
for temp_path in temp_files:
|
| 9575 |
+
try:
|
| 9576 |
+
os.unlink(temp_path)
|
| 9577 |
+
except Exception:
|
| 9578 |
+
pass
|
| 9579 |
+
|
| 9580 |
+
error_msg = str(e)
|
| 9581 |
+
if "403 Forbidden" in error_msg and "write token" in error_msg:
|
| 9582 |
+
return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
|
| 9583 |
+
else:
|
| 9584 |
+
return gr.update(value=f"Error uploading multi-file app: {e}", visible=True)
|
| 9585 |
+
else:
|
| 9586 |
+
# Fallback to single file if parsing failed
|
| 9587 |
+
pass
|
| 9588 |
+
|
| 9589 |
+
# Single file upload (fallback or non-multi-file format)
|
| 9590 |
file_name = "app.py"
|
| 9591 |
with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
|
| 9592 |
f.write(code)
|