ars-fabula-vn-embed / trace_log.py
ArsVie's picture
Deploy Ars Fabula VN (zerogpu)
ce06a8c verified
Raw
History Blame Contribute Delete
2.64 kB
"""
trace_log.py — capture model prompt→response traces for later fine-tuning.
Every real narration turn is appended as one JSON line to a dated file under
the trace directory. Each record is already in the standard SFT chat shape
(a `messages` list ending in the assistant's raw response), with extra
`context` fields alongside for curating/filtering the dataset later:
{"ts": "...", "model": "...", "context": {...},
"messages": [{"role":"system",...},{"role":"user",...},
{"role":"assistant","content": "<raw model output>"}]}
To build a training set, read the lines and keep `record["messages"]`
(drop the trailing assistant turn at inference time).
Controls:
ARS_FABULA_TRACE = "1"|"on" (default) to log, "0"|"off"|"false" to disable
ARS_FABULA_TRACE_DIR = output directory (default: "logs/traces")
Mock/scripted responses are NOT logged — the engine skips logging when the
model reports `is_mock` (see run_turn), so tests and mock fallbacks don't
pollute the dataset.
"""
from __future__ import annotations
import os
import json
from datetime import datetime, date
from pathlib import Path
from typing import Optional
_DISABLED = {"0", "off", "false", "no", ""}
def traces_enabled() -> bool:
return os.getenv("ARS_FABULA_TRACE", "1").strip().lower() not in _DISABLED
def _trace_dir() -> Path:
return Path(os.getenv("ARS_FABULA_TRACE_DIR", "logs/traces"))
def log_turn(messages: list[dict], response: str, *,
model: str = "", context: Optional[dict] = None) -> None:
"""Append one prompt→response trace as a JSON line.
Best-effort: any IO/serialization error is swallowed so a logging
problem can never break a turn. `messages` is the prompt sent to the
model (system + user); the assistant's raw `response` is appended so the
stored `messages` list is directly usable for supervised fine-tuning.
"""
if not traces_enabled():
return
try:
rec = {
"ts": datetime.now().isoformat(timespec="seconds"),
"model": model,
"context": context or {},
"messages": list(messages) + [{"role": "assistant", "content": response}],
}
out_dir = _trace_dir()
out_dir.mkdir(parents=True, exist_ok=True)
path = out_dir / f"turns-{date.today().isoformat()}.jsonl"
with open(path, "a", encoding="utf-8") as f:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
except Exception:
# Never let trace logging interfere with the turn loop.
pass