"""Content Generation v2 — Struggle-First Prompt Builder. Builds prompts with style example injection for authentic narrative voice. """ import logging from typing import Optional, Dict, Any from ..style.loader import get_style_prompt_context logger = logging.getLogger(__name__) def build_struggle_first_prompt( brief: Dict[str, Any], style_reference: Optional[str] = None ) -> str: """ Build v2 prompt from struggle-first brief. Args: brief: The content brief with struggle-first structure style_reference: Slug of style example to inject (e.g., "dns-night") Returns: Complete prompt for LLM generation """ # Load style example if provided style_context = "" if style_reference: style_context = get_style_prompt_context(style_reference) if not style_context: logger.warning(f"Style reference '{style_reference}' not found") # Default style guidance if no example if not style_context: style_context = """Style Reference: The Night I Broke DNS - Opening hook: timestamp + location + who was affected - First-person only: "I", "my", "we" — never "you should" - Include at least 2 failed attempts with escalating frustration - Quote someone real (Aundrea said "...") or internal monologue ("I thought...") - Admit one thing wrong: "I was wrong", "I didn't expect", "should have" - Mention the cost: time, sleep, relationship friction - End with honest reflection, not preachy advice""" # Build attempts section attempts_section = "" for i, attempt in enumerate(brief.get("attempts", []), 1): attempts_section += f"\nAttempt {i}: {attempt.get('attempt', '')}" attempts_section += f"\nWhy it failed: {attempt.get('why_failed', '')}\n" prompt = f"""Write a first-person narrative blog post about the following incident. === STYLE EXAMPLE === {style_context} === INCIDENT DETAILS === **What broke:** {brief.get('struggle_angle', '')} **Origin story:** {brief.get('origin_story', '')} **Failed attempts:**{attempts_section} **The moment of realization:** {brief.get('the_moment', '')} **What worked (with caveats):** {brief.get('the_fix', '')} **Honest reflection:** {brief.get('reflection', '')} === WRITING RULES === Voice Requirements (MANDATORY): - [ ] First person only ("I", "my", "we") — NEVER "you should" - [ ] Specific timestamp or location in first paragraph - [ ] At least one quote from a real person or internal monologue - [ ] "I thought... but..." pattern somewhere - [ ] Admit one thing you got wrong - [ ] Mention the cost: time, sleep, relationship friction Structure Requirements: - Hook: timestamp + location + crisis in first 2 sentences - Struggle: build tension through failed attempts - Moment: the realization or break point - Fix: what worked, with explicit caveats/tradeoffs - Reflection: "I learned..." (not "you should...") Forbidden: - Generic setup instructions - "Best practices" without personal context - Tutorial-first structure (story-first, tutorial-second) - Polished conclusions that hide the struggle Target length: {brief.get('target_length', 1500)} words Write the complete blog post now:""" return prompt def build_content_type_prompt( content_type: str, topic: str, brief: Optional[Dict[str, Any]] = None ) -> str: """ Build prompt based on content type. Fallback for non-struggle-first content types. """ if brief and brief.get("struggle_angle"): return build_struggle_first_prompt(brief) # Fallback to original style return f"""Write a {content_type} blog post about {topic}. Tone: Personal, conversational, technical but accessible. Structure: Hook → Problem → Solution → Reflection. Include specific examples and lessons learned. Write the complete post:""" # Prompt templates for specific content types HOMELAB_PAIN_PROMPT = """Write a first-person narrative about a home infrastructure experiment gone wrong. Required elements: - Opening: timestamp + location + who was affected (Aundrea, kids, etc.) - The struggle: at least 2 specific failed attempts - The moment: when you realized what was actually broken - The fix: what worked, with explicit caveats - Reflection: honest admission of what you'd do differently Voice rules: - First person only ("I", "my", "we") - One quote from real person or internal monologue - "I thought... but..." pattern - Admit one thing wrong - Mention cost: time, sleep, relationship friction Forbidden: - Generic setup instructions - "Best practices" without personal context - Tutorial-first structure Target: 1200 words """ AGENT_CHAOS_PROMPT = """Write a first-person narrative about an AI agent experiment or failure. Required elements: - Opening: what you were trying to build - The struggle: at least 2 things that went wrong - The moment: when the agent did something unexpected - The fix: what you changed, with caveats - Reflection: what you learned about agent behavior Voice rules: - First person ("I set up...", "My agent...") - Specific technical details (model names, config values) - "I expected... but..." pattern - Honest about limitations and surprises - Mention: time spent, tokens burned, lessons learned Forbidden: - Hype without substance - Generic "AI is changing everything" conclusions - Tutorial-first structure Target: 1500 words """ SOVEREIGN_CHOICE_PROMPT = """Write a first-person narrative about choosing local/self-hosted over cloud. Required elements: - Opening: the specific moment you decided to go sovereign - The struggle: tradeoffs, limitations, things you gave up - The moment: when the local solution actually saved you - The fix: your current setup, with explicit caveats - Reflection: when cloud still makes sense, honest tradeoffs Voice rules: - First person ("I chose...", "My setup...") - Specific numbers (cost, time, hardware specs) - "I thought... but..." pattern about cloud dependency - Admit when cloud is actually better - Mention: real costs, maintenance burden, family impact Forbidden: - Ideological preaching - "Privacy is priceless" without specifics - Hiding the real tradeoffs Target: 1200 words """