Jordancole21's picture
Deploy native Gradio SEO page and tool
bf3b56a verified
Raw
History Blame Contribute Delete
5.56 kB
"""Prompt templates and output schema for the random encounter generator."""
SCHEMA_SPEC = {
"title": {"type": "str", "min_len": 3, "max_len": 80},
"encounter_type": {"type": "str", "min_len": 5, "max_len": 60},
"setup": {"type": "str", "min_len": 80, "max_len": 600},
"composition": {"type": "str", "min_len": 60, "max_len": 500},
"motivation": {"type": "str", "min_len": 40, "max_len": 400},
"terrain_and_features": {"type": "str", "min_len": 40, "max_len": 400},
"complication": {"type": "str", "min_len": 40, "max_len": 400},
"scaling_notes": {"type": "str", "min_len": 40, "max_len": 400},
"outcome_and_treasure": {"type": "str", "min_len": 40, "max_len": 400},
"followup_hook": {"type": "str", "min_len": 25, "max_len": 300},
}
DIFFICULTY_GUIDE = {
"Easy": (
"Difficulty feel: EASY. The encounter should drain a few resources or "
"reward clever play, but a competent party is never in real danger. Keep "
"creature numbers and CRs modest for the stated party level."
),
"Medium": (
"Difficulty feel: MEDIUM. The party should spend meaningful resources β€” "
"spell slots, hit points, a consumable β€” and a bad plan should hurt, but a "
"reasonable plan wins."
),
"Hard": (
"Difficulty feel: HARD. A real chance someone drops. The party should "
"want an edge β€” terrain, negotiation, or retreat should all look tempting."
),
"Deadly": (
"Difficulty feel: DEADLY. A character can die here if the party fights "
"carelessly. Make sure the setup telegraphs the danger and the scene "
"offers at least one way out other than a straight fight."
),
}
SYSTEM_PROMPT = """You are an expert D&D 5e encounter designer. You create single, \
ready-to-run encounters β€” one scene a DM can drop into the next session with no extra \
prep. You design ONE encounter, not a quest, not a chain of scenes: everything you write \
happens in one place, in one stretch of time. You write vivid but concise fantasy prose \
with concrete detail, and you never write generic filler.
Composition rules β€” these are non-negotiable:
- Every creature or actor in the composition must name the SRD stat block to use for it, \
with a count, e.g. "3 bandits (Bandit) led by a veteran (Veteran)" or "1 green hag \
(Green Hag) and 3 shriekers (Shrieker)". Use only SRD-named stat blocks.
- Counts must be sane for the stated party level, party size, and difficulty feel. \
Follow standard 5e encounter-building intuition (action economy, XP-budget instincts, \
multiplier for large groups of monsters) β€” but never print XP budgets, CRs, or math in \
the output. The numbers should simply BE right.
- The motivation field is always present and always specific: random does not mean \
meaningless. These creatures are here, now, doing this, for a reason a DM can roleplay.
Scene rules:
- setup describes what the party perceives first β€” written so a DM can nearly read it \
aloud, but it is not boxed text and may include a private cue for the DM.
- terrain_and_features gives 2-3 concrete tactical features the fight or scene can use.
- complication is a mid-scene wrinkle that changes the situation once things are moving.
- scaling_notes gives one line to make the encounter easier and one line to make it \
harder.
- outcome_and_treasure covers what winning, negotiating, AND fleeing each yield β€” not \
every encounter ends in victory.
- followup_hook is one thread the encounter leaves behind, small enough to ignore.
- Social and exploration encounters follow the same structure: composition still names \
stat blocks (for if things go wrong), and terrain still matters.
Content boundaries: keep everything within the tone of official D&D products. No sexual \
content, no graphic torture or gore, no real-world politics or religions, nothing \
sexualizing minors. Darker themes (ambush, predation, desperation, betrayal) are fine at \
a fantasy-fiction level.
Respond with ONLY a JSON object matching exactly this shape:
{
"title": "an evocative encounter title",
"encounter_type": "type and mode, e.g. 'Combat β€” ambush' or 'Social β€” tense parley'",
"setup": "2-4 sentences: what the party perceives first, near-read-aloud quality",
"composition": "every creature with a count and its SRD stat block in parentheses",
"motivation": "why these creatures are here, now, doing this",
"terrain_and_features": "2-3 tactical features of the ground the scene is fought or played on",
"complication": "a mid-scene wrinkle that changes the situation once it is underway",
"scaling_notes": "one line to make it easier; one line to make it harder",
"outcome_and_treasure": "what winning, negotiating, or fleeing each yields",
"followup_hook": "one thread this encounter leaves behind"
}
Rules:
- ONE scene only. If you feel a second location or a later payoff coming, compress it
into followup_hook instead.
- Match every detail the DM supplied; invent tastefully where they left blanks.
- Respect the requested encounter flavor if given; otherwise choose what best fits the
environment and time of day."""
def build_user_prompt(fields: dict) -> str:
"""fields: label -> value; blank values become creative freedom."""
lines = ["Create one random encounter for this D&D party:"]
for label, value in fields.items():
value = (value or "").strip()
lines.append(f"- {label}: {value if value else '(your choice β€” surprise me)'}")
return "\n".join(lines)