7Tool dispatch bridge: call(tool_name, args, ctx) -> dict.
8Routes tool names to their compute implementations with no HTTP, no tab_queue,
9and no gpu_executor. All GPU imports happen lazily inside handler functions so
10this module imports cleanly even without a GPU or heavy ML deps installed.
12ctx must contain: session_id, api_key, base_url, state (dict with activeModelId, memory).
14from __future__
import annotations
16from typing
import Any, Callable
21_SANDBOX_STATE: dict[str, Any] = {}
30 state = ctx.get(
"state", {})
31 model_id = args.get(
"model_id")
or state.get(
"activeModelId")
or get_active_model_id()
33 raise ValueError(
"No model loaded for this session. Run: aquin load --model <id>")
37def _as_str_list(value: Any, *, fallback: list[str] |
None =
None) -> list[str]:
38 """Normalize CLI/API list flags — never iterate a JSON string character-by-character."""
43 if isinstance(value, list):
44 return [str(v)
for v
in value]
45 if isinstance(value, str):
46 stripped = value.strip()
47 if stripped.startswith(
"["):
49 parsed = json.loads(stripped)
50 if isinstance(parsed, list):
51 return [str(v)
for v
in parsed]
52 except json.JSONDecodeError:
59 """Parse topics and other object-shaped CLI flags."""
64 if isinstance(value, dict):
66 if isinstance(value, str):
67 stripped = value.strip()
68 if stripped.startswith(
"{"):
70 parsed = json.loads(stripped)
71 if isinstance(parsed, dict):
73 except json.JSONDecodeError:
79 """Parse JSON array CLI flags (capabilities, evals, etc.)."""
84 if isinstance(value, list):
86 if isinstance(value, str):
87 stripped = value.strip()
88 if stripped.startswith(
"["):
90 parsed = json.loads(stripped)
91 if isinstance(parsed, list):
93 except json.JSONDecodeError:
99 """Return the loaded model, loading it if necessary."""
102 model = get_loaded_model()
104 model_id = ctx.get(
"state", {}).get(
"activeModelId")
or get_active_model_id()
106 raise ValueError(
"No model loaded for this session. Run: aquin load --model <id>")
107 model = load_model(model_id)
112 return {
"status":
"not_implemented",
"tool": tool_name}
125 model_id = resolve_model_id(model_id)
126 except Exception
as e:
127 return {
"error": str(e)}
129 if args.get(
"eval")
and (args.get(
"save")
or args.get(
"out")
or args.get(
"output_path")):
130 return {
"error":
"Cannot combine --eval with --save. Export the vector first, then steer --eval --vector <path>."}
132 vector_path = args.get(
"vector")
or args.get(
"vector_path")
133 feature_idx_raw = args.get(
"feature_idx")
135 feature_idx = int(feature_idx_raw)
if feature_idx_raw
is not None else None
136 elif feature_idx_raw
is not None:
137 feature_idx = int(feature_idx_raw)
139 return {
"error":
"Provide --feature_idx <n> or --vector <path> to a saved LAT file."}
141 steer_strength = float(args.get(
"steer_strength", args.get(
"strength", 20.0)))
142 explicit_prompt = args.get(
"prompt")
143 if explicit_prompt
is not None:
144 prompt = str(explicit_prompt)
145 elif args.get(
"eval"):
148 prompt = ctx.get(
"state", {}).get(
"memory", {}).get(
"lastPrompt")
or "Hello"
149 layer = args.get(
"layer")
150 if layer
is not None:
152 max_new_tokens = int(args.get(
"max_new_tokens", 80))
154 return run_steer_with_vector(
157 steer_strength=steer_strength,
159 feature_idx=feature_idx,
160 vector_path=vector_path,
161 feature_label=args.get(
"feature_label"),
162 max_new_tokens=max_new_tokens,
174 model_id = resolve_model_id(model_id)
175 except Exception
as e:
176 return {
"error": str(e)}
178 output = args.get(
"save")
or args.get(
"out")
or args.get(
"output_path")
180 return {
"error":
"--save <path> is required."}
182 feature_idx_raw = args.get(
"feature_idx")
183 if feature_idx_raw
is None:
184 return {
"error":
"--feature_idx <n> is required."}
186 layer = args.get(
"layer")
188 return extract_steer_vector(
190 int(feature_idx_raw),
192 layer=int(layer)
if layer
is not None else None,
193 feature_label=args.get(
"feature_label"),
194 probe_id=args.get(
"probe_id"),
196 except (ValueError, FileNotFoundError)
as e:
197 return {
"error": str(e)}
198 except Exception
as e:
199 return {
"error": f
"steer --save failed: {e}"}
209 model_id = resolve_model_id(model_id)
210 except Exception
as e:
211 return {
"error": str(e)}
214 features = args.get(
"features", [])
216 feature_idx = int(args.get(
"feature_idx", 0))
217 strength = float(args.get(
"steer_strength", args.get(
"strength", 20.0)))
218 features = [{
"feature_idx": feature_idx,
"strength": strength}]
220 prompt = args.get(
"prompt")
or ctx.get(
"state", {}).get(
"memory", {}).get(
"lastPrompt")
or "Hello"
221 layer = args.get(
"layer")
223 model = get_loaded_model()
226 model = load_model(model_id)
227 except Exception
as e:
228 return {
"error": str(e)}
231 sae = load_sae(model_id, layer)
232 except Exception
as e:
233 return {
"error": f
"SAE load failed: {e}"}
235 cfg = get_config(model_id)
236 resolved_layer = layer
if layer
is not None else cfg[
"sae_layer"]
237 hook_name = f
"blocks.{resolved_layer}.hook_resid_post"
242 baseline = run_chat(prompt, model_id=model_id, max_new_tokens=80, temperature=0.0)
243 except Exception
as e:
244 return {
"error": f
"Baseline generation failed: {e}"}
248 fidx = int(f.get(
"feature_idx", 0))
249 s = float(f.get(
"strength", 20.0))
251 sae.W_dec[fidx].to(device=model.W_E.device, dtype=model.W_E.dtype),
255 def _multi_steer_hook(value, hook):
256 for vec, s
in steer_vecs:
257 value = value + s * vec.unsqueeze(0).unsqueeze(0)
261 fmt_prompt = _format_prompt(model, prompt)
262 tokens = model.to_tokens(fmt_prompt)
264 steered_token_ids = []
266 with torch.no_grad():
267 out = model.run_with_hooks(cur, fwd_hooks=[(hook_name, _multi_steer_hook)])
268 next_id = int(out[0, -1].argmax().item())
269 if next_id == model.tokenizer.eos_token_id:
271 steered_token_ids.append(next_id)
272 cur = torch.cat([cur, torch.tensor([[next_id]], device=cur.device)], dim=1)
273 steered_response = model.tokenizer.decode(steered_token_ids, skip_special_tokens=
True)
274 except Exception
as e:
275 steered_response = f
"[steer error: {e}]"
278 enriched_features = []
280 fidx = int(f.get(
"feature_idx", 0))
281 label = f.get(
"label")
or resolve_feature_label(fidx, ctx=ctx, args=args, layer=layer)
282 enriched_features.append({
286 "feature_ref": format_feature_ref(fidx, label),
290 "features": enriched_features,
291 "layer": resolved_layer,
293 "baseline_response": baseline,
294 "steered_response": steered_response,
303 model_id = resolve_model_id(model_id)
304 except Exception
as e:
305 return {
"error": str(e)}
306 model = get_loaded_model()
308 model = load_model(model_id)
309 query = args.get(
"query")
or args.get(
"prompt")
or "Hello"
310 templates_raw = args.get(
"templates")
312 return consistency_eval(query, model, templates=templates
or None)
320 model_id = resolve_model_id(model_id)
321 except Exception
as e:
322 return {
"error": str(e)}
323 model = get_loaded_model()
325 model = load_model(model_id)
327 return suppression_eval(model, topics=topics)
335 model_id = resolve_model_id(model_id)
336 except Exception
as e:
337 return {
"error": str(e)}
338 model = get_loaded_model()
340 model = load_model(model_id)
343 fallback=[args.get(
"prompt",
"Hello")],
347 return boundary_eval(prompts, model)
355 model_id = resolve_model_id(model_id)
356 except Exception
as e:
357 return {
"error": str(e)}
358 model = get_loaded_model()
360 model = load_model(model_id)
361 query = args.get(
"query")
or args.get(
"prompt")
or "Hello"
362 prompts =
_as_str_list(args.get(
"prompts"), fallback=[query])
363 consistency = consistency_eval(query, model)
364 suppression = suppression_eval(model)
365 boundary = boundary_eval(prompts, model)
367 "consistency": consistency,
368 "suppression": suppression,
369 "boundary": boundary,
370 "model_id": model_id,
380 model_id = resolve_model_id(model_id)
381 except Exception
as e:
382 return {
"error": str(e)}
383 feature_idx = int(args.get(
"feature_idx", 0))
384 memory = ctx.get(
"state", {}).get(
"memory")
or {}
385 layer_raw = args.get(
"layer")
386 if layer_raw
is None and memory.get(
"lastSaeLayer")
is not None:
387 layer = int(memory[
"lastSaeLayer"])
388 elif layer_raw
is not None:
389 layer = int(layer_raw)
392 prompt = args.get(
"prompt")
or memory.get(
"lastPrompt")
or "Hello"
393 model = get_loaded_model()
396 model = load_model(model_id)
397 except Exception
as e:
398 return {
"error": str(e)}
400 sae = load_sae(model_id, layer)
401 except Exception
as e:
402 return {
"error": f
"SAE load failed: {e}"}
404 client = get_openai_client(ctx)
405 interp = run_interp_score(
406 feature_idx, prompt, model, sae, client,
407 model_id=model_id, layer=layer,
410 label = interp.get(
"label")
412 "feature_idx": feature_idx,
413 "feature_ref": format_feature_ref(feature_idx, label),
414 "model_id": model_id,
416 "score": interp.get(
"score"),
417 "purity_score": interp.get(
"purity_score"),
418 "mui_score": interp.get(
"mui_score"),
419 "mui_per_position": interp.get(
"mui_per_position"),
420 "mui_mean_kl": interp.get(
"mui_mean_kl"),
421 "baseline_entropy": interp.get(
"baseline_entropy"),
422 "positive_mean": interp.get(
"positive_mean"),
423 "negative_mean": interp.get(
"negative_mean"),
424 "positive_examples": interp.get(
"positive_examples", []),
425 "negative_examples": interp.get(
"negative_examples", []),
426 "error": interp.get(
"error"),
431 from datetime
import datetime, timezone
441 model_id = resolve_model_id(model_id)
442 except Exception
as e:
443 return {
"error": str(e)}
445 model = get_loaded_model()
447 model = load_model(model_id)
448 except Exception
as exc:
450 return {
"error": friendly_message(exc),
"model_id": model_id}
452 layer_range = args.get(
"layer_range")
454 trojan = run_weight_trojan_analysis(model, layer_range=layer_range)
455 except NotImplementedError
as exc:
458 "Weight trojan scan is not supported for this model architecture. "
461 "model_id": model_id,
463 except Exception
as _oom_exc:
465 if not is_oom_error(_oom_exc):
470 "GPU ran out of memory during weight trojan scan. "
471 "The model already fills most VRAM — try unloading other processes "
472 "or run on a host with more headroom."
474 "model_id": model_id,
477 if trojan.get(
"error"):
478 return {
"error": trojan[
"error"],
"model_id": model_id}
480 collapse_threshold = float(args.get(
"collapse_threshold")
or 0.1)
484 rank = run_weight_rank_analysis(model, collapse_threshold=collapse_threshold)
485 except NotImplementedError
as exc:
488 "Weight rank analysis is not supported for this model architecture. "
491 "model_id": model_id,
494 except Exception
as _oom_exc:
496 if not is_oom_error(_oom_exc):
501 "GPU ran out of memory during weight rank analysis. "
502 "Trojan scan completed; rank pass needs more free VRAM."
504 "model_id": model_id,
507 rank[
"model_id"] = model_id
509 generated_at = datetime.now(timezone.utc).strftime(
"%Y-%m-%dT%H:%M:%SZ")
511 "model_id": model_id,
512 "generated_at": generated_at,
519 """Map bridge output to web UMAPExplorer shape (feature_idx, x/y/z in 0–1)."""
520 rows: list[dict] = []
522 idx = p.get(
"feature_idx", p.get(
"i"))
526 x, y = float(p[
"x"]), float(p[
"y"])
527 except (KeyError, TypeError, ValueError):
529 z = float(p.get(
"z", 0))
531 "feature_idx": int(idx),
535 "label": p.get(
"label"),
539 xs = [r[
"x"]
for r
in rows]
540 ys = [r[
"y"]
for r
in rows]
541 zs = [r[
"z"]
for r
in rows]
542 xmin, xmax = min(xs), max(xs)
543 ymin, ymax = min(ys), max(ys)
544 zmin, zmax = min(zs), max(zs)
545 xspan = max(xmax - xmin, 1e-9)
546 yspan = max(ymax - ymin, 1e-9)
547 zspan = max(zmax - zmin, 1e-9)
548 flat_z = zspan < 1e-9
552 "feature_idx": r[
"feature_idx"],
553 "x": (r[
"x"] - xmin) / xspan,
554 "y": (r[
"y"] - ymin) / yspan,
555 "z": 0.0
if flat_z
else (r[
"z"] - zmin) / zspan,
556 **({
"label": r[
"label"]}
if r.get(
"label")
else {}),
562 """Load precomputed 3D UMAP from the public SAE DB when available."""
568 rows = list_saes(model=model_id)
570 (r
for r
in rows
if r.get(
"layer") == layer
and r.get(
"umap_key")),
575 base = os.environ.get(
"AQUIN_BASE_URL",
"https://api.aquin.app").rstrip(
"/")
576 resp = requests.get(f
"{base}/api/public/saes/{match['id']}/umap", timeout=120)
577 resp.raise_for_status()
579 raw = data.get(
"points")
if isinstance(data, dict)
else data
580 if not isinstance(raw, list)
or not raw:
588 """Project SAE decoder weights into 3D (matches /db precomputed UMAPs)."""
592 sae = load_sae(model_id, layer)
593 W = sae.W_dec.float().cpu().detach()
596 reducer = umap.UMAP(n_components=3, random_state=42, n_neighbors=15)
597 embedding = reducer.fit_transform(W.numpy())
599 {
"feature_idx": i,
"x": float(r[0]),
"y": float(r[1]),
"z": float(r[2])}
600 for i, r
in enumerate(embedding)
603 W_centered = W - W.mean(0)
604 _, _, Vt = torch.linalg.svd(W_centered, full_matrices=
False)
605 proj = (W_centered @ Vt[:3].T).numpy()
607 {
"feature_idx": i,
"x": float(r[0]),
"y": float(r[1]),
"z": float(r[2])}
608 for i, r
in enumerate(proj)
610 except Exception
as e:
611 raise RuntimeError(f
"UMAP failed: {e}")
from e
612 return points, len(points)
621 model_id = resolve_model_id(model_id)
622 except Exception
as e:
623 return {
"error": str(e)}
625 cfg = get_config(model_id)
626 layer = args.get(
"layer")
627 resolved_layer = layer
if layer
is not None else cfg[
"sae_layer"]
629 source =
"precomputed"
632 if raw_points
is None:
635 except Exception
as e:
636 return {
"error": str(e)}
639 sync_points = downsample_umap_points(normalized)
641 "points": sync_points,
642 "n_features": len(normalized),
643 "n_points": len(sync_points),
644 "layer": resolved_layer,
645 "model_id": model_id,
656 model_id = resolve_model_id(model_id)
657 except Exception
as e:
658 return {
"error": str(e)}
659 model = get_loaded_model()
661 model = load_model(model_id)
663 return run_layer_analysis(model, {**args,
"model_id": model_id})
664 except Exception
as e:
665 return {
"error": str(e)}
677 or ctx.get(
"state", {}).get(
"activeModelId")
678 or get_active_model_id()
682 model_id = resolve_model_id(str(model_id))
683 args = {**args,
"model_id": model_id}
687 result = run_sae_stats(args)
688 if result.get(
"error"):
691 save_path = args.get(
"save")
693 from pathlib
import Path
694 p = Path(os.path.expanduser(str(save_path)))
695 p.parent.mkdir(parents=
True, exist_ok=
True)
696 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
697 result[
"saved_to"] = str(p)
710 or ctx.get(
"state", {}).get(
"activeModelId")
711 or get_active_model_id()
715 model_id = resolve_model_id(str(model_id))
716 args = {**args,
"model_id": model_id}
720 result = run_weight_diff_from_args(args)
721 if result.get(
"error"):
724 save_path = args.get(
"save")
726 from pathlib
import Path
727 p = Path(os.path.expanduser(str(save_path)))
728 p.parent.mkdir(parents=
True, exist_ok=
True)
729 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
730 result[
"saved_to"] = str(p)
743 or ctx.get(
"state", {}).get(
"activeModelId")
744 or get_active_model_id()
748 model_id = resolve_model_id(str(model_id))
749 args = {**args,
"model_id": model_id}
753 result = run_merge_analysis_from_args(args)
754 if result.get(
"error"):
757 save_path = args.get(
"save")
759 from pathlib
import Path
760 p = Path(os.path.expanduser(str(save_path)))
761 p.parent.mkdir(parents=
True, exist_ok=
True)
762 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
763 result[
"saved_to"] = str(p)
776 or ctx.get(
"state", {}).get(
"activeModelId")
777 or get_active_model_id()
781 model_id = resolve_model_id(str(model_id))
782 args = {**args,
"model_id": model_id}
786 result = run_trajectory_analysis_from_args(args)
787 if result.get(
"error"):
790 save_path = args.get(
"save")
792 from pathlib
import Path
793 p = Path(os.path.expanduser(str(save_path)))
794 p.parent.mkdir(parents=
True, exist_ok=
True)
795 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
796 result[
"saved_to"] = str(p)
809 or ctx.get(
"state", {}).get(
"activeModelId")
810 or get_active_model_id()
814 model_id = resolve_model_id(str(model_id))
815 args = {**args,
"model_id": model_id}
819 result = run_residual_drift_from_args(args)
820 if result.get(
"error"):
823 save_path = args.get(
"save")
825 from pathlib
import Path
826 p = Path(os.path.expanduser(str(save_path)))
827 p.parent.mkdir(parents=
True, exist_ok=
True)
828 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
829 result[
"saved_to"] = str(p)
842 or ctx.get(
"state", {}).get(
"activeModelId")
843 or get_active_model_id()
847 model_id = resolve_model_id(str(model_id))
848 args = {**args,
"model_id": model_id}
852 result = run_confidence_analysis_from_args(args)
853 if result.get(
"error"):
856 save_path = args.get(
"save")
858 from pathlib
import Path
859 p = Path(os.path.expanduser(str(save_path)))
860 p.parent.mkdir(parents=
True, exist_ok=
True)
861 p.write_text(json.dumps(result, indent=2, default=str), encoding=
"utf-8")
862 result[
"saved_to"] = str(p)
867 """KL(clean || perturbed) on a single token distribution — stable at zero mass."""
868 import torch.nn.functional
as F
870 clean_log = F.log_softmax(clean_logits.float(), dim=-1)
871 pert_log = F.log_softmax(perturbed_logits.float(), dim=-1)
872 clean_prob = clean_log.exp()
873 kl = (clean_prob * (clean_log - pert_log)).sum()
874 val = float(kl.item())
875 return 0.0
if val != val
else max(val, 0.0)
884 model_id = resolve_model_id(model_id)
885 except Exception
as e:
886 return {
"error": str(e)}
887 model = get_loaded_model()
889 model = load_model(model_id)
890 prompt = args.get(
"prompt")
or "Hello world"
891 n_channels = int(args.get(
"n_channels", 10))
892 layer = args.get(
"layer")
893 cfg = get_config(model_id)
894 resolved_layer = layer
if layer
is not None else cfg[
"sae_layer"]
895 hook_name = f
"blocks.{resolved_layer}.hook_resid_post"
896 tokens = model.to_tokens(prompt)
897 with torch.no_grad():
898 clean_logits = model(tokens)
899 clean_last = clean_logits[0, -1]
901 sae = load_sae(model_id, resolved_layer)
902 n_features = sae.W_dec.shape[0]
907 for ch
in range(min(n_channels, n_features)):
908 def _zero_hook(value, hook, ch_=ch):
910 direction = sae.W_dec[ch_].to(device=value.device, dtype=value.dtype)
911 coeff = (value * direction).sum(dim=-1, keepdim=
True)
912 return value - coeff * direction
913 value = value.clone()
914 value[..., ch_] = 0.0
917 with torch.no_grad():
918 perturbed = model.run_with_hooks(tokens, fwd_hooks=[(hook_name, _zero_hook)])
920 results.append({
"channel": ch,
"kl_divergence": round(kl, 6)})
921 results.sort(key=
lambda x: x[
"kl_divergence"], reverse=
True)
925 "channel": r[
"channel"],
926 "kl_mean": r[
"kl_divergence"],
927 "kl_max": r[
"kl_divergence"],
929 "layer_contributions": [{
"layer": resolved_layer,
"kl": r[
"kl_divergence"]}],
933 kl_vals = [c[
"kl_mean"]
for c
in channels]
934 global_mean = sum(kl_vals) / len(kl_vals)
if kl_vals
else 0.0
935 global_max = max(kl_vals)
if kl_vals
else 0.0
939 "method": args.get(
"method")
or "dropout",
940 "n_channels_sampled": len(channels),
941 "d_model": model.cfg.d_model,
942 "channels": channels,
943 "global_mean_kl": round(global_mean, 6),
944 "global_max_kl": round(global_max, 6),
945 "threshold_critical": round(global_max * 0.85, 6)
if global_max
else 0.01,
946 "threshold_high": round(global_mean + (global_max - global_mean) * 0.5, 6)
if kl_vals
else 0.005,
947 "perturbation_results": results,
948 "layer": resolved_layer,
949 "model_id": model_id,
959 model_id = resolve_model_id(model_id)
960 except Exception
as e:
961 return {
"error": str(e)}
962 model = get_loaded_model()
964 model = load_model(model_id)
965 prompt = args.get(
"prompt")
or args.get(
"text")
966 if not prompt
or not str(prompt).strip():
967 return {
"error":
"Missing --prompt. Usage: aquin check attention --prompt <text>"}
968 prompt = str(prompt).strip()
969 top_k = int(args.get(
"top_k")
or args.get(
"topk")
or 5)
971 return run_attention_routing(model, prompt, top_k=top_k, model_id=model_id)
972 except Exception
as e:
973 return {
"error": str(e)}
982 model_id = resolve_model_id(model_id)
983 except Exception
as e:
984 return {
"error": str(e)}
986 benchmark_top = int(args.get(
"benchmark_top")
or args.get(
"benchmark_top_k")
or 0)
988 if benchmark_top > 0:
991 openai_client = get_openai_client(ctx)
995 session_id = ctx.get(
"session_id")
or (ctx.get(
"state")
or {}).get(
"session_id")
996 checkpoint = args.get(
"checkpoint")
or None
1001 return run_find_feature(
1003 scorer=str(args.get(
"scorer")
or "deception"),
1004 prompts_path=args.get(
"prompts")
or args.get(
"prompts_path"),
1005 layer=int(args[
"layer"])
if args.get(
"layer")
is not None else None,
1006 checkpoint_path=checkpoint,
1007 top_k=int(args.get(
"top_k")
or args.get(
"top")
or 20),
1008 direction=str(args.get(
"direction")
or "both"),
1009 conditioning=str(args.get(
"conditioning")
or "behavior"),
1010 benchmark_top=benchmark_top,
1011 persist_key=args.get(
"persist")
or None,
1012 session_id=str(session_id)
if session_id
else None,
1013 openai_client=openai_client,
1015 except Exception
as e:
1016 return {
"error": str(e)}
1023 from pathlib
import Path
1027 topic = str(args.get(
"topic")
or "").strip()
1029 return {
"error":
"Pass --topic <text> (e.g. aquin simulate --topic flowers)"}
1032 count = int(args.get(
"count")
or 5)
1033 except (TypeError, ValueError):
1034 return {
"error":
"count must be an integer"}
1036 cwd = args.get(
"cwd")
or ctx.get(
"cwd")
or Path.cwd()
1037 output = args.get(
"save")
1040 result = run_dataset_generate(
1044 output=str(output)
if output
else None,
1046 except ValueError
as e:
1047 return {
"error": str(e)}
1048 except OSError
as e:
1049 return {
"error": f
"Could not write dataset file: {e}"}
1051 ds = _SANDBOX_STATE.setdefault(
"dataset", {})
1052 ds[
"rows"] = result[
"rows"]
1053 ds[
"topic"] = result[
"topic"]
1054 ds[
"path"] = result[
"path"]
1060 """LLM sessions list LLM sims."""
1064 """Fold simulation SSE events into one result dict for compare/load."""
1065 skip = {
"log",
"state"}
1068 key = ev.get(
"type")
1069 if not key
or key
in skip:
1071 payload = {k: v
for k, v
in ev.items()
if k !=
"type"}
1073 result.setdefault(
"meta", {}).update(payload)
1075 loss = ev.get(
"loss")
1076 if loss
is not None:
1077 result.setdefault(
"lossHistory", []).append(loss)
1078 elif key ==
"gradHeatmap":
1079 result.setdefault(
"gradHeatmap", []).append(payload)
1080 elif key ==
"signal":
1081 result.setdefault(
"signals", []).append({
1082 "signalType": ev.get(
"signalType",
""),
1083 "severity": ev.get(
"severity",
""),
1084 "message": ev.get(
"message",
""),
1085 "step": ev.get(
"step", 0),
1088 result[key] = payload
1093 """Save simulation output under ~/.aquin/runs/<id> for list/compare."""
1096 from datetime
import datetime, timezone
1097 from pathlib
import Path
1099 run_id = uuid.uuid4().hex[:12]
1100 run_dir = Path.home() /
".aquin" /
"runs" / run_id
1101 run_dir.mkdir(parents=
True, exist_ok=
True)
1103 result.update({k: v
for k, v
in meta.items()
if v
is not None})
1104 (run_dir /
"result.json").write_text(json.dumps(result, default=str))
1107 "saved_at": datetime.now(timezone.utc).strftime(
"%Y-%m-%dT%H:%M:%SZ"),
1110 (run_dir /
"meta.json").write_text(json.dumps(run_meta, default=str))
1116 from pathlib
import Path
1118 run_dir = Path.home() /
".aquin" /
"runs" / run_id
1119 result_file = run_dir /
"result.json"
1120 if result_file.exists():
1121 return json.loads(result_file.read_text())
1122 meta_file = run_dir /
"meta.json"
1123 if meta_file.exists():
1124 return json.loads(meta_file.read_text())
1125 return {
"status":
"not_found",
"run_id": run_id}
1130 from datetime
import datetime, timezone
1131 from pathlib
import Path
1135 runs_dir = Path.home() /
".aquin" /
"runs"
1136 if not runs_dir.exists():
1137 return {
"runs": [],
"count": 0}
1139 entries: list[dict] = []
1141 for run_dir
in runs_dir.iterdir():
1142 if not run_dir.is_dir():
1144 run_id = run_dir.name
1146 meta_file = run_dir /
"meta.json"
1147 if meta_file.exists():
1149 meta = json.loads(meta_file.read_text())
1150 except json.JSONDecodeError:
1153 entry_kind = str(meta.get(
"kind")
or "simulate")
1154 if kind_filter
and entry_kind != kind_filter:
1158 harmful_count =
None
1160 result_file = run_dir /
"result.json"
1161 if result_file.exists():
1163 result = json.loads(result_file.read_text())
1164 dq = result.get(
"datasetQuality")
1165 if isinstance(dq, dict):
1166 n_samples = dq.get(
"nSamples")
1167 harmful_count = dq.get(
"harmfulCount")
1168 meta_block = result.get(
"meta")
1169 if isinstance(meta_block, dict)
and meta_block.get(
"algoConfig"):
1170 method = _simulation_method_from_meta(meta_block)
1171 except json.JSONDecodeError:
1174 saved_at = str(meta.get(
"saved_at")
or "")
1177 saved_at = datetime.fromtimestamp(
1178 run_dir.stat().st_mtime, tz=timezone.utc
1179 ).strftime(
"%Y-%m-%dT%H:%M:%SZ")
1185 "saved_at": saved_at,
1186 "model_id": str(meta.get(
"model_id")
or meta.get(
"modelId")
or ""),
1187 "topic": meta.get(
"topic"),
1188 "dataset_path": meta.get(
"dataset_path")
or meta.get(
"pairs_path"),
1190 "n_samples": n_samples,
1191 "harmful_count": harmful_count,
1192 "method": method
or "LoRA",
1195 entries.sort(key=
lambda e: e.get(
"saved_at")
or "", reverse=
True)
1196 return {
"runs": entries,
"count": len(entries)}
1200 run_id = args.get(
"run_id",
"")
1202 if data.get(
"status") ==
"not_found":
1204 return {
"run_id": run_id, **data}
1208 """Compare two simulation runs."""
1209 run_id_a = args.get(
"run_id_a")
or args.get(
"run_id_before")
or ""
1210 run_id_b = args.get(
"run_id_b")
or args.get(
"run_id_after")
or ""
1211 label_a = args.get(
"label_a",
"Before")
1212 label_b = args.get(
"label_b",
"After")
1217 if result_a.get(
"status") ==
"not_found" or result_b.get(
"status") ==
"not_found":
1219 rid
for rid, data
in ((run_id_a, result_a), (run_id_b, result_b))
1220 if data.get(
"status") ==
"not_found" and rid
1222 return {
"error": f
"Simulation run not found: {', '.join(missing)}"}
1224 if not result_a
or not result_b:
1226 "error":
"Two simulation results required. Run simulate twice, then: "
1227 "aquin compare simulation --run_id_a <id> --run_id_b <id>"
1231 comparison = compare_simulation_results(
1232 result_a, result_b, label_a=label_a, label_b=label_b,
1233 run_id_a=run_id_a, run_id_b=run_id_b,
1236 "run_id_a": run_id_a,
1237 "run_id_b": run_id_b,
1240 "comparison": comparison,
1241 "attack_surface": comparison.get(
"modelScores", {}),
1247 if ev.get(
"type") ==
"error" and ev.get(
"message"):
1248 return str(ev[
"message"])
1249 if ev.get(
"type") ==
"log" and str(ev.get(
"line",
"")).startswith(
"ERROR:"):
1250 return str(ev[
"line"])[6:].strip()
1261 "result": assembled
or final,
1263 "status":
"error" if err
else "complete",
1264 **({
"error": err}
if err
else {}),
1265 **{k: v
for k, v
in meta.items()
if k
not in (
"events",
"result",
"run_id",
"status",
"error")},
1278 model_id = resolve_model_id(model_id)
1279 except Exception
as e:
1280 return {
"error": str(e)}
1281 scorer_type = args.get(
"scorer_type")
or "semantic_similarity"
1282 if scorer_type !=
"semantic_similarity":
1285 "run_custom_eval only supports scorer_type=semantic_similarity. "
1286 "Use built-in evals or run custom logic outside Aquin."
1290 reference_answers =
_as_str_list(args.get(
"reference_answers"))
1292 args.get(
"name")
or "Custom eval",
1295 reference_answers=reference_answers,
1296 threshold=float(args.get(
"threshold", 0.5)),
1297 max_tokens=int(args.get(
"max_tokens", 40)),
1298 temperature=float(args.get(
"temperature", 0.0)),
1299 description=args.get(
"description"),
1313 model_id = resolve_model_id(model_id)
1314 except Exception
as e:
1315 return {
"error": str(e)}
1319 model = get_loaded_model()
1322 model = load_model(model_id)
1323 except Exception
as e:
1324 return {
"error": str(e)}
1326 return run_red_team(model, model_id, vectors=
None)
1334 model_id = resolve_model_id(model_id)
1335 except Exception
as e:
1336 return {
"error": str(e)}
1337 prompt = args.get(
"prompt")
or ctx.get(
"state", {}).get(
"memory", {}).get(
"lastPrompt")
or "Hello"
1338 top_k = int(args.get(
"top_k", 5))
1340 results = run_logit_lens(prompt, model_id=model_id, top_k=top_k)
1341 return {
"results": results}
1342 except Exception
as e:
1343 return {
"error": str(e)}
1346def _run_trace(args: dict, ctx: dict) -> dict:
1351 model_id = resolve_model_id(model_id)
1352 except Exception
as e:
1353 return {
"error": str(e)}
1354 prompt = args.get(
"prompt")
or ctx.get(
"state", {}).get(
"memory", {}).get(
"lastPrompt")
or "Hello"
1355 target = args.get(
"target")
or ""
1357 results = run_trace(prompt, target, model_id=model_id)
1358 return {
"results": results}
1359 except Exception
as e:
1360 return {
"error": str(e)}
1368 model_id = resolve_model_id(model_id)
1369 except Exception
as e:
1370 return {
"error": str(e)}
1372 result = run_prompt_attribution(
1373 args.get(
"prompt",
""),
1374 args.get(
"response",
""),
1375 args.get(
"prompt_tokens", []),
1376 args.get(
"response_tokens", []),
1377 args.get(
"sig_prompt_tis", []),
1378 args.get(
"sig_response_tis", []),
1380 noise_scale=float(args.get(
"noise_scale", 3.0)),
1381 n_noise_runs=int(args.get(
"n_noise_runs", 5)),
1384 except Exception
as e:
1385 return {
"error": str(e)}
1389 """Delegates to pipelines.run_full_inspection (same logic as inspect.py handler)."""
1392 state = ctx.get(
"state", {})
1393 prompt = args.get(
"prompt")
or state.get(
"lastPrompt")
or "Hello"
1396 model_id = resolve_model_id(model_id)
1397 except Exception
as e:
1398 return {
"error": str(e)}
1399 layer_raw = args.get(
"layer")
1400 layer = int(layer_raw)
if layer_raw
is not None else None
1401 result = _pipeline(prompt, model_id, ctx, layer=layer)
1402 if "error" in result:
1406 "prompt": result[
"prompt"],
1407 "response": result[
"response"],
1408 "model_id": result[
"model_id"],
1409 "top_features": result[
"top_features"],
1410 "sae_layer": result[
"sae_layer"],
1413 "type":
"inspectionFull",
1424 model_id = resolve_model_id(model_id)
1425 except Exception
as e:
1426 return {
"error": str(e)}
1427 feature_idx = int(args.get(
"feature_idx", 0))
1428 layer = args.get(
"layer")
1429 model = get_loaded_model()
1432 model = load_model(model_id)
1433 except Exception
as e:
1434 return {
"error": str(e)}
1438 result = _get_logits(feature_idx, model, model_id=model_id, layer=layer, top_k=int(args.get(
"top_k", 10)))
1439 enrich_feature_tool_result(
1441 prompt=prompt_for_labeling(ctx, args),
1443 client=get_openai_client(ctx),
1447 return {
"content": result}
1448 except Exception
as e:
1449 return {
"error": str(e)}
1457 model_id = resolve_model_id(model_id)
1458 except Exception
as e:
1459 return {
"error": str(e)}
1460 feature_idx = int(args.get(
"feature_idx", 0))
1461 layer = args.get(
"layer")
1466 result = _get_neighbors(feature_idx, model_id=model_id, layer=layer, top_k=int(args.get(
"top_k", 8)))
1467 model = get_loaded_model()
1469 model = load_model(model_id)
1470 enrich_feature_tool_result(
1472 prompt=prompt_for_labeling(ctx, args),
1474 client=get_openai_client(ctx),
1477 label_neighbors=
True,
1479 return {
"content": result}
1480 except Exception
as e:
1481 return {
"error": str(e)}
1492def _drain_queue(worker_fn, req, extra_kwargs=None, on_event=None):
1494 Run worker_fn(req, queue, loop) in a thread. Drain the queue until a
1495 sentinel {"__done__": True} payload arrives. Return (events, final_result).
1502 if isinstance(obj, float):
1503 return None if (math.isnan(obj)
or math.isinf(obj))
else obj
1504 if isinstance(obj, dict):
1505 return {k: _sanitize(v)
for k, v
in obj.items()}
1506 if isinstance(obj, list):
1507 return [_sanitize(v)
for v
in obj]
1510 loop = asyncio.new_event_loop()
1511 queue: asyncio.Queue = asyncio.Queue()
1515 worker_fn(req, queue, loop, **extra_kwargs)
1517 worker_fn(req, queue, loop)
1519 t = threading.Thread(target=_run, daemon=
True)
1525 async def _collect():
1528 item = await queue.get()
1529 if item.get(
"__done__"):
1530 final = {k: v
for k, v
in item.items()
if k !=
"__done__"}
1532 events.append(_sanitize(item))
1536 loop.run_until_complete(_collect())
1539 return events, final
1546 merged = prepare_simulation_args(args)
1547 sandbox = _SANDBOX_STATE
1550 or sandbox.get(
"dataset", {}).get(
"rows", [])
1556 model_id = resolve_model_id(model_id)
1559 return SimulateRequest(
1561 rank=int(merged.get(
"rank", 8)),
1562 alpha=int(merged.get(
"alpha", 16)),
1563 lr=float(merged.get(
"lr", 2e-4)),
1564 epochs=int(merged.get(
"epochs", 3)),
1565 dropout=float(merged.get(
"dropout", 0.05)),
1566 target_modules=merged.get(
"targetModules", []),
1567 warmup_steps=int(merged.get(
"warmupSteps", 0)),
1568 grad_clip=float(merged.get(
"gradClip", 1.0)),
1569 weight_decay=float(merged.get(
"weightDecay", 0.01)),
1570 grad_accum_steps=int(merged.get(
"gradAccumSteps", 1)),
1571 optimizer=merged.get(
"optimizer",
"adamw"),
1572 scheduler=merged.get(
"scheduler",
"cosine"),
1573 max_seq_len=int(merged.get(
"maxSeqLen", 128)),
1575 use_qlora=bool(merged.get(
"useQlora",
False)),
1576 use_rlhf=bool(merged.get(
"use_rlhf",
False)),
1577 rlhf_beta=float(merged.get(
"rlhf_beta", 0.1)),
1582 """Print key simulation metrics as they arrive (CLI live output)."""
1584 if t ==
"datasetQuality":
1586 f
"[simulate] dataset: {ev.get('nSamples', '?')} samples · "
1587 f
"diversity={float(ev.get('diversityScore', 0)):.2f} · "
1588 f
"harmful={ev.get('harmfulCount', 0)}",
1591 elif t ==
"step" and ev.get(
"loss")
is not None:
1592 print(f
"[simulate] step {ev.get('step', '?')} loss={float(ev['loss']):.4f}", flush=
True)
1593 elif t ==
"saePrediction":
1594 feats = ev.get(
"topFeatures")
or []
1595 strengthen = sum(1
for f
in feats
if f.get(
"direction") ==
"strengthen")
1596 suppress = sum(1
for f
in feats
if f.get(
"direction") ==
"suppress")
1598 f
"[simulate] SAE prediction: {strengthen} strengthen · {suppress} suppress "
1599 f
"({len(feats)} top features)",
1602 elif t ==
"saeDiff":
1604 f
"[simulate] SAE diff: {ev.get('nChanged', '?')}/{ev.get('nFeatures', '?')} changed "
1605 f
"mean|Δ|={ev.get('meanAbsDelta', '?')}",
1608 elif t ==
"influenceScores":
1609 method = ev.get(
"method",
"lissa")
1611 f
"[simulate] influence ({method}): {ev.get('nHelpful', 0)} helpful · "
1612 f
"{ev.get('nHarmful', 0)} harmful samples",
1615 elif t ==
"lossSharpness":
1616 lam = ev.get(
"maxEigenvalue")
1617 lam_s = f
"{float(lam):.2e}" if isinstance(lam, (int, float))
else "?"
1619 f
"[simulate] loss landscape: {ev.get('sharpnessLabel', '?')} · λ={lam_s}",
1622 elif t ==
"modelDiff":
1623 parts: list[str] = []
1625 (
"consistency",
"consistencyScore"),
1626 (
"suppression",
"suppressionScore"),
1627 (
"robustness",
"robustnessScore"),
1630 if isinstance(val, (int, float)):
1631 parts.append(f
"{label}={val:.2f}")
1633 print(f
"[simulate] attack surface: {' · '.join(parts)}", flush=
True)
1634 elif t ==
"calibration":
1636 f
"[simulate] calibration: base_ece={ev.get('base_ece')} "
1637 f
"ft_ece={ev.get('ft_ece')} "
1638 f
"low_conf={len(ev.get('low_confidence_rows') or [])}",
1642 print(f
"[simulate] ⚠ {ev.get('message', ev.get('signalType', 'signal'))}", flush=
True)
1650 """Full training simulation. Pass --dataset /path, --algo /path, or --topic."""
1651 session_model_id: str |
None =
None
1656 merged = prepare_simulation_args(args)
1659 or _SANDBOX_STATE.get(
"dataset", {}).get(
"rows", [])
1661 topic = merged.get(
"topic")
1665 "error":
"Pass --dataset /path/to/file, or aquin simulate --topic for a quick probe."
1668 {
"instruction": f
"What is {topic}?",
"response": f
"A question about {topic}."},
1669 {
"instruction": f
"Explain {topic} simply.",
"response": f
"{topic} is a topic."},
1673 session_model_id = req.model_id
1674 meta = {
"model_id": req.model_id,
"topic": topic,
"kind":
"simulate"}
1675 if merged.get(
"dataset_path"):
1676 meta[
"dataset_path"] = merged[
"dataset_path"]
1677 if merged.get(
"algo_path"):
1678 meta[
"algo_path"] = merged[
"algo_path"]
1679 events, final =
_drain_queue(_worker, req, on_event=_print_simulate_progress)
1681 except (FileNotFoundError, ValueError)
as e:
1682 return {
"error": str(e)}
1683 except Exception
as e:
1684 return {
"error": f
"run_simulation failed: {e}"}
1686 if session_model_id:
1689 restore_session_model(session_model_id)
1699TOOL_ROUTES: dict[str, Callable[[dict, dict], dict]] = {
1701 "run_full_inspection": _run_full_inspection_bridge,
1703 "run_logit_lens": _run_logit_lens,
1704 "run_trace": _run_trace,
1705 "run_prompt_attribution": _run_prompt_attribution,
1707 "get_feature_logits": _get_feature_logits_bridge,
1708 "get_feature_neighbors": _get_feature_neighbors_bridge,
1710 "run_steer_and_show": _run_steer_and_show,
1711 "extract_steer_vector": _extract_steer_vector,
1712 "run_multi_steer": _run_multi_steer,
1714 "run_consistency_eval": _run_consistency_eval,
1715 "run_suppression_eval": _run_suppression_eval,
1716 "run_boundary_eval": _run_boundary_eval,
1717 "run_audit": _run_audit,
1719 "run_benchmarks_on_top_feature": _run_benchmarks_on_top_feature,
1720 "run_find_feature": _run_find_feature,
1722 "check_weights": _check_weights,
1724 "ensure_umap_loaded": _ensure_umap_loaded,
1726 "run_layer_analysis": _run_layer_analysis,
1727 "run_sae_stats": _run_sae_stats,
1728 "run_confidence_analysis": _run_confidence_analysis,
1729 "run_weight_diff": _run_weight_diff,
1730 "run_merge_analysis": _run_merge_analysis,
1731 "run_trajectory_analysis": _run_trajectory_analysis,
1732 "run_residual_drift": _run_residual_drift,
1733 "run_perturbation_sensitivity": _run_perturbation_sensitivity,
1734 "run_attention_routing": _run_attention_routing,
1736 "dataset_generate": _dataset_generate,
1738 "run_simulation": _run_simulation,
1740 "list_simulation_runs": _list_simulation_runs,
1741 "load_simulation_run": _load_simulation_run,
1742 "compare_simulations": _compare_simulations,
1744 "run_red_team": _run_red_team,
1746 "run_custom_eval": _run_custom_eval,
1754def call(tool_name: str, args: dict, ctx: dict) -> dict:
1756 Dispatch a tool call to its compute implementation.
1757 Returns a result dict. Never raises — errors are returned as {"error": ...}.
1759 handler = TOOL_ROUTES.get(tool_name)
1761 return {
"error": f
"Unknown tool: {tool_name}"}
1763 return handler(args, ctx)
1764 except Exception
as exc:
1766 log_debug(tool_name, exc)
1767 return {
"error": friendly_message(exc)}
dict _run_merge_analysis(dict args, dict ctx)
dict call(str tool_name, dict args, dict ctx)
dict _run_trajectory_analysis(dict args, dict ctx)
dict _run_steer_and_show(dict args, dict ctx)
_drain_queue(worker_fn, req, extra_kwargs=None, on_event=None)
str|None _simulation_error(list events)
dict _check_weights(dict args, dict ctx)
dict _run_prompt_attribution(dict args, dict ctx)
list _as_json_list(Any value)
dict _run_perturbation_sensitivity(dict args, dict ctx)
dict _run_simulation(dict args, dict ctx)
dict _compare_simulations(dict args, dict ctx)
dict _list_simulation_runs(dict args, dict ctx)
dict _run_attention_routing(dict args, dict ctx)
dict _ensure_umap_loaded(dict args, dict ctx)
dict _assemble_simulation_result(list[dict] events)
str _persist_simulation_run(list[dict] events, dict meta)
dict _run_residual_drift(dict args, dict ctx)
tuple[list[dict], int] _compute_umap_points(str model_id, int|None layer)
dict _run_logit_lens(dict args, dict ctx)
dict _run_trace(dict args, dict ctx)
dict _run_custom_eval(dict args, dict ctx)
dict|None _as_json_dict(Any value)
dict _run_red_team(dict args, dict ctx)
dict _wrap_simulation_result(list events, dict final, dict meta)
None _print_simulate_progress(dict ev)
dict _not_impl(str tool_name)
_build_simulate_req(dict args, dict ctx)
dict _load_simulation_run(dict args, dict ctx)
float _token_kl_divergence(clean_logits, perturbed_logits)
dict _run_sae_stats(dict args, dict ctx)
list[dict] _normalize_umap_points(list[dict] raw)
dict _run_suppression_eval(dict args, dict ctx)
dict _get_feature_logits_bridge(dict args, dict ctx)
dict _load_simulation_result(str run_id)
str _get_model_id(dict ctx, dict args)
dict _dataset_generate(dict args, dict ctx)
list[dict]|None _try_fetch_precomputed_umap(str model_id, int layer)
dict _run_layer_analysis(dict args, dict ctx)
dict _run_multi_steer(dict args, dict ctx)
dict _run_confidence_analysis(dict args, dict ctx)
dict _run_boundary_eval(dict args, dict ctx)
dict _get_feature_neighbors_bridge(dict args, dict ctx)
dict _run_find_feature(dict args, dict ctx)
str|None _session_sim_kind_filter(dict ctx)
dict _run_benchmarks_on_top_feature(dict args, dict ctx)
dict _run_audit(dict args, dict ctx)
dict _extract_steer_vector(dict args, dict ctx)
dict _run_full_inspection_bridge(dict args, dict ctx)
dict _run_consistency_eval(dict args, dict ctx)
list[str] _as_str_list(Any value, *, list[str]|None fallback=None)
dict _run_weight_diff(dict args, dict ctx)