9.5 KiB
hermes-nabil (VPS Contabo) — images, flyers et rendu navigateur
Prototype du futur
hermes-desktop(Mac). Completecommon/hermes-nabil-vps-deploiement.md.
1. Toolset navigateur : hermes postinstall doit etre BAKE dans l'image
hermes postinstall installe Node, ffmpeg, ripgrep, agent-browser + Chromium
(/root/.agent-browser/browsers/chrome-<version>/chrome, ~390 Mo). Tout cela vit dans
le filesystem du conteneur : un docker compose up -d qui recree le conteneur le perd.
Piege vecu
Ajouter simplement RUN hermes postinstall a un FROM python:3.11-slim echoue en silence
(build exit 0, image sans navigateur) :
- listes apt vides ->
Unable to locate package ripgrep / ffmpeg Node.js not found — Could not find Node.js 22 binary for linux-x64
Dockerfile correct
FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates xz-utils nodejs npm ffmpeg ripgrep \
fonts-dejavu-core fonts-liberation \
libnss3 libnspr4 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
libgbm1 libpango-1.0-0 libcairo2 libasound2 libatspi2.0-0 libx11-6 \
libxcb1 libxext6 libexpat1 libuuid1 \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir hermes-agent \
python-docx python-pptx openpyxl XlsxWriter reportlab pypdf pillow markdown requests
ENV HERMES_HOME=/data
WORKDIR /data
RUN hermes postinstall && rm -rf /var/lib/apt/lists/* /tmp/*
CMD ["hermes","gateway","run"]
Verification apres build (sans toucher au conteneur en service) :
docker run --rm --entrypoint bash hermes-nabil:latest -lc \
'which node ffmpeg rg; ls -d /root/.agent-browser/browsers/chrome-*'
Image finale ~666 Mo. Build ~4 min -> toujours en detache (nohup docker build ... > log 2>&1 &),
le connecteur mcp-vps tombe sur les operations longues.
2. Rendu HTML/CSS -> PNG / PDF
Pas besoin de Playwright : Chrome headless en CLI suffit et est plus robuste.
CH=$(ls -d /root/.agent-browser/browsers/chrome-*/chrome | tail -1)
$CH --headless --disable-gpu --no-sandbox --hide-scrollbars \
--window-size=1080,1350 --screenshot=/data/out.png file:///data/in.html
$CH --headless --disable-gpu --no-sandbox --no-pdf-header-footer \
--print-to-pdf=/data/out.pdf file:///data/in.html
Les ERROR:dbus/bus.cc sont cosmetiques (pas de bus D-Bus en conteneur), le fichier est bien ecrit.
--no-sandbox obligatoire en conteneur. @page{size:WxH;margin:0} en CSS pour un PDF au format exact.
3. Generation d'images IA — ni OpenCode, ni Gemini free tier
OpenCode : aucune capacite image (teste 18/07/2026)
/images/generations: 404, l'endpoint n'existe pas.mimo-v2-omni: 400 "Upstream request failed" meme sur un prompt texte trivial -> modele mort cote provider.mimo-v2.5(200) invente un lien imgur bidon quand on lui demande une image./models: 403 Cloudflare 1010 sans en-teteUser-Agent.
Gemini direct (Google AI Studio) : quota 0 sur le free tier
Cle valide (50 modeles listes), mais tous les modeles image renvoient 429 limit: 0 :
gemini-3-pro-image, gemini-3.1-flash-image, gemini-2.5-flash-image et variantes.
Les imagen-4.0-* renvoient 404 "no longer available to new users".
=> La generation d'images Google est reservee au tier payant (facturation activee sur le projet).
Ce n'est pas un probleme de cle : la meme cle liste les modeles et fonctionne en texte.
Backend retenu : OpenRouter -> google/gemini-2.5-flash-image
Meme modele (nano-banana), accessible immediatement, cout une fraction de centime par image.
Voir openrouter-image-gen.md pour les pieges (modalities, message.images[]).
Cle OPENROUTER_API_KEY dans data/.env, reprise de HERMES_PERSO_OPENROUTER_KEY
(hermes-platform/.env du NAS) et transferee sans jamais l'exposer :
voir transfert-secret-inter-machines.md.
/data/nyora/nyora_image.py implemente la cascade OpenRouter -> Gemini direct :
Gemini reste cable et prendra le relais sans modification de code le jour ou la facturation
Google sera activee. Le backend OpenRouter accepte en plus une image source (edition :
fond neutre, retouche), ce que la voie Gemini directe ne fait pas ici.
4. Pipeline flyer Nyora
/data/nyora/nyora_flyer.py : image IA -> HTML/CSS charte Or Bruni
(navy #1A1A2E, cream #F5F5F0, gold #B8862A) -> PNG + PDF -> envoi Telegram (multipart urllib, zero dependance).
Skill agent : /data/skills/nyora-flyer/SKILL.md.
python3 /data/nyora/nyora_flyer.py '{"nom":"x","titre":"Titre <em>or</em>","sous_titre":"NYORA",
"corps":["ligne 1","ligne 2"],"cta":"Contact","prompt_image":"... sans texte","telegram":true}'
Regle : le prompt image precise sans texte — le texte est pose en HTML (net, orthographe maitrisee). Image encodee en base64 inline dans le HTML : un seul fichier, pas de probleme de chemin cote Chrome.
5. Rappels
- Verifier une cle sans l'afficher :
set -a; . /data/.env; set +a; eval v=\${VAR}; echo ${#v}. Attention :${#VAR}sur le nom renvoie la longueur du nom, pas de la valeur. data/.envest le seul fichier lu par Hermes (pas le.envracine).- Le wrapper mcp interdit
docker rm -f: utiliserdocker compose up -d(recreate propre) etdocker run --rmpour les tests jetables.
6. Edition d'image (Fal.ai + repli OpenRouter)
/data/nyora/nyora_image_edit.py. Cascade Fal.ai -> OpenRouter nano-banana.
Fal.ai
- Endpoint synchrone :
POST https://fal.run/<modele>, en-teteAuthorization: Key <FAL_KEY>(pasBearer). Reponse :images[0].url(URL hebergee, a telecharger). - L'image source passe en data-URI base64, pas besoin d'heberger le fichier.
- Piege de schema : le nom du champ source change selon le modele —
nano-banana/editattendimage_urls(tableau),flux-pro/kontextetqwen-image-editattendentimage_url(chaine). Un mauvais champ = 422. - Modeles :
fal-ai/nano-banana/edit(respect fidele du sujet, fond/retouche),fal-ai/flux-pro/kontext(edition dirigee),fal-ai/qwen-image-edit(texte et details).
Etat du compte (18/07/2026) — Fal est PAYANT, pas de quota gratuit
Cle valide et authentifiee, mais les trois modeles renvoient
User is locked. Reason: Exhausted balance.
Fal.ai n'a aucun quota gratuit recurrent : seulement des credits de bienvenue one-shot,
consommes une fois pour toutes. Toute utilisation ulterieure est facturee a l'usage.
Ce n'est ni un probleme de cle ni de code.
DECISION (18/07/2026) : ne pas recharger pour l'instant. OpenRouter couvre deja generation ET edition avec le meme nano-banana pour une fraction de centime par image, sans nouvel abonnement. Fal reste cable mais dormant : son interet est la largeur du catalogue (flux kontext, qwen-edit, upscalers, modeles video Kling/Sora, entrainement LoRA), utile au moment de la production video Dr Nexum sur le MacBook (septembre 2026) — c'est la le bon moment pour crediter le compte, pas avant. Le code bascule seul des que le solde existe.
Lecon : un repli silencieux masque un probleme de facturation
La cascade avait fait son travail (les editions ont abouti via OpenRouter) mais sans rien signaler :
les trois tests semblaient reussis alors que Fal etait mort. Correction appliquee — quand un repli
se declenche, la raison de l'echec du backend principal est ecrite sur stderr
(AVERTISSEMENT repli sur OpenRouter — Fal indisponible : ...).
Regle generale : toute cascade de backends doit journaliser pourquoi elle est descendue d'un cran.
Consignes d'edition
Toujours formuler en conservant explicitement le sujet ("garde X exactement identique"), sinon le modele reinvente l'image. Pour un PNG reellement transparent : sujet sur fond blanc plat puis detourage flood-fill PIL depuis les bords (cf openrouter-image-gen.md).
7. Routage image OpenRouter : le moins cher qui couvre le besoin
/data/nyora/nyora_models.py. Le catalogue OpenRouter est interroge (cache 24 h),
filtre sur les modeles a sortie image, puis classe par cout estime par image.
Piege de comparaison : comparer les prix au token entre familles est faux —
une image consomme ~1290 tokens de sortie chez Gemini contre ~1568 chez OpenAI.
Sans cette normalisation, gpt-5-image-mini paraissait moins cher que
gemini-2.5-flash-image alors qu'il est plus cher a l'image.
Piege de sous-chaine : "gpt-5-image" in "gpt-5-image-mini" est vrai — le mini
etait classe premium par erreur. Les modeles brides (lite, -mini) sont exclus explicitement.
| besoin | usage | modele (18/07/2026) | cout/image | latence mesuree |
|---|---|---|---|---|
brouillon |
iteration, essai jetable | google/gemini-3.1-flash-lite-image |
~0,0019 $ | 3 s |
flyer |
visuel de production, edition | google/gemini-2.5-flash-image |
~0,0032 $ | 6 s |
premium |
texte DANS l'image, logo, affiche | google/gemini-3-pro-image |
~0,0155 $ | 26 s |
Regle economique : premium seulement si l'image doit contenir du texte lisible.
Dans un flyer Nyora le texte est pose en HTML (net, orthographie, corrigeable) —
donc flyer suffit et coute 5 fois moins cher pour un rendu final superieur.
Aucun modele n'est code en dur : une baisse de prix ou un nouveau modele chez OpenRouter
est pris en compte automatiquement (--refresh pour forcer). En cas d'echec du moins cher,
les suivants sont essayes par cout croissant, et l'ecart est journalise sur stderr.