Thank you for your interest in contributing to Vanna! This guide will help you get started with contributing to the Vanna 2.0+ codebase.
- Getting Started
- Development Setup
- Code Standards
- Testing
- Pull Request Process
- Architecture Overview
- Adding New Features
- Python 3.11 or higher
- Git
- A GitHub account
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/vanna.git cd vanna -
Add the upstream repository:
git remote add upstream https://github.com/vanna-ai/vanna.git
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate# Install the package in editable mode with all extras
pip install -e ".[all]"
# Install development tools
pip install tox ruff mypy pytest pytest-asyncio# Run unit tests
tox -e py311-unit
# Run type checking
tox -e mypy
# Run format checking
tox -e ruffWe use ruff for code formatting and linting.
# Check formatting
ruff format --check src/vanna/ tests/
# Apply formatting
ruff format src/vanna/ tests/
# Run linting
ruff check src/vanna/ tests/We use mypy with strict mode for type checking:
tox -e mypyAll new code should include type hints.
- Follow PEP 8 style guidelines
- Use descriptive variable and function names
- Add docstrings to all public functions and classes
- Keep functions focused and single-purpose
- Avoid circular imports by using
TYPE_CHECKING
Example:
"""Module docstring explaining the purpose."""
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from vanna.core.user import User
class MyClass:
"""Class docstring explaining what this class does."""
async def my_method(self, user: "User", count: int = 10) -> Optional[str]:
"""Method docstring explaining parameters and return value.
Args:
user: The user making the request
count: Maximum number of items to return
Returns:
Result string if found, None otherwise
"""
passTests are organized in the tests/ directory:
test_tool_permissions.py- Tool access control teststest_llm_context_enhancer.py- LLM enhancer teststest_legacy_adapter.py- Legacy compatibility teststest_agent_memory.py- Agent memory teststest_database_sanity.py- Database integration teststest_agents.py- End-to-end agent tests
# Run all unit tests (no external dependencies)
tox -e py311-unit
# Run specific test file
pytest tests/test_tool_permissions.py -v
# Run tests with a specific marker
pytest tests/ -v -m anthropic
# Run legacy adapter tests
tox -e py311-legacy-
Unit tests should not require external dependencies (databases, APIs, etc.)
-
Use pytest markers for tests that require external services:
@pytest.mark.anthropic @pytest.mark.asyncio async def test_with_anthropic(): # Test code here pass
-
Mock external dependencies in unit tests:
class MockLlmService(LlmService): async def send_request(self, request): # Mock implementation pass
-
Test both success and failure cases
-
Use descriptive test names that explain what is being tested
When adding new features, ensure:
- Core functionality is covered by unit tests
- Integration points are tested
- Error handling is validated
- Edge cases are considered
git checkout -b feature/my-new-feature
# or
git checkout -b fix/bug-description- Write your code following the code standards
- Add tests for your changes
- Update documentation as needed
# Format code
ruff format src/vanna/ tests/
# Run linting
ruff check src/vanna/ tests/
# Run type checking
tox -e mypy
# Run tests
tox -e py311-unitUse clear, descriptive commit messages:
git add .
git commit -m "feat: add new LLM context enhancer for RAG
- Implements TextMemoryEnhancer class
- Adds tests for memory retrieval
- Updates documentation"Commit message format:
feat:- New featurefix:- Bug fixdocs:- Documentation changestest:- Adding or updating testsrefactor:- Code refactoringchore:- Maintenance tasks
git push origin feature/my-new-featureThen create a pull request on GitHub with:
- Clear title describing the change
- Description of what was changed and why
- Link to any related issues
- Screenshots or examples if applicable
- Address review feedback promptly
- Keep discussions focused and professional
- Be open to suggestions and alternative approaches
Vanna 2.0+ is built around several key abstractions:
The main orchestrator that coordinates tools, memory, and LLM interactions.
Modular capabilities that the agent can use. Each tool:
- Has a schema defining its inputs
- Implements an
execute()method - Declares access control via
access_groups
Manages tool registration and access control.
Stores and retrieves tool usage patterns and documentation.
Abstract interface for different LLM providers (Anthropic, OpenAI, etc.).
Abstract interface for executing SQL against different databases.
Rich UI components for rendering results (tables, charts, status cards, etc.).
User Request → Agent → LLM Service → Tool Selection → Tool Execution → Response Components
↓ ↓
Agent Memory SQL Runner / Other Capabilities
- Create the tool class in
src/vanna/tools/:
from vanna.core.tool import Tool, ToolContext, ToolResult
from pydantic import BaseModel, Field
class MyToolArgs(BaseModel):
"""Arguments for my tool."""
query: str = Field(description="The query to process")
class MyTool(Tool[MyToolArgs]):
"""Tool that does something useful."""
@property
def name(self) -> str:
return "my_tool"
@property
def description(self) -> str:
return "Does something useful with a query"
def get_args_schema(self) -> type[MyToolArgs]:
return MyToolArgs
async def execute(
self,
context: ToolContext,
args: MyToolArgs
) -> ToolResult:
# Implement your tool logic
result = f"Processed: {args.query}"
return ToolResult(
success=True,
result_for_llm=result,
ui_component=None
)-
Add tests in
tests/test_my_tool.py -
Register the tool in examples or documentation
- Implement SqlRunner in
src/vanna/integrations/mydb/:
from vanna.capabilities.sql_runner import SqlRunner, RunSqlToolArgs
from vanna.core.tool import ToolContext
import pandas as pd
class MyDbRunner(SqlRunner):
"""SQL runner for MyDB database."""
def __init__(self, connection_string: str):
self.connection_string = connection_string
# Initialize your DB connection
async def run_sql(
self,
args: RunSqlToolArgs,
context: ToolContext
) -> pd.DataFrame:
# Execute SQL and return DataFrame
pass-
Add sanity tests in
tests/test_database_sanity.py -
Add tox target in
tox.ini -
Update documentation
- Implement LlmService in
src/vanna/integrations/myllm/:
from vanna.core.llm.base import LlmService
from vanna.core.llm.models import LlmRequest, LlmResponse, LlmStreamChunk
from typing import AsyncGenerator
class MyLlmService(LlmService):
"""LLM service for MyLLM provider."""
def __init__(self, api_key: str, model: str = "default"):
self.api_key = api_key
self.model = model
async def send_request(self, request: LlmRequest) -> LlmResponse:
# Implement API call
pass
async def stream_request(
self,
request: LlmRequest
) -> AsyncGenerator[LlmStreamChunk, None]:
# Implement streaming API call
yield LlmStreamChunk(...)
async def validate_tools(self, tools) -> list[str]:
# Validate tool schemas
return []-
Add tests with the
@pytest.mark.myllmmarker -
Add tox target for integration tests
- Implement AgentMemory in
src/vanna/integrations/mystore/:
from vanna.capabilities.agent_memory import (
AgentMemory,
ToolMemory,
ToolMemorySearchResult,
TextMemory,
TextMemorySearchResult
)
from vanna.core.tool import ToolContext
class MyStoreMemory(AgentMemory):
"""Agent memory using MyStore vector database."""
async def save_tool_usage(self, question, tool_name, args, context, success=True, metadata=None):
# Implement storage
pass
async def search_similar_usage(self, question, context, *, limit=10, similarity_threshold=0.7, tool_name_filter=None):
# Implement search
pass
# Implement other AgentMemory methods...-
Add tests in
tests/test_agent_memory.py -
Add to extras in
pyproject.toml
If you're working on legacy VannaBase compatibility:
- The
LegacyVannaAdapterbridges legacy code with Vanna 2.0+ - Add tests to
tests/test_legacy_adapter.py - See
src/vanna/legacy/adapter.pyfor examples
- Documentation: https://vanna.ai/docs/
- GitHub Issues: https://github.com/vanna-ai/vanna/issues
- Discussions: https://github.com/vanna-ai/vanna/discussions
By contributing to Vanna, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to Vanna! 🎉