Naia
Índice
  1. 1Manual em vídeo
  2. 2Naia OS Live USB
  3. 3Instalação e implantação
  4. 3.1Instalação do Naia OS (ISO)
  5. 3.2Instalação do app
  6. 4Primeiros passos
  7. 4.1Naia Model Pricing
  8. 4.2naia-omni-cascade
  9. 4.3demo
  10. 4.4naia-offline
  11. 4.5naia-model-dev
  12. 4.6naia-online
  13. 5Tela principal
  14. 6Bate-papo
  15. 6.1DJ de rádio pessoal e guia de exposição
  16. 7Histórico de conversas
  17. 8Progresso do Trabalho
  18. 9Habilidades
  19. 10Canais
  20. 11Agentes
  21. 12Diagnóstico
  22. 13Área de Trabalho
  23. 14Navegador
  24. 15Gerenciamento de Painéis
  25. 16Conversa por Voz
  26. 17Configurações
  27. 18Detalhes da ferramenta
  28. 19Conta Naia
  29. 20Solução de problemas
  30. 21Uso e contribuição de código aberto

4.5. naia-model-dev

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.1 no mesmo PC)
  • Realtime (WS): ws://<host>:8892/v1/realtime (um ws://<host>:8892 simples 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 URL127.0.0.1 no mesmo PC, ou Tailscale/VPN (§4.4) a partir de outro dispositivo. Ele não chama o gateway a cada conexão.
  • O api_key nos 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)

EndpointUso
GET /healthProntidão {"ready":true,"services":{tts,stt,llm},"vad":true} (sem autenticação)
GET /v1/modelsLista de modelos
WS /v1/realtimeSessão de voz em tempo real (VAD, barge-in, emoção)
POST /v1/chat/completionsChat (streaming)
POST /v1/audio/speechTexto para fala (TTS)
POST /v1/audio/transcriptionsFala para texto (STT)
POST /v1/embeddingsEmbeddings

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.)

  1. Conectar — abra ws://<host>:8892.

  2. 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" } }
    
  3. Quando o servidor enviar session.created, configure a sessão com session.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)>"
      }
    }
    
  4. 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 textoconversation.item.create e depois response.create
    Barge-inresponse.cancel
    Servidor → 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) em setup.locale ou no input_audio_transcription.language do session.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.updated 1: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.delta envia 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 como breath·pause nã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-os shell/src/lib/vrm/expression.ts extractExpression).

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:

  1. O modelo padrão é um LLM aberto integrado. Você pode voltar ao padrão a qualquer momento depois de trocar.
  2. 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).
  3. 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 em vrm/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