Entwicklerhandbuch zur Nutzung des Naia-Modells aus dem Code. Nachdem du das Modell über 4.4 Naia-Modell-Download ausgeführt hast, nutze die lokal bereitgestellte OpenAI-kompatible API (kein Gateway, keine Warteschlange) unverändert. Mit jedem OpenAI-SDK oder -Tool musst du nur die baseURL auf dieses Modell richten.
Nicht nur für naia-os/die Oberfläche — jeder Code, der OpenAI Realtime/Chat/Audio/Embeddings spricht, verbindet sich unverändert, und du kannst auf diesem Modell neue Anwendungen erstellen und ausführen.
1. Verbindung · Authentifizierung
- REST-Basis:
http://<host>:8892/v1(127.0.0.1auf demselben PC) - Realtime (WS):
ws://<host>:8892/v1/realtime(ein nacktesws://<host>:8892funktioniert ebenfalls — Pfad/v1/realtime+ Standardmodell werden automatisch angewendet) - Verbindung: lokal (
127.0.0.1) / Tailscale, ist keine Authentifizierung erforderlich — der Container verifiziert seine Lizenz selbst. Clients, die ein Schlüsselfeld benötigen (OpenAI-SDK usw.), können einen beliebigen Wert übergeben (naia). Bei externer Bereitstellung §4.4 Tailscale/VPN davorschalten.
🔑 Ein Schlüssel — der Abonnementschlüssel
- Abonnementschlüssel — der Abonnementschlüssel, den du aus dem Portal erhältst. Wird nur zur Laufzeit des Containers (Aktivierung) verwendet (
-e NAIA_ACCOUNT_TOKEN=<subscription-key>). Er prüft das Abonnement und erhält eine zeitlich begrenzte Lizenz (Zertifikat). - Es gibt keinen separaten Verbindungsschlüssel. Nach der Aktivierung verifiziert der Container lokal selbst mit dem Zertifikat, sodass sich Clients (naia-os, OpenAI-SDK) nur noch per URL verbinden müssen —
127.0.0.1auf demselben PC oder Tailscale/VPN (§4.4) von einem anderen Gerät. Er ruft das Gateway nicht pro Verbindung auf. - Der
api_keyin den folgenden Beispielen ist ein Platzhalter (das OpenAI-SDK verlangt das Feld) — der Offline-Container prüft ihn nicht, daher funktioniert jeder Wert wie"naia".
2. Endpunkte (OpenAI-kompatibel)
| Endpunkt | Verwendung |
|---|---|
GET /health | Bereitschaft {"ready":true,"services":{tts,stt,llm},"vad":true} (ohne Authentifizierung) |
GET /v1/models | Modellliste |
WS /v1/realtime | Echtzeit-Sprachsitzung (VAD, Barge-in, Emotion) |
POST /v1/chat/completions | Chat (Streaming) |
POST /v1/audio/speech | Sprachsynthese (TTS) |
POST /v1/audio/transcriptions | Spracherkennung (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}'
OpenAI-SDK (Python) — einfach baseURL austauschen:
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)
Transkription (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. Echtzeit-Sprache — Verbindungsablauf (WS)
Derselbe Ablauf, den die 4.3 Live-Demo nutzt. (Offline startet sie sofort, ohne Gateway-Warteschlange/-Zuweisung.)
-
Verbinden — öffne
ws://<host>:8892. -
Erster Frame (Authentifizierung · Sprache) — Browser-WebSockets können keine Header senden, also sende dies als erste Nachricht:
{ "setup": { "apiKey": "naia", "locale": "en" } } -
Wenn der Server
session.createdsendet, konfiguriere die Sitzung mitsession.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)>" } } -
Austausch
Client → Server Spracheingabe {"type":"input_audio_buffer.append","audio":"<base64 PCM16 24kHz>"}(das Server-VAD erkennt das Ende der Sprache)Texteingabe conversation.item.create, dannresponse.createBarge-in response.cancelServer → Client response.audio.deltabase64-PCM16-24kHz-Audiosegment response.audio_transcript.delta/response.text.deltaAntworttext (Streaming) conversation.item.input_audio_transcription.completedTranskription deiner Sprache emotion.updatedEmotions- / Prosodie-Tag (§5) response.doneEnde eines Turns
4. Sprachen — 30 Sprachen (Standard = auto/global)
Das Modell unterstützt 30 Sprachen (Arabisch, Birmanisch, Chinesisch, Dänisch, Niederländisch, Englisch, Finnisch, Französisch, Deutsch, Griechisch, Hebräisch, Hindi, Indonesisch, Italienisch, Japanisch, Khmer, Koreanisch, Laotisch, Malaiisch, Norwegisch, Polnisch, Portugiesisch, Russisch, Spanisch, Swahili, Schwedisch, Tagalog, Thai, Türkisch, Vietnamesisch).
- Standard (nicht gesetzt) = global/auto — es erkennt die von dir gesprochene Sprache und antwortet in dieser Sprache (pro Turn).
- Um eine bestimmte Sprache festzulegen, gib einen ISO-639-1-Code (z. B.
ko/en/ja) insetup.localeoder ininput_audio_transcription.languagevonsession.updatean.
5. Ausgabeformat (Emotions- · Prosodie-Tags)
Das Ausgabeformat ist abgestimmt auf die Sprachunterhaltung — wenn der Client es kennt, kann er sich reicher ausdrücken.
- Prosodie-Tags: Der Antworttext enthält englische Tags in eckigen Klammern in Kleinschreibung wie
[laughing],[sigh],[breath],[pause],[hesitation], eingestreut dort, wo sich die Emotion verschiebt (für die Sprachprosodie). Das Modell wird angewiesen, keine koreanischen Tags wie[웃음], eingeklammerte Regieanweisungen wie(smiling)oder Sternchen wie*smiles*zu verwenden. Bekanntes Vokabular:laughing/laugh/laughter/chuckle/giggle · sigh/exhale · breath/inhale · pause · hesitation · gasp/cough/sneeze/yawn/sniff/hum · cry/sob/moan/whisper/shout/cheer(andere Tags werden unverändert durchgereicht). - Für jedes Tag sendet der Server ein 1:1-
emotion.updated-Ereignis (state== Tag-Name, Kleinschreibung):{ "type": "emotion.updated", "state": "laughing", "tag": "[laughing]", "known": true } - Der TTS-Pfad behält die Tags bei und gibt sie in die Synthese ein, um die Sprachprosodie zu erzeugen, während das Chat-
text.deltasauberen Text mit entfernten Tags sendet. (Keine Emojis, kein Markdown und keine eingeklammerte Selbst-Narration in der Ausgabe.) - Client-Mapping (naia-os-Referenz): Ordne
emotion.updated.state(Prosodie-Tag) den Avatar-Ausdrücken zu —laughing/chuckle/giggle/cheer → happy,sigh/exhale/cry/sob → sad,gasp → surprised,shout → angry,hesitation → think. Nicht-emotionale Prosodie wiebreath·pauseändert den Ausdruck nicht (behalte den vorherigen bei — damit er nicht bei jedem Atemzug auf neutral zurückspringt). - Robuste Verarbeitung empfohlen: Die LLM-Ausgabe ist nicht immer exakt. Bevorzuge
emotion.updated, aber falls es fehlt, erkenne automatisch Tags in der Transkription selbst (Großschreibung[HAPPY]/ Prosodie-Tags in Kleinschreibung) oder durchgesickerte Regieanweisungen ((smiles)·*sigh*) und spiegle sie im Ausdruck wider; gibt es keinen Hinweis, behalte den aktuellen Ausdruck bei (vgl. naia-osshell/src/lib/vrm/expression.tsextractExpression).
6. Konversationsmodell wechseln · neue Version aufspielen (Betrieb)
Ausführliche Anleitung zum direkten Wechsel über die Kommandozeile. Auch Privatabonnenten können sie unverändert nutzen (kein Schlüssel nötig), und für den gemeinsamen Betrieb bzw. den Kiosk-Betrieb sind Sperroptionen enthalten. Eine einfache Zusammenfassung findest du unter 4.4 Offline.
6.1 Konversationsmodell wechseln (ab 0.91)
Du lässt den Container unverändert und wechselst zur Laufzeit nur das Modell, das die Konversation übernimmt. Stimme (Sprechen · Hören), Wasserzeichen und Abonnementverifizierung bleiben erhalten.
Drei Dinge, die du zuerst wissen solltest:
- Das Standardmodell ist ein integriertes Open-Weight-LLM. Du kannst jederzeit auf den Standard zurückkehren, nachdem du gewechselt hast.
- Das neu aufzuspielende Modell muss im GGUF-Format vorliegen. Und da die Sprachfunktionen etwa 10 GB Speicher belegen, kann das Konversationsmodell bis etwa 14 GB groß sein. Größere Modelle werden abgelehnt, und falls das Aufspielen fehlschlägt, wird automatisch auf das bisher genutzte Modell zurückgeschaltet (die Konversation wird nicht unterbrochen).
- Privatabonnenten brauchen keinen separaten Schlüssel. Die Abonnementverifizierung (Lizenz) auf deinem eigenen Rechner ist gleichbedeutend mit der Berechtigung, daher kannst du einfach mit dem untenstehenden Befehl wechseln — genau wie die Stimme keinen Schlüssel benötigt. (Nur auf einer gemeinsam genutzten Kiosk-Box, die mehrere Personen verwenden, kann der Betreiber beim Start mit
-e NAIA_ADMIN_KEY=festgelegtes_passworteine Sperre setzen; dann sendet man bei der Anfrage zusätzlich-H "Authorization: Bearer festgelegtes_passwort".)
Praxis — lege nur die Adresse fest:
BASE=http://127.0.0.1:8892 # auf einem anderen Gerät: die https-Adresse aus §4.4 (z. B. ...:8443)
① Sieh nach, welches Modell gerade läuft und wie viel Speicher übrig ist:
curl -s $BASE/admin/llm/status
② Wechsle das Modell — ersetze nur die Modellstelle innerhalb der Anführungszeichen und füge es ein.
Gib die Adresse der HuggingFace-Modellkarte (https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF) oder deren id (Qwen/Qwen2.5-7B-Instruct-GGUF) unverändert an:
curl -s -X POST $BASE/admin/llm/swap \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'
Das hf.co/-Präfix und die Quantisierung (quant) werden automatisch angehängt (Standard ist Q4_K_M). Wenn du eine bestimmte Quantisierung möchtest, schreibst du sie wie Qwen/Qwen2.5-7B-Instruct-GGUF:Q5_K_M hinten dran. Beim ersten Herunterladen eines Modells dauert es einige Dutzend Sekunden bis ein paar Minuten.
②-Offline — ohne Internet, mit einer eigenen GGUF-Datei wechseln.
Wenn es wie bei einer Ausstellung oder Beratung kein Internet gibt, lädst du nicht von HuggingFace, sondern registrierst eine bereits vorhandene GGUF-Datei und wechselst zu ihr. (Unterscheidungsregel: Enthält der Name einen Schrägstrich wie organisation/repository, ist es HuggingFace online; ist es ein einfacher Name ohne Schrägstrich, ist es ein lokales Modell.)
Kopiere die Zeilen einzeln und füge sie ein. Setze an die Stelle meinmodell den gewünschten Namen und an die Stelle meinmodell.gguf den tatsächlichen Dateinamen:
podman cp ./meinmodell.gguf naia-omni:/app/models/meinmodell.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/meinmodell.gguf\n" > /tmp/Modelfile && ollama create meinmodell -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"meinmodell:latest","pull":false}'
⚠️ Bei einer selbst konvertierten · zusammengeführten GGUF fehlt unter Umständen die Chat-Vorlage, sodass die Antworten zusammenhanglos werden oder abgeschnitten sind. In diesem Fall füge im Modelfile aus Schritt 2 die Chat-Vorlage der Modellfamilie (
TEMPLATE) und die Stopp-Tokens (PARAMETER stop) hinzu und registriere es so — Entwicklerdetails unter [Referenzimplementierung §7]. (Offizielle Instruct-GGUFs von HuggingFace enthalten sie meist bereits und funktionieren unverändert.)
③ Auf das Standardmodell zurücksetzen:
curl -s -X POST $BASE/admin/llm/restore
Auf einer gemeinsam genutzten Kiosk-Box (wenn der Betreiber
NAIA_ADMIN_KEYgesetzt hat) füge jedem der obigen Befehle-H "Authorization: Bearer festgelegtes_passwort"hinzu. Privatabonnenten brauchen das nicht.
Auch nach dem Wechsel verbinden sich Apps wie naia-os einfach unverändert mit derselben Adresse (eine erneute Verbindung ist nicht nötig). Wenn du möchtest, dass auch nach einem Neustart oder Update weiterhin mit diesem Modell gestartet wird, gib beim Start des Containers mit -e NAIA_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct-GGUF das Standardmodell an.
6.2 Auf eine neue Version aktualisieren
Wenn eine neue Version erscheint, wechselst du nur das Image (die Version) und lässt Abonnement und Einstellungen unverändert. Beim ersten Start der neuen Version reauthentifiziert sich der Container automatisch über das Internet (Abonnement · Gerät bleiben gleich — der Schlüssel muss nicht von Hand erneut eingegeben werden). Daher musst du beim Aktualisieren mit dem Internet verbunden sein.
podman pull ghcr.io/nextain/naia-0.9-omni-24g:latest # die neueste Version holen
podman stop naia-omni && podman rm naia-omni # nur den Container aufräumen (siehe Hinweis unten)
# Führe denselben Startbefehl erneut aus, den du bei der Erstinstallation verwendet hast — du musst nur dasselbe Lizenz-Volume erneut einbinden.
⚠️ Drücke beim Aktualisieren nicht auf „Gerät freigeben (release)". Die Freigabe ist nur dafür da, wenn du den genutzten Computer auf einen anderen Computer umziehst. Gibst du beim Versuch zu aktualisieren das Gerät frei, musst du dich von Grund auf neu authentifizieren. Beim Update bleiben Abonnement und Geräteregistrierung erhalten, solange du nur das Lizenz-Volume unverändert lässt.
Wer sich zuvor schon authentifiziert hat, muss wie oben nur die neueste Version holen und neu starten, und geht damit direkt zur neuen Version über, die den Modellwechsel erlaubt (Authentifizierung bleibt erhalten). Um eine bestimmte Version gezielt zu holen, schreibe statt :latest eine Versionsnummer wie :0.91.
7. Siehe auch
- Referenzimplementierung / Beispielcode (Open Source): der Sprach-Client von naia-os
shell/src/lib/voice/(Apache 2.0) — enthält den tatsächlichen Client, der mit dieser API kommuniziert (naia-omni.ts), sowie die Emotions-/Prosodie-Verarbeitung (emotion-tags.ts; Ausdrucks-Mapping & robuste Extraktion invrm/expression.ts). Nutze ihn als Ausgangspunkt zum Testen neuer Modelle und zum Erstellen von Tauri-Apps. Probiere ihn live aus auf der 4.3 Live-Demo. - Modellpalette & Preise: 4.1 Modellpreise
- Cloud (geplant): 4.6 Online