Provider Registry¶
The registry package provides types for provider registration and discovery, enabling dynamic provider selection and configuration at runtime.
Overview¶
The registry pattern allows applications to:
- Register provider factories at startup
- Discover available providers at runtime
- Create providers with unified configuration
- Support plugin-style extensibility
┌─────────────────────────────────────────────────────────────────┐
│ Application │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Registry │ │
│ ├──────────────────┬───────────────────┬───────────────────┤ │
│ │ TTS Providers │ STT Providers │ CallSystem Provs │ │
│ │ │ │ │ │
│ │ - elevenlabs │ - deepgram │ - twilio │ │
│ │ - openai │ - openai │ - telnyx │ │
│ │ - google │ - google │ - vonage │ │
│ │ - azure │ - assemblyai │ - bandwidth │ │
│ └──────────────────┴───────────────────┴───────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Provider Creation │ │
│ │ │ │
│ │ provider, err := registry.GetTTSProvider("elevenlabs", │ │
│ │ registry.WithAPIKey(apiKey), │ │
│ │ ) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Registry Interface¶
The Registry interface defines methods for each provider category:
type Registry interface {
// TTS providers
RegisterTTSProvider(name string, factory TTSProviderFactory)
GetTTSProvider(name string, opts ...ProviderOption) (tts.Provider, error)
ListTTSProviders() []string
HasTTSProvider(name string) bool
// STT providers
RegisterSTTProvider(name string, factory STTProviderFactory)
GetSTTProvider(name string, opts ...ProviderOption) (stt.Provider, error)
ListSTTProviders() []string
HasSTTProvider(name string) bool
// CallSystem providers
RegisterCallSystemProvider(name string, factory CallSystemProviderFactory)
GetCallSystemProvider(name string, opts ...ProviderOption) (callsystem.CallSystem, error)
ListCallSystemProviders() []string
HasCallSystemProvider(name string) bool
}
Provider Factories¶
Factories are functions that create providers from configuration:
// TTS provider factory
type TTSProviderFactory func(config ProviderConfig) (tts.Provider, error)
// STT provider factory
type STTProviderFactory func(config ProviderConfig) (stt.Provider, error)
// CallSystem provider factory
type CallSystemProviderFactory func(config ProviderConfig) (callsystem.CallSystem, error)
Provider Configuration¶
ProviderConfig¶
Common configuration for all providers:
type ProviderConfig struct {
// APIKey is the authentication key for the provider
APIKey string
// BaseURL is an optional custom API endpoint
BaseURL string
// Extensions holds provider-specific configuration
Extensions map[string]any
}
ProviderOption¶
Functional options for configuring providers:
type ProviderOption func(*ProviderConfig)
// Apply options to create config
config := registry.ApplyOptions(
registry.WithAPIKey(apiKey),
registry.WithBaseURL(customURL),
)
Usage Examples¶
Registering Providers¶
Register provider factories at application startup:
import (
"github.com/plexusone/omnivoice-core/registry"
"github.com/plexusone/omnivoice-core/tts"
)
func init() {
// Register ElevenLabs TTS provider
registry.RegisterTTSProvider("elevenlabs", func(cfg registry.ProviderConfig) (tts.Provider, error) {
return elevenlabs.New(
elevenlabs.WithAPIKey(cfg.APIKey),
)
})
// Register OpenAI TTS provider
registry.RegisterTTSProvider("openai", func(cfg registry.ProviderConfig) (tts.Provider, error) {
return openaitts.New(
openaitts.WithAPIKey(cfg.APIKey),
)
})
}
Getting Providers¶
Retrieve providers by name with configuration options:
// Get TTS provider
ttsProvider, err := registry.GetTTSProvider("elevenlabs",
registry.WithAPIKey(os.Getenv("ELEVENLABS_API_KEY")),
)
if err != nil {
log.Fatal(err)
}
// Get STT provider
sttProvider, err := registry.GetSTTProvider("deepgram",
registry.WithAPIKey(os.Getenv("DEEPGRAM_API_KEY")),
)
if err != nil {
log.Fatal(err)
}
// Get CallSystem provider
callProvider, err := registry.GetCallSystemProvider("twilio",
registry.WithAPIKey(os.Getenv("TWILIO_AUTH_TOKEN")),
registry.WithExtension("account_sid", os.Getenv("TWILIO_ACCOUNT_SID")),
)
if err != nil {
log.Fatal(err)
}
Listing Available Providers¶
Discover what providers are registered:
// List all TTS providers
ttsProviders := registry.ListTTSProviders()
fmt.Println("Available TTS providers:", ttsProviders)
// Output: Available TTS providers: [elevenlabs openai google azure]
// Check if a provider exists
if registry.HasSTTProvider("deepgram") {
// Use Deepgram
}
Provider-Specific Configuration¶
Use Extensions for provider-specific settings:
provider, err := registry.GetCallSystemProvider("twilio",
registry.WithAPIKey(authToken),
registry.WithExtension("account_sid", accountSID),
registry.WithExtension("region", "us1"),
registry.WithExtension("edge", "ashburn"),
)
Configuration-Driven Provider Selection¶
From Environment Variables¶
func getProviderFromEnv(providerType, envPrefix string) (string, []registry.ProviderOption) {
name := os.Getenv(envPrefix + "_PROVIDER")
apiKey := os.Getenv(envPrefix + "_API_KEY")
opts := []registry.ProviderOption{
registry.WithAPIKey(apiKey),
}
if baseURL := os.Getenv(envPrefix + "_BASE_URL"); baseURL != "" {
opts = append(opts, registry.WithBaseURL(baseURL))
}
return name, opts
}
// Usage
ttsName, ttsOpts := getProviderFromEnv("tts", "TTS")
ttsProvider, err := registry.GetTTSProvider(ttsName, ttsOpts...)
From Configuration File¶
type VoiceConfig struct {
TTS struct {
Provider string `yaml:"provider"`
APIKey string `yaml:"api_key"`
Options map[string]any `yaml:"options"`
} `yaml:"tts"`
STT struct {
Provider string `yaml:"provider"`
APIKey string `yaml:"api_key"`
Options map[string]any `yaml:"options"`
} `yaml:"stt"`
}
func createProvidersFromConfig(cfg VoiceConfig) (tts.Provider, stt.Provider, error) {
ttsOpts := []registry.ProviderOption{
registry.WithAPIKey(cfg.TTS.APIKey),
}
for k, v := range cfg.TTS.Options {
ttsOpts = append(ttsOpts, registry.WithExtension(k, v))
}
ttsProvider, err := registry.GetTTSProvider(cfg.TTS.Provider, ttsOpts...)
if err != nil {
return nil, nil, fmt.Errorf("tts provider: %w", err)
}
sttOpts := []registry.ProviderOption{
registry.WithAPIKey(cfg.STT.APIKey),
}
for k, v := range cfg.STT.Options {
sttOpts = append(sttOpts, registry.WithExtension(k, v))
}
sttProvider, err := registry.GetSTTProvider(cfg.STT.Provider, sttOpts...)
if err != nil {
return nil, nil, fmt.Errorf("stt provider: %w", err)
}
return ttsProvider, sttProvider, nil
}
CallSystem Client¶
The callsystem package includes a Client type that manages multiple providers with automatic failover:
import "github.com/plexusone/omnivoice-core/callsystem"
// Create providers
twilioProvider, _ := registry.GetCallSystemProvider("twilio", ...)
telnyxProvider, _ := registry.GetCallSystemProvider("telnyx", ...)
// Create client with multiple providers
client := callsystem.NewClient(twilioProvider, telnyxProvider)
client.SetPrimary("twilio")
client.SetFallbacks("telnyx")
// MakeCall automatically falls back on failure
call, err := client.MakeCall(ctx, "+15559876543",
callsystem.WithFrom("+15551234567"),
)
See the CallSystem Client section for more details.
Implementing a Custom Registry¶
The registry package provides types; implement your own registry:
type MyRegistry struct {
ttsFactories map[string]registry.TTSProviderFactory
sttFactories map[string]registry.STTProviderFactory
callFactories map[string]registry.CallSystemProviderFactory
mu sync.RWMutex
}
func NewRegistry() *MyRegistry {
return &MyRegistry{
ttsFactories: make(map[string]registry.TTSProviderFactory),
sttFactories: make(map[string]registry.STTProviderFactory),
callFactories: make(map[string]registry.CallSystemProviderFactory),
}
}
func (r *MyRegistry) RegisterTTSProvider(name string, factory registry.TTSProviderFactory) {
r.mu.Lock()
defer r.mu.Unlock()
r.ttsFactories[name] = factory
}
func (r *MyRegistry) GetTTSProvider(name string, opts ...registry.ProviderOption) (tts.Provider, error) {
r.mu.RLock()
factory, ok := r.ttsFactories[name]
r.mu.RUnlock()
if !ok {
return nil, fmt.Errorf("unknown TTS provider: %s", name)
}
config := registry.ApplyOptions(opts...)
return factory(config)
}
// ... implement remaining methods
Best Practices¶
-
Register at init() - Register provider factories in
init()functions for early availability -
Validate configuration - Factories should validate required fields and return descriptive errors
-
Use Extensions sparingly - Prefer typed configuration in factories over generic Extensions
-
Thread-safe registration - Registries should be safe for concurrent access
-
Lazy initialization - Create providers when needed, not at registration time
-
Provider names - Use lowercase, hyphenated names (e.g., "elevenlabs", "google-cloud")
API Reference¶
See the GoDoc for complete API documentation.