iamrahulreddy's picture
Release L31H14+ code, results, and SAE checkpoint
938f0c7 verified
Raw
History Blame Contribute Delete
72.4 kB
"""Unified command-line interface for the Mechanistic Interpretability Workbench."""
from __future__ import annotations
import argparse
import csv
import dataclasses
import hashlib
import json
import math
import random
import sys
import zipfile
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from mech_workbench.model_utils import is_finite as _finite, safe_mean as _mean
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
VALID_PROBES = ("residual", "logit_lens", "gradient", "attention")
DEFAULT_ROBUSTNESS_PROBES = ("logit_lens", "gradient", "attention")
def _parse_probe_names(raw: str | None) -> list[str] | None:
if not raw:
return None
names = [part.strip() for part in raw.split(",") if part.strip()]
unknown = [name for name in names if name not in VALID_PROBES]
if unknown:
raise ValueError(f"Unknown probe(s): {unknown}. Valid: {list(VALID_PROBES)}")
return names
def _cmd_run(args: argparse.Namespace) -> int:
"""Load the model once, run selected probes, and write numeric artifacts."""
from mech_workbench.config import config_to_dict, load_config
try:
probe_names = _parse_probe_names(args.probe)
except ValueError as exc:
print(f"[error] {exc}")
return 1
config = load_config(args.config)
if getattr(args, "limit", None):
config = dataclasses.replace(config, prompt_pairs=config.prompt_pairs[: args.limit])
if args.dry_run:
_print_dry_run(config, probe_names)
return 0
from mech_workbench.logger import ResultLogger
from mech_workbench.model_loader import load_model_and_tokenizer
from mech_workbench.probes import run_all_probes
selected = probe_names or list(DEFAULT_ROBUSTNESS_PROBES)
logger = ResultLogger(args.results_root)
logger.write_config(config_to_dict(config))
print("")
print("=" * 60)
print(f" mech run - {config.model_name}")
print(f" Probes : {selected}")
print(f" Pairs : {len(config.prompt_pairs)}")
print("=" * 60)
print("")
print("[1/4] Loading model...")
model, tokenizer = load_model_and_tokenizer(
config.model_name, device=config.device, dtype=config.dtype
)
print("[2/4] Running probes...")
try:
results = run_all_probes(model, tokenizer, config, logger, selected)
except Exception:
logger.finalize()
raise
print("[3/4] Writing probe artifacts...")
raw_dir = logger.run_dir / "raw"
for probe_name, result in results.items():
path = raw_dir / f"{probe_name}_results.json"
path.write_text(
json.dumps(result, indent=2, default=str) + "\n", encoding="utf-8"
)
print(f" wrote raw/{path.name}")
_write_probe_csvs(logger.run_dir, results)
print("[4/4] Writing summary, manifest, integrity...")
summary = _write_probe_run_artifacts(logger.run_dir, results, config)
_write_run_card(logger.run_dir, config, ["discovery"])
from mech_workbench.integrity import write_integrity, write_package_manifest
write_integrity(logger.run_dir)
write_package_manifest(logger.run_dir)
archive_path = None
if args.archive_root:
archive_path = _create_run_zip(logger.run_dir, Path(args.archive_root).resolve())
print("")
print(f"Results directory: {logger.run_dir}")
print(f"Run status: {summary['verdict']['status']} - {summary['verdict']['claim']}")
if archive_path:
print(f"Artifact zip: {archive_path}")
print("")
return 0
def _cmd_ablate(args: argparse.Namespace) -> int:
"""Run targeted attention-head ablations."""
from mech_workbench.config import config_to_dict, load_config
from mech_workbench.probes.ablation import (
HeadAblationProbe,
parse_ablation_modes,
parse_head_targets,
)
try:
targets = parse_head_targets(args.target)
if args.also:
seen = set(targets)
for target in parse_head_targets(args.also):
if target not in seen:
targets.append(target)
seen.add(target)
modes = parse_ablation_modes(args.mode)
except ValueError as exc:
print(f"[error] {exc}")
return 1
config = load_config(args.config)
if getattr(args, "limit", None):
config = dataclasses.replace(config, prompt_pairs=config.prompt_pairs[: args.limit])
combined = getattr(args, "combined", False)
if args.dry_run:
_print_ablation_dry_run(config, targets, modes)
return 0
from mech_workbench.integrity import write_integrity, write_package_manifest
from mech_workbench.logger import ResultLogger
from mech_workbench.model_loader import load_model_and_tokenizer
run_name = f"mech_ablation_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
logger = ResultLogger(args.results_root, run_name=run_name)
logger.write_config(config_to_dict(config))
mode_label = "combined" if combined else "individual"
print("")
print("=" * 60)
print(f" mech ablate - {config.model_name}")
print(f" Targets : {[target.label for target in targets]}")
print(f" Modes : {modes} [{mode_label}]")
print(f" Pairs : {len(config.prompt_pairs)}")
print("=" * 60)
print("")
print("[1/4] Loading model...")
model, tokenizer = load_model_and_tokenizer(
config.model_name, device=config.device, dtype=config.dtype
)
print("[2/4] Running head ablations...")
probe = HeadAblationProbe(targets=targets, modes=modes, combined=combined)
try:
result = probe.run(model, tokenizer, config, logger)
except Exception:
logger.finalize()
raise
print("[3/4] Writing ablation artifacts...")
raw_dir = logger.run_dir / "raw"
result_path = raw_dir / "ablation_results.json"
result_path.write_text(
json.dumps(result, indent=2, default=str) + "\n", encoding="utf-8"
)
print(" wrote raw/ablation_results.json")
rows = _ablation_rows(result)
if rows:
_write_csv(logger.run_dir / "data" / "ablation_results.csv", rows)
print(" wrote data/ablation_results.csv")
print("[4/4] Writing summary, manifest, integrity...")
summary = _write_ablation_run_artifacts(logger.run_dir, result, config)
_write_run_card(logger.run_dir, config, ["ablation"])
write_integrity(logger.run_dir)
write_package_manifest(logger.run_dir)
archive_path = None
if args.archive_root:
archive_path = _create_run_zip(logger.run_dir, Path(args.archive_root).resolve())
print("")
print(f"Results directory: {logger.run_dir}")
print(f"Run status: {summary['verdict']['status']} - {summary['verdict']['claim']}")
if archive_path:
print(f"Artifact zip: {archive_path}")
print("")
return 0
def _cmd_source(args: argparse.Namespace) -> int:
"""Run source-layer probing for a target attention head."""
from mech_workbench.config import config_to_dict, load_config
from mech_workbench.probes.ablation import parse_head_target
from mech_workbench.probes.source import (
SourceProbe,
parse_source_layers,
parse_source_position,
)
try:
target = parse_head_target(args.target)
source_layers = parse_source_layers(args.source_layers)
source_position = parse_source_position(args.source_position)
except ValueError as exc:
print(f"[error] {exc}")
return 1
config = load_config(args.config)
if getattr(args, "limit", None):
config = dataclasses.replace(config, prompt_pairs=config.prompt_pairs[: args.limit])
if args.dry_run:
_print_source_dry_run(config, target, source_layers, source_position)
return 0
from mech_workbench.integrity import write_integrity, write_package_manifest
from mech_workbench.logger import ResultLogger
from mech_workbench.model_loader import load_model_and_tokenizer
run_name = f"mech_source_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
logger = ResultLogger(args.results_root, run_name=run_name)
logger.write_config(config_to_dict(config))
print("")
print("=" * 60)
print(f" mech source - {config.model_name}")
print(f" Target : {target.label}")
print(f" Layers : {source_layers}")
print(f" Position: {source_position}")
print(f" Pairs : {len(config.prompt_pairs)}")
print("=" * 60)
print("")
print("[1/4] Loading model...")
model, tokenizer = load_model_and_tokenizer(
config.model_name, device=config.device, dtype=config.dtype
)
print("[2/4] Running source probing...")
probe = SourceProbe(
target=target,
source_layers=source_layers,
source_position=source_position,
)
try:
result = probe.run(model, tokenizer, config, logger)
except Exception:
logger.finalize()
raise
print("[3/4] Writing source artifacts...")
raw_dir = logger.run_dir / "raw"
result_path = raw_dir / "source_results.json"
result_path.write_text(
json.dumps(result, indent=2, default=str) + "\n", encoding="utf-8"
)
print(" wrote raw/source_results.json")
rows = _source_rows(result)
if rows:
_write_csv(logger.run_dir / "data" / "source_results.csv", rows)
print(" wrote data/source_results.csv")
print("[4/4] Writing summary, manifest, integrity...")
summary = _write_source_run_artifacts(logger.run_dir, result, config)
_write_run_card(logger.run_dir, config, ["source"])
write_integrity(logger.run_dir)
write_package_manifest(logger.run_dir)
archive_path = None
if args.archive_root:
archive_path = _create_run_zip(logger.run_dir, Path(args.archive_root).resolve())
print("")
print(f"Results directory: {logger.run_dir}")
print(f"Run status: {summary['verdict']['status']} - {summary['verdict']['claim']}")
if archive_path:
print(f"Artifact zip: {archive_path}")
print("")
return 0
def _print_ablation_dry_run(config: Any, targets: list[Any], modes: list[str]) -> None:
layer_end = config.layer_end if config.layer_end is not None else "(all)"
print("[dry-run] Ablation config validated:")
print(f" Model : {config.model_name}")
print(f" Prompt pairs : {len(config.prompt_pairs)}")
print(f" Layer range : {config.layer_start} - {layer_end}")
print(f" Patch pos : {config.patch_position}")
print(f" Targets : {[target.label for target in targets]}")
print(f" Modes : {modes}")
for pair in config.prompt_pairs:
headline = "headline" if getattr(pair, "headline", True) else "specificity"
print(
f" Pair '{pair.id}' [{pair.family}/{pair.variant}/{headline}]: "
f"'{pair.correct_token}' vs '{pair.incorrect_token}'"
)
def _print_source_dry_run(
config: Any,
target: Any,
source_layers: list[int],
source_position: str,
) -> None:
print("[dry-run] Source config validated:")
print(f" Model : {config.model_name}")
print(f" Prompt pairs : {len(config.prompt_pairs)}")
print(f" Patch pos : {config.patch_position}")
print(f" Target : {target.label}")
print(f" Source layers: {source_layers}")
print(f" Source pos : {source_position}")
for pair in config.prompt_pairs:
headline = "headline" if getattr(pair, "headline", True) else "diagnostic"
print(
f" Pair '{pair.id}' [{pair.family}/{pair.variant}/{headline}]: "
f"'{pair.correct_token}' vs '{pair.incorrect_token}'"
)
def _print_dry_run(config: Any, probe_names: list[str] | None) -> None:
layer_end = config.layer_end if config.layer_end is not None else "(all)"
probes = probe_names or list(DEFAULT_ROBUSTNESS_PROBES)
print("[dry-run] Config validated:")
print(f" Model : {config.model_name}")
print(f" Prompt pairs : {len(config.prompt_pairs)}")
print(f" Layer range : {config.layer_start} - {layer_end}")
print(f" Patch pos : {config.patch_position}")
print(f" Probes : {probes}")
print(f" Random heads : {getattr(config, 'random_baseline_samples', 32)} samples/pair")
for pair in config.prompt_pairs:
headline = "headline" if getattr(pair, "headline", True) else "diagnostic"
print(
f" Pair '{pair.id}' [{pair.family}/{pair.variant}/{headline}]: "
f"'{pair.correct_token}' vs '{pair.incorrect_token}'"
)
def _write_probe_csvs(run_dir: Path, results: dict[str, Any]) -> None:
"""Write per-probe CSV files to the data/ subdirectory."""
data_dir = run_dir / "data"
attn = results.get("attention", {})
head_rows: list[dict[str, Any]] = []
for pr in attn.get("pair_results", []):
head_rows.extend(pr.get("head_rows", []))
if head_rows:
_write_csv(data_dir / "head_recovery.csv", head_rows)
print(" wrote data/head_recovery.csv")
lens = results.get("logit_lens", {})
lens_rows: list[dict[str, Any]] = []
for pr in lens.get("pair_results", []):
for row in pr.get("rows", []):
lens_rows.append({"pair_id": pr["pair_id"], **row})
if lens_rows:
_write_csv(data_dir / "logit_lens.csv", lens_rows)
print(" wrote data/logit_lens.csv")
grad = results.get("gradient", {})
grad_rows: list[dict[str, Any]] = []
for pr in grad.get("pair_results", []):
for row in pr.get("layer_scores", []):
grad_rows.append({"pair_id": pr["pair_id"], **row})
if grad_rows:
_write_csv(data_dir / "gradient_attribution.csv", grad_rows)
print(" wrote data/gradient_attribution.csv")
def _ablation_rows(result: dict[str, Any]) -> list[dict[str, Any]]:
rows: list[dict[str, Any]] = []
for pair in result.get("pair_results", []):
rows.extend(pair.get("head_results", []))
return rows
def _source_rows(result: dict[str, Any]) -> list[dict[str, Any]]:
rows: list[dict[str, Any]] = []
for pair in result.get("pair_results", []):
rows.extend(pair.get("source_rows", []))
return rows
def _write_csv(path: Path, rows: list[dict[str, Any]]) -> None:
fieldnames = list(rows[0].keys())
with path.open("w", newline="", encoding="utf-8") as handle:
writer = csv.DictWriter(handle, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
def _write_probe_run_artifacts(run_dir: Path, results: dict[str, Any], config: Any) -> dict[str, Any]:
"""Write discovery summary and log artifacts."""
summary = _summarize_probe_results(run_dir, results, config)
summaries_dir = run_dir / "summaries"
(summaries_dir / "run_summary.json").write_text(
json.dumps(summary, indent=2, default=str) + "\n", encoding="utf-8"
)
(run_dir / "logs" / "run_log.txt").write_text(_render_run_log(summary), encoding="utf-8")
_write_candidate_heads_csv(summaries_dir, summary)
return summary
def _write_candidate_heads_csv(summaries_dir: Path, summary: dict[str, Any]) -> None:
attn = summary.get("attention", {})
top = (
attn.get("top_headline_control_adjusted_heads")
or attn.get("top_mean_margin_heads")
or attn.get("top_heads_by_mean_margin")
or []
)
if top:
rows = []
for rank, row in enumerate(top, start=1):
out = dict(row)
out.setdefault("rank", rank)
out.setdefault("head_id", f"L{out.get('layer')}H{out.get('head')}")
rows.append(out)
_write_csv(summaries_dir / "candidate_heads.csv", rows)
print(" wrote summaries/candidate_heads.csv")
def _write_ablation_run_artifacts(
run_dir: Path,
result: dict[str, Any],
config: Any,
) -> dict[str, Any]:
summary = _summarize_ablation_result(run_dir, result, config)
summaries_dir = run_dir / "summaries"
(summaries_dir / "run_summary.json").write_text(
json.dumps(summary, indent=2, default=str) + "\n", encoding="utf-8"
)
(run_dir / "logs" / "run_log.txt").write_text(_render_ablation_log(summary), encoding="utf-8")
_write_ablation_summary_csv(summaries_dir, summary)
return summary
def _write_ablation_summary_csv(summaries_dir: Path, summary: dict[str, Any]) -> None:
rows = summary.get("aggregate", {}).get("by_target_mode_scope", [])
if rows:
_write_csv(summaries_dir / "ablation_summary.csv", rows)
print(" wrote summaries/ablation_summary.csv")
def _write_source_run_artifacts(
run_dir: Path,
result: dict[str, Any],
config: Any,
) -> dict[str, Any]:
summary = _summarize_source_result(run_dir, result, config)
summaries_dir = run_dir / "summaries"
(summaries_dir / "run_summary.json").write_text(
json.dumps(summary, indent=2, default=str) + "\n", encoding="utf-8"
)
(run_dir / "logs" / "run_log.txt").write_text(_render_source_log(summary), encoding="utf-8")
_write_source_summary_csv(summaries_dir, summary)
return summary
def _write_source_summary_csv(summaries_dir: Path, summary: dict[str, Any]) -> None:
rows = summary.get("pair_summaries", [])
if rows:
_write_csv(summaries_dir / "source_summary.csv", rows)
print(" wrote summaries/source_summary.csv")
def _summarize_probe_results(run_dir: Path, results: dict[str, Any], config: Any) -> dict[str, Any]:
pair_meta = _pair_metadata(config)
random_samples = int(getattr(config, "random_baseline_samples", 32) or 32)
seed = int(getattr(config, "seed", 1234) or 1234)
logit_lens = _summarize_logit_lens(results.get("logit_lens", {}), pair_meta)
attention = _summarize_attention(
results.get("attention", {}),
pair_meta,
seed=seed,
random_baseline_samples=random_samples,
)
gradient = _summarize_gradient(results.get("gradient", {}), pair_meta)
warnings: list[str] = []
if logit_lens["control_bias_warnings"]:
warnings.append(
f"{len(logit_lens['control_bias_warnings'])} control prompt(s) have positive final logit bias."
)
weak_margin = [
row["pair_id"]
for row in attention["pair_summaries"]
if row.get("best_margin_patch_recovery") is not None
and row["best_margin_patch_recovery"] < 0
]
if weak_margin:
warnings.append(
"Best-margin head has negative patch recovery for: " + ", ".join(weak_margin)
)
if gradient["nan_scores"] > 0:
warnings.append(f"{gradient['nan_scores']} gradient score(s) are NaN.")
unstable = [
row["canonical_id"]
for row in attention.get("paraphrase_stability", [])
if row.get("num_variants", 0) > 1 and not row.get("stable_best_margin_heads")
]
if unstable:
warnings.append(
"No repeated best-margin head across paraphrases for: " + ", ".join(unstable)
)
status = "pass_with_warnings" if warnings else "pass"
if not results:
status = "no_result"
claim = "No probe result files were produced."
elif not any(results.get(name) for name in DEFAULT_ROBUSTNESS_PROBES):
claim = "Residual probe run completed."
else:
claim = (
"Robustness run completed; interpret heads using control-adjusted "
"recovery, paraphrase stability, family grouping, and random-head baselines."
)
return {
"created_at_utc": datetime.now(timezone.utc).isoformat(),
"run_dir": str(run_dir),
"model_name": getattr(config, "model_name", None),
"prompt_pairs": len(getattr(config, "prompt_pairs", [])),
"probes": sorted(results.keys()),
"pair_metadata": list(pair_meta.values()),
"random_baseline_samples": random_samples,
"verdict": {
"status": status,
"claim": claim,
"warnings": warnings,
},
"logit_lens": logit_lens,
"attention": attention,
"gradient": gradient,
}
def _summarize_ablation_result(
run_dir: Path,
result: dict[str, Any],
config: Any,
) -> dict[str, Any]:
rows = _ablation_rows(result)
aggregate = result.get("aggregate", {})
targets = [row.get("label") for row in result.get("targets", [])]
modes = result.get("modes", [])
primary_target = _primary_ablation_target(aggregate, targets)
warnings: list[str] = []
primary_mean = _find_ablation_aggregate(
aggregate,
target_head=primary_target,
mode="mean",
headline=True,
)
primary_control = _find_specificity(
aggregate,
target_head=primary_target,
mode="mean",
)
if primary_mean:
mean_drop = primary_mean.get("mean_logit_diff_drop")
rel_drop = primary_mean.get("mean_relative_drop_fraction")
if not _finite(mean_drop) or float(mean_drop) <= 1.5:
warnings.append(
f"{primary_target} mean ablation headline drop did not clear 1.5 logits."
)
if not _finite(rel_drop) or float(rel_drop) <= 0.25:
warnings.append(
f"{primary_target} mean ablation headline relative drop did not clear 25%."
)
else:
warnings.append(f"No headline mean-ablation aggregate found for {primary_target}.")
if primary_control:
ratio = primary_control.get("factual_to_control_drop_ratio")
if _is_nan(ratio) or (_finite(ratio) and float(ratio) <= 2.0):
warnings.append(
f"{primary_target} factual/control specificity ratio did not clear 2x."
)
status = "pass_with_warnings" if warnings else "pass"
claim = (
"Targeted head ablation completed; interpret necessity using "
"absolute drop, gap-normalized drop, and factual-vs-control specificity."
)
return {
"created_at_utc": datetime.now(timezone.utc).isoformat(),
"run_dir": str(run_dir),
"model_name": getattr(config, "model_name", None),
"prompt_pairs": len(getattr(config, "prompt_pairs", [])),
"probe": "ablation",
"targets": targets,
"modes": modes,
"rows": len(rows),
"verdict": {
"status": status,
"claim": claim,
"warnings": warnings,
},
"aggregate": aggregate,
"pair_summaries": _summarize_ablation_pairs(result),
}
def _primary_ablation_target(
aggregate: dict[str, Any],
targets: list[Any],
) -> str | None:
mean_headline_labels = {
str(row.get("target_head"))
for row in aggregate.get("by_target_mode_scope", [])
if row.get("ablation_mode") == "mean" and bool(row.get("headline")) is True
}
target_labels = [str(target) for target in targets if target is not None]
combined_label = "+".join(target_labels)
if combined_label and combined_label in mean_headline_labels:
return combined_label
if target_labels and target_labels[0] in mean_headline_labels:
return target_labels[0]
if mean_headline_labels:
return sorted(mean_headline_labels)[0]
return target_labels[0] if target_labels else None
def _find_ablation_aggregate(
aggregate: dict[str, Any],
*,
target_head: str | None,
mode: str,
headline: bool,
) -> dict[str, Any] | None:
for row in aggregate.get("by_target_mode_scope", []):
if (
row.get("target_head") == target_head
and row.get("ablation_mode") == mode
and bool(row.get("headline")) == headline
):
return row
return None
def _find_specificity(
aggregate: dict[str, Any],
*,
target_head: str | None,
mode: str,
) -> dict[str, Any] | None:
for row in aggregate.get("specificity", []):
if row.get("target_head") == target_head and row.get("ablation_mode") == mode:
return row
return None
def _summarize_ablation_pairs(result: dict[str, Any]) -> list[dict[str, Any]]:
summaries: list[dict[str, Any]] = []
for pair in result.get("pair_results", []):
head_rows = pair.get("head_results", [])
best_drop = _best_ablation_row(head_rows, "logit_diff_drop")
summaries.append(
{
"pair_id": pair.get("pair_id"),
"family": pair.get("family"),
"canonical_id": pair.get("canonical_id"),
"variant": pair.get("variant"),
"headline": pair.get("headline"),
"clean_logit_diff": pair.get("clean_logit_diff"),
"corrupted_logit_diff": pair.get("corrupted_logit_diff"),
"control_logit_diff": pair.get("control_logit_diff"),
"largest_drop": {
"target_head": best_drop.get("target_head"),
"ablation_mode": best_drop.get("ablation_mode"),
"logit_diff_drop": _float_or_none(best_drop.get("logit_diff_drop")),
"relative_drop_fraction": _float_or_none(
best_drop.get("relative_drop_fraction")
),
"gap_normalized_drop": _float_or_none(
best_drop.get("gap_normalized_drop")
),
} if best_drop else {},
}
)
return summaries
def _summarize_source_result(
run_dir: Path,
result: dict[str, Any],
config: Any,
) -> dict[str, Any]:
rows = _source_rows(result)
aggregate = result.get("aggregate", {})
source_position = str(result.get("source_position", "unknown"))
best_layer_input = (
aggregate.get("best_token_layer_summaries", [])
if source_position == "sweep"
else aggregate.get("source_layer_summaries", [])
)
best_layer = _best_source_layer(best_layer_input)
warnings: list[str] = []
if not best_layer:
warnings.append("No finite source-layer recovery values were produced.")
else:
best_layer_score = _source_layer_target_score(best_layer)
if not _finite(best_layer_score) or float(best_layer_score) <= 0.2:
if source_position == "sweep":
warnings.append("Best-token source-layer mean target recovery did not clear 0.2.")
else:
warnings.append("Best source-layer mean target recovery did not clear 0.2.")
if source_position == "all":
warnings.append(
"All-position source patch is a ceiling sanity check, not source localization."
)
saturated = [
row for row in aggregate.get("source_layer_summaries", [])
if _finite(row.get("mean_target_recovery_fraction"))
and float(row["mean_target_recovery_fraction"]) >= 0.99
and _finite(row.get("mean_logit_recovery_fraction"))
and float(row["mean_logit_recovery_fraction"]) >= 0.99
]
if len(saturated) == len(aggregate.get("source_layer_summaries", [])) and saturated:
warnings.append(
"All candidate source layers saturated near 1.0; rerun with source_position=sweep."
)
status = "pass_with_warnings" if warnings else "pass"
claim = (
"Source probing completed; interpret source layers by target-head recovery "
"and logit recovery after residual-stream patching."
)
target = result.get("target", {}).get("label")
return {
"created_at_utc": datetime.now(timezone.utc).isoformat(),
"run_dir": str(run_dir),
"model_name": getattr(config, "model_name", None),
"prompt_pairs": len(getattr(config, "prompt_pairs", [])),
"probe": "source",
"target": target,
"source_layers": result.get("source_layers", []),
"source_position": source_position,
"rows": len(rows),
"verdict": {
"status": status,
"claim": claim,
"warnings": warnings,
},
"aggregate": aggregate,
"best_source_layer": best_layer,
"pair_summaries": _summarize_source_pairs(result),
}
def _best_source_layer(source_summaries: list[dict[str, Any]]) -> dict[str, Any]:
finite = [row for row in source_summaries if _finite(_source_layer_target_score(row))]
if not finite:
return {}
return max(finite, key=lambda row: float(_source_layer_target_score(row)))
def _source_layer_target_score(row: dict[str, Any]) -> Any:
if "mean_best_target_recovery_fraction" in row:
return row.get("mean_best_target_recovery_fraction")
return row.get("mean_target_recovery_fraction")
def _source_layer_logit_score(row: dict[str, Any]) -> Any:
if "mean_best_logit_recovery_fraction" in row:
return row.get("mean_best_logit_recovery_fraction")
return row.get("mean_logit_recovery_fraction")
def _source_layer_shift_score(row: dict[str, Any]) -> Any:
if "mean_best_target_output_shift" in row:
return row.get("mean_best_target_output_shift")
return row.get("mean_target_output_shift")
def _summarize_source_pairs(result: dict[str, Any]) -> list[dict[str, Any]]:
summaries: list[dict[str, Any]] = []
for pair in result.get("pair_results", []):
best = pair.get("best_source_layer") or {}
summaries.append(
{
"pair_id": pair.get("pair_id"),
"family": pair.get("family"),
"canonical_id": pair.get("canonical_id"),
"variant": pair.get("variant"),
"headline": pair.get("headline"),
"clean_logit_diff": pair.get("clean_logit_diff"),
"corrupted_logit_diff": pair.get("corrupted_logit_diff"),
"best_source_layer": best.get("source_layer"),
"source_position": best.get("source_position") or pair.get("source_position"),
"source_position_index": best.get("source_position_index"),
"source_positions_patched": best.get("source_positions_patched"),
"clean_source_token": best.get("clean_source_token"),
"corrupted_source_token": best.get("corrupted_source_token"),
"target_recovery_fraction": _float_or_none(
best.get("target_recovery_fraction")
),
"target_output_shift": _float_or_none(best.get("target_output_shift")),
"logit_recovery_fraction": _float_or_none(
best.get("logit_recovery_fraction")
),
}
)
return summaries
def _best_ablation_row(rows: list[dict[str, Any]], key: str) -> dict[str, Any]:
finite = [row for row in rows if _finite(row.get(key))]
if not finite:
return {}
return max(finite, key=lambda row: float(row[key]))
def _pair_metadata(config: Any) -> dict[str, dict[str, Any]]:
metadata: dict[str, dict[str, Any]] = {}
for pair in getattr(config, "prompt_pairs", []) or []:
pair_id = getattr(pair, "id", None)
if not pair_id:
continue
metadata[pair_id] = {
"pair_id": pair_id,
"family": getattr(pair, "family", "general"),
"canonical_id": getattr(pair, "canonical_id", None) or pair_id,
"variant": getattr(pair, "variant", "base"),
"headline": bool(getattr(pair, "headline", True)),
}
return metadata
def _meta_for_pair(pair_id: Any, pair_meta: dict[str, dict[str, Any]]) -> dict[str, Any]:
pid = str(pair_id)
return pair_meta.get(
pid,
{
"pair_id": pid,
"family": "general",
"canonical_id": pid,
"variant": "base",
"headline": True,
},
)
def _summarize_logit_lens(
data: dict[str, Any],
pair_meta: dict[str, dict[str, Any]],
control_threshold: float = 0.5,
) -> dict[str, Any]:
pair_summaries = []
warnings = []
token_bias_diagnostics = []
for pr in data.get("pair_results", []):
meta = _meta_for_pair(pr.get("pair_id"), pair_meta)
rows_by_label: dict[str, list[dict[str, Any]]] = defaultdict(list)
for row in pr.get("rows", []):
rows_by_label[str(row.get("prompt_label"))].append(row)
clean = _last_logit_diff(rows_by_label.get("clean", []))
corrupted = _last_logit_diff(rows_by_label.get("corrupted", []))
control = pr.get("final_control_logit_diff")
if control is None:
control = _last_logit_diff(rows_by_label.get("control", []))
summary = {
"pair_id": pr.get("pair_id"),
"family": meta["family"],
"canonical_id": meta["canonical_id"],
"variant": meta["variant"],
"headline": meta["headline"],
"first_positive_layer": pr.get("first_positive_layer"),
"final_clean_logit_diff": clean,
"final_corrupted_logit_diff": corrupted,
"final_control_logit_diff": control,
}
pair_summaries.append(summary)
severity = _control_bias_severity(control, control_threshold)
token_bias_diagnostics.append(
{
"pair_id": pr.get("pair_id"),
"family": meta["family"],
"canonical_id": meta["canonical_id"],
"variant": meta["variant"],
"headline": meta["headline"],
"final_control_logit_diff": _float_or_none(control),
"severity": severity,
}
)
if _finite(control) and float(control) > control_threshold:
warnings.append(
{
"pair_id": pr.get("pair_id"),
"final_control_logit_diff": float(control),
"threshold": control_threshold,
}
)
return {
"num_pairs": len(pair_summaries),
"control_bias_threshold": control_threshold,
"control_bias_warnings": warnings,
"token_bias_diagnostics": token_bias_diagnostics,
"pair_summaries": pair_summaries,
}
def _control_bias_severity(value: Any, threshold: float) -> str:
if not _finite(value):
return "unknown"
numeric = float(value)
if numeric > threshold:
return "biased_toward_correct"
if numeric > 0:
return "mild_correct_prior"
return "neutral_or_opposite"
def _last_logit_diff(rows: list[dict[str, Any]]) -> float | None:
if not rows:
return None
rows = sorted(rows, key=lambda row: int(row.get("layer", 0)))
value = rows[-1].get("logit_diff")
return float(value) if _finite(value) else None
def _summarize_attention(
data: dict[str, Any],
pair_meta: dict[str, dict[str, Any]],
*,
seed: int,
random_baseline_samples: int,
) -> dict[str, Any]:
pair_summaries = []
all_rows: list[dict[str, Any]] = []
margin_counter: Counter[str] = Counter()
patch_counter: Counter[str] = Counter()
headline_margin_counter: Counter[str] = Counter()
headline_patch_counter: Counter[str] = Counter()
for pr in data.get("pair_results", []):
meta = _meta_for_pair(pr.get("pair_id"), pair_meta)
rows = pr.get("head_rows", [])
rows = [_row_with_metadata(row, pair_meta) for row in rows]
all_rows.extend(rows)
best_margin = pr.get("best_margin_head") or pr.get("best_head") or {}
best_patch = pr.get("best_patch_head") or _best_head(rows, "patch_recovery")
best_margin = _row_with_metadata(best_margin, pair_meta) if best_margin else {}
best_patch = _row_with_metadata(best_patch, pair_meta) if best_patch else {}
if best_margin:
margin_counter[_head_id(best_margin)] += 1
if best_patch:
patch_counter[_head_id(best_patch)] += 1
if meta["headline"] and best_margin:
headline_margin_counter[_head_id(best_margin)] += 1
if meta["headline"] and best_patch:
headline_patch_counter[_head_id(best_patch)] += 1
pair_summaries.append(
{
"pair_id": pr.get("pair_id"),
"family": meta["family"],
"canonical_id": meta["canonical_id"],
"variant": meta["variant"],
"headline": meta["headline"],
"clean_logit_diff": pr.get("clean_logit_diff"),
"corrupted_logit_diff": pr.get("corrupted_logit_diff"),
"best_margin_head": _compact_head(best_margin),
"best_patch_head": _compact_head(best_patch),
"best_margin_patch_recovery": _float_or_none(best_margin.get("patch_recovery")),
"best_margin_recovery_margin": _float_or_none(best_margin.get("recovery_margin")),
"best_margin_percentile": _percentile_rank(
[row.get("recovery_margin") for row in rows],
best_margin.get("recovery_margin"),
),
"random_head_baseline": _random_head_baseline(
rows,
pair_id=str(pr.get("pair_id")),
seed=seed,
samples=random_baseline_samples,
),
"best_patch_recovery": _float_or_none(best_patch.get("patch_recovery")),
"best_patch_recovery_margin": _float_or_none(best_patch.get("recovery_margin")),
}
)
finite_rows = [
row for row in all_rows if _finite(row.get("recovery_margin"))
]
mean_heads = _top_mean_margin_heads(finite_rows)
headline_rows = [row for row in finite_rows if bool(row.get("headline", True))]
return {
"num_pairs": len(pair_summaries),
"total_head_rows": len(all_rows),
"finite_margin_rows": len(finite_rows),
"repeated_best_margin_heads": {
head: count for head, count in margin_counter.items() if count > 1
},
"repeated_best_patch_heads": {
head: count for head, count in patch_counter.items() if count > 1
},
"headline_repeated_best_margin_heads": {
head: count for head, count in headline_margin_counter.items() if count > 1
},
"headline_repeated_best_patch_heads": {
head: count for head, count in headline_patch_counter.items() if count > 1
},
"top_mean_margin_heads": mean_heads,
"top_headline_control_adjusted_heads": _top_mean_margin_heads(headline_rows),
"family_summaries": _summarize_attention_groups(pair_summaries, "family"),
"paraphrase_stability": _summarize_attention_groups(pair_summaries, "canonical_id"),
"pair_summaries": pair_summaries,
}
def _row_with_metadata(
row: dict[str, Any],
pair_meta: dict[str, dict[str, Any]],
) -> dict[str, Any]:
if not row:
return {}
meta = _meta_for_pair(row.get("pair_id"), pair_meta)
merged = dict(row)
merged.setdefault("family", meta["family"])
merged.setdefault("canonical_id", meta["canonical_id"])
merged.setdefault("variant", meta["variant"])
merged.setdefault("headline", meta["headline"])
if "control_adjusted_recovery" not in merged:
merged["control_adjusted_recovery"] = merged.get("recovery_margin")
return merged
def _percentile_rank(values: list[Any], value: Any) -> float | None:
finite = sorted(float(v) for v in values if _finite(v))
if not finite or not _finite(value):
return None
numeric = float(value)
below_or_equal = sum(1 for v in finite if v <= numeric)
return 100.0 * below_or_equal / len(finite)
def _random_head_baseline(
rows: list[dict[str, Any]],
*,
pair_id: str,
seed: int,
samples: int,
) -> dict[str, Any]:
finite = [row for row in rows if _finite(row.get("recovery_margin"))]
if not finite:
return {
"samples": 0,
"mean_recovery_margin": None,
"median_recovery_margin": None,
"p95_recovery_margin": None,
"max_recovery_margin": None,
}
digest = hashlib.sha256(f"{seed}:{pair_id}".encode("utf-8")).hexdigest()
rng = random.Random(int(digest[:16], 16))
sample_count = min(max(int(samples), 1), len(finite))
sampled = rng.sample(finite, sample_count)
margins = sorted(float(row["recovery_margin"]) for row in sampled)
return {
"samples": sample_count,
"mean_recovery_margin": sum(margins) / len(margins),
"median_recovery_margin": _percentile_value(margins, 0.50),
"p95_recovery_margin": _percentile_value(margins, 0.95),
"max_recovery_margin": max(margins),
}
def _percentile_value(sorted_values: list[float], q: float) -> float | None:
if not sorted_values:
return None
if len(sorted_values) == 1:
return sorted_values[0]
pos = _clamp_float(q, 0.0, 1.0) * (len(sorted_values) - 1)
lower = int(math.floor(pos))
upper = int(math.ceil(pos))
if lower == upper:
return sorted_values[lower]
frac = pos - lower
return sorted_values[lower] * (1 - frac) + sorted_values[upper] * frac
def _clamp_float(value: float, lo: float, hi: float) -> float:
return max(lo, min(hi, value))
def _summarize_attention_groups(
pair_summaries: list[dict[str, Any]],
key: str,
) -> list[dict[str, Any]]:
grouped: dict[str, list[dict[str, Any]]] = defaultdict(list)
for row in pair_summaries:
grouped[str(row.get(key) or "unknown")].append(row)
summaries: list[dict[str, Any]] = []
for group_id, rows in sorted(grouped.items()):
margins = [
float(row["best_margin_recovery_margin"])
for row in rows
if _finite(row.get("best_margin_recovery_margin"))
]
patches = [
float(row["best_patch_recovery"])
for row in rows
if _finite(row.get("best_patch_recovery"))
]
margin_heads = Counter(
_head_id(row["best_margin_head"])
for row in rows
if row.get("best_margin_head")
)
patch_heads = Counter(
_head_id(row["best_patch_head"])
for row in rows
if row.get("best_patch_head")
)
summaries.append(
{
key: group_id,
"family": rows[0].get("family", "general"),
"num_pairs": len(rows),
"num_variants": len({row.get("variant") for row in rows}),
"headline_pairs": sum(1 for row in rows if row.get("headline")),
"variants": sorted(str(row.get("variant", "base")) for row in rows),
"mean_best_margin": _mean(margins),
"mean_best_patch_recovery": _mean(patches),
"best_margin_heads": dict(margin_heads),
"best_patch_heads": dict(patch_heads),
"stable_best_margin_heads": {
head: count for head, count in margin_heads.items() if count > 1
},
"stable_best_patch_heads": {
head: count for head, count in patch_heads.items() if count > 1
},
}
)
return summaries
def _best_head(rows: list[dict[str, Any]], key: str) -> dict[str, Any]:
finite = [row for row in rows if _finite(row.get(key))]
if not finite:
return {}
return max(finite, key=lambda row: float(row[key]))
def _compact_head(head: dict[str, Any]) -> dict[str, Any]:
if not head:
return {}
return {
"layer": head.get("layer"),
"head": head.get("head"),
"patch_recovery": _float_or_none(head.get("patch_recovery")),
"control_recovery": _float_or_none(head.get("control_recovery")),
"recovery_margin": _float_or_none(head.get("recovery_margin")),
}
def _head_id(head: dict[str, Any]) -> str:
return f"L{head.get('layer')}H{head.get('head')}"
def _top_mean_margin_heads(rows: list[dict[str, Any]], limit: int = 10) -> list[dict[str, Any]]:
grouped: dict[tuple[int, int], list[float]] = defaultdict(list)
for row in rows:
grouped[(int(row["layer"]), int(row["head"]))].append(float(row["recovery_margin"]))
ranked = []
for (layer, head), values in grouped.items():
ranked.append(
{
"layer": layer,
"head": head,
"mean_recovery_margin": sum(values) / len(values),
"max_recovery_margin": max(values),
"min_recovery_margin": min(values),
"num_pairs": len(values),
}
)
return sorted(ranked, key=lambda row: row["mean_recovery_margin"], reverse=True)[:limit]
def _summarize_gradient(
data: dict[str, Any],
pair_meta: dict[str, dict[str, Any]],
) -> dict[str, Any]:
pair_summaries = []
total = 0
finite = 0
top1: Counter[int] = Counter()
top3: Counter[int] = Counter()
for pr in data.get("pair_results", []):
meta = _meta_for_pair(pr.get("pair_id"), pair_meta)
rows = pr.get("layer_scores", [])
total += len(rows)
finite_rows = [row for row in rows if _finite(row.get("attribution"))]
finite += len(finite_rows)
tops = pr.get("top_5_layers", [])
if tops:
top1[int(tops[0]["layer"])] += 1
for row in tops[:3]:
top3[int(row["layer"])] += 1
pair_summaries.append(
{
"pair_id": pr.get("pair_id"),
"family": meta["family"],
"canonical_id": meta["canonical_id"],
"variant": meta["variant"],
"headline": meta["headline"],
"finite_scores": len(finite_rows),
"total_scores": len(rows),
"top_5_layers": [int(row["layer"]) for row in tops[:5]],
}
)
return {
"num_pairs": len(pair_summaries),
"total_scores": total,
"finite_scores": finite,
"nan_scores": total - finite,
"top1_layer_counts": dict(sorted(top1.items())),
"top3_layer_counts": dict(sorted(top3.items())),
"pair_summaries": pair_summaries }
def _is_nan(value: Any) -> bool:
try:
return math.isnan(float(value))
except (TypeError, ValueError):
return False
def _float_or_none(value: Any) -> float | None:
return float(value) if _finite(value) else None
def _render_run_log(summary: dict[str, Any]) -> str:
lines = [
"Mechanistic Interpretability Probe Run",
"=====================================",
"",
f"Run directory: {summary['run_dir']}",
f"Model: {summary.get('model_name')}",
f"Probes: {', '.join(summary.get('probes', []))}",
f"Verdict: {summary['verdict']['status']} - {summary['verdict']['claim']}",
"",
]
warnings = summary["verdict"].get("warnings", [])
if warnings:
lines.append("Warnings:")
for warning in warnings:
lines.append(f" - {warning}")
lines.append("")
lens = summary["logit_lens"]
lines.append("Control Quality:")
if lens["control_bias_warnings"]:
for row in lens["control_bias_warnings"]:
lines.append(
" "
f"{row['pair_id']}: final_control_logit_diff="
f"{row['final_control_logit_diff']:.4f}"
)
else:
lines.append(" No control bias warnings.")
mild = [
row for row in lens.get("token_bias_diagnostics", [])
if row.get("severity") == "mild_correct_prior"
]
if mild:
lines.append(f" Mild positive control priors: {len(mild)}")
lines.append("")
attention = summary["attention"]
if attention.get("family_summaries"):
lines.append("Pair Families:")
for row in attention["family_summaries"]:
repeated = _head_counts(row.get("stable_best_margin_heads", {}))
lines.append(
" "
f"{row['family']}: pairs={row['num_pairs']} "
f"headline={row['headline_pairs']} "
f"mean_margin={_fmt(row.get('mean_best_margin'))} "
f"stable_margin={repeated or 'none'}"
)
lines.append("")
paraphrases = [
row for row in attention.get("paraphrase_stability", [])
if row.get("num_variants", 0) > 1
]
if paraphrases:
lines.append("Paraphrase Stability:")
for row in paraphrases:
stable = _head_counts(row.get("stable_best_margin_heads", {}))
margin_heads = _head_counts(row.get("best_margin_heads", {}))
lines.append(
" "
f"{row['canonical_id']}: variants={row['num_variants']} "
f"stable_margin={stable or 'none'} "
f"all_margin_heads={margin_heads or 'none'}"
)
lines.append("")
lines.append("Attention Highlights:")
lines.append(
f" finite margins: {attention['finite_margin_rows']}/"
f"{attention['total_head_rows']}"
)
for row in attention["pair_summaries"]:
bm = row.get("best_margin_head", {})
bp = row.get("best_patch_head", {})
lines.append(
" "
f"{row['pair_id']}: "
f"[{row.get('family', 'general')}/{row.get('variant', 'base')}] "
f"best-margin L{bm.get('layer')}H{bm.get('head')} "
f"margin={_fmt(bm.get('recovery_margin'))} "
f"patch={_fmt(bm.get('patch_recovery'))} "
f"pct={_fmt(row.get('best_margin_percentile'))}; "
f"best-patch L{bp.get('layer')}H{bp.get('head')} "
f"patch={_fmt(bp.get('patch_recovery'))} "
f"margin={_fmt(bp.get('recovery_margin'))}"
)
lines.append("")
gradient = summary["gradient"]
lines.append("Gradient Attribution:")
lines.append(
f" finite scores: {gradient['finite_scores']}/{gradient['total_scores']}"
)
lines.append(f" top-1 layer counts: {gradient['top1_layer_counts']}")
lines.append(f" top-3 layer counts: {gradient['top3_layer_counts']}")
lines.append("")
lines.extend(
[
"Interpretation Notes:",
" - Best-margin means control-adjusted recovery: patch recovery minus control recovery.",
" - Best-patch heads show the largest raw causal recovery, even if controls also move.",
" - Headline claims should focus on repeated heads across factual paraphrases.",
]
)
return "\n".join(lines).rstrip() + "\n"
def _render_ablation_log(summary: dict[str, Any]) -> str:
lines = [
"Mechanistic Interpretability Ablation Run",
"========================================",
"",
f"Run directory: {summary['run_dir']}",
f"Model: {summary.get('model_name')}",
f"Targets: {', '.join(str(target) for target in summary.get('targets', []))}",
f"Modes: {', '.join(str(mode) for mode in summary.get('modes', []))}",
f"Verdict: {summary['verdict']['status']} - {summary['verdict']['claim']}",
"",
]
warnings = summary["verdict"].get("warnings", [])
if warnings:
lines.append("Warnings:")
for warning in warnings:
lines.append(f" - {warning}")
lines.append("")
aggregate = summary.get("aggregate", {})
lines.append("Aggregate Drops:")
for row in aggregate.get("by_target_mode_scope", []):
scope = "headline" if row.get("headline") else "specificity"
lines.append(
" "
f"{row.get('target_head')} {row.get('ablation_mode')} {scope}: "
f"pairs={row.get('num_pairs')} "
f"mean_drop={_fmt(row.get('mean_logit_diff_drop'))} "
f"mean_rel={_fmt(row.get('mean_relative_drop_fraction'))} "
f"mean_gap={_fmt(row.get('mean_gap_normalized_drop'))}"
)
lines.append("")
if aggregate.get("specificity"):
lines.append("Specificity:")
for row in aggregate["specificity"]:
lines.append(
" "
f"{row.get('target_head')} {row.get('ablation_mode')}: "
f"headline_drop={_fmt(row.get('headline_mean_drop'))} "
f"control_drop={_fmt(row.get('control_mean_drop'))} "
f"ratio={_fmt(row.get('factual_to_control_drop_ratio'))}"
)
lines.append("")
lines.append("Pair Highlights:")
for row in summary.get("pair_summaries", []):
best = row.get("largest_drop", {})
scope = "headline" if row.get("headline") else "specificity"
lines.append(
" "
f"{row.get('pair_id')} [{row.get('family')}/{row.get('variant')}/{scope}]: "
f"clean={_fmt(row.get('clean_logit_diff'))} "
f"corrupted={_fmt(row.get('corrupted_logit_diff'))}; "
f"largest_drop={best.get('target_head')} {best.get('ablation_mode')} "
f"drop={_fmt(best.get('logit_diff_drop'))} "
f"gap={_fmt(best.get('gap_normalized_drop'))}"
)
lines.append("")
lines.extend(
[
"Interpretation Notes:",
" - Positive drop means ablation reduced the clean correct-minus-incorrect logit diff.",
" - Mean ablation is the primary intervention; zero ablation is a sanity check.",
" - Specificity increases when factual headline drops are much larger than non-factual control drops.",
]
)
return "\n".join(lines).rstrip() + "\n"
def _render_source_log(summary: dict[str, Any]) -> str:
lines = [
"Mechanistic Interpretability Source Run",
"======================================",
"",
f"Run directory: {summary['run_dir']}",
f"Model: {summary.get('model_name')}",
f"Target: {summary.get('target')}",
f"Source layers: {summary.get('source_layers')}",
f"Source position: {summary.get('source_position')}",
f"Verdict: {summary['verdict']['status']} - {summary['verdict']['claim']}",
"",
]
warnings = summary["verdict"].get("warnings", [])
if warnings:
lines.append("Warnings:")
for warning in warnings:
lines.append(f" - {warning}")
lines.append("")
aggregate = summary.get("aggregate", {})
if summary.get("source_position") == "sweep":
lines.append("Best-Token Layer Summary:")
for row in aggregate.get("best_token_layer_summaries", []):
top = row.get("top_tokens", [])[:3]
token_bits = []
for token in top:
token_bits.append(
f"{token.get('pair_id')} pos={token.get('source_position')} "
f"{token.get('clean_source_token')}->{token.get('corrupted_source_token')}"
)
lines.append(
" "
f"L{row.get('source_layer')}: "
f"pairs={row.get('num_pairs')} "
f"mean_best_target_rec={_fmt(row.get('mean_best_target_recovery_fraction'))} "
f"mean_best_shift={_fmt(row.get('mean_best_target_output_shift'))} "
f"mean_best_logit_rec={_fmt(row.get('mean_best_logit_recovery_fraction'))}"
)
if token_bits:
lines.append(" top_tokens: " + " | ".join(token_bits))
lines.append("")
lines.append("All-Token Mean Diagnostic:")
else:
lines.append("Source Layer Summary:")
for row in aggregate.get("source_layer_summaries", []):
lines.append(
" "
f"L{row.get('source_layer')}: "
f"mean_target_rec={_fmt(row.get('mean_target_recovery_fraction'))} "
f"mean_shift={_fmt(row.get('mean_target_output_shift'))} "
f"mean_logit_rec={_fmt(row.get('mean_logit_recovery_fraction'))}"
)
lines.append("")
best = summary.get("best_source_layer", {})
if best:
lines.append(
"Best Source Layer: "
f"L{best.get('source_layer')} "
f"mean_target_rec={_fmt(_source_layer_target_score(best))} "
f"mean_logit_rec={_fmt(_source_layer_logit_score(best))}"
)
lines.append("")
lines.append("Pair Highlights:")
for row in summary.get("pair_summaries", []):
lines.append(
" "
f"{row.get('pair_id')} [{row.get('family')}/{row.get('variant')}]: "
f"best_source=L{row.get('best_source_layer')} "
f"source_pos={row.get('source_position')} "
f"token={row.get('clean_source_token')}->{row.get('corrupted_source_token')} "
f"patched_tokens={row.get('source_positions_patched')} "
f"target_rec={_fmt(row.get('target_recovery_fraction'))} "
f"shift={_fmt(row.get('target_output_shift'))} "
f"logit_rec={_fmt(row.get('logit_recovery_fraction'))}"
)
lines.append("")
lines.extend(
[
"Interpretation Notes:",
" - target_rec measures how much the target head output moves along its clean-minus-corrupted direction.",
" - logit_rec measures downstream logit recovery from the same source-layer patch.",
" - Source candidates should show positive target recovery across multiple factual variants.",
]
)
return "\n".join(lines).rstrip() + "\n"
def _head_counts(counts: dict[str, Any]) -> str:
if not counts:
return ""
return ", ".join(
f"{head}x{count}" for head, count in sorted(counts.items(), key=lambda item: (-int(item[1]), item[0]))
)
def _fmt(value: Any) -> str:
try:
numeric = float(value)
except (TypeError, ValueError):
return "nan"
if math.isinf(numeric):
return "inf" if numeric > 0 else "-inf"
return f"{numeric:.4f}" if math.isfinite(numeric) else "nan"
def _create_run_zip(run_dir: Path, archive_root: Path) -> Path:
archive_root.mkdir(parents=True, exist_ok=True)
archive_path = archive_root / f"{run_dir.name}.zip"
with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
for path in sorted(run_dir.rglob("*")):
if path.is_file():
archive.write(path, path.relative_to(run_dir).as_posix())
return archive_path
def inspect_results_root(results_root: Path) -> int:
packages = sorted(
list(results_root.glob("mech_results_*"))
+ list(results_root.glob("mech_run_*"))
+ list(results_root.glob("mech_ablation_*"))
+ list(results_root.glob("mech_source_*")),
key=lambda path: path.name,
)
if not packages:
print(f"No result packages found in {results_root}")
return 1
latest = packages[-1]
print(f"Latest package: {latest}")
for log_name in ("logs/run_log.txt", "run_log.txt", "validation_log.txt"):
log_path = latest / log_name
if log_path.exists():
print("")
print(log_path.read_text(encoding="utf-8").rstrip())
break
_print_probe_summary(latest)
report = latest / "report.html"
if report.exists():
print(f"\nHTML report: {report}")
package_manifest = latest / "package_manifest.json"
integrity = latest / "integrity.json"
print("")
print(f"Package manifest: {'present' if package_manifest.exists() else 'missing'}")
print(f"Integrity: {'present' if integrity.exists() else 'missing'}")
return 0
def _print_probe_summary(run_dir: Path) -> None:
for probe in ("residual", "attention", "logit_lens", "gradient", "ablation", "source"):
path = run_dir / "raw" / f"{probe}_results.json"
if not path.exists():
path = run_dir / f"{probe}_results.json"
if not path.exists():
continue
data = json.loads(path.read_text(encoding="utf-8"))
pair_results = data.get("pair_results", [])
print(f"\n[{probe}] {len(pair_results)} pair(s) recorded")
for pr in pair_results:
pid = pr.get("pair_id", "?")
if probe == "attention":
_print_attention_pair(pid, pr)
elif probe == "logit_lens":
ctrl = pr.get("final_control_logit_diff")
flag = " [WARN] control biased" if _finite(ctrl) and float(ctrl) > 0.5 else ""
print(
f" {pid}: first positive layer = {pr.get('first_positive_layer', '?')}, "
f"control L23 = {_fmt(ctrl)}{flag}"
)
elif probe == "gradient":
top = pr.get("top_5_layers", [])
top_str = ", ".join(f"L{row['layer']}" for row in top[:3])
print(f" {pid}: top layers = {top_str}")
elif probe == "ablation":
_print_ablation_pair(pid, pr)
elif probe == "source":
_print_source_pair(pid, pr)
def _print_attention_pair(pid: str, pr: dict[str, Any]) -> None:
bm = pr.get("best_margin_head") or pr.get("best_head", {})
bp = pr.get("best_patch_head", {})
if bm:
print(
f" {pid}: best-margin L{bm.get('layer', '?')}H{bm.get('head', '?')} "
f"margin={_fmt(bm.get('recovery_margin'))} "
f"patch={_fmt(bm.get('patch_recovery'))}"
)
if bp and bp != bm:
print(
f" {pid}: best-patch L{bp.get('layer', '?')}H{bp.get('head', '?')} "
f"patch={_fmt(bp.get('patch_recovery'))} "
f"margin={_fmt(bp.get('recovery_margin'))}"
)
def _print_ablation_pair(pid: str, pr: dict[str, Any]) -> None:
rows = pr.get("head_results", [])
best = _best_ablation_row(rows, "logit_diff_drop")
if not best:
print(f" {pid}: no finite ablation drops")
return
print(
f" {pid}: largest drop {best.get('target_head')} "
f"{best.get('ablation_mode')} "
f"drop={_fmt(best.get('logit_diff_drop'))} "
f"gap={_fmt(best.get('gap_normalized_drop'))}"
)
def _print_source_pair(pid: str, pr: dict[str, Any]) -> None:
best = pr.get("best_source_layer") or {}
if not best:
print(f" {pid}: no finite source recovery")
return
print(
f" {pid}: best source L{best.get('source_layer')} "
f"target_rec={_fmt(best.get('target_recovery_fraction'))} "
f"logit_rec={_fmt(best.get('logit_recovery_fraction'))}"
)
def _cmd_inspect(args: argparse.Namespace) -> int:
return inspect_results_root(Path(args.results_root))
def _cmd_package(args: argparse.Namespace) -> int:
import zipfile as _zf
out = Path(args.output or "mech_workbench_core.zip").resolve()
out.parent.mkdir(parents=True, exist_ok=True)
include_dirs = ["mech_workbench", "configs", "prompt curation", "scripts"]
include_files = [
"pyproject.toml",
"README.md",
"requirements.txt",
]
with _zf.ZipFile(out, "w", compression=_zf.ZIP_DEFLATED) as zf:
for d in include_dirs:
p = ROOT / d
if p.exists():
for f in p.rglob("*"):
if f.is_file() and "__pycache__" not in str(f):
zf.write(f, f.relative_to(ROOT))
for name in include_files:
p = ROOT / name
if p.exists():
zf.write(p, name)
print(f"Package written: {out}")
return 0
def _cmd_prompt_validate(args: argparse.Namespace) -> int:
"""Validate prompt pairs against the loaded model."""
from mech_workbench.config import load_config
from mech_workbench.validation import validate_prompt_pairs
config = load_config(args.config)
if getattr(args, "limit", None):
config = dataclasses.replace(config, prompt_pairs=config.prompt_pairs[: args.limit])
from mech_workbench.model_loader import load_model_and_tokenizer
model, tokenizer = load_model_and_tokenizer(
config.model_name, device=config.device, dtype=config.dtype
)
results = validate_prompt_pairs(model, tokenizer, config.prompt_pairs)
total = len(results)
valid = sum(1 for r in results if r["status"] == "valid")
flagged = sum(1 for r in results if r["status"] == "flagged")
excluded = sum(1 for r in results if r["status"] == "excluded")
print(f"\nValidation: {valid}/{total} valid, {flagged} flagged, {excluded} excluded\n")
for r in results:
sym = {"valid": "[OK]", "flagged": "[!!]", "excluded": "[XX]"}.get(r["status"], "[??]")
print(f" {sym} {r['pair_id']:30s} {r['status']:10s} {r.get('reason', '')}")
if excluded > 0 or flagged > 0:
print(f"\n[FAIL] {flagged + excluded} prompt(s) did not pass validation.")
print("Fix or remove flagged pairs before running discovery.")
return 1
print("\n[OK] All prompts passed validation.")
return 0
def _cmd_verify(args: argparse.Namespace) -> int:
"""Verify a completed run bundle."""
from mech_workbench.bundle import verify_bundle
import glob
patterns = args.run_path
paths: list[Path] = []
for pattern in patterns:
matched = glob.glob(pattern)
if matched:
paths.extend(Path(m) for m in matched)
else:
paths.append(Path(pattern))
if not paths:
print("[error] No run paths specified.")
return 1
all_ok = True
for path in paths:
ok = verify_bundle(path)
if not ok:
all_ok = False
return 0 if all_ok else 1
def _write_run_card(run_dir: Path, config: Any, stages: list[str]) -> None:
card_path = run_dir / "RUN_CARD.json"
existing: dict[str, Any] = {}
if card_path.exists():
try:
existing = json.loads(card_path.read_text(encoding="utf-8"))
except Exception:
pass
completed = list(existing.get("stages_completed", []))
for s in stages:
if s not in completed:
completed.append(s)
card = {
"created_at_utc": existing.get("created_at_utc", datetime.now(timezone.utc).isoformat()),
"updated_at_utc": datetime.now(timezone.utc).isoformat(),
"model": getattr(config, "model_name", None),
"prompt_pairs": len(getattr(config, "prompt_pairs", [])),
"stages_completed": completed,
"claim_status": "pending_review",
}
card_path.write_text(json.dumps(card, indent=2) + "\n", encoding="utf-8")
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="mech",
description="Mechanistic Interpretability Workbench CLI",
)
sub = parser.add_subparsers(dest="command", required=True)
p_run = sub.add_parser("run", help="Load model and run interpretability probes.")
p_run.add_argument(
"--config",
default=str(ROOT / "configs" / "discovery_4b.yaml"),
help="Path to experiment YAML config.",
)
p_run.add_argument(
"--probe",
default=None,
metavar="PROBES",
help=(
"Comma-separated probe names. Choices: residual, attention, "
"logit_lens, gradient. Default suite: logit_lens,gradient,attention."
),
)
p_run.add_argument(
"--limit",
type=int,
default=None,
metavar="N",
help="Only process the first N prompt pairs.",
)
p_run.add_argument(
"--results-root",
default=str(ROOT / "results"),
help="Root directory for timestamped run folders.",
)
p_run.add_argument(
"--archive-root",
default=None,
help="Optional directory where a downloadable <run_name>.zip is written.",
)
p_run.add_argument(
"--dry-run",
action="store_true",
help="Validate config and print summary without loading the model.",
)
p_abl = sub.add_parser("ablate", help="Run targeted attention-head ablations.")
p_abl.add_argument(
"--config",
default=str(ROOT / "configs" / "ablation_4b.yaml"),
help="Path to ablation YAML config.",
)
p_abl.add_argument(
"--target",
default="L31H1",
help="Primary head target, e.g. L31H1. Comma-separated targets are allowed.",
)
p_abl.add_argument(
"--also",
default="",
help="Optional comma-separated comparison heads. Defaults to none.",
)
p_abl.add_argument(
"--mode",
default="both",
help="Ablation mode: mean, zero, mean,zero, or both.",
)
p_abl.add_argument(
"--combined",
action="store_true",
help="Ablate all targets simultaneously in one forward pass.",
)
p_abl.add_argument(
"--limit",
type=int,
default=None,
metavar="N",
help="Only process the first N prompt pairs.",
)
p_abl.add_argument(
"--results-root",
default=str(ROOT / "results"),
help="Root directory for timestamped ablation folders.",
)
p_abl.add_argument(
"--archive-root",
default=None,
help="Optional directory where a downloadable <run_name>.zip is written.",
)
p_abl.add_argument(
"--dry-run",
action="store_true",
help="Validate config and ablation settings without loading the model.",
)
p_src = sub.add_parser("source", help="Probe source layers for a target attention head.")
p_src.add_argument(
"--config",
default=str(ROOT / "configs" / "discovery_4b.yaml"),
help="Path to source-probing YAML config.",
)
p_src.add_argument(
"--target",
default="L31H1",
help="Target head, e.g. L31H1.",
)
p_src.add_argument(
"--source-layers",
default="15-30",
help="Candidate source layers, e.g. 15-30 or 12,15,18-28.",
)
p_src.add_argument(
"--source-position",
default="sweep",
help="Residual token positions to restore: sweep, all, last, first, or an integer.",
)
p_src.add_argument(
"--limit",
type=int,
default=None,
metavar="N",
help="Only process the first N prompt pairs.",
)
p_src.add_argument(
"--results-root",
default=str(ROOT / "results"),
help="Root directory for timestamped source-probing folders.",
)
p_src.add_argument(
"--archive-root",
default=None,
help="Optional directory where a downloadable <run_name>.zip is written.",
)
p_src.add_argument(
"--dry-run",
action="store_true",
help="Validate config and source settings without loading the model.",
)
p_pval = sub.add_parser("validate", help="Validate prompt pairs against the model.")
p_pval.add_argument(
"--config",
default=str(ROOT / "configs" / "discovery_4b.yaml"),
help="Path to experiment YAML config.",
)
p_pval.add_argument(
"--limit",
type=int,
default=None,
metavar="N",
help="Only validate the first N prompt pairs.",
)
p_vfy = sub.add_parser("verify", help="Verify a completed run bundle.")
p_vfy.add_argument(
"run_path",
nargs="+",
metavar="PATH",
help="Path(s) to run directory/directories to verify.",
)
p_ins = sub.add_parser("inspect", help="Print the latest result summary.")
p_ins.add_argument("--results-root", default=str(ROOT / "results"))
p_pkg = sub.add_parser("package", help="Build a distributable zip of the codebase.")
p_pkg.add_argument("--output", default=None)
return parser
def main() -> int:
parser = _build_parser()
args = parser.parse_args()
dispatch = {
"run": _cmd_run,
"ablate": _cmd_ablate,
"source": _cmd_source,
"validate": _cmd_prompt_validate,
"verify": _cmd_verify,
"inspect": _cmd_inspect,
"package": _cmd_package,
}
return dispatch[args.command](args)
if __name__ == "__main__":
raise SystemExit(main())