Skip to content

Providers

All bundled providers share the credential shape from @ottervoice/provider-utils:

Field Use
apiKey Long-lived key (server only — never ship to clients)
tokenBrokerUrl Direct-client token mint only when credentials are short-lived and least-privilege
tokenBrokerHeaders / tokenBrokerSessionId Application auth and ownership binding for broker calls
fetch Injectable
Capability Package Transport
Streaming ASR @ottervoice/provider-deepgram WebSocket
Streaming ASR @ottervoice/provider-elevenlabs WebSocket
LLM / SSE @ottervoice/provider-openrouter HTTP SSE
TTS / STT / Audio LLM @ottervoice/provider-openrouter HTTP (demo-optional)
TTS @ottervoice/provider-azure-speech REST + SSML

Custom ASR must declare capabilities.streaming correctly — core uses it to choose live chunks vs full-turn audio.

For OpenRouter in browsers/apps, use server-managed factories. Their client options contain no model, prompt, voice, or generation controls:

import {
createOpenRouterGatewayASR,
createOpenRouterGatewayAudioLLM,
createOpenRouterGatewayVoiceTurn,
} from '@ottervoice/provider-openrouter';
// Server-composed ASR → LLM → TTS; input transcript and reply share one SSE request.
const cascadedVoice = createOpenRouterGatewayVoiceTurn({
baseUrl: '/api/voice/asr-llm-tts',
prepareAudio,
});
// A native Audio LLM may need a separate caption ASR.
const asr = createOpenRouterGatewayASR({
baseUrl: '/api/voice/asr',
format: 'webm',
});
const audioLlm = createOpenRouterGatewayAudioLLM({
baseUrl: '/api/voice/audio-llm',
prepareAudio,
});

Configure createOpenRouterGateway() on the server with locked policy and its mandatory authorize hook. The composite asr_llm_tts profile is enabled only when policy.asr, policy.llm, and policy.tts all exist; standalone audio_llm is the native speech-model route and standalone asr is only for optional captions. There are no client LLM/TTS profiles. Direct factories such as createOpenRouterLLM and createOpenRouterTTS are trusted-backend building blocks for a composite AudioLLMProvider.

Direct WebSocket services such as Deepgram may still use a broker when the issued credential is short-lived and tightly scoped:

import { createDeepgramASR } from '@ottervoice/provider-deepgram';
const asr = createDeepgramASR({
tokenBrokerUrl: '/api/voice/token',
tokenBrokerHeaders: { authorization: `Bearer ${applicationSessionToken}` },
tokenBrokerSessionId: voiceSessionId,
});

Short-lived is not sufficient by itself. If a token can still select arbitrary models/routes or spend the account budget, keep it behind a policy gateway.

Credential details: provider-utils. Factory option tables: API reference and each package’s JSDoc.