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 id.
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) if you only want usable ones.
The provider kind to filter by.
A new array of registrations sorted by ascending priority.
const allSTT = resolver.listProviders('stt');
const configured = allSTT.filter(r => r.isConfigured);
console.log(`${configured.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 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 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 VAD provider is registered and configured.
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 three-phase refresh sequence is:
registerCoreProviders() — register all built-in providers from the
static catalog, marking each as configured/unconfigured based on env vars.discoverExtensionProviders() — if an ExtensionManager is provided,
discover and register any additional speech providers from extensions.applyPreferredPriorities() — boost priority for providers listed in
the user's config.stt.preferred / config.tts.preferred arrays.Optional extensionManager: anyOptional object exposing getDescriptorsByKind(kind).
Uses any type because the ExtensionManager interface is defined in the
extensions package and importing it here would create a circular dependency.
const resolver = new SpeechProviderResolver(config, process.env);
await resolver.refresh(extensionManager);
// Now all providers are registered and ready to resolve.
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 bykindandisConfigured === true.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