diff --git a/src/slidedeckai/agents/content_generator.py b/src/slidedeckai/agents/content_generator.py index 3394bb4..a7cf0fa 100644 --- a/src/slidedeckai/agents/content_generator.py +++ b/src/slidedeckai/agents/content_generator.py @@ -61,9 +61,10 @@ def generate_subtitle(self, slide_title: str, purpose: str, return "Analysis" def generate_bullets(self, slide_title: str, purpose: str, - search_facts: List[str], max_bullets: int = 5) -> List[str]: + search_facts: List[str], max_bullets: int = 5, + max_words_per_bullet: int = 15) -> List[str]: """ - Generate bullet points from search facts + Generate bullet points from search facts with strict length control """ facts_text = "\n".join(search_facts) if search_facts else "No data available" @@ -78,7 +79,7 @@ def generate_bullets(self, slide_title: str, purpose: str, Requirements: - Generate EXACTLY {max_bullets} bullet points -- Each bullet: 10-20 words +- Each bullet MUST be under {max_words_per_bullet} words to fit layout - Include QUANTITATIVE data (numbers, percentages) - Professional, executive-level tone - NO preamble, ONLY bullet points diff --git a/src/slidedeckai/agents/execution_orchestrator.py b/src/slidedeckai/agents/execution_orchestrator.py index b7da58d..44cf204 100644 --- a/src/slidedeckai/agents/execution_orchestrator.py +++ b/src/slidedeckai/agents/execution_orchestrator.py @@ -278,11 +278,18 @@ def _gen_for_ph(ph_id, ph_info): ) return (ph_id, {'type': 'kpi', 'kpi_data': kpi}) else: + max_bullets = self._calculate_max_bullets(ph_info.get('area', 5)) + max_words = self._calculate_word_limit( + ph_info.get('width', 5), + ph_info.get('height', 5), + max_bullets + ) bullets = self.content_generator.generate_bullets( section.section_title, section.section_purpose, relevant_facts, - max_bullets=self._calculate_max_bullets(ph_info.get('area', 5)) + max_bullets=max_bullets, + max_words_per_bullet=max_words ) return (ph_id, {'type': 'bullets', 'bullets': bullets}) except Exception as e: @@ -1037,13 +1044,19 @@ def _fill_content(self, placeholder, ph_id: int, ph_info: Dict, if query.query in search_results: relevant_facts.extend(search_results[query.query]) - max_bullets = self._calculate_max_bullets(ph_info['area']) + max_bullets = self._calculate_max_bullets(ph_info.get('area', 5)) + max_words = self._calculate_word_limit( + ph_info.get('width', 0), + ph_info.get('height', 0), + max_bullets + ) bullets = self.content_generator.generate_bullets( section.section_title, section.section_purpose, relevant_facts, - max_bullets=max_bullets + max_bullets=max_bullets, + max_words_per_bullet=max_words ) text_frame = placeholder.text_frame @@ -1089,6 +1102,21 @@ def _calculate_max_bullets(self, area: float) -> int: else: return 10 + def _calculate_word_limit(self, width: float, height: float, max_bullets: int) -> int: + """Calculate max words per bullet to fit in placeholder""" + if height <= 0 or width <= 0 or max_bullets <= 0: + return 15 + + # Estimate based on standard 18pt font (~0.3 inch line height) + lines_available = height / 0.3 + lines_per_bullet = lines_available / max_bullets + + # Estimate words per line (width * 8 chars/inch / 6 chars/word) + words_per_line = (width * 8) / 6 + + limit = int(lines_per_bullet * words_per_line) + return max(5, min(limit, 40)) # Clamp between 5 and 40 + def _calculate_font_size_from_area(self, area: float, size_type: str) -> int: """FIX #4: Calculate from template base size""" from pptx.util import Pt