Creates a new SpeechProviderResolver.
Optional config: SpeechResolverConfigOptional resolver configuration controlling preferred providers and fallback behaviour for each provider kind.
Environment variable map used to check whether a provider's
required API keys are present. Defaults to process.env so unit tests
can inject a controlled map without polluting the real environment.
// Production: use real env vars
const resolver = new SpeechProviderResolver({ stt: { fallback: true } });
// Testing: inject controlled env
const resolver = new SpeechProviderResolver(undefined, { OPENAI_API_KEY: 'test' });
Register a provider, overwriting any existing registration with the same
kind and id. Re-registering at the priority the resolver shows (a preferred
boost included), as register({ ...entry, provider }) does, keeps the
priority the provider was first registered with.
Emits a provider_registered event with { id, kind, source } so that
listeners (e.g. UI dashboards, logging middleware) can track what's available.
The full registration object including the provider instance, catalog entry, priority, and configuration status.
resolver.register({
id: 'custom-stt',
kind: 'stt',
provider: myCustomProvider,
catalogEntry: { id: 'custom-stt', kind: 'stt', label: 'Custom', envVars: [], local: false, description: '' },
isConfigured: true,
priority: 150,
source: 'extension',
});
List all registrations for a given provider kind, sorted ascending by priority (lower number = higher priority = tried first).
This returns both configured and unconfigured providers — use
.filter(r => r.isConfigured && r.provider) for the ones the resolve
methods can return (keys set and an instance registered).
The provider kind to filter by.
A new array of registrations sorted by ascending priority.
const allSTT = resolver.listProviders('stt');
const ready = allSTT.filter(r => r.isConfigured && r.provider);
console.log(`${ready.length} of ${allSTT.length} STT providers ready`);
Resolve the best STT provider matching optional ProviderRequirements.
When config.stt.fallback is true and multiple candidates exist, wraps
them in a FallbackSTTProxy that tries providers in priority order.
Otherwise returns the single highest-priority match.
Optional requirements: ProviderRequirementsOptional filtering criteria (streaming, local, features, preferredIds).
The resolved STT provider, possibly wrapped in a FallbackSTTProxy.
When no configured STT provider with an instance matches the requirements.
// Simple: best available
const stt = resolver.resolveSTT();
// With requirements
const stt = resolver.resolveSTT({ streaming: true, features: ['diarization'] });
// With explicit preference ordering
const stt = resolver.resolveSTT({ preferredIds: ['deepgram-batch', 'openai-whisper'] });
Resolve the best TTS provider matching optional ProviderRequirements.
When config.tts.fallback is true and multiple candidates exist, wraps
them in a FallbackTTSProxy that tries providers in priority order.
Otherwise returns the single highest-priority match.
Optional requirements: ProviderRequirementsOptional filtering criteria (streaming, local, features, preferredIds).
The resolved TTS provider, possibly wrapped in a FallbackTTSProxy.
When no configured TTS provider with an instance matches the requirements.
const tts = resolver.resolveTTS();
const result = await tts.synthesize('Hello!');
Resolve the highest-priority configured VAD provider.
VAD providers don't support fallback chains because voice activity detection is a real-time frame-by-frame operation where mid-session provider switching would cause state inconsistency.
The resolved VAD provider instance.
When no configured VAD provider with an instance is registered.
const vad = resolver.resolveVAD();
const decision = vad.processFrame(audioFrame);
Resolve the highest-priority configured wake-word provider, or null
when none is registered.
Returns null instead of throwing because wake-word detection is an
optional feature — many deployments operate without it.
The resolved wake-word provider, or null if none is available.
const wakeWord = resolver.resolveWakeWord();
if (wakeWord) {
const detection = await wakeWord.detect(frame, sampleRate);
}
Populate the resolver by registering core providers from the static catalog, optionally discovering extension providers, and applying user-configured preferred priorities.
The refresh sequence is:
discoverExtensionProviders() — if an ExtensionManager is provided, read
the speech providers it lists as active, and drop the ones an earlier
refresh read from it that it no longer lists.registerCoreProviders() — register all built-in providers from the
static catalog, marking each as configured/unconfigured based on env vars.applyPreferredPriorities() — boost priority for providers listed in
the user's config.stt.preferred / config.tts.preferred arrays.Optional extensionManager: anyOptional ExtensionManager (its speech provider
registries are read through getRegistry(kind).listActive()), or any
object exposing getDescriptorsByKind(kind). Uses any type because
importing the ExtensionManager type here would create a circular dependency.
const runtime = new SpeechRuntime({ env: process.env });
await runtime.resolver.refresh(extensionManager);
// The manager's speech providers now resolve beside the runtime's own.
Validates language, PCM format, immutable models/voices, options and every fallback before construction.
Optional optionsOptional credentialOptional optionsOptional credentialOptional optionsOptional optionsResolves a scoped batch provider through the same registered options, credentials and request budget.
Optional onSupplierUsage: VoiceSupplierUsageSink
Central resolver for speech providers (STT, TTS, VAD, wake-word).
Resolution Algorithm
Registration — Providers are registered via
register()with a uniqueid, akind(stt/tts/vad/wake-word), a numericpriority, and a booleanisConfiguredflag.Filtering — When a consumer calls
resolveSTT(),resolveTTS(),resolveVAD(), orresolveWakeWord(), the resolver filters registrations bykind,isConfigured === trueand a registered provider instance (provider). A core catalog entry with its keys set but no instance does not resolve.Requirements matching — Optional ProviderRequirements further filter by
streaming,local, andfeaturescapabilities from the provider's catalog entry.Priority ordering — Remaining candidates are sorted by ascending
priority(lower number = tried first). Core providers default to 100, extensions to 200, and user-preferred providers get boosted to 50+.Fallback wrapping — When
config.stt.fallbackorconfig.tts.fallbackistrueand multiple candidates survive, they are wrapped in a FallbackSTTProxy or FallbackTTSProxy that tries each in order, emittingprovider_fallbackevents on failure.Priority Tiers
config.stt.preferred/config.tts.preferredEvents
provider_registered{ id, kind, source }register()See
Example