AQIT 0.1.0
Loading...
Searching...
No Matches
user_errors.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2"""User-facing CLI errors — no raw HTTP tracebacks unless AQUIN_DEBUG=1."""
3from __future__ import annotations
4
5import json
6import os
7import re
8import sys
9from typing import Any
10
11_URL_RE = re.compile(r"https?://[^\s'\"<>]+")
12
13
14def debug_enabled() -> bool:
15 return os.environ.get("AQUIN_DEBUG", "").strip().lower() in ("1", "true", "yes")
16
17
18def log_debug(label: str, detail: Any) -> None:
19 if debug_enabled():
20 print(f"[debug/{label}] {detail}", file=sys.stderr)
21
23def _scrub_urls(text: str) -> str:
24 return _URL_RE.sub("Aquin cloud", text)
25
26
27def sanitize_error_string(text: str) -> str:
28 """Turn raw API/library strings into short user-facing messages."""
29 if not text:
30 return "Something went wrong. Try again."
32 s = _scrub_urls(str(text).strip())
33 low = s.lower()
34
35 if s.startswith(("Could not ", "Cannot ", "No API key", "Not logged in", "Run:", "Usage:")):
36 return s
37 if "run: aquin login" in low or "aquin.app/profile" in low:
38 return s
39
40 if "401" in s or "unauthorized" in low:
41 return "Authentication failed. Set HF_TOKEN / OPENAI_API_KEY if this call needs cloud credentials."
42 if "token revoked" in low or "revoked_at" in low:
43 return "Cloud token rejected. Use a local path or set the required env token."
44 if "403" in s or "forbidden" in low:
45 return "Access denied for this resource."
46 if "404" in s and ("sae" in low or "not found" in low):
47 return (
48 "SAE not found in the Aquin library. Try aquin load sae <model-l{n}> "
49 "or aquin load sae --path <file.pt>\n"
50 "Some models (gpt2-small, pythia-70m) pull SAE weights from Hugging Face instead."
51 )
52 if "404" in s or "not found" in low:
53 return "Not found."
54 if "503" in s or "not configured on" in low:
55 return "This feature is temporarily unavailable. Try again later."
56 if any(x in low for x in ("connection", "timeout", "network", "could not reach", "name resolution")):
57 return "Could not reach Aquin. Check your internet connection."
58 if isinstance(text, json.JSONDecodeError) or "jsondecode" in low or "extra data" in low:
59 return "Could not read the server response. Try again."
60 if "hugging face access via aquin proxy" in low:
61 return s.split("\n")[0] if "\n" in s else s
62 if any(x in low for x in ("zip archive", "central directory", "pytorchstreamreader")):
63 return (
64 "SAE checkpoint file is corrupted or incomplete. "
65 "Delete it under ~/.aquin/sae/ and run: aquin load sae <model>-l<layer>"
66 )
67 if any(x in low for x in ("out of memory", "cuda out of memory")):
68 return s if len(s) <= 500 else s[:500] + "…"
69 if any(x in low for x in ("huggingface", "gated repo", "hf hub", "gated")):
70 return (
71 "Could not download from Hugging Face. "
72 "Set HF_TOKEN for gated models, or use a public checkpoint."
73 )
74 if "client error" in low or "httperror" in low or "for url" in low:
75 return "Request failed. Check network / credentials and try aquin status."
76 if " failed:" in low:
77 _, tail = re.split(r"\bfailed:\s*", s, maxsplit=1, flags=re.I)
78 if tail:
79 return sanitize_error_string(tail.strip())
80
81 if len(s) > 180 and not debug_enabled():
82 return "Something went wrong. Try again. (Set AQUIN_DEBUG=1 for details)"
83 return s
84
85
86def friendly_message(exc: BaseException) -> str:
87 if isinstance(exc, KeyboardInterrupt):
88 raise exc
89
90 try:
91 import httpx
92
93 if isinstance(exc, httpx.HTTPStatusError):
94 code = exc.response.status_code if exc.response is not None else 0
95 if code in (401, 403):
96 return "Authentication failed. Set the required env token (e.g. HF_TOKEN)."
97 if code == 404:
98 return "Not found."
99 if code >= 500:
100 return "Remote service is having trouble. Try again shortly."
101 return sanitize_error_string(str(exc))
102 except ImportError:
103 pass
104
105 try:
106 import requests
107 from requests.exceptions import HTTPError, ConnectionError, Timeout
108
109 if isinstance(exc, HTTPError):
110 code = exc.response.status_code if exc.response is not None else 0
111 if code in (401, 403):
112 return "Authentication failed. Set the required env token (e.g. HF_TOKEN)."
113 if code == 404:
114 return sanitize_error_string(str(exc))
115 if code >= 500:
116 return "Remote service is having trouble. Try again shortly."
117 return sanitize_error_string(str(exc))
118 if isinstance(exc, (ConnectionError, Timeout)):
119 return "Could not reach remote service. Check your internet connection."
120 except ImportError:
121 pass
122
123 if isinstance(exc, FileNotFoundError):
124 msg = str(exc).strip()
125 if msg and (
126 "sae" in msg.lower()
127 or "layer" in msg.lower()
128 or "dictionary" in msg.lower()
129 or "\n" in msg
130 ):
131 return msg
132 return "File not found. Check the path and try again."
133 if isinstance(exc, json.JSONDecodeError):
134 return "Could not read JSON input. Check the file format."
135 if isinstance(exc, NotImplementedError):
136 msg = str(exc).strip()
137 if msg and ("not yet ported" in msg.lower() or "not available in" in msg.lower()):
138 return msg
139 if msg and debug_enabled():
140 return f"This command is not available yet. ({msg})"
141 if msg and len(msg) < 120:
142 return msg
143 return "This command is not available yet."
144 if isinstance(exc, ValueError):
145 msg = str(exc).strip()
146 if msg and not msg.startswith("HTTP"):
147 if "corrupt" in msg.lower() or "re-download" in msg.lower() or len(msg) < 400:
148 return msg
149 if isinstance(exc, RuntimeError):
150 from aquin.compute.torch_io import is_corrupt_pytorch_zip
151
152 msg = str(exc).strip()
153 if msg and is_corrupt_pytorch_zip(exc):
154 return (
155 "SAE checkpoint file is corrupted or incomplete. "
156 "Delete it under ~/.aquin/sae/ and run: aquin load sae <model>-l<layer>"
157 )
158
159 msg = str(exc).strip()
160 if msg:
161 return sanitize_error_string(msg)
162 return "Something went wrong. Try again."
163
164
165def die(
166 message: str | None = None,
167 *,
168 exc: BaseException | None = None,
169 code: int = 1,
170 label: str = "aquin",
171) -> None:
172 if exc is not None:
173 log_debug(label, exc)
174 text = message or friendly_message(exc)
175 else:
176 text = message or "Something went wrong. Try again."
177 print(text, file=sys.stderr)
178 raise SystemExit(code)
179
180
181def exit_on(exc: BaseException, *, label: str = "aquin", code: int = 1) -> None:
182 die(exc=exc, label=label, code=code)
None exit_on(BaseException exc, *, str label="aquin", int code=1)
str friendly_message(BaseException exc)
str _scrub_urls(str text)
None die(str|None message=None, *, BaseException|None exc=None, int code=1, str label="aquin")
str sanitize_error_string(str text)
None log_debug(str label, Any detail)