fix: nombre slides proportionnel aux vrais mots de la note
- stripNoteMarkdown() : supprime #, **, URLs, métadonnées (Connection to seed, Novelty score, Origin...), frontmatter, code blocks - countNoteWords() : compte les mots sémantiques réels - slideLimit() : <50 mots → 3 slides, 50-150 → 4, 150-350 → 6, >350 → 8 - Le prompt injecte la règle dynamique avec priorité absolue (⚠️) - Suppression des 3 occurrences hardcodées '6-12 slides' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -945,7 +945,7 @@ Appelle generate_slides avec un objet JSON structuré :
|
||||
12. "summary" → { type:"summary", title:"...", items:["Point clé 1", "Point clé 2", ...] }
|
||||
|
||||
═══ RÈGLES ═══
|
||||
- 6 à 12 slides par présentation
|
||||
- Nombre de slides : adapté au contenu réel de la note (la règle ⚠️ dans le message utilisateur est ABSOLUE)
|
||||
- Slide 1 OBLIGATOIREMENT type "title"
|
||||
- Dernière slide OBLIGATOIREMENT type "summary"
|
||||
- Au moins 1 slide "chart" ou "stats" si des chiffres existent dans la note
|
||||
@@ -989,7 +989,7 @@ Call generate_slides with a structured JSON object:
|
||||
12. "summary" → { type:"summary", title:"...", items:["Key point 1", "Key point 2", ...] }
|
||||
|
||||
═══ RULES ═══
|
||||
- 6 to 12 slides per presentation
|
||||
- Slide count: adapt to the real content (the ⚠️ rule in the user message is ABSOLUTE)
|
||||
- Slide 1 MUST be type "title"
|
||||
- Last slide MUST be type "summary"
|
||||
- At least 1 "chart" or "stats" slide if numbers exist in the note
|
||||
@@ -1027,6 +1027,49 @@ You MUST use the task_extract tool. Do NOT respond with text, call the tool dire
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip markdown syntax, URLs, metadata blocks, and frontmatter from note content.
|
||||
* Returns only the semantic plain text, used to count real words before generating slides.
|
||||
*/
|
||||
function stripNoteMarkdown(raw: string): string {
|
||||
return raw
|
||||
// Remove frontmatter / YAML blocks
|
||||
.replace(/^---[\s\S]*?---/m, '')
|
||||
// Remove markdown headers
|
||||
.replace(/^#{1,6}\s+/gm, '')
|
||||
// Remove bold/italic
|
||||
.replace(/[*_]{1,3}([^*_]+)[*_]{1,3}/g, '$1')
|
||||
// Remove inline links — keep label only
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
||||
// Remove bare URLs
|
||||
.replace(/https?:\/\/\S+/g, '')
|
||||
// Remove metadata lines (Connection to seed, Novelty score, Source brainstorm, Origin, Derived from, etc.)
|
||||
.replace(/^\*{0,2}(Connection to seed|Novelty score|Source brainstorm|Source note|Origin|Derived from|## Origin)[^:\n]*:?.*$/gim, '')
|
||||
// Remove horizontal rules
|
||||
.replace(/^---+$/gm, '')
|
||||
// Remove blockquote markers
|
||||
.replace(/^>\s*/gm, '')
|
||||
// Remove code blocks
|
||||
.replace(/```[\s\S]*?```/g, '')
|
||||
.replace(/`[^`]+`/g, '')
|
||||
// Collapse whitespace
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
}
|
||||
|
||||
/** Count real semantic words in a note */
|
||||
function countNoteWords(raw: string): number {
|
||||
return stripNoteMarkdown(raw).split(/\s+/).filter(Boolean).length
|
||||
}
|
||||
|
||||
/** Return max slides based on real word count */
|
||||
function slideLimit(wordCount: number): { max: number; label: string } {
|
||||
if (wordCount < 50) return { max: 3, label: '3 slides MAX (note très courte)' }
|
||||
if (wordCount < 150) return { max: 4, label: '4 slides MAX (note courte)' }
|
||||
if (wordCount < 350) return { max: 6, label: '6 slides MAX (note moyenne)' }
|
||||
return { max: 8, label: '8 slides MAX (note longue)' }
|
||||
}
|
||||
|
||||
function extractJsonFromText(text: string): any {
|
||||
if (!text) return null
|
||||
|
||||
@@ -1274,7 +1317,16 @@ async function executeToolUseAgent(
|
||||
const notesContext = notes.map(n =>
|
||||
`### ${n.title || untitled} (${n.createdAt.toLocaleDateString(dateLocale)})\n${n.content.substring(0, 2000)}`
|
||||
).join('\n\n')
|
||||
|
||||
// Compute real word count (stripped of markdown/metadata) to determine slide limit
|
||||
const totalWords = notes.reduce((sum, n) => sum + countNoteWords(n.content || ''), 0)
|
||||
const { max: maxSlides, label: slideRule } = slideLimit(totalWords)
|
||||
|
||||
prompt += `\n\n${lang === 'fr' ? 'Notes source' : 'Source notes'}:\n\n${notesContext}`
|
||||
prompt += `\n\n${lang === 'fr'
|
||||
? `⚠️ RÈGLE ABSOLUE : Cette note fait ${totalWords} mots réels (hors markdown/URLs/métadonnées). Tu DOIS générer EXACTEMENT ${slideRule}. Respecter cette limite est PLUS IMPORTANT que la diversité ou la densité.`
|
||||
: `⚠️ ABSOLUTE RULE: This note has ${totalWords} real words (excluding markdown/URLs/metadata). You MUST generate EXACTLY ${slideRule}. This limit is MORE IMPORTANT than diversity or density.`
|
||||
}`
|
||||
}
|
||||
|
||||
// ── Executive Template Structure (HTML-compatible) ──
|
||||
@@ -1317,8 +1369,8 @@ async function executeToolUseAgent(
|
||||
: `\n\nMANDATORY DENSITY: Every slide must have RICH CONTENT. Minimum 5 items per "bullets" slide (≥15 words each). Minimum 4 cards when using "cards". Minimum 4 data points for "chart". You MUST include at least 1 "chart" slide with REAL data from the notes. If no numbers in notes, create a maturity radar or qualitative comparison scored out of 5.`
|
||||
|
||||
prompt += `\n\n${lang === 'fr'
|
||||
? 'IMPORTANT : Appelle OBLIGATOIREMENT generate_slides avec le JSON structuré {title, theme, slides:[...]}. Ne réponds JAMAIS avec du texte brut. 6-12 slides variées.'
|
||||
: 'IMPORTANT: You MUST call generate_slides with structured JSON {title, theme, slides:[...]}. NEVER respond with plain text. 6-12 varied slides.'}`
|
||||
? 'IMPORTANT : Appelle OBLIGATOIREMENT generate_slides avec le JSON structuré {title, theme, slides:[...]}. Ne réponds JAMAIS avec du texte brut. Respecte ABSOLUMENT la limite de slides indiquée.'
|
||||
: 'IMPORTANT: You MUST call generate_slides with structured JSON {title, theme, slides:[...]}. NEVER respond with plain text. STRICTLY respect the slide count limit indicated.'}`
|
||||
|
||||
if (agent.slideTheme && agent.slideTheme !== 'auto') {
|
||||
prompt += `\n${lang === 'fr'
|
||||
|
||||
Reference in New Issue
Block a user