Skip to content
Docs

Multi-Provider Voice Routing

Production voice deployments rarely use a single provider for all sessions. Enterprise customers may require specific STT providers for compliance, regional latency differences may favor different TTS engines, and A/B testing demands side-by-side provider comparison. Multi-provider session routing selects the optimal voice provider combination per session based on tenant identity, geographic region, or feature flags — all without code changes. This guide covers implementing a routing layer on top of Beluga AI’s voice/session package.

Rather than hardcoding a single STT/TTS provider, session routing evaluates context at session creation time and selects the appropriate providers. This supports multi-tenant deployments, A/B testing, and regional optimization without code changes.

  • Go 1.23 or later
  • Multiple STT/TTS or S2S providers configured
  • Routing criteria defined (tenant, region, feature flags)
Terminal window
go get github.com/lookatitude/beluga-ai

Establish what drives provider selection for each session:

type RouteContext struct {
SessionID string
TenantID string
Region string
Features map[string]bool
}

Select providers based on the routing context:

import (
"context"
"github.com/lookatitude/beluga-ai/voice/stt"
"github.com/lookatitude/beluga-ai/voice/tts"
s2spkg "github.com/lookatitude/beluga-ai/voice/s2s"
)
func SelectProviders(ctx context.Context, rc RouteContext) (stt.Provider, tts.Provider, s2spkg.Provider, error) {
if rc.Features["s2s"] {
s2sProvider, err := createS2SProvider(ctx, rc.TenantID, rc.Region)
return nil, nil, s2sProvider, err
}
sttProvider, err := createSTTProvider(ctx, rc.TenantID, rc.Region)
if err != nil {
return nil, nil, nil, err
}
ttsProvider, err := createTTSProvider(ctx, rc.TenantID, rc.Region)
return sttProvider, ttsProvider, nil, err
}

Use a configuration file or database lookup to map tenant/region to concrete provider types and configurations.

import "github.com/lookatitude/beluga-ai/voice/session"
sttProvider, ttsProvider, s2sProvider, err := SelectProviders(ctx, routeCtx)
if err != nil {
return nil, err
}
if s2sProvider != nil {
return session.NewVoiceSession(ctx,
session.WithS2SProvider(s2sProvider),
session.WithConfig(session.DefaultConfig()),
)
}
return session.NewVoiceSession(ctx,
session.WithSTTProvider(sttProvider),
session.WithTTSProvider(ttsProvider),
session.WithConfig(session.DefaultConfig()),
)

Optionally attach VAD and turn detection providers selected by route:

import (
"github.com/lookatitude/beluga-ai/voice/vad"
"github.com/lookatitude/beluga-ai/voice/turndetection"
)
vadProvider, err := vad.NewProvider(ctx, "webrtc", vad.DefaultConfig())
if err != nil {
return nil, err
}
td, err := turndetection.NewProvider(ctx, "heuristic", turndetection.DefaultConfig())
if err != nil {
return nil, err
}
sess, err := session.NewVoiceSession(ctx,
session.WithSTTProvider(sttProvider),
session.WithTTSProvider(ttsProvider),
session.WithVADProvider(vadProvider),
session.WithTurnDetector(td),
session.WithConfig(session.DefaultConfig()),
)
OptionDescriptionDefault
Routing keyTenant, region, feature flagsApplication-defined
Provider mapTenant/region to STT/TTS/S2S configConfig file or DB
FallbackDefault provider when route is missingApplication-defined

Fall back to a default provider. Log and emit metrics for missing mappings to identify configuration gaps.

Validate provider configuration at startup or in a readiness check. Return clear errors with the routing context included for debugging.

The session API does not support swapping providers during an active session. Provider failover requires creating a new session. Design routing logic to execute at session start only.

  • Caching: Reuse provider instances per tenant/region to avoid creating new ones for every session
  • Observability: Tag metrics and traces with route context (tenant, region) for debugging
  • Testing: Unit-test the router with mock providers; integration-test one or two representative routes