📄 style_loader.py 4,785 bytes Apr 22, 2026 📋 Raw

"""Style reference loader for Content Pipeline v2.

Loads JSON style examples from shared/project-docs/blog/style-examples/.
These examples teach the LLM the target voice and structure.
"""

import json
import os
from pathlib import Path
from typing import Optional

STYLE_EXAMPLES_DIR = Path("/home/hoffmann_admin/.openclaw/shared/project-docs/blog/style-examples")

def load_style_example(slug: str) -> Optional[dict]:
"""Load a style example by slug.

Looks for {slug}.json in the style-examples directory.
Returns None if not found.
"""
path = STYLE_EXAMPLES_DIR / f"{slug}.json"
if not path.exists():
    return None

try:
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)
except (json.JSONDecodeError, IOError):
    return None

def list_style_examples() -> list[dict]:
"""List all available style examples.

Returns list of {slug, title, excerpt} dicts.
"""
if not STYLE_EXAMPLES_DIR.exists():
    return []

examples = []
for path in sorted(STYLE_EXAMPLES_DIR.glob("*.json")):
    try:
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)
            examples.append({
                "slug": data.get("slug", path.stem),
                "title": data.get("title", path.stem),
                "excerpt": data.get("excerpt", "")[:200],
            })
    except (json.JSONDecodeError, IOError):
        continue

return examples

def build_style_prompt(style_reference: Optional[str]) -> str:
"""Build a style injection prompt from a style reference.

If style_reference is None or not found, returns a generic prompt.
"""
if not style_reference:
    return _default_style_prompt()

example = load_style_example(style_reference)
if not example:
    return _default_style_prompt()

# Extract voice markers
voice = example.get("voice_markers", {})
structure = example.get("structure", {})

prompt_parts = [
    "## Voice and Style Guide (from reference: {title})",
    "",
    "Write in first person. Use 'I' statements throughout.",
    "Include specific timestamps and temporal markers (2 AM, yesterday, etc.)",
    "Describe struggle honestly — what broke, what you tried, why it failed.",
    "Mention the real cost: sleep lost, family disrupted, frustration felt.",
    "End with reflection — what you learned, what you'd do differently.",
    "",
    "## Structural Template",
    "",
]

if "hook" in structure:
    prompt_parts.append(f"1. Hook: {structure['hook'].get('type', 'timestamp_location_crisis')}")
if "struggle" in structure:
    s = structure["struggle"]
    prompt_parts.append(f"2. Struggle: {s.get('attempts', 2)} failed attempts, escalating tension")
if "moment" in structure:
    prompt_parts.append(f"3. The Moment: {structure['moment'].get('realization', 'The realization')}")
if "fix" in structure:
    f = structure["fix"]
    prompt_parts.append(f"4. The Fix: {f.get('solution', 'The solution')} (note caveats: {f.get('caveats', 'none')})")
if "reflection" in structure:
    prompt_parts.append(f"5. Reflection: {structure['reflection'].get('lesson', 'The lesson learned')}")

prompt_parts.extend([
    "",
    "## Voice Markers to Emulate",
    "",
    f"- I-statements: ~{voice.get('i_statements', 8)} per article",
    f"- Direct quotes: {', '.join(voice.get('quotes', ['Aundrea said...']))}",
    f"- Cost mentions: {', '.join(voice.get('cost_mentions', ['sleep', 'time']))}",
    "",
])

return "\n".join(prompt_parts).format(title=example.get("title", "Reference"))

def _default_style_prompt() -> str:
"""Default style prompt when no reference is available."""
return """## Voice and Style Guide

Write in first person. Use 'I' statements throughout.
Include specific timestamps and temporal markers.
Describe struggle honestly — what broke, what you tried, why it failed.
Mention the real cost: sleep lost, family disrupted, frustration felt.
End with reflection — what you learned, what you'd do differently.

Structural Template

  1. Hook: Start with a specific moment of crisis (timestamp + location)
  2. Struggle: 2-3 failed attempts, escalating tension
  3. The Moment: The realization or breakthrough
  4. The Fix: The solution, with honest caveats
  5. Reflection: What you learned, what you'd do differently
    """

def ensure_style_examples_dir():
"""Ensure the style examples directory exists."""
STYLE_EXAMPLES_DIR.mkdir(parents=True, exist_ok=True)

Initialize on import

ensure_style_examples_dir()