coreprinciple's picture
Trace: create the dataset repo before pushing (upload_file does not auto-create)
d336f72
Raw
History Blame Contribute Delete
5.93 kB
"""Inference trace logging (Open Trace badge).
Every model call — vibe→weights extraction and route narration — emits one JSON
row. Rows are always appended to ``logs/traces.jsonl`` locally; when a Hugging
Face write token is present (``HF_TOKEN``), each row is *also* pushed to the
configured HF Dataset on a daemon thread (non-blocking — the user never waits on
the write). With no token the push is a silent no-op, so nothing breaks locally.
Both successful calls and fallback activations are logged (``used_fallback``),
so the dataset reflects exactly what the model did vs. what the safety net did.
"""
from __future__ import annotations
import json
import threading
from datetime import datetime, timezone
from discoverroute import config
LOG_DIR = config.PROJECT_ROOT / "logs"
TRACES_PATH = LOG_DIR / "traces.jsonl"
PLANS_PATH = LOG_DIR / "plans.jsonl"
# Monotonic, process-local counter so concurrently-written HF filenames never
# collide (datetime alone can repeat under load).
_seq_lock = threading.Lock()
_seq = 0
# upload_file does NOT auto-create a missing repo (unlike the `hf upload` CLI), so
# the dataset must be created once before the first push or every push 404s. Guard
# the create with a flag so we only attempt it once per process.
_repo_lock = threading.Lock()
_repo_ready = False
def _ensure_repo(token: str) -> None:
"""Create the trace dataset once (idempotent). Raises on a real auth failure."""
global _repo_ready
with _repo_lock:
if _repo_ready:
return
from huggingface_hub import HfApi
HfApi(token=token).create_repo(
repo_id=config.TRACE_REPO, repo_type="dataset", exist_ok=True, private=False
)
_repo_ready = True
def _next_seq() -> int:
global _seq
with _seq_lock:
_seq += 1
return _seq
def _utcnow_iso() -> str:
return datetime.now(timezone.utc).isoformat()
def _append_local(path, row: dict) -> None:
try:
LOG_DIR.mkdir(parents=True, exist_ok=True)
with open(path, "a", encoding="utf-8") as fh:
fh.write(json.dumps(row, ensure_ascii=False) + "\n")
except Exception: # noqa: BLE001 - logging must never break a route
pass
def _push_hf_async(row: dict, kind: str) -> None:
"""Best-effort async upload of one row to the trace dataset (token-gated)."""
token = config.HF_TOKEN
if not token:
return # stub: local-only until a token is provided on the Space
seq = _next_seq()
stamp = row.get("timestamp", _utcnow_iso()).replace(":", "-")
def _push() -> None:
try:
from huggingface_hub import HfApi
_ensure_repo(token) # create the dataset on first push (no auto-create)
api = HfApi(token=token)
api.upload_file(
path_or_fileobj=json.dumps(row, ensure_ascii=False).encode("utf-8"),
path_in_repo=f"{kind}/{stamp}-{seq:06d}.json",
repo_id=config.TRACE_REPO,
repo_type="dataset",
)
except Exception as exc: # noqa: BLE001 - never break a route, but DO log it
# A swallowed push failure (bad/scopeless token, wrong repo) is exactly
# what made the trace dataset silently never appear. Log it so the Space
# operator can see why instead of debugging blind.
print(f"[trace] push to {config.TRACE_REPO} FAILED "
f"({type(exc).__name__}): {exc}", flush=True)
threading.Thread(target=_push, daemon=True).start()
def selftest() -> None:
"""Boot-time check: is HF_TOKEN present and can it write to TRACE_REPO?
Prints a clear verdict to the Space logs so a misconfigured secret (missing,
wrong name, or a token without org write) is obvious instead of failing silently.
"""
token = config.HF_TOKEN
if not token:
print("[trace] HF_TOKEN NOT detected — traces stay local only. Set a Space "
"secret named exactly 'HF_TOKEN' to a write token to enable Hub push.",
flush=True)
return
print(f"[trace] HF_TOKEN detected (len={len(token)}); ensuring + testing write to "
f"{config.TRACE_REPO} …", flush=True)
try:
from huggingface_hub import HfApi
_ensure_repo(token) # create the dataset if missing (the actual fix)
HfApi(token=token).upload_file(
path_or_fileobj=b'{"selftest": true}',
path_in_repo="_selftest/boot.json",
repo_id=config.TRACE_REPO, repo_type="dataset",
commit_message="trace selftest",
)
print(f"[trace] ✅ push OK — {config.TRACE_REPO} is writable; traces will flow.",
flush=True)
except Exception as exc: # noqa: BLE001
print(f"[trace] ❌ push FAILED ({type(exc).__name__}): {exc} — if 403, the "
"token lacks WRITE access to the build-small-hackathon org.", flush=True)
def log_trace(call_type: str, input_data: dict, output_data: dict,
latency_ms: int, used_fallback: bool = False,
model: str | None = None) -> dict:
"""Record one inference call (``vibe_extraction`` or ``narration``)."""
row = {
"timestamp": _utcnow_iso(),
"call_type": call_type,
"model": model or config.LLM_MODEL,
"input": json.dumps(input_data, ensure_ascii=False),
"output": json.dumps(output_data, ensure_ascii=False),
"latency_ms": int(latency_ms),
"used_fallback": bool(used_fallback),
}
_append_local(TRACES_PATH, row)
_push_hf_async(row, "traces")
return row
def log_plan(params: dict, summary: dict) -> dict:
"""Record one end-to-end plan_route call (feeds the Field Notes write-up)."""
row = {"timestamp": _utcnow_iso(), "params": params, "summary": summary}
_append_local(PLANS_PATH, row)
_push_hf_async(row, "plans")
return row