Skip to navigation

Contributing

View as Markdown

Set up a development checkout of NeMo Labs Voice Agent, satisfy the automated gates, and prepare a pull request for merge. The authoritative source is CONTRIBUTING.md at the repository root. This page summarizes that file and links to related documentation.

Development Environment

The development environment has the following requirements:

RequirementNotes
Python 3.12 or 3.13requires-python = ">=3.12,<3.14" in pyproject.toml
uvManages the virtual environment, lockfile, and its own managed Python (python-preference = "only-managed").
GitSign-off is required on every commit, as described in the sign-off section.
Linux and aptinstall.sh uses apt-get. Install the required operating system packages manually on other platforms.

Clone and bootstrap:

git clone https://github.com/NVIDIA-NeMo/Voice-Agent.git
cd Voice-Agent
bash install.sh

install.sh does more than uv sync. It installs npm, nodejs, build-essential, and python3-dev. The cdifflib transitive dependency is source-distribution-only and compiles from source. The script also installs uv if needed, runs uv sync, and prefetches two Natural Language Toolkit (NLTK) corpora for the Apache-2.0 grapheme-to-phoneme (G2P) fallback. Prefer it to a bare uv sync on a new system.

Then add the test tools and Git hooks:

uv sync --group test # pytest, pytest-timeout, coverage
uv run pre-commit install

Run subsequent commands through uv run, or activate the virtual environment for the current shell session with source .venv/bin/activate. Review these constraints:

  • Do not run uv sync inside an active non-base Conda environment. install.sh exits early when CONDA_DEFAULT_ENV is set to anything other than base. Conda’s gcc combined with system Python headers breaks C extensions. Run conda deactivate first.
  • The default install pulls CUDA 13.0 wheels (torch-backend = "cu130" under [tool.uv]). Edit that key in pyproject.toml before syncing if you need cu128, cu124, or CPU-only. Refer to Installation.

Code Style

Ruff is the only formatter and linter in this repository. black and isort are not installed and are not dependencies—do not add them or reformat files with them. Configuration lives in ruff.toml:

SettingValue
line-length119
target-versionpy312
format.quote-styledouble. Strings are normalized to double quotes.
lint selectF401, F541, F821, F841, E741, and I (isort)
lint ignoreE501. The formatter owns line length.
lint.isort.known-first-partynemo_voice_agent
lint.isort.lines-after-imports2

Run it on the paths you touched:

uv run ruff format <path> # format
uv run ruff check --fix <path> # lint + import sorting

Or run the full hook set the way CI does:

uv run pre-commit run --all-files

.pre-commit-config.yaml configures ruff with --fix, a second ruff pass restricted to --select I, and ruff-format, plus check-yaml, check-case-conflict, detect-private-key, check-added-large-files with --maxkb=1000, and requirements-txt-fixer. The lint-check job in .github/workflows/cicd-main.yml runs pre-commit run --all-files and blocks the rest of the continuous integration (CI) pipeline on failure.

License Headers

Every .py file needs an SPDX and Apache-2.0 header in its first lines. .github/workflows/copyright-check.yml runs on every pull request and fails without it. Copy the header from any existing module. Markdown and YAML are not checked, but repository convention is to include one anyway.

Running Tests

Suites live in tests/unit/ and tests/functional/. There are no test modules directly under tests/.

# Fast lane: unit tests, no GPU
uv run pytest tests/unit -m "not gpu"
# A single module
uv run pytest tests/unit/test_config_manager.py
# With coverage (pytest-cov is not a dependency; use the coverage CLI)
uv run coverage run --source=nemo_voice_agent -m pytest tests/unit
uv run coverage report -m

Markers declared in pyproject.toml: unit, functional, gpu, slow, skipduringci, pleasefixme. CI’s unit lane deselects pleasefixme. The functional lane runs on H100 runners. Refer to Testing for the full suite layout and how to write new tests.

Commits

Use conventional-commit subjects, as documented in CONTRIBUTING.md:

<type>(<scope>): <description>
[optional body]
[optional footer(s)]

Types: feat, fix, docs, style, refactor, test, chore.

Sign-Off (DCO) — Required

Every commit must carry a Signed-off-by trailer certifying the Developer Certificate of Origin. Commits without it are not accepted.

git commit -s -m "fix(tts): drop trailing silence on interruption"

That appends:

Signed-off-by: Your Name <your@email.com>

To fix an existing branch, amend a single commit with git commit --amend -s or rebase a series with git rebase --signoff main. The full Developer Certificate of Origin (DCO) text is reproduced in CONTRIBUTING.md. Sign-off is distinct from GitHub signed (GPG-verified) commits, which affect CI triggering only.

Pull Request Flow

To prepare and submit a pull request, complete the following steps:

  1. Open an issue first for a significant change so contributors can discuss the approach.

  2. Fork the repository and create a branch from main with a descriptive name (feature/..., fix/...).

  3. Implement the change together with tests and documentation updates.

  4. Run the gates locally:

    uv run pre-commit run --all-files
    uv run pytest tests/unit -m "not gpu"
  5. Push to your fork and open a pull request against main that describes what changed and why.

  6. Address review feedback, and squash the history before merge.

Requesting Review

Comment /review on your pull request to request an automated review. Use /review mode=strict for a strict review.

The claude-review.yml workflow only posts command guidance for repository owners, members, and collaborators. Exact /claude review and /claude strict-review comments receive guidance to use /review and /review mode=strict, respectively. Bot comments do not trigger the workflow, and the workflow does not run Claude Code.

Triggering CI

The pipeline runs on pushes to main, to deploy-release/*, and to the bot-mirrored pull-request/<number> branches. If your GitHub account uses signed (GPG-verified) commits, CI starts automatically on each push. Otherwise, comment on the pull request with the SHA of the commit you want tested:

/ok to test a1b2c3d4e5f6

Repeat the comment for each new commit. Get the SHA from git log --oneline -1.

Documentation-only pull requests take a different path. fern-docs-ci.yml checks MDX safety, navigation, Fern configuration, and offline links when a pull request changes docs/**. Separately, fern-docs-preview.yml runs on pushes to bot-mirrored pull-request/<number> branches. It builds a hosted preview and posts its link when PUBLISH_FERN=true and the pull request changes docs/** or .github/workflows/fern-docs-ci.yml. Refer to Building the Docs before editing anything under docs/.

Where Changes Usually Go

Use this table to find the documentation and implementation guidance for a change:

ChangeStart Here
New or swapped model, new pipeline stageThe Builder API
New config key or defaultConfiguration Model
New agent-callable toolWriting Your Own Tools
New evaluation scenario or domainAuthoring Scenarios
Re-importing upstream benchmark fixturesRegenerating Benchmark Data
Reporting a vulnerabilitySecurity Policy

Local artifacts are gitignored and must not be committed: .venv/, nemo_voice_agent.egg-info/, nemo_experiments/, eval_results/, and *.log files.