Contributing¶
Thank you for your interest in contributing to nagents! This guide will help you get started.
Getting Started¶
Prerequisites¶
- Python 3.11 or higher
- Poetry for dependency management
- 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 --with dev,docs # (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 nagents
# Build documentation locally
poetry run mkdocs serve
- The
--with dev,docsflag installs development and documentation dependencies
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
# Function with type hints
def process_message(
content: str,
max_tokens: int | None = None,
) -> AsyncIterator[Event]:
...
# Class with type hints
class Agent:
def __init__(
self,
provider: Provider,
session_manager: SessionManager,
tools: list[Tool] | None = None,
) -> None:
self.provider = provider
self.session_manager = session_manager
self.tools = tools or []
Docstrings¶
Use Google-style docstrings:
def create_session(
self,
metadata: dict[str, Any] | None = None,
) -> Session:
"""Create a new conversation session.
Args:
metadata: Optional metadata to attach to the session.
Returns:
The newly created Session object.
Raises:
SessionError: If session creation fails.
Example:
>>> session = await manager.create_session({"user": "alice"})
>>> print(session.id)
'sess_abc123'
"""
...
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/test_agent.py
# Run tests matching a pattern
poetry run pytest -k "test_session"
# Run with verbose output
poetry run pytest -v
Writing Tests¶
import pytest
from pathlib import Path
from nagents import SessionManager
@pytest.fixture
def session_manager(tmp_path: Path) -> SessionManager:
"""Create a session manager with temporary database."""
return SessionManager(tmp_path / "test.db")
async def test_create_session(session_manager: SessionManager):
"""Test session creation."""
session = await session_manager.create_session()
assert session.id is not None
assert session.created_at is not None
async def test_session_with_metadata(session_manager: SessionManager):
"""Test session creation with metadata."""
metadata = {"user": "test", "purpose": "testing"}
session = await session_manager.create_session(metadata=metadata)
assert session.metadata == metadata
import pytest
from pathlib import Path
from nagents import Agent, SessionManager
from nagents.providers import OpenAIProvider
@pytest.fixture
def agent(tmp_path: Path) -> Agent:
"""Create an agent for testing."""
return Agent(
provider=OpenAIProvider(model="gpt-4o-mini"),
session_manager=SessionManager(tmp_path / "test.db"),
)
@pytest.mark.integration
async def test_agent_response(agent: Agent):
"""Test that agent produces a response."""
events = []
async for event in agent.run("Say 'hello'"):
events.append(event)
# Should have at least a text event and completion
assert any(e.type == "text_delta" for e in events)
assert any(e.type == "message_complete" for e in events)
Test Markers¶
| Marker | Description | Command |
|---|---|---|
@pytest.mark.unit |
Unit tests (fast, no external deps) | pytest -m unit |
@pytest.mark.integration |
Integration tests (may need API keys) | pytest -m integration |
@pytest.mark.slow |
Slow tests | pytest -m slow |
Pre-commit Hooks¶
Pre-commit hooks run automatically on git commit:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
hooks:
- id: mypy
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
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¶
Releases are automated via GitHub Actions when a tag is pushed:
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.