Spaces:
Running
A newer version of the Gradio SDK is available: 6.12.0
Guidelines
Build, Lint, and Test Commands
Critical: Docker-Only Testing
All tests MUST be run using Docker. Do not run tests directly with Python. This ensures consistent environments and proper dependency management.
Why Docker for Testing?
- Consistent environment across all machines
- Automatic llama.cpp binary installation
- No manual dependency management
- Isolated test execution
- Matches production environment
Running Tests
Quick Tests (Unit Tests Only)
docker compose -f docker-compose.test.yml run --rm test-quick
Runs all unit tests except integration tests (excludes test_integration.py).
Full Test Suite
docker compose -f docker-compose.test.yml run --rm test
Runs complete test suite including integration tests.
Integration Tests Only
docker compose -f docker-compose.test.yml run --rm test-integration
Runs only integration tests with real GGUF file downloads.
Running a Single Test
docker compose -f docker-compose.test.yml run --rm test pytest tests/test_gguf_utils.py::TestGetLlamaGgufSplitPath::test_finds_binary -v --tb=short
Running Tests for a Specific Module
docker compose -f docker-compose.test.yml run --rm test pytest tests/test_gguf_utils.py -v --tb=short
Running the Application
Local Development (No Upload)
RUN_LOCALLY=1 docker compose -f docker-compose.local.yml up
Production Mode
HF_TOKEN=$(cat ~/.cache/huggingface/token) docker compose up
Code Style Guidelines
Imports
Order imports in three sections separated by blank lines:
- Standard library imports (
os,pathlib,re, etc.) - Third-party imports (
gradio,huggingface_hub) - Local imports (
from .config import ...)
Use specific imports, not import *:
from typing import Optional, Final
from huggingface_hub import HfApi
from .config import DOWNLOAD_TIMEOUT
Type Hints
Use type hints for all function parameters and return values:
def validate_repo_id(repo_id: str) -> bool:
"""Validate Hugging Face repository ID format"""
...
Use Optional[T] for nullable types and Final for constants:
DOWNLOAD_TIMEOUT: Final[int] = 600
def extract_username(user_info: object) -> Optional[str]:
...
Prefer modern type syntax: list[str] instead of List[str].
Naming Conventions
- Functions and variables:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private functions:
_snake_case(leading underscore) - Test classes:
PascalCasewithTestprefix
Docstrings
Use triple-quoted docstrings with concise descriptions:
def calculate_optimal_split_size(gguf_path: str, output_prefix: str) -> int:
"""Calculate optimal split size using dry-run"""
...
Error Handling
Catch specific exceptions and provide informative messages:
try:
api.list_repo_files(repo_id)
return True
except RepositoryNotFoundError:
return False
except Exception:
return False
Use logging for errors and warnings:
logger.error(f"Download failed: {e}")
logger.warning(f"Failed to remove file: {e}")
Testing
- Use pytest for all tests
- Test classes use descriptive names:
TestGetLlamaGgufSplitPath - Test methods use descriptive names:
test_finds_binary,test_returns_positive_integer - Use fixtures in
conftest.pyfor shared test data - Integration tests should be marked with
@pytest.mark.integrationdecorator - Unit tests should not download large files or depend on external services
Code Organization
- Keep functions focused and single-purpose
- Use helper functions prefixed with
_for internal implementation details - Group related functions together with blank lines between sections
- Maintain separation of concerns: utilities, processing, UI, config
Formatting
- 4-space indentation
- Maximum line length: ~100 characters (use judgment)
- Blank lines between functions and logical sections
- No trailing whitespace
Constants and Configuration
Define constants in src/config.py using Final type hints:
DOWNLOAD_TIMEOUT: Final[int] = 600
SPLIT_TIMEOUT: Final[int] = 1800
File Paths
Use pathlib.Path for all file operations:
from pathlib import Path
gguf_path = Path(tmp_dir) / "file.gguf"
Subprocess Calls
Use subprocess.run() with explicit parameters:
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=SPLIT_TIMEOUT,
check=True
)
Logging
Use the configured logger (set up via setup_logging()):
from .logging_config import setup_logging
logger = setup_logging()
logger.info("Processing started")
logger.error(f"Error occurred: {e}")
Project Structure
├── app.py # Main entry point (HF Spaces compatible)
├── requirements.txt # Python dependencies
├── start.sh # Startup script
├── src/
│ ├── __init__.py # Package initialization
│ ├── config.py # Environment configuration
│ ├── gguf_utils.py # GGUF splitting utilities
│ ├── hf_utils.py # Hugging Face API utilities
│ ├── processing.py # Main processing logic
│ └── ui.py # Gradio UI components
├── tests/
│ ├── __init__.py # Test package initialization
│ ├── conftest.py # Pytest configuration
│ ├── test_gguf_utils.py # GGUF utilities tests
│ ├── test_hf_utils.py # HF utilities tests
│ └── test_integration.py # Integration tests
├── Dockerfile # Production image
├── Dockerfile.local # Local development image
├── Dockerfile.test # Testing image
├── docker-compose.yml # Production compose
├── docker-compose.local.yml # Local development compose
└── docker-compose.test.yml # Testing compose
Project Structure Notes
src/contains all source codetests/contains all tests (mirrors `src/ structure)- Integration tests download real GGUF files (~100MB)
- Unit tests should be fast and not depend on external resources
- All external dependencies are managed via
requirements.txtandrequirements-dev.txt