"""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
"""