🀝 Contributing to AI Cortex#

AI Cortex is an open-source project and we welcome contributions of all kinds β€” bug fixes, new features, documentation improvements, model additions, and more. This guide walks you through everything you need to get started.

πŸ“‹ Table of Contents#

🏁 Before You Start#

Find Something to Work On#

Browse the Issues page and look for:

Label

Meaning

good first issue

Great for newcomers β€” well-scoped with clear requirements

help wanted

Maintainers are actively seeking contributions here

bug

Something is broken and needs a fix

enhancement

New feature or improvement to existing behavior

documentation

Docs-only change, no code needed

If you have an idea that isn’t tracked yet, open an issue to discuss it before building β€” this avoids duplicate work and ensures alignment with project goals.

πŸ› οΈ Setting Up Your Environment#

Prerequisites#

  • Python 3.8+ (3.11 recommended)

  • Git

  • Ollama β€” for running integration tests against a real server

Clone and Install#

# 1. Fork the repo on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/aicortex.git
cd aicortex

# 2. Create a virtual environment
python -m venv venv
source venv/bin/activate        # macOS/Linux
# venv\Scripts\activate         # Windows

# 3. Install in editable mode with all dev dependencies
pip install -e .[dev,server]

# 4. (Optional) Install pre-commit hooks for automatic quality checks
pip install pre-commit
pre-commit install

Verify Your Setup#

# Run the test suite β€” all tests should pass
pytest

# Check types
mypy aicortex

# Check formatting
black --check aicortex tests

# Run everything via tox
tox

If everything passes, you’re ready to contribute.

πŸ”„ Development Workflow#

1. 🌿 Create a Branch#

Branch names should be descriptive and follow this convention:

# New feature
git checkout -b feature/async-chat-support

# Bug fix (reference the issue number)
git checkout -b fix/123-streaming-timeout

# Documentation update
git checkout -b docs/update-quickstart

2. ✏️ Make Your Changes#

Write your code following the Code Standards below. As you work:

  • Keep commits small and focused β€” one logical change per commit

  • Write tests alongside new code, not after

  • Update documentation if your change affects public behavior

3. πŸ§ͺ Run Quality Checks#

Before committing, run the full quality suite:

# Format code (auto-fixes in place)
black aicortex tests

# Static type checking
mypy aicortex

# Lint for common issues
flake8 aicortex tests

# Run tests with coverage
pytest --cov=aicortex --cov-report=term-missing

All checks must pass before submitting a PR.

4. πŸ“ Commit Your Changes#

Use Conventional Commits format:

git commit -m "feat: add async support for chat()"
git commit -m "fix: resolve streaming timeout on slow connections"
git commit -m "docs: add examples for tool pipeline usage"
git commit -m "test: add unit tests for resolve_models edge cases"
git commit -m "refactor: extract server discovery into helper"
git commit -m "chore: bump ollama dependency to 0.3.0"

Prefix reference:

Prefix

Use for

feat:

New features

fix:

Bug fixes

docs:

Documentation only

style:

Formatting, no logic changes

refactor:

Code restructuring without behavior change

test:

Adding or updating tests

chore:

Maintenance tasks, dependency updates

5. πŸš€ Push and Open a PR#

git push origin feature/your-feature-name

Then open a Pull Request on GitHub against the main branch.

πŸ“ Code Standards#

Python Style#

  • Follow PEP 8 β€” enforced by black and flake8

  • Maximum line length: 88 characters (Black default)

  • Type hints required for all function parameters and return values

  • Docstrings required for all public functions, classes, and methods

Naming Conventions#

Thing

Convention

Example

Functions & methods

snake_case

get_model_info()

Classes

PascalCase

StreamEvent

Constants

UPPER_CASE

DEFAULT_HOST

Private members

_leading_underscore

_parse_response()

Modules

snake_case

fetch_models.py

Import Order#

# 1. Standard library
import os
import json
from pathlib import Path
from typing import Iterator, List

# 2. Third-party
import ollama
from pydantic import BaseModel

# 3. Local imports
from .api import _OllamaAPI
from .chat import Stream, StreamEvent

Docstring Format#

Use Google-style docstrings:

def chat(prompt: str, *, model: str = "llama3.2:3b", stream: bool = False) -> str:
    """Send a prompt to a model and return its response.

    A high-level convenience function that handles model selection,
    server discovery, and response parsing automatically.

    Args:
        prompt: The text prompt to send to the model.
        model: Model identifier in ``family:size`` format.
            Defaults to ``"llama3.2:3b"``.
        stream: If ``True``, returns a :class:`Stream` object instead
            of a plain string. Defaults to ``False``.

    Returns:
        The model's response as a plain string, or a :class:`Stream`
        object if ``stream=True``.

    Raises:
        ModelNotFoundError: If the specified model is not available
            on any configured server.
        ServerError: If all servers are unreachable.

    Example:
        >>> response = chat("What is machine learning?")
        >>> print(response)
        'Machine learning is a subset of artificial intelligence...'
    """

πŸ§ͺ Testing#

Test Structure#

tests/
β”œβ”€β”€ __init__.py
β”œβ”€β”€ conftest.py              # Shared fixtures and mock setup
β”œβ”€β”€ test_chat.py             # chat() function tests
β”œβ”€β”€ test_api.py              # _OllamaAPI class tests
β”œβ”€β”€ test_models.py           # Model loading and metadata tests
β”œβ”€β”€ test_tools.py            # Tool pipeline tests
β”œβ”€β”€ test_server.py           # FastAPI server endpoint tests
└── fixtures/
    β”œβ”€β”€ mock_responses.json  # Pre-recorded API responses
    └── test_models.json     # Minimal model metadata for tests

Writing Tests#

import pytest
from unittest.mock import Mock, patch
from aicortex import chat, families, models


class TestChat:
    """Tests for the chat() function."""

    def test_chat_returns_string(self, mock_ollama_client):
        """chat() should return a non-empty string for valid prompts."""
        response = chat("Hello")
        assert isinstance(response, str)
        assert len(response) > 0

    def test_chat_with_explicit_model(self, mock_ollama_client):
        """chat() should accept a model parameter."""
        response = chat("Hello", model="mistral:7b")
        assert isinstance(response, str)

    def test_chat_raises_on_invalid_model(self, mock_ollama_client):
        """chat() should raise ModelNotFoundError for unknown models."""
        from aicortex.exceptions import ModelNotFoundError
        with pytest.raises(ModelNotFoundError):
            chat("Hello", model="nonexistent:model")

Mocking Ollama#

Tests must not require a live Ollama server. Use the shared fixture from conftest.py:

@pytest.fixture
def mock_ollama_client():
    """Patch ollama.Client with a pre-configured mock."""
    with patch("ollama.Client") as mock_class:
        instance = Mock()
        instance.chat.return_value = {
            "message": {"content": "Mocked response"},
            "done": True,
        }
        instance.list.return_value = {
            "models": [{"name": "llama3.2:3b"}, {"name": "mistral:7b"}]
        }
        mock_class.return_value = instance
        yield instance

Coverage Requirements#

  • Aim for 90%+ coverage on all new code

  • Check your coverage before submitting:

pytest --cov=aicortex --cov-report=term-missing --cov-fail-under=90

πŸ“– Documentation#

If your change affects user-facing behavior, update the relevant docs in docs/.

Changed

Update

Public function signature or behavior

docs/api.md

Streaming behavior

docs/streaming.md

Installation or dependencies

docs/installation.md

Model metadata schema

docs/models.md

Tool pipeline

docs/tools.md

Server endpoints

docs/server.md

Also update any relevant code examples in examples/ to reflect the change.

Building the Docs Locally#

# Install Sphinx dependencies
pip install -e .[dev]

# Build HTML docs
tox -e docs

# Open in browser (macOS)
open docs/_build/html/index.html

πŸ“¬ Pull Request Process#

PR Checklist#

Before submitting, confirm:

  • All tests pass: pytest

  • Types check: mypy aicortex

  • Code is formatted: black --check aicortex tests

  • No new lint errors: flake8 aicortex tests

  • New code has tests with adequate coverage

  • Documentation updated for any public API changes

  • Commit messages follow Conventional Commits format

  • PR description clearly explains the change and its motivation

PR Description Template#

## Summary
Brief description of what this PR does and why.

## Type of Change
- [ ] Bug fix (non-breaking)
- [ ] New feature (non-breaking)
- [ ] Breaking change (existing functionality changes)
- [ ] Documentation update
- [ ] Refactor / internal improvement

## Testing
Describe how you tested the changes:
- Unit tests added/updated: yes/no
- Tested against live Ollama server: yes/no
- Coverage delta: +X%

## Related Issues
Closes #123

Review Process#

  1. βœ… Automated CI β€” Tests, types, formatting, and linting run automatically

  2. πŸ‘€ Code review β€” A maintainer reviews your changes within a few days

  3. πŸ’¬ Address feedback β€” Respond to comments and push updates as needed

  4. βœ… Approval & merge β€” Once approved, a maintainer merges the PR

πŸ› Reporting Issues#

Bug Reports#

When reporting a bug, include:

**Describe the bug**
A clear description of what is happening vs. what you expected.

**To Reproduce**
Minimal code to reproduce the issue:

    import aicortex
    response = aicortex.chat("Hello", model="llama3.2:3b")
    # -> Error: ...

**Environment**
- OS: macOS 14.3 / Ubuntu 22.04 / Windows 11
- Python: 3.11.2
- AI Cortex: 1.0.3
- Ollama: 0.3.1

**Full traceback**
Paste the complete error output here.

Feature Requests#

When requesting a feature:

**Problem**
What are you trying to do that you can't currently do?

**Proposed Solution**
What API or behavior would solve it?
    response = aicortex.chat("Hello", timeout=30)  # example

**Alternatives Considered**
What workarounds have you tried?

βž• Adding Models & Features#

Adding a New Model Family#

  1. Create a new JSON file in aicortex/models/ β€” see llama.json for the schema

  2. Follow the existing field structure: name, family, size, parameters, servers

  3. Add at least one server entry to make the model immediately usable

  4. Test model loading: from aicortex import models; print(models("yourfamily"))

  5. Update docs/models.md with the new family

Adding a New Tool#

  1. Create a new module in aicortex/tools/

  2. Export it in aicortex/tools/__init__.py

  3. Add type stubs in aicortex/stubs/tools/ if it’s part of the public API

  4. Add CLI entry point in setup.py if the tool should be runnable from the command line

  5. Write tests in tests/test_tools.py

  6. Document it in docs/tools.md

Adding a New Public Function#

  1. Implement in the appropriate module (api.py, chat.py, etc.)

  2. Export from aicortex/__init__.py

  3. Add type stub to aicortex/stubs/__init__.pyi

  4. Write tests

  5. Document in docs/api.md with full parameter table and example

🌍 Community#

Getting Help#

Code of Conduct#

We are committed to a welcoming, inclusive community. In all interactions:

  • Be respectful and patient β€” people are here to learn and help

  • Give constructive, specific feedback rather than vague criticism

  • Assume good intent unless clearly demonstrated otherwise

  • Help newcomers get oriented rather than dismissing their questions

Recognition#

All contributors are recognized in release notes and the GitHub contributor graph. Significant contributions may be highlighted in the CHANGELOG.md.

Thank you for helping make AI Cortex better. Every contribution β€” no matter how small β€” is valued. πŸš€