Adding a Provider
VoiceGateway uses a provider registry pattern that makes adding new providers straightforward. Each provider is a single Python file that extends `BaseProvider`. This guide walks through the full proc
Adding a Provider
VoiceGateway uses a provider registry pattern that makes adding new providers straightforward. Each provider is a single Python file that extends BaseProvider. This guide walks through the full process.
Prerequisites
- Development environment set up
- Familiarity with the provider's API (SDK, auth, pricing)
- Account with the provider for testing
10-step checklist
1. Create the provider file
Create src/voicegateway/providers/<name>_provider.py. Use an existing provider as a template (e.g., deepgram_provider.py for STT, cartesia_provider.py for TTS).
"""<Provider Name> provider implementation."""
from __future__ import annotations
from typing import Any
from voicegateway.providers.base import BaseProvider
class <Name>Provider(BaseProvider):
"""<Provider Name> provider for <STT/LLM/TTS>."""
def __init__(self, config: dict[str, Any]) -> None:
self._api_key = config.get("api_key", "")
# Initialize any SDK client here
def create_stt(self, model: str, **kwargs: Any) -> Any:
"""Create an STT instance, or call self._unsupported('stt')."""
self._unsupported("stt")
def create_llm(self, model: str, **kwargs: Any) -> Any:
"""Create an LLM instance, or call self._unsupported('llm')."""
self._unsupported("llm")
def create_tts(self, model: str, voice: str | None = None, **kwargs: Any) -> Any:
"""Create a TTS instance, or call self._unsupported('tts')."""
self._unsupported("tts")
async def health_check(self) -> bool:
"""Check if the provider is reachable."""
# Make a lightweight API call to verify connectivity
return True2. Implement the BaseProvider ABC
The BaseProvider abstract class in src/voicegateway/providers/base.py requires four methods:
| Method | Purpose | Return type |
|---|---|---|
create_stt(model, **kwargs) | Create an STT plugin instance | LiveKit STT plugin or None |
create_llm(model, **kwargs) | Create an LLM plugin instance | LiveKit LLM plugin or None |
create_tts(model, voice, **kwargs) | Create a TTS plugin instance | LiveKit TTS plugin or None |
health_check() | Verify provider connectivity | bool |
For modalities the provider does not support, call self._unsupported("modality_name") to raise a clear error.
Pricing is not a provider-level concern. LLM, STT, and TTS rates all resolve via voice-prices (the wrappers live at src/voicegateway/pricing/{llm,stt,tts}.py). To add pricing for a new model, see step 4.
3. Register the provider
Add your provider to the registry in src/voicegateway/core/registry.py:
_PROVIDER_REGISTRY: dict[str, tuple[str, str]] = {
# ... existing providers ...
"<name>": ("voicegateway.providers.<name>_provider", "<Name>Provider"),
}The registry uses lazy imports -- your provider module is only loaded when a user configures it. This means optional dependencies do not break the install.
4. Add pricing data
Pricing for every modality (LLM, STT, TTS) resolves through voice-prices, so there is no VoiceGateway-side catalog entry to add. Confirm the model id resolves with the matching wrapper:
from voicegateway.inference.pricing import llm, stt, tts
llm.calculate_llm_cost("<name>/<model>", 1000, 500) # LLM
stt.calculate_stt_cost("<name>/<model>", 60) # STT (audio seconds)
tts.calculate_tts_cost("<name>/<model>", 1000) # TTS (characters)If a call returns None, the model is not yet in voice-prices. Add it upstream in voice-prices (each entry carries prices_checked and pricing_source_url), publish a new voice-prices version, and bump the pin in pyproject.toml. Self-hosted models (local/*, ollama/*) price at $0 automatically and need no entry.
5. Add optional dependency
Add a new extra in pyproject.toml:
[project.optional-dependencies]
<name> = ["<sdk-package>>=1.0.0"]
# Update the cloud or local group as appropriate
cloud = ["voicegateway[deepgram,openai,...,<name>]"]6. Add fake API key to test fixtures
In src/voicegateway/tests/conftest.py, add the key to the _test_env fixture:
@pytest.fixture(autouse=True)
def _test_env(monkeypatch):
for key in [
# ... existing keys ...
"<NAME>_API_KEY",
]:
monkeypatch.setenv(key, "test-key-value")7. Write tests
Create src/voicegateway/tests/test_<name>_provider.py:
"""Tests for the <Name> provider."""
import pytest
from voicegateway.providers.<name>_provider import <Name>Provider
@pytest.fixture
def provider():
return <Name>Provider({"api_key": "test-key"})
def test_create_stt(provider):
# Test STT creation or verify it raises NotImplementedError
...
def test_create_llm(provider):
...
def test_create_tts(provider):
...
async def test_health_check(provider):
# Mock the HTTP call
...
def test_pricing_resolves_stt(provider):
"""Pricing for a known STT model resolves to a positive Decimal via the catalog."""
from voicegateway.pricing import catalog
cost = catalog.calculate_cost("stt", "<name>/model-name", audio_seconds=60)
assert cost is not None and cost > 0
# For an LLM provider, dispatch with token kwargs:
#
# cost = catalog.calculate_cost(
# "llm", "<name>/model-name", input_tokens=1000, output_tokens=500
# )
#
# For a TTS provider, use character_count:
#
# cost = catalog.calculate_cost(
# "tts", "<name>/model-name", character_count=100
# )See the testing guide for mock patterns and fixture usage.
8. Update documentation
Add the provider to relevant documentation pages:
docs/guide/what-is-voicegateway.md-- provider listdocs/guide/installation.md-- extras tabledocs/configuration/-- config exampleREADME.md-- provider count and list
9. Test the full flow
# Lint
ruff check src/voicegateway/providers/<name>_provider.py
# Type check
mypy -p voicegateway.providers.<name>_provider
# Run your tests
pytest src/voicegateway/tests/providers/test_<name>_provider.py -v
# Run the full suite to check for regressions
pytest10. Open a PR
Create a PR with:
- Title:
feat(providers): add <Provider Name> support - Description: what modalities are supported, link to provider docs, pricing source
- Checklist: all items from the contributing guide
Example: anatomy of an existing provider
Looking at the registry, VoiceGateway ships with these 11 providers:
| Provider | Module | Class | Modalities |
|---|---|---|---|
| openai | openai_provider | OpenAIProvider | STT, LLM, TTS |
| deepgram | deepgram_provider | DeepgramProvider | STT, TTS |
| anthropic | anthropic_provider | AnthropicProvider | LLM |
| groq | groq_provider | GroqProvider | STT, LLM |
| cartesia | cartesia_provider | CartesiaProvider | TTS |
| elevenlabs | elevenlabs_provider | ElevenLabsProvider | TTS |
| assemblyai | assemblyai_provider | AssemblyAIProvider | STT |
| ollama | ollama_provider | OllamaProvider | LLM |
| whisper | whisper_provider | WhisperProvider | STT |
| kokoro | kokoro_provider | KokoroProvider | TTS |
| piper | piper_provider | PiperProvider | TTS |
Related pages
Contributing to VoiceGateway
Thank you for your interest in contributing to VoiceGateway. This guide covers everything you need to get started, whether you are reporting a bug, suggesting a feature, or submitting code.
Code Style
VoiceGateway enforces consistent code style through automated tooling. This page documents the rules and conventions.