Modèles parole-à-parole
Soniqo prend en charge deux familles de modèles MLX natifs pour la conversation parole-à-parole : PersonaPlex 7B et VoiceChat 11B. Tous deux reçoivent de la parole et génèrent directement l'audio du modèle, mais leurs architectures duplex et leurs contrats d'exécution diffèrent.
Choisir un modèle
| Modèle | Modèle de conversation | Idéal pour |
|---|---|---|
| PersonaPlex 7B | Full-duplex simultané avec Moshi, Depformer et Mimi | Écoute et parole simultanées, avec 18 voix sélectionnables |
| VoiceChat 11B | Duplex continu et synchronisé par trame avec FastConformer, Nemotron-H, EAR-TTS et un codec neuronal | Trames de streaming de 80 ms, événements texte/fonction et voix native du checkpoint |
Il s'agit de modèles conversationnels parole-à-parole. Hibiki transforme aussi directement la parole en parole, mais sa tâche est la traduction vocale en streaming et sa documentation est séparée.
PersonaPlex 7B
Modèle de dialogue parole-à-parole full-duplex basé sur l'architecture Moshi (Kyutai). PersonaPlex 7B génère des réponses parlées directement à partir d'une entrée parlée — sans pipeline texte intermédiaire. Le modèle est livré avec 18 préréglages de voix et est disponible en quantification 8 bits (recommandée) et 4 bits. Le 8 bits est la valeur par défaut — il est 30 % plus rapide et produit des réponses cohérentes, tandis que le 4 bits dégrade la qualité de sortie.
VoiceChat 11B
Référence speech-to-speech duplex : SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), citée par la fiche VoiceChat amont de NVIDIA.
M5 Pro, 48 Go, Release, cadence live de 80 ms, INT5 avec têtes protégées : trois exécutions contrôlées ont mesuré un RTF du pipeline complet de 0.94, 0.92 et 0.92, avec environ 8.70 Go de RSS maximal. Le premier texte parlé est arrivé en 44.3–46.7 ms et le premier audio lisible en 74.0–76.4 ms. Ce sont des mesures de calcul à chaud, pas une latence de prise de tour apprise ; les performances INT8 optimisées n'ont pas encore été remesurées.
VoiceChat est l'autre runtime parole-à-parole MLX natif de speech-swift. Il combine perception FastConformer causale, canaux duplex texte/fonction Nemotron-H, EAR-TTS autonome conditionné par le texte et codec neuronal à 22,05 kHz. Une session reçoit en continu de l'audio mono 16 kHz et renvoie toutes les 80 ms des événements texte ainsi qu'une trame de sortie de 1 764 échantillons.
Chargez avec VoiceChatModel.load(from:), démarrez une VoiceChatSession et envoyez l'audio via pushAudio. Un bundle complet doit contenir encoder/, llm/ et tts/ ; les anciens bundles limités à la compréhension sont refusés.
INT5 maintient un débit global sous RTF 1 sur le M5 Pro testé. Les trois exécutions ont aussi gardé le p95 total par trame sous 80 ms, entre 76.2 et 78.6 ms ; la marge d'échéance reste donc faible. Mesurez séparément le début de tour du modèle et la latence de calcul matériel.
Architecture et utilisation de PersonaPlex
PersonaPlex est un modèle autorégressif multi-flux avec trois composants principaux :
| Composant | Détails |
|---|---|
| Temporal Transformer | 32 couches, dim=4096, 32 têtes, SwiGLU (hidden_scale=4.125), RoPE, quantifié 8 bits (par défaut) |
| Depformer | 6 couches, dim=1024, 16 têtes, MultiLinear (weights_per_step=true), dep_q=16 |
| Mimi Codec | 16 codebooks, taux de trame 12,5 Hz, sortie audio 24 kHz |
Le modèle traite 17 flux simultanément : 1 flux texte + 8 flux audio utilisateur + 8 flux audio agent. Cette architecture permet une conversation full-duplex où le modèle peut écouter et parler en même temps.
Préréglages de voix
PersonaPlex inclut 18 préréglages de voix intégrés dans des styles naturels et variés :
| Catégorie | Préréglages |
|---|---|
| Femme naturelle | NATF0, NATF1, NATF2, NATF3 |
| Homme naturel | NATM0, NATM1, NATM2, NATM3 |
| Femme variée | VARF0, VARF1, VARF2, VARF3, VARF4 |
| Homme varié | VARM0, VARM1, VARM2, VARM3, VARM4 |
Monologue intérieur
PersonaPlex génère deux flux parallèles à chaque étape : 8 tokens de codebook audio pour le codec Mimi et un token de texte pour le monologue intérieur du modèle. Le flux de texte est ce que le modèle « pense » pendant qu'il parle — il peut diverger légèrement de l'audio final, mais en pratique il reflète suffisamment la réponse parlée pour être utilisé comme transcription en direct.
Les tokens de texte reviennent sous forme d'IDs de pièces SentencePiece bruts. Décodez-les avec le SentencePieceDecoder fourni par 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 mode streaming, respondStream émet des chunks textTokens à mesure qu'ils sont produits — décodez-les de façon incrémentale pour piloter une vue de sous-titres en direct pendant que l'audio est encore en génération. L'option CLI --transcript fait exactement cela en coulisses.
Pourquoi c'est important : SentencePieceDecoder est construit sur le lecteur protobuf partagé AudioCommon.SentencePieceModel, donc PersonaPlex, OmnilingualASR et tout futur modèle basé sur SentencePiece décodent via la même implémentation de tokenizer. Voir la référence SentencePieceModel.
Prompts système
Passez n'importe quel prompt système personnalisé sous forme de chaîne simple — aucune tokenisation externe n'est nécessaire :
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
Ou utilisez un préréglage intégré :
assistant— Assistant utile polyvalent (par défaut)focused— Réponses concises et directescustomer-service— Agent de support poli et orienté solutionteacher— Style d'enseignement patient et explicatif
Utilisation en CLI
Générer une réponse parlée à partir d'une entrée 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
Options
| Option | Description |
|---|---|
--input | Fichier audio d'entrée (WAV, requis) |
--voice | Nom de préréglage de voix (ex. NATM0, VARF2) |
--system-prompt | Préréglage de prompt système : assistant, focused, customer-service, teacher |
--system-prompt-text | Texte de prompt système personnalisé (prime sur --system-prompt) |
--max-steps | Étapes de génération maximum |
--stream | Émettre des chunks audio pendant la génération |
--compile | Utiliser l'inférence compilée MLX pour une génération plus rapide |
--transcript | Afficher la transcription textuelle avec l'audio |
--json | Sortie JSON avec métadonnées |
Les paramètres d'échantillonnage peuvent également être surchargés :
| Option | Par défaut | Description |
|---|---|---|
--audio-temp | 0,8 | Température d'échantillonnage des tokens audio |
--audio-top-k | 250 | Échantillonnage top-k des tokens audio |
--text-temp | 0,7 | Température d'échantillonnage des tokens texte |
--text-top-k | 25 | Échantillonnage top-k des tokens texte |
Streaming
L'option --stream active la sortie audio en temps réel. Les chunks audio sont émis à mesure qu'ils sont générés, donc la lecture peut commencer avant que la réponse complète ne soit terminée. C'est particulièrement utile pour les applications interactives où une faible latence compte.
Performance
| Métrique | Valeur |
|---|---|
| Facteur temps réel (RTF) | ~1,4 (8 bits, proche du temps réel) |
| Latence par étape | ~112 ms/étape sur M2 Max (8 bits) |
| Taille du modèle (8 bits) | ~9,1 Go |
| RAM en pic (8 bits) | ~11 Go |
| Taille du modèle (4 bits) | ~4,9 Go |
| RAM en pic (4 bits) | ~7 Go |
PersonaPlex 7B (8 bits) requiert au moins 24 Go de RAM. La variante 4 bits tient sur des appareils 16 Go mais produit une sortie dégradée. Sur des appareils 8 Go, aucune variante ne tiendra. Utilisez --compile pour de meilleures performances sur le matériel pris en charge.
Variantes du modèle
| Modèle | Taille | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8 bits) recommandé | 9,1 Go | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4 bits) | 4,9 Go | 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"))