Pular para o conteúdo principal

Voice SDK

O VoiceSDK fornece recursos avançados de conversação por voz com integração automática ao sistema de chat e hooks do React para construir aplicativos habilitados por voz. Crie interações de voz naturais com agentes de IA, completas com visualização de áudio, transcrição e gerenciamento de conversas. Neste artigo, você encontrará um exemplo de Início Rápido que não apenas o fará começar rapidamente, mas também o ajudará a entender como funciona. O restante do artigo detalha os vários métodos que você pode usar, para que servem e práticas recomendadas.

Instalação​

npm install @odin-ai-staging/sdk @elevenlabs/react

Início Rápido​

Conversação Básica por Voz​

Neste exemplo, você aprenderá como implementar conversas por voz em tempo real usando o VoiceSDK em aplicações TypeScript e React. Você começa inicializando o VoiceSDK com suas credenciais de API, incluindo um agentId específico que define qual agente de voz de IA lidará com a conversa, em seguida usa startVoiceConversation() para iniciar uma sessão de voz com handlers de callback que permitem responder a eventos de conexão, receber mensagens da IA, lidar com desconexões e processar transcrições ao vivo do que o usuário está dizendo (com isFinal indicando quando uma frase completa foi reconhecida). A opção saveToChat permite persistir a conversa por voz como texto no seu histórico de chat para referência posterior. Para aplicações React, você usará o hook useVoiceConversation, que fornece uma interface mais limpa com gerenciamento de estado integrado: ele fornece uma variável status para rastrear o estado da conexão, métodos startSession() e endSession() para controlar a conversa, setVolume() para ajustar os níveis de áudio, getInputByteFrequencyData() para visualizar a entrada de áudio (perfeito para criar exibições de forma de onda) e conversationState que contém informações em tempo real como níveis de volume atuais. Isso fornece tudo que você precisa para construir aplicativos de IA habilitados por voz com reconhecimento e síntese de fala em tempo real, perfeito para criar assistentes de voz, interfaces mãos livres ou experiências de IA conversacional — com o hook do React cuidando de todo o complexo gerenciamento de estado e conexões WebSocket para você.

import { VoiceSDK } from '@odin-ai-staging/sdk';

// Initialize the SDK
const voiceSDK = new VoiceSDK({
baseUrl: 'https://your-api-endpoint.com/',
projectId: 'your-project-id',
apiKey: 'your-api-key',
apiSecret: 'your-api-secret',
agentId: 'your-agent-id'
});

// Start a voice conversation
async function startVoiceChat() {
const sessionId = await voiceSDK.startVoiceConversation({
saveToChat: true,
callbacks: {
onConnect: () => console.log('Voice connected'),
onMessage: (message) => console.log('Voice message:', message),
onDisconnect: () => console.log('Voice disconnected'),
onTranscription: (text, isFinal) => {
if (isFinal) console.log('User said:', text);
}
}
});

console.log('Voice session started:', sessionId);
}

Uso do Hook React​

import { useVoiceConversation } from '@odin-ai-staging/sdk';

function VoiceChat() {
const {
status,
startSession,
endSession,
setVolume,
getInputByteFrequencyData,
conversationState
} = useVoiceConversation({
sdkConfig: {
baseUrl: 'https://your-api-endpoint.com/',
projectId: 'your-project-id',
agentId: 'your-agent-id'
},
callbacks: {
onConnect: () => console.log('Connected!'),
onMessage: (message) => console.log('Message:', message)
}
});

return (
<div>
<button
onClick={() => startSession()}
disabled={status === 'connected'}
>
Start Voice Chat
</button>

<button
onClick={() => endSession()}
disabled={status !== 'connected'}
>
End Chat
</button>

<div>Status: {status}</div>
<div>Volume: {conversationState.volume}</div>
</div>
);
}

Configuração​

Interface VoiceSDKConfig​

interface VoiceSDKConfig extends BaseClientConfig {
agentId?: string; // Default agent ID for conversations
defaultVoiceSettings?: VoiceSettings; // Default voice configuration
}

VoiceSettings​

interface VoiceSettings {
stability?: number; // Voice stability (0.0 to 1.0)
similarityBoost?: number; // Voice similarity boost (0.0 to 1.0)
style?: number; // Voice style (0.0 to 1.0)
useSpeakerBoost?: boolean; // Enable speaker boost
}

Exemplo de Configuração:

const voiceSDK = new VoiceSDK({
baseUrl: 'https://api.example.com/',
projectId: 'proj_123',
apiKey: 'your-api-key',
apiSecret: 'your-api-secret',
agentId: 'agent_456',
defaultVoiceSettings: {
stability: 0.8,
similarityBoost: 0.7,
style: 0.3,
useSpeakerBoost: true
}
});

Recursos Principais​

Sessões de Conversação por Voz​

O VoiceSDK gerencia sessões de conversação por voz com integração automática ao chat:

interface VoiceConversationSession {
id: string; // Session identifier
chatId?: string; // Associated chat ID
startTime: number; // Session start timestamp
endTime?: number; // Session end timestamp
messages: VoiceMessage[]; // Voice messages in session
metadata?: {
agentId?: string;
voiceSettings?: VoiceSettings;
totalDuration?: number;
userInfo?: { name: string; id: string };
};
}

Mensagens de Voz​

interface VoiceMessage {
id: string; // Message ID
type: 'user_speech' | 'ai_speech' | 'system';
text: string; // Transcribed/generated text
audioUrl?: string; // Audio file URL
timestamp: number; // Message timestamp
duration?: number; // Audio duration in seconds
voiceSettings?: VoiceSettings; // Voice settings used
saved?: boolean; // Whether saved to database
}

Gerenciamento de Sessões​

startVoiceConversation(options?)​

Inicie uma nova sessão de conversação por voz.

async startVoiceConversation(
options?: StartVoiceConversationOptions
): Promise<string>

StartVoiceConversationOptions:

interface StartVoiceConversationOptions {
callbacks?: VoiceConversationCallbacks;
saveToChat?: boolean; // Auto-save to chat history
existingChatId?: string; // Continue existing chat
agentId?: string; // Override default agent
voiceSettings?: VoiceSettings; // Custom voice settings
userInfo?: { name: string; id: string };
}

Exemplo:

const sessionId = await voiceSDK.startVoiceConversation({
saveToChat: true,
existingChatId: 'chat_123',
voiceSettings: {
stability: 0.9,
similarityBoost: 0.8
},
userInfo: {
name: 'John Doe',
id: 'user_456'
},
callbacks: {
onConnect: () => console.log('Voice conversation started'),
onMessage: (message) => handleVoiceMessage(message),
onTranscription: (text, isFinal) => {
if (isFinal) displayTranscription(text);
},
onConversationSaved: (chatId, messageId) => {
console.log(`Conversation saved to chat ${chatId}`);
}
}
});

endVoiceSession(sessionId, reason?)​

Finalize uma sessão de conversação por voz.

async endVoiceSession(sessionId: string, reason?: string): Promise<void>

Exemplo:

await voiceSDK.endVoiceSession(sessionId, 'User ended conversation');

getVoiceState(sessionId)​

Obtenha o estado atual da conversação por voz.

getVoiceState(sessionId: string): VoiceConversationState | null

Exemplo:

const state = voiceSDK.getVoiceState(sessionId);
if (state) {
console.log('Connection status:', state.connectionStatus);
console.log('Is speaking:', state.isSpeaking);
console.log('Volume:', state.volume);
}

Integração com React​

Hook useVoiceConversation​

O hook useVoiceConversation fornece integração com React e gerenciamento de estado:

function useVoiceConversation(options: VoiceHookOptions): {
// Hook properties
status: VoiceStatus;
isSpeaking: boolean;
startSession: (config?: VoiceSessionConfig) => Promise<string>;
endSession: () => Promise<void>;
setVolume: (options: { volume: number }) => void;

// Enhanced SDK properties
conversationState: VoiceConversationState;
currentSessionId: string | null;
getInputByteFrequencyData: () => Uint8Array | null;
getOutputByteFrequencyData: () => Uint8Array | null;
}

Exemplo Completo com React:

import React, { useState } from 'react';
import { useVoiceConversation } from '@odin-ai-staging/sdk';

function VoiceConversationComponent() {
const [messages, setMessages] = useState<string[]>([]);
const [isRecording, setIsRecording] = useState(false);

const {
status,
isSpeaking,
startSession,
endSession,
setVolume,
conversationState,
currentSessionId,
getInputByteFrequencyData
} = useVoiceConversation({
sdkConfig: {
baseUrl: process.env.REACT_APP_API_BASE_URL,
projectId: process.env.REACT_APP_PROJECT_ID,
agentId: process.env.REACT_APP_AGENT_ID
},
callbacks: {
onConnect: () => {
console.log('Connected to voice chat');
setIsRecording(true);
},
onDisconnect: () => {
console.log('Disconnected from voice chat');
setIsRecording(false);
},
onTranscription: (text, isFinal) => {
if (isFinal) {
setMessages(prev => [...prev, `You: ${text}`]);
}
},
onMessage: (message) => {
if (message.type === 'ai_speech') {
setMessages(prev => [...prev, `AI: ${message.text}`]);
}
},
onError: (error) => {
console.error('Voice error:', error);
setIsRecording(false);
}
}
});

const handleStartConversation = async () => {
try {
await startSession({
saveToChat: true,
voiceSettings: {
stability: 0.8,
similarityBoost: 0.7
}
});
} catch (error) {
console.error('Failed to start conversation:', error);
}
};

const handleEndConversation = async () => {
try {
await endSession();
} catch (error) {
console.error('Failed to end conversation:', error);
}
};

const handleVolumeChange = (volume: number) => {
setVolume({ volume });
};

return (
<div className="voice-conversation">
<div className="controls">
<button
onClick={handleStartConversation}
disabled={status === 'connected'}
>
Start Voice Chat
</button>

<button
onClick={handleEndConversation}
disabled={status !== 'connected'}
>
End Voice Chat
</button>
</div>

<div className="status">
<div>Status: {status}</div>
<div>Speaking: {isSpeaking ? 'Yes' : 'No'}</div>
<div>Recording: {isRecording ? 'Yes' : 'No'}</div>
<div>Volume: {conversationState.volume}</div>
</div>

<div className="volume-control">
<label>Volume:</label>
<input
type="range"
min="0"
max="100"
value={conversationState.volume}
onChange={(e) => handleVolumeChange(parseInt(e.target.value))}
/>
</div>

<div className="messages">
{messages.map((message, index) => (
<div key={index} className="message">
{message}
</div>
))}
</div>

{currentSessionId && (
<AudioVisualizer
getInputData={getInputByteFrequencyData}
isActive={status === 'connected'}
/>
)}
</div>
);
}

Controles de Voz​

Controle de Volume​

// Set volume (0-100)
await voiceSDK.setVolume(sessionId, 75);

Controle do Microfone​

// Mute/unmute microphone
await voiceSDK.setMicrophoneMuted(sessionId, true); // Mute
await voiceSDK.setMicrophoneMuted(sessionId, false); // Unmute

Atualizações de Configuração de Voz​

// Update voice settings during conversation
await voiceSDK.updateVoiceSettings(sessionId, {
stability: 0.9,
similarityBoost: 0.8,
style: 0.4
});

Visualização de Áudio​

Dados de Áudio em Tempo Real​

// Get audio frequency data for visualization
const audioData = voiceSDK.getAudioFrequencyData(sessionId);

if (audioData) {
const inputData = audioData.input; // User's audio input
const outputData = audioData.output; // AI's audio output

// Use for audio visualization
renderAudioVisualization(inputData, outputData);
}

Componente de Visualização de Áudio​

import React, { useRef, useEffect } from 'react';

interface AudioVisualizerProps {
getInputData: () => Uint8Array | null;
isActive: boolean;
}

function AudioVisualizer({ getInputData, isActive }: AudioVisualizerProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);

useEffect(() => {
if (!isActive) return;

const canvas = canvasRef.current;
if (!canvas) return;

const ctx = canvas.getContext('2d');
if (!ctx) return;

const animate = () => {
const data = getInputData();

if (data) {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Draw frequency bars
const barWidth = canvas.width / data.length;

for (let i = 0; i < data.length; i++) {
const barHeight = (data[i] / 255) * canvas.height;

ctx.fillStyle = `hsl(${i * 2}, 100%, 50%)`;
ctx.fillRect(
i * barWidth,
canvas.height - barHeight,
barWidth - 1,
barHeight
);
}
}

requestAnimationFrame(animate);
};

animate();
}, [isActive, getInputData]);

return (
<canvas
ref={canvasRef}
width={400}
height={100}
className="audio-visualizer"
/>
);
}

Integração com Chat​

Salvamento Automático de Chat​

Conversas por voz podem ser salvas automaticamente no seu sistema de chat:

const sessionId = await voiceSDK.startVoiceConversation({
saveToChat: true, // Enable automatic saving
existingChatId: 'chat_123', // Optional: continue existing chat
callbacks: {
onConversationSaved: (chatId, messageId) => {
console.log(`Voice conversation saved to chat ${chatId}`);
// Update UI to show the saved conversation
refreshChatHistory(chatId);
}
}
});

Integração Manual de Chat​

// Get conversation history from voice session
const messages = voiceSDK.getConversationHistory(sessionId);

// Save to chat manually
for (const message of messages) {
if (message.type === 'user_speech') {
await chatSDK.sendMessage(message.text, {
chatId: 'chat_123',
metadata: {
voiceMessage: true,
audioUrl: message.audioUrl,
sessionId: sessionId
}
});
}
}

Atualizações Contextuais​

Envie contexto adicional para a conversação por voz:

// Send context from chat history
await voiceSDK.sendContextualUpdate(
sessionId,
'User previously asked about pricing. Current conversation is about features.'
);

Tratamento de Erros​

try {
const sessionId = await voiceSDK.startVoiceConversation({
callbacks: {
onError: (error) => {
console.error('Voice conversation error:', error);

// Handle specific error types
if (error.message.includes('microphone')) {
showMicrophonePermissionDialog();
} else if (error.message.includes('network')) {
showNetworkErrorMessage();
}
},
onDisconnect: (details) => {
console.log('Disconnected:', details?.reason);

// Handle different disconnection reasons
if (details?.reason === 'user_ended') {
showConversationSummary();
} else if (details?.reason === 'error') {
showReconnectOption();
}
}
}
});
} catch (error) {
console.error('Failed to start voice conversation:', error);

if (error.message.includes('agent')) {
showAgentConfigError();
}
}

Exemplos​

Suporte ao Cliente Habilitado por Voz​

import { VoiceSDK, ChatSDK } from '@odin-ai-staging/sdk';

class VoiceCustomerSupport {
private voiceSDK: VoiceSDK;
private chatSDK: ChatSDK;
private activeSession?: string;

constructor() {
const config = {
baseUrl: process.env.API_BASE_URL,
projectId: process.env.PROJECT_ID,
apiKey: process.env.API_KEY,
apiSecret: process.env.API_SECRET,
};

this.voiceSDK = new VoiceSDK(config);
this.chatSDK = new ChatSDK(config);
}

async startSupportSession(customerId: string, issueType: string) {
try {
// Create a new chat for this support session
const chat = await this.chatSDK.createChat(
`Voice Support - ${issueType}`,
[] // Could add relevant document keys based on issue type
);

// Start voice conversation
this.activeSession = await this.voiceSDK.startVoiceConversation({
saveToChat: true,
existingChatId: chat.chat_id,
agentId: this.getAgentForIssueType(issueType),
userInfo: {
name: `Customer ${customerId}`,
id: customerId
},
callbacks: {
onConnect: () => {
console.log('Support session started');
this.logSupportEvent('session_started', { customerId, issueType });
},
onTranscription: (text, isFinal) => {
if (isFinal) {
this.logSupportEvent('customer_spoke', {
customerId,
text: text.substring(0, 100) // Log first 100 chars
});
}
},
onMessage: (message) => {
if (message.type === 'ai_speech') {
this.logSupportEvent('agent_responded', {
customerId,
responseLength: message.text.length
});
}
},
onConversationSaved: (chatId, messageId) => {
console.log(`Support conversation saved to chat ${chatId}`);
},
onDisconnect: (details) => {
this.logSupportEvent('session_ended', {
customerId,
reason: details?.reason,
duration: this.getSessionDuration()
});
}
}
});

return {
sessionId: this.activeSession,
chatId: chat.chat_id
};
} catch (error) {
console.error('Failed to start support session:', error);
throw error;
}
}

async endSupportSession() {
if (this.activeSession) {
await this.voiceSDK.endVoiceSession(this.activeSession);
this.activeSession = undefined;
}
}

private getAgentForIssueType(issueType: string): string {
const agentMap = {
'technical': 'agent_technical_support',
'billing': 'agent_billing_support',
'general': 'agent_general_support'
};
return agentMap[issueType] || agentMap['general'];
}

private logSupportEvent(event: string, data: any) {
console.log(`Support Event: ${event}`, data);
// Send to your analytics/logging system
}

private getSessionDuration(): number {
// Calculate session duration
return 0; // Placeholder
}
}

Práticas Recomendadas​

Tratamento de Erros e Alternativas​

const voiceSupport = {
async startWithFallback() {
try {
return await this.voiceSDK.startVoiceConversation(options);
} catch (error) {
console.warn('Voice failed, falling back to text chat:', error);

// Fallback to text-only chat
return await this.chatSDK.createChat('Support Chat (Text)');
}
}
};

Gerenciamento de Recursos​

class VoiceManager {
private activeSessions = new Set<string>();

async startSession(options: any) {
const sessionId = await this.voiceSDK.startVoiceConversation(options);
this.activeSessions.add(sessionId);
return sessionId;
}

async cleanup() {
// End all active sessions
for (const sessionId of this.activeSessions) {
try {
await this.voiceSDK.endVoiceSession(sessionId);
} catch (error) {
console.warn('Failed to end session:', sessionId, error);
}
}
this.activeSessions.clear();
}
}

Otimização de Desempenho​

// Use React.memo for audio visualization components
const AudioVisualizer = React.memo(({ getInputData, isActive }) => {
// Throttle animation updates
const throttledAnimate = useCallback(
throttle(() => {
// Animation logic
}, 16), // ~60fps
[]
);

// ... component logic
});

Acessibilidade​

function VoiceAccessibleChat() {
const [transcript, setTranscript] = useState('');

const { startSession } = useVoiceConversation({
callbacks: {
onTranscription: (text, isFinal) => {
setTranscript(text);

// Update screen reader
if (isFinal) {
announceToScreenReader(`You said: ${text}`);
}
}
}
});

return (
<div>
<button
aria-label="Start voice conversation"
onClick={startSession}
>
🎤 Start Voice Chat
</button>

<div
aria-live="polite"
aria-label="Voice transcript"
>
{transcript}
</div>
</div>
);
}