Guia para desenvolvedores sobre como usar o modelo Naia a partir de código. Depois de executar o modelo via 4.4 Download do modelo Naia, use a API compatível com OpenAI servida localmente (sem gateway, sem fila) como está. Com qualquer SDK ou ferramenta OpenAI, você só precisa apontar a baseURL para este modelo.
Não é só para naia-os/interface — qualquer código que fale OpenAI Realtime/Chat/Audio/Embeddings se conecta como está, e você pode criar e executar novos aplicativos sobre este modelo.
1. Conectar · autenticação
- Base REST:
http://<host>:8892/v1(127.0.0.1no mesmo PC) - Realtime (WS):
ws://<host>:8892/v1/realtime(umws://<host>:8892simples também funciona — o caminho/v1/realtime+ o modelo padrão são aplicados automaticamente) - Conexão: localmente (
127.0.0.1) / via Tailscale, nenhuma autenticação é necessária — o contêiner verifica sua própria licença. Clientes que precisam de um campo de chave (OpenAI SDK etc.) podem passar qualquer valor (naia). Ao expor remotamente, coloque o Tailscale/VPN da §4.4 na frente.
🔑 Uma chave — a chave de assinatura
- Chave de assinatura — a chave de assinatura que você obtém no portal. Usada apenas no momento da execução do contêiner (ativação) (
-e NAIA_ACCOUNT_TOKEN=<subscription-key>). Ela verifica a assinatura e obtém uma licença com prazo definido (certificado). - Não há uma chave de conexão separada. Uma vez ativado, o contêiner verifica a licença localmente por conta própria com o certificado, então os clientes (naia-os, OpenAI SDK) só precisam se conectar pela URL —
127.0.0.1no mesmo PC, ou Tailscale/VPN (§4.4) a partir de outro dispositivo. Ele não chama o gateway a cada conexão. - O
api_keynos exemplos abaixo é um placeholder (o OpenAI SDK exige o campo) — o contêiner offline não o verifica, então qualquer valor como"naia"funciona.
2. Endpoints (compatíveis com OpenAI)
| Endpoint | Uso |
|---|---|
GET /health | Prontidão {"ready":true,"services":{tts,stt,llm},"vad":true} (sem autenticação) |
GET /v1/models | Lista de modelos |
WS /v1/realtime | Sessão de voz em tempo real (VAD, barge-in, emoção) |
POST /v1/chat/completions | Chat (streaming) |
POST /v1/audio/speech | Texto para fala (TTS) |
POST /v1/audio/transcriptions | Fala para texto (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) — basta trocar a 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)
Transcrição (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. Voz em tempo real — fluxo de conexão (WS)
O mesmo fluxo que a 4.3 demo ao vivo usa. (Offline começa imediatamente, sem fila/atribuição de gateway.)
-
Conectar — abra
ws://<host>:8892. -
Primeiro quadro (autenticação · idioma) — WebSockets de navegador não podem enviar cabeçalhos, então envie como primeira mensagem:
{ "setup": { "apiKey": "naia", "locale": "en" } } -
Quando o servidor enviar
session.created, configure a sessão comsession.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)>" } } -
Troca de mensagens
Cliente → Servidor Entrada de voz {"type":"input_audio_buffer.append","audio":"<base64 PCM16 24kHz>"}(o server VAD detecta o fim da fala)Entrada de texto conversation.item.createe depoisresponse.createBarge-in response.cancelServidor → Cliente response.audio.deltabloco de áudio base64 PCM16 24kHz response.audio_transcript.delta/response.text.deltatexto da resposta (streaming) conversation.item.input_audio_transcription.completedtranscrição da sua fala emotion.updatedtag de emoção / prosódia (§5) response.donefim de um turno
4. Idiomas — 30 idiomas (padrão = auto/global)
O modelo suporta 30 idiomas (árabe, birmanês, chinês, dinamarquês, neerlandês, inglês, finlandês, francês, alemão, grego, hebraico, hindi, indonésio, italiano, japonês, khmer, coreano, laosiano, malaio, norueguês, polonês, português, russo, espanhol, suaíli, sueco, tagalo, tailandês, turco, vietnamita).
- Padrão (não definido) = global/auto — ele detecta o idioma em que você falou e responde nesse idioma (por turno).
- Para fixar um idioma específico, forneça um código ISO-639-1 (por exemplo
ko/en/ja) emsetup.localeou noinput_audio_transcription.languagedosession.update.
5. Formato de saída (emoção · tags de prosódia)
O formato de saída é ajustado para conversa de voz — se o cliente o conhecer, pode expressar de forma mais rica.
- Tags de prosódia: o texto da resposta contém tags em inglês minúsculo entre colchetes como
[laughing],[sigh],[breath],[pause],[hesitation]misturadas onde a emoção muda (para a prosódia da fala). O modelo é instruído a não usar tags em coreano como[웃음], indicações cênicas entre parênteses como(smiling), ou asteriscos como*smiles*. Vocabulário conhecido:laughing/laugh/laughter/chuckle/giggle · sigh/exhale · breath/inhale · pause · hesitation · gasp/cough/sneeze/yawn/sniff/hum · cry/sob/moan/whisper/shout/cheer(outras tags são repassadas). - Para cada tag, o servidor envia um evento
emotion.updated1:1 (state== nome da tag, em minúsculas):{ "type": "emotion.updated", "state": "laughing", "tag": "[laughing]", "known": true } - O caminho TTS mantém as tags e as alimenta na síntese para a prosódia da fala, enquanto o chat
text.deltaenvia texto limpo com as tags removidas. (Sem emojis, markdown ou autonarração entre parênteses na saída.) - Mapeamento no cliente (referência naia-os): mapeie
emotion.updated.state(tag de prosódia) para expressões do avatar —laughing/chuckle/giggle/cheer → happy,sigh/exhale/cry/sob → sad,gasp → surprised,shout → angry,hesitation → think. Prosódia não emocional comobreath·pausenão muda a expressão (mantenha a anterior — para que o avatar não pisque para neutro a cada respiração). - Recomenda-se tratamento robusto: a saída do LLM nem sempre é exata. Prefira
emotion.updated, mas se ela faltar, detecte automaticamente tags na própria transcrição (maiúsculas[HAPPY]/ tags de prosódia minúsculas) ou indicações cênicas vazadas ((smiles)·*sigh*) e reflita-as na expressão; se não houver pista, mantenha a expressão atual (cf. naia-osshell/src/lib/vrm/expression.tsextractExpression).
6. Trocar o modelo de conversa · Subir uma nova versão (operação)
Guia detalhado para trocar diretamente pela linha de comando. Assinantes individuais podem usá-lo como está (sem chave necessária), e também inclui opções de bloqueio para operação compartilhada/quiosque. Um resumo simplificado está em 4.4 Offline.
6.1 Trocar o modelo de conversa (a partir da 0.91)
Deixe o contêiner como está e troque apenas o modelo encarregado da conversa em tempo de execução. A voz (fala/escuta), a marca d'água e a autenticação da assinatura permanecem intactas.
Três coisas para saber primeiro:
- O modelo padrão é um LLM aberto integrado. Você pode voltar ao padrão a qualquer momento depois de trocar.
- O novo modelo a subir precisa estar no formato GGUF. E como o recurso de voz usa cerca de 10GB de memória, o modelo de conversa pode chegar a cerca de 14GB. Modelos maiores são recusados, e se uma subida falhar no meio, ele volta automaticamente ao modelo que estava em uso (a conversa não é interrompida).
- Assinantes individuais não precisam de uma chave separada. A autenticação da assinatura (licença) na sua própria máquina já é a sua permissão, então basta trocar com o comando abaixo — assim como a voz não precisa de chave. (Somente em uma caixa compartilhada/quiosque usada por várias pessoas, o operador pode aplicar um bloqueio na execução com
-e NAIA_ADMIN_KEY=senha_definida, e nesse caso você envia-H "Authorization: Bearer senha_definida"junto com a requisição.)
Prática — primeiro defina apenas o endereço:
BASE=http://127.0.0.1:8892 # de outro dispositivo, use o endereço https da §4.4 (ex.: ...:8443)
① Veja qual é o modelo atual e quanta memória resta:
curl -s $BASE/admin/llm/status
② Troque o modelo — substitua apenas a parte do modelo entre aspas e cole.
Coloque o endereço do cartão de modelo do HuggingFace (https://huggingface.co/Qwen/Qwen2.5-7B-Instruct-GGUF) ou seu id (Qwen/Qwen2.5-7B-Instruct-GGUF) como está:
curl -s -X POST $BASE/admin/llm/swap \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen2.5-7B-Instruct-GGUF","pull":true}'
O prefixo hf.co/ e a quantização são adicionados automaticamente (o padrão é Q4_K_M). Se quiser uma quantização específica, escreva-a ao final, como Qwen/Qwen2.5-7B-Instruct-GGUF:Q5_K_M. Baixar um modelo pela primeira vez leva de dezenas de segundos a alguns minutos.
②-offline — trocar pelo seu próprio arquivo GGUF, sem internet.
Em situações como exposições ou consultas sem internet, em vez de baixar do HuggingFace, você registra um arquivo GGUF que já possui e troca por ele. (Regra de distinção: se o nome tem uma barra como organização/repositório, é HuggingFace online; se for um nome simples sem barra, é um modelo local.)
Copie e cole uma linha de cada vez. Coloque o nome desejado no lugar de meumodelo e o nome real do arquivo no lugar de meumodelo.gguf:
podman cp ./meumodelo.gguf naia-omni:/app/models/meumodelo.gguf
podman exec naia-omni sh -lc 'printf "FROM /app/models/meumodelo.gguf\n" > /tmp/Modelfile && ollama create meumodelo -f /tmp/Modelfile'
curl -s -X POST $BASE/admin/llm/swap -H "Content-Type: application/json" -d '{"model":"meumodelo:latest","pull":false}'
⚠️ GGUF convertidos/mesclados por conta própria podem não ter o template de chat, fazendo a resposta divagar/cortar. Nesse caso, adicione o template de chat da família do modelo (
TEMPLATE) e os tokens de parada (PARAMETER stop) ao Modelfile no passo 2 e registre — detalhes para desenvolvedores na [implementação de referência §7]. (GGUF Instruct oficiais do HuggingFace normalmente já incluem isso e podem ser usados como estão.)
③ Volte ao modelo padrão:
curl -s -X POST $BASE/admin/llm/restore
Em uma caixa compartilhada/quiosque (quando o operador definiu
NAIA_ADMIN_KEY), adicione-H "Authorization: Bearer senha_definida"a cada comando acima. Assinantes individuais não precisam disso.
Mesmo depois de trocar, aplicativos como o naia-os continuam se conectando ao mesmo endereço, como antes (não é preciso reconectar). Se quiser que continue iniciando com esse modelo mesmo após reinicializações ou atualizações, defina o modelo padrão ao executar o contêiner com -e NAIA_LLM_MODEL=Qwen/Qwen2.5-7B-Instruct-GGUF.
6.2 Atualizar para uma nova versão
Quando sair uma nova versão, troque apenas a imagem (versão) e deixe a assinatura/configurações como estão. Na primeira vez que você ligar a nova versão, o contêiner se reautentica automaticamente pela internet (mesma assinatura/dispositivo — não é preciso reinserir a chave manualmente). Por isso, ao atualizar você deve estar conectado à internet.
podman pull ghcr.io/nextain/naia-0.9-omni-24g:latest # baixar a versão mais recente
podman stop naia-omni && podman rm naia-omni # limpar apenas o contêiner (veja o aviso abaixo)
# Execute novamente o mesmo comando de execução usado na primeira instalação — basta montar o mesmo volume de licença.
⚠️ Ao atualizar, não clique em "liberar dispositivo (release)". A liberação só é usada quando você move para outro computador. Se você liberar tentando atualizar, terá que autenticar do zero. A atualização mantém a assinatura e o registro do dispositivo desde que você mantenha o volume de licença intacto.
Usuários já autenticados anteriormente, basta baixar a versão mais recente e religar como acima para migrar diretamente para a nova versão que permite trocar o modelo (autenticação mantida). Para baixar uma versão específica, use o número da versão como :0.91 em vez de :latest.
7. Veja também
- Implementação de referência / código de exemplo (código aberto): o cliente de voz do naia-os
shell/src/lib/voice/(Apache 2.0) — contém o cliente real que conversa com esta API (naia-omni.ts) e o tratamento de emoção/prosódia (emotion-tags.ts; mapeamento de expressões e extração robusta emvrm/expression.ts). Use-o como ponto de partida para testar novos modelos e criar aplicativos Tauri. Experimente ao vivo em 4.3 demo ao vivo. - Linha e preços: 4.1 Preços dos modelos
- Nuvem (planejado): 4.6 Online