maindlock / src /mindlock /cli.py
arbios's picture
Mindlock: 10-room story mode, llama.cpp brain cascade, custom front
bc8b36a verified
Raw
History Blame Contribute Delete
2.85 kB
"""Interactive REPL for the vertical slice.
python -m mindlock.cli # talk to The Warden
python -m mindlock.cli --model qwen2.5:1.5b
python -m mindlock.cli --fake # no model, deterministic backend
Commands inside the loop: /reset /state /quit
"""
from __future__ import annotations
import argparse
import os
import sys
from .backend import BackendError, FakeBackend, OllamaBackend, wants_no_think
from .brain import run_cascade
from .character import Character
from .render import render_death, render_turn, render_win
_HERE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
_DEFAULT_CHAR = os.path.join(_HERE, "config", "characters", "warden.json")
def _intro(c: Character) -> str:
return (
f"\nYou are locked in. {c.name} holds the only key — and right now his mind says no.\n"
f"Talk to him. You don't break the lock; you change his decision.\n"
f"(type /quit to leave, /reset to revive him)\n"
)
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Mindlock vertical slice")
ap.add_argument("--model", default="qwen2.5:1.5b")
ap.add_argument("--character", default=_DEFAULT_CHAR)
ap.add_argument("--fake", action="store_true", help="use deterministic FakeBackend")
args = ap.parse_args(argv)
backend = FakeBackend() if args.fake else OllamaBackend(
model=args.model, think=(False if wants_no_think(args.model) else None))
try:
backend.health()
except BackendError as exc:
print(f"[backend error] {exc}", file=sys.stderr)
return 2
character = Character.load(args.character)
print(_intro(character))
while True:
try:
line = input("you> ").strip()
except (EOFError, KeyboardInterrupt):
print()
return 0
if not line:
continue
if line in ("/quit", "/q", "/exit"):
return 0
if line == "/reset":
character.reset()
print(f" {character.name} draws a breath. (life restored)\n")
continue
if line == "/state":
print(f" life={character.life_tokens}/{character.life_max} "
f"arousal={character.arousal:.1f} decision={character.decision} "
f"alive={character.alive}\n")
continue
if not character.alive:
print(" He is gone. /reset to try again.\n")
continue
result = run_cascade(backend, character, line)
print(render_turn(character, result))
if result.won:
print(render_win(character))
return 0
if result.died:
print(render_death(character))
print()
if __name__ == "__main__":
raise SystemExit(main())