Skip to main content
VoiceGateway does not construct STT, LLM, or TTS instances for your agent. You build the native plugin yourself (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:
See Refreshing Pricing for the full workflow, including what to do when a provider changes an existing rate.

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 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:
Call 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 _PROVIDER_REGISTRY in src/voicegateway/core/registry.py:
The registry lazily imports the module on first 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.