AQIT 0.1.0
Loading...
Searching...
No Matches
feature_logits_check.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2"""`aquin feature logit --check` — save JSON + diagram to the current working directory."""
3
4from __future__ import annotations
5
6import json
7from datetime import datetime, timezone
8from pathlib import Path
9from typing import Any
10
11_JSON_NAME = "feature-logits-check.json"
12_PNG_NAME = "feature-logits-check.png"
13
14
15def _flatten_feature_logits_result(result: dict[str, Any]) -> dict[str, Any]:
16 if result.get("error"):
17 return dict(result)
18
19 data = result.get("content") or result
20 if not isinstance(data, dict):
21 return {"error": "Invalid feature-logits result"}
22
23 boosts = data.get("boosts") or data.get("top") or []
24 suppresses = data.get("suppresses") or data.get("bottom") or []
25 return {
26 **data,
27 "boosts": boosts,
28 "suppresses": suppresses,
29 "top": boosts,
30 "bottom": suppresses,
31 }
32
33
35 result: dict[str, Any],
36 *,
37 tool_name: str | None,
38 cwd: str | Path,
39) -> tuple[Path, Path]:
40 cwd = Path(cwd)
41 json_path = cwd / _JSON_NAME
42 png_path = cwd / _PNG_NAME
43
45 payload = {
46 "saved_at": datetime.now(timezone.utc).isoformat(),
47 "tool": tool_name,
48 **flat,
49 }
50 json_path.write_text(json.dumps(payload, indent=2, default=str), encoding="utf-8")
51 _plot_feature_logits_check(flat, png_path)
52 return json_path, png_path
53
54
55def _plot_feature_logits_check(result: dict[str, Any], png_path: Path) -> None:
56 import matplotlib
57
58 matplotlib.use("Agg")
59 import matplotlib.pyplot as plt
60
61 if result.get("error"):
62 fig, ax = plt.subplots(figsize=(6, 2), facecolor="#0f1117")
63 ax.axis("off")
64 ax.text(0.5, 0.5, f"Error: {result['error']}", ha="center", va="center", wrap=True, color="#e5e7eb")
65 fig.savefig(png_path, dpi=140, bbox_inches="tight", facecolor="#0f1117")
66 plt.close(fig)
67 return
68
69 ref = result.get("feature_ref") or result.get("feature_idx", "?")
70 layer = result.get("layer", "?")
71 label = str(result.get("label") or result.get("causal_label") or "")
72 boosts = result.get("boosts") or []
73 suppresses = result.get("suppresses") or []
74
75 title = f"Feature logits — {ref} · layer {layer}"
76 if label:
77 title += f" · {label[:32]}"
78
79 fig, axes = plt.subplots(1, 2, figsize=(11, max(4, min(12, max(len(boosts), len(suppresses)) * 0.32 + 1.8))), facecolor="#0f1117")
80 fig.suptitle(title, color="#e5e7eb", fontsize=11, y=1.02)
81
82 _plot_token_panel(axes[0], boosts, "Boosted tokens", "#34d399", positive=True)
83 _plot_token_panel(axes[1], suppresses, "Suppressed tokens", "#f87171", positive=False)
84
85 fig.tight_layout()
86 fig.savefig(png_path, dpi=140, bbox_inches="tight", facecolor="#0f1117")
87 plt.close(fig)
88
89
91 ax: Any,
92 rows: list[dict[str, Any]],
93 title: str,
94 color: str,
95 *,
96 positive: bool,
97) -> None:
98 ax.set_facecolor("#0f1117")
99 ax.set_title(title, color="#e5e7eb", fontsize=10)
100
101 if not rows:
102 ax.axis("off")
103 ax.text(0.5, 0.5, "No data", ha="center", va="center", color="#9ca3af")
104 return
105
106 labels = [_short_token(str(r.get("token") or "")) for r in rows]
107 vals = [float(r.get("logit") or 0) for r in rows]
108 scale = max((abs(v) for v in vals), default=1.0) or 1.0
109 widths = [abs(v) / scale for v in vals]
110
111 y_pos = list(range(len(rows)))
112 ax.barh(y_pos, widths, color=color, height=0.72, alpha=0.9)
113 ax.set_yticks(y_pos)
114 ax.set_yticklabels(labels, fontsize=8, color="#d1d5db")
115 ax.invert_yaxis()
116 ax.set_xlabel("Relative |logit|", color="#9ca3af", fontsize=8)
117 ax.tick_params(axis="x", colors="#6b7280", labelsize=7)
118 ax.grid(axis="x", alpha=0.2, color="#4b5563")
119 for spine in ax.spines.values():
120 spine.set_color("#374151")
121
122 for i, (v, w) in enumerate(zip(vals, widths)):
123 sign = "+" if positive else ""
124 ax.text(w + 0.02, i, f"{sign}{v:.3f}", va="center", fontsize=7, color="#9ca3af")
125
126
127def _short_token(tok: str, limit: int = 16) -> str:
128 t = tok.replace("Ġ", " ").replace("##", "").strip()
129 if len(t) <= limit:
130 return t or "·"
131 return t[: limit - 1] + "…"
dict[str, Any] _flatten_feature_logits_result(dict[str, Any] result)
None _plot_feature_logits_check(dict[str, Any] result, Path png_path)
tuple[Path, Path] write_feature_logits_check(dict[str, Any] result, *, str|None tool_name, str|Path cwd)
None _plot_token_panel(Any ax, list[dict[str, Any]] rows, str title, str color, *, bool positive)
str _short_token(str tok, int limit=16)