Guide développeur pour utiliser le modèle Naia depuis du code. Après avoir exécuté le modèle via 4.4 Téléchargement du modèle Naia, utilisez l'API compatible OpenAI servie localement (sans passerelle, sans file d'attente) telle quelle. Avec n'importe quel SDK ou outil OpenAI, vous n'avez qu'à pointer le baseURL vers ce modèle.
Pas uniquement pour naia-os/l'interface — tout code qui parle OpenAI Realtime/Chat/Audio/Embeddings se connecte tel quel, et vous pouvez construire et exécuter de nouvelles applications par-dessus ce modèle.
1. Connexion · authentification
- Base REST :
http://<host>:8892/v1(127.0.0.1sur le même PC) - Realtime (WS) :
ws://<host>:8892/v1/realtime(unws://<host>:8892nu fonctionne aussi — chemin/v1/realtime+ modèle par défaut appliqués automatiquement) - Connexion : en local (
127.0.0.1) / Tailscale, aucune authentification n'est requise — le conteneur vérifie lui-même sa licence. Les clients qui ont besoin d'un champ clé (SDK OpenAI, etc.) peuvent passer n'importe quelle valeur (naia). En cas d'exposition à distance, placez §4.4 Tailscale/VPN en amont.
🔑 Une seule clé — la clé d'abonnement
- Clé d'abonnement — la clé d'abonnement que vous obtenez depuis le portail. Utilisée uniquement au moment de l'exécution du conteneur (activation) (
-e NAIA_ACCOUNT_TOKEN=<subscription-key>). Elle vérifie l'abonnement et obtient une licence à durée limitée (certificat). - Il n'y a pas de clé de connexion séparée. Une fois activé, le conteneur vérifie lui-même localement avec le certificat, de sorte que les clients (naia-os, SDK OpenAI) n'ont qu'à se connecter par URL —
127.0.0.1sur le même PC, ou Tailscale/VPN (§4.4) depuis un autre appareil. Il n'appelle pas la passerelle à chaque connexion. - Le
api_keydans les exemples ci-dessous est un espace réservé (le SDK OpenAI exige ce champ) — le conteneur hors ligne ne le vérifie pas, donc n'importe quelle valeur comme"naia"fonctionne.
2. Points de terminaison (compatibles OpenAI)
| Point de terminaison | Usage |
|---|---|
GET /health | État de disponibilité {"ready":true,"services":{tts,stt,llm},"vad":true} (sans authentification) |
GET /v1/models | Liste des modèles |
WS /v1/realtime | Session vocale en temps réel (VAD, barge-in, émotion) |
POST /v1/chat/completions | Chat (streaming) |
POST /v1/audio/speech | Synthèse vocale (TTS) |
POST /v1/audio/transcriptions | Reconnaissance vocale (STT) |
POST /v1/embeddings | Embeddings |
Chat (curl) :
curl -s http://127.0.0.1:8892/v1/chat/completions \
-H "Authorization: Bearer naia" -H "Content-Type: application/json" \
-d '{"model":"naia-0.9-omni-24g","messages":[{"role":"user","content":"hi"}],"stream":false}'
SDK OpenAI (Python) — il suffit de changer baseURL :
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8892/v1", api_key="naia")
print(client.chat.completions.create(
model="naia-0.9-omni-24g",
messages=[{"role": "user", "content": "hi"}],
).choices[0].message.content)
Transcription (STT) :
curl -s http://127.0.0.1:8892/v1/audio/transcriptions \
-H "Authorization: Bearer naia" \
-F file=@sample.wav -F model=naia-0.9-omni-24g
3. Voix en temps réel — flux de connexion (WS)
Même flux que celui utilisé par la démo en direct 4.3. (Hors ligne, cela démarre immédiatement, sans file d'attente/affectation de la passerelle.)
-
Connexion — ouvrez
ws://<host>:8892. -
Première trame (authentification · langue) — les WebSockets du navigateur ne peuvent pas envoyer d'en-têtes, alors envoyez ceci comme premier message :
{ "setup": { "apiKey": "naia", "locale": "en" } } -
Lorsque le serveur envoie
session.created, configurez la session avecsession.update:{ "type": "session.update", "session": { "modalities": ["text", "audio"], "input_audio_format": "pcm16", "output_audio_format": "pcm16", "instructions": "<persona instructions>", "turn_detection": { "type": "server_vad" }, "input_audio_transcription": { "language": "en" }, "ref_audio_url": "<URL of a voice sample to mimic (optional)>" } } -
Échange
Client → Serveur Entrée vocale {"type":"input_audio_buffer.append","audio":"<base64 PCM16 24kHz>"}(le VAD du serveur détecte la fin de la parole)Entrée texte conversation.item.createpuisresponse.createBarge-in response.cancelServeur → Client response.audio.deltamorceau audio base64 PCM16 24kHz response.audio_transcript.delta/response.text.deltatexte de la réponse (streaming) conversation.item.input_audio_transcription.completedtranscription de votre parole emotion.updatedbalise d'émotion / de prosodie (§5) response.donefin d'un tour
4. Langues — 30 langues (par défaut = auto/global)
Le modèle prend en charge 30 langues (arabe, birman, chinois, danois, néerlandais, anglais, finnois, français, allemand, grec, hébreu, hindi, indonésien, italien, japonais, khmer, coréen, lao, malais, norvégien, polonais, portugais, russe, espagnol, swahili, suédois, tagalog, thaï, turc, vietnamien).
- Par défaut (non défini) = global/auto — il détecte la langue que vous avez parlée et répond dans cette langue (par tour).
- Pour fixer une langue spécifique, indiquez un code ISO-639-1 (par ex.
ko/en/ja) danssetup.localeou dansinput_audio_transcription.languagedesession.update.
5. Format de sortie (balises d'émotion · de prosodie)
Le format de sortie est ajusté pour la conversation vocale — si le client le comprend, il peut s'exprimer plus richement.
- Balises de prosodie : le texte de la réponse contient des balises entre crochets en anglais minuscule comme
[laughing],[sigh],[breath],[pause],[hesitation]insérées là où l'émotion change (pour la prosodie vocale). Le modèle est instruit de ne pas utiliser de balises en coréen comme[웃음], d'indications scéniques entre parenthèses comme(smiling), ni d'astérisques comme*smiles*. Vocabulaire connu :laughing/laugh/laughter/chuckle/giggle · sigh/exhale · breath/inhale · pause · hesitation · gasp/cough/sneeze/yawn/sniff/hum · cry/sob/moan/whisper/shout/cheer(les autres balises sont transmises telles quelles). - Pour chaque balise, le serveur envoie un événement
emotion.updateden correspondance 1:1 (state== nom de la balise, en minuscule) :{ "type": "emotion.updated", "state": "laughing", "tag": "[laughing]", "known": true } - Le chemin TTS conserve les balises et les intègre dans la synthèse pour la prosodie vocale, tandis que le
text.deltadu chat envoie un texte propre, balises retirées. (Aucun emoji, markdown ou auto-narration entre parenthèses dans la sortie.) - Mappage côté client (référence naia-os) : mappez
emotion.updated.state(balise de prosodie) sur les expressions de l'avatar —laughing/chuckle/giggle/cheer → happy,sigh/exhale/cry/sob → sad,gasp → surprised,shout → angry,hesitation → think. La prosodie non émotionnelle commebreath·pausene change pas l'expression (gardez la précédente — pour qu'elle ne revienne pas à neutre à chaque respiration). - Traitement robuste recommandé : la sortie du LLM n'est pas toujours exacte. Préférez
emotion.updated, mais si elle est absente, détectez automatiquement les balises dans la transcription elle-même (majuscules[HAPPY]/ balises de prosodie en minuscule) ou les indications scéniques qui ont fuité ((smiles)·*sigh*) et reflétez-les dans l'expression ; s'il n'y a aucun indice, gardez l'expression actuelle (cf. naia-osshell/src/lib/vrm/expression.tsextractExpression).
6. Changer le modèle de conversation · publier une nouvelle version (exploitation)
Guide détaillé pour changer directement en ligne de commande. Les abonnés individuels peuvent l'utiliser tel quel (aucune clé nécessaire), et il inclut aussi des options de verrouillage pour l'exploitation partagée / en kiosque. Un résumé simple se trouve dans 4.4 Hors ligne.
6.1 Changer le modèle de conversation (à partir de 0.91)
Sans toucher au conteneur, vous changez uniquement le modèle qui gère la conversation pendant l'exécution. La voix (parole · écoute), le filigrane et l'authentification d'abonnement restent inchangés.
Trois choses à savoir d'abord :
- Le modèle par défaut est un LLM ouvert intégré. Vous pouvez à tout moment revenir au modèle par défaut après avoir changé.
- Le nouveau modèle à publier doit être au format GGUF. Et comme la fonction vocale occupe environ 10 Go de mémoire, le modèle de conversation peut monter jusqu'à environ 14 Go. Les modèles plus grands sont refusés, et si le chargement échoue, il revient automatiquement au modèle utilisé (la conversation n'est pas interrompue).
- Les abonnés individuels n'ont besoin d'aucune clé séparée. L'authentification d'abonnement (licence) de votre propre machine fait office d'autorisation, vous pouvez donc simplement changer avec la commande ci-dessous — comme pour la voix qui ne nécessite pas de clé. (Uniquement sur un boîtier partagé / en kiosque utilisé par plusieurs personnes, l'exploitant peut poser un verrou au moment de l'exécution avec
-e NAIA_ADMIN_KEY=mot_de_passe_choisi, et il faut alors envoyer-H "Authorization: Bearer mot_de_passe_choisi"avec la requête.)
Pratique — définissez juste l'adresse :
BASE=http://127.0.0.1:8892 # depuis un autre appareil, utilisez l'adresse https du §4.4 (par ex. ...:8443)
① Voyez quel modèle est en cours et combien de mémoire reste :
curl -s $BASE/admin/llm/status
② Changez le modèle — ne remplacez que la partie modèle entre guillemets et collez.
Mettez tel quel l'adresse de la fiche de modèle HuggingFace (https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF) ou son id (Qwen/Qwen2.5-7B-Instruct-GGUF) :
curl -s -X POST $BASE/admin/llm/swap \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'
Le préfixe hf.co/ et la quantification (quant) sont ajoutés automatiquement (Q4_K_M par défaut). Si vous voulez une quantification précise, indiquez-la à la fin comme Qwen/Qwen2.5-7B-Instruct-GGUF:Q5_K_M. Le premier téléchargement d'un modèle prend de quelques dizaines de secondes à quelques minutes.
②-hors ligne — changer sans Internet, avec votre propre fichier GGUF.
Pour une exposition ou une consultation sans Internet, plutôt que de télécharger depuis HuggingFace, enregistrez et changez avec un fichier GGUF que vous possédez déjà. (Règle de distinction : si le nom contient une barre oblique comme organisation/dépôt, c'est HuggingFace en ligne ; un simple nom sans barre oblique, c'est un modèle local.)
Copiez et collez ligne par ligne. Mettez le nom voulu à la place de monmodele et le nom de fichier réel à la place de monmodele.gguf :
podman cp ./monmodele.gguf naia-omni:/app/models/monmodele.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/monmodele.gguf\n" > /tmp/Modelfile && ollama create monmodele -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"monmodele:latest","pull":false}'
⚠️ Un GGUF que vous avez converti / fusionné vous-même peut manquer du gabarit de chat, ce qui rend les réponses incohérentes / tronquées. Dans ce cas, ajoutez à l'étape 2 dans le Modelfile le gabarit de chat de la famille du modèle (
TEMPLATE) et les jetons d'arrêt (PARAMETER stop) avant de l'enregistrer — les détails pour développeurs sont dans l'[implémentation de référence §7]. (Les GGUF Instruct officiels de HuggingFace les intègrent généralement et fonctionnent tels quels.)
③ Revenez au modèle par défaut :
curl -s -X POST $BASE/admin/llm/restore
Sur un boîtier partagé / en kiosque (si l'exploitant a posé un
NAIA_ADMIN_KEY), ajoutez-H "Authorization: Bearer mot_de_passe_choisi"à chacune des commandes ci-dessus. Les abonnés individuels n'en ont pas besoin.
Après le changement, les applications comme naia-os se connectent toujours à la même adresse, telle quelle (pas besoin de se reconnecter). Si vous voulez continuer à démarrer avec ce modèle après un redémarrage ou une mise à jour, spécifiez le modèle par défaut au lancement du conteneur avec -e NAIA_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct-GGUF.
6.2 Mettre à jour vers une nouvelle version
Quand une nouvelle version sort, ne changez que l'image (la version) et laissez l'abonnement · les réglages tels quels. Au premier lancement de la nouvelle version, le conteneur se ré-authentifie automatiquement via Internet (même abonnement · même appareil — pas besoin de ressaisir la clé à la main). Il faut donc être connecté à Internet lors de la mise à jour.
podman pull ghcr.io/nextain/naia-0.9-omni-24g:latest # récupérer la dernière version
podman stop naia-omni && podman rm naia-omni # nettoyer uniquement le conteneur (voir l'avertissement ci-dessous)
# Réexécutez exactement la commande de lancement utilisée à l'installation — il suffit de remonter le même volume de licence.
⚠️ N'appuyez pas sur « libérer (release) l'appareil » lors de la mise à jour. La libération ne sert que lorsque vous déplacez vers un autre ordinateur la machine utilisée. Si vous libérez en voulant mettre à jour, vous devrez vous ré-authentifier depuis le début. La mise à jour conserve l'abonnement et l'enregistrement de l'appareil tant que le volume de licence reste en place.
Les utilisateurs déjà authentifiés n'ont qu'à récupérer la dernière version et la relancer comme ci-dessus pour passer à la nouvelle version qui permet de changer de modèle (authentification conservée). Pour récupérer une version précise, utilisez le numéro de version comme :0.91 au lieu de :latest.
7. Voir aussi
- Implémentation de référence / exemple de code (open source) : le client vocal de naia-os
shell/src/lib/voice/(Apache 2.0) — contient le client réel qui dialogue avec cette API (naia-omni.ts) et la gestion de l'émotion/prosodie (emotion-tags.ts; mappage des expressions et extraction robuste dansvrm/expression.ts). Utilisez-le comme point de départ pour tester de nouveaux modèles et construire des applications Tauri. Essayez-le en direct sur la démo en direct 4.3. - Gamme et tarifs : 4.1 Tarifs des modèles
- Cloud (prévu) : 4.6 En ligne