aethex 0.1.0__tar.gz
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-0.1.0/PKG-INFO +192 -0
- aethex-0.1.0/README.md +160 -0
- aethex-0.1.0/aethex/__init__.py +51 -0
- aethex-0.1.0/aethex/_version.py +15 -0
- aethex-0.1.0/aethex/client.py +348 -0
- aethex-0.1.0/aethex/exceptions.py +71 -0
- aethex-0.1.0/aethex.egg-info/PKG-INFO +192 -0
- aethex-0.1.0/aethex.egg-info/SOURCES.txt +13 -0
- aethex-0.1.0/aethex.egg-info/dependency_links.txt +1 -0
- aethex-0.1.0/aethex.egg-info/requires.txt +5 -0
- aethex-0.1.0/aethex.egg-info/top_level.txt +1 -0
- aethex-0.1.0/pyproject.toml +65 -0
- aethex-0.1.0/setup.cfg +4 -0
- aethex-0.1.0/setup.py +12 -0
- aethex-0.1.0/tests/test_client.py +171 -0
aethex-0.1.0/PKG-INFO
ADDED
|
@@ -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).
|
aethex-0.1.0/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Aethex Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for [Aethex](https://github.com/aethexa1/aethexai) —
|
|
4
|
+
a multi-domain AI threat verification API with explainable reasoning chains.
|
|
5
|
+
|
|
6
|
+
Aethex runs threat events through 10 deterministic reasoning engines and
|
|
7
|
+
returns a verdict with severity, confidence, and a traceable reasoning
|
|
8
|
+
chain. Same engines work across three product domains:
|
|
9
|
+
|
|
10
|
+
- **Cyber** — SOC alerts, intrusion patterns, insider threats
|
|
11
|
+
- **Finance** — AML, sanctions screening, FATF typologies
|
|
12
|
+
- **LLM safety** — agent failure modes, prompt injection, tool-use exfiltration
|
|
13
|
+
|
|
14
|
+
Verdicts are deterministic. Same input produces the same output every
|
|
15
|
+
time, with the same audit trail. This is the core differentiator vs
|
|
16
|
+
LLM-only detection systems.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install aethex
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
No external dependencies. Pure stdlib (urllib, json).
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from aethex import AethexClient
|
|
30
|
+
|
|
31
|
+
client = AethexClient(api_key="aex_live_...")
|
|
32
|
+
|
|
33
|
+
# Cyber threat analysis
|
|
34
|
+
verdict = client.cyber.analyze(events=[
|
|
35
|
+
{"type": "failed_login"},
|
|
36
|
+
{"type": "failed_login"},
|
|
37
|
+
{"type": "successful_login", "suspicious": True},
|
|
38
|
+
{"type": "privilege_escalation", "suspicious": True},
|
|
39
|
+
{"type": "lateral_movement", "suspicious": True},
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
print(verdict["final_severity"]) # CRITICAL
|
|
43
|
+
print(verdict["final_confidence"]) # 0.92
|
|
44
|
+
print(verdict["verdict"]) # human-readable summary
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Three product domains
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
# Cyber
|
|
51
|
+
client.cyber.analyze(events=[...])
|
|
52
|
+
|
|
53
|
+
# Finance (AML / sanctions / fraud)
|
|
54
|
+
client.finance.analyze(events=[
|
|
55
|
+
{"type": "ofac_full_match", "suspicious": True},
|
|
56
|
+
{"type": "sanctioned_geography_routing", "suspicious": True},
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
# LLM safety
|
|
60
|
+
client.llm.analyze(events=[
|
|
61
|
+
{"type": "external_document_retrieval"},
|
|
62
|
+
{"type": "instruction_override_detected", "suspicious": True},
|
|
63
|
+
{"type": "tool_use_data_exfiltration", "suspicious": True},
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
# Discover the canonical LLM event vocabulary
|
|
67
|
+
mapping = client.llm.vocabulary()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Account operations
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# Audit log export (compliance)
|
|
74
|
+
audit = client.account.export_audit(format="json", from_="2026-01-01")
|
|
75
|
+
|
|
76
|
+
# Customer-tunable severity weighting
|
|
77
|
+
client.account.replace_loss_matrix({
|
|
78
|
+
"failed_login": 0.5, # downweight noisy events
|
|
79
|
+
"external_transfer": 5.0, # upweight critical events
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
# Webhooks for real-time alerts
|
|
83
|
+
webhook = client.account.create_webhook(
|
|
84
|
+
url="https://your-app.com/aethex-webhook",
|
|
85
|
+
threshold="CRITICAL",
|
|
86
|
+
)
|
|
87
|
+
# Save webhook["secret"] now -- it's shown only once.
|
|
88
|
+
|
|
89
|
+
deliveries = client.account.list_deliveries(webhook["id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Error handling
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from aethex import (
|
|
96
|
+
AethexAuthError,
|
|
97
|
+
AethexRateLimitError,
|
|
98
|
+
AethexValidationError,
|
|
99
|
+
AethexConnectionError,
|
|
100
|
+
AethexError,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
verdict = client.cyber.analyze(events=[...])
|
|
105
|
+
except AethexAuthError:
|
|
106
|
+
# 401 — bad / missing / revoked API key
|
|
107
|
+
...
|
|
108
|
+
except AethexRateLimitError:
|
|
109
|
+
# 429 — back off and retry
|
|
110
|
+
...
|
|
111
|
+
except AethexValidationError as e:
|
|
112
|
+
# 400 — request body malformed
|
|
113
|
+
print(e.response)
|
|
114
|
+
except AethexConnectionError:
|
|
115
|
+
# network / timeout / DNS / SSL
|
|
116
|
+
...
|
|
117
|
+
except AethexError:
|
|
118
|
+
# catch-all for anything Aethex-related
|
|
119
|
+
...
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## What you get back
|
|
123
|
+
|
|
124
|
+
Every `analyze` call returns a verdict dict with:
|
|
125
|
+
|
|
126
|
+
- `final_severity` — `LOW` / `MEDIUM` / `HIGH` / `CRITICAL`
|
|
127
|
+
- `final_confidence` — 0.0 to 1.0
|
|
128
|
+
- `verdict` — human-readable summary
|
|
129
|
+
- `action` — recommended operational response
|
|
130
|
+
- `engines` — per-engine outputs from all 10 reasoning engines
|
|
131
|
+
- `nyaya` — the formal reasoning chain that produced the verdict
|
|
132
|
+
- `chakravyuha` — defense-in-depth layers breached
|
|
133
|
+
- `disagreement` — when engines disagree, the system surfaces uncertainty
|
|
134
|
+
rather than producing false confidence
|
|
135
|
+
|
|
136
|
+
See the [API documentation](https://github.com/aethexa1/aethexai)
|
|
137
|
+
for the full verdict shape.
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
client = AethexClient(
|
|
143
|
+
api_key="aex_live_...",
|
|
144
|
+
base_url="https://aethex-api-1yqe.onrender.com", # default
|
|
145
|
+
timeout=30, # seconds
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Status
|
|
150
|
+
|
|
151
|
+
Alpha. APIs are stabilizing. Breaking changes possible until 1.0.
|
|
152
|
+
|
|
153
|
+
## License
|
|
154
|
+
|
|
155
|
+
MIT. See LICENSE for details.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
Built by Aethex. Questions: open an issue at the
|
|
160
|
+
[GitHub repo](https://github.com/aethexa1/aethexai/issues).
|
|
@@ -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
|
+
]
|
|
@@ -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"
|
|
@@ -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
|
+
)
|
|
@@ -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,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
aethex/__init__.py
|
|
5
|
+
aethex/_version.py
|
|
6
|
+
aethex/client.py
|
|
7
|
+
aethex/exceptions.py
|
|
8
|
+
aethex.egg-info/PKG-INFO
|
|
9
|
+
aethex.egg-info/SOURCES.txt
|
|
10
|
+
aethex.egg-info/dependency_links.txt
|
|
11
|
+
aethex.egg-info/requires.txt
|
|
12
|
+
aethex.egg-info/top_level.txt
|
|
13
|
+
tests/test_client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
aethex
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "aethex"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Official Python SDK for Aethex — multi-domain AI threat verification (cyber, finance, LLM safety) with explainable reasoning chains."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Aethex", email = "lizsanchez@aethexai.net" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"ai-safety",
|
|
17
|
+
"threat-detection",
|
|
18
|
+
"llm-safety",
|
|
19
|
+
"cybersecurity",
|
|
20
|
+
"fraud-detection",
|
|
21
|
+
"aml",
|
|
22
|
+
"fatf",
|
|
23
|
+
"mitre-attack",
|
|
24
|
+
"explainable-ai",
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 3 - Alpha",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Intended Audience :: Information Technology",
|
|
30
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
31
|
+
"Topic :: Security",
|
|
32
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
33
|
+
"License :: OSI Approved :: MIT License",
|
|
34
|
+
"Programming Language :: Python :: 3",
|
|
35
|
+
"Programming Language :: Python :: 3.8",
|
|
36
|
+
"Programming Language :: Python :: 3.9",
|
|
37
|
+
"Programming Language :: Python :: 3.10",
|
|
38
|
+
"Programming Language :: Python :: 3.11",
|
|
39
|
+
"Programming Language :: Python :: 3.12",
|
|
40
|
+
"Operating System :: OS Independent",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# No runtime dependencies. Pure stdlib (urllib, json).
|
|
44
|
+
# Customers shouldn't need to install requests or httpx just to use Aethex.
|
|
45
|
+
dependencies = []
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=7.0",
|
|
50
|
+
"build>=1.0",
|
|
51
|
+
"twine>=4.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[project.urls]
|
|
55
|
+
Homepage = "https://github.com/aethexa1/aethexai"
|
|
56
|
+
Documentation = "https://github.com/aethexa1/aethexai/tree/main/docs"
|
|
57
|
+
Repository = "https://github.com/aethexa1/aethexai"
|
|
58
|
+
Issues = "https://github.com/aethexa1/aethexai/issues"
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.packages.find]
|
|
61
|
+
where = ["."]
|
|
62
|
+
include = ["aethex*"]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.dynamic]
|
|
65
|
+
version = { attr = "aethex._version.__version__" }
|
aethex-0.1.0/setup.cfg
ADDED
aethex-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex Python SDK — setup.py
|
|
3
|
+
|
|
4
|
+
Modern packaging metadata lives in pyproject.toml. This file exists
|
|
5
|
+
for legacy compatibility with build tools that still expect a
|
|
6
|
+
setup.py to be present in the package root. setuptools will read
|
|
7
|
+
the pyproject.toml automatically.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from setuptools import setup
|
|
11
|
+
|
|
12
|
+
setup()
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aethex SDK — basic tests.
|
|
3
|
+
|
|
4
|
+
Run with:
|
|
5
|
+
cd sdk && pip install -e .[dev] && pytest tests/
|
|
6
|
+
|
|
7
|
+
These are unit tests that don't hit the live API. They verify:
|
|
8
|
+
- Client construction validates inputs
|
|
9
|
+
- Exception mapping is correct for each HTTP status code
|
|
10
|
+
- The repr doesn't leak the API key
|
|
11
|
+
|
|
12
|
+
Integration tests (which actually call the live API) live separately
|
|
13
|
+
and require AETHEX_API_KEY to be set. See test_integration.py if it
|
|
14
|
+
exists.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from aethex import (
|
|
20
|
+
AethexClient,
|
|
21
|
+
AethexAuthError,
|
|
22
|
+
AethexConnectionError,
|
|
23
|
+
AethexError,
|
|
24
|
+
AethexNotFoundError,
|
|
25
|
+
AethexPermissionError,
|
|
26
|
+
AethexRateLimitError,
|
|
27
|
+
AethexServerError,
|
|
28
|
+
AethexValidationError,
|
|
29
|
+
)
|
|
30
|
+
from aethex.client import _raise_for_status
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ─── client construction ──────────────────────────────────
|
|
34
|
+
|
|
35
|
+
class TestClientConstruction:
|
|
36
|
+
|
|
37
|
+
def test_valid_key(self):
|
|
38
|
+
client = AethexClient(api_key="aex_live_test123")
|
|
39
|
+
assert client._api_key == "aex_live_test123"
|
|
40
|
+
|
|
41
|
+
def test_default_base_url(self):
|
|
42
|
+
client = AethexClient(api_key="aex_live_test123")
|
|
43
|
+
assert client._base_url == "https://aethex-api-1yqe.onrender.com"
|
|
44
|
+
|
|
45
|
+
def test_custom_base_url(self):
|
|
46
|
+
client = AethexClient(
|
|
47
|
+
api_key="aex_live_test123",
|
|
48
|
+
base_url="http://localhost:8000",
|
|
49
|
+
)
|
|
50
|
+
assert client._base_url == "http://localhost:8000"
|
|
51
|
+
|
|
52
|
+
def test_base_url_trailing_slash_stripped(self):
|
|
53
|
+
client = AethexClient(
|
|
54
|
+
api_key="aex_live_test123",
|
|
55
|
+
base_url="http://localhost:8000/",
|
|
56
|
+
)
|
|
57
|
+
assert client._base_url == "http://localhost:8000"
|
|
58
|
+
|
|
59
|
+
def test_empty_key_rejected(self):
|
|
60
|
+
with pytest.raises(ValueError, match="non-empty string"):
|
|
61
|
+
AethexClient(api_key="")
|
|
62
|
+
|
|
63
|
+
def test_none_key_rejected(self):
|
|
64
|
+
with pytest.raises(ValueError, match="non-empty string"):
|
|
65
|
+
AethexClient(api_key=None)
|
|
66
|
+
|
|
67
|
+
def test_non_string_key_rejected(self):
|
|
68
|
+
with pytest.raises(ValueError, match="non-empty string"):
|
|
69
|
+
AethexClient(api_key=12345)
|
|
70
|
+
|
|
71
|
+
def test_bad_format_key_rejected(self):
|
|
72
|
+
with pytest.raises(ValueError, match="invalid format"):
|
|
73
|
+
AethexClient(api_key="not_aethex_key")
|
|
74
|
+
|
|
75
|
+
def test_subclients_present(self):
|
|
76
|
+
client = AethexClient(api_key="aex_live_test123")
|
|
77
|
+
assert client.cyber is not None
|
|
78
|
+
assert client.finance is not None
|
|
79
|
+
assert client.llm is not None
|
|
80
|
+
assert client.account is not None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestClientRepr:
|
|
84
|
+
|
|
85
|
+
def test_repr_does_not_leak_key(self):
|
|
86
|
+
"""The repr must never include the full API key."""
|
|
87
|
+
client = AethexClient(api_key="aex_live_supersecret_dontleakme_abcdef")
|
|
88
|
+
r = repr(client)
|
|
89
|
+
assert "supersecret" not in r
|
|
90
|
+
assert "dontleakme" not in r
|
|
91
|
+
# But the suffix is fine for debugging
|
|
92
|
+
assert "cdef" in r
|
|
93
|
+
|
|
94
|
+
def test_repr_includes_base_url(self):
|
|
95
|
+
client = AethexClient(
|
|
96
|
+
api_key="aex_live_test123",
|
|
97
|
+
base_url="http://localhost:8000",
|
|
98
|
+
)
|
|
99
|
+
assert "localhost:8000" in repr(client)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ─── exception mapping ────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
class TestExceptionMapping:
|
|
105
|
+
|
|
106
|
+
def test_401_raises_auth(self):
|
|
107
|
+
with pytest.raises(AethexAuthError) as exc:
|
|
108
|
+
_raise_for_status(401, '{"detail":"Invalid key"}')
|
|
109
|
+
assert exc.value.status_code == 401
|
|
110
|
+
|
|
111
|
+
def test_403_raises_permission(self):
|
|
112
|
+
with pytest.raises(AethexPermissionError):
|
|
113
|
+
_raise_for_status(403, '{"detail":"Wrong product"}')
|
|
114
|
+
|
|
115
|
+
def test_404_raises_not_found(self):
|
|
116
|
+
with pytest.raises(AethexNotFoundError):
|
|
117
|
+
_raise_for_status(404, '{"detail":"Webhook not found"}')
|
|
118
|
+
|
|
119
|
+
def test_429_raises_rate_limit(self):
|
|
120
|
+
with pytest.raises(AethexRateLimitError):
|
|
121
|
+
_raise_for_status(429, '{"detail":"Slow down"}')
|
|
122
|
+
|
|
123
|
+
def test_400_raises_validation(self):
|
|
124
|
+
with pytest.raises(AethexValidationError):
|
|
125
|
+
_raise_for_status(400, '{"detail":"Bad event"}')
|
|
126
|
+
|
|
127
|
+
def test_422_raises_validation(self):
|
|
128
|
+
"""FastAPI returns 422 for Pydantic validation errors."""
|
|
129
|
+
with pytest.raises(AethexValidationError):
|
|
130
|
+
_raise_for_status(422, '{"detail":[{"msg":"field required"}]}')
|
|
131
|
+
|
|
132
|
+
def test_500_raises_server(self):
|
|
133
|
+
with pytest.raises(AethexServerError):
|
|
134
|
+
_raise_for_status(500, "Internal Server Error")
|
|
135
|
+
|
|
136
|
+
def test_502_raises_server(self):
|
|
137
|
+
with pytest.raises(AethexServerError):
|
|
138
|
+
_raise_for_status(502, "Bad Gateway")
|
|
139
|
+
|
|
140
|
+
def test_503_raises_server(self):
|
|
141
|
+
with pytest.raises(AethexServerError):
|
|
142
|
+
_raise_for_status(503, "Service Unavailable")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ─── exception class behavior ─────────────────────────────
|
|
146
|
+
|
|
147
|
+
class TestExceptionClasses:
|
|
148
|
+
|
|
149
|
+
def test_all_inherit_from_aethex_error(self):
|
|
150
|
+
"""Customers should be able to catch AethexError to catch any SDK error."""
|
|
151
|
+
for exc_cls in (
|
|
152
|
+
AethexAuthError,
|
|
153
|
+
AethexPermissionError,
|
|
154
|
+
AethexNotFoundError,
|
|
155
|
+
AethexRateLimitError,
|
|
156
|
+
AethexValidationError,
|
|
157
|
+
AethexServerError,
|
|
158
|
+
AethexConnectionError,
|
|
159
|
+
):
|
|
160
|
+
assert issubclass(exc_cls, AethexError)
|
|
161
|
+
|
|
162
|
+
def test_carries_status_code(self):
|
|
163
|
+
e = AethexAuthError("test", status_code=401, response="body")
|
|
164
|
+
assert e.status_code == 401
|
|
165
|
+
assert e.response == "body"
|
|
166
|
+
|
|
167
|
+
def test_repr_includes_class_name_and_status(self):
|
|
168
|
+
e = AethexAuthError("auth failed", status_code=401)
|
|
169
|
+
r = repr(e)
|
|
170
|
+
assert "AethexAuthError" in r
|
|
171
|
+
assert "401" in r
|