modus-sdk 1.1.2__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.
Potentially problematic release.
This version of modus-sdk might be problematic. Click here for more details.
- modus/__init__.py +117 -0
- modus/_http.py +79 -0
- modus/async_client.py +604 -0
- modus/client.py +604 -0
- modus/crypto.py +77 -0
- modus/exceptions.py +52 -0
- modus/py.typed +0 -0
- modus/types.py +446 -0
- modus_sdk-1.1.2.dist-info/METADATA +296 -0
- modus_sdk-1.1.2.dist-info/RECORD +11 -0
- modus_sdk-1.1.2.dist-info/WHEEL +4 -0
modus/__init__.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Modus Python SDK — trust infrastructure for AI agents."""
|
|
2
|
+
|
|
3
|
+
from .async_client import AsyncModusClient
|
|
4
|
+
from .client import ModusClient
|
|
5
|
+
from .crypto import (
|
|
6
|
+
compute_content_hash,
|
|
7
|
+
verify_attestation_signature,
|
|
8
|
+
verify_content_hash,
|
|
9
|
+
verify_ed25519,
|
|
10
|
+
)
|
|
11
|
+
from .exceptions import (
|
|
12
|
+
AuthenticationError,
|
|
13
|
+
AuthorizationError,
|
|
14
|
+
ConflictError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
ModusError,
|
|
17
|
+
RateLimitError,
|
|
18
|
+
ValidationError,
|
|
19
|
+
)
|
|
20
|
+
from .types import (
|
|
21
|
+
AgentPoP,
|
|
22
|
+
AnonymousPolicy,
|
|
23
|
+
Attestation,
|
|
24
|
+
AttestationRecord,
|
|
25
|
+
CatalogCreate,
|
|
26
|
+
CatalogPermission,
|
|
27
|
+
CatalogSnapshot,
|
|
28
|
+
CheckResult,
|
|
29
|
+
ConsumeRequest,
|
|
30
|
+
ConsumeResult,
|
|
31
|
+
ConstraintTemplate,
|
|
32
|
+
ConstraintType,
|
|
33
|
+
CumulativeState,
|
|
34
|
+
DiscoveryQuery,
|
|
35
|
+
DiscoveryResult,
|
|
36
|
+
EnforceRequest,
|
|
37
|
+
EnforceResult,
|
|
38
|
+
EnforcementAttestation,
|
|
39
|
+
Gate,
|
|
40
|
+
GateCreate,
|
|
41
|
+
GateUpdate,
|
|
42
|
+
Issuer,
|
|
43
|
+
Passport,
|
|
44
|
+
PassportIssue,
|
|
45
|
+
PublishedCatalog,
|
|
46
|
+
PublishResult,
|
|
47
|
+
ReissueResult,
|
|
48
|
+
SettlementRequest,
|
|
49
|
+
SettlementSummary,
|
|
50
|
+
SLAComplianceReport,
|
|
51
|
+
SLAMetric,
|
|
52
|
+
VerifyResult,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
# Clients
|
|
57
|
+
"ModusClient",
|
|
58
|
+
"AsyncModusClient",
|
|
59
|
+
# Exceptions
|
|
60
|
+
"ModusError",
|
|
61
|
+
"AuthenticationError",
|
|
62
|
+
"AuthorizationError",
|
|
63
|
+
"NotFoundError",
|
|
64
|
+
"RateLimitError",
|
|
65
|
+
"ValidationError",
|
|
66
|
+
"ConflictError",
|
|
67
|
+
# Crypto
|
|
68
|
+
"verify_ed25519",
|
|
69
|
+
"compute_content_hash",
|
|
70
|
+
"verify_attestation_signature",
|
|
71
|
+
"verify_content_hash",
|
|
72
|
+
# Types — Gates
|
|
73
|
+
"Gate",
|
|
74
|
+
"GateCreate",
|
|
75
|
+
"GateUpdate",
|
|
76
|
+
# Types — Passports
|
|
77
|
+
"Passport",
|
|
78
|
+
"PassportIssue",
|
|
79
|
+
# Types — Issuers
|
|
80
|
+
"Issuer",
|
|
81
|
+
# Types — Attestations
|
|
82
|
+
"Attestation",
|
|
83
|
+
"AttestationRecord",
|
|
84
|
+
# Types — Catalog
|
|
85
|
+
"CatalogPermission",
|
|
86
|
+
"CatalogCreate",
|
|
87
|
+
"CatalogSnapshot",
|
|
88
|
+
"PublishedCatalog",
|
|
89
|
+
"PublishResult",
|
|
90
|
+
# Types — Constraints
|
|
91
|
+
"ConstraintType",
|
|
92
|
+
"ConstraintTemplate",
|
|
93
|
+
# Types — Enforcement
|
|
94
|
+
"EnforceRequest",
|
|
95
|
+
"EnforceResult",
|
|
96
|
+
"EnforcementAttestation",
|
|
97
|
+
# Types — Anonymous
|
|
98
|
+
"AnonymousPolicy",
|
|
99
|
+
# Types — State
|
|
100
|
+
"CumulativeState",
|
|
101
|
+
# Types — Commerce
|
|
102
|
+
"AgentPoP",
|
|
103
|
+
"ConsumeRequest",
|
|
104
|
+
"ConsumeResult",
|
|
105
|
+
"DiscoveryResult",
|
|
106
|
+
"DiscoveryQuery",
|
|
107
|
+
"SettlementRequest",
|
|
108
|
+
"SettlementSummary",
|
|
109
|
+
"SLAMetric",
|
|
110
|
+
"SLAComplianceReport",
|
|
111
|
+
# Types — Check & Verify
|
|
112
|
+
"CheckResult",
|
|
113
|
+
"ReissueResult",
|
|
114
|
+
"VerifyResult",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
__version__ = "1.1.0"
|
modus/_http.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Low-level HTTP transport shared by sync and async clients."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
AuthenticationError,
|
|
11
|
+
AuthorizationError,
|
|
12
|
+
ConflictError,
|
|
13
|
+
NotFoundError,
|
|
14
|
+
ModusError,
|
|
15
|
+
RateLimitError,
|
|
16
|
+
ValidationError,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
DEFAULT_BASE_URL = "https://modusoperator.com"
|
|
20
|
+
DEFAULT_TIMEOUT = 30.0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _raise_for_status(response: httpx.Response) -> None:
|
|
24
|
+
"""Map HTTP error status codes to typed exceptions."""
|
|
25
|
+
if response.is_success:
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
body = response.json()
|
|
30
|
+
message = body.get("message") or body.get("error") or f"API error: {response.status_code}"
|
|
31
|
+
except Exception:
|
|
32
|
+
body = None
|
|
33
|
+
message = f"API error: {response.status_code} {response.reason_phrase}"
|
|
34
|
+
|
|
35
|
+
kwargs: dict[str, Any] = {"status_code": response.status_code, "body": body}
|
|
36
|
+
|
|
37
|
+
if response.status_code == 401:
|
|
38
|
+
raise AuthenticationError(message, **kwargs)
|
|
39
|
+
if response.status_code == 403:
|
|
40
|
+
raise AuthorizationError(message, **kwargs)
|
|
41
|
+
if response.status_code == 404:
|
|
42
|
+
raise NotFoundError(message, **kwargs)
|
|
43
|
+
if response.status_code == 409:
|
|
44
|
+
raise ConflictError(message, **kwargs)
|
|
45
|
+
if response.status_code == 429:
|
|
46
|
+
retry_after = response.headers.get("retry-after")
|
|
47
|
+
raise RateLimitError(
|
|
48
|
+
message,
|
|
49
|
+
retry_after=float(retry_after) if retry_after else None,
|
|
50
|
+
**kwargs,
|
|
51
|
+
)
|
|
52
|
+
if response.status_code in (400, 422):
|
|
53
|
+
raise ValidationError(message, **kwargs)
|
|
54
|
+
|
|
55
|
+
raise ModusError(message, **kwargs)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _parse_response(response: httpx.Response) -> Any:
|
|
59
|
+
"""Parse a successful HTTP response, handling 204 No Content."""
|
|
60
|
+
_raise_for_status(response)
|
|
61
|
+
if response.status_code == 204:
|
|
62
|
+
return {}
|
|
63
|
+
if not response.content:
|
|
64
|
+
return {}
|
|
65
|
+
return response.json()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _build_headers(api_key: str) -> dict[str, str]:
|
|
69
|
+
return {
|
|
70
|
+
"Authorization": f"Bearer {api_key}",
|
|
71
|
+
"Content-Type": "application/json",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _clean_params(params: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
|
|
76
|
+
"""Remove None values from query params."""
|
|
77
|
+
if params is None:
|
|
78
|
+
return None
|
|
79
|
+
return {k: v for k, v in params.items() if v is not None}
|