SDK Reference
A provider-agnostic voice SDK. Program against one interface, swap voice backends without changing your UI code.
Why an SDK?
Voice AI moves fast. Today we use ElevenLabs ConvAI. Tomorrow we might use a custom pipeline with Claude + ElevenLabs TTS directly. The CompanionVoiceProvider interface lets you switch providers with one config change.
The interface
interface CompanionVoiceProvider {
name: string;
startSession(config: SessionConfig): Promise<VoiceSession>;
}
interface VoiceSession {
sendMessage(text: string): void;
setMicMuted(muted: boolean): void;
getInputFrequencyData(): Uint8Array | null;
getOutputFrequencyData(): Uint8Array | null;
endSession(): Promise<void>;
}
interface SessionConfig {
agentId: string;
textOnly?: boolean;
systemPrompt?: string | null;
language?: string;
callbacks: {
onConnect?: (data: { conversationId: string }) => void;
onDisconnect?: () => void;
onMessage?: (data: { message: string; source: 'user' | 'ai' }) => void;
onModeChange?: (data: { mode: 'speaking' | 'listening' | null }) => void;
onStatusChange?: (data: { status: VoiceStatus }) => void;
onError?: (message: string, context?: unknown) => void;
};
}ts
Using it
import { getVoiceProvider } from '@/app/lib/voice';
// 1. Pick a provider
const provider = getVoiceProvider('elevenlabs-convai');
// 2. Get an agent ID from the Kyndred API
const res = await fetch('/api/embed/YOUR_TOKEN/voice-token', { method: 'POST' });
const { agent_id } = await res.json();
// 3. Start a session
const session = await provider.startSession({
agentId: agent_id,
callbacks: {
onConnect: () => console.log('connected'),
onMessage: ({ message, source }) => console.log(source, ':', message),
onModeChange: ({ mode }) => console.log('mode:', mode),
onDisconnect: () => console.log('disconnected'),
onError: (msg) => console.error(msg),
},
});
// Control the session
session.sendMessage('Hello');
session.setMicMuted(true);
await session.endSession();ts
Visualization
Both frequency data accessors return Uint8Array from an AnalyserNode. Perfect for building audio visualizations (reactive rings, waveforms, etc.).
// In an animation frame loop:
function draw() {
const freqData = mode === 'speaking'
? session.getOutputFrequencyData()
: session.getInputFrequencyData();
if (freqData) {
// freqData[0..n] = amplitude per frequency bin (0-255)
const avgAmplitude = freqData.reduce((a, b) => a + b, 0) / freqData.length;
drawReactiveRing(avgAmplitude / 255);
}
requestAnimationFrame(draw);
}tsx
Available providers
| Name | Status | Backend |
|---|---|---|
| elevenlabs-convai | Available | ElevenLabs ConvAI (STT + LLM + TTS) |
| kyndred | Coming soon | Custom pipeline: Deepgram + Claude + ElevenLabs TTS |
Adding a custom provider
Implement the CompanionVoiceProvider interface and register it:
// my-custom-provider.ts
export class MyCustomProvider implements CompanionVoiceProvider {
readonly name = 'my-custom';
async startSession(config: SessionConfig): Promise<VoiceSession> {
// your implementation
}
}
// register in app/lib/voice/index.ts
const providers: Record<string, () => CompanionVoiceProvider> = {
'elevenlabs-convai': () => new ElevenLabsConvAIProvider(),
'my-custom': () => new MyCustomProvider(),
};ts