AQIT 0.1.0
Loading...
Searching...
No Matches
saes.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
6from __future__ import annotations
7
8import os
9import sys
10from pathlib import Path
11
12
13_BASE_URL = "https://api.aquin.app"
14
15
16def _base_url() -> str:
17 return os.environ.get("AQUIN_BASE_URL", _BASE_URL).rstrip("/")
18
19
20def _get_api_key(cfg: dict) -> str:
21 key = cfg.get("api_key") or os.environ.get("AQUIN_API_KEY") or ""
22 if not key:
23 print("No API key found. Set AQUIN_API_KEY, or use aquin load sae --path <file.pt>.")
24 sys.exit(1)
25 return key
26
27
28# ── Public (no auth) ─────────────────────────────────────────────────────────
29
30def list_saes(model: str | None = None) -> list[dict]:
31 import requests
32 from aquin.user_errors import die
33
34 try:
35 resp = requests.get(f"{_base_url()}/api/public/saes", timeout=15)
36 resp.raise_for_status()
37 except Exception as exc:
38 die(message="Could not load SAE catalog. Try again shortly.", exc=exc, label="load sae")
39 data = resp.json()
40 if model:
41 data = [s for s in data if s["model_slug"] == model or s["model_name"].lower() == model.lower()]
42 return data
43
44
45def get_sae(id: str) -> dict:
46 import requests
47 from aquin.user_errors import die, sanitize_error_string
48
49 resp = requests.get(f"{_base_url()}/api/public/saes/{id}", timeout=15)
50 if resp.status_code == 404:
51 die(sanitize_error_string(f"SAE '{id}' not found"))
52 try:
53 resp.raise_for_status()
54 except Exception as exc:
55 die(message="Could not load SAE info. Try again shortly.", exc=exc, label="info sae")
56 return resp.json()
57
58
59# ── Authenticated ─────────────────────────────────────────────────────────────
60
61def pull_sae(id: str, output: str | Path, api_key: str) -> None:
62 import requests
63
64 out = Path(output)
65 out.parent.mkdir(parents=True, exist_ok=True)
66
67 resp = requests.get(
68 f"{_base_url()}/api/public/saes/{id}/pull",
69 headers={"Authorization": f"Bearer {api_key}"},
70 stream=True,
71 timeout=600,
72 allow_redirects=True,
73 )
74
75 if resp.status_code == 503:
76 out.unlink(missing_ok=True)
77 print(f"Error: SAE '{id}' is invalid in catalog storage (not real weights).", file=sys.stderr)
78 print("Contact Aquin support or use: aquin load sae --path <local.pt> --layer <n>", file=sys.stderr)
79 sys.exit(1)
80 if resp.status_code == 401:
81 from aquin.auth_config import print_token_rejected
82 print_token_rejected(api_key)
83 if resp.status_code == 403:
84 data = resp.json()
85 print(data.get("error", "Access not granted."))
86 sys.exit(1)
87 if resp.status_code == 404:
88 print(f"SAE '{id}' is not in the Aquin SAE library.", file=sys.stderr)
89 print("", file=sys.stderr)
90 print("Try: aquin load sae <model-l{n}>", file=sys.stderr)
91 print(" aquin load sae --path <file.pt> --layer <n>", file=sys.stderr)
92 print("", file=sys.stderr)
93 print(
94 "Train your own: aquin sae train --layer <n> --name <tag>",
95 file=sys.stderr,
96 )
97 sys.exit(1)
98 try:
99 resp.raise_for_status()
100 except Exception as exc:
101 from aquin.user_errors import die
102 die(
103 message=f"Could not download SAE '{id}'. Set AQUIN_API_KEY or use --path <file.pt>.",
104 exc=exc,
105 label="load sae",
106 )
107
108 total = int(resp.headers.get("content-length", 0))
109 downloaded = 0
110 with open(out, "wb") as f:
111 for chunk in resp.iter_content(chunk_size=1024 * 1024):
112 f.write(chunk)
113 downloaded += len(chunk)
114 if total:
115 pct = downloaded / total * 100
116 mb = downloaded / 1_000_000
117 print(f"\r {mb:.1f} MB / {total/1_000_000:.1f} MB ({pct:.0f}%)", end="", flush=True)
118 print(f"\r Saved to {out} ")
119
120 from aquin.compute.torch_io import (
121 is_valid_sae_checkpoint_path,
122 looks_like_invalid_norm_blob,
123 looks_like_json_error_blob,
124 )
125
126 if looks_like_json_error_blob(out) or not is_valid_sae_checkpoint_path(out):
127 out.unlink(missing_ok=True)
128 print(f"Error: download for '{id}' was not a valid SAE checkpoint.", file=sys.stderr)
129 print("Catalog storage may be corrupt — train locally:", file=sys.stderr)
130 print(" aquin sae train --model <id> --layer <n> --name <tag> --quick", file=sys.stderr)
131 print(" aquin load sae --user <tag> --layer <n>", file=sys.stderr)
132 sys.exit(1)
133
134 try:
135 row = get_sae(id)
136 except Exception:
137 row = {}
138 if row.get("meta_key"):
139 layer = row.get("layer")
140 norm_name = f"norm_layer{layer}.pt" if layer is not None else "norm.pt"
141 norm_out = out.parent / norm_name
142 norm_resp = requests.get(
143 f"{_base_url()}/api/public/saes/{id}/pull-norm",
144 headers={"Authorization": f"Bearer {api_key}"},
145 stream=True,
146 timeout=600,
147 allow_redirects=True,
148 )
149 if norm_resp.status_code == 200:
150 with open(norm_out, "wb") as f:
151 for chunk in norm_resp.iter_content(chunk_size=1024 * 1024):
152 f.write(chunk)
153 if looks_like_invalid_norm_blob(norm_out):
154 norm_out.unlink(missing_ok=True)
155 print(
156 f" Note: norm stats unavailable for '{id}' (catalog storage issue) — inspect works without them",
157 file=sys.stderr,
158 )
159 else:
160 print(f" Saved norm to {norm_out}")
161 elif norm_resp.status_code == 503:
162 print(
163 f" Warning: norm for '{id}' is invalid in catalog storage — inspect will run without normalization",
164 file=sys.stderr,
165 )
166 elif norm_resp.status_code != 404:
167 print(f" Warning: norm download failed ({norm_resp.status_code})", file=sys.stderr)
168
169
170# ── SDK client namespace ──────────────────────────────────────────────────────
171
172class SaesNamespace:
173 def __init__(self, api_key: str | None = None):
174 self._api_key = api_key
175
176 def list(self, model: str | None = None) -> list[dict]:
177 return list_saes(model=model)
179 def get(self, id: str) -> dict:
180 return get_sae(id)
181
182 def pull(self, id: str | None = None, model: str | None = None,
183 layer: int | None = None, output: str | Path = "./sae.pt") -> None:
184 if id is None:
185 if model is None:
186 raise ValueError("Provide id or model.")
187 rows = list_saes(model=model)
188 if layer is not None:
189 rows = [r for r in rows if r["layer"] == layer]
190 if not rows:
191 raise ValueError(f"No SAE found for model={model} layer={layer}")
192 id = rows[0]["id"]
193 if not self._api_key:
194 raise ValueError("api_key required for pull(). Pass it to aquin.Client().")
195 pull_sae(id, output, self._api_key)
__init__(self, str|None api_key=None)
Definition saes.py:177
None pull(self, str|None id=None, str|None model=None, int|None layer=None, str|Path output="./sae.pt")
Definition saes.py:187
dict get(self, str id)
Definition saes.py:183
list[dict] list(self, str|None model=None)
Definition saes.py:180
None pull_sae(str id, str|Path output, str api_key)
Definition saes.py:65
dict get_sae(str id)
Definition saes.py:49
str _get_api_key(dict cfg)
Definition saes.py:24
str _base_url()
Definition saes.py:20
list[dict] list_saes(str|None model=None)
Definition saes.py:34