akhaliq HF Staff commited on
Commit
999c623
·
1 Parent(s): f2d985d
Files changed (1) hide show
  1. app.py +170 -48
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. Instead, output the complete modified files using the same format as the original generation.
 
 
1731
 
1732
- **Output Format for Modified Gradio Apps:**
1733
- When modifying multi-file Gradio applications, use this exact format:
1734
 
1735
- ```
1736
  === app.py ===
1737
  [complete modified app.py content]
1738
 
1739
- === utils.py ===
1740
- [complete modified utils.py content - only if it exists and needs changes]
1741
-
1742
- === requirements.txt ===
1743
- [complete modified requirements.txt content]
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 complete multi-file Gradio output (new generation or followup with multi-file format)
 
 
 
 
 
 
 
 
 
 
 
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
- # Model returned search/replace changes for Gradio - apply them
6536
  last_content = _history[-1][1] if _history and len(_history[-1]) > 1 else ""
6537
- modified_content = apply_search_replace_changes(last_content, content)
6538
- _history.append([query, modified_content])
6539
- yield {
6540
- code_output: modified_content,
6541
- history: _history,
6542
- history_output: history_to_chatbot_messages(_history),
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
- # Upload requirements.txt only if needed
9441
- if should_upload_requirements:
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
- # Now upload the main app.py file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)