dominus-sdk-python 2.7.1__tar.gz → 2.8.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.
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/PKG-INFO +1 -1
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/__init__.py +10 -1
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/core.py +255 -2
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/ai.py +61 -43
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/PKG-INFO +1 -1
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/pyproject.toml +1 -1
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/README.md +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/errors.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/open.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/__init__.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/audio_capture.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/session.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/types.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/vad_gate.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/start.py +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/setup.cfg +0 -0
|
@@ -126,6 +126,12 @@ from .helpers.cache import (
|
|
|
126
126
|
exponential_backoff_with_jitter,
|
|
127
127
|
)
|
|
128
128
|
|
|
129
|
+
# Export JWT verification utilities
|
|
130
|
+
from .helpers.core import (
|
|
131
|
+
verify_jwt_locally,
|
|
132
|
+
is_jwt_valid,
|
|
133
|
+
)
|
|
134
|
+
|
|
129
135
|
# Export error classes
|
|
130
136
|
from .errors import (
|
|
131
137
|
DominusError,
|
|
@@ -140,7 +146,7 @@ from .errors import (
|
|
|
140
146
|
TimeoutError as DominusTimeoutError,
|
|
141
147
|
)
|
|
142
148
|
|
|
143
|
-
__version__ = "2.
|
|
149
|
+
__version__ = "2.8.0"
|
|
144
150
|
__all__ = [
|
|
145
151
|
# Main SDK instance
|
|
146
152
|
"dominus",
|
|
@@ -186,6 +192,9 @@ __all__ = [
|
|
|
186
192
|
"DominusCache",
|
|
187
193
|
"orchestrator_circuit_breaker",
|
|
188
194
|
"exponential_backoff_with_jitter",
|
|
195
|
+
# JWT verification utilities
|
|
196
|
+
"verify_jwt_locally",
|
|
197
|
+
"is_jwt_valid",
|
|
189
198
|
# Error classes
|
|
190
199
|
"DominusError",
|
|
191
200
|
"AuthenticationError",
|
|
@@ -41,10 +41,10 @@ def _b64url_decode(s: str) -> bytes:
|
|
|
41
41
|
def _decode_jwt_payload(jwt: str) -> dict:
|
|
42
42
|
"""
|
|
43
43
|
Decode JWT payload without verification (for exp checks only).
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
Args:
|
|
46
46
|
jwt: JWT token string
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
Returns:
|
|
49
49
|
Decoded payload dict
|
|
50
50
|
"""
|
|
@@ -59,6 +59,259 @@ def _decode_jwt_payload(jwt: str) -> dict:
|
|
|
59
59
|
raise ValueError(f"Failed to decode JWT payload: {e}")
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def _decode_jwt_header(jwt: str) -> dict:
|
|
63
|
+
"""
|
|
64
|
+
Decode JWT header without verification.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
jwt: JWT token string
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Decoded header dict (contains alg, kid, typ)
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
parts = jwt.split('.')
|
|
74
|
+
if len(parts) != 3:
|
|
75
|
+
raise ValueError("Invalid JWT format")
|
|
76
|
+
header_b64url = parts[0]
|
|
77
|
+
header_bytes = _b64url_decode(header_b64url)
|
|
78
|
+
return json.loads(header_bytes.decode('utf-8'))
|
|
79
|
+
except Exception as e:
|
|
80
|
+
raise ValueError(f"Failed to decode JWT header: {e}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ============================================
|
|
84
|
+
# JWKS and Local JWT Verification
|
|
85
|
+
# ============================================
|
|
86
|
+
|
|
87
|
+
# JWKS cache
|
|
88
|
+
_cached_jwks: Optional[dict] = None
|
|
89
|
+
_jwks_cache_time: float = 0
|
|
90
|
+
_JWKS_CACHE_TTL = 3600 # 1 hour in seconds
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def _fetch_jwks() -> dict:
|
|
94
|
+
"""
|
|
95
|
+
Fetch JWKS from gateway.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
JWKS dict with 'keys' array
|
|
99
|
+
"""
|
|
100
|
+
global _cached_jwks, _jwks_cache_time
|
|
101
|
+
|
|
102
|
+
# Check cache first
|
|
103
|
+
if _cached_jwks and time.time() - _jwks_cache_time < _JWKS_CACHE_TTL:
|
|
104
|
+
return _cached_jwks
|
|
105
|
+
|
|
106
|
+
from ..config.endpoints import get_gateway_url
|
|
107
|
+
gateway_url = get_gateway_url()
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
111
|
+
response = await client.get(f"{gateway_url}/jwt/jwks")
|
|
112
|
+
response.raise_for_status()
|
|
113
|
+
|
|
114
|
+
# Response is base64 encoded
|
|
115
|
+
result = _b64_decode(response.text)
|
|
116
|
+
|
|
117
|
+
# Handle wrapped response or direct JWKS
|
|
118
|
+
if isinstance(result, dict):
|
|
119
|
+
if result.get("data", {}).get("keys"):
|
|
120
|
+
jwks = result["data"]
|
|
121
|
+
elif result.get("keys"):
|
|
122
|
+
jwks = result
|
|
123
|
+
else:
|
|
124
|
+
raise ValueError("Invalid JWKS response format")
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError("Invalid JWKS response format")
|
|
127
|
+
|
|
128
|
+
_cached_jwks = jwks
|
|
129
|
+
_jwks_cache_time = time.time()
|
|
130
|
+
return jwks
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
# If fetch fails but we have cached JWKS, use it (stale is better than nothing)
|
|
134
|
+
if _cached_jwks:
|
|
135
|
+
return _cached_jwks
|
|
136
|
+
raise RuntimeError(f"Failed to fetch JWKS: {e}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _jwk_to_rsa_public_key(jwk: dict):
|
|
140
|
+
"""
|
|
141
|
+
Convert JWK to RSA public key for verification.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
jwk: JWK dict with n, e components
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
RSA public key object
|
|
148
|
+
"""
|
|
149
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
150
|
+
from cryptography.hazmat.backends import default_backend
|
|
151
|
+
|
|
152
|
+
# Decode n and e from base64url
|
|
153
|
+
n_bytes = _b64url_decode(jwk["n"])
|
|
154
|
+
e_bytes = _b64url_decode(jwk["e"])
|
|
155
|
+
|
|
156
|
+
# Convert to integers
|
|
157
|
+
n = int.from_bytes(n_bytes, byteorder='big')
|
|
158
|
+
e = int.from_bytes(e_bytes, byteorder='big')
|
|
159
|
+
|
|
160
|
+
# Create RSA public numbers and derive public key
|
|
161
|
+
public_numbers = rsa.RSAPublicNumbers(e, n)
|
|
162
|
+
return public_numbers.public_key(default_backend())
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
async def verify_jwt_signature(jwt_token: str) -> dict:
|
|
166
|
+
"""
|
|
167
|
+
Verify JWT signature locally using JWKS.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
jwt_token: The JWT token to verify
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The decoded payload if valid
|
|
174
|
+
|
|
175
|
+
Raises:
|
|
176
|
+
RuntimeError: If verification fails
|
|
177
|
+
"""
|
|
178
|
+
global _cached_jwks
|
|
179
|
+
|
|
180
|
+
from cryptography.hazmat.primitives import hashes
|
|
181
|
+
from cryptography.hazmat.primitives.asymmetric import padding
|
|
182
|
+
from cryptography.exceptions import InvalidSignature
|
|
183
|
+
|
|
184
|
+
parts = jwt_token.split('.')
|
|
185
|
+
if len(parts) != 3:
|
|
186
|
+
raise RuntimeError("Invalid JWT format")
|
|
187
|
+
|
|
188
|
+
header_b64, payload_b64, signature_b64 = parts
|
|
189
|
+
|
|
190
|
+
# Decode header to get kid
|
|
191
|
+
header = _decode_jwt_header(jwt_token)
|
|
192
|
+
|
|
193
|
+
# Verify algorithm
|
|
194
|
+
if header.get("alg") != "RS256":
|
|
195
|
+
raise RuntimeError(f"Unsupported JWT algorithm: {header.get('alg')}")
|
|
196
|
+
|
|
197
|
+
# Fetch JWKS and find the key
|
|
198
|
+
jwks = await _fetch_jwks()
|
|
199
|
+
kid = header.get("kid")
|
|
200
|
+
|
|
201
|
+
key = None
|
|
202
|
+
if kid:
|
|
203
|
+
for k in jwks.get("keys", []):
|
|
204
|
+
if k.get("kid") == kid:
|
|
205
|
+
key = k
|
|
206
|
+
break
|
|
207
|
+
else:
|
|
208
|
+
# Use first key if no kid
|
|
209
|
+
keys = jwks.get("keys", [])
|
|
210
|
+
if keys:
|
|
211
|
+
key = keys[0]
|
|
212
|
+
|
|
213
|
+
if not key:
|
|
214
|
+
# Key not found - refresh JWKS and try again
|
|
215
|
+
_cached_jwks = None
|
|
216
|
+
jwks = await _fetch_jwks()
|
|
217
|
+
|
|
218
|
+
if kid:
|
|
219
|
+
for k in jwks.get("keys", []):
|
|
220
|
+
if k.get("kid") == kid:
|
|
221
|
+
key = k
|
|
222
|
+
break
|
|
223
|
+
else:
|
|
224
|
+
keys = jwks.get("keys", [])
|
|
225
|
+
if keys:
|
|
226
|
+
key = keys[0]
|
|
227
|
+
|
|
228
|
+
if not key:
|
|
229
|
+
raise RuntimeError(f"JWT signing key not found: {kid}")
|
|
230
|
+
|
|
231
|
+
# Import the public key
|
|
232
|
+
public_key = _jwk_to_rsa_public_key(key)
|
|
233
|
+
|
|
234
|
+
# Verify signature
|
|
235
|
+
signature_input = f"{header_b64}.{payload_b64}".encode('utf-8')
|
|
236
|
+
signature = _b64url_decode(signature_b64)
|
|
237
|
+
|
|
238
|
+
try:
|
|
239
|
+
public_key.verify(
|
|
240
|
+
signature,
|
|
241
|
+
signature_input,
|
|
242
|
+
padding.PKCS1v15(),
|
|
243
|
+
hashes.SHA256()
|
|
244
|
+
)
|
|
245
|
+
except InvalidSignature:
|
|
246
|
+
raise RuntimeError("JWT signature verification failed")
|
|
247
|
+
|
|
248
|
+
# Decode and return payload
|
|
249
|
+
return _decode_jwt_payload(jwt_token)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
async def verify_jwt_locally(jwt_token: str) -> dict:
|
|
253
|
+
"""
|
|
254
|
+
Verify JWT locally: signature + claims.
|
|
255
|
+
|
|
256
|
+
Performs full local verification:
|
|
257
|
+
1. Signature verification using JWKS
|
|
258
|
+
2. Expiry check (exp claim)
|
|
259
|
+
3. Not-before check (nbf claim if present)
|
|
260
|
+
4. Issued-at sanity check (iat claim)
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
jwt_token: The JWT token to verify
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
The decoded payload if valid
|
|
267
|
+
|
|
268
|
+
Raises:
|
|
269
|
+
RuntimeError: If verification fails
|
|
270
|
+
"""
|
|
271
|
+
# Verify signature
|
|
272
|
+
payload = await verify_jwt_signature(jwt_token)
|
|
273
|
+
|
|
274
|
+
now = int(time.time())
|
|
275
|
+
|
|
276
|
+
# Check expiry
|
|
277
|
+
if payload.get("exp") and payload["exp"] < now:
|
|
278
|
+
raise RuntimeError("JWT has expired")
|
|
279
|
+
|
|
280
|
+
# Check not-before
|
|
281
|
+
if payload.get("nbf") and payload["nbf"] > now:
|
|
282
|
+
raise RuntimeError("JWT not yet valid")
|
|
283
|
+
|
|
284
|
+
# Sanity check issued-at (not more than 24 hours in the future)
|
|
285
|
+
if payload.get("iat") and payload["iat"] > now + 86400:
|
|
286
|
+
raise RuntimeError("JWT issued-at is in the future")
|
|
287
|
+
|
|
288
|
+
return payload
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def is_jwt_valid(jwt_token: str) -> bool:
|
|
292
|
+
"""
|
|
293
|
+
Quick local JWT validation (expiry only, no signature verification).
|
|
294
|
+
Use this for fast checks before making API calls.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
jwt_token: The JWT token to check
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
True if token appears valid (not expired), False otherwise
|
|
301
|
+
"""
|
|
302
|
+
try:
|
|
303
|
+
payload = _decode_jwt_payload(jwt_token)
|
|
304
|
+
now = int(time.time())
|
|
305
|
+
|
|
306
|
+
# Check if expired (with 60 second buffer)
|
|
307
|
+
if payload.get("exp") and payload["exp"] < now - 60:
|
|
308
|
+
return False
|
|
309
|
+
|
|
310
|
+
return True
|
|
311
|
+
except Exception:
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
|
|
62
315
|
async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
63
316
|
"""
|
|
64
317
|
Get service JWT by calling gateway /jwt/mint with PSK.
|
|
@@ -211,7 +211,11 @@ class RagSubNamespace(GatewayMixin):
|
|
|
211
211
|
slug: str,
|
|
212
212
|
identifier: str,
|
|
213
213
|
content: str,
|
|
214
|
+
name: Optional[str] = None,
|
|
215
|
+
description: Optional[str] = None,
|
|
214
216
|
category: Optional[str] = None,
|
|
217
|
+
subcategory: Optional[str] = None,
|
|
218
|
+
source_reference: Optional[Dict[str, Any]] = None,
|
|
215
219
|
metadata: Optional[Dict[str, Any]] = None
|
|
216
220
|
) -> Dict[str, Any]:
|
|
217
221
|
"""
|
|
@@ -220,13 +224,25 @@ class RagSubNamespace(GatewayMixin):
|
|
|
220
224
|
Args:
|
|
221
225
|
slug: Corpus identifier
|
|
222
226
|
identifier: Entry identifier (unique within corpus)
|
|
223
|
-
content: Text content to embed
|
|
227
|
+
content: Text content to embed (stored as content_markdown)
|
|
228
|
+
name: Optional human-readable name
|
|
229
|
+
description: Optional brief description
|
|
224
230
|
category: Optional category for filtering
|
|
231
|
+
subcategory: Optional subcategory
|
|
232
|
+
source_reference: Optional source attribution
|
|
225
233
|
metadata: Optional metadata dict
|
|
226
234
|
"""
|
|
227
|
-
body: Dict[str, Any] = {"
|
|
235
|
+
body: Dict[str, Any] = {"content_markdown": content}
|
|
236
|
+
if name:
|
|
237
|
+
body["name"] = name
|
|
238
|
+
if description:
|
|
239
|
+
body["description"] = description
|
|
228
240
|
if category:
|
|
229
241
|
body["category"] = category
|
|
242
|
+
if subcategory:
|
|
243
|
+
body["subcategory"] = subcategory
|
|
244
|
+
if source_reference:
|
|
245
|
+
body["source_reference"] = source_reference
|
|
230
246
|
if metadata:
|
|
231
247
|
body["metadata"] = metadata
|
|
232
248
|
|
|
@@ -260,11 +276,30 @@ class RagSubNamespace(GatewayMixin):
|
|
|
260
276
|
|
|
261
277
|
Args:
|
|
262
278
|
slug: Corpus identifier
|
|
263
|
-
entries: List of
|
|
279
|
+
entries: List of entry dicts with:
|
|
280
|
+
- identifier: string (required)
|
|
281
|
+
- content: string (required) - Will be sent as content_markdown
|
|
282
|
+
- name: string (optional)
|
|
283
|
+
- description: string (optional)
|
|
284
|
+
- category: string (optional)
|
|
285
|
+
- subcategory: string (optional)
|
|
286
|
+
- source_reference: dict (optional)
|
|
287
|
+
- metadata: dict (optional)
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Dict with "processed", "failed", "errors" counts
|
|
264
291
|
"""
|
|
292
|
+
# Transform 'content' to 'content_markdown' for API compatibility
|
|
293
|
+
transformed = []
|
|
294
|
+
for entry in entries:
|
|
295
|
+
e = dict(entry)
|
|
296
|
+
if "content" in e and "content_markdown" not in e:
|
|
297
|
+
e["content_markdown"] = e.pop("content")
|
|
298
|
+
transformed.append(e)
|
|
299
|
+
|
|
265
300
|
return await self._api(
|
|
266
301
|
endpoint=f"/api/rag/{slug}/bulk",
|
|
267
|
-
body={"entries":
|
|
302
|
+
body={"entries": transformed}
|
|
268
303
|
)
|
|
269
304
|
|
|
270
305
|
async def search(
|
|
@@ -390,16 +425,12 @@ class ArtifactsSubNamespace(GatewayMixin):
|
|
|
390
425
|
async def get(
|
|
391
426
|
self,
|
|
392
427
|
artifact_id: str,
|
|
393
|
-
conversation_id:
|
|
428
|
+
conversation_id: str
|
|
394
429
|
) -> Dict[str, Any]:
|
|
395
430
|
"""Get artifact by ID."""
|
|
396
|
-
body: Dict[str, Any] = {"artifact_id": artifact_id}
|
|
397
|
-
if conversation_id:
|
|
398
|
-
body["conversation_id"] = conversation_id
|
|
399
|
-
|
|
400
431
|
return await self._api(
|
|
401
|
-
endpoint=f"/api/agent/artifacts/{artifact_id}",
|
|
402
|
-
|
|
432
|
+
endpoint=f"/api/agent/artifacts/{artifact_id}?conversation_id={conversation_id}",
|
|
433
|
+
method="GET"
|
|
403
434
|
)
|
|
404
435
|
|
|
405
436
|
async def create(
|
|
@@ -407,27 +438,28 @@ class ArtifactsSubNamespace(GatewayMixin):
|
|
|
407
438
|
name: str,
|
|
408
439
|
content: str,
|
|
409
440
|
conversation_id: str,
|
|
410
|
-
artifact_type: str = "text",
|
|
441
|
+
artifact_type: str = "text/plain",
|
|
442
|
+
is_base64: bool = False,
|
|
411
443
|
metadata: Optional[Dict[str, Any]] = None
|
|
412
444
|
) -> Dict[str, Any]:
|
|
413
445
|
"""
|
|
414
446
|
Create a new artifact.
|
|
415
447
|
|
|
416
448
|
Args:
|
|
417
|
-
name: Artifact
|
|
418
|
-
content: Artifact content (text or base64 for binary)
|
|
449
|
+
name: Artifact key/filename
|
|
450
|
+
content: Artifact content (text or base64-encoded for binary)
|
|
419
451
|
conversation_id: Associated conversation ID
|
|
420
|
-
artifact_type:
|
|
421
|
-
|
|
452
|
+
artifact_type: MIME content type (e.g., "text/plain", "text/html", "image/png")
|
|
453
|
+
is_base64: If True, content is base64-encoded binary
|
|
454
|
+
metadata: Optional metadata dict (not stored, for SDK use)
|
|
422
455
|
"""
|
|
423
456
|
body: Dict[str, Any] = {
|
|
424
|
-
"
|
|
457
|
+
"key": name,
|
|
425
458
|
"content": content,
|
|
426
459
|
"conversation_id": conversation_id,
|
|
427
|
-
"
|
|
460
|
+
"content_type": artifact_type,
|
|
461
|
+
"is_base64": is_base64
|
|
428
462
|
}
|
|
429
|
-
if metadata:
|
|
430
|
-
body["metadata"] = metadata
|
|
431
463
|
|
|
432
464
|
return await self._api(
|
|
433
465
|
endpoint="/api/agent/artifacts",
|
|
@@ -436,38 +468,24 @@ class ArtifactsSubNamespace(GatewayMixin):
|
|
|
436
468
|
|
|
437
469
|
async def list(
|
|
438
470
|
self,
|
|
439
|
-
conversation_id: str
|
|
440
|
-
limit: int = 100,
|
|
441
|
-
offset: int = 0
|
|
471
|
+
conversation_id: str
|
|
442
472
|
) -> List[Dict[str, Any]]:
|
|
443
473
|
"""List artifacts for a conversation."""
|
|
444
|
-
body: Dict[str, Any] = {
|
|
445
|
-
"conversation_id": conversation_id,
|
|
446
|
-
"limit": limit,
|
|
447
|
-
"offset": offset
|
|
448
|
-
}
|
|
449
|
-
|
|
450
474
|
result = await self._api(
|
|
451
|
-
endpoint="/api/agent/artifacts",
|
|
452
|
-
method="GET"
|
|
453
|
-
body=body
|
|
475
|
+
endpoint=f"/api/agent/artifacts?conversation_id={conversation_id}",
|
|
476
|
+
method="GET"
|
|
454
477
|
)
|
|
455
478
|
return result.get("artifacts", result) if isinstance(result, dict) else result
|
|
456
479
|
|
|
457
480
|
async def delete(
|
|
458
481
|
self,
|
|
459
482
|
artifact_id: str,
|
|
460
|
-
conversation_id:
|
|
483
|
+
conversation_id: str
|
|
461
484
|
) -> Dict[str, Any]:
|
|
462
485
|
"""Delete an artifact."""
|
|
463
|
-
body: Dict[str, Any] = {}
|
|
464
|
-
if conversation_id:
|
|
465
|
-
body["conversation_id"] = conversation_id
|
|
466
|
-
|
|
467
486
|
return await self._api(
|
|
468
|
-
endpoint=f"/api/agent/artifacts/{artifact_id}",
|
|
469
|
-
method="DELETE"
|
|
470
|
-
body=body if body else None
|
|
487
|
+
endpoint=f"/api/agent/artifacts/{artifact_id}?conversation_id={conversation_id}",
|
|
488
|
+
method="DELETE"
|
|
471
489
|
)
|
|
472
490
|
|
|
473
491
|
|
|
@@ -900,10 +918,10 @@ class AiNamespace(GatewayMixin):
|
|
|
900
918
|
List of history messages
|
|
901
919
|
"""
|
|
902
920
|
result = await self._api(
|
|
903
|
-
endpoint=f"/api/agent/history/{conversation_id}",
|
|
904
|
-
|
|
921
|
+
endpoint=f"/api/agent/history/{conversation_id}?limit={limit}",
|
|
922
|
+
method="GET"
|
|
905
923
|
)
|
|
906
|
-
return result.get("
|
|
924
|
+
return result.get("messages", result) if isinstance(result, dict) else result
|
|
907
925
|
|
|
908
926
|
# ========================================
|
|
909
927
|
# LLM Completions
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/audio_capture.py
RENAMED
|
File without changes
|
{dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus/namespaces/oracle/oracle_websocket.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-2.7.1 → dominus_sdk_python-2.8.0}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|