Skip to content

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

# Install dependencies
poetry install

# Install pre-commit hooks
poetry run pre-commit install

# Verify setup
poetry run pytest
# 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
  1. The --with dev,docs flag 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-support
  • fix/session-manager-memory-leak
  • docs/update-quickstart
  • refactor/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:

poetry run mypy nagents
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

tests/test_session.py
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
tests/test_integration.py
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:

.pre-commit-config.yaml
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:

!!! note "Title"
    Note content here.

!!! warning
    Warning without custom title.

??? tip "Collapsible"
    Hidden by default.
```python title="example.py" linenums="1" hl_lines="2 3"
def hello():
    message = "Hello"  # Highlighted
    print(message)     # Highlighted
```
=== "Python"
    ```python
    print("Hello")
    ```

=== "JavaScript"
    ```javascript
    console.log("Hello");
    ```

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:

# Create a new version tag
git tag v0.2.0
git push origin v0.2.0

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.