Consulte a versão em inglês para obter as informações mais recentes sobre chamadas de ferramentas e desempenho do VoiceChat.
Modelos de fala para fala
A Soniqo oferece duas famílias de modelos MLX nativos para conversa de fala para fala: PersonaPlex 7B e VoiceChat 11B. Ambos recebem fala e geram diretamente o áudio do modelo, mas usam arquiteturas duplex e contratos de runtime diferentes.
Escolha um modelo
| Modelo | Modelo de conversa | Mais indicado para |
|---|---|---|
| PersonaPlex 7B | Full duplex simultâneo com Moshi, Depformer e Mimi | Escuta e fala sobrepostas, com 18 vozes selecionáveis |
| VoiceChat 11B | Duplex contínuo e sincronizado por frame com FastConformer, Nemotron-H, EAR-TTS e codec neural | Frames de streaming de 80 ms, eventos de texto/função e voz nativa do checkpoint |
Estes são modelos conversacionais de fala para fala. O Hibiki também transforma fala diretamente em fala, mas sua tarefa é tradução de fala em streaming e está documentada separadamente.
PersonaPlex 7B
Modelo de dialogo fala para fala full-duplex baseado na arquitetura Moshi (Kyutai). PersonaPlex 7B gera respostas faladas diretamente de entrada falada — sem pipeline intermediario de texto. O modelo vem com 18 presets de voz e esta disponivel em quantizacao 8-bit (recomendado) e 4-bit. 8-bit e o padrao — e 30% mais rapido e produz respostas coerentes, enquanto 4-bit degrada a qualidade de saida.
VoiceChat 11B
Referência de speech-to-speech duplex: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), citada pelo model card upstream da NVIDIA.
M5 Pro, 48 GB, Release, cadência live de 80 ms, INT5 com heads protegidos: três execuções controladas mediram RTF do pipeline completo de 0.94, 0.92 e 0.92, com cerca de 8.70 GB de RSS de pico. O primeiro texto falado chegou em 44.3–46.7 ms e o primeiro áudio reproduzível em 74.0–76.4 ms. São valores de computação aquecida, não latência de troca de turno aprendida; o desempenho INT8 otimizado ainda não foi medido novamente.
VoiceChat é o outro runtime nativo MLX de fala para fala do speech-swift. Ele combina percepção FastConformer causal, canais duplex de texto/função Nemotron-H, EAR-TTS independente condicionado por texto e codec neural de 22,05 kHz. A sessão recebe continuamente áudio mono de 16 kHz e retorna eventos de texto e um quadro de saída de 1.764 amostras a cada 80 ms.
Carregue com VoiceChatModel.load(from:), inicie uma VoiceChatSession e envie áudio com pushAudio. Um bundle completo precisa conter encoder/, llm/ e tts/; bundles antigos apenas de compreensão são rejeitados.
INT5 mantém throughput agregado abaixo de RTF 1 no M5 Pro testado. As três execuções também mantiveram o p95 total por quadro abaixo de 80 ms, entre 76.2 e 78.6 ms; portanto, a margem de prazo continua estreita. Meça separadamente o início do turno do modelo e a latência de computação do hardware.
CLI VoiceChat ao vivo
Converse com a Soniqo usando o bundle INT5 completo com protected head, baseado no NVIDIA Nemotron VoiceChat 11B. O comando mantém captura e reprodução em um único AVAudioEngine para o AEC da Apple, transmite as legendas RNN-T do usuário incluídas no bundle e reproduz áudio do modelo a 22,05 kHz enquanto continua ouvindo.
O prompt padrão da Soniqo deixa os limites claros: este CLI pode conversar e responder perguntas, mas não acessa calendários, lembretes, aplicativos, contas, dispositivos nem serviços externos. Nesses pedidos, a Soniqo informa claramente que não pode executar a ação, não pede uma confirmação que não poderá cumprir e sugere brevemente o que o usuário pode fazer.
speech voice-chat
speech voice-chat --input question.wav --output response.wav
speech voice-chat \\
--mcp-config Examples/VoiceChatMCP/apple-reminders.json
Ferramentas MCP configuráveis
O adaptador incluído apple-reminders-eventkit agora expõe ao modelo apenas list_reminders, create_reminder e update_reminder. A descoberta de listas fica privada no adaptador. Para atualizar, o usuário diz o nome do lembrete e o runtime o resolve para um ID EventKit privado e confiável, que nunca aparece no resultado visível ao modelo. A criação e o vencimento continuam sendo confirmados em uma única gravação EventKit. O primeiro início recebe pelo menos 60 segundos e as chamadas normais mantêm 15 segundos por padrão.
O MCP só é ativado quando --mcp-config é fornecido. O JSON independente de fornecedor pode iniciar qualquer servidor MCP stdio e escolhe explicitamente até cinco ferramentas por enabledTools. Toda ferramenta fora de readOnlyTools é tratada como escrita. A política padrão é confirm: a primeira chamada fica pendente; uma confirmação determinística da Soniqo repete os argumentos interpretados e informa que nada foi alterado. Só uma nova confirmação afirmativa do usuário pode executá-la uma vez. Uma chamada idêntica não pode ser executada duas vezes sem nova fala do usuário, mesmo com allow. Também há deny e a autorização explícita allow. O resultado real retorna pelo canal de funções do modelo e o sucesso exige uma resposta ok.
Os argumentos de escrita são comparados à transcrição do usuário antes da confirmação. Datas relativas usam a data local atual; nomes de listas inventados, datas obsoletas, prioridades e outros valores não verificados nunca são executados. Nova fala RNN-T seguida do fim da enunciação resolve a confirmação mesmo após reiniciar a transcrição. A Soniqo só anuncia sucesso quando o servidor MCP realmente retorna ok: true; caso contrário, informa que não conseguiu confirmar a execução.
“Quais lembretes eu tenho?” faz uma única chamada plana a list_reminders que cobre todas as listas EventKit. Lista, vencimento, estado de conclusão e o ID estável reservado ao runtime ficam em cache, portanto perguntas de detalhe e atualizações seguras não releem cada lista. O painel mostra separadamente o tempo, os passos de token e os token/s da decodificação de função; o RTF de áudio continua medindo apenas os frames de áudio de 80 ms em primeiro plano.
A confirmação falada pergunta se o usuário deseja “prosseguir” e evita de propósito todas as frases afirmativas permitidas, para que o eco da reprodução não possa autorizar a ação pendente.
Os servidores MCP filhos herdam apenas PATH, HOME, o diretório temporário e variáveis de localidade. Credenciais devem ser passadas explicitamente no mapa env do servidor; outros segredos do processo pai não são encaminhados.
A projeção de funções de 8 bits ocupa cerca de 518 MB. Durante a fala normal, o VoiceChat avalia apenas uma sonda em cache de 36 KB para PAD e início de ferramenta. A cabeça completa de 131.072 linhas só roda quando o início de ferramenta primeiro supera PAD, verifica então o argmax global e permanece ativa enquanto uma chamada JSON aberta é gerada. Quando o resultado MCP já é conhecido, o VoiceChat o reproduz em blocos de prefill causal limitados a 16 tokens, preservando o feedback de função treinado e o estado dos caches de linguagem e fala sem executar um decode de 11B para cada token do resultado. Assim, a conversa MCP continua em tempo real sem enfraquecer as decisões de início de ferramenta.
A reprodução pré-carrega por padrão três frames de 80 ms (240 ms). Após uma falha, ela aguarda uma reserva de recuperação de oito frames. Após o primeiro callback de 88 ms ou três frames na fila, o refinamento cai de oito para duas etapas; com 120 ms, seis frames ou uma resincronização, cai diretamente para uma etapa. Se a fila ainda chegar a oito frames (640 ms), somente o mínimo de áudio antigo necessário para aceitar a nova entrada é descartado. Uma sobrecarga contínua é tratada como um único episódio de recuperação, e um turno já confirmado pelo RNN-T permanece ativo em vez de ser apagado por reinicializações repetidas. Palavras ignoradas continuam marcadas e talvez precisem ser repetidas.
As métricas descrevem a experiência: behind 0.3 s é o áudio do microfone em espera, RTF 0.93× é o tempo de cálculo dividido pelo tempo de áudio (abaixo de 1 acompanha; acima de 1 atrasa), e last 74 ms for 80 ms audio compara o último tempo de cálculo com os 80 ms de áudio representados por cada frame. A média móvel usa os últimos 120 callbacks do microfone; eventos de replay não contam como tempo de áudio adicional.
O modo microfone usa por padrão o controle de turnos RNN-T da NVIDIA. As decisões BOS/EOS aprendidas pelo modelo continuam sendo o caminho normal de baixa latência. A primeira previsão de cada frame de 80 ms é tratada como blank ou non-blank; após a confirmação da fala do usuário, 40 frames blank consecutivos (3,2 s) servem apenas como fallback de segurança para iniciar a resposta, e 40 novos frames non-blank fornecem o fallback correspondente de interrupção. O contador de fala é zerado quando a Soniqo começa, portanto a atividade do turno concluído do usuário não pode cortar a resposta imediatamente. Todos os frames de áudio continuam chegando ao modelo duplex; --no-rnnt-turn-taking desativa esses fallbacks e deixa BOS/EOS inteiramente a cargo do cabeçote de linguagem aprendido. O painel informa barge-ins RNN-T separadamente das falhas de reprodução.
No modo normal, o BOS nativo do modelo é suprimido até a fala do usuário ser confirmada, portanto a Soniqo não fala primeiro. --greet permite explicitamente a saudação inicial. Após o conteúdo audível, os PADs blank permanecem no turno aberto por pelo menos 16 frames ou por três frames por token de conteúdo, o que for maior. O primeiro PAD blank além desse orçamento fecha o turno lógico. Essa cauda proporcional ao conteúdo preserva palavras tardias que um corte fixo em 1,28 segundo pode silenciar.
O modo interativo usa um painel fixo na tela alternativa do terminal; as atualizações são redesenhadas no lugar sem preencher o histórico. Use --plain para saída cumulativa.
Para diagnóstico local, --debug-timeline marca cada frase do usuário e da Soniqo e insere na mesma cronologia os argumentos da chamada nativa analisada, o início/fim do MCP e a sincronização do cache de resultados. Também marca pronunciation ended na última janela PCM gerada audível; esse é o tempo de saída do modelo, não o término da reprodução no dispositivo. A demo omite os blocos históricos de tempo; métricas detalhadas de decodificação nativa, provedor, cache e pressão do microfone ficam no JSON do voicechat-bench. Ele pode expor argumentos de ferramentas, portanto não o use em logs compartilhados. Os temporizadores de fase são reiniciados entre a decodificação nativa e a espera do provedor.
As sessões ao vivo mantêm o prompt de voz imutável de 37 frames mais 20 segundos de histórico EAR-TTS recente; o Nemotron-H mantém separadamente o contexto semântico da conversa. Os PADs dentro da cauda acústica proporcional ao conteúdo são gerados normalmente; só os PADs silenciosos posteriores avançam causalmente em lotes de oito. Assim a resposta falada não é cortada e a atenção TTS e o trabalho ocioso continuam limitados. --live-speech-context-seconds 0 restaura todo o histórico TTS e o modo de arquivo continua exato e completo por padrão.
Em um perfil de 63,6 segundos no M5 Pro com a cauda segura, o caminho ao vivo mediu RTF agregado 0,87, RTF 0,71 na janela final, síntese final de 4,1 ms/frame, RSS máximo de 8,72 GB e footprint físico máximo de 23,67 GB. As janelas ativas de oito etapas chegaram a RTF 1,05 e 1,12, por isso o CLI interativo mantém a redução reversível para duas etapas e um modo de emergência de uma etapa quando a entrada se acumula. A similaridade cosseno mínima entre os estados de cache ocioso sequencial e em lote foi 0,9999999.
O detector de estalo inicial do voicechat-bench examina a entrada gerada e os primeiros 50 ms de fala sustentada. Ele só marca saltos acima de 0,05 de amplitude e seis vezes a base p99 da fala estável; um cenário pode definir maximum_suspect_onset_transients como zero.
Três execuções Release em um M5 Pro de 48 GB mediram RTF 0,92, p95 do frame total de 74,8–75,8 ms, primeiro áudio reproduzível em 74,2–74,9 ms e cerca de 8,74 GB de RSS máximo. Transcrição e resposta foram idênticas.
Arquitetura e uso do PersonaPlex
PersonaPlex e um modelo autoregressivo multi-stream com tres componentes principais:
| Componente | Detalhes |
|---|---|
| Temporal Transformer | 32 camadas, dim=4096, 32 heads, SwiGLU (hidden_scale=4.125), RoPE, quantizado em 8-bit (padrao) |
| Depformer | 6 camadas, dim=1024, 16 heads, MultiLinear (weights_per_step=true), dep_q=16 |
| Codec Mimi | 16 codebooks, frame rate de 12.5 Hz, saida de audio a 24 kHz |
O modelo processa 17 streams simultaneamente: 1 stream de texto + 8 streams de audio do usuario + 8 streams de audio do agente. Esta arquitetura permite conversa full-duplex onde o modelo pode ouvir e falar ao mesmo tempo.
Presets de voz
PersonaPlex inclui 18 presets de voz integrados em estilos naturais e variados:
| Categoria | Presets |
|---|---|
| Feminino natural | NATF0, NATF1, NATF2, NATF3 |
| Masculino natural | NATM0, NATM1, NATM2, NATM3 |
| Feminino variado | VARF0, VARF1, VARF2, VARF3, VARF4 |
| Masculino variado | VARM0, VARM1, VARM2, VARM3, VARM4 |
Monologo interno
PersonaPlex gera dois streams paralelos a cada passo: 8 tokens de codebook de audio para o codec Mimi e um token de texto para o monologo interno do modelo. O stream de texto e o que o modelo esta “pensando” enquanto fala — ele pode divergir ligeiramente do audio final, mas na pratica espelha a resposta falada de perto o suficiente para usar como transcricao ao vivo.
Os tokens de texto voltam como IDs brutos de pieces do SentencePiece. Decodifique-os com o SentencePieceDecoder que o PersonaPlex fornece:
import PersonaPlex
import AudioCommon
let model = try await PersonaPlexModel.fromPretrained()
let decoder = model.tokenizer // SentencePieceDecoder?
let result = model.respond(userAudio: userSamples, voice: .NATM0)
let transcript = decoder?.decode(result.textTokens) ?? ""
print(transcript) // "Sure, I can help with that..."
playAudio(result.audio) // 24 kHz mono Float32
No modo streaming, respondStream emite chunks textTokens conforme sao produzidos — decodifique-os incrementalmente para alimentar uma visualizacao de legendas ao vivo enquanto o audio ainda esta sendo gerado. A flag CLI --transcript faz exatamente isso por baixo dos panos.
Por que importa: SentencePieceDecoder e construido sobre o leitor protobuf compartilhado AudioCommon.SentencePieceModel, entao PersonaPlex, OmnilingualASR e qualquer futuro modelo baseado em SentencePiece decodificam pela mesma implementacao de tokenizer. Veja a referencia do SentencePieceModel.
System prompts
Passe qualquer system prompt customizado como uma string simples — sem tokenizacao externa necessaria:
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
Ou use um preset integrado:
assistant— Assistente de proposito geral (padrao)focused— Respostas concisas e diretascustomer-service— Agente de suporte educado e orientado a solucoesteacher— Estilo de ensino paciente e explicativo
Uso do CLI
Gerar uma resposta falada a partir de uma entrada de audio:
# Basic speech-to-speech
.build/release/speech respond --input question.wav
# Choose a voice preset
.build/release/speech respond --input question.wav --voice NATM0
# Stream audio output during generation
.build/release/speech respond --input question.wav --stream
# Custom system prompt text
.build/release/speech respond --input question.wav --system-prompt-text "You enjoy having a good conversation."
# Use a preset system prompt
.build/release/speech respond --input question.wav --system-prompt customer-service
# Get transcript alongside audio
.build/release/speech respond --input question.wav --transcript
# JSON output with metadata
.build/release/speech respond --input question.wav --json
Opcoes
| Opcao | Descricao |
|---|---|
--input | Arquivo de audio de entrada (WAV, obrigatorio) |
--voice | Nome do preset de voz (por exemplo, NATM0, VARF2) |
--system-prompt | Preset de system prompt: assistant, focused, customer-service, teacher |
--system-prompt-text | Texto de system prompt customizado (sobrescreve --system-prompt) |
--max-steps | Maximo de passos de geracao |
--stream | Emitir chunks de audio durante a geracao |
--compile | Usar inferencia compilada MLX para geracao mais rapida |
--transcript | Emitir a transcricao de texto junto com o audio |
--json | Saida JSON com metadados |
Parametros de sampling tambem podem ser sobrescritos:
| Opcao | Padrao | Descricao |
|---|---|---|
--audio-temp | 0.8 | Temperatura de sampling de tokens de audio |
--audio-top-k | 250 | Sampling top-k de tokens de audio |
--text-temp | 0.7 | Temperatura de sampling de tokens de texto |
--text-top-k | 25 | Sampling top-k de tokens de texto |
Streaming
A flag --stream habilita saida de audio em tempo real. Os chunks de audio sao emitidos conforme sao gerados, entao a reproducao pode comecar antes da resposta completa terminar. Isso e particularmente util para aplicacoes interativas onde baixa latencia importa.
Desempenho
| Metrica | Valor |
|---|---|
| Fator de tempo real (RTF) | ~1.4 (8-bit, proximo de tempo real) |
| Latencia por passo | ~112 ms/passo em M2 Max (8-bit) |
| Tamanho do modelo (8-bit) | ~9.1 GB |
| RAM de pico (8-bit) | ~11 GB |
| Tamanho do modelo (4-bit) | ~4.9 GB |
| RAM de pico (4-bit) | ~7 GB |
PersonaPlex 7B (8-bit) requer pelo menos 24 GB de RAM. A variante 4-bit cabe em dispositivos de 16 GB mas produz saida degradada. Em dispositivos de 8 GB, nenhuma das variantes cabera. Use --compile para melhor desempenho em hardware suportado.
Variantes do modelo
| Modelo | Tamanho | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8-bit) recomendado | 9.1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4-bit) | 4.9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
API Swift
import PersonaPlex
import AudioCommon
let model = try await PersonaPlexModel.fromPretrained()
let response = model.respond(
userAudio: userSamples,
voice: .NATM0,
systemPrompt: "You are a helpful assistant."
)
try WAVWriter.write(samples: response.audio, sampleRate: 24000, to: URL(filePath: "answer.wav"))