# Contributing to DeltaLLM Thank you for your interest in contributing to DeltaLLM! This document provides guidelines for contributing to the project. ## Getting Started ### Prerequisites - Python 3.11+ - Node.js 20+ - PostgreSQL 15+ - Redis 7+ (optional but recommended) - [uv](https://docs.astral.sh/uv/) for Python dependency management ### Setting Up Your Development Environment 1. **Fork and clone the repository** ```bash git clone https://github.com/YOUR_USERNAME/deltallm.git cd deltallm ``` 2. **Install Python dependencies** ```bash uv sync --dev ``` 3. **Install UI dependencies** ```bash cd ui npm ci cd .. ``` 4. **Set up environment variables** ```bash export DATABASE_URL="postgresql://postgres:postgres@localhost:5432/deltallm" export DELTALLM_CONFIG_PATH=./config.yaml export DELTALLM_MASTER_KEY="$(python3 -c 'import secrets; print(\"sk-\" + secrets.token_hex(20) + \"A1\")')" export DELTALLM_SALT_KEY="$(openssl rand -hex 32)" export OPENAI_API_KEY="sk-your-openai-key" ``` 5. **Create config and initialize the database** ```bash cp config.example.yaml config.yaml uv run prisma generate --schema=./prisma/schema.prisma uv run prisma db push --schema=./prisma/schema.prisma ``` 6. **Start the development servers** Backend: ```bash uv run uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload ``` UI (in another terminal): ```bash cd ui npm run dev ``` See [Local Development](README.md#local-development) in the README for more details. ## Development Workflow 1. **Create a branch** for your changes ```bash git checkout -b feature/your-feature-name ``` 2. **Make your changes** and ensure they follow our code style 3. **Run tests** to make sure nothing is broken ```bash uv run pytest ``` 4. **Run the linter** to check code style ```bash uv run ruff check . ``` 5. **Commit your changes** with a clear commit message 6. **Push to your fork** and submit a pull request ## Pull Request Guidelines - **Describe what your PR does** and why it's needed - **Reference any related issues** using `Fixes #123` or `Closes #123` - **Ensure tests pass** before submitting - **Keep changes focused** — one feature or fix per PR - **Update documentation** if your changes affect usage ## Code Style - We use [Ruff](https://docs.astral.sh/ruff/) for Python linting - Follow PEP 8 style guidelines - Write docstrings for public functions and classes - Keep functions focused and modular ## Testing - Write tests for new features - Ensure existing tests pass - Use `pytest` for running tests Every Python test belongs to exactly one dependency lane. Pytest assigns `app` automatically when a test uses the shared `test_app` fixture and assigns `hermetic` when no other lane applies. A test module that opens a real infrastructure client must declare its lane for every test in that module, normally with a module-level `pytestmark`. | Lane | What it exercises | Required dependency | | --- | --- | --- | | `hermetic` | Domain, service, repository-fake, and focused component behavior | None outside the Python process | | `app` | The complete in-process FastAPI route graph with fake adapters and stores | None outside the Python process | | `postgres` | SQL, constraints, transactions, locking, and Prisma behavior | Migrated PostgreSQL plus the generated Prisma client | | `redis` | Lua, cross-client coordination, reconnect, TTL, and outage behavior | Real Redis through `DELTALLM_TEST_REDIS_URL` | | `helm` | Chart schema, rendering, and deployment profiles | Helm CLI and built chart dependencies | The `integration` marker remains an aggregate selector for the `postgres`, `redis`, and `helm` lanes. It is not a primary lane. ```bash # Run all tests uv run pytest # Show the current test count in each lane uv run pytest --collect-only -qq --dependency-lane-report | tail -n 7 # Run one dependency lane uv run pytest -q -m hermetic uv run pytest -q -m app # Run PostgreSQL tests after generating the client and migrating the test database DATABASE_URL=postgresql://postgres:postgres@localhost:5432/deltallm_test \ uv run pytest -q -m postgres # Run Redis integration tests DELTALLM_TEST_REDIS_URL=redis://localhost:6379/0 \ REDIS_URL=redis://localhost:6379/0 \ uv run pytest -q -m redis # Run Helm tests after building chart dependencies uv run pytest -q -m helm # Run specific test file uv run pytest tests/test_specific.py ``` The collection hook fails when a test declares multiple primary lanes or when real Prisma, Redis, or Helm usage is missing its matching explicit marker. This keeps lane selection exhaustive and prevents infrastructure tests from silently running in a fake-only job. Pull requests that change only the UI (`ui/`), the docs (`docs/`, `mkdocs.yml`, top-level Markdown) or the docs workflow skip the kind cluster jobs (`lifecycle-acceptance` and `capacity-acceptance`). The UI build, every Python lane and Helm still run. The PostgreSQL migration paths run only when the change touches `prisma/`, the migration tooling in `src/` or `scripts/`, `pyproject.toml`, `uv.lock`, the `Dockerfile` or `ci.yml`. The `cluster-changes` job decides; the required `test` check accepts a skipped job only when that job chose to skip it. Pushes to main always run every job. In GitHub Actions, the exact `-m app` selection uses two pytest-xdist worker processes. Each test still creates its own application and fake stores. The `test_app` fixture builds the full route table once per process and gives each application its own copies of those routes, bound to that application so `dependency_overrides` still apply. Rebuilding the routes for every test took about a second each and most of the lane's time. The other lanes, collection-only commands, and local commands remain serial by default. Use `-n 0` to request serial execution or `-n 2` to reproduce the application CI run locally. Explicit worker settings take precedence. This policy keeps the existing CI deadline and all required checks. The PostgreSQL lane uses four CI runners. Each runner has its own PostgreSQL and Redis services. `--postgres-shard=0/4` through `--postgres-shard=3/4` divide all collected PostgreSQL modules by a stable path hash. Each test runs once, and each module stays on one runner. With two shards the hash split was about 40/60 by run time, so one runner took about 12 minutes; with four the largest shard holds under a third of the lane. All shards must pass the `test` check. The time limit stays at 20 minutes per runner. To reproduce a shard, add its option to `-m postgres` and use an isolated test database and Redis service. Local runs without this option still run the full lane. When a Python test lane fails in CI, the failing test IDs are also posted as job annotations, so they stay visible even when a long service log follows them. Every workflow that calls `uv run` sets `UV_FROZEN=1`. Without it, `uv run` checks that `uv.lock` is current, which downloads each direct-URL wheel in the lock, including the 400 MB presidio language model from github.com. CI lanes never install that extra, so the download only added time and failed jobs whenever GitHub answered 504. Run `uv lock` locally after changing dependencies. Tests that check a deadline should give the code enough time to reach the step under test on a busy runner (about a second), and tests that wait for background work should poll for it rather than sleep for a fixed few milliseconds. Query-plan tests read plans from `auto_explain` at the `NOTICE` level, so the plans reach the test connection but not the PostgreSQL server log. GitHub Actions prints the service container's log after the job and the readable job log keeps only its end, so a noisy server log hides the pytest summary. Plan assertions accept any bounded index access, including a bitmap over the same index: the planner's choice can change when autovacuum refreshes statistics during a run. Test Prisma clients that run accounting SQL use `jit=off`, like the accounting pool they stand in for. Tests that check outcomes rather than latency pass a wider budget to components with sub-second production transaction budgets, such as external-auth exchanges and billing recovery lanes, because a shared CI runner can overrun them. ## Reporting Issues When reporting bugs, please include: - **Clear description** of the issue - **Steps to reproduce** the problem - **Expected vs actual behavior** - **Environment details** (OS, Python version, etc.) - **Relevant logs or error messages** ## Questions? - Check the [documentation](https://deltallm.readthedocs.io) - Open a [discussion](https://github.com/deltawi/deltallm/discussions) for questions - Join our community conversations ## License By contributing to DeltaLLM, you agree that your contributions will be licensed under the MIT License. --- Thank you for helping make DeltaLLM better! 🚀