"""Gameplay trace log — console + logs/trace.log. Tags: flow (game events), llm (transport: live/mock/timing), ai (full model payloads), vald (validation rejects with reason), econ (guardrails/economy). """ from __future__ import annotations import json import logging import pathlib LOG_PATH = pathlib.Path(__file__).resolve().parent.parent / "logs" / "trace.log" logger = logging.getLogger("bds") if not logger.handlers: logger.setLevel(logging.INFO) logger.propagate = False fmt = logging.Formatter("%(asctime)s %(message)s", datefmt="%H:%M:%S") stream = logging.StreamHandler() stream.setFormatter(fmt) logger.addHandler(stream) # always log to stdout (HF captures this) # file log is best-effort — on read-only / non-writable hosts (HF Spaces) # just skip it rather than crash the whole app at import try: LOG_PATH.parent.mkdir(exist_ok=True) filehandler = logging.FileHandler(LOG_PATH, encoding="utf-8") filehandler.setFormatter(fmt) logger.addHandler(filehandler) except OSError: pass def trace(tag: str, msg: str) -> None: logger.info(f"[{tag:<4}] {msg}") def trace_payload(payload: dict) -> None: try: trace("ai", json.dumps(payload, ensure_ascii=False)) except (TypeError, ValueError): trace("ai", repr(payload)[:800])