From dec6211f7cdfcabccf0caa6efed111cd2cc09c77 Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Sun, 5 Apr 2026 02:23:34 -0600 Subject: [PATCH] Add batch prompt builder for caller_gen --- backend/services/caller_gen.py | 43 ++++++++++++++++++++++++++++++++++ tests/test_caller_gen.py | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/backend/services/caller_gen.py b/backend/services/caller_gen.py index b51e044..5f10dfc 100644 --- a/backend/services/caller_gen.py +++ b/backend/services/caller_gen.py @@ -45,3 +45,46 @@ def resolve_voice(suggestion: str, roster: list[str]) -> str: return roster[0] if roster else "" lower_map = {v.lower(): v for v in roster} return lower_map.get(suggestion.lower(), roster[0]) + + +BATCH_SYSTEM_PROMPT = """You are writing a roster of callers for Luke's late-night radio show in New Mexico. + +CREATIVE RANGE: Your callers must span the emotional range of Howard Stern (chaos, strong characters), Coast to Coast AM (earnest weirdos, sincere believers), Loveline (real problems, real advice-seeking), Delilah (emotional vulnerability, connection), and Opie and Anthony (sharp, irreverent, specific people). + +Maximum character distance between callers. No two callers should feel like siblings. Do not default to sitcom plots. Real humans are specific and strange. Give each caller details that could only belong to them. + +You will output strict JSON with a "callers" array. Each caller has exactly these fields: name, age, voice_suggestion, location, identity, situation, reason_calling, opening_line, secret_want, specific_details (array of 2-3 strings), emotional_register.""" + + +def build_batch_prompt(ctx: dict) -> str: + lines = [ + f"Tonight is {ctx['date']}. {ctx['weather']}.", + "", + "Today's news headlines (ground callers in real context, but do not force topicality):", + ] + for h in ctx["headlines"]: + lines.append(f"- {h}") + lines.append("") + + if ctx["recent_caller_summaries"]: + lines.append("Recent callers (DO NOT repeat these archetypes or situations):") + for s in ctx["recent_caller_summaries"]: + lines.append(f"- {s}") + lines.append("") + + if ctx["regulars_included"]: + lines.append("RECURRING CHARACTERS IN TONIGHT'S LINEUP:") + lines.append("") + for r in ctx["regulars_included"]: + lines.append(f"### {r['name']}") + lines.append(r["lore"]) + lines.append(f"Current arc state: {r['arc_state']}") + lines.append("") + lines.append(f"For {r['name']}: invent a fresh reason he is calling tonight — a new development, grievance, or specific recent event. DO NOT alter his voice, personality, or core traits. Write a new scene for an existing character.") + lines.append("") + + lines.append(f"Available voices (voice_suggestion must match one of these exactly):") + lines.append(", ".join(ctx["voice_roster"])) + lines.append("") + lines.append(f"Generate {ctx['caller_count']} callers. Output JSON only, no prose.") + return BATCH_SYSTEM_PROMPT + "\n\n" + "\n".join(lines) diff --git a/tests/test_caller_gen.py b/tests/test_caller_gen.py index 87a08ee..aba1957 100644 --- a/tests/test_caller_gen.py +++ b/tests/test_caller_gen.py @@ -59,3 +59,45 @@ def test_resolve_voice_falls_back_when_no_match(): def test_resolve_voice_empty_suggestion_falls_back(): from backend.services.caller_gen import resolve_voice assert resolve_voice("", ["Marcus"]) == "Marcus" + + +def test_build_batch_prompt_includes_context(): + from backend.services.caller_gen import build_batch_prompt + ctx = { + "date": "Saturday, April 5, 2026", + "weather": "cool desert night, 48°F", + "headlines": ["New Mexico legislature approves water bill"], + "recent_caller_summaries": ["Jerry called about his neighbor's goat"], + "regulars_included": [], + "caller_count": 12, + "voice_roster": ["Marcus", "Dennis", "Priya"], + } + prompt = build_batch_prompt(ctx) + assert "Saturday, April 5, 2026" in prompt + assert "water bill" in prompt + assert "Jerry called about his neighbor's goat" in prompt + assert "12 callers" in prompt + assert "Marcus" in prompt # voice roster listed + assert "Stern" in prompt + assert "Coast to Coast" in prompt + assert "Loveline" in prompt + assert "Delilah" in prompt + assert "Opie and Anthony" in prompt + + +def test_build_batch_prompt_includes_silas_lore_when_present(): + from backend.services.caller_gen import build_batch_prompt + ctx = { + "date": "...", + "weather": "...", + "headlines": [], + "recent_caller_summaries": [], + "regulars_included": [{"name": "Silas", "lore": "Silas leads a small desert cult...", "arc_state": "seeking new members"}], + "caller_count": 12, + "voice_roster": ["Marcus"], + } + prompt = build_batch_prompt(ctx) + assert "Silas" in prompt + assert "desert cult" in prompt + assert "seeking new members" in prompt + assert "DO NOT alter his voice, personality, or core traits" in prompt