Files
nas-runbooks/common/hermes-nabil-images-flyers-navigateur.md
T

168 lines
9.5 KiB
Markdown

# hermes-nabil (VPS Contabo) — images, flyers et rendu navigateur
> Prototype du futur `hermes-desktop` (Mac). Complete `common/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
```dockerfile
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) :
```bash
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.
```bash
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-tete `User-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](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](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`.
```bash
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/.env` est le seul fichier lu par Hermes (pas le `.env` racine).
- Le wrapper mcp interdit `docker rm -f` : utiliser `docker compose up -d` (recreate propre) et
`docker run --rm` pour 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-tete **`Authorization: Key <FAL_KEY>`**
(pas `Bearer`). 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/edit` attend `image_urls` (**tableau**), `flux-pro/kontext` et `qwen-image-edit`
attendent `image_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](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.