apical 0.2.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.
- apical-0.2.0/PKG-INFO +132 -0
- apical-0.2.0/README.md +104 -0
- apical-0.2.0/apical/__init__.py +88 -0
- apical-0.2.0/apical/classifier.py +205 -0
- apical-0.2.0/apical/client.py +371 -0
- apical-0.2.0/apical/exceptions.py +66 -0
- apical-0.2.0/apical/orthogonal.py +93 -0
- apical-0.2.0/apical/plexus.py +288 -0
- apical-0.2.0/apical/router.py +150 -0
- apical-0.2.0/apical/verifier.py +88 -0
- apical-0.2.0/apical.egg-info/PKG-INFO +132 -0
- apical-0.2.0/apical.egg-info/SOURCES.txt +18 -0
- apical-0.2.0/apical.egg-info/dependency_links.txt +1 -0
- apical-0.2.0/apical.egg-info/requires.txt +7 -0
- apical-0.2.0/apical.egg-info/top_level.txt +1 -0
- apical-0.2.0/pyproject.toml +46 -0
- apical-0.2.0/setup.cfg +4 -0
- apical-0.2.0/tests/test_client.py +271 -0
- apical-0.2.0/tests/test_orthogonal.py +83 -0
- apical-0.2.0/tests/test_verifier.py +97 -0
apical-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apical
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Apical Labs — Zero-Retention Continual Memory & Embedding Router
|
|
5
|
+
Author-email: Apical Labs Ltd <research@apical-labs.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://apical-labs.com
|
|
8
|
+
Project-URL: Documentation, https://docs.apical-labs.com
|
|
9
|
+
Project-URL: Repository, https://github.com/apical-labs/apical-sdk
|
|
10
|
+
Project-URL: Changelog, https://github.com/apical-labs/apical-sdk/releases
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Security :: Cryptography
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: numpy>=1.24.0
|
|
23
|
+
Requires-Dist: httpx>=0.25.0
|
|
24
|
+
Requires-Dist: cryptography>=41.0.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# `apical` — Python Client SDK for Apical Labs
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/apical/)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
Official Python client for the **Apical Labs** Zero-Retention Continual Memory & Embedding Router platform.
|
|
35
|
+
|
|
36
|
+
- **Zero Data Retention:** Every update operates in constant $O(1)$ memory (`persisted_bytes_delta == 0`). No replay buffers, no logs, and no historical samples.
|
|
37
|
+
- **Client-Side Privacy Blindness:** Transform features via a customer-held orthogonal secret ($Q \in O(d)$) before they leave your servers.
|
|
38
|
+
- **Cryptographic Receipts:** Every API request returns a tamper-evident `Ed25519` cryptographic receipt provable offline.
|
|
39
|
+
- **Instant Machine Unlearning:** Purge any class coordinate back to quiescent hold in under $1\text{ ms}$ (GDPR / UK GDPR Article 17).
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install apical
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Dependencies: `numpy>=1.24.0`, `httpx>=0.25.0`, `cryptography>=41.0.0`.
|
|
50
|
+
*Requires ZERO PyTorch and ZERO local training frameworks.*
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
### 1. Continual Classifier with Instant Machine Unlearning
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from apical import ApicalClient
|
|
60
|
+
|
|
61
|
+
# Initialize client (optionally pass privacy_secret for client-side orthogonal encryption)
|
|
62
|
+
client = ApicalClient(
|
|
63
|
+
api_key="apk_test_...",
|
|
64
|
+
privacy_secret="my-super-secret-salt-key",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Bind to model
|
|
68
|
+
classifier = client.classifier("customer-intent-v1")
|
|
69
|
+
|
|
70
|
+
# Create model on Apical Cloud (128-D input embedding, 10 classes)
|
|
71
|
+
classifier.create(in_features=128, num_classes=10)
|
|
72
|
+
|
|
73
|
+
# Online update (learn a new sample with zero data retention)
|
|
74
|
+
embedding = [0.05] * 128
|
|
75
|
+
learn_result = classifier.learn(embedding, target_class=2)
|
|
76
|
+
print(f"Updated: status={learn_result.status}, delta_bytes={learn_result.persisted_bytes_delta}")
|
|
77
|
+
|
|
78
|
+
# Predict class
|
|
79
|
+
pred = classifier.predict(embedding)
|
|
80
|
+
print(f"Predicted class {pred.prediction} with confidence {pred.confidence:.4f}")
|
|
81
|
+
|
|
82
|
+
# Instant GDPR Article 17 Machine Unlearning (< 1 ms)
|
|
83
|
+
unlearn_result = classifier.unlearn(class_id=2)
|
|
84
|
+
print(f"Erased class 2: status={unlearn_result.status}, delta_bytes={unlearn_result.persisted_bytes_delta}")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### 2. Real-Time Dynamic Memory Router
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from apical import ApicalClient
|
|
93
|
+
|
|
94
|
+
client = ApicalClient(api_key="apk_test_...")
|
|
95
|
+
router = client.memory_router("agent-tool-router")
|
|
96
|
+
|
|
97
|
+
# Create router: 512-D embeddings, 32 rule destinations
|
|
98
|
+
router.create(embedding_dim=512, num_rules=32)
|
|
99
|
+
|
|
100
|
+
# Route incoming query embedding to top 3 matching tools/rules
|
|
101
|
+
query_embedding = [0.12] * 512
|
|
102
|
+
route_result = router.route(query_embedding, top_k=3)
|
|
103
|
+
for r in route_result.routes:
|
|
104
|
+
print(f"Rule ID: {r.rule_id}, Affinity: {r.affinity}")
|
|
105
|
+
|
|
106
|
+
# Reinforce a target rule based on positive execution outcome
|
|
107
|
+
router.reinforce(query_embedding, target_rule=5)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### 3. Offline Ed25519 Zero-Retention Receipt Verification
|
|
113
|
+
|
|
114
|
+
Every response returns an `Ed25519` cryptographic receipt certifying:
|
|
115
|
+
1. `persisted_bytes_delta == 0` (mathematical proof that no raw sample was appended)
|
|
116
|
+
2. `input_buffer_zeroed == True` (transient RAM scrubber timestamp)
|
|
117
|
+
3. Cryptographic signature over canonical JSON
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from apical import verify_zero_retention_receipt
|
|
121
|
+
|
|
122
|
+
# Verify receipt offline without making any network requests
|
|
123
|
+
is_valid = verify_zero_retention_receipt(pred.receipt)
|
|
124
|
+
assert is_valid is True
|
|
125
|
+
print("Zero-retention receipt cryptographically verified!")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
Apache-2.0. Copyright © 2026 Apical Labs Ltd. All rights reserved.
|
apical-0.2.0/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# `apical` — Python Client SDK for Apical Labs
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/apical/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
Official Python client for the **Apical Labs** Zero-Retention Continual Memory & Embedding Router platform.
|
|
7
|
+
|
|
8
|
+
- **Zero Data Retention:** Every update operates in constant $O(1)$ memory (`persisted_bytes_delta == 0`). No replay buffers, no logs, and no historical samples.
|
|
9
|
+
- **Client-Side Privacy Blindness:** Transform features via a customer-held orthogonal secret ($Q \in O(d)$) before they leave your servers.
|
|
10
|
+
- **Cryptographic Receipts:** Every API request returns a tamper-evident `Ed25519` cryptographic receipt provable offline.
|
|
11
|
+
- **Instant Machine Unlearning:** Purge any class coordinate back to quiescent hold in under $1\text{ ms}$ (GDPR / UK GDPR Article 17).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install apical
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Dependencies: `numpy>=1.24.0`, `httpx>=0.25.0`, `cryptography>=41.0.0`.
|
|
22
|
+
*Requires ZERO PyTorch and ZERO local training frameworks.*
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quickstart
|
|
27
|
+
|
|
28
|
+
### 1. Continual Classifier with Instant Machine Unlearning
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from apical import ApicalClient
|
|
32
|
+
|
|
33
|
+
# Initialize client (optionally pass privacy_secret for client-side orthogonal encryption)
|
|
34
|
+
client = ApicalClient(
|
|
35
|
+
api_key="apk_test_...",
|
|
36
|
+
privacy_secret="my-super-secret-salt-key",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Bind to model
|
|
40
|
+
classifier = client.classifier("customer-intent-v1")
|
|
41
|
+
|
|
42
|
+
# Create model on Apical Cloud (128-D input embedding, 10 classes)
|
|
43
|
+
classifier.create(in_features=128, num_classes=10)
|
|
44
|
+
|
|
45
|
+
# Online update (learn a new sample with zero data retention)
|
|
46
|
+
embedding = [0.05] * 128
|
|
47
|
+
learn_result = classifier.learn(embedding, target_class=2)
|
|
48
|
+
print(f"Updated: status={learn_result.status}, delta_bytes={learn_result.persisted_bytes_delta}")
|
|
49
|
+
|
|
50
|
+
# Predict class
|
|
51
|
+
pred = classifier.predict(embedding)
|
|
52
|
+
print(f"Predicted class {pred.prediction} with confidence {pred.confidence:.4f}")
|
|
53
|
+
|
|
54
|
+
# Instant GDPR Article 17 Machine Unlearning (< 1 ms)
|
|
55
|
+
unlearn_result = classifier.unlearn(class_id=2)
|
|
56
|
+
print(f"Erased class 2: status={unlearn_result.status}, delta_bytes={unlearn_result.persisted_bytes_delta}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### 2. Real-Time Dynamic Memory Router
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from apical import ApicalClient
|
|
65
|
+
|
|
66
|
+
client = ApicalClient(api_key="apk_test_...")
|
|
67
|
+
router = client.memory_router("agent-tool-router")
|
|
68
|
+
|
|
69
|
+
# Create router: 512-D embeddings, 32 rule destinations
|
|
70
|
+
router.create(embedding_dim=512, num_rules=32)
|
|
71
|
+
|
|
72
|
+
# Route incoming query embedding to top 3 matching tools/rules
|
|
73
|
+
query_embedding = [0.12] * 512
|
|
74
|
+
route_result = router.route(query_embedding, top_k=3)
|
|
75
|
+
for r in route_result.routes:
|
|
76
|
+
print(f"Rule ID: {r.rule_id}, Affinity: {r.affinity}")
|
|
77
|
+
|
|
78
|
+
# Reinforce a target rule based on positive execution outcome
|
|
79
|
+
router.reinforce(query_embedding, target_rule=5)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### 3. Offline Ed25519 Zero-Retention Receipt Verification
|
|
85
|
+
|
|
86
|
+
Every response returns an `Ed25519` cryptographic receipt certifying:
|
|
87
|
+
1. `persisted_bytes_delta == 0` (mathematical proof that no raw sample was appended)
|
|
88
|
+
2. `input_buffer_zeroed == True` (transient RAM scrubber timestamp)
|
|
89
|
+
3. Cryptographic signature over canonical JSON
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from apical import verify_zero_retention_receipt
|
|
93
|
+
|
|
94
|
+
# Verify receipt offline without making any network requests
|
|
95
|
+
is_valid = verify_zero_retention_receipt(pred.receipt)
|
|
96
|
+
assert is_valid is True
|
|
97
|
+
print("Zero-retention receipt cryptographically verified!")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
Apache-2.0. Copyright © 2026 Apical Labs Ltd. All rights reserved.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Apical Labs — Zero-Retention Continual Memory & Embedding Router.
|
|
2
|
+
|
|
3
|
+
Client SDK providing:
|
|
4
|
+
- ApicalClient: Main client connection pool
|
|
5
|
+
- Classifier: Continual classification with instant GDPR Art. 17 unlearning
|
|
6
|
+
- MemoryRouter: Real-time dynamic embedding routing and reinforcement
|
|
7
|
+
- OrthogonalPrivacyProjector: Client-side mathematical input de-identification
|
|
8
|
+
- verify_zero_retention_receipt: Offline Ed25519 receipt verification
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from apical.classifier import (
|
|
14
|
+
Classifier,
|
|
15
|
+
LearnResult,
|
|
16
|
+
PredictionResult,
|
|
17
|
+
UnlearnResult,
|
|
18
|
+
)
|
|
19
|
+
from apical.client import ApicalClient, ArborNamespace, BridgeResult
|
|
20
|
+
|
|
21
|
+
# Backward-compatible convenience alias
|
|
22
|
+
Client = ApicalClient
|
|
23
|
+
from apical.exceptions import (
|
|
24
|
+
APIError,
|
|
25
|
+
ApicalError,
|
|
26
|
+
AuthenticationError,
|
|
27
|
+
FixedByteInvariantError,
|
|
28
|
+
ModelNotFoundError,
|
|
29
|
+
ProductNotEntitledError,
|
|
30
|
+
RateLimitError,
|
|
31
|
+
SparseProbeError,
|
|
32
|
+
SweepDetectedError,
|
|
33
|
+
VerificationError,
|
|
34
|
+
)
|
|
35
|
+
from apical.orthogonal import OrthogonalPrivacyProjector
|
|
36
|
+
from apical.plexus import (
|
|
37
|
+
PlexusClassifier,
|
|
38
|
+
PlexusLearnResult,
|
|
39
|
+
PlexusNetwork,
|
|
40
|
+
PlexusPredictionResult,
|
|
41
|
+
PlexusRouteResult,
|
|
42
|
+
PlexusRouter,
|
|
43
|
+
PlexusUnlearnResult,
|
|
44
|
+
)
|
|
45
|
+
from apical.router import (
|
|
46
|
+
MemoryRouter,
|
|
47
|
+
ReinforceResult,
|
|
48
|
+
RouteItem,
|
|
49
|
+
RouteResult,
|
|
50
|
+
)
|
|
51
|
+
from apical.verifier import verify_zero_retention_receipt
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__version__ = "0.2.0"
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"APIError",
|
|
58
|
+
"ApicalClient",
|
|
59
|
+
"ApicalError",
|
|
60
|
+
"ArborNamespace",
|
|
61
|
+
"AuthenticationError",
|
|
62
|
+
"BridgeResult",
|
|
63
|
+
"Classifier",
|
|
64
|
+
"Client",
|
|
65
|
+
"FixedByteInvariantError",
|
|
66
|
+
"LearnResult",
|
|
67
|
+
"MemoryRouter",
|
|
68
|
+
"ModelNotFoundError",
|
|
69
|
+
"OrthogonalPrivacyProjector",
|
|
70
|
+
"PlexusClassifier",
|
|
71
|
+
"PlexusLearnResult",
|
|
72
|
+
"PlexusNetwork",
|
|
73
|
+
"PlexusPredictionResult",
|
|
74
|
+
"PlexusRouteResult",
|
|
75
|
+
"PlexusRouter",
|
|
76
|
+
"PlexusUnlearnResult",
|
|
77
|
+
"PredictionResult",
|
|
78
|
+
"ProductNotEntitledError",
|
|
79
|
+
"RateLimitError",
|
|
80
|
+
"ReinforceResult",
|
|
81
|
+
"RouteItem",
|
|
82
|
+
"RouteResult",
|
|
83
|
+
"SparseProbeError",
|
|
84
|
+
"SweepDetectedError",
|
|
85
|
+
"UnlearnResult",
|
|
86
|
+
"VerificationError",
|
|
87
|
+
"verify_zero_retention_receipt",
|
|
88
|
+
]
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Classifier Surface for the Apical Labs Platform."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, NamedTuple, Optional, Sequence, Union
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from apical.orthogonal import OrthogonalPrivacyProjector
|
|
9
|
+
from apical.verifier import verify_zero_retention_receipt
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PredictionResult(NamedTuple):
|
|
13
|
+
"""Result of a classifier inference pass."""
|
|
14
|
+
prediction: int
|
|
15
|
+
confidence: float
|
|
16
|
+
receipt: Dict[str, Any]
|
|
17
|
+
crystallization_pct: Optional[float] = None
|
|
18
|
+
mean_plasticity: Optional[float] = None
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def signature(self) -> str:
|
|
22
|
+
"""Return the Ed25519 signature from the cryptographic attestation receipt."""
|
|
23
|
+
return str(self.receipt.get("ed25519_signature", ""))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LearnResult(NamedTuple):
|
|
27
|
+
"""Result of a single continual update."""
|
|
28
|
+
status: str
|
|
29
|
+
target_class: int
|
|
30
|
+
winner_class: int
|
|
31
|
+
confidence: float
|
|
32
|
+
update_count: int
|
|
33
|
+
persisted_bytes_delta: int
|
|
34
|
+
receipt: Dict[str, Any]
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def signature(self) -> str:
|
|
38
|
+
"""Return the Ed25519 signature from the cryptographic attestation receipt."""
|
|
39
|
+
return str(self.receipt.get("ed25519_signature", ""))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class UnlearnResult(NamedTuple):
|
|
43
|
+
"""Result of an instant GDPR Article 17 class erasure."""
|
|
44
|
+
status: str
|
|
45
|
+
erased_class_id: int
|
|
46
|
+
persisted_bytes_delta: int
|
|
47
|
+
receipt: Dict[str, Any]
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def signature(self) -> str:
|
|
51
|
+
"""Return the Ed25519 signature from the cryptographic attestation receipt."""
|
|
52
|
+
return str(self.receipt.get("ed25519_signature", ""))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Classifier:
|
|
56
|
+
"""Client interface for continuous classification models."""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
client: Any,
|
|
61
|
+
model_id: str,
|
|
62
|
+
in_features: Optional[int] = None,
|
|
63
|
+
num_classes: Optional[int] = None,
|
|
64
|
+
privacy_secret: Optional[str] = None,
|
|
65
|
+
) -> None:
|
|
66
|
+
self.client = client
|
|
67
|
+
self.model_id = model_id
|
|
68
|
+
self.in_features = in_features
|
|
69
|
+
self.num_classes = num_classes
|
|
70
|
+
self.privacy_secret = privacy_secret
|
|
71
|
+
|
|
72
|
+
self._projector: Optional[OrthogonalPrivacyProjector] = None
|
|
73
|
+
if self.privacy_secret and self.in_features:
|
|
74
|
+
self._projector = OrthogonalPrivacyProjector(self.privacy_secret, self.in_features)
|
|
75
|
+
|
|
76
|
+
def _prepare_vector(self, vector: Union[Sequence[float], np.ndarray]) -> list[float]:
|
|
77
|
+
"""Optionally apply orthogonal privacy projection, returning a JSON-serializable list."""
|
|
78
|
+
arr = np.asarray(vector, dtype=np.float32).ravel()
|
|
79
|
+
|
|
80
|
+
if self._projector is not None:
|
|
81
|
+
arr = self._projector.project(arr)
|
|
82
|
+
elif self.privacy_secret and not self._projector:
|
|
83
|
+
# Lazy initialize projector once vector dimension is known
|
|
84
|
+
self.in_features = arr.size
|
|
85
|
+
self._projector = OrthogonalPrivacyProjector(self.privacy_secret, self.in_features)
|
|
86
|
+
arr = self._projector.project(arr)
|
|
87
|
+
|
|
88
|
+
return arr.tolist()
|
|
89
|
+
|
|
90
|
+
def create(self, in_features: int, num_classes: int) -> Dict[str, Any]:
|
|
91
|
+
"""Create and initialize a new fixed-byte model container on Apical Cloud."""
|
|
92
|
+
self.in_features = in_features
|
|
93
|
+
self.num_classes = num_classes
|
|
94
|
+
|
|
95
|
+
if self.privacy_secret and not self._projector:
|
|
96
|
+
self._projector = OrthogonalPrivacyProjector(self.privacy_secret, self.in_features)
|
|
97
|
+
|
|
98
|
+
payload = {
|
|
99
|
+
"model_id": self.model_id,
|
|
100
|
+
"in_features": in_features,
|
|
101
|
+
"out_features": num_classes,
|
|
102
|
+
}
|
|
103
|
+
resp = self.client.request("POST", f"/api/v1/models", payload)
|
|
104
|
+
data = resp.get("data", resp)
|
|
105
|
+
|
|
106
|
+
if "receipt" in data:
|
|
107
|
+
verify_zero_retention_receipt(data["receipt"])
|
|
108
|
+
|
|
109
|
+
return data
|
|
110
|
+
|
|
111
|
+
def predict(
|
|
112
|
+
self,
|
|
113
|
+
vector: Union[Sequence[float], np.ndarray],
|
|
114
|
+
tier: int = 0,
|
|
115
|
+
verify_receipt: bool = True,
|
|
116
|
+
) -> PredictionResult:
|
|
117
|
+
"""Execute directional classification, returning class prediction and signed receipt."""
|
|
118
|
+
prep_vec = self._prepare_vector(vector)
|
|
119
|
+
payload = {
|
|
120
|
+
"model_id": self.model_id,
|
|
121
|
+
"vector": prep_vec,
|
|
122
|
+
"tier": tier,
|
|
123
|
+
}
|
|
124
|
+
resp = self.client.request("POST", f"/api/v1/predict", payload)
|
|
125
|
+
data = resp.get("data", resp)
|
|
126
|
+
|
|
127
|
+
receipt = data.get("receipt", {})
|
|
128
|
+
if verify_receipt and receipt:
|
|
129
|
+
verify_zero_retention_receipt(receipt)
|
|
130
|
+
|
|
131
|
+
return PredictionResult(
|
|
132
|
+
prediction=int(data["prediction"]),
|
|
133
|
+
confidence=float(data["confidence"]),
|
|
134
|
+
receipt=receipt,
|
|
135
|
+
crystallization_pct=data.get("crystallization_pct"),
|
|
136
|
+
mean_plasticity=data.get("mean_plasticity"),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def learn(
|
|
140
|
+
self,
|
|
141
|
+
vector: Union[Sequence[float], np.ndarray],
|
|
142
|
+
target_class: int,
|
|
143
|
+
tier: int = 0,
|
|
144
|
+
verify_receipt: bool = True,
|
|
145
|
+
) -> LearnResult:
|
|
146
|
+
"""Stream a single continual update, updating model state under zero retention."""
|
|
147
|
+
prep_vec = self._prepare_vector(vector)
|
|
148
|
+
payload = {
|
|
149
|
+
"model_id": self.model_id,
|
|
150
|
+
"vector": prep_vec,
|
|
151
|
+
"target_class": target_class,
|
|
152
|
+
"tier": tier,
|
|
153
|
+
}
|
|
154
|
+
resp = self.client.request("POST", f"/api/v1/learn", payload)
|
|
155
|
+
data = resp.get("data", resp)
|
|
156
|
+
|
|
157
|
+
receipt = data.get("receipt", {})
|
|
158
|
+
if verify_receipt and receipt:
|
|
159
|
+
verify_zero_retention_receipt(receipt)
|
|
160
|
+
|
|
161
|
+
return LearnResult(
|
|
162
|
+
status=str(data.get("status", "updated")),
|
|
163
|
+
target_class=int(data["target_class"]),
|
|
164
|
+
winner_class=int(data["winner_class"]),
|
|
165
|
+
confidence=float(data["confidence"]),
|
|
166
|
+
update_count=int(data.get("update_count", 0)),
|
|
167
|
+
persisted_bytes_delta=int(data.get("persisted_bytes_delta", 0)),
|
|
168
|
+
receipt=receipt,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def unlearn(
|
|
172
|
+
self,
|
|
173
|
+
class_id: int,
|
|
174
|
+
verify_receipt: bool = True,
|
|
175
|
+
) -> UnlearnResult:
|
|
176
|
+
"""Execute instant GDPR Article 17 Machine Unlearning for a specific class coordinate."""
|
|
177
|
+
payload = {
|
|
178
|
+
"model_id": self.model_id,
|
|
179
|
+
"class_id": class_id,
|
|
180
|
+
}
|
|
181
|
+
resp = self.client.request("POST", f"/api/v1/unlearn", payload)
|
|
182
|
+
data = resp.get("data", resp)
|
|
183
|
+
|
|
184
|
+
receipt = data.get("receipt", {})
|
|
185
|
+
if verify_receipt and receipt:
|
|
186
|
+
verify_zero_retention_receipt(receipt)
|
|
187
|
+
|
|
188
|
+
return UnlearnResult(
|
|
189
|
+
status=str(data.get("status", "erased")),
|
|
190
|
+
erased_class_id=int(data.get("erased_class_id", class_id)),
|
|
191
|
+
persisted_bytes_delta=int(data.get("persisted_bytes_delta", 0)),
|
|
192
|
+
receipt=receipt,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def erase_class(
|
|
196
|
+
self,
|
|
197
|
+
target_class: Optional[int] = None,
|
|
198
|
+
class_id: Optional[int] = None,
|
|
199
|
+
verify_receipt: bool = True,
|
|
200
|
+
) -> UnlearnResult:
|
|
201
|
+
"""Execute instant GDPR Article 17 Machine Unlearning for a specific class coordinate (alias for unlearn)."""
|
|
202
|
+
target = target_class if target_class is not None else class_id
|
|
203
|
+
if target is None:
|
|
204
|
+
raise ValueError("Either target_class or class_id must be provided")
|
|
205
|
+
return self.unlearn(class_id=target, verify_receipt=verify_receipt)
|