Skip to main content
Prerequisites: Python 3.11+, uv installed, Ollama installed.

Contribution pathways

Three pathways exist for contributing to Mellea: Core repository — bug fixes, standard library additions (Requirements, Components, Sampling Strategies), backend improvements, documentation, and tests. Follow the Pull request process below. Applications and libraries — build tools or applications on top of Mellea in your own repository. Use the mellea- prefix for discoverability (e.g., github.com/my-company/mellea-legal-utils). Community components — contribute experimental or specialized components to mellea-contribs. Open an issue first for general-purpose additions to decide whether they belong in the standard library or in mellea-contribs.

Development setup

  1. Fork and clone the repository:
    git clone ssh://git@github.com/<your-username>/mellea.git
    cd mellea/
    
  2. Create a virtual environment:
    uv venv .venv
    source .venv/bin/activate  # On Windows: .venv\Scripts\activate
    
  3. Install dependencies:
    # Install all dependencies (recommended for development)
    uv sync --all-extras --all-groups
    
    # Or install only backend dependencies
    uv sync --extra backends --all-groups
    
  4. Install pre-commit hooks (required):
    pre-commit install
    
Note: Python 3.13+ requires a Rust compiler for the outlines dependency. Use Python 3.12 if you prefer to avoid this.

Set up with conda or mamba

  1. Fork and clone the repository:
    git clone ssh://git@github.com/<your-username>/mellea.git
    cd mellea/
    
  2. Run the installation script:
    conda/install.sh
    
    The script handles environment setup, dependency installation, and pre-commit hook installation.

Verify the installation

# Start Ollama (required for most tests)
ollama serve

# Run fast tests (skip qualitative tests, ~2 min)
uv run pytest -m "not qualitative"

Coding standards

Type annotations

Type annotations are required on all core functions:
def process_text(text: str, max_length: int = 100) -> str:
    """Process text with maximum length."""
    return text[:max_length]

Docstrings

Docstrings serve as prompts — the LLM reads them, so be specific. Use Google-style docstrings:
def extract_entities(text: str, entity_types: list[str]) -> dict[str, list[str]]:
    """Extract named entities from text.

    Args:
        text: The input text to analyze.
        entity_types: List of entity types to extract (e.g., ["PERSON", "ORG"]).

    Returns:
        Dictionary mapping entity types to lists of extracted entities.

    Example:
        >>> extract_entities("Alice works at IBM", ["PERSON", "ORG"])
        {"PERSON": ["Alice"], "ORG": ["IBM"]}
    """
    ...

Code style

  • Use Ruff for linting and formatting.
  • Use ... in @generative function bodies.
  • Prefer primitives over classes.
  • Keep functions focused and single-purpose.

Linting and formatting

# Format code
uv run ruff format .

# Lint code
uv run ruff check .

# Fix auto-fixable issues
uv run ruff check --fix .

# Type check
uv run mypy .

Development workflow

Commit messages

Follow Angular commit format:
<type>: <subject>

<body>

<footer>
Types: feat, fix, docs, test, refactor, release Example:
feat: add support for streaming responses

Implements streaming for all backend types with proper
error handling and timeout management.

Closes #123
Always sign off commits with -s or --signoff:
git commit -s -m "feat: your commit message"
Branch naming: feat/topic, fix/issue-id, docs/topic

Pre-commit hooks

Pre-commit hooks run automatically before each commit and check:
  • Ruff — linting and formatting
  • mypy — type checking
  • uv-lock — dependency lock file sync
  • codespell — spell checking
Run hooks manually:
pre-commit run --all-files
Warning: pre-commit --all-files may take several minutes. Do not cancel mid-run as it can corrupt state.
Use the -n flag to bypass hooks for intermediate work-in-progress commits:
git commit -n -m "wip: intermediate work"

Testing

Test markers

Tests are categorized using pytest markers:
MarkerRequirement
@pytest.mark.ollamaOllama running locally (lightweight)
@pytest.mark.huggingfaceHuggingFace backend (local, heavy)
@pytest.mark.vllmvLLM backend (GPU required)
@pytest.mark.openaiOpenAI API key
@pytest.mark.watsonxWatsonx API key
@pytest.mark.litellmLiteLLM backend
@pytest.mark.requires_gpuGPU available
@pytest.mark.requires_heavy_ram48 GB+ RAM
@pytest.mark.requires_api_keyExternal API key
@pytest.mark.qualitativeLLM output quality (skipped in CI via CICD=1)
@pytest.mark.llmMakes LLM calls (needs at least Ollama)
@pytest.mark.slowTests taking more than 5 minutes
Warning: Do not add qualitative to trivial tests — keep the fast loop fast. Mark tests taking more than 5 minutes with slow.

Running tests

# Install all dependencies (required for tests)
uv sync --all-extras --all-groups

# Start Ollama (required for most tests)
ollama serve

# Default: runs qualitative tests, skips slow tests
uv run pytest

# Fast tests only (no qualitative, ~2 min)
uv run pytest -m "not qualitative"

# Run only slow tests (>5 min)
uv run pytest -m slow

# Run specific backend tests
uv run pytest -m "ollama"
uv run pytest -m "openai"

# Run tests without LLM calls (unit tests only)
uv run pytest -m "not llm"

# CI/CD mode (skips qualitative tests)
CICD=1 uv run pytest

Timing expectations

RunDuration
Fast tests (-m "not qualitative")~2 minutes
Default (qualitative, no slow)Several minutes
Slow tests (-m slow)More than 5 minutes
Pre-commit hooks1–5 minutes

Replicate CI locally

# Run pre-commit checks (same as CI)
pre-commit run --all-files

# Run tests with CICD flag (same as CI, skips qualitative tests)
CICD=1 uv run pytest

Pull request process

  1. Create an issue describing your change (if one does not already exist).
  2. Fork the repository.
  3. Create a branch in your fork using the naming convention above.
  4. Make your changes following the coding standards.
  5. Add tests for new functionality.
  6. Run the test suite to confirm everything passes.
  7. Update documentation as needed.
  8. Push to your fork and open a pull request.
  9. Follow the automated PR workflow instructions in the PR template.

Troubleshooting

ProblemFix
ComponentParseErrorLLM output did not match expected type. Add examples to the docstring.
uv.lock out of syncRun uv sync to update the lock file.
Ollama refused connectionRun ollama serve to start the Ollama server.
ConnectionRefusedError (port 11434)Ollama is not running. Start with ollama serve.
TypeError: missing positional argumentFirst argument to a @generative function must be session m.
Output is wrong or NoneModel too small or prompt insufficient. Try a larger model or add a reasoning field.
error: can't find Rust compilerPython 3.13+ requires Rust for outlines. Install Rust or use Python 3.12.
Tests fail on Intel MacUse conda: conda install 'torchvision>=0.22.0' then uv pip install mellea.
Pre-commit hooks failRun pre-commit run --all-files to see specific issues. Fix them, or use git commit -n to bypass.

Debugging tips

from mellea.core import FancyLogger

# Enable debug logging
FancyLogger.get_logger().setLevel("DEBUG")

# Inspect the exact prompt sent to the LLM
print(m.last_prompt())

Contributing to the docs

Documentation lives in docs/docs/. The writing guide at docs/docs/guide/CONTRIBUTING covers conventions, the PR checklist, and the review process for documentation contributions. Key points:
  • Start body content with H2 — Mintlify renders the frontmatter title as the page heading.
  • Omit .md extensions from internal links.
  • Tag every fenced code block with a language.
  • Run npx markdownlint-cli2 and fix all warnings before committing.

Getting help


See also: Building Extensions