| """ |
| inspect_logs.py |
| DRIFT Log Inspector |
| -------------------- |
| Use this after the first baseline session to manually verify the database |
| contains all expected fields before running ablations. |
| |
| DO NOT skip this step. If fields are missing here, your ablation data |
| will be incomplete and you will not know until analysis. |
| |
| Usage: |
| python inspect_logs.py # inspect all runs |
| python inspect_logs.py --run_id run_xyz # inspect specific run |
| python inspect_logs.py --summary # summary stats only |
| """ |
|
|
| import sqlite3 |
| import json |
| import argparse |
| from pathlib import Path |
|
|
|
|
| DB_PATH = "experiment_log.db" |
|
|
| REQUIRED_EVENT_TYPES = [ |
| "state_snapshot", |
| "memory_selection", |
| "continuity_metrics", |
| ] |
|
|
| REQUIRED_RUN_FIELDS = [ |
| "run_id", |
| "config", |
| "git_hash", |
| "start_time", |
| ] |
|
|
|
|
| def connect(): |
| if not Path(DB_PATH).exists(): |
| print(f"[Inspector] Database not found: {DB_PATH}") |
| print("Run collect_baseline.py first.") |
| return None |
| return sqlite3.connect(DB_PATH) |
|
|
|
|
| def list_runs(conn): |
| cur = conn.execute( |
| "SELECT run_id, git_hash, start_time, config FROM runs ORDER BY start_time DESC" |
| ) |
| rows = cur.fetchall() |
| print(f"\n[Runs] Found {len(rows)} runs:\n") |
| for run_id, git_hash, start_time, config_str in rows: |
| config = json.loads(config_str) |
| mode = config.get("mode", "?") |
| print(f" {run_id}") |
| print(f" mode: {mode}") |
| print(f" git_hash: {git_hash}") |
| print(f" started: {start_time}") |
| print() |
| return [r[0] for r in rows] |
|
|
|
|
| def inspect_run(conn, run_id: str): |
| print(f"\n[Inspecting] Run: {run_id}") |
|
|
| |
| cur = conn.execute("SELECT * FROM runs WHERE run_id = ?", (run_id,)) |
| run = cur.fetchone() |
| if not run: |
| print(" β Run not found in runs table.") |
| return |
|
|
| print(" β
Run record found.") |
|
|
| |
| git_hash = run[2] |
| if git_hash == "unknown": |
| print(" β οΈ git_hash = 'unknown'. Not in a git repo or git not available.") |
| else: |
| print(f" β
git_hash: {git_hash}") |
|
|
| |
| cur = conn.execute( |
| "SELECT DISTINCT event_type FROM events WHERE run_id = ?", (run_id,) |
| ) |
| found_types = {row[0] for row in cur.fetchall()} |
| print("\n [Event Types Found]") |
| for et in REQUIRED_EVENT_TYPES: |
| status = "β
" if et in found_types else "β MISSING" |
| print(f" {status} {et}") |
| for et in found_types - set(REQUIRED_EVENT_TYPES): |
| print(f" β {et}") |
|
|
| |
| cur = conn.execute( |
| "SELECT COUNT(*) FROM events WHERE run_id = ? AND event_type = 'run_end'", |
| (run_id,), |
| ) |
| has_end = cur.fetchone()[0] > 0 |
| print( |
| f"\n [Run End] {'β
Clean run_end logged' if has_end else 'β No run_end found β run may have crashed'}" |
| ) |
|
|
| |
| cur = conn.execute( |
| "SELECT payload FROM events WHERE run_id = ? AND event_type = 'memory_selection' LIMIT 1", |
| (run_id,), |
| ) |
| row = cur.fetchone() |
| if row: |
| payload = json.loads(row[0]) |
| print("\n [Sample memory_selection]") |
| selected = payload.get("selected", []) |
| rejected = payload.get("rejected", []) |
| print(f" selected: {len(selected)} memories") |
| print(f" rejected: {len(rejected)} candidates") |
| if selected: |
| s = selected[0] |
| components = s.get("components") |
| if components: |
| print(f" β
score_components present: {list(components.keys())}") |
| else: |
| print(" β score_components MISSING from selected[0]") |
| else: |
| print("\n β οΈ No memory_selection events found.") |
|
|
| |
| cur = conn.execute( |
| "SELECT payload FROM events WHERE run_id = ? AND event_type = 'continuity_metrics' LIMIT 1", |
| (run_id,), |
| ) |
| row = cur.fetchone() |
| if row: |
| payload = json.loads(row[0]) |
| print("\n [Sample continuity_metrics]") |
| normalized = payload.get("normalized", {}) |
| raw = payload.get("raw", {}) |
| axes = [ |
| "entity_overlap", |
| "goal_overlap", |
| "tone_similarity", |
| "memory_reference_rate", |
| "state_influence", |
| ] |
| for axis in axes: |
| n_val = normalized.get(axis, "MISSING") |
| r_val = raw.get(axis, "MISSING") |
| status = "β
" if n_val != "MISSING" else "β" |
| print(f" {status} {axis}: raw={r_val}, normalized={n_val}") |
| else: |
| print("\n β οΈ No continuity_metrics events found.") |
|
|
| |
| cur = conn.execute("SELECT COUNT(*) FROM events WHERE run_id = ?", (run_id,)) |
| count = cur.fetchone()[0] |
| print(f"\n [Total Events] {count}") |
|
|
|
|
| def summary(conn): |
| cur = conn.execute("SELECT COUNT(*) FROM runs") |
| n_runs = cur.fetchone()[0] |
| cur = conn.execute("SELECT COUNT(*) FROM events") |
| n_events = cur.fetchone()[0] |
| cur = conn.execute( |
| "SELECT COUNT(DISTINCT run_id) FROM events WHERE event_type = 'run_end'" |
| ) |
| n_clean = cur.fetchone()[0] |
|
|
| print("\n[Summary]") |
| print(f" Total runs: {n_runs}") |
| print(f" Total events: {n_events}") |
| print(f" Clean completions: {n_clean}") |
| print(f" Incomplete runs: {n_runs - n_clean}") |
|
|
|
|
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description="DRIFT Log Inspector") |
| parser.add_argument("--run_id", type=str, default=None) |
| parser.add_argument("--summary", action="store_true") |
| args = parser.parse_args() |
|
|
| conn = connect() |
| if conn is None: |
| exit(1) |
|
|
| if args.summary: |
| summary(conn) |
| elif args.run_id: |
| inspect_run(conn, args.run_id) |
| else: |
| run_ids = list_runs(conn) |
| if run_ids: |
| print("[Inspector] Inspecting most recent run...") |
| inspect_run(conn, run_ids[0]) |
| summary(conn) |
|
|
| conn.close() |
|
|