Pular para o conteúdo principal
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>
  );
}