Communitygithub.com

V4TIME/V4SKILL

Construisez des applications Flask IA dans Termux avec intégration NaraRouter.

Qu'est-ce que V4SKILL ?

Cette compétence vous permet de construire et d'exécuter une application web IA locale directement sur votre appareil Android via Termux. Elle intègre notamment le routeur d'API unifié NaraRouter, permettant d'accéder à plus de 53 modèles d'IA, ainsi que l'agent Hermes comme backend de raisonnement optionnel. La compétence couvre l'architecture complète : un backend Flask servant d'interface, le routage dynamique basé sur la description des tâches, la gestion des sessions et de la mémoire, et l'ajout de mode vocal via l'API Web Speech du navigateur. Elle est particulièrement utile pour les développeurs souhaitant créer un assistant IA local, une interface de chat, ou un outil de développement avec multiples API, le tout fonctionnant sur un réseau mobile. Des conseils pratiques y sont inclus, comme l'utilisation de la bibliothèque standard `urllib` au lieu de `requests` pour éviter les timeouts réseau, et des designs minimaux et professionnels sans fioritures esthétiques.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Application Web IA Locale dans Termux

Construisez et exécutez une application web locale sur un appareil Android fonctionnant sous Termux, en l'intégrant aux API IA (particulièrement NaraRouter) et éventuellement à l'agent Hermes comme backend de raisonnement.

Quand l'utiliser

  • L'utilisateur souhaite une interface de chat/assistant IA locale sur son téléphone
  • Construction d'une application web dans Termux avec Python/Flask
  • Intégration de multiples API IA avec routage basé sur la description
  • Ajout du mode vocal via l'API Web Speech du navigateur
  • Gestion des sessions et de la mémoire entre les conversations
  • L'utilisateur colle des instructions d'installation pour un outil de développement (binaire, Rust/cargo, npm/npx, pip) et demande de l'installer et de l'utiliser — évaluez d'abord la faisabilité dans l'environnement téléphone/Termux, puis installez avec autorisation

Schéma d'architecture

Navigateur (Chrome sur Android) → Backend Flask (Termux) → API IA
                                 ↓
                             Agent Hermes (optionnel, comme cerveau)

Principe clé : L'application web est la couche d'interface. L'agent Hermes (s'il est utilisé) est le cerveau. Le backend Flask fait le pont entre eux.

Démarrage rapide

# Dans Termux
mkdir ~/sayip && cd ~/sayip
mkdir -p templates static/css static/js storage/sessions
# app.py — démarrage minimal Flask
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()
    # ... gérer le message, appeler l'API, retourner la réponse

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000)
# Exécuter
python3 app.py
# Ouvrir dans Chrome : http://127.0.0.1:5000

Modèles d'intégration API

NaraRouter (recommandation principale)

NaraRouter est une passerelle IA unifiée avec plus de 53 modèles, avec un niveau gratuit disponible.

Format de clé : sk-nry-... (commence par sk-nry-)

Points de terminaison (tous sous la même URL de base + clé Bearer) :

Point de terminaisonTypeUtilisation
https://router.bynara.id/v1/chat/completionsCompatible OpenAIChat, tâches générales
https://router.bynara.id/v1/messagesCompatible AnthropicRequêtes au format Anthropic
https://router.bynara.id/v1/responsesOpenAI ResponsesAPI de réponses avec état
https://router.bynara.id/v1/embeddingsOpenAI EmbeddingsPlongements vectoriels
https://router.bynara.id/v1/modelsLISTEObtenir les modèles disponibles pour votre forfait
https://api-images.bynara.id/v1/images/generationsGénération d'imagesTexte vers image
https://api-images.bynara.id/v1/images/editsÉdition d'imageÉdition d'image

Tester une clé avant de l'utiliser :

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

Modèles gratuits connus fonctionnels : agnes-2.5-flash, agnes-2.0-flash

Vérifier les modèles de votre forfait :

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

IMPORTANT : Tous les 53 modèles ne sont pas sur tous les forfaits. deepseek-v4-pro-0813-bynara nécessite des crédits. deepseek-v4-flash-free peut ne pas être inclus. Testez toujours avant de configurer.

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

NVIDIA NIM fournit plus de 82 modèles via un seul point de terminaison compatible OpenAI.\nURL de base : https://integrate.api.nvidia.com/v1\nPoint de terminaison : https://integrate.api.nvidia.com/v1/chat/completions\nFormat de clé : nvapi- (par ex. nvapi-_qJeGmEP...)\n\nTester une clé avant de l'utiliser :\n\nbash\ncurl -s -X POST https://integrate.api.nvidia.com/v1/chat/completions \\\n -H "Authorization: Bearer nvapi-..." \\\n -H "Content-Type: application/json" \\\n -H "Accept: text/event-stream" \\\n -d '{"model":"nvidia/nemotron-3.5-lightning-30b-a3b","messages":[{"role":"user","content":"salut"}],"max_tokens":10,"stream":true}'\n\n\nModèles gratuits connus fonctionnels (testés sur le réseau mobile Termux) :\n\n| ID du modèle | Clé | Statut | Remarques |\n|----------|-----|--------|-------|\n| nvidia/nemotron-3.5-lightning-30b-a3b | nvapi-... | Fonctionne | Rapide (~5-7s pour simple), raisonnement solide, prend en charge le mode réflexion. Le plus fiable sur mobile. |\n| gpt-oss-20b | nvapi-... | Fonctionne | Compatible OpenAI |\n| deepseek-ai/deepseek-v4-pro-0813 | nvapi-... | Risque de timeout | Fonctionne sur ordinateur de bureau, se coupe fréquemment sur le réseau mobile Termux |\n| moonshotai/kimi-k3 | nvapi-... | Risque de timeout | Capacité de vision, mais se coupe sur le réseau mobile |\n\nAvertissement d'asymétrie de clé : Tous les modèles ne sont pas disponibles sur toutes les clés. La clé 1 (nvapi-_qJeGmEP...) a un accès plus large. La clé 2 (nvapi-WDb7Rr...) est plus restreinte — certains modèles renvoient 404. Testez toujours un modèle avec une clé spécifique avant de configurer.\n\nIndicateurs de clé morte :\n- 401 Unauthorized — clé invalide/expirée\n- 404 Not Found sur un modèle — modèle non disponible sur ce compte/clé\n- Timeout de connexion (30s+) — API lente/inaccessible depuis ce réseau\n- Clés Teamorouter sk-teamo-... → insufficient_balance sur le point de terminaison OpenAI (https://api.teamorouter.com/v1) ; modèle non pris en charge sur le point de terminaison Anthropic (https://api.teamorouter.com). Mort. Fournit le modèle deepseek-v4-pro-free mais inutilisable sans solde.\n\nVoir references/nvidia-api.md pour les détails complets : motifs Python spécifiques aux modèles (mode réflexion Nemotron, DeepSeek non streamé, multimodal vision Kimi), extraction du reasoning_content en streaming, motif de timeout dur basé sur les threads pour urllib, et format de charge utile pour la vision.

Moteur de répartition API

Lorsque vous avez plusieurs API configurées, construisez un répartiteur qui :

  1. Classifie la tâche (mathématiques, codage, science, général)
  2. Choisit la meilleure API en fonction du texte de description
  3. L'appelle, retourne le résultat
  4. Replie sur d'autres API si la première échoue

Voir references/nararouter-api.md pour les détails d'intégration.

Utiliser urllib de la bibliothèque standard, pas requests

Dans Termux, pip install requests peut expirer lors de gros téléchargements. Utilisez plutôt urllib de la bibliothèque standard Python — il est toujours disponible et gère bien les API 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())

Préférences de conception (spécifiques à l'utilisateur)

Look minimal, professionnel, non généré par IA :

  • Thème sombre, espacement serré, pas de dégradés, pas d'ombres décoratives
  • Polices monospace là où approprié (code, clés API, horodatages)
  • Rectangles simples pour les bulles de chat — pas de coins arrondis sauf fonctionnels
  • Barre latérale = liste simple, pas d'icônes sauf si significatives
  • Rechercher des implémentations de référence plutôt que de générer des designs à partir de zéro
  • En cas de doute, supprimer des fonctionnalités plutôt que d'en ajouter

Anti-patrons concrets — ces éléments donnent un aspect généré par IA à une application web, supprimez-les :

  • Émojis dans chaque en-tête/tuile/bouton (🤖, ⚙, 🎭, 🔌, 🧠, 💬, 🎤) — supprimez. Uniquement des étiquettes en texte brut.
  • Lueur d'accentuation / ombre de boîte au survol (box-shadow: 0 0 12px var(--accent-glow)) — supprimez. Uniquement des couleurs plates.
  • Animations de rebond / pulsation / glissement vers le bas (@keyframes bounce, @keyframes pulse, @keyframes slideDown) — supprimez. Pas de conception animée.
  • Carte de bienvenue avec écran de démarrage et icônes (.welcome-screen avec welcome-icon, welcome-hints grille) — remplacez par un espace réservé de saisie d'une ligne dans la zone d'entrée.
  • Icône de marque décorative à côté du logo (.brand-icon avec emoji) — supprimez.
  • Rubans, badges, points de statut décorant l'en-tête (.health-badge, .health-dot) — supprimez ou rendez purement fonctionnels.
  • Arrière-plans dégradés, vitromorphisme, superpositions translucides — jamais.

Règle d'or : ouvrez la page dans un navigateur et demandez-vous "un développeur humain écrirait-il ce CSS à la main pour un outil utilitaire ?" Si la réponse est non (parce qu'il y a du mouvement, de la lueur ou des décorations par émojis), supprimez. Un outil normal ressemble à un terminal, une application de notes ou un chat minimal — pas à une page d'accueil.

Mode vocal :

  • Utiliser l'API Web Speech (SpeechRecognition + SpeechSynthesis) — gratuit, intégré au navigateur
  • Bouton de bascule pour passer en mode texte/voix
  • En mode vocal : le micro écoute en continu, parole→texte→envoi, réponse→oral
  • La qualité vocale est limitée à ce que Chrome sur Android fournit — ne promettez pas "voix Ultron"

Préférences de flux de travail (spécifiques à l'utilisateur)

Demander avant d'installer des paquets : Avant d'exécuter pip install ou apt install, demandez d'abord à l'utilisateur. Ce n'est pas négociable.

Afficher la progression en continu : Si une tâche prend plus de quelques minutes, montrez les résultats intermédiaires. Ne disparaissez pas pendant de longues périodes sans sortie. Si vous êtes bloqué sur quelque chose, dites-le et ce que vous essayez.

Pas de leçons de sécurité : L'utilisateur comprend les risques liés aux clés API. Ne faites pas la leçon sur la sécurité des clés, l'exposition ou les meilleures pratiques, sauf demande explicite. Ils ont fourni leur propre explication le cas échéant.

Préférer des solutions fonctionnelles à une architecture parfaite : Obtenez d'abord quelque chose en fonctionnement. Le test de la clé NaraRouter l'a montré — testez la clé, trouvez un modèle fonctionnel, configurez-le. Ne planifiez pas trop.

Le stockage est volatil — sauvegardez sur GitHub tôt : Sur Termux/Android, le stockage d'application (~/sayip/storage/) peut être effacé lors d'un redémarrage du serveur, d'un redémarrage de Termux ou d'un redémarrage de l'appareil. Après avoir configuré les clés API et la configuration, poussez le projet vers un dépôt GitHub via SSH immédiatement afin que la configuration puisse être récupérée. En session, ne supposez pas que storage/ survit à un redémarrage — revérifiez-le après tout redémarrage. L'astuce SSH sur le port 443 (voir references/termux-git-ssh.md) est souvent nécessaire car les réseaux mobiles bloquent le port 22.

Le mode vocal doit être testé de bout en bout avant de le déclarer terminé : Le câblage de l'API Web Speech dans chat.js (setupVoice, startListening, speakText) n'est pas la même chose que le mode vocal fonctionnel. Testez en parlant réellement dans le micro du téléphone et en entendant la réponse orale. Chrome sur Android est le navigateur cible — c'est ce qui compte, pas le Chrome de bureau.

Débogage

Port 5000 déjà utilisé :

fuser -k 5000/tcp  # Tuer ce qui est sur le port 5000
# Ou utiliser ss pour vérifier :
ss -tlnp | grep 5000

L'application Flask ne démarre pas / plante :

  • Vérifier la syntaxe : python3 -c "import py_compile; py_compile.compile('app.py', doraise=True)"
  • Vérifier si le port est libre avant de démarrer
  • Exécuter avec timeout 15 python3 app.py pour voir la sortie de démarrage sans bloquer

L'API renvoie 401/403/payment_required :

  • La clé peut être invalide, expirée ou le modèle n'est pas sur votre forfait
  • Tester la clé directement avec curl avant de la configurer dans l'application
  • Vérifier /v1/models pour voir quels modèles votre forfait inclut réellement

Timeouts pip install :

  • Termux sur mobile peut avoir un réseau lent/peu fiable pour les gros paquets
  • Utiliser des alternatives de la bibliothèque standard lorsque possible (urllib au lieu de requests)
  • Si vous devez installer, utilisez pip3 install --timeout 30 <package> pour échouer rapidement

Les processus Flask en arrière-plan tués de force laissent le port occupé brièvement :

  • Après pkill -9 -f "[p]ython3 app.py", attendez 2-3s avant de redémarrer.
  • Si vous obtenez "Address already in use", l'ancien processus n'a pas encore libéré le socket — attendez plus longtemps.

Avant de redémarrer le serveur Flask pour prendre en compte les modifications de code, tuez d'abord l'ancien processus :

  • Motif : pkill -9 -f "[p]ython3 app.py" && sleep 3 && cd ~/sayip && python3 app.py &
  • Sauter la tuerie provoque "Address already in use" et la nouvelle instance ne démarrera pas.
  • Toujours vérifier avec curl -s --max-time 3 http://127.0.0.1:5000/api/health après le redémarrage.

Lorsque l'utilisateur signale que l'application web est cassée / JS bugué / ne fonctionne pas, diagnostiquez dans cet ordre :

  1. Confirmer que le serveur fonctionne : curl -s --max-time 3 http://127.0.0.1:5000/api/health
  2. Vérifier que les fichiers statiques renvoient 200 : curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/static/style.css (et app.js, chat.js, voice.js, settings.js)
  3. Vérifier quel HTML le serveur sert réellement : curl -s http://127.0.0.1:5000/ | grep -E 'static/|style.css|app.js|chat.js|voice.js' — confirme que les chemins dans le HTML correspondent à ce que Flask sert
  4. Si les fichiers statiques renvoient 404 mais que les liens HTML montrent des chemins relatifs comme style.css au lieu de /static/style.css, c'est le bug — corrigez les deux templates pour utiliser le préfixe /static/
  5. Lire les fichiers JS et vérifier chaque appel document.querySelector / getElementById / .querySelector par rapport à ce que le HTML contient réellement — un élément manquant plante tout le script
  6. Vérifier les plantages par référence nulle : el("hamburger") sur une page qui n'a pas d'élément hamburger, messagesEl.querySelector(".wrap") quand .wrap n'existe pas, document.querySelector(".composer .wrap") quand .composer n'existe pas — tous plantent l'IIFE et désactivent tout le JS de la page
  7. Protéger les recherches d'éléments : var el = document.querySelector(selector); if (!el) return; avant d'utiliser le résultat

Vérifier les correctifs frontend après toute modification HTML/template en vérifiant ce que le serveur sert réellement :

  • Un curl vers l'URL de la page et un grep pour le chemin statique (/static/style.css, /static/js/app.js) confirme que le correctif est arrivé.
  • Un curl directement vers l'URL du fichier statique avec -o /dev/null -w '%{http_code}' confirme qu'il renvoie 200.
  • Ne supposez pas qu'une modification HTML est en ligne tant que les deux vérifications ne passent pas sur le serveur en cours d'exécution.

La cause la plus courante de "tout JS est bugué" sur ce projet :

  • Les templates HTML utilisent des chemins statiques relatifs (style.css, app.js, chat.js) au lieu du préfixe /static/ de Flask (/static/style.css, /static/js/app.js, /static/js/chat.js).
  • Le navigateur demande http://127.0.0.1:5000/style.css → 404. Pas de CSS, pas de JS chargé → page sans style et non fonctionnelle.
  • Correctif : corriger les deux templates

…

[Le contenu a été tronqué pour la traduction.]

Individual skills in this repo

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

Skills associés