livekit.plugins.* or pipecat.services.*) and pass it to attach() or guard(). VoiceGateway meters that instance by its model_id string and prices it through voice-prices.
That means “adding a provider” almost always means one thing: making sure the provider/model id resolves to a price. There is no VoiceGateway provider class to write for this.
Add or confirm a pricing entry
1
Check whether the model already prices
None means the model is not yet in voice-prices. Self-hosted ids (local/*, ollama/*) always return Decimal('0') and need no entry.2
Add the model to voice-prices
Add the model id, match pattern, and
prices block in the relevant provider file under voice-prices’s prices/providers/. Every entry carries a prices_checked date and a pricing_source_url. Publish a new voice-prices version.3
Bump the pin
Update the
voice-prices dependency spec in VoiceGateway’s pyproject.toml (currently voice-prices>=0.1.0,<0.2) to require the new version, then confirm it resolves:Document it
If the provider is new to VoiceGateway (not just a new model on an existing provider), add it to:docs/guide/what-is-voicegateway.md(owns the provider list)docs/guide/installation.md(owns the extras matrix)docs/configuration/providers.md(per-provider config block)
Appendix: the BaseProvider health-check path (optional)
This section is unrelated to cost tracking. Skip it unless you specifically want the provider to work with the dashboard’s Test Connection button,voicegw doctor, or the MCP server’s admin-only provider tools (test_provider, vg_test_provider_key, gated behind VOICEGW_MCP_ADMIN=1).
Those three surfaces are the only production callers of BaseProvider. Its create_stt() / create_llm() / create_tts() methods have no other callers in the codebase; attach() and guard() never touch a BaseProvider instance.
1
Create the provider file
Add Call
src/voicegateway/inference/providers/<name>_provider.py, subclassing BaseProvider from src/voicegateway/inference/providers/base_provider.py. Use an existing provider as a template, for example anthropic_provider.py:self._unsupported("<modality>") for whichever create_* methods do not apply. health_check() is the only method that runs in production; implement it against a cheap endpoint (see anthropic_provider.py’s GET /v1/models call for the pattern).2
Register it
Add an entry to The registry lazily imports the module on first
_PROVIDER_REGISTRY in src/voicegateway/core/registry.py:create_provider() call, so an uninstalled plugin does not break the rest of the install.3
Add a fake key to test fixtures
In
src/voicegateway/tests/conftest.py, add "<NAME>_API_KEY" to the _test_env fixture’s key list.4
Write a health_check test
Follow
src/voicegateway/tests/providers/test_cartesia_health_check.py: mock httpx.AsyncClient, assert health_check() returns True on 200, False on a bad status, missing key, or a network error.Registered providers
This table is
_PROVIDER_REGISTRY in src/voicegateway/core/registry.py. It governs the health-check surface above, not what attach()/guard() can meter: those meter any provider/model id that resolves in voice-prices, registry membership notwithstanding.