assistant-runtime-sdk 1.0.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.
- assistant_runtime_sdk/__init__.py +235 -0
- assistant_runtime_sdk/async_client.py +1989 -0
- assistant_runtime_sdk/auth.py +161 -0
- assistant_runtime_sdk/base.py +2277 -0
- assistant_runtime_sdk/client.py +3039 -0
- assistant_runtime_sdk/exceptions.py +146 -0
- assistant_runtime_sdk/skills.py +328 -0
- assistant_runtime_sdk/streaming.py +223 -0
- assistant_runtime_sdk/types.py +546 -0
- assistant_runtime_sdk-1.0.0.dist-info/METADATA +259 -0
- assistant_runtime_sdk-1.0.0.dist-info/RECORD +13 -0
- assistant_runtime_sdk-1.0.0.dist-info/WHEEL +4 -0
- assistant_runtime_sdk-1.0.0.dist-info/licenses/LICENSE +17 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Assistant Runtime SDK - Authentication
|
|
2
|
+
# Copyright (C) 2025 Paul Clinton
|
|
3
|
+
# AGPL-3.0 License
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
HMAC-SHA256 authentication for Assistant Runtime API requests.
|
|
7
|
+
|
|
8
|
+
Assistant Runtime uses HMAC-SHA256 signatures to authenticate API requests.
|
|
9
|
+
The signature format is: {timestamp}:{signature}
|
|
10
|
+
Where signature = HMAC-SHA256(tenant_secret, "{tenant_id}:{timestamp}:{params_json}")
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import hmac
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
import time
|
|
17
|
+
from typing import Dict, Any, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_signature(
|
|
21
|
+
tenant_id: str,
|
|
22
|
+
tenant_secret: str,
|
|
23
|
+
params: Dict[str, Any],
|
|
24
|
+
for_query_string: bool = False,
|
|
25
|
+
timestamp: Optional[int] = None,
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Generate HMAC-SHA256 signature for Assistant Runtime API request.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
tenant_id: Unique tenant identifier
|
|
32
|
+
tenant_secret: HMAC secret for request signing
|
|
33
|
+
params: Request parameters (will be sorted by key)
|
|
34
|
+
for_query_string: If True, convert values to strings (for GET requests).
|
|
35
|
+
If False, keep original types (for POST JSON body).
|
|
36
|
+
timestamp: Optional timestamp override (for testing). Uses current time if None.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Signature header value in format "timestamp:signature"
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> sig = generate_signature("tenant-123", "secret", {"message": "Hello"})
|
|
43
|
+
>>> print(sig) # "1704067200:a1b2c3d4..."
|
|
44
|
+
"""
|
|
45
|
+
if timestamp is None:
|
|
46
|
+
timestamp = int(time.time())
|
|
47
|
+
timestamp_str = str(timestamp)
|
|
48
|
+
|
|
49
|
+
if for_query_string:
|
|
50
|
+
# For GET requests: convert all values to strings to match how
|
|
51
|
+
# frappe.request.args returns all values as strings
|
|
52
|
+
params_to_sign = {k: str(v) if not isinstance(v, str) else v for k, v in params.items()}
|
|
53
|
+
else:
|
|
54
|
+
# For POST requests with JSON body: keep original types
|
|
55
|
+
# Assistant Runtime receives the parsed JSON with original types
|
|
56
|
+
params_to_sign = params
|
|
57
|
+
|
|
58
|
+
# JSON format must match Python's json.dumps with sort_keys and separators
|
|
59
|
+
# Assistant Runtime uses: json.dumps(params, sort_keys=True, separators=(', ', ': '))
|
|
60
|
+
params_json = json.dumps(params_to_sign, sort_keys=True, separators=(", ", ": "))
|
|
61
|
+
|
|
62
|
+
# Build message: tenant_id:timestamp:params_json
|
|
63
|
+
message = f"{tenant_id}:{timestamp_str}:{params_json}"
|
|
64
|
+
|
|
65
|
+
# Generate HMAC-SHA256 signature
|
|
66
|
+
signature = hmac.new(
|
|
67
|
+
tenant_secret.encode("utf-8"),
|
|
68
|
+
message.encode("utf-8"),
|
|
69
|
+
hashlib.sha256,
|
|
70
|
+
).hexdigest()
|
|
71
|
+
|
|
72
|
+
return f"{timestamp_str}:{signature}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def verify_signature(
|
|
76
|
+
signature_header: str,
|
|
77
|
+
tenant_id: str,
|
|
78
|
+
tenant_secret: str,
|
|
79
|
+
params: Dict[str, Any],
|
|
80
|
+
for_query_string: bool = False,
|
|
81
|
+
max_age_seconds: int = 300,
|
|
82
|
+
) -> bool:
|
|
83
|
+
"""
|
|
84
|
+
Verify HMAC-SHA256 signature from Assistant Runtime request.
|
|
85
|
+
|
|
86
|
+
Useful for servers receiving callbacks or webhooks from Assistant Runtime.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
signature_header: The X-AR-Signature header value ("timestamp:signature")
|
|
90
|
+
tenant_id: Unique tenant identifier
|
|
91
|
+
tenant_secret: HMAC secret for request signing
|
|
92
|
+
params: Request parameters to verify
|
|
93
|
+
for_query_string: If True, treat params as query string (values as strings)
|
|
94
|
+
max_age_seconds: Maximum age of signature in seconds (default 5 minutes)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
True if signature is valid and not expired, False otherwise
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> is_valid = verify_signature(
|
|
101
|
+
... "1704067200:a1b2c3d4...",
|
|
102
|
+
... "tenant-123",
|
|
103
|
+
... "secret",
|
|
104
|
+
... {"callback": "data"}
|
|
105
|
+
... )
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
parts = signature_header.split(":", 1)
|
|
109
|
+
if len(parts) != 2:
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
timestamp_str, received_signature = parts
|
|
113
|
+
timestamp = int(timestamp_str)
|
|
114
|
+
|
|
115
|
+
# Check if signature is too old
|
|
116
|
+
current_time = int(time.time())
|
|
117
|
+
if abs(current_time - timestamp) > max_age_seconds:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
# Generate expected signature with same timestamp
|
|
121
|
+
expected = generate_signature(
|
|
122
|
+
tenant_id,
|
|
123
|
+
tenant_secret,
|
|
124
|
+
params,
|
|
125
|
+
for_query_string=for_query_string,
|
|
126
|
+
timestamp=timestamp,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Compare signatures (timing-safe comparison)
|
|
130
|
+
return hmac.compare_digest(expected, signature_header)
|
|
131
|
+
|
|
132
|
+
except (ValueError, TypeError):
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_signature_header(
|
|
137
|
+
tenant_id: str,
|
|
138
|
+
tenant_secret: str,
|
|
139
|
+
params: Dict[str, Any],
|
|
140
|
+
for_query_string: bool = False,
|
|
141
|
+
) -> Dict[str, str]:
|
|
142
|
+
"""
|
|
143
|
+
Generate headers dict with Assistant Runtime signature for requests.
|
|
144
|
+
|
|
145
|
+
Convenience function that returns a dict ready to merge with other headers.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
tenant_id: Unique tenant identifier
|
|
149
|
+
tenant_secret: HMAC secret
|
|
150
|
+
params: Request parameters
|
|
151
|
+
for_query_string: True for GET requests, False for POST JSON
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Dict with X-AR-Signature header
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
>>> headers = get_signature_header("tenant", "secret", {"msg": "hi"})
|
|
158
|
+
>>> requests.get(url, params=params, headers={**headers, **other_headers})
|
|
159
|
+
"""
|
|
160
|
+
signature = generate_signature(tenant_id, tenant_secret, params, for_query_string)
|
|
161
|
+
return {"X-AR-Signature": signature}
|