Accueil › Notes de version › Release 2026-06-02 — Audit liens morts backoffice + rate limit LLM + Aïssa support persona
Menu du document← Accueil
Notes de version
  1. Release 2026-09-24 — Salle de marché institutionnelle (Lots 0-8)
  2. Release 2026-06-02 — Audit liens morts backoffice + rate limit LLM + Aïssa support persona
  3. Release 2026-05-26 — Requêteur IA, cession Kankele 3-parties, anonymisation marketplace + analyse IA enrichie
Notes de version

Release 2026-06-02 — Audit liens morts backoffice + rate limit LLM + Aïssa support persona

Status : en production. tauraco.io app SPA déployé, backend redéployé, widget support déployé sur LSWS et bust-cache CF appliqué.

TL;DR

Trois jets sur la même journée :

  1. Audit dead-links backoffice (/app/*) sur 4 profils — 332 nav tests, 6 bugs corrigés, 0 lien mort en fin de journée.
  2. Prompt caching + payload sans état sur les 4 endpoints LLM — coût input divisé par ~5 sur les sessions multi-tours.
  3. Rate limit quotidien Redis sur tous les endpoints LLM (cap même en premium) + redesign complet du widget support tauraco.io en persona « Aïssa » humaine, animée, avec réponses courtes structurées.
AvantAprès
3 pages SUPER_ADMIN crashent en console (51 erreurs cumulées)0 erreur, 0 crash
3 endpoints API morts laissaient des pages avec des consoles errorsCatch 404 propres, empty state propre
1 form après création FX rate restait bloquéRedirige correctement vers la liste

Note rétroactive

La section initiale de cette release notes indiquait « aucun changement fonctionnel sur les assistants aujourd'hui ». Cela a été corrigé en fin de journée par les PR #50, #52, #53 — voir les sections en bas du document. À noter : la persona Aïssa est ce que voient les visiteurs non connectés sur tauraco.io ; le requêteur authentifié (/app/assistant) n'a pas changé d'apparence (uniquement caching + rate limit côté serveur).

Méthode de l'audit

Crawl Playwright qui pour chaque profil :

  1. Set le JWT en localStorage (tauraco:refresh)
  2. Navigue à chaque route nommée du menu via router.push({ name })
  3. Capture l'URL résolue, le texte rendu, console.error, fetch failures
  4. Classe : granted / forbidden / NOT_FOUND / STUCK_LOADING / ERROR_STATE

Profils utilisés :

ProfilUserTenantRôle
INVESTOR[email protected]t_platformINVESTOR
SELLER[email protected]t_demo_001SELLER
ERP_OWNER[email protected]t_erp_demo_001ERP_OWNER
SUPER_ADMIN[email protected]t_demo_001SUPER_ADMIN

Bugs trouvés et corrigés

PR #47 — Premier crawl : 3 bugs critiques sur les pages SUPER_ADMIN

#URLSymptômeCauseFix
1/app/erp/fx-rates/new après CreateBloqué sur le form, pas de retour à la listerouter.push({name:'erp-fx-rates'}) — la vraie route est erp-fx ; le .catch(()=>{}) du dispatcher swallow l'erreurname: 'erp-fx'
2/app/compliance/feesCrash Cannot read properties of undefined (reading 'length')adminPortalService appelait /v3/admin/fees (endpoint summary depuis renommage) ; CRUD vit à /v3/admin/payments/feesswitched + res?.items ?? []
3/app/ops/audit50× t?.some is not a function PrimeVueexpandedRows typé {} mais PrimeVue v4 attend un arrayunknown[] = []

PR #48 — Re-crawl post-fix : 3 endpoints API morts

Le re-crawl a fait remonter des console.error "Failed to load resource 404" même si les pages rendaient leur empty state correctement. Trois endpoints obsolètes :

#Endpoint frontendÉtat backendFix
4/v1/approvals/instances (pipelineService)n'existe pasrenommé en /v1/approvals/requests (le vrai path)
5/v2/admin/contact-requests (contactService)non implémenté.catch(404) → { items: [], total: 0 }
6/v2/admin/support/conversations* (supportChatService)non implémentéhelper _safeEmpty<T>() qui retourne [] sur 404

Les deux endpoints admin (#5, #6) restent à implémenter côté backend si l'on veut un jour lister les contact-requests et l'historique des conversations support. Pas d'urgence : les empty states s'affichent correctement en attendant.

Audit final post-déploiement

ProfilRoutes accordéesForbidden (attendu)Anomalies UIconsole.errorHTTP failures
INVESTOR1271000
SELLER875000
ERP_OWNER3845000
SUPER_ADMIN830002 swallowed

Total : 332 nav tests, 0 lien mort, 0 erreur Vue/Pinia/PrimeVue.

---

PR #50 — Prompt caching Anthropic + payload stateless

Problème : chaque tour du widget support et du requêteur renvoyait tout l'historique de conversation à Anthropic, ce qui faisait grossir le coût input linéairement avec la session.

Fix :

  • cache_control: {type: "ephemeral"} sur les system prompts des 4 endpoints LLM (/v1/support/chat, /v2/ai/chat, /v2/ai/insights, /v2/ai/requeteur). Le prompt ~3 KB est envoyé une fois par fenêtre de 5 min puis servi depuis le cache Anthropic → ~80 % d'économie input sur les sessions multi-tours.
  • Côté client (widget statique + RequeteurChatView.vue), envoi du seul dernier message utilisateur dans messages. Le serveur est désormais stateless par tour, le cache Anthropic remplace l'historique pour la cohérence du système.
  • Tools array du requêteur également marqué cache_control ephemeral.

PR #52 — Daily rate limit Redis sur tous les endpoints LLM + persona Aïssa

Problème : aucun garde-fou empêchait un script ou une fuite de clé de drainer le budget Anthropic via les endpoints LLM. Le requêteur avait paywall premium mais pas de cap par jour. Les 3 autres endpoints n'avaient rien.

Fix backend — app/services/llm_rate_limit.py (nouveau, Redis-backed) :

EndpointCap quotidienIdentité
/v1/support/chat30par IP (header CF-Connecting-IP aware)
/v2/ai/chat100par user
/v2/ai/insights100par user
/v2/ai/requeteur200par user (premium)
  • Caps configurables via settings.LLM_RATE_LIMIT_* — pas besoin de redéploiement pour les ajuster.
  • Cap appliqué même en premium : explicitement demandé par sega pour borner la dépense Anthropic si une clé fuite.
  • Compteur INCR + EXPIRE sur clé llm:rl:<endpoint>:{user|ip}:<YYYY-MM-DD> qui survit 26 h pour absorber le rollover UTC.
  • HTTP 429 avec headers Retry-After, X-RateLimit-Limit, X-RateLimit-Used.
  • Fail-open : si Redis tombe, on ne bloque pas les vrais users (logué, mais pas de 503 ni de 429).

Fix support_chat.py — persona « Aïssa » :

  • System prompt entièrement réécrit : nom Aïssa, ton conseillère, ≤ 60 mots, structure imposée « 1 phrase d'accroche → 2-4 puces → 1 CTA », tutoie/vouvoie selon le visiteur, FR/EN selon la langue détectée, JAMAIS inventer une feature absente de la liste produit.
  • max_tokens baissé de 2048 → 400.

Fix frontend — frontend/js/support-chat.js (redesign complet, ~14 pages publiques chargent le widget) :

  • FAB « Aï » initiales en gradient vert (plus de 💬), ring « breathing », point en ligne toujours visible.
  • Window slide-in avec overshoot doux ; chaque bulle de message fade-in.
  • Greeting + 3 quick replies (Voir les offres / Je ne peux pas me connecter / Demander une démo) après 700 ms de typing indicator au premier ouvrage.
  • Markdown-lite étendu : puces -, auto-lien [email protected].
  • Nudge bubble « Une question ? » 6 s après chargement, scoped session.
  • HTTP 429 surfacé proprement : message poli + input verrouillé, pas de retry silencieux.
  • Stateless : un seul message dans le payload.

PR #53 — Bump cache-buster v=14.0.0 → v=14.0.1

Problème : Cloudflare avait mis en cache l'URL ?v=14.0.0 avec le body pre-deploy (le docroot LSWS n'avait pas encore été synchronisé au premier hit). CF servait ensuite le body obsolète comme immutable pendant 7 jours → aucun visiteur ne voyait Aïssa.

Fix : bump du buster pour contourner l'entrée CF morte. Pas de token CF purge nécessaire.

Smoke test prod (Playwright sur tauraco.io)

VérifRésultat
FAB rendu « Aï » 64 px (EN + FR)✅
Window s'ouvre avec typing + greeting✅
Quick replies cliquables, round-trip LLM✅
Réponse EN — What are your plans? → 1 hook + 3 bullets + CTA, ~74 mots✅
Réponse FR — Je n'arrive pas à me connecter → 3 étapes (reset / email invit / sega) en tutoiement✅
Payload réseau : {messages:[{role:"user",content:"…"}]} seul (stateless)✅
429 forcé EN → « Daily request limit reached… » + input lock✅
429 forcé FR → « Trop de requêtes pour aujourd'hui… » + input lock✅

PRs du jour

PRTitreInsertionsDeletions
#47fix(backoffice): repair 3 dead/broken pages128
#48fix(backoffice): graceful 404 on 3 dead API endpoints317
#49docs(release): 2026-06-02 backoffice dead-link audit notes——
#50perf(ai): Anthropic prompt caching + stateless per-turn payload62436
#52feat(llm + widget): daily rate limit + Aïssa support persona516195
#53chore(widget): bump cache buster to v=14.0.1 (CF stale fix)1414
Total~1 200~260

Suite (deferred — pas urgents)

  • Backend : implémenter /v2/admin/contact-requests (listing) et /v2/admin/support/conversations* si on veut surfacer ces données dans l'admin. Pour l'instant les pages restent fonctionnelles via empty state.
  • Frontend : retirer le silent-catch router.push().catch(() => {}) dans AppSidebar.vue:80 — il a masqué le bug erp-fx-rates pendant longtemps. Logger les nav failures en console pour faciliter le debug futur.
  • Observabilité rate limit LLM : surfacer X-RateLimit-Used / Limit dans l'UI requêteur pour que l'utilisateur premium voie sa consommation quotidienne avant de toucher le 429.
  • Tuning caps : reconsidérer les valeurs après quelques jours d'observation (Redis SCAN llm:rl:* pour stats). 30/IP sur /v1/support/chat peut être bas si plusieurs collègues d'une même entreprise testent depuis le même WAN.