AQIT 0.1.0
Loading...
Searching...
No Matches
pipelines.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2# This file is part of the Aquin Engine. Unauthorized copying, modification,
3# distribution, or use of this file, via any medium, is strictly prohibited.
4# Proprietary and confidential. See LICENSE for terms.
5
6"""
7Multi-step orchestrations that combine multiple compute calls.
8No new algorithms — these are the same sub-call sequences as the legacy
9runFullPipeline flow, exposed as importable functions so bridge.py and raw
10CLI commands can call them without the tool registry.
11"""
12from __future__ import annotations
13
14from typing import Any
15
16
18 prompt: str,
19 model_id: str,
20 ctx: dict,
21 *,
22 layer: int | None = None,
23) -> dict:
24 """
25 chat + feature_analysis + logit_lens — same sub-calls as legacy runFullPipeline.
26 Returns a dict with keys: prompt, response, model_id, top_features, sae_layer,
27 logit_lens, trace_results, color_map, prompt_tokens, response_tokens, attribution.
28 """
29 from aquin.compute.model_loader import (
30 get_loaded_model,
31 load_model,
32 require_sae_layer,
33 resolve_model_id,
34 )
35 from aquin.compute.causal_trace import run_chat, run_logit_lens
36 from aquin.compute.feature_analysis import run_feature_analysis_unlabeled
37
38 try:
39 short = resolve_model_id(model_id)
40 except Exception as e:
41 return {"error": f"Unknown model '{model_id}': {e}"}
42
43 try:
44 sae_layer = require_sae_layer(
45 short,
46 layer,
47 command="trace",
48 example_suffix=f'--prompt "{prompt[:60]}"' if prompt else "",
49 )
50 except ValueError as e:
51 return {"error": str(e)}
52
53 model = get_loaded_model()
54 if model is None:
55 try:
56 model = load_model(short)
57 except Exception as e:
58 return {"error": f"Could not load model: {e}"}
59
60 # 1. Generate response
61 try:
62 response = run_chat(prompt, model_id=short, max_new_tokens=200, temperature=0.7)
63 except Exception as e:
64 return {"error": f"Generation failed: {e}"}
65
66 # 2. SAE feature analysis
67 try:
68 feat_result = run_feature_analysis_unlabeled(
69 prompt, response, model, model_id=short, layer=sae_layer,
70 )
71 except Exception as e:
72 return {"error": f"Feature analysis failed: {e}"}
73
74 actual_layer = feat_result.get("sae_layer")
75 if actual_layer is not None and int(actual_layer) != int(sae_layer):
76 return {
77 "error": (
78 f"SAE layer mismatch: requested layer {sae_layer}, "
79 f"feature analysis ran at layer {actual_layer}."
80 ),
81 }
82
83 prompt_tokens: list[str] = feat_result.get("prompt_tokens", [])
84 response_tokens: list[str] = feat_result.get("response_tokens", [])
85 top_features: list[dict] = feat_result.get("top_response_features", [])
86 attribution: list[dict] = feat_result.get("attribution", [])
87 sae_layer = int(feat_result.get("sae_layer", sae_layer))
88
89 # 3. Logit lens
90 try:
91 logit_lens = run_logit_lens(prompt, model_id=short, top_k=5)
92 except Exception as e:
93 print(f"[pipelines] logit-lens failed: {e}", flush=True)
94 logit_lens = []
95
96 # Build trace results (probability delta per layer)
97 trace_results: list[dict] = []
98 if logit_lens:
99 final_prob = logit_lens[-1]["top_tokens"][0]["prob"] if logit_lens[-1].get("top_tokens") else 0.0
100 for i, row in enumerate(logit_lens):
101 prob = row["top_tokens"][0]["prob"] if row.get("top_tokens") else 0.0
102 next_row = logit_lens[i + 1] if i + 1 < len(logit_lens) else None
103 next_prob = next_row["top_tokens"][0]["prob"] if next_row and next_row.get("top_tokens") else prob
104 delta = max(0.0, next_prob - prob)
105 trace_results.append({
106 "layer": row["layer"],
107 "drop": round(delta, 4),
108 "attn_drop": round(delta * 0.6, 4),
109 "mlp_drop": round(delta * 0.4, 4),
110 "baseline_prob": round(final_prob, 4),
111 })
112
113 # Build color map from attribution
114 color_map: dict | None = None
115 feature_color: dict[int, int] = {}
116 counter = [0]
117
118 def _color(fidx: int) -> int:
119 if fidx not in feature_color:
120 feature_color[fidx] = counter[0] % 10
121 counter[0] += 1
122 return feature_color[fidx]
123
124 prompt_cm: dict[int, int] = {}
125 response_cm: dict[int, int] = {}
126 for entry in attribution:
127 ri = entry["response_ti"]
128 for feat in entry.get("driven_by_features", []):
129 cidx = _color(feat["feature_idx"])
130 response_cm[ri] = cidx
131 for pi in feat.get("also_in_prompt_positions", []):
132 prompt_cm[pi] = cidx
133
134 if prompt_cm or response_cm:
135 color_map = {"prompt": prompt_cm, "response": response_cm}
136
137 trace_target = response_tokens[0].strip() if response_tokens else ""
138
139 return {
140 "prompt": prompt,
141 "response": response,
142 "model_id": short,
143 "top_features": top_features,
144 "prompt_tokens": prompt_tokens,
145 "response_tokens": response_tokens,
146 "attribution": attribution,
147 "sae_layer": sae_layer,
148 "logit_lens": logit_lens,
149 "trace_results": trace_results,
150 "trace_target": trace_target,
151 "color_map": color_map,
152 }
dict run_full_inspection(str prompt, str model_id, dict ctx, *, int|None layer=None)
Definition pipelines.py:27