Modelos de voz a voz
Soniqo admite dos familias de modelos MLX nativos para conversación de voz a voz: PersonaPlex 7B y VoiceChat 11B. Ambos consumen voz y generan directamente el audio del modelo, pero usan arquitecturas dúplex y contratos de runtime distintos.
Elige un modelo
| Modelo | Modelo de conversación | Ideal para |
|---|---|---|
| PersonaPlex 7B | Full-duplex simultáneo con Moshi, Depformer y Mimi | Escucha y habla solapados, con 18 voces seleccionables |
| VoiceChat 11B | Dúplex continuo y sincronizado por tramas con FastConformer, Nemotron-H, EAR-TTS y un códec neuronal | Tramas de streaming de 80 ms, eventos de texto/función y voz nativa del checkpoint |
Estos son modelos conversacionales de voz a voz. Hibiki también transforma voz directamente en voz, pero su tarea es la traducción de voz en streaming y se documenta por separado.
PersonaPlex 7B
Modelo de diálogo voz a voz full-duplex basado en la arquitectura Moshi (Kyutai). PersonaPlex 7B genera respuestas habladas directamente a partir de la entrada hablada — sin pipeline intermedio de texto. El modelo incluye 18 presets de voz y está disponible con cuantización de 8 bits (recomendada) y 4 bits. 8 bits es el valor por defecto — es un 30 % más rápido y produce respuestas coherentes, mientras que 4 bits degrada la calidad de la salida.
VoiceChat 11B
Referencia de speech-to-speech dúplex: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), citada por la ficha upstream de VoiceChat de NVIDIA.
M5 Pro, 48 GB, Release, cadencia live de 80 ms, INT5 con cabezales protegidos: tres ejecuciones controladas midieron un RTF de la ruta completa de 0.94, 0.92 y 0.92, con unos 8.70 GB de RSS máximo. El primer texto hablado llegó en 44.3–46.7 ms y el primer audio reproducible en 74.0–76.4 ms. Son cifras de cálculo en caliente, no latencia de toma de turno aprendida; el rendimiento INT8 optimizado aún no se ha vuelto a medir.
VoiceChat es el otro runtime nativo MLX de voz a voz de speech-swift. Combina percepción FastConformer causal, canales dúplex de texto/función Nemotron-H, EAR-TTS independiente condicionado por texto y un códec neuronal de 22,05 kHz. La sesión acepta audio mono de 16 kHz de forma continua y devuelve eventos de texto y una trama de salida de 1.764 muestras cada 80 ms.
Carga con VoiceChatModel.load(from:), inicia una VoiceChatSession y envía audio mediante pushAudio. Un bundle completo debe contener encoder/, llm/ y tts/; se rechazan los bundles antiguos de solo comprensión.
INT5 mantiene un rendimiento agregado inferior a RTF 1 en el M5 Pro probado. Las tres ejecuciones también mantuvieron el p95 total por trama por debajo de 80 ms, entre 76.2 y 78.6 ms; por tanto, el margen de plazo sigue siendo estrecho. Mide por separado el inicio del turno del modelo y la latencia de cálculo del hardware.
Arquitectura y uso de PersonaPlex
PersonaPlex es un modelo autorregresivo multi-stream con tres componentes principales:
| Componente | Detalles |
|---|---|
| Temporal Transformer | 32 capas, dim=4096, 32 cabezas, SwiGLU (hidden_scale=4.125), RoPE, cuantizado a 8 bits (por defecto) |
| Depformer | 6 capas, dim=1024, 16 cabezas, MultiLinear (weights_per_step=true), dep_q=16 |
| Codec Mimi | 16 codebooks, frame rate de 12.5 Hz, salida de audio a 24 kHz |
El modelo procesa 17 streams simultáneamente: 1 stream de texto + 8 streams de audio del usuario + 8 streams de audio del agent. Esta arquitectura permite la conversación full-duplex en la que el modelo puede escuchar y hablar al mismo tiempo.
Presets de voz
PersonaPlex incluye 18 presets de voz integrados con estilos naturales y variados:
| Categoría | Presets |
|---|---|
| Femenino natural | NATF0, NATF1, NATF2, NATF3 |
| Masculino natural | NATM0, NATM1, NATM2, NATM3 |
| Femenino variado | VARF0, VARF1, VARF2, VARF3, VARF4 |
| Masculino variado | VARM0, VARM1, VARM2, VARM3, VARM4 |
Monólogo interno
PersonaPlex genera dos streams paralelos en cada paso: 8 tokens de codebook de audio para el codec Mimi y un token de texto para el monólogo interno del modelo. El stream de texto es lo que el modelo está “pensando” mientras habla — puede divergir ligeramente del audio final, pero en la práctica refleja la respuesta hablada con suficiente fidelidad como para usarlo como transcripción en vivo.
Los tokens de texto se devuelven como IDs de piezas SentencePiece en crudo. Decodifícalos con el SentencePieceDecoder que incluye PersonaPlex:
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
En modo streaming, respondStream emite chunks de textTokens según se van produciendo — decodifícalos de forma incremental para alimentar una vista de subtítulos en vivo mientras el audio sigue generándose. El flag --transcript de la CLI hace exactamente esto por detrás.
Por qué importa: SentencePieceDecoder está construido sobre el lector de protobuf compartido AudioCommon.SentencePieceModel, así que PersonaPlex, OmnilingualASR y cualquier futuro modelo basado en SentencePiece decodifican a través de la misma implementación del tokenizador. Consulta la referencia de SentencePieceModel.
Prompts del sistema
Pasa cualquier prompt del sistema personalizado como una simple cadena de texto — no hace falta tokenización externa:
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
O usa un preset integrado:
assistant— Asistente útil de propósito general (por defecto)focused— Respuestas concisas y directascustomer-service— Agente de soporte educado y orientado a solucionesteacher— Estilo docente paciente y explicativo
Uso de la CLI
Genera una respuesta hablada a partir de una 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
Opciones
| Opción | Descripción |
|---|---|
--input | Archivo de audio de entrada (WAV, obligatorio) |
--voice | Nombre del preset de voz (p. ej., NATM0, VARF2) |
--system-prompt | Preset de prompt del sistema: assistant, focused, customer-service, teacher |
--system-prompt-text | Texto personalizado del prompt del sistema (sobrescribe --system-prompt) |
--max-steps | Pasos máximos de generación |
--stream | Emite chunks de audio durante la generación |
--compile | Usa inferencia compilada de MLX para una generación más rápida |
--transcript | Emite la transcripción de texto junto al audio |
--json | Salida JSON con metadatos |
Los parámetros de sampling también se pueden sobrescribir:
| Opción | Por defecto | Descripción |
|---|---|---|
--audio-temp | 0.8 | Temperatura de sampling de tokens de audio |
--audio-top-k | 250 | Top-k sampling para tokens de audio |
--text-temp | 0.7 | Temperatura de sampling de tokens de texto |
--text-top-k | 25 | Top-k sampling para tokens de texto |
Streaming
El flag --stream activa la salida de audio en tiempo real. Los chunks de audio se emiten conforme se generan, por lo que la reproducción puede comenzar antes de que la respuesta completa esté lista. Esto resulta especialmente útil para aplicaciones interactivas donde la baja latencia importa.
Rendimiento
| Métrica | Valor |
|---|---|
| Factor de tiempo real (RTF) | ~1.4 (8 bits, casi en tiempo real) |
| Latencia por paso | ~112 ms/paso en M2 Max (8 bits) |
| Tamaño del modelo (8 bits) | ~9.1 GB |
| RAM máxima (8 bits) | ~11 GB |
| Tamaño del modelo (4 bits) | ~4.9 GB |
| RAM máxima (4 bits) | ~7 GB |
PersonaPlex 7B (8 bits) requiere al menos 24 GB de RAM. La variante de 4 bits cabe en dispositivos con 16 GB pero produce una salida degradada. En dispositivos con 8 GB no cabe ninguna de las variantes. Usa --compile para obtener el mejor rendimiento en el hardware compatible.
Variantes del modelo
| Modelo | Tamaño | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8 bits) recomendado | 9.1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4 bits) | 4.9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
API de 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"))