# Contributing to OpenRTC

Thanks for contributing to OpenRTC.

OpenRTC is a small Python framework for running multiple standard LiveKit voice
agents inside a single worker process with shared runtime assets. The project
prioritizes clarity, correctness, backward compatibility, and contributor
readability.

## Local setup

This repository uses `uv` for local development.

```bash
uv sync --group dev
```

The dev group includes Typer and Rich so `uv run openrtc …` works without extra
install flags. End users install the CLI with `pip install 'openrtc[cli]'`.

If you prefer, you can also install the package and dev dependencies with pip,
but `uv` is the preferred workflow for contributors.

**Python version:** OpenRTC requires **3.11+** (transitive `onnxruntime` from
LiveKit plugins does not support 3.10).

## Common development commands

### Run tests

```bash
uv run pytest
```

With `uv sync --group dev`, the real `livekit-agents` package is installed, so
pytest uses the upstream SDK—not the fallback shim in `tests/conftest.py`. That
shim only loads when `livekit.agents` is missing (minimal or broken installs). It
is hand-maintained to match APIs OpenRTC uses; when you upgrade the
`livekit-agents` pin in `pyproject.toml` or add new LiveKit imports in `src/`,
re-run the full suite locally and update `conftest.py` if anything still relies
on the stub.

### Run integration tests against a local LiveKit server

Tests under `tests/integration/` (marked `@pytest.mark.integration`) talk to a
real LiveKit dev server. Bring one up with the bundled compose file:

```bash
docker compose -f docker-compose.test.yml up -d
uv run pytest -m integration
docker compose -f docker-compose.test.yml down
```

The `livekit_dev_server` fixture in `tests/integration/conftest.py` skips the
test cleanly when no server is reachable, so `pytest -m integration` is safe to
run in CI environments that do not start the harness.

### Run Ruff lint checks

```bash
uv run ruff check
```

### Format code

```bash
uv run ruff format
```

### Type check (mypy)

CI runs `mypy src/` on pull requests (see `.github/workflows/lint.yml`). Locally:

```bash
uv run mypy src/
```

The mypy config in `pyproject.toml` enables `strict = true`, so untyped defs,
implicit `Optional`, redundant casts, and `Any` returns are all hard failures.
The wheel and sdist ship `src/openrtc/py.typed` (empty PEP 561 marker) so tools
like mypy and pyright treat `openrtc` as a typed dependency.

### Run every CI gate at once

```bash
make ci
```

Runs `lint format-check typecheck test` in the same order as CI. Cheapest checks
first, so a broken lint short-circuits before the test suite. Use this before
`git push` to catch every PR-blocking failure locally.

### Pre-commit hooks

The repo ships a pre-commit config (ruff, ruff-format, basic file hygiene, and
`mypy --strict src/`). Install once:

```bash
uv run pre-commit install
```

Subsequent `git commit` runs the hooks automatically. The mypy hook only fires
when source code or `pyproject.toml` change, so commits that only touch tests,
docs, or workflow YAMLs skip the typecheck cost.

## Project architecture

Keep these responsibilities in mind when contributing:

- `src/openrtc/core/pool.py` contains the core pooling, discovery, routing, and
  session-construction logic.
- `src/openrtc/cli/` is the console entrypoint package; `src/openrtc/cli/commands.py`
  implements the Typer/Rich CLI (optional extra ``openrtc[cli]``; dev deps
  include it for local runs).
- `src/openrtc/__init__.py` defines the public package surface.
- `tests/` contains unit tests for pool behavior, discovery, routing, and CLI
  behavior.

## Contribution rules

When making changes, please preserve the core product constraints:

1. User agents should remain standard `livekit.agents.Agent` subclasses.
2. Do not introduce a custom OpenRTC agent base class.
3. Shared runtime assets such as VAD and turn detection must load in prewarm,
   not per call.
4. Keep the public API explicit and easy to understand.
5. Prefer additive, backward-compatible changes unless a breaking change is
   clearly intentional and documented.

## Testing expectations

- Add or update tests for any non-trivial change.
- Prefer unit tests for routing, validation, discovery, and configuration
  behavior.
- Mock LiveKit runtime boundaries where practical.
- Keep tests deterministic and readable.

## Documentation expectations

If your change affects public behavior, update the relevant docs:

- `README.md` for user-facing usage changes
- `docs/` (VitePress site: CLI, getting started, API, architecture)—keep these in
  sync when you change the public CLI, install extras, or `AgentPool` / discovery
  behavior
- docstrings in `src/openrtc/`
- examples, when new behavior should be demonstrated

## Releasing

The release flow is documented in `docs/release-v0.1.md` (a single-page
operator checklist). For v0.1.0 specifically, the changelog migration
block is staged in the `[Unreleased]` section of `docs/changelog.md` and
the publish workflow auto-prepends a versioned section after the tag
fires.

## Pull requests

Good pull requests for OpenRTC are:

- focused
- well-typed
- covered by tests
- easy to review
- aligned with the existing architecture

If you are unsure where a change belongs, start by reading `src/openrtc/core/pool.py`
and open a small, incremental PR.
