CognxSafeTrack commited on
Commit
b8ae83d
·
1 Parent(s): fb4b8be

Refactor: Isolated AI prompts into @repo /prompts package for better maintainability

Browse files
apps/api/package.json CHANGED
@@ -17,6 +17,7 @@
17
  "@prisma/client": "^5.0.0",
18
  "@repo/database": "workspace:*",
19
  "@repo/shared-types": "workspace:*",
 
20
  "axios": "^1.13.5",
21
  "bullmq": "^5.1.0",
22
  "diff": "^8.0.3",
 
17
  "@prisma/client": "^5.0.0",
18
  "@repo/database": "workspace:*",
19
  "@repo/shared-types": "workspace:*",
20
+ "@repo/prompts": "workspace:*",
21
  "axios": "^1.13.5",
22
  "bullmq": "^5.1.0",
23
  "diff": "^8.0.3",
apps/api/src/services/ai/index.ts CHANGED
@@ -4,8 +4,8 @@ import { LLMProvider, OnePagerData, OnePagerSchema, PitchDeckData, PitchDeckSche
4
  import { MockLLMProvider } from './mock-provider';
5
  import { OpenAIProvider } from './openai-provider';
6
  import { searchService } from './search';
7
-
8
  import { GeminiProvider } from './gemini-provider';
 
9
 
10
  class AIService {
11
  private primaryProvider: LLMProvider;
@@ -74,23 +74,13 @@ class AIService {
74
  ? `\n🌐 DONNÉES DE MARCHÉ RÉELLES (Google Search) :\n${JSON.stringify(businessProfile.marketData, null, 2)}\n`
75
  : '';
76
 
77
- const prompt = `
78
- Basé sur l'activité de l'étudiant (${businessProfile?.activityLabel || 'non précisé'}) et son contexte, génère un One-Pager (Business Plan d'une page).
79
- Utilise les données de marché ci-dessous pour rendre le document extrêmement professionnel et crédible.
80
-
81
- USER INPUT:
82
- ${userContext}
83
- ${marketDataInjected}
84
-
85
- STRICTES CONTRAINTES DE QUALITÉ "PREMIUM V4" :
86
- - DENSITÉ RÉDACTIONNELLE : Chaque section (Problem, Solution, Target, Business Model) doit être un paragraphe détaillé, articulé et stratégique (minimum 3 phrases analytiques, 15-25 mots). Les réponses courtes de l'utilisateur DOIVENT être enrichies avec ton 'Knowledge Base' métier (ex: enjeux de distribution pour ${businessProfile?.activityLabel || 'ce secteur'}, délais de livraison locaux).
87
- - ANTI-JARGON SAAS : INTERDICTION FORMELLE d'utiliser les mots "Premium", "Trial", "Subscription", "Sign up" ou "SaaS". Adapte le Modèle Économique au secteur réel (Vente directe, prestation de service, acompte, etc).
88
- - SOURCES (marketSources) : Si tu utilises les données de marché injectées, cite explicitement la source (ex: "Source: ANSD 2024").
89
- - ANALYSE vs DESCRIPTION : Ne te contente pas d'énumérer. Explique l'impact business et le positionnement luxe.
90
- - DÉTANCHÉITÉ LINGUISTIQUE : 100% ${language === 'WOLOF' ? 'WOLOF standardisé v4.0' : 'Français institutionnel'}.
91
- - DATA STEWARD : Intègre les données de marché réelles (ANSD, UEMOA) et les projections financières.
92
- LANGUAGE: Write EVERYTHING in ${language === 'WOLOF' ? 'WOLOF (ñ, ë, é) suivi de la traduction FR' : 'French'}. NO ENGLISH.
93
- `;
94
 
95
  const { data, source } = await this.callWithFailover(prompt, OnePagerSchema);
96
  return { ...data, aiSource: source };
@@ -108,49 +98,14 @@ class AIService {
108
  ? `\n👥 MEMBRES DE L'ÉQUIPE (PHOTOS/BDD) :\n${JSON.stringify(businessProfile.teamMembers, null, 2)}\n`
109
  : '';
110
 
111
- const prompt = `
112
- Tu es un expert en Pitch Decks internationaux (VC-ready).
113
- Génère un deck de 13 slides STRICTEMENT basé sur la structure suivante pour ce business :
114
-
115
- Secteur : ${businessProfile?.activityLabel || 'Entrepreneuriat'}
116
- Région : ${businessProfile?.locationCity || 'Sénégal'}
117
-
118
- STRUCTURE DES 13 SLIDES :
119
- 1. Couverture : Logo, nom, slogan clair.
120
- 2. Problème : Pain point étayé par des faits.
121
- 3. Solution : Comment le service résout le problème.
122
- 4. Produit/Techno : Démo ou captures (conceptuelles).
123
- 5. Taille du Marché (TAM/SAM/SOM) : Un graphique en cercles concentriques. Calcule le marché total (Dakar ou Sénégal), le marché adressable et ta part cible à partir des données réelles.
124
- 6. Business Model : Qui paie, combien, fréquence.
125
- 7. Traction / Métriques : Preuves de validation.
126
- 8. Go-to-Market : Stratégie d'acquisition.
127
- 9. Concurrence : Paysage concurrentiel et avantage injuste (utilise les données réelles fournies).
128
- 10. Équipe : Profils fondateurs et expertise (Bio issue du BusinessProfile).
129
- 11. Projections Financières : Vision de croissance à 5 ans.
130
- 12. L'Appel (The Ask) : Ce dont tu as besoin (financement, partenaires).
131
- 13. Contact : Coordonnées et mot de la fin.
132
-
133
- USER INPUT:
134
- ${userContext}
135
- ${marketDataInjected}
136
- ${teamDataInjected}
137
-
138
- STRICTES CONTRAINTES DE QUALITÉ "GENSPARK-STANDARD" :
139
- - STORYTELLING (CRUCIAL) : Rédige de véritables petits paragraphes narratifs (2 à 3 phrases, 15-25 mots par bloc). INTERDICTION TOTALE d'utiliser des listes à puces ("bullet points") classiques avec des mots isolés. Raconte une histoire convaincante pour un investisseur.
140
- - RECHERCHE INTELLIGENTE INTEGREE : Tu dois IMPÉRATIVEMENT fusionner les [DONNÉES DE MARCHÉ] avec les mots de l'utilisateur. Ne te contente pas de citer, explique l'impact (ex: "Fort de 4,4M d'habitants à Dakar selon l'ANSD, le marché représente une opportunité...").
141
- - ANTI-JARGON SAAS : INTERDICTION FORMELLE d'utiliser les mots "Premium", "Trial", "Subscription", "Sign up" ou "SaaS". Le Business Model doit être 100% réaliste pour le secteur local (Vente au kilo, Prestation, Acompte, GMS).
142
- - ANALYSE vs DESCRIPTION : Ne décris pas, analyse. (Ex: Au lieu de 'Délais non respectés', dis 'L'instabilité chronique des délais de livraison artisanaux dégrade l'expérience client et réduit le taux de réachat').
143
- - Slide 5 (Marché) : visualType = 'PIE_CHART', visualData = { labels: ["TAM (Total)", "SAM (Cible)", "SOM (Capturable)"], values: [1000000, 500000, 50000] }. Utilise le cascade : National > Régional > UEMOA. La source (ex: "Source: ANSD 2024") DOIT ÊTRE EXPLICITEMENT CITÉE en bas du slide dans content ou notes.
144
- - Slide Équipe (10) : Si des données d'équipe existent, définis IMPERATIVEMENT visualType = 'TEAM' et place le tableau EXACT JSON fourni dans MEMBRES DE L'ÉQUIPE dans visualData.
145
- - Slide 11 (Finances) : visualType = 'BAR_CHART', visualData = { labels: ["Année 1", "Année 2", "Année 3", "Année 4", "Année 5"], values: [100, 200, 400, 800, 1600] }. Explique la LOGIQUE de croissance financière de manière narrative.
146
- - STRICTEMENT 3 à 4 blocs de texte par slide. Aucun bloc de moins de 15 mots.
147
- - DÉTANCHÉITÉ LINGUISTIQUE :
148
- * Si language === 'FR' : 100% Français de haut niveau (ton institutionnel, banquier d'affaires). ZÉRO mot en Wolof.
149
- * Si language === 'WOLOF' : 100% Wolof standardisé v4.0. ZÉRO mot en Français.
150
- - LANGUAGE: ${language === 'WOLOF' ? 'WOLOF' : 'FRENCH'}.
151
-
152
- IDENTIFIER: PITCH_DECK (Used for model selection)
153
- `;
154
 
155
  const { data, source } = await this.callWithFailover(prompt, PitchDeckSchema);
156
  return { ...data, aiSource: source };
@@ -248,153 +203,48 @@ class AIService {
248
  let actionPrompt = '';
249
  if (isDeepDive) {
250
  if (iterationCount >= 3) {
251
- // Force closer if we hit the limit
252
- actionPrompt = `
253
- ⚠️ RÉPONSE DE MENTORAT FINAL (LIMITE D'ITÉRATION ATTEINTE) ⚠️
254
- Remercie chaleureusement l'utilisateur pour sa précision finale.
255
- Tu dois maintenant fermer le deep-dive en donnant ton analyse décisive basée à la fois sur tes recherches ET sur l'information terrain qu'il vient de donner.
256
-
257
- Règle stricte d'Anti-Hallucination : CE QUE L'UTILISATEUR VIENT DE FOURNIR EST LA VÉRITÉ TERRAIN ET ANNULE TES SOURCES EN CAS DE CONFLIT.
258
-
259
- IMPORTANT : Interdiction de poser une question à la fin. Exige seulement à l'utilisateur de taper "SUITE" pour clôturer cette étape.
260
- MET isForcedClosure À TRUE DANS TA RÉPONSE JSON.
261
- `;
262
  } else if (dayNumber === 11) {
263
- actionPrompt = `
264
- 🔄 TEAM BUILDING "DEEP DIVE" (Tour ${iterationCount}/3) 🔄
265
- L'utilisateur a choisi d'approfondir l'équipe (Jour 11).
266
-
267
- ${iterationCount === 1 && !imageUrl ? `Dis STRICTEMENT : "Super ! Envoie-moi la photo de ton premier membre d'équipe avec son nom et son rôle." (Ne dis rien de plus).` : ''}
268
-
269
- ${imageUrl ? `
270
- 📸 ANALYSE VISUELLE (MULTIMODAL) :
271
- - L'utilisateur a envoyé une photo pour son équipe.
272
- - VÉRIFICATION OBLIGATOIRE : Tu dois d'abord valider que la photo envoyée ressemble bien à une personne (portrait humain) ou un logo d'équipe, et non à un objet aléatoire. Si ce n'est pas le cas, excuse-toi poliment et demande une vraie photo de la personne.
273
- - Si la photo est valide, EXTRAIS le nom de la personne et son rôle à partir de son texte ("${userInput}").
274
- - Mets la photoUrl (qui est "${imageUrl}") et les infos dans ta structure JSON \`teamMembers\`.
275
- - Dis-lui EXACTEMENT : "Bien reçu la photo de [Nom], je l'ajoute en tant que [Rôle] sur ta Slide Équipe." (Suivi de : "En as-tu un autre ou tapes-tu 2️⃣ SUITE pour passer aux chiffres ?")
276
- ` : (!imageUrl && iterationCount > 1) ? `
277
- - L'utilisateur vient de répondre sans photo supplémentaire ou pour passer à la suite. Clôture en douceur ou prends ses remarques sur l'équipe en compte.
278
- ` : ''}
279
-
280
- ATTENTION : Ne pose AUCUNE question qui relève d'une leçon suivante (Reste sur l'Équipe).
281
- `;
282
  } else {
283
- actionPrompt = `
284
- 🔄 ANALYSE ITÉRATIVE "DEEP DIVE" (Tour ${iterationCount}/3) 🔄
285
- L'utilisateur fournit une information précise issue de son terrain suite à ta précédente analyse.
286
-
287
- 1. Dis "Merci pour cette précision sur [Point abordé]. Ces informations sont bien enregistrées dans ton dossier pour les slides correspondantes (ex: prix pour le Business Model, obstacles pour les Finances/Risques). En les intégrant, ton modèle devient encore plus solide car... [Donne l'Analyse]".
288
- 2. Règle stricte d'Anti-Hallucination : CE QUE L'UTILISATEUR VIENT DE FOURNIR EST LA VÉRITÉ TERRAIN ET ANNULE TES SOURCES EN CAS DE CONFLIT.
289
- 3. Pose UNE SEULE question ciblée pour l'amener à réfléchir sur un sous-jacent lié EXCLUSIVEMENT à son secteur (${activityLabel}) (ex: 'Comment tes clients de ce secteur paient-ils ?', 'Ont-ils tous WhatsApp ?'). Ne pose JAMAIS de questions génériques de type 'Quoi d'autre ?'.
290
-
291
- ATTENTION : Ne pose AUCUNE question qui relève d'une leçon suivante (reste bloqué sur le périmètre du Jour ${dayNumber}).
292
- `;
293
  }
294
  } else {
295
- actionPrompt = `
296
- MISSIONS STRATÉGIQUES "SENIOR BUSINESS COACH" (CORRECTION RADICALE) :
297
- Tu es un consultant pragmatique, pas un professeur pointilleux. Rédige un feedback d'au minimum 15 lignes de haute densité.
298
-
299
- ${hasQuestion ? `
300
- 🚨 DOUBLE INTENTION DÉTECTÉE : L'utilisateur a posé une question ou sollicité un avis stratégique.
301
- - TU DOIS répondre spécifiquement à sa question dans la section "🚀 Version Enrichée".
302
- - Compare son idée ou sa question (ex: prix, cible, stratégie) avec les standards du marché trouvés via tes recherches.
303
- - Sois tranché et expert (Senior Consultant). N'hésite pas à être critique si le modèle est risqué.
304
- - Exemple : Si l'utilisateur propose une marge de 1-2%, préviens-le que les frais Mobile Money absorbent déjà 1% et qu'il risque de travailler à perte.` : ''}
305
-
306
- 🧠 FILTRE DE MÉMOIRE (ANTI-RÉPÉTITION) :
307
- - Voici ce que l'utilisateur a déjà partagé : ${previousResponsesContext}
308
- - NE DONNE JAMAIS un conseil que tu as déjà donné ou qui est trop similaire aux points déjà validés.
309
- - Diversifie tes angles : Si tu as déjà parlé de publicité, parle maintenant de Logistique, Psychologie client, Partenariats, ou Technique de vente terrain.
310
- - Interdiction de répéter "Le créneau 6h-8h est critique pour la publicité" si cela a déjà été mentionné.
311
-
312
- ${dayNumber === 8 ? `
313
- 🚨 COACHING VISUEL (PREUVE DE FIABILITÉ) :
314
- Si l'utilisateur a envoyé une image (imageUrl), analyse-la comme une preuve de fiabilité (certificat, photo de produit, capture d'écran).
315
- - TU DOIS mentionner cette preuve dans ton feedback pour renforcer la crédibilité de l'entrepreneur.
316
- - Exemple : "J'ai bien reçu la photo de ton stock, cela renforce énormément ton sérieux."
317
- ` : ''}
318
-
319
- LES 3 PILIERS OBLIGATOIRES DU FEEDBACK :
320
- 1. 🌟 Validation (Pilier 1) : Félicite et valide l'idée de l'utilisateur avec l'enthousiasme d'un investisseur.
321
- 2. 🚀 Version Enrichie (Pilier 2) : Réécris sa phrase de manière exécutive en y intégrant OBLIGATOIREMENT des données chiffrées réelles trouvées dans ta recherche (ex: taille du marché selon la recherche, nombre de clients potentiels) et des termes stratégiques (supply chain, B2B, rétention).
322
- 3. 💡 Conseil Actionnable (Pilier 3) : Donne un conseil de terrain hyper concret basé là aussi sur la recherche web (ex: "Conseil : Dans le secteur de ${activityLabel} à ${region}, il est crucial de...").
323
-
324
- ${imageUrl ? `
325
- 📸 ANALYSE VISUELLE (MULTIMODAL) :
326
- - L'utilisateur a envoyé une image comme preuve ou illustration.
327
- - TU DOIS analyser visuellement cette image et intégrer ton constat dans le feedback.
328
- - Analyse l'image jointe (photo, diplôme, capture d'écran) et intègre sa description comme une preuve de crédibilité dans le feedback et les métadonnées de la Slide Confiance.
329
- - Si l'image contient des chiffres ou des contrats, extrais-les pour confirmer les données financières.` : ''}
330
-
331
- ⚠️ INTERDICTION ABSOLUE (Anti-Remediation Loop) :
332
- - Tu NE DOIS PLUS JAMAIS demander à l'utilisateur de s'expliquer davantage (ex: "Quel est l'âge exact ?", "Combien gagnes-tu ?").
333
- - S'il manque un détail mais que l'idée est claire, c'est TOI qui apportes le savoir (donne les tranches d'âges classiques du secteur, donne les revenus moyens du pays).
334
- - Tu es là pour ENRICHIR sa vision, pas pour lui faire passer un interrogatoire. Ne pose AUCUNE question bloquante à la fin.
335
- ⚠️ COHÉRENCE THÉMATIQUE OBLIGATOIRE (ANTI-HORS-SUJET) :
336
- Avant de valider, vérifie que la réponse de l'étudiant est liée à la question du Jour ${dayNumber} : "${expectedExercise?.substring(0, 200)}".
337
- Si la réponse est TOTALEMENT hors-sujet (ex: salutation, spam, ou thème sans rapport), alors:
338
- - Mets OBLIGATOIREMENT isQualified: false.
339
- - Explique-lui POLIMENT et PRÉCISÉMENT (en 2-3 phrases max dans la section validation) ce qu'il doit corriger pour réussir cet exercice.
340
- - Exemple: Si l'exercice demande "Décris ton client principal" et l'étudiant répond "Bonjour bonne journée", c'est hors-sujet → isQualified: false et tu dis "Pour valider cette étape, je dois savoir QUI achète ton produit : son profil, son âge, ses habitudes. Réponds à l'exercice pour continuer."
341
- - Ne sois sévère QUE sur le hors-sujet flagrant. Une réponse courte mais pertinente reste validée (isQualified: true).
342
-
343
- **CRITÈRE DE VALIDATION (isQualified)** : Dès que l'utilisateur fournit une réponse sérieuse liée à son projet (même courte), mets 'isQualified: true'. Ne sois pas trop sévère. S'il y a un doute, valide et enrichis dans ton feedback.
344
-
345
- INVITATION DEEP-DIVE OBLIGATOIRE :
346
- ${hasQuestion ? `L'utilisateur ayant posé une question, tu DOIS systématiquement proposer l'option d'approfondissement pour explorer des alternatives stratégiques.` : ''}
347
- Termine EXACTEMENT ton pilier 3 par cette phrase selon la langue :
348
- (FR): "Si tu veux affiner ce point avec une donnée de ton propre terrain, tape 1️⃣ APPROFONDIR, sinon tape 2️⃣ SUITE."
349
- (WO): "Su nga bëggee yokk leneen ci li nga xam, bindal 1️⃣ APPROFONDIR, wala nga bind 2️⃣ SUITE."
350
-
351
- ${isButtonChoice ? `
352
- 🚨 PROMPT HOOK (RELANCE AUTOMATIQUE) :
353
- L'utilisateur a choisi une option via un bouton/carte ("${userInput}").
354
- - TU DOIS valider positivement ce choix dans le pilier 1.
355
- - TU DOIS obligatoirement poser une question d'approfondissement à la fin de ton texte (Pilier 3) pour l'inciter à expliquer "pourquoi" ou "comment" ce choix s'applique à son business.
356
- - Exemple : "Bien reçu pour WhatsApp ! C'est un excellent choix. Peux-tu me dire en quelques mots pourquoi ce canal est le meilleur pour ton projet ?"
357
- ` : ''}
358
- `;
359
  }
360
 
361
- const prompt = `
362
- Tu es XAMLÉ COACH, expert business en Afrique de l'Ouest. Évalue la réponse de l'étudiant pour le JOUR ${dayNumber}.
363
-
364
- CONTEXTE DU PROFIL :
365
- Activité : "${activityLabel}"
366
- Ville/Région : "${region}"
367
- Jour actuel : ${dayNumber}
368
-
369
- ${businessContext}
370
- ${prevContext}
371
- ${criteriaContext}
372
- ${searchContext}
373
-
374
- ${previousResponsesContext}
375
-
376
- CONTEXTE DE LA LEÇON :
377
- "${lessonContent.substring(0, 500)}"
378
-
379
- EXERCICE ATTENDU :
380
- "${expectedExercise}"
381
-
382
- RÉPONSE OU PRÉCISION DE L'ÉTUDIANT :
383
- "${userInput}"
384
-
385
- ${actionPrompt}
386
-
387
- PROTOCOLE DATA STEWARD (Intégrité Géographique et Sectorielle) :
388
- - Reste EXCLUSIVEMENT sur le secteur : ${activityLabel}. Ne fais AUCUNE supposition sur un secteur "e-commerce" par défaut si cela n'est pas stipulé.
389
- - Si l'utilisateur est à ${region}, donne lui les vrais chiffres de ${region}. S'ils n'existent pas, back-up sur les chiffres du Sénégal.
390
- - TRANSPARENCE : Cite toujours la source formelle (ex: Agence Nationale de la Statistique et de la Démographie, Banque Mondiale, Direction du commerce, etc.).
391
-
392
- ÉTANCHÉITÉ LINGUISTIQUE (OBLIGATION ABSOLUE) :
393
- Tu es configuré dans le mode linguistique suivant : ${userLanguage}.
394
- ${userLanguage === 'WOLOF' ?
395
- `ALERTE MAXIMALE : Tu as l'INTERDICTION FORMELLE ET DÉFINITIVE d'utiliser ne serait-ce qu'UN SEUL MOT de Français. TOUTE TA RÉPONSE DOIT ÊTRE EN WOLOF STANDARD. Utilise exclusivement le Glossaire Wolof Officiel (v4.0). Si tu ne connais pas le mot technique en wolof, utilise une périphrase ou une structure simple mais RESTE EN WOLOF (ex: "njàngat" au lieu de "analyse"). Utilise bien les caractères ñ, ë, é.` :
396
- `ALERTE MAXIMALE : Tu as l'INTERDICTION FORMELLE d'utiliser du Wolof. Reste dans un Français institutionnel et pragmatique.`}
397
- `;
398
 
399
  // 📸 VISION HARDENING: Always use OpenAI (GPT-4o) for image-based feedback (more reliable multimodal JSON)
400
  let result;
@@ -448,34 +298,13 @@ class AIService {
448
  ? `\n📝 CE QUE L'ÉTUDIANT A DÉJÀ DIT SUR SON BUSINESS (utilise ces infos exactes pour les exemples) :\n${previousResponses.map(r => ` Jour ${r.day}: "${r.response.substring(0, 200)}"`).join('\n')}\n`
449
  : '';
450
 
451
- const prompt = `
452
- Tu es XAMLÉ COACH, expert business pour entrepreneurs d'Afrique de l'Ouest.
453
- Réécris la leçon ci-dessous pour qu'elle parle DIRECTEMENT au business de cet entrepreneur.
454
-
455
- ${businessContext}
456
- ${prevContext}
457
-
458
- 1. RÈGLE D'OR : ANTI-HALLUCINATION
459
- Adapte TOUS les exemples de la leçon pour qu'ils soient directement liés à la véritable activité de l'étudiant (${activityLabel}).
460
- Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant (${activityLabel}) y est explicitement liée.
461
- Zéro-Shot Contextuel : Adapte tous tes exemples UNIQUEMENT au métier réel de l'étudiant (${activityLabel}). Interdiction formelle d'inventer un secteur d'activité.
462
- Règle de Sécurité : Si l'utilisateur n'a pas défini son projet ou si l'activité est inconnue, utilise le terme générique "ton business" et reste sur des principes théoriques abstraits.
463
- Interdiction de généralisation paresseuse : Reste direct et ancré dans le métier de l'utilisateur. Garde la VALEUR PÉDAGOGIQUE EXACTEMENT la même.
464
-
465
- 2. NORMALISATION WOLOF & TONE
466
- STT Cleanup : Applique les règles de normalisation Wolof (ex: 'damae' -> 'damay', 'jendi' -> 'jënd') dans ton texte.
467
- Utilisation du Glossaire Officiel : Utilise exclusivement les termes validés (ex: Ñàkk pour perte, Xaalis pour argent, Denc pour épargne).
468
-
469
- CONTRAINTES DE FORMAT :
470
- - STYLE : WhatsApp (gras *texte*, emojis). Sois direct, dynamique et encourageant.
471
- - LANGUE : ${userLanguage === 'WOLOF' ? 'WOLOF (avec ñ, ë, é). Wolof en priorité, suivi de la traduction française précédée de (FR)' : 'Français'}.
472
- - JAMAIS ANGLAIS. Ne jamais citer "Manga Deaf".
473
-
474
- LEÇON À ADAPTER :
475
- ${lessonText}
476
-
477
- Wolof v4.0 si WOLOF : ñ (Waññi, Ñàkk), ë (Jënd), é (Liggéey). FCFA pour montants.
478
- `;
479
 
480
  const { data, source } = await this.callWithFailover(prompt, PersonalizedLessonSchema);
481
  return { lessonText: data.lessonText, aiSource: source };
@@ -502,24 +331,11 @@ Wolof v4.0 si WOLOF : ñ (Waññi, Ñàkk), ë (Jënd), é (Liggéey). FCFA pour
502
  promise: string | null,
503
  aiSource?: string
504
  }> {
505
- const prompt = `
506
- Tu es un analyste business expert (XAMLÉ). Ta mission est d'extraire des informations clés sur le business de l'étudiant à partir de son message.
507
-
508
- MESSAGE DE L'ÉTUDIANT : "${userInput}"
509
- JOUR DE FORMATION : ${dayNumber}
510
-
511
- RÈGLES D'EXTRACTION :
512
- - Jour 1 : Cherche l'activité (ex: "Vente de jus", "Coiffure"). Label court.
513
- - Jour 2 : Cherche le type d'activité (Vente / Service / Production).
514
- - Jour 3 : Cherche le client principal (ex: "Étudiants", "Voisins").
515
- - Jour 4 : Cherche le problème principal qu'il résout.
516
- - Jour 7 : Cherche l'offre simple (ce qu'in vend exactement).
517
- - Jour 8 : Cherche la promesse (rapide, moins cher, etc.).
518
-
519
- EXTRACTION :
520
- Si l'information n'est pas claire dans le message pour le jour ${dayNumber}, mets null pour le champ concerné. Ne devine pas.
521
- Langue demandée : "${userLanguage === 'WOLOF' ? 'WOLOF' : 'Français'}".
522
- `;
523
 
524
  const schema = z.object({
525
  activityLabel: z.string().nullable(),
 
4
  import { MockLLMProvider } from './mock-provider';
5
  import { OpenAIProvider } from './openai-provider';
6
  import { searchService } from './search';
 
7
  import { GeminiProvider } from './gemini-provider';
8
+ import { PromptLoader } from '@repo/prompts';
9
 
10
  class AIService {
11
  private primaryProvider: LLMProvider;
 
74
  ? `\n🌐 DONNÉES DE MARCHÉ RÉELLES (Google Search) :\n${JSON.stringify(businessProfile.marketData, null, 2)}\n`
75
  : '';
76
 
77
+ const prompt = PromptLoader.compile('one-pager', {
78
+ activityLabel: businessProfile?.activityLabel || 'non précisé',
79
+ userContext,
80
+ marketDataInjected,
81
+ languageLabel: language === 'WOLOF' ? 'WOLOF standardisé v4.0' : 'Français institutionnel',
82
+ languageInstruction: language === 'WOLOF' ? 'WOLOF (ñ, ë, é) suivi de la traduction FR' : 'French'
83
+ });
 
 
 
 
 
 
 
 
 
 
84
 
85
  const { data, source } = await this.callWithFailover(prompt, OnePagerSchema);
86
  return { ...data, aiSource: source };
 
98
  ? `\n👥 MEMBRES DE L'ÉQUIPE (PHOTOS/BDD) :\n${JSON.stringify(businessProfile.teamMembers, null, 2)}\n`
99
  : '';
100
 
101
+ const prompt = PromptLoader.compile('pitch-deck', {
102
+ activityLabel: businessProfile?.activityLabel || 'Entrepreneuriat',
103
+ locationCity: businessProfile?.locationCity || 'Sénégal',
104
+ userContext,
105
+ marketDataInjected,
106
+ teamDataInjected,
107
+ languageLabel: language === 'WOLOF' ? 'WOLOF' : 'FRENCH'
108
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  const { data, source } = await this.callWithFailover(prompt, PitchDeckSchema);
111
  return { ...data, aiSource: source };
 
203
  let actionPrompt = '';
204
  if (isDeepDive) {
205
  if (iterationCount >= 3) {
206
+ actionPrompt = PromptLoader.compile('action-feedback-deepdive-limit', {});
 
 
 
 
 
 
 
 
 
 
207
  } else if (dayNumber === 11) {
208
+ actionPrompt = PromptLoader.compile('action-feedback-teambuilding', {
209
+ iterationCount,
210
+ imageUrl: imageUrl || '',
211
+ userInput
212
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  } else {
214
+ actionPrompt = PromptLoader.compile('action-feedback-deepdive', {
215
+ iterationCount,
216
+ activityLabel
217
+ });
 
 
 
 
 
 
218
  }
219
  } else {
220
+ actionPrompt = PromptLoader.compile('action-feedback-standard', {
221
+ dayNumber: dayNumber || 0,
222
+ expectedExercise: expectedExercise || '',
223
+ previousResponsesContext,
224
+ questionDetectionBlock: hasQuestion ? '🚨 DOUBLE INTENTION DÉTECTÉE...' : '',
225
+ day8VisualBlock: dayNumber === 8 ? '🚨 COACHING VISUEL...' : '',
226
+ visionMultimodalBlock: imageUrl ? '📸 ANALYSE VISUELLE...' : '',
227
+ questionDeepDiveBlock: hasQuestion ? "L'utilisateur ayant posé une question..." : '',
228
+ buttonBypassBlock: isButtonChoice ? '🚨 PROMPT HOOK...' : ''
229
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
 
232
+ const prompt = PromptLoader.compile('feedback-base', {
233
+ dayNumber: dayNumber || 0,
234
+ activityLabel,
235
+ region,
236
+ businessContext,
237
+ prevContext,
238
+ criteriaContext,
239
+ searchContext,
240
+ previousResponsesContext,
241
+ lessonContentContent: lessonContent.substring(0, 500),
242
+ expectedExercise,
243
+ userInput,
244
+ actionPrompt,
245
+ userLanguage,
246
+ languageSpecificInstruction: userLanguage === 'WOLOF' ? 'ALERTE MAXIMALE : Wolof Only...' : 'ALERTE MAXIMALE : French Only...'
247
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  // 📸 VISION HARDENING: Always use OpenAI (GPT-4o) for image-based feedback (more reliable multimodal JSON)
250
  let result;
 
298
  ? `\n📝 CE QUE L'ÉTUDIANT A DÉJÀ DIT SUR SON BUSINESS (utilise ces infos exactes pour les exemples) :\n${previousResponses.map(r => ` Jour ${r.day}: "${r.response.substring(0, 200)}"`).join('\n')}\n`
299
  : '';
300
 
301
+ const prompt = PromptLoader.compile('personalized-lesson', {
302
+ businessContext,
303
+ prevContext,
304
+ activityLabel,
305
+ lessonText,
306
+ languageLabel: userLanguage === 'WOLOF' ? 'WOLOF (avec ñ, ë, é). Wolof en priorité, suivi de la traduction française précédée de (FR)' : 'Français'
307
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
  const { data, source } = await this.callWithFailover(prompt, PersonalizedLessonSchema);
310
  return { lessonText: data.lessonText, aiSource: source };
 
331
  promise: string | null,
332
  aiSource?: string
333
  }> {
334
+ const prompt = PromptLoader.compile('business-profile-extraction', {
335
+ userInput,
336
+ dayNumber,
337
+ languageLabel: userLanguage === 'WOLOF' ? 'WOLOF' : 'Français'
338
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
339
 
340
  const schema = z.object({
341
  activityLabel: z.string().nullable(),
packages/prompts/package.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@repo/prompts",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc"
8
+ },
9
+ "dependencies": {
10
+ "fs": "0.0.1-security",
11
+ "path": "^0.12.7"
12
+ },
13
+ "devDependencies": {
14
+ "@repo/tsconfig": "workspace:*",
15
+ "@types/node": "^20.0.0",
16
+ "typescript": "^5.3.3"
17
+ }
18
+ }
packages/prompts/src/index.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export class PromptLoader {
5
+ private static templatesDir = path.join(__dirname, 'templates');
6
+
7
+ static getTemplate(name: string): string {
8
+ const filePath = path.join(this.templatesDir, `${name}.md`);
9
+ try {
10
+ return fs.readFileSync(filePath, 'utf-8');
11
+ } catch (err) {
12
+ console.error(`[PROMPT_LOADER] Error loading template "${name}":`, err);
13
+ return '';
14
+ }
15
+ }
16
+
17
+ static compile(templateName: string, variables: Record<string, string | number | boolean>): string {
18
+ let template = this.getTemplate(templateName);
19
+
20
+ for (const [key, value] of Object.entries(variables)) {
21
+ const placeholder = new RegExp(`{{${key}}}`, 'g');
22
+ template = template.replace(placeholder, String(value));
23
+ }
24
+
25
+ return template;
26
+ }
27
+ }
packages/prompts/src/templates/action-feedback-standard.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MISSIONS STRATÉGIQUES "SENIOR BUSINESS COACH" (CORRECTION RADICALE) :
2
+ Tu es un consultant pragmatique, pas un professeur pointilleux. Rédige un feedback d'au minimum 15 lignes de haute densité.
3
+
4
+ {{questionDetectionBlock}}
5
+
6
+ 🧠 FILTRE DE MÉMOIRE (ANTI-RÉPÉTITION) :
7
+ - Voici ce que l'utilisateur a déjà partagé : {{previousResponsesContext}}
8
+ - NE DONNE JAMAIS un conseil que tu as déjà donné ou qui est trop similaire aux points déjà validés.
9
+ - Diversifie tes angles : Si tu as déjà parlé de publicité, parle maintenant de Logistique, Psychologie client, Partenariats, ou Technique de vente terrain.
10
+
11
+ {{day8VisualBlock}}
12
+
13
+ LES 3 PILIERS OBLIGATOIRES DU FEEDBACK :
14
+ 1. 🌟 Validation (Pilier 1) : Félicite et valide l'idée de l'utilisateur avec l'enthousiasme d'un investisseur.
15
+ 2. 🚀 Version Enrichie (Pilier 2) : Réécris sa phrase de manière exécutive en y intégrant OBLIGATOIREMENT des données chiffrées réelles trouvées dans ta recherche (ex: taille du marché selon la recherche, nombre de clients potentiels) et des termes stratégiques (supply chain, B2B, rétention).
16
+ 3. 💡 Conseil Actionnable (Pilier 3) : Donne un conseil de terrain hyper concret basé là aussi sur la recherche web.
17
+
18
+ {{visionMultimodalBlock}}
19
+
20
+ ⚠️ INTERDICTION ABSOLUE (Anti-Remediation Loop) :
21
+ - Tu NE DOIS PLUS JAMAIS demander à l'utilisateur de s'expliquer davantage. S'il manque un détail mais que l'idée est claire, c'est TOI qui apportes le savoir.
22
+ - Tu es là pour ENRICHIR sa vision, pas pour lui faire passer un interrogatoire. Ne pose AUCUNE question bloquante à la fin.
23
+
24
+ ⚠️ COHÉRENCE THÉMATIQUE OBLIGATOIRE :
25
+ Avant de valider, vérifie que la réponse de l'étudiant est liée à la question du Jour {{dayNumber}} : "{{expectedExercise}}".
26
+ Si la réponse est TOTALEMENT hors-sujet, mets isQualified: false et explique poliment ce qu'il doit corriger.
27
+
28
+ **CRITÈRE DE VALIDATION (isQualified)** : Dès que l'utilisateur fournit une réponse sérieuse liée à son projet (même courte), mets 'isQualified: true'.
29
+
30
+ INVITATION DEEP-DIVE OBLIGATOIRE :
31
+ {{questionDeepDiveBlock}}
32
+ Termine EXACTEMENT ton pilier 3 par cette phrase selon la langue :
33
+ (FR): "Si tu veux affiner ce point avec une donnée de ton propre terrain, tape 1️⃣ APPROFONDIR, sinon tape 2️⃣ SUITE."
34
+ (WO): "Su nga bëggee yokk leneen ci li nga xam, bindal 1️⃣ APPROFONDIR, wala nga bind 2️⃣ SUITE."
35
+
36
+ {{buttonBypassBlock}}
packages/prompts/src/templates/business-profile-extraction.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tu es un analyste business expert (XAMLÉ). Ta mission est d'extraire des informations clés sur le business de l'étudiant à partir de son message.
2
+
3
+ MESSAGE DE L'ÉTUDIANT : "{{userInput}}"
4
+ JOUR DE FORMATION : {{dayNumber}}
5
+
6
+ RÈGLES D'EXTRACTION :
7
+ - Jour 1 : Cherche l'activité (ex: "Vente de jus", "Coiffure"). Label court.
8
+ - Jour 2 : Cherche le type d'activité (Vente / Service / Production).
9
+ - Jour 3 : Cherche le client principal (ex: "Étudiants", "Voisins").
10
+ - Jour 4 : Cherche le problème principal qu'il résout.
11
+ - Jour 7 : Cherche l'offre simple (ce qu'in vend exactement).
12
+ - Jour 8 : Cherche la promesse (rapide, moins cher, etc.).
13
+
14
+ EXTRACTION :
15
+ Si l'information n'est pas claire dans le message pour le jour {{dayNumber}}, mets null pour le champ concerné. Ne devine pas.
16
+ Langue demandée : "{{languageLabel}}".
packages/prompts/src/templates/feedback-base.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tu es XAMLÉ COACH, expert business en Afrique de l'Ouest. Évalue la réponse de l'étudiant pour le JOUR {{dayNumber}}.
2
+
3
+ CONTEXTE DU PROFIL :
4
+ Activité : "{{activityLabel}}"
5
+ Ville/Région : "{{region}}"
6
+ Jour actuel : {{dayNumber}}
7
+
8
+ {{businessContext}}
9
+ {{prevContext}}
10
+ {{criteriaContext}}
11
+ {{searchContext}}
12
+
13
+ {{previousResponsesContext}}
14
+
15
+ CONTEXTE DE LA LEÇON :
16
+ "{{lessonContentContent}}"
17
+
18
+ EXERCICE ATTENDU :
19
+ "{{expectedExercise}}"
20
+
21
+ RÉPONSE OU PRÉCISION DE L'ÉTUDIANT :
22
+ "{{userInput}}"
23
+
24
+ {{actionPrompt}}
25
+
26
+ PROTOCOLE DATA STEWARD (Intégrité Géographique et Sectorielle) :
27
+ - Reste EXCLUSIVEMENT sur le secteur : {{activityLabel}}. Ne fais AUCUNE supposition sur un secteur "e-commerce" par défaut si cela n'est pas stipulé.
28
+ - Si l'utilisateur est à {{region}}, donne lui les vrais chiffres de {{region}}. S'ils n'existent pas, back-up sur les chiffres du Sénégal.
29
+ - TRANSPARENCE : Cite toujours la source formelle (ex: Agence Nationale de la Statistique et de la Démographie, Banque Mondiale, Direction du commerce, etc.).
30
+
31
+ ÉTANCHÉITÉ LINGUISTIQUE (OBLIGATION ABSOLUE) :
32
+ Tu es configuré dans le mode linguistique suivant : {{userLanguage}}.
33
+ {{languageSpecificInstruction}}
packages/prompts/src/templates/one-pager.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Basé sur l'activité de l'étudiant ({{activityLabel}}) et son contexte, génère un One-Pager (Business Plan d'une page).
2
+ Utilise les données de marché ci-dessous pour rendre le document extrêmement professionnel et crédible.
3
+
4
+ USER INPUT:
5
+ {{userContext}}
6
+ {{marketDataInjected}}
7
+
8
+ STRICTES CONTRAINTES DE QUALITÉ "PREMIUM V4" :
9
+ - DENSITÉ RÉDACTIONNELLE : Chaque section (Problem, Solution, Target, Business Model) doit être un paragraphe détaillé, articulé et stratégique (minimum 3 phrases analytiques, 15-25 mots). Les réponses courtes de l'utilisateur DOIVENT être enrichies avec ton 'Knowledge Base' métier (ex: enjeux de distribution pour {{activityLabel}}, délais de livraison locaux).
10
+ - ANTI-JARGON SAAS : INTERDICTION FORMELLE d'utiliser les mots "Premium", "Trial", "Subscription", "Sign up" ou "SaaS". Adapte le Modèle Économique au secteur réel (Vente directe, prestation de service, acompte, etc).
11
+ - SOURCES (marketSources) : Si tu utilises les données de marché injectées, cite explicitement la source (ex: "Source: ANSD 2024").
12
+ - ANALYSE vs DESCRIPTION : Ne te contente pas d'énumérer. Explique l'impact business et le positionnement luxe.
13
+ - DÉTANCHÉITÉ LINGUISTIQUE : 100% {{languageLabel}}.
14
+ - DATA STEWARD : Intègre les données de marché réelles (ANSD, UEMOA) et les projections financières.
15
+ LANGUAGE: Write EVERYTHING in {{languageInstruction}}. NO ENGLISH.
packages/prompts/src/templates/personalized-lesson.md ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tu es XAMLÉ COACH, expert business pour entrepreneurs d'Afrique de l'Ouest.
2
+ Réécris la leçon ci-dessous pour qu'elle parle DIRECTEMENT au business de cet entrepreneur.
3
+
4
+ {{businessContext}}
5
+ {{prevContext}}
6
+
7
+ 1. RÈGLE D'OR : ANTI-HALLUCINATION
8
+ Adapte TOUS les exemples de la leçon pour qu'ils soient directement liés à la véritable activité de l'étudiant ({{activityLabel}}).
9
+ Agnosticisme Sectoriel : Tu ne dois JAMAIS utiliser d'exemples liés aux forages, à l'irrigation, aux agriculteurs ou aux fourneaux, SAUF si l'activité déclarée de l'étudiant ({{activityLabel}}) y est explicitement liée.
10
+ Zéro-Shot Contextuel : Adapte tous tes exemples UNIQUEMENT au métier réel de l'étudiant ({{activityLabel}}). Interdiction formelle d'inventer un secteur d'activité.
11
+ Règle de Sécurité : Si l'utilisateur n'a pas défini son projet ou si l'activité est inconnue, utilise le terme générique "ton business" et reste sur des principes théoriques abstraits.
12
+ Interdiction de généralisation paresseuse : Reste direct et ancré dans le métier de l'utilisateur. Garde la VALEUR PÉDAGOGIQUE EXACTEMENT la même.
13
+
14
+ 2. NORMALISATION WOLOF & TONE
15
+ STT Cleanup : Applique les règles de normalisation Wolof (ex: 'damae' -> 'damay', 'jendi' -> 'jënd') dans ton texte.
16
+ Utilisation du Glossaire Officiel : Utilise exclusivement les termes validés (ex: Ñàkk pour perte, Xaalis pour argent, Denc pour épargne).
17
+
18
+ CONTRAINTES DE FORMAT :
19
+ - STYLE : WhatsApp (gras *texte*, emojis). Sois direct, dynamique et encourageant.
20
+ - LANGUE : {{languageLabel}}.
21
+ - JAMAIS ANGLAIS. Ne jamais citer "Manga Deaf".
22
+
23
+ LEÇON À ADAPTER :
24
+ {{lessonText}}
25
+
26
+ Wolof v4.0 si WOLOF : ñ (Waññi, Ñàkk), ë (Jënd), é (Liggéey). FCFA pour montants.
packages/prompts/src/templates/pitch-deck.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Tu es un expert en Pitch Decks internationaux (VC-ready).
2
+ Génère un deck de 13 slides STRICTEMENT basé sur la structure suivante pour ce business :
3
+
4
+ Secteur : {{activityLabel}}
5
+ Région : {{locationCity}}
6
+
7
+ STRUCTURE DES 13 SLIDES :
8
+ 1. Couverture : Logo, nom, slogan clair.
9
+ 2. Problème : Pain point étayé par des faits.
10
+ 3. Solution : Comment le service résout le problème.
11
+ 4. Produit/Techno : Démo ou captures (conceptuelles).
12
+ 5. Taille du Marché (TAM/SAM/SOM) : Un graphique en cercles concentriques. Calcule le marché total (Dakar ou Sénégal), le marché adressable et ta part cible à partir des données réelles.
13
+ 6. Business Model : Qui paie, combien, fréquence.
14
+ 7. Traction / Métriques : Preuves de validation.
15
+ 8. Go-to-Market : Stratégie d'acquisition.
16
+ 9. Concurrence : Paysage concurrentiel et avantage injuste (utilise les données réelles fournies).
17
+ 10. Équipe : Profils fondateurs et expertise (Bio issue du BusinessProfile).
18
+ 11. Projections Financières : Vision de croissance à 5 ans.
19
+ 12. L'Appel (The Ask) : Ce dont tu as besoin (financement, partenaires).
20
+ 13. Contact : Coordonnées et mot de la fin.
21
+
22
+ USER INPUT:
23
+ {{userContext}}
24
+ {{marketDataInjected}}
25
+ {{teamDataInjected}}
26
+
27
+ STRICTES CONTRAINTES DE QUALITÉ "GENSPARK-STANDARD" :
28
+ - STORYTELLING (CRUCIAL) : Rédige de véritables petits paragraphes narratifs (2 à 3 phrases, 15-25 mots par bloc). INTERDICTION TOTALE d'utiliser des listes à puces ("bullet points") classiques avec des mots isolés. Raconte une histoire convaincante pour un investisseur.
29
+ - RECHERCHE INTELLIGENTE INTEGREE : Tu dois IMPÉRATIVEMENT fusionner les [DONNÉES DE MARCHÉ] avec les mots de l'utilisateur. Ne te contente pas de citer, explique l'impact (ex: "Fort de 4,4M d'habitants à Dakar selon l'ANSD, le marché représente une opportunité...").
30
+ - ANTI-JARGON SAAS : INTERDICTION FORMELLE d'utiliser les mots "Premium", "Trial", "Subscription", "Sign up" ou "SaaS". Le Business Model doit être 100% réaliste pour le secteur local (Vente au kilo, Prestation, Acompte, GMS).
31
+ - ANALYSE vs DESCRIPTION : Ne décris pas, analyse. (Ex: Au lieu de 'Délais non respectés', dis 'L'instabilité chronique des délais de livraison artisanaux dégrade l'expérience client et réduit le taux de réachat').
32
+ - Slide 5 (Marché) : visualType = 'PIE_CHART', visualData = { labels: ["TAM (Total)", "SAM (Cible)", "SOM (Capturable)"], values: [1000000, 500000, 50000] }. Utilise le cascade : National > Régional > UEMOA. La source (ex: "Source: ANSD 2024") DOIT ÊTRE EXPLICITEMENT CITÉE en bas du slide dans content ou notes.
33
+ - Slide Équipe (10) : Si des données d'équipe existent, définis IMPERATIVEMENT visualType = 'TEAM' et place le tableau EXACT JSON fourni dans MEMBRES DE L'ÉQUIPE dans visualData.
34
+ - Slide 11 (Finances) : visualType = 'BAR_CHART', visualData = { labels: ["Année 1", "Année 2", "Année 3", "Année 4", "Année 5"], values: [100, 200, 400, 800, 1600] }. Explique la LOGIQUE de croissance financière de manière narrative.
35
+ - STRICTEMENT 3 à 4 blocs de texte par slide. Aucun bloc de moins de 15 mots.
36
+ - DÉTANCHÉITÉ LINGUISTIQUE :
37
+ * Si language === 'FR' : 100% Français de haut niveau (ton institutionnel, banquier d'affaires). ZÉRO mot en Wolof.
38
+ * Si language === 'WOLOF' : 100% Wolof standardisé v4.0. ZÉRO mot en Français.
39
+ - LANGUAGE: {{languageLabel}}.
40
+
41
+ IDENTIFIER: PITCH_DECK (Used for model selection)
packages/prompts/tsconfig.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "extends": "@repo/tsconfig/base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "lib": ["ESNext"],
7
+ "module": "commonjs",
8
+ "moduleResolution": "node",
9
+ "target": "es2017",
10
+ "declaration": true,
11
+ "composite": true,
12
+ "noEmit": false,
13
+ "allowImportingTsExtensions": false,
14
+ "skipLibCheck": true,
15
+ "esModuleInterop": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
pnpm-lock.yaml CHANGED
@@ -91,6 +91,9 @@ importers:
91
  '@repo/database':
92
  specifier: workspace:*
93
  version: link:../../packages/database
 
 
 
94
  '@repo/shared-types':
95
  specifier: workspace:*
96
  version: link:../../packages/shared-types
@@ -276,6 +279,25 @@ importers:
276
  specifier: ^5.0.0
277
  version: 5.22.0
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  packages/shared-types:
280
  dependencies:
281
  zod:
@@ -2207,6 +2229,9 @@ packages:
2207
  fs.realpath@1.0.0:
2208
  resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
2209
 
 
 
 
2210
  fsevents@2.3.3:
2211
  resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
2212
  engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -2307,6 +2332,9 @@ packages:
2307
  resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
2308
  deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
2309
 
 
 
 
2310
  inherits@2.0.4:
2311
  resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
2312
 
@@ -2587,6 +2615,9 @@ packages:
2587
  path-parse@1.0.7:
2588
  resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
2589
 
 
 
 
2590
  pathe@2.0.3:
2591
  resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
2592
 
@@ -2702,6 +2733,10 @@ packages:
2702
  process-warning@5.0.0:
2703
  resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
2704
 
 
 
 
 
2705
  progress@2.0.3:
2706
  resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
2707
  engines: {node: '>=0.4.0'}
@@ -3083,6 +3118,9 @@ packages:
3083
  util-deprecate@1.0.2:
3084
  resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
3085
 
 
 
 
3086
  uuid@11.1.0:
3087
  resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
3088
  hasBin: true
@@ -5427,6 +5465,8 @@ snapshots:
5427
 
5428
  fs.realpath@1.0.0: {}
5429
 
 
 
5430
  fsevents@2.3.3:
5431
  optional: true
5432
 
@@ -5538,6 +5578,8 @@ snapshots:
5538
  once: 1.4.0
5539
  wrappy: 1.0.2
5540
 
 
 
5541
  inherits@2.0.4: {}
5542
 
5543
  ioredis@5.9.2:
@@ -5805,6 +5847,11 @@ snapshots:
5805
 
5806
  path-parse@1.0.7: {}
5807
 
 
 
 
 
 
5808
  pathe@2.0.3: {}
5809
 
5810
  pend@1.2.0: {}
@@ -5931,6 +5978,8 @@ snapshots:
5931
 
5932
  process-warning@5.0.0: {}
5933
 
 
 
5934
  progress@2.0.3: {}
5935
 
5936
  proxy-addr@2.0.7:
@@ -6405,6 +6454,10 @@ snapshots:
6405
 
6406
  util-deprecate@1.0.2: {}
6407
 
 
 
 
 
6408
  uuid@11.1.0: {}
6409
 
6410
  uuid@9.0.1: {}
 
91
  '@repo/database':
92
  specifier: workspace:*
93
  version: link:../../packages/database
94
+ '@repo/prompts':
95
+ specifier: workspace:*
96
+ version: link:../../packages/prompts
97
  '@repo/shared-types':
98
  specifier: workspace:*
99
  version: link:../../packages/shared-types
 
279
  specifier: ^5.0.0
280
  version: 5.22.0
281
 
282
+ packages/prompts:
283
+ dependencies:
284
+ fs:
285
+ specifier: 0.0.1-security
286
+ version: 0.0.1-security
287
+ path:
288
+ specifier: ^0.12.7
289
+ version: 0.12.7
290
+ devDependencies:
291
+ '@repo/tsconfig':
292
+ specifier: workspace:*
293
+ version: link:../tsconfig
294
+ '@types/node':
295
+ specifier: ^20.0.0
296
+ version: 20.19.33
297
+ typescript:
298
+ specifier: ^5.3.3
299
+ version: 5.9.3
300
+
301
  packages/shared-types:
302
  dependencies:
303
  zod:
 
2229
  fs.realpath@1.0.0:
2230
  resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
2231
 
2232
+ fs@0.0.1-security:
2233
+ resolution: {integrity: sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==}
2234
+
2235
  fsevents@2.3.3:
2236
  resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
2237
  engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
 
2332
  resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
2333
  deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
2334
 
2335
+ inherits@2.0.3:
2336
+ resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
2337
+
2338
  inherits@2.0.4:
2339
  resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
2340
 
 
2615
  path-parse@1.0.7:
2616
  resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
2617
 
2618
+ path@0.12.7:
2619
+ resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==}
2620
+
2621
  pathe@2.0.3:
2622
  resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
2623
 
 
2733
  process-warning@5.0.0:
2734
  resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
2735
 
2736
+ process@0.11.10:
2737
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
2738
+ engines: {node: '>= 0.6.0'}
2739
+
2740
  progress@2.0.3:
2741
  resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
2742
  engines: {node: '>=0.4.0'}
 
3118
  util-deprecate@1.0.2:
3119
  resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
3120
 
3121
+ util@0.10.4:
3122
+ resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==}
3123
+
3124
  uuid@11.1.0:
3125
  resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
3126
  hasBin: true
 
5465
 
5466
  fs.realpath@1.0.0: {}
5467
 
5468
+ fs@0.0.1-security: {}
5469
+
5470
  fsevents@2.3.3:
5471
  optional: true
5472
 
 
5578
  once: 1.4.0
5579
  wrappy: 1.0.2
5580
 
5581
+ inherits@2.0.3: {}
5582
+
5583
  inherits@2.0.4: {}
5584
 
5585
  ioredis@5.9.2:
 
5847
 
5848
  path-parse@1.0.7: {}
5849
 
5850
+ path@0.12.7:
5851
+ dependencies:
5852
+ process: 0.11.10
5853
+ util: 0.10.4
5854
+
5855
  pathe@2.0.3: {}
5856
 
5857
  pend@1.2.0: {}
 
5978
 
5979
  process-warning@5.0.0: {}
5980
 
5981
+ process@0.11.10: {}
5982
+
5983
  progress@2.0.3: {}
5984
 
5985
  proxy-addr@2.0.7:
 
6454
 
6455
  util-deprecate@1.0.2: {}
6456
 
6457
+ util@0.10.4:
6458
+ dependencies:
6459
+ inherits: 2.0.3
6460
+
6461
  uuid@11.1.0: {}
6462
 
6463
  uuid@9.0.1: {}