GGUF-Splitter / agents.md
Felladrin's picture
Initial commit
2de2584

A newer version of the Gradio SDK is available: 6.12.0

Upgrade

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:

  1. Standard library imports (os, pathlib, re, etc.)
  2. Third-party imports (gradio, huggingface_hub)
  3. 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: PascalCase with Test prefix

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.py for shared test data
  • Integration tests should be marked with @pytest.mark.integration decorator
  • 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 code
  • tests/ 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.txt and requirements-dev.txt