dominus-sdk-python 2.8.0__tar.gz → 2.8.1__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.8.0 → dominus_sdk_python-2.8.1}/PKG-INFO +1 -1
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/config/endpoints.py +27 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/core.py +63 -33
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/PKG-INFO +1 -1
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/pyproject.toml +1 -1
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/README.md +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/errors.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/open.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/audio_capture.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/session.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/types.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/vad_gate.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/start.py +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/setup.cfg +0 -0
|
@@ -30,6 +30,33 @@ ARCHITECT_URL = BASE_URL
|
|
|
30
30
|
ORCHESTRATOR_URL = BASE_URL
|
|
31
31
|
WARDEN_URL = BASE_URL
|
|
32
32
|
|
|
33
|
+
# Proxy configuration (optional)
|
|
34
|
+
# DOMINUS_* variants take precedence over standard HTTP_PROXY/HTTPS_PROXY
|
|
35
|
+
HTTP_PROXY = os.environ.get("DOMINUS_HTTP_PROXY") or os.environ.get("HTTP_PROXY")
|
|
36
|
+
HTTPS_PROXY = os.environ.get("DOMINUS_HTTPS_PROXY") or os.environ.get("HTTPS_PROXY")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_proxy_config() -> dict | None:
|
|
40
|
+
"""
|
|
41
|
+
Get proxy configuration for httpx clients.
|
|
42
|
+
|
|
43
|
+
Returns a dict suitable for httpx's `proxies` parameter, or None if no proxy is configured.
|
|
44
|
+
|
|
45
|
+
Environment variables (in order of precedence):
|
|
46
|
+
- DOMINUS_HTTP_PROXY / DOMINUS_HTTPS_PROXY (SDK-specific)
|
|
47
|
+
- HTTP_PROXY / HTTPS_PROXY (standard)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dict mapping protocol to proxy URL, or None if no proxies configured.
|
|
51
|
+
Example: {"http://": "http://proxy:8080", "https://": "http://proxy:8080"}
|
|
52
|
+
"""
|
|
53
|
+
proxies = {}
|
|
54
|
+
if HTTP_PROXY:
|
|
55
|
+
proxies["http://"] = HTTP_PROXY
|
|
56
|
+
if HTTPS_PROXY:
|
|
57
|
+
proxies["https://"] = HTTPS_PROXY
|
|
58
|
+
return proxies if proxies else None
|
|
59
|
+
|
|
33
60
|
|
|
34
61
|
def get_gateway_url() -> str:
|
|
35
62
|
"""
|
|
@@ -16,6 +16,10 @@ from .cache import dominus_cache, sovereign_circuit_breaker, exponential_backoff
|
|
|
16
16
|
# Max retries for HTTP requests (reduced from implicit to explicit)
|
|
17
17
|
MAX_RETRIES = 3
|
|
18
18
|
|
|
19
|
+
# Mutex for JWT refresh to prevent race conditions
|
|
20
|
+
# When multiple concurrent coroutines need a new JWT, only one will mint
|
|
21
|
+
_jwt_refresh_lock = asyncio.Lock()
|
|
22
|
+
|
|
19
23
|
# Type alias
|
|
20
24
|
DominusResponse = dict[str, Any]
|
|
21
25
|
|
|
@@ -103,11 +107,12 @@ async def _fetch_jwks() -> dict:
|
|
|
103
107
|
if _cached_jwks and time.time() - _jwks_cache_time < _JWKS_CACHE_TTL:
|
|
104
108
|
return _cached_jwks
|
|
105
109
|
|
|
106
|
-
from ..config.endpoints import get_gateway_url
|
|
110
|
+
from ..config.endpoints import get_gateway_url, get_proxy_config
|
|
107
111
|
gateway_url = get_gateway_url()
|
|
112
|
+
proxy_config = get_proxy_config()
|
|
108
113
|
|
|
109
114
|
try:
|
|
110
|
-
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
115
|
+
async with httpx.AsyncClient(timeout=10.0, proxies=proxy_config) as client:
|
|
111
116
|
response = await client.get(f"{gateway_url}/jwt/jwks")
|
|
112
117
|
response.raise_for_status()
|
|
113
118
|
|
|
@@ -329,8 +334,9 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
329
334
|
Raises:
|
|
330
335
|
RuntimeError: If circuit is open or auth fails after retries
|
|
331
336
|
"""
|
|
332
|
-
from ..config.endpoints import get_gateway_url
|
|
337
|
+
from ..config.endpoints import get_gateway_url, get_proxy_config
|
|
333
338
|
gateway_url = get_gateway_url()
|
|
339
|
+
proxy_config = get_proxy_config()
|
|
334
340
|
|
|
335
341
|
# Circuit breaker check
|
|
336
342
|
if not sovereign_circuit_breaker.can_execute():
|
|
@@ -357,7 +363,7 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
357
363
|
|
|
358
364
|
for attempt in range(JWT_MINT_RETRIES):
|
|
359
365
|
try:
|
|
360
|
-
async with httpx.AsyncClient(base_url=gateway_url, headers=headers, timeout=30.0) as client:
|
|
366
|
+
async with httpx.AsyncClient(base_url=gateway_url, headers=headers, timeout=30.0, proxies=proxy_config) as client:
|
|
361
367
|
response = await client.post("/jwt/mint", content=body_b64)
|
|
362
368
|
|
|
363
369
|
# Check for retryable status codes before raise_for_status
|
|
@@ -455,43 +461,56 @@ def _get_architect_url(psk_token: str = None, sovereign_url: str = None, environ
|
|
|
455
461
|
async def _ensure_valid_jwt(psk_token: str, sovereign_url: str) -> str:
|
|
456
462
|
"""
|
|
457
463
|
Ensure we have a valid JWT, fetching and caching if needed.
|
|
458
|
-
|
|
464
|
+
|
|
465
|
+
Thread-safe via asyncio.Lock to prevent duplicate mints when
|
|
466
|
+
multiple concurrent coroutines need a new JWT.
|
|
467
|
+
|
|
459
468
|
Cache key: "jwt:self:service"
|
|
460
469
|
Cache TTL: 14 minutes (JWT lifetime is 15 minutes)
|
|
461
470
|
Refresh when <60 seconds remain.
|
|
462
|
-
|
|
471
|
+
|
|
463
472
|
Args:
|
|
464
473
|
psk_token: PSK token (DOMINUS_TOKEN)
|
|
465
474
|
sovereign_url: Sovereign base URL
|
|
466
|
-
|
|
475
|
+
|
|
467
476
|
Returns:
|
|
468
477
|
Valid JWT token string
|
|
469
478
|
"""
|
|
470
479
|
cache_key = "jwt:self:service"
|
|
471
|
-
|
|
472
|
-
#
|
|
480
|
+
|
|
481
|
+
# Fast path: check cache without lock
|
|
473
482
|
cached_jwt = dominus_cache.get(cache_key)
|
|
474
483
|
if cached_jwt:
|
|
475
484
|
try:
|
|
476
|
-
# Decode payload to check expiry
|
|
477
485
|
payload = _decode_jwt_payload(cached_jwt)
|
|
478
486
|
exp = payload.get("exp", 0)
|
|
479
487
|
current_time = int(time.time())
|
|
480
|
-
|
|
481
|
-
# Refresh if <60 seconds remain
|
|
482
488
|
if exp - current_time > 60:
|
|
483
489
|
return cached_jwt
|
|
484
490
|
except Exception:
|
|
485
|
-
# If decode fails, fetch new JWT
|
|
486
491
|
pass
|
|
487
|
-
|
|
488
|
-
#
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
492
|
+
|
|
493
|
+
# Slow path: acquire lock and refresh
|
|
494
|
+
async with _jwt_refresh_lock:
|
|
495
|
+
# Double-check cache (another coroutine may have refreshed while we waited)
|
|
496
|
+
cached_jwt = dominus_cache.get(cache_key)
|
|
497
|
+
if cached_jwt:
|
|
498
|
+
try:
|
|
499
|
+
payload = _decode_jwt_payload(cached_jwt)
|
|
500
|
+
exp = payload.get("exp", 0)
|
|
501
|
+
current_time = int(time.time())
|
|
502
|
+
if exp - current_time > 60:
|
|
503
|
+
return cached_jwt
|
|
504
|
+
except Exception:
|
|
505
|
+
pass
|
|
506
|
+
|
|
507
|
+
# Fetch new JWT
|
|
508
|
+
jwt = await _get_service_jwt(psk_token, sovereign_url)
|
|
509
|
+
|
|
510
|
+
# Cache for 14 minutes (840 seconds)
|
|
511
|
+
dominus_cache.set(cache_key, jwt, ttl=840)
|
|
512
|
+
|
|
513
|
+
return jwt
|
|
495
514
|
|
|
496
515
|
|
|
497
516
|
def verify_token_format(token: str) -> bool:
|
|
@@ -524,12 +543,16 @@ async def health_check_all(base_url: str) -> dict:
|
|
|
524
543
|
Returns:
|
|
525
544
|
Health status dict with service results
|
|
526
545
|
"""
|
|
546
|
+
from ..config.endpoints import get_proxy_config
|
|
547
|
+
proxy_config = get_proxy_config()
|
|
548
|
+
|
|
527
549
|
results = {}
|
|
528
550
|
|
|
529
551
|
# Check orchestrator via /api/health
|
|
552
|
+
# Timeout: 15s to handle cold starts on Cloud Run
|
|
530
553
|
try:
|
|
531
554
|
start = time.time()
|
|
532
|
-
async with httpx.AsyncClient(base_url=base_url, timeout=
|
|
555
|
+
async with httpx.AsyncClient(base_url=base_url, timeout=15.0, proxies=proxy_config) as client:
|
|
533
556
|
response = await client.get("/api/health")
|
|
534
557
|
response.raise_for_status()
|
|
535
558
|
|
|
@@ -587,32 +610,35 @@ async def execute_with_retry(
|
|
|
587
610
|
) -> DominusResponse:
|
|
588
611
|
"""
|
|
589
612
|
Execute HTTP request with retry logic.
|
|
590
|
-
|
|
613
|
+
|
|
591
614
|
Args:
|
|
592
615
|
route_info: (method, path, requires_auth, cacheable)
|
|
593
616
|
base_url: Base URL for API
|
|
594
617
|
token: Auth token (if needed)
|
|
595
618
|
kwargs: Request parameters
|
|
596
|
-
|
|
619
|
+
|
|
597
620
|
Returns:
|
|
598
621
|
Response dict
|
|
599
622
|
"""
|
|
623
|
+
from ..config.endpoints import get_proxy_config
|
|
624
|
+
proxy_config = get_proxy_config()
|
|
625
|
+
|
|
600
626
|
method, path, requires_auth, cacheable = route_info
|
|
601
|
-
|
|
627
|
+
|
|
602
628
|
# Check cache first
|
|
603
629
|
if cacheable and kwargs:
|
|
604
630
|
cache_key = f"{path}:{str(sorted(kwargs.items()))}"
|
|
605
631
|
cached = dominus_cache.get(cache_key)
|
|
606
632
|
if cached:
|
|
607
633
|
return cached
|
|
608
|
-
|
|
634
|
+
|
|
609
635
|
# Validate token
|
|
610
636
|
if requires_auth and not token:
|
|
611
637
|
raise RuntimeError(
|
|
612
638
|
"DOMINUS_TOKEN not set. "
|
|
613
639
|
"Set the environment variable: export DOMINUS_TOKEN=your_token"
|
|
614
640
|
)
|
|
615
|
-
|
|
641
|
+
|
|
616
642
|
# Retry loop with exponential backoff and jitter
|
|
617
643
|
for attempt in range(MAX_RETRIES):
|
|
618
644
|
try:
|
|
@@ -620,7 +646,7 @@ async def execute_with_retry(
|
|
|
620
646
|
headers = {}
|
|
621
647
|
if requires_auth:
|
|
622
648
|
headers["Authorization"] = f"Bearer {_b64_token(token)}"
|
|
623
|
-
|
|
649
|
+
|
|
624
650
|
# Prepare body (encode if auth required and kwargs provided)
|
|
625
651
|
# For auth routes: send raw base64 string as text/plain (matches middleware expectation)
|
|
626
652
|
# For non-auth routes: send JSON as normal
|
|
@@ -630,9 +656,9 @@ async def execute_with_retry(
|
|
|
630
656
|
body = body_b64
|
|
631
657
|
else:
|
|
632
658
|
body = kwargs if kwargs else {}
|
|
633
|
-
|
|
659
|
+
|
|
634
660
|
# Make request
|
|
635
|
-
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0) as client:
|
|
661
|
+
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0, proxies=proxy_config) as client:
|
|
636
662
|
if method == "GET":
|
|
637
663
|
response = await client.get(path, params=kwargs if kwargs else None)
|
|
638
664
|
else:
|
|
@@ -720,6 +746,9 @@ async def execute_bridge_call(
|
|
|
720
746
|
Returns:
|
|
721
747
|
Response data (the "data" field from successful response)
|
|
722
748
|
"""
|
|
749
|
+
from ..config.endpoints import get_proxy_config
|
|
750
|
+
proxy_config = get_proxy_config()
|
|
751
|
+
|
|
723
752
|
# Check cache first
|
|
724
753
|
if cacheable and params:
|
|
725
754
|
cache_key = f"bridge:{method}:{str(sorted(params.items()))}"
|
|
@@ -749,7 +778,7 @@ async def execute_bridge_call(
|
|
|
749
778
|
# Retry loop with exponential backoff and jitter
|
|
750
779
|
for attempt in range(MAX_RETRIES):
|
|
751
780
|
try:
|
|
752
|
-
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0) as client:
|
|
781
|
+
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0, proxies=proxy_config) as client:
|
|
753
782
|
response = await client.post(endpoint, content=body_b64)
|
|
754
783
|
|
|
755
784
|
response.raise_for_status()
|
|
@@ -878,8 +907,9 @@ async def _execute_auth_call(
|
|
|
878
907
|
Returns:
|
|
879
908
|
Response data
|
|
880
909
|
"""
|
|
881
|
-
from ..config.endpoints import get_gateway_url
|
|
910
|
+
from ..config.endpoints import get_gateway_url, get_proxy_config
|
|
882
911
|
gateway_url = get_gateway_url()
|
|
912
|
+
proxy_config = get_proxy_config()
|
|
883
913
|
|
|
884
914
|
headers = {
|
|
885
915
|
"Content-Type": "text/plain"
|
|
@@ -901,7 +931,7 @@ async def _execute_auth_call(
|
|
|
901
931
|
else:
|
|
902
932
|
endpoint = "/jwt/mint"
|
|
903
933
|
|
|
904
|
-
async with httpx.AsyncClient(base_url=gateway_url, headers=headers, timeout=30.0) as client:
|
|
934
|
+
async with httpx.AsyncClient(base_url=gateway_url, headers=headers, timeout=30.0, proxies=proxy_config) as client:
|
|
905
935
|
# JWKS uses GET (public endpoint), other auth endpoints use POST
|
|
906
936
|
if method == "auth.jwks":
|
|
907
937
|
response = await client.get(endpoint)
|
|
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
|
|
File without changes
|
{dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus/namespaces/oracle/audio_capture.py
RENAMED
|
File without changes
|
{dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/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.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-2.8.0 → dominus_sdk_python-2.8.1}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|