Contributing¶
Thank you for your interest in contributing to nagents! This guide will help you get started.
Getting Started¶
Prerequisites¶
- Python
>=3.11, <4.0 - Poetry for the workflow below, or an existing virtual environment with pip
- Git for version control
Development Setup¶
# Clone the repository
git clone https://github.com/abi-jey/nagents.git
cd nagents
# Create virtual environment and install all dependencies
poetry install -E dev -E docs -E tui -E voice -E web # (1)!
# Install pre-commit hooks
poetry run pre-commit install
# Run the full test suite
poetry run pytest -v
# Run type checking
poetry run mypy src/nagents
# Build documentation and check warnings
poetry run mkdocs build --strict
- These are package extras from
[project.optional-dependencies], not Poetry dependency groups.dev,tui,voice, andwebmatch the test installation in CI;docsadds MkDocs and mkdocstrings.
If you already use a local .venv, keep that environment rather than letting
multiple tools manage it. The equivalent editable install is
.venv/bin/python -m pip install -e '.[dev,docs,tui,voice,web]'; use .venv/bin/pytest,
.venv/bin/mypy, and .venv/bin/mkdocs in place of poetry run below.
The build backend is Hatchling, regardless of the environment manager.
Test the checkout, not a release snapshot
Published wheels include the terminal/headless CLI and built ngn serve
frontend, verified in v0.15.0. Use an editable checkout for development; it
can contain changes beyond an installed release, and matching version
metadata alone does not prove matching code. See
release availability.
Web Client Development¶
The React/TypeScript frontend lives in src/nagents/web-ui/; the Python backend
is src/nagents/web/. With the checkout installed in .venv, run from the
repository root:
.venv/bin/python -m pip install -e '.[web]'
npm --prefix src/nagents/web-ui ci
npm --prefix src/nagents/web-ui run typecheck
npm --prefix src/nagents/web-ui test
npm --prefix src/nagents/web-ui run build
.venv/bin/ngn serve --dev --demo
Use Node.js 24.20.0 to match the current CI build. Vite emits assets into
src/nagents/web/static/; build them before packaging or launching without --dev.
The wheel includes generated assets, while the source distribution also carries
the frontend source and lockfile. Installing [web] adds optional Python HTTP
dependencies, not a JavaScript build step. Plain ngn serve uses the verified
bundle without building or reloading, and is also the Docker default.
ngn serve --dev installs locked npm dependencies and builds when assets are
missing or stale, then watches Python and frontend source to restart the backend
and rebuild frontend changes. Refresh the browser after reload; active tasks are
cancelled by a restart. Use a separate virtualenv and editable installation in
each worktree. See the web client guide for operation.
Development Workflow¶
flowchart LR
A[Fork Repo] --> B[Create Branch]
B --> C[Make Changes]
C --> D[Run Tests]
D --> E{Tests Pass?}
E -->|No| C
E -->|Yes| F[Commit]
F --> G[Push]
G --> H[Open PR]
Creating a Branch¶
# Create a feature branch
git checkout -b feature/your-feature-name
# Or a bugfix branch
git checkout -b fix/issue-description
Branch Naming
Use descriptive branch names:
feature/add-streaming-supportfix/session-manager-memory-leakdocs/update-quickstartrefactor/simplify-provider-interface
Code Standards¶
Code Style¶
We use Ruff for linting and formatting:
# Format code
poetry run ruff format .
# Lint code
poetry run ruff check .
# Fix auto-fixable issues
poetry run ruff check --fix .
Type Hints¶
All code must include type hints. We use mypy for type checking:
Type Hint Examples
Docstrings¶
Use Google-style docstrings:
def percentage(percent: float, amount: float) -> float:
"""Calculate a percentage of an amount.
Args:
percent: Percentage to calculate, such as 15.
amount: Original amount.
Returns:
The calculated portion of the amount.
Example:
>>> percentage(15, 100)
15.0
"""
return percent * amount / 100
Testing¶
Running Tests¶
# Run all tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=nagents --cov-report=html
# Run specific test file
poetry run pytest tests/agent/test_dynamic_tools.py
# Run tests matching a pattern
poetry run pytest -k "test_session"
# Run with verbose output
poetry run pytest -v
Writing Tests¶
Tests use ordinary synchronous pytest functions and asyncio.run() for async
scenarios; pytest-asyncio is not a declared dependency. Use tmp_path for
databases and synthetic credentials with mocked providers/HTTP, not real API
keys. tests/conftest.py isolates user configuration and data directories.
import asyncio
from pathlib import Path
from nagents import Message, SessionManager
def test_session_history(tmp_path: Path) -> None:
async def scenario() -> None:
manager = SessionManager(tmp_path / "test.db")
session_id = await manager.get_or_create_session("conversation-1", "user-1")
await manager.add_message(session_id, Message(role="user", content="Hello"))
history = await manager.get_history(session_id)
assert len(history) == 1
assert history[0].content == "Hello"
assert await manager.session_exists(session_id)
asyncio.run(scenario())
For an agent test with a fake provider, see tests/agent/test_dynamic_tools.py. For
HTTP-contract fixtures, see tests/providers/test_gateway_provider.py. Keep live-service
experiments explicit and separate from the default test suite.
Test Markers¶
The custom marker currently registered in tests/conftest.py is
@pytest.mark.requires_posix. Those tests are automatically skipped on
non-POSIX hosts. Select them with poetry run pytest -m requires_posix or
exclude them with poetry run pytest -m "not requires_posix".
There are no registered unit, integration, or slow marker groups. Select
tests by file or -k expression instead of assuming those categories exist.
Pre-commit Hooks¶
After pre-commit install, hooks run automatically on git commit. The checked-in
.pre-commit-config.yaml is authoritative for hook versions and arguments. It
includes whitespace, YAML, large-file and merge-conflict checks, Ruff linting
and formatting, and mypy. In particular, the YAML hook allows MkDocs' Python
tags; do not replace its configuration with a generic YAML example.
Run hooks manually:
# Run on all files
poetry run pre-commit run --all-files
# Run specific hook
poetry run pre-commit run ruff --all-files
Documentation¶
Building Docs¶
# Serve docs locally with hot reload
poetry run mkdocs serve
# Build static docs
poetry run mkdocs build --strict
The API reference is generated from this checkout's src/nagents using
mkdocstrings. Keep navigation targets and local links valid, and distinguish
research/proposals from implemented behavior. Do not infer feature availability
from a package version or an unverified upstream model name.
Documentation Style¶
Use Material for MkDocs features:
Pull Request Process¶
Before Submitting¶
- Code follows project style guidelines
- All tests pass locally
- Pre-commit hooks pass
- Documentation is updated (if applicable)
- Commit messages are clear and descriptive
PR Template¶
## Description
Brief description of changes.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
Describe how to test the changes.
## Checklist
- [ ] Tests added/updated
- [ ] Documentation updated
- [ ] CHANGELOG updated (if applicable)
Review Process¶
flowchart TD
A[Submit PR] --> B[Automated Checks]
B --> C{Checks Pass?}
C -->|No| D[Fix Issues]
D --> B
C -->|Yes| E[Code Review]
E --> F{Approved?}
F -->|Changes Requested| G[Address Feedback]
G --> E
F -->|Yes| H[Merge]
Issue Guidelines¶
Reporting Bugs¶
Bug Report Template
Description: Clear description of the bug
Steps to Reproduce: 1. Step one 2. Step two 3. ...
Expected Behavior: What should happen
Actual Behavior: What actually happens
Environment: - OS: [e.g., Ubuntu 22.04] - Python: [e.g., 3.11.4] - nagents version: [e.g., 0.1.0]
Feature Requests¶
Feature Request Template
Problem: What problem does this solve?
Proposed Solution: How should it work?
Alternatives: Other approaches considered
Additional Context: Any other information
Release Process¶
A version tag triggers the stable release workflow, including publication to
PyPI. Update [project].version in pyproject.toml in the reviewed change first.
Require the final PR commit to pass lint, every platform test job, and Build
Distribution. After merging, verify the exact merge commit, its main-branch CI
and documentation build, and the built wheel/sdist before tagging. The tag
version must match the distribution metadata; never move or reuse a release tag.
From a clean checkout of that verified merge commit, with its virtual environment active:
release_sha=$(git rev-parse HEAD)
release_version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
git tag -a "v${release_version}" "$release_sha" -m "nagents ${release_version}"
git push origin "refs/tags/v${release_version}"
Wait for the tag workflow to finish, including both package publication and the container build. Verify the published version, tag commit, and distribution hashes against that workflow's artifacts. Documentation deployment is a separate workflow; package publication does not wait for it.
Version numbering follows Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backwards compatible)
- PATCH: Bug fixes (backwards compatible)
Getting Help¶
- GitHub Issues: For bugs and feature requests
- GitHub Discussions: For questions and general discussion
- Pull Requests: For code contributions
Code of Conduct
Please be respectful and constructive in all interactions. We're all here to build something great together.
License¶
By contributing, you agree that your contributions will be licensed under the project's MIT License.