Files
office_translator/_bmad-output/implementation-artifacts/spec-strategie-llm-abonnements-admin.md
sepehr 6c26712687
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m51s
feat(abonnements): paliers LLM Essentielle/Premium, admin modeles par forfait, statistiques revenus, emails marketing
- Gamme officielle : Essentielle (Pro) = deepseek-v4-flash, glm-5.3-flash,
  minimax-m3 ; Premium (Business) = claude-sonnet-5, deepseek-v4-pro, glm-5.3
- Routage reel : le modele employe suit le palier IA du forfait (reglages
  admin > defaut du plan), garde anti-croisement de palier
- Nouvelle page admin « Modeles & abonnements » (matrice forfaits/paliers,
  modele par defaut, catalogue OpenRouter)
- Statistiques enrichies : revenus (30 j + total), MRR estime, credits,
  liste d'attente, paliers utilises
- Nouvelle page admin « Marketing » : audiences avec compteurs, apercu,
  envoi test obligatoire, historique, desabonnement public + en-tetes
  List-Unsubscribe ; .gitignore pour les donnees d'execution
- Interface (accueil + tarifs) alignee sur la gamme, 13 langues completes
- Tests : routage par palier (fonction + tache), marketing, revenus en base
2026-09-05 12:51:49 +02:00

13 KiB
Raw Blame History

title, type, created, status, baseline_commit, review_loop_iteration, context
title type created status baseline_commit review_loop_iteration context
Stratégie LLM par abonnement + admin (modèles, statistiques, emails marketing) feature 2026-09-05 done 4e6850d6b1 0

Intent

Problem : Les modèles IA affichés sur la page d'accueil et la page tarifs ne correspondent plus à la gamme réelle (Claude Sonnet 4.6, « Claude Haiku », Gemini) ; aucun modèle n'est réellement routé par abonnement (le modèle employé vient des réglages fournisseur) ; l'admin ne peut ni gérer les modèles par type d'abonnement, ni voir de statistiques de revenus, ni envoyer d'emails de relance marketing.

Approach : (1) Nouvelle gamme officielle — IA Essentielle (plan Pro) : deepseek/deepseek-v4-flash, z-ai/glm-5.3-flash, minimax/minimax-m3 ; IA Premium (plan Business) : anthropic/claude-sonnet-5 (remplace le 4.6, $2/$10 /1M), alternatives deepseek/deepseek-v4-pro et z-ai/glm-5.3. (2) Routage réel : le modèle utilisé par traduction est résolu par le palier IA du plan (réglages admin > défauts des plans). (3) Page admin dédiée « Modèles & abonnements » (matrice plan → palier → modèles actifs + modèle par défaut). (4) Statistiques enrichies : revenus (encaissé, MRR estimé, crédits), conversion, liste d'attente. (5) Page admin « Marketing » : envoi d'emails de relance par audience (liste d'attente, inactifs, par plan) avec aperçu, envoi test, historique et désabonnement — bonnes pratiques type Brevo/Mailchimp.

Boundaries & Constraints

Always : Prix/textes en français correct dans l'interface ; les noms de modèles restent en latin tel quel dans les 13 langues ; ne jamais exposer de clé API dans les réponses admin ; un utilisateur Pro ne doit jamais déclencher un modèle Premium ; SMTP existant (services/email_service.py) réutilisé, pas de nouveau fournisseur.

Ask First : Changer les prix des plans (19 €/49 €) ; créer une table de BDD nouvelle (préférer les fichiers JSON du dossier data/ comme le font waitlist et réglages) ; toucher à la chaîne de secours (FALLBACK_CHAIN).

Never : Modifier le dossier office-translator-landing-page/ (prototype hors production) ni .claude//.agent/ ; supprimer les anciennes clés ai_model_essential/ai_model_premium utilisées ailleurs ; Stripe en mode live ; envoyer un email en masse sans passer par l'envoi test ; citer DeepL comme moteur.

I/O & Edge-Case Matrix

Scenario Input / State Expected Output / Behavior Error Handling
Traduction Pro, moteur openrouter plan=pro, réglages admin essential.default=z-ai/glm-5.3-flash Job exécuté avec ce modèle, facteur coût 1 Si vide → défaut du plan (deepseek/deepseek-v4-flash)
Traduction Business, premium plan=business, openrouter_premium Modèle = premium.default (anthropic/claude-sonnet-5) Si clé absente → échec gracieux existant
Envoi massif partiel 200 destinataires, 3 échecs SMTP Réponse : envoyés/échoués détaillés ; historique journalisé Continuer après échec individuel, jamais d'HTTP 500
Désabonné email présent dans data/unsubscribes.json Exclu de tout envoi d'audience N/A

Code Map

  • models/subscription.py -- PLANS (l.38-187) : clés ai_model_essential/ai_model_premium à remplacer par listes ai_models_essential/ai_models_premium (PRO l.100, BUSINESS l.130-131) ; commentaire d'en-tête l.34-37.
  • routes/translate_routes.py -- résolution modèle l.1277-1280 (openrouter, défaut google/gemini-3.5-flash + remap ancien IDs) et l.1364-1375 (openrouter_premium, défaut anthropic/claude-sonnet-4.6) ; _compute_cost_factor l.562 ; _allowed_providers_for_plan l.179. C'est ici que le routage par palier s'applique (via load_settings déjà importé l.1269).
  • routes/admin_routes.py -- SettingsConfig l.914-928 (ajouter section ai_tiers) ; GET/PUT /settings l.956-1054 ; GET /stats l.562 (usage seul, à enrichir) ; catalogue modèles OpenRouter GET /providers/openrouter/models existant (réutiliser pour la combobox admin) ; POST /providers/smtp/test-send l.1332 (exemple d'envoi).
  • services/email_service.py -- send_email_async(to, subject, body) l.94 ; is_smtp_configured() l.53. Réutiliser tel quel.
  • routes/waitlist_routes.py -- data/waitlist.json (email, interest, created_at) : audience marketing n°1, motif de fichier JSON à copier (verrou _write_lock) pour historique d'envois et désabonnements.
  • database/models.py -- PaymentHistory l.293 (amount_cents, payment_type, status, created_at) et Translation l.149 (created_at, provider) : sources des stats de revenus ; repositories.py a les accesseurs.
  • services/pricing_config.py -- get_effective_monthly_yearly pour le MRR estimé (prix effectifs, overrides inclus).
  • routes/auth_routes.py l.763-811 -- GET /plans : exposer les listes de modèles (ai_models_essential/premium) au frontend.
  • frontend/src/components/landing/landing-page.tsx l.440-582 -- PricingSection : textes via i18n uniquement.
  • frontend/src/app/pricing/page.tsx -- STATIC_PLANS l.62-198 (business.feat3 « Claude Haiku », enterprise.feat2 « Claude Opus 4.6 ») ; section aiModels textée via i18n.
  • frontend/src/lib/i18n/messages/<lang>/{landing,pricing,admin}.json (13 langues : fr,en,de,es,it,pt,nl,ar,fa,ja,ko,ru,zh) -- clés à mettre à jour ; structure identique entre langues.
  • frontend/src/app/admin/ -- constants.ts (nav, ajouter 2 entrées), settings/page.tsx (motif de page avec combobox modèles), stats/page.tsx + useTranslationStats.ts (motif graphiques), types.ts. Nouvelles pages : models/page.tsx, marketing/page.tsx.
  • tests/ -- pytest, motif test_admin_*.py.

Tasks & Acceptance

Execution :

  • models/subscription.py -- remplacer les deux clés modèle par les listes de gamme (Essentielle/Premium ci-dessus) -- source de vérité.
  • routes/auth_routes.py -- exposer ai_models_essential/ai_models_premium dans GET /plans -- affichage public.
  • routes/translate_routes.py -- résoudre le modèle par palier : réglages admin (ai_tiers) > défaut du plan ; défauts premium → anthropic/claude-sonnet-5 ; nettoyer le remap l.1279 -- routage réel par abonnement.
  • routes/admin_routes.py -- SettingsConfig.ai_tiers (essential/premium : models[], default_model) avec valeurs par défaut issues de PLANS ; enrichir GET /stats (revenus encaissés 30 j + total via PaymentHistory, MRR estimé par plan, crédits achetés, compteur liste d'attente, répartition paliers IA utilisés) ; nouveaux endpoints GET/POST /marketing/audiences (comptes par audience), POST /marketing/email/send (audience, sujet, html, test_mode ; exclude désabonnés ; historique dans data/marketing_emails.json), GET /marketing/email/history, et public GET /api/v1/marketing/unsubscribe -- gestion + stats + relances.
  • services/email_service.py -- ne rien changer sauf si un helper pied-de-page désabonnement s'avère nécessaire.
  • frontend/src/app/admin/models/page.tsx + marketing/page.tsx + constants.ts + types.ts -- deux pages admin (matrice modèles par plan/palier avec défaut radio + combobox du catalogue OpenRouter ; marketing : audience + compteur, sujet/HTML avec aperçu, envoi test, envoi réel, historique) -- l'exigence de Sepehr.
  • frontend/src/app/admin/stats/ -- cartes et graphiques des nouvelles métriques (revenus, MRR, liste d'attente) -- visibilité business.
  • frontend/src/app/pricing/page.tsx + components/landing/landing-page.tsx -- textes statiques à jour (Claude 5, gamme) -- cohérence marketing.
  • i18n landing.json/pricing.json/admin.json (13 langues) -- clés modifiées/nouvelles traduites ; noms de modèles inchangés -- aucune chaîne manquante.
  • tests/ -- tests : routage palier (pro→essential, business→premium, défaut admin), refus modèle premium pour plan pro, endpoint marketing (envoi test, désabonné exclu, historique), stats revenus.

Acceptance Criteria :

  • Given un utilisateur Pro, when il traduit avec le moteur openrouter, then le modèle employé est celui du palier Essentielle (jamais un modèle Premium) et la page tarifs affiche la gamme exacte.
  • Given l'admin, when il change le modèle par défaut Essentielle puis traduit, then le nouveau modèle est utilisé sans redéploiement.
  • Given un désabonné, when un envoi marketing part, then il ne reçoit rien et l'historique trace envoyés/échecs.
  • Given npm run build et pytest, when exécutés, then succès sans erreur ni chaîne i18n manquante.

Design Notes

Matrice admin (bonne pratique type LiteLLM/OpenRouter) : lignes = plans Pro/Business, colonnes = palier, contenu = modèles actifs (ordre = priorité de secours) + radio « par défaut ». Emails : audience = waitlist | inactive_30 | plan:pro | all_users ; envoi séquentiel 0,2 s d'intervalle dans une tâche de fond ; pied de page avec lien /api/v1/marketing/unsubscribe?email=. Chiffres marketing autorisés : Essentielle « à partir de 0,09 $/M jetons (≈3× moins cher qu'annoncé auparavant) », contexte jusqu'à 1,3 M (GLM-5.3 Flash) ; Premium « Claude 5, 1M de contexte ».

Verification

Commands :

  • cd frontend && npm run build -- expected: compilation sans erreur
  • cd frontend && npm run test:run -- expected: tests vitest verts
  • pytest -x -q -- expected: nouveaux tests + régression verts
  • node -e "…vérif clés i18n identiques fr/en/…" -- expected: aucune clé manquante (script de comparaison)

Manual checks (if no CLI):

  • Page d'accueil + /pricing : gamme affichée = deepseek-v4-flash, glm-5.3-flash, minimax-m3 / Claude 5, deepseek-v4-pro, glm-5.3 ; plus aucun « Claude Haiku » ni « Sonnet 4.6 ».
  • Admin : pages Modèles et Marketing fonctionnelles ; envoi test reçu.

Spec Change Log

Suggested Review Order

Gamme officielle et exposition publique

  • La source de vérité : les deux gammes officielles (Essentielle Pro / Premium Business) et les défauts. subscription.py:41

  • L'endpoint public des forfaits expose les listes de modèles aux pages tarifs. auth_routes.py:786

Routage réel par palier

  • Résolution du modèle : réglages admin > premier de la liste > défaut du plan, avec garde anti-croisement de palier. translate_routes.py:220

  • Le point d'entrée historique affiche désormais le modèle réellement routé. legacy_routes.py:91

Côté admin : réglages et protections

  • Bloc ai_tiers : défauts, normalisation (dédoublonnage, refus de croisement de gamme). admin_routes.py:1055

  • Garde « pristine » : une sauvegarde depuis la page Fournisseurs n'écrase plus les paliers personnalisés. admin_routes.py:1232

Marketing : audiences, envoi, désabonnement

  • En-têtes de délivrabilité et lien de désabonnement par destinataire. admin_routes.py:2388

  • Audiences avec compteurs (liste d'attente, inactifs 30 jours, par forfait). admin_routes.py:2417

  • Envoi : test obligatoire (empreinte), historique « running » avant la boucle, verrou anti-campagne simultanée. admin_routes.py:2499

Interfaces

  • Page « Modèles & abonnements » : matrice forfaits → paliers, défaut radio, catalogue OpenRouter. page.tsx:1

  • Page « Marketing » : audience, aperçu, envoi test, historique (textes 100 % i18n). page.tsx:1

  • Cartes de revenus / MRR / liste d'attente des statistiques. RevenueOverview.tsx:22

  • Listes de modèles côté client alignées sur la gamme. store.ts:10

Périphériques

  • En-têtes optionnels du service SMTP existant (aucun changement pour les appels en place). email_service.py:63

  • Test d'intégration du routage dans la tâche de traduction (faux fournisseur capturant le modèle). test_worker_tier_routing.py:114

  • Tests marketing (désabonnement en cours de campagne, campagne simultanée, échappement) et revenus en base. test_admin_marketing.py:1