Spaces:
Sleeping
Sleeping
| """μΉ΄λ₯΄λ°λΌ μ±ν°1 νλ μ΄ CLI β λ¦¬λ© λ£¨ν + κ°μ ν¬μΌ (M4 ν΅ν©). | |
| μ¬μ©λ²: | |
| .venv/bin/python scripts/play.py # λνν (ν¬μΌμ LLM ν€ μμΌλ©΄ μ±ν ) | |
| .venv/bin/python scripts/play.py --policy trust --quiet # μλ νλ μ΄, μλ© νμΈ | |
| .venv/bin/python scripts/play.py --chat # ν¬μΌ μμ μ±ν κ°μ (OPENAI_API_KEY νμ) | |
| .venv/bin/python scripts/play.py --no-chat # ν¬μΌ μ±ν λκΈ° (μ νμ§λ§) | |
| ν¬μΌ μ±ν : μ νμ§ μΉ΄λμμ λ²νΈ λμ μμ ν μ€νΈλ₯Ό μ λ ₯νλ©΄ μΉ΄λ₯΄λ°λΌμ λνκ° μ΄λ¦¬κ³ , | |
| director μλ ΄(λλ ν΄ μν) ν μ νμ§λ‘ 볡κ·νλ€. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import asyncio | |
| import os | |
| import sys | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from engine.repositories.content import ContentRepository # noqa: E402 | |
| from engine.services.story import StepResult, StoryEngine # noqa: E402 | |
| DIM = "\033[2m" | |
| BOLD = "\033[1m" | |
| RESET = "\033[0m" | |
| def render(step: StepResult, quiet: bool): | |
| if quiet or step.card is None: | |
| return | |
| c = step.card | |
| print(f"\n{BOLD}ββ {c.id} Β· {c.title} ββ{RESET}") | |
| for b in step.narration: | |
| if b.type == "dialogue_center": | |
| print(f"\n{BOLD} β {b.text} β{RESET}\n") | |
| elif b.type == "character_dialogue": | |
| print(f' {b.speaker}: "{b.text}"') | |
| elif b.type == "stage_direction": | |
| print(f"{DIM} ({b.text}){RESET}") | |
| else: | |
| print("\n" + b.text) | |
| async def pocket_chat(pocket_service, engine: StoryEngine, card_id: str, first_line: str): | |
| """κ°μ ν¬μΌ μμ μ±ν β μλ ΄/ν΄ μνκΉμ§ λκ³ μ¨μ μνλ₯Ό 컀λ°νλ€.""" | |
| sid = await pocket_service.open(card_id, engine.state) | |
| line = first_line | |
| while True: | |
| turn = await pocket_service.run_turn(sid, line) | |
| print(f"\n {BOLD}μΉ΄λ₯΄λ°λΌ{RESET}: {turn.reply}") | |
| if turn.converged: | |
| print(f"{DIM} (μ₯λ©΄μ΄ μ‘°μ©ν λ€μμΌλ‘ κΈ°μ΄λ€ β {turn.reason}){RESET}") | |
| break | |
| line = input("λ(λ‘λΌ) > ").strip() | |
| if not line: | |
| break | |
| log = await pocket_service.close(sid, engine.state) | |
| return log | |
| def pick_or_chat(step: StepResult, policy: str, chat_enabled: bool): | |
| """μ νμ§ μ λ ₯ λλ μμ ν μ€νΈ(ν¬μΌ μ±ν μ§μ ). λ°ν: ('choice', idx) | ('chat', text).""" | |
| choices = step.choices | |
| if policy != "ask": | |
| for i, ch in enumerate(choices): | |
| if ch.axis == policy: | |
| return "choice", i | |
| for i, ch in enumerate(choices): | |
| if ch.axis in (None, "neutral"): | |
| return "choice", i | |
| return "choice", 0 | |
| for i, ch in enumerate(choices): | |
| print(f" [{i + 1}] {ch.label}") | |
| if chat_enabled: | |
| print(f"{DIM} (λ²νΈ λμ νκ³ μΆμ λ§μ μ°λ©΄ μΉ΄λ₯΄λ°λΌμ λνν©λλ€){RESET}") | |
| while True: | |
| raw = input("μ ν > ").strip() | |
| if raw.isdigit() and 1 <= int(raw) <= len(choices): | |
| return "choice", int(raw) - 1 | |
| if raw and chat_enabled: | |
| return "chat", raw | |
| if raw and not chat_enabled: | |
| print(f"{DIM} (ν¬μΌ μ±ν κΊΌμ§ β λ²νΈλ₯Ό μ λ ₯νμΈμ){RESET}") | |
| async def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--policy", default="ask", choices=["ask", "trust", "doubt", "neutral"]) | |
| ap.add_argument("--quiet", action="store_true") | |
| ap.add_argument("--content", default="content") | |
| ap.add_argument("--chat", action="store_true", help="ν¬μΌ μμ μ±ν κ°μ νμ±ν") | |
| ap.add_argument("--no-chat", action="store_true", help="ν¬μΌ μμ μ±ν λΉνμ±ν") | |
| args = ap.parse_args() | |
| chat_enabled = not args.no_chat and (args.chat or bool(os.environ.get("OPENAI_API_KEY"))) | |
| repo = ContentRepository(args.content) | |
| engine = StoryEngine(repo) | |
| pocket_service = None | |
| if chat_enabled: | |
| from engine.plugins.logging import TurnLoggingPlugin | |
| from engine.services.pocket import PocketService | |
| pocket_service = PocketService(repo, plugins=[TurnLoggingPlugin()]) | |
| step = engine.start() | |
| while step.ending is None: | |
| render(step, args.quiet) | |
| if step.choices: | |
| while True: | |
| kind, val = pick_or_chat(step, args.policy, chat_enabled) | |
| if kind == "choice": | |
| choice, step = engine.choose(val) | |
| if not args.quiet: | |
| print(f"\n{DIM}β· {choice.label}{RESET}") | |
| if choice.result: | |
| print(choice.result) | |
| break | |
| card = repo.get_card(engine.current_id) | |
| if not card.can_chat: | |
| print(f"{DIM} (μ΄ μ₯λ©΄μμ κ·Έλ κ° μ리μ μλ€ β λ²νΈλ₯Ό μ λ ₯νμΈμ){RESET}") | |
| continue | |
| await pocket_chat(pocket_service, engine, engine.current_id, val) | |
| # μ±ν ν κ°μ μΉ΄λμ μ νμ§λ‘ λ³΅κ· (λλ¬μ Β·λΆκΈ°λ μ½λκ° λ³΄μ₯) | |
| else: | |
| if not args.quiet and args.policy == "ask": | |
| input(f"{DIM}(λ€μ β){RESET}") | |
| step = engine.advance() | |
| s = engine.state | |
| print(f"\n{BOLD}βββ μ±ν° μ’ λ£: {step.ending.label} βββ{RESET}") | |
| print(f"{DIM}[λλ²κ·Έ β μ μ λΉλ ΈμΆ] trust={s.trust_score} doubt={s.doubt_score} " | |
| f"frailty={s.frailty} level={s.trust_level} μΉ΄λ {len(engine.visited)}μ₯ λ°©λ¬Έ{RESET}") | |
| if __name__ == "__main__": | |
| asyncio.run(main()) | |