Communitygithub.com

V4TIME/V4SKILL

Crie aplicativos Flask com IA no Termux, integrando o NaraRouter.

O que é V4SKILL?

Esta skill permite construir e executar uma aplicação web local de IA diretamente em um dispositivo Android rodando Termux. Ela foca na criação de uma interface de chat com IA usando Python e Flask, integrando múltiplas APIs de IA como NaraRouter (gateway unificado com 53+ modelos) e NVIDIA NIM. A arquitetura segue o padrão Navegador → Flask → API(s) de IA, com suporte opcional ao Hermes Agent como backend de raciocínio. A skill inclui um motor de despacho de API que classifica tarefas (matemática, código, ciência, geral) e seleciona a melhor API disponível, além de suporte a modo de voz via Web Speech API. É ideal para usuários que desejam uma assistente de IA local no celular, com gerenciamento de sessões e memória entre conversas, seguindo um design minimalista e profissional, sem elementos decorativos típicos de geradores de IA.

Funciona com✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/V4TIME/V4SKILL/tree/HEAD/termux-flask

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Aplicativo Web de IA Local no Termux

Construa e execute um aplicativo web local em um dispositivo Android rodando Termux, integrando APIs de IA (especialmente NaraRouter) e, opcionalmente, o Hermes Agent como backend de raciocínio.

Quando usar

  • O usuário quer uma interface de chat/assistente de IA local rodando no celular
  • Construindo um aplicativo web no Termux com Python/Flask
  • Integrando múltiplas APIs de IA com roteamento baseado em descrição
  • Adicionando modo de voz via Web Speech API do navegador
  • Gerenciando sessões e memória entre conversas
  • O usuário cola instruções de instalação para uma ferramenta de desenvolvimento (binário, Rust/cargo, npm/npx, pip) e pede para instalar e usar — avalie primeiro a viabilidade para o ambiente do celular/Termux, depois instale com permissão

Padrão de arquitetura

Navegador (Chrome no Android) → Backend Flask (Termux) → API(s) de IA
                                 ↓
                             Hermes Agent (opcional, como cérebro)

Princípio chave: O aplicativo web é a camada de interface. O Hermes Agent (se usado) é o cérebro. O backend Flask faz a ponte entre eles.

Início rápido

# No Termux
mkdir ~/sayip && cd ~/sayip
mkdir -p templates static/css static/js storage/sessions
# app.py — Flask mínimo para começar
from flask import Flask, render_template, request, jsonify
from pathlib import Path
import json, time, uuid, os

app = Flask(__name__, template_folder="templates")
HERMES_HOME = Path(os.path.expanduser("~/.hermes"))
SAYIP_STORAGE = Path(os.path.expanduser("~/sayip/storage"))

@app.route("/")
def index():
    return render_template("chat.html")

@app.route("/api/chat", methods=["POST"])
def chat():
    data = request.get_json()
    # ... processa mensagem, chama API, retorna resposta

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000)
# Executar
python3 app.py
# Abrir no Chrome: http://127.0.0.1:5000

Padrões de integração de API

NaraRouter (recomendação principal)

O NaraRouter é um gateway unificado de IA com 53+ modelos, com plano gratuito disponível.

Formato da chave: sk-nry-... (começa com sk-nry-)

Endpoints (todos sob a mesma URL base + chave Bearer):

EndpointTipoUso
https://router.bynara.id/v1/chat/completionsCompatível com OpenAIChat, tarefas gerais
https://router.bynara.id/v1/messagesCompatível com AnthropicRequisições no formato Anthropic
https://router.bynara.id/v1/responsesOpenAI ResponsesAPI de respostas com estado
https://router.bynara.id/v1/embeddingsOpenAI EmbeddingsEmbeddings vetoriais
https://router.bynara.id/v1/modelsLISTAObter modelos disponíveis para seu plano
https://api-images.bynara.id/v1/images/generationsGeração de imagemTexto-para-imagem
https://api-images.bynara.id/v1/images/editsEdição de imagemEdição de imagens

Teste uma chave antes de usar:

curl -s -X POST https://router.bynara.id/v1/chat/completions \
  -H "Authorization: Bearer sk-nry-SUA_CHAVE" \
  -H "Content-Type: application/json" \
  -d '{"model":"agnes-2.5-flash","messages":[{"role":"user","content":"oi"}],"max_tokens":10}'

Modelos gratuitos conhecidos que funcionam: agnes-2.5-flash, agnes-2.0-flash

Verifique os modelos do seu plano:

curl -s https://router.bynara.id/v1/models \
  -H "Authorization: Bearer sk-nry-SUA_CHAVE" | python3 -c "import sys,json; [print(m['id']) for m in json.load(sys.stdin).get('data',[])]"

IMPORTANTE: Nem todos os 53 modelos estão em todos os planos. deepseek-v4-pro-0813-bynara requer créditos. deepseek-v4-flash-free pode não estar incluído. Sempre teste antes de configurar.

API NVIDIA NIM (integrate.api.nvidia.com/v1)

O NVIDIA NIM oferece 82+ modelos através de um único endpoint compatível com OpenAI. URL base: https://integrate.api.nvidia.com/v1 Endpoint: https://integrate.api.nvidia.com/v1/chat/completions Formato da chave: nvapi- (ex.: nvapi-_qJeGmEP...)

Teste uma chave antes de usar:

curl -s -X POST https://integrate.api.nvidia.com/v1/chat/completions \
  -H "Authorization: Bearer nvapi-..." \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{"model":"nvidia/nemotron-3.5-lightning-30b-a3b","messages":[{"role":"user","content":"oi"}],"max_tokens":10,"stream":true}'

Modelos gratuitos conhecidos que funcionam (testados na rede móvel do Termux):

ID do ModeloChaveStatusNotas
nvidia/nemotron-3.5-lightning-30b-a3bnvapi-...FuncionaRápido (~5-7s para simples), raciocínio forte, suporta modo thinking. Mais confiável no móvel.
gpt-oss-20bnvapi-...FuncionaCompatível com OpenAI
deepseek-ai/deepseek-v4-pro-0813nvapi-...Risco de timeoutFunciona no desktop, frequentemente timeout na rede móvel do Termux
moonshotai/kimi-k3nvapi-...Risco de timeoutCapaz de visão, mas timeout na rede móvel

Aviso de assimetria de chaves: Nem todos os modelos estão disponíveis em todas as chaves. Chave 1 (nvapi-_qJeGmEP...) tem acesso mais amplo. Chave 2 (nvapi-WDb7Rr...) é mais restrita — alguns modelos retornam 404. Sempre teste um modelo com uma chave específica antes de configurar.

Indicadores de chave morta:

  • 401 Unauthorized — chave inválida/expirada
  • 404 Not Found em um modelo — modelo não está nesta conta/chave
  • Timeout de conexão (30s+) — API lenta/inacessível desta rede
  • Chaves Teamorouter sk-teamo-... → insufficient_balance no endpoint OpenAI (https://api.teamorouter.com/v1); modelo não suportado no endpoint Anthropic (https://api.teamorouter.com). Morta. Oferece modelo deepseek-v4-pro-free mas inutilizável sem saldo.

Consulte references/nvidia-api.md para detalhes completos: padrões Python específicos de modelos (modo thinking do Nemotron, DeepSeek não-streaming, multimodal Kimi vision), extração de reasoning_content em streaming, padrão de timeout rígido baseado em thread para urllib e formato de payload de visão.

Motor de despacho de API

Quando você tem múltiplas APIs configuradas, construa um despachante que:

  1. Classifica a tarefa (matemática, código, ciência, geral)
  2. Escolhe a melhor API com base no texto da descrição
  3. Chama a API e retorna o resultado
  4. Cai para outra API se a primeira falhar

Consulte references/nararouter-api.md para detalhes de integração.

Use a stdlib urllib, não requests

No Termux, pip install requests pode timeout em downloads grandes. Use urllib da stdlib do Python — está sempre disponível e lida bem com APIs JSON:

import urllib.request, json

def call_api(url, payload, api_key):
    data = json.dumps(payload).encode()
    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read())

Preferências de design (específicas do usuário)

Aparência minimalista, profissional e não gerada por IA:

  • Tema escuro, espaçamento justo, sem gradientes, sem sombras decorativas
  • Fontes monoespaçadas quando apropriado (código, chaves de API, timestamps)
  • Retângulos simples para balões de chat — sem bordas arredondadas a menos que funcionais
  • Barra lateral = lista simples, sem ícones a menos que significativos
  • Busque implementações de referência em vez de gerar designs do zero
  • Quando em dúvida, remova recursos em vez de adicioná-los

Antipadrões concretos — estes fazem um webapp parecer gerado por IA, remova-os:

  • Emojis em todo cabeçalho/bloco/botão (🤖, ⚙, 🎭, 🔌, 🧠, 💬, 🎤) — remova. Apenas rótulos de texto simples.
  • Brilho de acento / sombra de caixa ao passar o mouse (box-shadow: 0 0 12px var(--accent-glow)) — remova. Apenas cores chapadas.
  • Animações keyframe de bounce / pulse / slideDown (@keyframes bounce, @keyframes pulse, @keyframes slideDown) — remova. Sem design de movimento.
  • Cartão de boas-vindas com ícones (.welcome-screen com welcome-icon, grade welcome-hints) — substitua por um placeholder de uma linha na área de entrada.
  • Ícone de marca decorativo ao lado do logotipo (.brand-icon com emoji) — remova.
  • Fitas, badges, pontos de status decorando o cabeçalho (.health-badge, .health-dot) — remova ou torne puramente funcional.
  • Fundos gradientes, glassmorfismo, sobreposições translúcidas — nunca.

Regra de ouro: abra a página no navegador e pergunte "um desenvolvedor humano escreveria este CSS manualmente para uma ferramenta utilitária?" Se a resposta for não (porque tem movimento, brilho ou decoração com emoji), remova. Uma ferramenta normal parece um terminal, um aplicativo de notas ou um chat mínimo — não uma página de destino.

Modo de voz:

  • Use Web Speech API (SpeechRecognition + SpeechSynthesis) — gratuita, integrada ao navegador
  • Botão de alternância para mudar modo texto/voz
  • No modo voz: microfone escuta continuamente, fala→texto→envia, resposta→fala
  • Qualidade de voz é limitada ao que o Chrome no Android oferece — não prometa "voz do Ultron"

Preferências de fluxo de trabalho (específicas do usuário)

Pergunte antes de instalar pacotes: Antes de executar pip install ou apt install, pergunte ao usuário primeiro. Isso é inegociável.

Mostre progresso continuamente: Se uma tarefa levar mais de alguns minutos, mostre resultados intermediários. Não desapareça por longos períodos sem saída. Se estiver travado em algo, diga isso e o que está tentando.

Sem palestras de segurança: O usuário entende os riscos das chaves de API. Não dê palestras sobre segurança de chaves, exposição ou melhores práticas a menos que seja explicitamente perguntado. Eles forneceram sua própria explicação quando relevante.

Prefira soluções funcionais a arquitetura perfeita: Faça algo funcionar primeiro. O teste da chave NaraRouter mostrou isso — teste a chave, encontre um modelo funcional, configure-o. Não planeje demais.

Armazenamento é volátil — faça backup no GitHub cedo: No Termux/Android, o armazenamento do app (~/sayip/storage/) pode ser apagado na reinicialização do servidor, reinicialização do Termux ou reinicialização do dispositivo. Após configurar chaves de API e configuração, envie o projeto para um repositório GitHub via SSH imediatamente para que a configuração possa ser recuperada. Durante a sessão, não presuma que storage/ sobrevive a uma reinicialização — verifique novamente após qualquer reinicialização. O truque SSH na porta 443 (veja references/termux-git-ssh.md) é frequentemente necessário porque redes móveis bloqueiam a porta 22.

O modo de voz deve ser testado de ponta a ponta antes de declarar concluído: A fiação da Web Speech API em chat.js (setupVoice, startListening, speakText) não é o mesmo que o modo de voz funcionando. Teste falando de fato no microfone do celular e ouvindo a resposta falada de volta. O Chrome no Android é o navegador alvo — é o que importa, não o Chrome no desktop.

Depuração

Porta 5000 já em uso:

fuser -k 5000/tcp  # Mata o que está na porta 5000
# Ou use ss para verificar:
ss -tlnp | grep 5000

Aplicativo Flask não inicia / trava:

  • Verifique a sintaxe: python3 -c "import py_compile; py_compile.compile('app.py', doraise=True)"
  • Verifique se a porta está livre antes de iniciar
  • Execute com timeout 15 python3 app.py para ver a saída de inicialização sem bloquear

API retorna 401/403/payment_required:

  • A chave pode ser inválida, expirada ou o modelo não está no seu plano
  • Teste a chave diretamente com curl antes de configurá-la no aplicativo
  • Verifique /v1/models para ver quais modelos seu plano realmente inclui

Timeouts no pip install:

  • O Termux no celular pode ter rede lenta/não confiável para pacotes grandes
  • Use alternativas da stdlib quando possível (urllib em vez de requests)
  • Se precisar instalar, use pip3 install --timeout 30 <pacote> para falhar rapidamente

Processos Flask em segundo plano mortos à força mantêm a porta ocupada brevemente:

  • Após pkill -9 -f "[p]ython3 app.py", aguarde 2-3s antes de reiniciar
  • Se obtiver "Address already in use", o processo antigo não liberou o socket completamente — aguarde mais

Antes de reiniciar o servidor Flask para aplicar mudanças no código, mate o processo antigo primeiro:

  • Padrão: pkill -9 -f "[p]ython3 app.py" && sleep 3 && cd ~/sayip && python3 app.py &
  • Pular a morte causa "Address already in use" e a nova instância não inicia
  • Sempre verifique com curl -s --max-time 3 http://127.0.0.1:5000/api/health após reiniciar

Quando o usuário relatar que o webapp está quebrado / JS bugado / não funcionando, diagnostique nesta ordem:

  1. Confirme que o servidor está rodando: curl -s --max-time 3 http://127.0.0.1:5000/api/health
  2. Verifique se os arquivos estáticos servem 200: curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/static/style.css (e app.js, chat.js, voice.js, settings.js)
  3. Verifique qual HTML o servidor realmente serve: curl -s http://127.0.0.1:5000/ | grep -E 'static/|style.css|app.js|chat.js|voice.js' — confirma se os caminhos no HTML correspondem ao que o Flask serve
  4. Se arquivos estáticos retornarem 404, mas os links HTML mostrarem caminhos relativos como style.css em vez de /static/style.css, esse é o bug — corrija ambos os templates para usar o prefixo /static/
  5. Leia os arquivos JS e verifique cada chamada document.querySelector / getElementById / .querySelector contra o que o HTML realmente contém — um elemento faltando quebra todo o script
  6. Verifique travamentos por referência nula: el("hamburger") em uma página que não tem elemento hamburger, messagesEl.querySelector(".wrap") quando .wrap não existe, document.querySelector(".composer .wrap") quando .composer não existe — todos quebram a IIFE e derrubam todo o JS da página
  7. Proteja buscas de elementos: var el = document.querySelector(selector); if (!el) return; antes de usar o resultado

Verifique correções no frontend após qualquer edição de template HTML verificando o que o servidor realmente serve:

  • Um curl para a URL da página e grep pelo caminho estático (/static/style.css, /static/js/app.js) confirma se a correção foi implantada
  • Um curl diretamente para a URL do arquivo estático com -o /dev/null -w '%{http_code}' confirma se serve 200
  • Não presuma que uma edição de HTML está ativa até que ambas as verificações passem no servidor em execução

A causa mais comum de "tudo JS bugado" neste projeto:

  • Templates HTML usando caminhos estáticos relativos (style.css, app.js, chat.js) em vez do prefixo /static/ do Flask (/static/style.css, /static/js/app.js, /static/js/chat.js)
  • O navegador solicita http://127.0.0.1:5000/style.css → 404. Sem CSS, sem JS carregado → página sem estilo e não funcional
  • Correção: corrija ambos os templates HTML para prefixar caminhos estáticos com /static/ e, opcionalmente, mova chat.js e voice.js para static/js/ se ainda não estiverem lá

Referências

  • references/nararouter-api.md — Padrões de integração detalhados com NaraRouter
  • references/nvidia-api.md — Padrões NVIDIA NIM (modo thinking do Nemotron, visão, streaming, timeout)
  • references/termux-git-ssh.md — SSH na porta 443 para fazer push para GitHub de rede móvel
  • references/termux-env.md — Configuração do ambiente Termux (Python, pacotes, ferramentas)

Individual skills in this repo

This repo contains 12 individual skills — each has its own dedicated page.

Habilidades Relacionadas