75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
context.py — System prompt and user message assembly for The Chaos engine.
|
|
|
|
All functions are standalone — no dependency on GameEngine.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from .paths import CHAR_PATH, WORLD_PATH, BOOK_PATH
|
|
from .prompts import SYSTEM_PROMPT
|
|
from . import state
|
|
|
|
|
|
def build_system_prompt() -> str:
|
|
"""Assemble the system prompt with current game state."""
|
|
char = state.read_file(CHAR_PATH) or "*No character sheet.*"
|
|
world = state.truncate_world(state.read_file(WORLD_PATH) or "") or "*No world state.*"
|
|
log = state.read_recent_log()
|
|
story = state.read_recent_book()
|
|
return SYSTEM_PROMPT.substitute(
|
|
character=char, world=world, log=log, story=story
|
|
)
|
|
|
|
|
|
def build_prose_prompt() -> str:
|
|
"""Assemble the prose-generation prompt with current game state."""
|
|
from .prompts import PROSE_PROMPT
|
|
char = state.read_file(CHAR_PATH) or "*No character sheet.*"
|
|
world = state.truncate_world(state.read_file(WORLD_PATH) or "") or "*No world state.*"
|
|
log = state.read_recent_log()
|
|
story = state.read_recent_book()
|
|
return PROSE_PROMPT.substitute(
|
|
character=char, world=world, log=log, story=story
|
|
)
|
|
|
|
|
|
def build_user_message(
|
|
player_action: str | None = None,
|
|
last_prompt: str | None = None,
|
|
**kwargs: str | None,
|
|
) -> str:
|
|
"""Build the user message for this turn's LLM call."""
|
|
if kwargs:
|
|
raise TypeError(
|
|
f"build_user_message() got unexpected keyword arguments: "
|
|
f"{set(kwargs)}. Did you mean 'last_prompt' instead of one of these?"
|
|
)
|
|
parts = []
|
|
|
|
if last_prompt:
|
|
parts.append(f"## Situation\n{last_prompt}")
|
|
if player_action:
|
|
parts.append(f"## Player's Request\n{player_action}")
|
|
|
|
has_existing_story = bool(
|
|
state.read_file(BOOK_PATH).strip()
|
|
) if not last_prompt else True
|
|
|
|
if not player_action and not last_prompt:
|
|
if has_existing_story:
|
|
raise RuntimeError("User action is required for every turn.")
|
|
else:
|
|
parts.append(
|
|
"## Instructions\n"
|
|
"This is a new story. Welcome the player and guide them through the game setup."
|
|
)
|
|
else:
|
|
parts.append(
|
|
"## Instructions\n"
|
|
"Advance the story based on the player's request. "
|
|
"All state is shown above — write the outcome directly."
|
|
)
|
|
return "\n\n".join(parts)
|