aethex 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aethex/__init__.py +51 -0
- aethex/_version.py +15 -0
- aethex/client.py +348 -0
- aethex/exceptions.py +71 -0
- aethex-0.1.0.dist-info/METADATA +192 -0
- aethex-0.1.0.dist-info/RECORD +8 -0
- aethex-0.1.0.dist-info/WHEEL +5 -0
- aethex-0.1.0.dist-info/top_level.txt +1 -0
aethex/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex Python SDK.
|
|
3
|
+
|
|
4
|
+
Quick start:
|
|
5
|
+
|
|
6
|
+
from aethex import AethexClient
|
|
7
|
+
|
|
8
|
+
client = AethexClient(api_key="aex_live_...")
|
|
9
|
+
|
|
10
|
+
verdict = client.cyber.analyze(events=[
|
|
11
|
+
{"type": "failed_login"},
|
|
12
|
+
{"type": "successful_login", "suspicious": True},
|
|
13
|
+
{"type": "privilege_escalation", "suspicious": True},
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
print(verdict["final_severity"])
|
|
17
|
+
|
|
18
|
+
Three product domains: cyber, finance, llm.
|
|
19
|
+
Account operations on client.account.
|
|
20
|
+
|
|
21
|
+
Full docs: https://github.com/aethexa1/aethexai
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from ._version import __version__
|
|
25
|
+
|
|
26
|
+
from .client import AethexClient
|
|
27
|
+
|
|
28
|
+
from .exceptions import (
|
|
29
|
+
AethexError,
|
|
30
|
+
AethexAuthError,
|
|
31
|
+
AethexConnectionError,
|
|
32
|
+
AethexNotFoundError,
|
|
33
|
+
AethexPermissionError,
|
|
34
|
+
AethexRateLimitError,
|
|
35
|
+
AethexServerError,
|
|
36
|
+
AethexValidationError,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"__version__",
|
|
42
|
+
"AethexClient",
|
|
43
|
+
"AethexError",
|
|
44
|
+
"AethexAuthError",
|
|
45
|
+
"AethexConnectionError",
|
|
46
|
+
"AethexNotFoundError",
|
|
47
|
+
"AethexPermissionError",
|
|
48
|
+
"AethexRateLimitError",
|
|
49
|
+
"AethexServerError",
|
|
50
|
+
"AethexValidationError",
|
|
51
|
+
]
|
aethex/_version.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex Python SDK — single source of truth for version number.
|
|
3
|
+
|
|
4
|
+
Imported by setup.py / pyproject.toml at build time, and by
|
|
5
|
+
aethex/__init__.py at runtime so customers can introspect the
|
|
6
|
+
SDK version they have installed (`aethex.__version__`).
|
|
7
|
+
|
|
8
|
+
Bump on every PyPI release. Pre-1.0 follows 0.x.y where:
|
|
9
|
+
- x bumps for breaking changes (rare pre-1.0)
|
|
10
|
+
- y bumps for new features and bug fixes
|
|
11
|
+
|
|
12
|
+
Post-1.0 we'll switch to strict semver.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
aethex/client.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex Python SDK — main client.
|
|
3
|
+
|
|
4
|
+
Provides AethexClient: a unified interface to all three product
|
|
5
|
+
domains (cyber, finance, llm) plus account-level operations
|
|
6
|
+
(audit export, loss matrix, webhooks).
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from aethex import AethexClient
|
|
10
|
+
|
|
11
|
+
client = AethexClient(api_key="aex_live_...")
|
|
12
|
+
|
|
13
|
+
# Cyber analysis
|
|
14
|
+
verdict = client.cyber.analyze(events=[
|
|
15
|
+
{"type": "failed_login"},
|
|
16
|
+
{"type": "successful_login", "suspicious": True},
|
|
17
|
+
{"type": "privilege_escalation", "suspicious": True},
|
|
18
|
+
])
|
|
19
|
+
print(verdict["final_severity"]) # e.g. "CRITICAL"
|
|
20
|
+
|
|
21
|
+
# Finance analysis
|
|
22
|
+
verdict = client.finance.analyze(events=[
|
|
23
|
+
{"type": "ofac_full_match", "suspicious": True},
|
|
24
|
+
{"type": "sanctioned_geography_routing", "suspicious": True},
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
# LLM safety analysis
|
|
28
|
+
verdict = client.llm.analyze(events=[
|
|
29
|
+
{"type": "external_document_retrieval"},
|
|
30
|
+
{"type": "instruction_override_detected", "suspicious": True},
|
|
31
|
+
{"type": "tool_use_data_exfiltration", "suspicious": True},
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
Implementation notes:
|
|
35
|
+
- Pure stdlib HTTP (urllib). No external dependencies. Customers
|
|
36
|
+
don't need to pip install requests just to use Aethex.
|
|
37
|
+
- One client instance is thread-safe for read-only attribute access
|
|
38
|
+
but should be created once and reused, not constructed per call.
|
|
39
|
+
- Default timeout: 30 seconds. Configurable per client or per call.
|
|
40
|
+
- All API errors map to typed exceptions in aethex.exceptions.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
import json
|
|
44
|
+
from typing import Any, Dict, List, Optional
|
|
45
|
+
from urllib.error import HTTPError, URLError
|
|
46
|
+
from urllib.request import Request, urlopen
|
|
47
|
+
|
|
48
|
+
from ._version import __version__
|
|
49
|
+
from .exceptions import (
|
|
50
|
+
AethexAuthError,
|
|
51
|
+
AethexConnectionError,
|
|
52
|
+
AethexError,
|
|
53
|
+
AethexNotFoundError,
|
|
54
|
+
AethexPermissionError,
|
|
55
|
+
AethexRateLimitError,
|
|
56
|
+
AethexServerError,
|
|
57
|
+
AethexValidationError,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ─── defaults ──────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
DEFAULT_BASE_URL = "https://aethex-api-1yqe.onrender.com"
|
|
64
|
+
DEFAULT_TIMEOUT = 30
|
|
65
|
+
USER_AGENT = f"aethex-python/{__version__}"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ─── http core ─────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
def _raise_for_status(status: int, body: str) -> None:
|
|
71
|
+
"""Map an HTTP status code to a typed AethexError."""
|
|
72
|
+
if status == 401:
|
|
73
|
+
raise AethexAuthError(
|
|
74
|
+
"API key invalid, missing, or revoked.",
|
|
75
|
+
status_code=status, response=body,
|
|
76
|
+
)
|
|
77
|
+
if status == 403:
|
|
78
|
+
raise AethexPermissionError(
|
|
79
|
+
"API key not scoped for this product domain.",
|
|
80
|
+
status_code=status, response=body,
|
|
81
|
+
)
|
|
82
|
+
if status == 404:
|
|
83
|
+
raise AethexNotFoundError(
|
|
84
|
+
"Resource not found.",
|
|
85
|
+
status_code=status, response=body,
|
|
86
|
+
)
|
|
87
|
+
if status == 429:
|
|
88
|
+
raise AethexRateLimitError(
|
|
89
|
+
"Rate limit exceeded.",
|
|
90
|
+
status_code=status, response=body,
|
|
91
|
+
)
|
|
92
|
+
if status == 400 or 405 <= status < 500:
|
|
93
|
+
raise AethexValidationError(
|
|
94
|
+
f"Request rejected ({status}): {body[:200]}",
|
|
95
|
+
status_code=status, response=body,
|
|
96
|
+
)
|
|
97
|
+
if 500 <= status < 600:
|
|
98
|
+
raise AethexServerError(
|
|
99
|
+
f"Aethex server error ({status}).",
|
|
100
|
+
status_code=status, response=body,
|
|
101
|
+
)
|
|
102
|
+
# Other unexpected status -- still surface it.
|
|
103
|
+
raise AethexError(
|
|
104
|
+
f"Unexpected HTTP {status}: {body[:200]}",
|
|
105
|
+
status_code=status, response=body,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ─── client ────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
class AethexClient:
|
|
112
|
+
"""
|
|
113
|
+
Main entry point for the Aethex SDK.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
api_key: your aex_live_... key (required)
|
|
117
|
+
base_url: override the API endpoint (defaults to production)
|
|
118
|
+
timeout: per-request timeout in seconds (default 30)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
api_key: str,
|
|
124
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
125
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
126
|
+
):
|
|
127
|
+
if not api_key or not isinstance(api_key, str):
|
|
128
|
+
raise ValueError("api_key must be a non-empty string")
|
|
129
|
+
if not api_key.startswith("aex_"):
|
|
130
|
+
raise ValueError(
|
|
131
|
+
"api_key has invalid format (expected to start with 'aex_')"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
self._api_key = api_key
|
|
135
|
+
self._base_url = base_url.rstrip("/")
|
|
136
|
+
self._timeout = timeout
|
|
137
|
+
|
|
138
|
+
# Sub-clients for each product domain
|
|
139
|
+
self.cyber = _CyberAPI(self)
|
|
140
|
+
self.finance = _FinanceAPI(self)
|
|
141
|
+
self.llm = _LLMAPI(self)
|
|
142
|
+
|
|
143
|
+
# Account-level operations
|
|
144
|
+
self.account = _AccountAPI(self)
|
|
145
|
+
|
|
146
|
+
# ─── internal HTTP ────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
def _request(
|
|
149
|
+
self,
|
|
150
|
+
method: str,
|
|
151
|
+
path: str,
|
|
152
|
+
body: Optional[Dict[str, Any]] = None,
|
|
153
|
+
timeout: Optional[int] = None,
|
|
154
|
+
) -> Dict[str, Any]:
|
|
155
|
+
"""
|
|
156
|
+
Internal HTTP helper. Customers should not call this directly --
|
|
157
|
+
use the typed methods on cyber/finance/llm/account instead.
|
|
158
|
+
"""
|
|
159
|
+
url = self._base_url + path
|
|
160
|
+
timeout = timeout or self._timeout
|
|
161
|
+
headers = {
|
|
162
|
+
"Authorization": f"Bearer {self._api_key}",
|
|
163
|
+
"User-Agent": USER_AGENT,
|
|
164
|
+
"Accept": "application/json",
|
|
165
|
+
}
|
|
166
|
+
data = None
|
|
167
|
+
if body is not None:
|
|
168
|
+
data = json.dumps(body).encode("utf-8")
|
|
169
|
+
headers["Content-Type"] = "application/json"
|
|
170
|
+
|
|
171
|
+
req = Request(url, data=data, method=method, headers=headers)
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
with urlopen(req, timeout=timeout) as resp:
|
|
175
|
+
response_body = resp.read().decode("utf-8")
|
|
176
|
+
if resp.status >= 400:
|
|
177
|
+
_raise_for_status(resp.status, response_body)
|
|
178
|
+
return json.loads(response_body) if response_body else {}
|
|
179
|
+
except HTTPError as e:
|
|
180
|
+
response_body = ""
|
|
181
|
+
try:
|
|
182
|
+
response_body = e.read().decode("utf-8", errors="replace")
|
|
183
|
+
except Exception:
|
|
184
|
+
pass
|
|
185
|
+
_raise_for_status(e.code, response_body)
|
|
186
|
+
raise # _raise_for_status always raises, but keep type checker happy
|
|
187
|
+
except URLError as e:
|
|
188
|
+
raise AethexConnectionError(
|
|
189
|
+
f"Network error reaching Aethex: {e.reason}"
|
|
190
|
+
) from e
|
|
191
|
+
except Exception as e:
|
|
192
|
+
if isinstance(e, AethexError):
|
|
193
|
+
raise
|
|
194
|
+
raise AethexConnectionError(
|
|
195
|
+
f"Unexpected SDK error: {type(e).__name__}: {e}"
|
|
196
|
+
) from e
|
|
197
|
+
|
|
198
|
+
# ─── repr ─────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
def __repr__(self) -> str:
|
|
201
|
+
# Don't leak the API key in repr.
|
|
202
|
+
suffix = self._api_key[-4:] if self._api_key else "????"
|
|
203
|
+
return f"AethexClient(api_key=aex_live_***{suffix}, base_url={self._base_url!r})"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# ─── sub-clients ───────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
class _DomainAPI:
|
|
209
|
+
"""Shared base for cyber/finance/llm domain sub-clients."""
|
|
210
|
+
|
|
211
|
+
_path = "" # subclass override
|
|
212
|
+
|
|
213
|
+
def __init__(self, client: AethexClient):
|
|
214
|
+
self._client = client
|
|
215
|
+
|
|
216
|
+
def analyze(
|
|
217
|
+
self,
|
|
218
|
+
events: List[Dict[str, Any]],
|
|
219
|
+
session_id: Optional[str] = None,
|
|
220
|
+
timestamp: Optional[str] = None,
|
|
221
|
+
frames: Optional[List[str]] = None,
|
|
222
|
+
) -> Dict[str, Any]:
|
|
223
|
+
"""
|
|
224
|
+
Run a sequence of events through Aethex's 10 ancient engines.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
events: list of {"type": "...", "suspicious": bool} dicts
|
|
228
|
+
session_id: optional client-supplied session identifier
|
|
229
|
+
timestamp: optional ISO 8601 timestamp (defaults to now)
|
|
230
|
+
frames: optional list of perspective frames
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
verdict dict containing final_severity, final_confidence,
|
|
234
|
+
engines (per-engine outputs), nyaya reasoning chain,
|
|
235
|
+
chakravyuha layers, and recommended action.
|
|
236
|
+
"""
|
|
237
|
+
body: Dict[str, Any] = {"events": events}
|
|
238
|
+
if session_id is not None: body["session_id"] = session_id
|
|
239
|
+
if timestamp is not None: body["timestamp"] = timestamp
|
|
240
|
+
if frames is not None: body["frames"] = frames
|
|
241
|
+
|
|
242
|
+
return self._client._request("POST", f"{self._path}/analyze", body=body)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class _CyberAPI(_DomainAPI):
|
|
246
|
+
"""Cyber threat analysis. Use for SOC alerts, intrusion patterns, insider threats."""
|
|
247
|
+
_path = "/api/cyber"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class _FinanceAPI(_DomainAPI):
|
|
251
|
+
"""Financial fraud / AML / sanctions analysis."""
|
|
252
|
+
_path = "/api/finance"
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class _LLMAPI(_DomainAPI):
|
|
256
|
+
"""LLM agent safety analysis. Translates LLM event types to engine vocabulary."""
|
|
257
|
+
_path = "/api/llm"
|
|
258
|
+
|
|
259
|
+
def vocabulary(self) -> Dict[str, Any]:
|
|
260
|
+
"""
|
|
261
|
+
Return the canonical LLM event-type -> cyber-vocabulary
|
|
262
|
+
mapping. Useful for understanding which LLM events Aethex
|
|
263
|
+
recognizes and what their engine-level equivalents are.
|
|
264
|
+
"""
|
|
265
|
+
return self._client._request("GET", "/api/llm/vocabulary")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class _AccountAPI:
|
|
269
|
+
"""Account-level operations: audit export, loss matrix, webhooks."""
|
|
270
|
+
|
|
271
|
+
def __init__(self, client: AethexClient):
|
|
272
|
+
self._client = client
|
|
273
|
+
|
|
274
|
+
# --- audit ---
|
|
275
|
+
|
|
276
|
+
def export_audit(
|
|
277
|
+
self,
|
|
278
|
+
format: str = "json",
|
|
279
|
+
from_: Optional[str] = None,
|
|
280
|
+
to: Optional[str] = None,
|
|
281
|
+
) -> Dict[str, Any]:
|
|
282
|
+
"""
|
|
283
|
+
Export the calling key's audit log.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
format: "json" (default) or "csv"
|
|
287
|
+
from_: ISO 8601 start (e.g. "2026-01-01")
|
|
288
|
+
to: ISO 8601 end
|
|
289
|
+
"""
|
|
290
|
+
params = []
|
|
291
|
+
if format: params.append(f"format={format}")
|
|
292
|
+
if from_: params.append(f"from={from_}")
|
|
293
|
+
if to: params.append(f"to={to}")
|
|
294
|
+
path = "/api/audit/export" + ("?" + "&".join(params) if params else "")
|
|
295
|
+
return self._client._request("GET", path)
|
|
296
|
+
|
|
297
|
+
# --- loss matrix ---
|
|
298
|
+
|
|
299
|
+
def get_loss_matrix(self) -> Dict[str, Any]:
|
|
300
|
+
"""Return the calling key's current loss matrix."""
|
|
301
|
+
return self._client._request("GET", "/api/keys/me/loss-matrix")
|
|
302
|
+
|
|
303
|
+
def replace_loss_matrix(self, matrix: Dict[str, float]) -> Dict[str, Any]:
|
|
304
|
+
"""Replace the entire loss matrix with the supplied dict."""
|
|
305
|
+
return self._client._request(
|
|
306
|
+
"PUT", "/api/keys/me/loss-matrix", body={"matrix": matrix},
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def patch_loss_matrix(self, matrix: Dict[str, float]) -> Dict[str, Any]:
|
|
310
|
+
"""Merge the supplied entries into the existing loss matrix."""
|
|
311
|
+
return self._client._request(
|
|
312
|
+
"PATCH", "/api/keys/me/loss-matrix", body={"matrix": matrix},
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# --- webhooks ---
|
|
316
|
+
|
|
317
|
+
def list_webhooks(self) -> List[Dict[str, Any]]:
|
|
318
|
+
"""List webhooks registered to the calling key."""
|
|
319
|
+
return self._client._request("GET", "/api/keys/me/webhooks")
|
|
320
|
+
|
|
321
|
+
def create_webhook(
|
|
322
|
+
self,
|
|
323
|
+
url: str,
|
|
324
|
+
threshold: str = "CRITICAL",
|
|
325
|
+
name: str = "default",
|
|
326
|
+
) -> Dict[str, Any]:
|
|
327
|
+
"""
|
|
328
|
+
Register a new webhook. The plaintext secret is returned ONCE
|
|
329
|
+
in the response. Save it immediately -- it is not retrievable
|
|
330
|
+
afterwards.
|
|
331
|
+
"""
|
|
332
|
+
return self._client._request(
|
|
333
|
+
"POST", "/api/keys/me/webhooks",
|
|
334
|
+
body={"url": url, "threshold": threshold, "name": name},
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
def delete_webhook(self, webhook_id: int) -> Dict[str, Any]:
|
|
338
|
+
"""Delete a webhook by id."""
|
|
339
|
+
return self._client._request(
|
|
340
|
+
"DELETE", f"/api/keys/me/webhooks/{webhook_id}",
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def list_deliveries(self, webhook_id: int, limit: int = 50) -> List[Dict[str, Any]]:
|
|
344
|
+
"""List recent delivery attempts for a webhook."""
|
|
345
|
+
return self._client._request(
|
|
346
|
+
"GET",
|
|
347
|
+
f"/api/keys/me/webhooks/{webhook_id}/deliveries?limit={limit}",
|
|
348
|
+
)
|
aethex/exceptions.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex SDK — exception hierarchy.
|
|
3
|
+
|
|
4
|
+
Customers catch these in their integration code. The hierarchy
|
|
5
|
+
lets them either catch broadly (every Aethex error) or narrowly
|
|
6
|
+
(only auth, only rate-limit, etc).
|
|
7
|
+
|
|
8
|
+
AethexError -- root; catch this for any SDK error
|
|
9
|
+
AethexAuthError -- 401: bad / missing / revoked API key
|
|
10
|
+
AethexPermissionError -- 403: key not scoped for this product
|
|
11
|
+
AethexNotFoundError -- 404: resource doesn't exist
|
|
12
|
+
AethexRateLimitError -- 429: over rate limit
|
|
13
|
+
AethexValidationError -- 400: malformed request
|
|
14
|
+
AethexServerError -- 5xx: Aethex side broke
|
|
15
|
+
AethexConnectionError -- network failure, timeout, DNS, SSL
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AethexError(Exception):
|
|
22
|
+
"""
|
|
23
|
+
Base class for every error this SDK raises.
|
|
24
|
+
Carries the HTTP status code (if applicable) and the response
|
|
25
|
+
body returned by the Aethex API for debugging.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
message: str,
|
|
31
|
+
status_code: Optional[int] = None,
|
|
32
|
+
response: Optional[str] = None,
|
|
33
|
+
):
|
|
34
|
+
super().__init__(message)
|
|
35
|
+
self.status_code = status_code
|
|
36
|
+
self.response = response
|
|
37
|
+
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return (
|
|
40
|
+
f"{type(self).__name__}("
|
|
41
|
+
f"message={str(self)!r}, "
|
|
42
|
+
f"status_code={self.status_code!r})"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AethexAuthError(AethexError):
|
|
47
|
+
"""401 — API key missing, malformed, or revoked."""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AethexPermissionError(AethexError):
|
|
51
|
+
"""403 — API key valid but not scoped for the requested product."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AethexNotFoundError(AethexError):
|
|
55
|
+
"""404 — resource (webhook, audit entry, etc) does not exist."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AethexRateLimitError(AethexError):
|
|
59
|
+
"""429 — over the per-key rate limit. Customer should back off."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AethexValidationError(AethexError):
|
|
63
|
+
"""400 — request body malformed (bad event types, missing fields)."""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AethexServerError(AethexError):
|
|
67
|
+
"""5xx — Aethex side broke. Retry may help."""
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AethexConnectionError(AethexError):
|
|
71
|
+
"""Network-layer failure: timeout, DNS error, SSL error, connection refused."""
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aethex
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for Aethex — multi-domain AI threat verification (cyber, finance, LLM safety) with explainable reasoning chains.
|
|
5
|
+
Author-email: Aethex <lizsanchez@aethexai.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/aethexa1/aethexai
|
|
8
|
+
Project-URL: Documentation, https://github.com/aethexa1/aethexai/tree/main/docs
|
|
9
|
+
Project-URL: Repository, https://github.com/aethexa1/aethexai
|
|
10
|
+
Project-URL: Issues, https://github.com/aethexa1/aethexai/issues
|
|
11
|
+
Keywords: ai-safety,threat-detection,llm-safety,cybersecurity,fraud-detection,aml,fatf,mitre-attack,explainable-ai
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=4.0; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# Aethex Python SDK
|
|
34
|
+
|
|
35
|
+
Official Python client for [Aethex](https://github.com/aethexa1/aethexai) —
|
|
36
|
+
a multi-domain AI threat verification API with explainable reasoning chains.
|
|
37
|
+
|
|
38
|
+
Aethex runs threat events through 10 deterministic reasoning engines and
|
|
39
|
+
returns a verdict with severity, confidence, and a traceable reasoning
|
|
40
|
+
chain. Same engines work across three product domains:
|
|
41
|
+
|
|
42
|
+
- **Cyber** — SOC alerts, intrusion patterns, insider threats
|
|
43
|
+
- **Finance** — AML, sanctions screening, FATF typologies
|
|
44
|
+
- **LLM safety** — agent failure modes, prompt injection, tool-use exfiltration
|
|
45
|
+
|
|
46
|
+
Verdicts are deterministic. Same input produces the same output every
|
|
47
|
+
time, with the same audit trail. This is the core differentiator vs
|
|
48
|
+
LLM-only detection systems.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install aethex
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
No external dependencies. Pure stdlib (urllib, json).
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from aethex import AethexClient
|
|
62
|
+
|
|
63
|
+
client = AethexClient(api_key="aex_live_...")
|
|
64
|
+
|
|
65
|
+
# Cyber threat analysis
|
|
66
|
+
verdict = client.cyber.analyze(events=[
|
|
67
|
+
{"type": "failed_login"},
|
|
68
|
+
{"type": "failed_login"},
|
|
69
|
+
{"type": "successful_login", "suspicious": True},
|
|
70
|
+
{"type": "privilege_escalation", "suspicious": True},
|
|
71
|
+
{"type": "lateral_movement", "suspicious": True},
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
print(verdict["final_severity"]) # CRITICAL
|
|
75
|
+
print(verdict["final_confidence"]) # 0.92
|
|
76
|
+
print(verdict["verdict"]) # human-readable summary
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Three product domains
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# Cyber
|
|
83
|
+
client.cyber.analyze(events=[...])
|
|
84
|
+
|
|
85
|
+
# Finance (AML / sanctions / fraud)
|
|
86
|
+
client.finance.analyze(events=[
|
|
87
|
+
{"type": "ofac_full_match", "suspicious": True},
|
|
88
|
+
{"type": "sanctioned_geography_routing", "suspicious": True},
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
# LLM safety
|
|
92
|
+
client.llm.analyze(events=[
|
|
93
|
+
{"type": "external_document_retrieval"},
|
|
94
|
+
{"type": "instruction_override_detected", "suspicious": True},
|
|
95
|
+
{"type": "tool_use_data_exfiltration", "suspicious": True},
|
|
96
|
+
])
|
|
97
|
+
|
|
98
|
+
# Discover the canonical LLM event vocabulary
|
|
99
|
+
mapping = client.llm.vocabulary()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Account operations
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Audit log export (compliance)
|
|
106
|
+
audit = client.account.export_audit(format="json", from_="2026-01-01")
|
|
107
|
+
|
|
108
|
+
# Customer-tunable severity weighting
|
|
109
|
+
client.account.replace_loss_matrix({
|
|
110
|
+
"failed_login": 0.5, # downweight noisy events
|
|
111
|
+
"external_transfer": 5.0, # upweight critical events
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
# Webhooks for real-time alerts
|
|
115
|
+
webhook = client.account.create_webhook(
|
|
116
|
+
url="https://your-app.com/aethex-webhook",
|
|
117
|
+
threshold="CRITICAL",
|
|
118
|
+
)
|
|
119
|
+
# Save webhook["secret"] now -- it's shown only once.
|
|
120
|
+
|
|
121
|
+
deliveries = client.account.list_deliveries(webhook["id"])
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Error handling
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from aethex import (
|
|
128
|
+
AethexAuthError,
|
|
129
|
+
AethexRateLimitError,
|
|
130
|
+
AethexValidationError,
|
|
131
|
+
AethexConnectionError,
|
|
132
|
+
AethexError,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
verdict = client.cyber.analyze(events=[...])
|
|
137
|
+
except AethexAuthError:
|
|
138
|
+
# 401 — bad / missing / revoked API key
|
|
139
|
+
...
|
|
140
|
+
except AethexRateLimitError:
|
|
141
|
+
# 429 — back off and retry
|
|
142
|
+
...
|
|
143
|
+
except AethexValidationError as e:
|
|
144
|
+
# 400 — request body malformed
|
|
145
|
+
print(e.response)
|
|
146
|
+
except AethexConnectionError:
|
|
147
|
+
# network / timeout / DNS / SSL
|
|
148
|
+
...
|
|
149
|
+
except AethexError:
|
|
150
|
+
# catch-all for anything Aethex-related
|
|
151
|
+
...
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## What you get back
|
|
155
|
+
|
|
156
|
+
Every `analyze` call returns a verdict dict with:
|
|
157
|
+
|
|
158
|
+
- `final_severity` — `LOW` / `MEDIUM` / `HIGH` / `CRITICAL`
|
|
159
|
+
- `final_confidence` — 0.0 to 1.0
|
|
160
|
+
- `verdict` — human-readable summary
|
|
161
|
+
- `action` — recommended operational response
|
|
162
|
+
- `engines` — per-engine outputs from all 10 reasoning engines
|
|
163
|
+
- `nyaya` — the formal reasoning chain that produced the verdict
|
|
164
|
+
- `chakravyuha` — defense-in-depth layers breached
|
|
165
|
+
- `disagreement` — when engines disagree, the system surfaces uncertainty
|
|
166
|
+
rather than producing false confidence
|
|
167
|
+
|
|
168
|
+
See the [API documentation](https://github.com/aethexa1/aethexai)
|
|
169
|
+
for the full verdict shape.
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
client = AethexClient(
|
|
175
|
+
api_key="aex_live_...",
|
|
176
|
+
base_url="https://aethex-api-1yqe.onrender.com", # default
|
|
177
|
+
timeout=30, # seconds
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Status
|
|
182
|
+
|
|
183
|
+
Alpha. APIs are stabilizing. Breaking changes possible until 1.0.
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT. See LICENSE for details.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
Built by Aethex. Questions: open an issue at the
|
|
192
|
+
[GitHub repo](https://github.com/aethexa1/aethexai/issues).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
aethex/__init__.py,sha256=tClu2EPjtkU5oxDjCWSmPNx3LPTBLookib6VkalY63o,1060
|
|
2
|
+
aethex/_version.py,sha256=qLVCe9Pq4w9YtTvXGkK-5M-YLI3POEYf0_oy78UaEdc,465
|
|
3
|
+
aethex/client.py,sha256=4fvszcu3jt98IWIZGtxZzmyjOHH09rFyyHHRYVHZv8Y,12354
|
|
4
|
+
aethex/exceptions.py,sha256=4x4JMGk3G5_JfnPOhb7ThILjHXCQ3vkm1-SbB6GbwsI,2233
|
|
5
|
+
aethex-0.1.0.dist-info/METADATA,sha256=6W9TsOtnmbg9kzKmHhycYJcCIq9rwBbR5dqL2YyGtbA,5865
|
|
6
|
+
aethex-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
aethex-0.1.0.dist-info/top_level.txt,sha256=eZHPcDAoPcEcBzMeQcgMdB05fMmcS-9g7JjUxG_i9VY,7
|
|
8
|
+
aethex-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
aethex
|