#!/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)