165 lines
5.2 KiB
Python
165 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
engine.py — The Chaos Game Engine
|
|
|
|
Thin coordinator that owns the GameEngine class. All heavy lifting is
|
|
delegated to sub-modules: paths, models, prompts, config, context,
|
|
state, tools_handler, llm, validation, parsing, strategies.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from engine_lib.paths import CONFIG_PATH
|
|
from engine_lib.models import GenerationResult, TurnResult
|
|
from engine_lib import config
|
|
from engine_lib import strategies
|
|
|
|
|
|
class GameEngine:
|
|
"""Owns configuration and delegates generation to standalone strategies."""
|
|
|
|
def __init__(self, session_dir: str | Path | None = None):
|
|
from engine_lib.paths import SESSION_DIR
|
|
self.session_dir = Path(session_dir) if session_dir else SESSION_DIR
|
|
self.config = config.load_config(CONFIG_PATH)
|
|
|
|
# ── Config accessors ────────────────────────────────────────────────
|
|
|
|
@property
|
|
def model(self) -> str:
|
|
return config.get_model(self.config)
|
|
|
|
@property
|
|
def api_key(self) -> str | None:
|
|
return config.get_api_key(self.config)
|
|
|
|
@property
|
|
def api_base(self) -> str | None:
|
|
return config.get_api_base(self.config)
|
|
|
|
@property
|
|
def temperature(self) -> float:
|
|
return config.get_temperature(self.config)
|
|
|
|
@property
|
|
def max_tokens(self) -> int:
|
|
return config.get_max_tokens(self.config)
|
|
|
|
@property
|
|
def timeout(self) -> int:
|
|
return config.get_timeout(self.config)
|
|
|
|
# ── Generation (delegated) ──────────────────────────────────────────
|
|
|
|
def generate(
|
|
self,
|
|
player_action: str | None = None,
|
|
last_narrative: str | None = None,
|
|
) -> GenerationResult:
|
|
return strategies.generate(
|
|
player_action=player_action,
|
|
last_narrative=last_narrative,
|
|
model=self.model,
|
|
temperature=self.temperature,
|
|
timeout=self.timeout,
|
|
max_tokens=self.max_tokens,
|
|
api_key=self.api_key,
|
|
api_base=self.api_base,
|
|
)
|
|
|
|
def generate_stream(self, player_action=None, last_narrative=None):
|
|
yield from strategies.generate_stream(
|
|
player_action=player_action,
|
|
last_narrative=last_narrative,
|
|
model=self.model,
|
|
temperature=self.temperature,
|
|
timeout=self.timeout,
|
|
max_tokens=self.max_tokens,
|
|
api_key=self.api_key,
|
|
api_base=self.api_base,
|
|
)
|
|
|
|
def generate_with_tools(
|
|
self,
|
|
player_action: str | None = None,
|
|
last_prompt: str | None = None,
|
|
on_thought: callable = None,
|
|
on_action: callable = None,
|
|
on_player_roll: callable = None,
|
|
on_debug: callable = None,
|
|
) -> TurnResult:
|
|
return strategies.generate_with_tools(
|
|
player_action=player_action,
|
|
last_prompt=last_prompt,
|
|
on_thought=on_thought,
|
|
on_action=on_action,
|
|
on_player_roll=on_player_roll,
|
|
on_debug=on_debug,
|
|
model=self.model,
|
|
temperature=self.temperature,
|
|
timeout=self.timeout,
|
|
max_tokens=self.max_tokens,
|
|
api_key=self.api_key,
|
|
api_base=self.api_base,
|
|
)
|
|
|
|
def generate_with_tools_single(
|
|
self,
|
|
player_action: str | None = None,
|
|
last_prompt: str | None = None,
|
|
on_thought: callable = None,
|
|
on_action: callable = None,
|
|
on_player_roll: callable = None,
|
|
on_debug: callable = None,
|
|
) -> TurnResult:
|
|
return strategies.generate_with_tools_single(
|
|
player_action=player_action,
|
|
last_prompt=last_prompt,
|
|
on_thought=on_thought,
|
|
on_action=on_action,
|
|
on_player_roll=on_player_roll,
|
|
on_debug=on_debug,
|
|
model=self.model,
|
|
temperature=self.temperature,
|
|
timeout=self.timeout,
|
|
max_tokens=self.max_tokens,
|
|
api_key=self.api_key,
|
|
api_base=self.api_base,
|
|
)
|
|
|
|
|
|
# ── CLI entry point (for testing) ─────────────────────────────────────────
|
|
def main():
|
|
"""Generate a turn from the command line (debug/testing)."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="The Chaos Game Engine (CLI)")
|
|
parser.add_argument("--action", "-a", help="Player action text")
|
|
parser.add_argument("--last", "-l", help="Last narrative text")
|
|
args = parser.parse_args()
|
|
|
|
engine = GameEngine()
|
|
result = engine.generate_with_tools_single(
|
|
player_action=args.action,
|
|
last_prompt=args.last,
|
|
)
|
|
|
|
if result.error:
|
|
print(f"ERROR: {result.error}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print(result.book_log)
|
|
if result.user_prompt:
|
|
print(f"\n{result.user_prompt}")
|
|
if result.log_entry:
|
|
print(f"\n[Log] {result.log_entry}")
|
|
if result.ambience:
|
|
print(f"[Ambience] {result.ambience}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|