dominus-sdk-python 9.0.10__tar.gz → 9.0.12__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-9.0.10 → dominus_sdk_python-9.0.12}/PKG-INFO +4 -4
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/README.md +3 -3
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/__init__.py +3 -1
- dominus_sdk_python-9.0.12/dominus/helpers/auth.py +57 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/core.py +18 -2
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/__init__.py +2 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/ai.py +30 -12
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/artifacts.py +12 -1
- dominus_sdk_python-9.0.12/dominus/namespaces/usage.py +214 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/start.py +4 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/PKG-INFO +4 -4
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/SOURCES.txt +5 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/pyproject.toml +1 -1
- dominus_sdk_python-9.0.12/tests/test_ai_namespace.py +75 -0
- dominus_sdk_python-9.0.12/tests/test_artifacts_namespace.py +93 -0
- dominus_sdk_python-9.0.12/tests/test_token_precedence.py +174 -0
- dominus_sdk_python-9.0.12/tests/test_usage_namespace.py +210 -0
- dominus_sdk_python-9.0.10/dominus/helpers/auth.py +0 -23
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/contract/__init__.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/contract/envelope.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/contract/projections.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/errors.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/authority.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/browser.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/coder.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/platform.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/publisher.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/recipes.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/stash.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/namespaces/workflow.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/py.typed +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/setup.cfg +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_auth.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_authority_public_vocabulary.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_browser_namespace.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_contract.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_control_plane_namespaces.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_error_recovery_action.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_errors.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_errors_aliases.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_health.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_logs.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_namespace_barrel_exports.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_platform_coder_namespaces.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_public_exports.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_publisher_namespace.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_readme_quickstart_catalog.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_recipes_namespace.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_recipes_stash_routing.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_secrets_presence_contract.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_stash_artifact_facade.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_stash_tables_namespace.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_transport_compat.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_version_consistency.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_workflow_lifecycle.py +0 -0
- {dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/tests/test_workflow_refs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.12
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -38,8 +38,8 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
38
38
|
## Agent Guide
|
|
39
39
|
|
|
40
40
|
Start with `docs/agent-guide/INDEX.md`. The current snapshot is
|
|
41
|
-
`docs/agent-guide/2026-09-
|
|
42
|
-
cleanup audit is `docs/janitor/2026-09-
|
|
41
|
+
`docs/agent-guide/2026-09-14-1300-sdk-python/00-reading-order.md`; the latest
|
|
42
|
+
cleanup audit is `docs/janitor/2026-09-14-1300-sdk-python-cleanup-audit.md`.
|
|
43
43
|
|
|
44
44
|
## What This Repo Ships
|
|
45
45
|
|
|
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-09-10-1002-sdk-python-cleanup-audit.md`.
|
|
|
48
48
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
49
49
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
50
50
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
51
|
-
- Current package version: `9.0.
|
|
51
|
+
- Current package version: `9.0.12`
|
|
52
52
|
|
|
53
53
|
## Install
|
|
54
54
|
|
|
@@ -5,8 +5,8 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
5
5
|
## Agent Guide
|
|
6
6
|
|
|
7
7
|
Start with `docs/agent-guide/INDEX.md`. The current snapshot is
|
|
8
|
-
`docs/agent-guide/2026-09-
|
|
9
|
-
cleanup audit is `docs/janitor/2026-09-
|
|
8
|
+
`docs/agent-guide/2026-09-14-1300-sdk-python/00-reading-order.md`; the latest
|
|
9
|
+
cleanup audit is `docs/janitor/2026-09-14-1300-sdk-python-cleanup-audit.md`.
|
|
10
10
|
|
|
11
11
|
## What This Repo Ships
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ cleanup audit is `docs/janitor/2026-09-10-1002-sdk-python-cleanup-audit.md`.
|
|
|
15
15
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
16
16
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
17
17
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
18
|
-
- Current package version: `9.0.
|
|
18
|
+
- Current package version: `9.0.12`
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -121,6 +121,7 @@ from .namespaces.platform import PlatformNamespace
|
|
|
121
121
|
from .namespaces.coder import CoderNamespace
|
|
122
122
|
from .namespaces.publisher import PublisherNamespace
|
|
123
123
|
from .namespaces.stash import StashNamespace, StashTablesNamespace
|
|
124
|
+
from .namespaces.usage import UsageNamespace
|
|
124
125
|
|
|
125
126
|
# Export AI namespace for agent-runtime operations
|
|
126
127
|
from .namespaces.ai import (
|
|
@@ -190,7 +191,7 @@ from .errors import (
|
|
|
190
191
|
TimeoutError as DominusTimeoutError,
|
|
191
192
|
)
|
|
192
193
|
|
|
193
|
-
__version__ = "9.0.
|
|
194
|
+
__version__ = "9.0.12"
|
|
194
195
|
__all__ = [
|
|
195
196
|
# Main SDK instance
|
|
196
197
|
"dominus",
|
|
@@ -249,6 +250,7 @@ __all__ = [
|
|
|
249
250
|
"PublisherNamespace",
|
|
250
251
|
"StashNamespace",
|
|
251
252
|
"StashTablesNamespace",
|
|
253
|
+
"UsageNamespace",
|
|
252
254
|
# AI namespace for agent-runtime operations
|
|
253
255
|
"AiNamespace",
|
|
254
256
|
"RagSubNamespace",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Token resolution helper.
|
|
3
|
+
|
|
4
|
+
Handles resolution of authentication token from environment variables.
|
|
5
|
+
No hardcoded tokens supported - environment variable only.
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _resolve_token() -> Optional[str]:
|
|
12
|
+
"""
|
|
13
|
+
Resolve auth token from environment variable.
|
|
14
|
+
|
|
15
|
+
Precedence: DOMINUS_USER_TOKEN outranks DOMINUS_TOKEN. This is intentional
|
|
16
|
+
(the README quick start is a gateway user-token flow), but it means an
|
|
17
|
+
ambient operator token silently shadows a project PSK. Callers that intend
|
|
18
|
+
project-PSK auth must scrub DOMINUS_USER_TOKEN (and DOMINUS_MCP_USER_TOKEN)
|
|
19
|
+
from the environment before importing this package.
|
|
20
|
+
|
|
21
|
+
Note: When fetching from Infisical via Sovereign, the secret is stored as
|
|
22
|
+
PROVISION_DOMINUS_TOKEN but should be set as DOMINUS_TOKEN in environment.
|
|
23
|
+
The PROVISION_ prefix is dropped when setting environment variables.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Token string or None if not set
|
|
27
|
+
"""
|
|
28
|
+
return os.getenv("DOMINUS_USER_TOKEN") or os.getenv("DOMINUS_TOKEN")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _shadowed_token_hint(token: Optional[str]) -> Optional[str]:
|
|
32
|
+
"""
|
|
33
|
+
Diagnose a token that may have been shadowed by environment precedence.
|
|
34
|
+
|
|
35
|
+
Returns a message naming BOTH variables when, and only when, the ambiguity
|
|
36
|
+
is real: DOMINUS_USER_TOKEN and DOMINUS_TOKEN are both set AND ``token`` is
|
|
37
|
+
the shadowing value that precedence selected. A caller-supplied token that
|
|
38
|
+
is not the environment's chosen value was never ambiguous, so it returns
|
|
39
|
+
None, as does any environment where only one variable is set.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
token: The token an auth attempt actually used
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Diagnostic message, or None when there is nothing ambiguous to report
|
|
46
|
+
"""
|
|
47
|
+
user_token = os.getenv("DOMINUS_USER_TOKEN")
|
|
48
|
+
psk_token = os.getenv("DOMINUS_TOKEN")
|
|
49
|
+
if not user_token or not psk_token or token != user_token:
|
|
50
|
+
return None
|
|
51
|
+
return (
|
|
52
|
+
"DOMINUS_USER_TOKEN outranks DOMINUS_TOKEN (dominus/helpers/auth.py) and "
|
|
53
|
+
"both are set in this environment, so this attempt authenticated with "
|
|
54
|
+
"DOMINUS_USER_TOKEN. If you intended project-PSK auth, unset "
|
|
55
|
+
"DOMINUS_USER_TOKEN (and DOMINUS_MCP_USER_TOKEN) before importing the "
|
|
56
|
+
"SDK and retry."
|
|
57
|
+
)
|
|
@@ -11,6 +11,7 @@ import json
|
|
|
11
11
|
import time
|
|
12
12
|
from typing import Any, Optional
|
|
13
13
|
|
|
14
|
+
from .auth import _shadowed_token_hint
|
|
14
15
|
from .cache import dominus_cache, gateway_circuit_breaker, exponential_backoff_with_jitter
|
|
15
16
|
|
|
16
17
|
# Max retries for HTTP requests (reduced from implicit to explicit)
|
|
@@ -349,6 +350,13 @@ def is_jwt_valid(jwt_token: str) -> bool:
|
|
|
349
350
|
return False
|
|
350
351
|
|
|
351
352
|
|
|
353
|
+
def _mint_failure_message(message: str, shadow_hint: Optional[str]) -> str:
|
|
354
|
+
"""Append the token-shadowing diagnostic to a failed-mint message."""
|
|
355
|
+
if not shadow_hint:
|
|
356
|
+
return message
|
|
357
|
+
return f"{message}\n{shadow_hint}"
|
|
358
|
+
|
|
359
|
+
|
|
352
360
|
async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
353
361
|
"""
|
|
354
362
|
Get service JWT by calling gateway /jwt/mint with PSK.
|
|
@@ -392,6 +400,10 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
392
400
|
body_json = {"method": "auth.self", "params": {}}
|
|
393
401
|
body_b64 = _b64_encode(body_json)
|
|
394
402
|
|
|
403
|
+
# A rejected mint is indistinguishable from a bad PSK when an ambient
|
|
404
|
+
# DOMINUS_USER_TOKEN shadowed the intended project PSK at import.
|
|
405
|
+
shadow_hint = _shadowed_token_hint(psk_token)
|
|
406
|
+
|
|
395
407
|
# Retry loop for JWT minting
|
|
396
408
|
JWT_MINT_RETRIES = 3
|
|
397
409
|
last_error = None
|
|
@@ -419,7 +431,9 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
419
431
|
if not result.get("success"):
|
|
420
432
|
error_msg = result.get("error", "Unknown auth error")
|
|
421
433
|
gateway_circuit_breaker.record_failure()
|
|
422
|
-
raise RuntimeError(
|
|
434
|
+
raise RuntimeError(
|
|
435
|
+
_mint_failure_message(f"Auth error: {error_msg}", shadow_hint)
|
|
436
|
+
)
|
|
423
437
|
|
|
424
438
|
data = result.get("data", {})
|
|
425
439
|
jwt = data.get("access_token") or data.get("token")
|
|
@@ -465,7 +479,9 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
465
479
|
raise RuntimeError(f"Failed to get JWT: {e}") from e
|
|
466
480
|
# For other errors, we've already handled retries in the status check above
|
|
467
481
|
gateway_circuit_breaker.record_failure()
|
|
468
|
-
raise RuntimeError(
|
|
482
|
+
raise RuntimeError(
|
|
483
|
+
_mint_failure_message(f"Failed to get JWT: {e}", shadow_hint)
|
|
484
|
+
) from e
|
|
469
485
|
|
|
470
486
|
# Should not reach here, but just in case
|
|
471
487
|
gateway_circuit_breaker.record_failure()
|
|
@@ -28,6 +28,7 @@ from .platform import PlatformNamespace
|
|
|
28
28
|
from .coder import CoderNamespace
|
|
29
29
|
from .publisher import PublisherNamespace
|
|
30
30
|
from .stash import StashNamespace, StashTablesNamespace
|
|
31
|
+
from .usage import UsageNamespace
|
|
31
32
|
from .ai import (
|
|
32
33
|
AiNamespace,
|
|
33
34
|
RagSubNamespace,
|
|
@@ -65,6 +66,7 @@ __all__ = [
|
|
|
65
66
|
"PublisherNamespace",
|
|
66
67
|
"StashNamespace",
|
|
67
68
|
"StashTablesNamespace",
|
|
69
|
+
"UsageNamespace",
|
|
68
70
|
"AiNamespace",
|
|
69
71
|
"RagSubNamespace",
|
|
70
72
|
"ArtifactsSubNamespace",
|
|
@@ -969,9 +969,10 @@ class AiNamespace(GatewayMixin):
|
|
|
969
969
|
tool_allowlist: Optional[List[str]] = None,
|
|
970
970
|
guardrails: Optional[Dict[str, Any]] = None,
|
|
971
971
|
output_schema: Optional[Dict[str, Any]] = None,
|
|
972
|
-
model: str =
|
|
972
|
+
model: Optional[str] = None,
|
|
973
973
|
tool_endpoint: Optional[str] = None,
|
|
974
|
-
timeout: float = 300.0
|
|
974
|
+
timeout: float = 300.0,
|
|
975
|
+
provider: str = "claude"
|
|
975
976
|
) -> Dict[str, Any]:
|
|
976
977
|
"""
|
|
977
978
|
Execute agent with blocking wait.
|
|
@@ -987,9 +988,10 @@ class AiNamespace(GatewayMixin):
|
|
|
987
988
|
tool_allowlist: Optional list of tool names to enable
|
|
988
989
|
guardrails: Optional guardrails config (max_steps, max_tool_calls, etc.)
|
|
989
990
|
output_schema: Optional JSON Schema for structured output
|
|
990
|
-
model: Model to use (default
|
|
991
|
+
model: Model to use (provider default when omitted)
|
|
991
992
|
tool_endpoint: Optional custom tool endpoint URL
|
|
992
993
|
timeout: Request timeout in seconds (default: 300s)
|
|
994
|
+
provider: Agent provider to use (default: claude)
|
|
993
995
|
|
|
994
996
|
Returns:
|
|
995
997
|
Dict with "conversation_id", "final_response", "machine_output",
|
|
@@ -1000,8 +1002,12 @@ class AiNamespace(GatewayMixin):
|
|
|
1000
1002
|
"system_prompt": system_prompt,
|
|
1001
1003
|
"user_prompt": user_prompt,
|
|
1002
1004
|
"history_source": history_source,
|
|
1003
|
-
"
|
|
1005
|
+
"provider": provider
|
|
1004
1006
|
}
|
|
1007
|
+
if model:
|
|
1008
|
+
body["model"] = model
|
|
1009
|
+
elif provider == "claude":
|
|
1010
|
+
body["model"] = "claude-sonnet-4-5"
|
|
1005
1011
|
if inline_history:
|
|
1006
1012
|
body["inline_history"] = inline_history
|
|
1007
1013
|
if preloaded_context:
|
|
@@ -1034,10 +1040,11 @@ class AiNamespace(GatewayMixin):
|
|
|
1034
1040
|
tool_allowlist: Optional[List[str]] = None,
|
|
1035
1041
|
guardrails: Optional[Dict[str, Any]] = None,
|
|
1036
1042
|
output_schema: Optional[Dict[str, Any]] = None,
|
|
1037
|
-
model: str =
|
|
1043
|
+
model: Optional[str] = None,
|
|
1038
1044
|
tool_endpoint: Optional[str] = None,
|
|
1039
1045
|
on_chunk: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
1040
|
-
timeout: float = 300.0
|
|
1046
|
+
timeout: float = 300.0,
|
|
1047
|
+
provider: str = "claude"
|
|
1041
1048
|
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
1042
1049
|
"""
|
|
1043
1050
|
Execute agent with SSE streaming.
|
|
@@ -1053,10 +1060,11 @@ class AiNamespace(GatewayMixin):
|
|
|
1053
1060
|
tool_allowlist: Optional list of tool names
|
|
1054
1061
|
guardrails: Optional guardrails config
|
|
1055
1062
|
output_schema: Optional JSON Schema for structured output
|
|
1056
|
-
model: Model to use
|
|
1063
|
+
model: Model to use (provider default when omitted)
|
|
1057
1064
|
tool_endpoint: Optional custom tool endpoint URL
|
|
1058
1065
|
on_chunk: Optional callback for each chunk
|
|
1059
1066
|
timeout: Request timeout in seconds
|
|
1067
|
+
provider: Agent provider to use (default: claude)
|
|
1060
1068
|
|
|
1061
1069
|
Yields:
|
|
1062
1070
|
Streaming chunks with "type" and content
|
|
@@ -1066,8 +1074,12 @@ class AiNamespace(GatewayMixin):
|
|
|
1066
1074
|
"system_prompt": system_prompt,
|
|
1067
1075
|
"user_prompt": user_prompt,
|
|
1068
1076
|
"history_source": history_source,
|
|
1069
|
-
"
|
|
1077
|
+
"provider": provider
|
|
1070
1078
|
}
|
|
1079
|
+
if model:
|
|
1080
|
+
body["model"] = model
|
|
1081
|
+
elif provider == "claude":
|
|
1082
|
+
body["model"] = "claude-sonnet-4-5"
|
|
1071
1083
|
if inline_history:
|
|
1072
1084
|
body["inline_history"] = inline_history
|
|
1073
1085
|
if preloaded_context:
|
|
@@ -1104,9 +1116,10 @@ class AiNamespace(GatewayMixin):
|
|
|
1104
1116
|
tool_allowlist: Optional[List[str]] = None,
|
|
1105
1117
|
guardrails: Optional[Dict[str, Any]] = None,
|
|
1106
1118
|
output_schema: Optional[Dict[str, Any]] = None,
|
|
1107
|
-
model: str =
|
|
1119
|
+
model: Optional[str] = None,
|
|
1108
1120
|
tool_endpoint: Optional[str] = None,
|
|
1109
|
-
webhook_url: Optional[str] = None
|
|
1121
|
+
webhook_url: Optional[str] = None,
|
|
1122
|
+
provider: str = "claude"
|
|
1110
1123
|
) -> Dict[str, Any]:
|
|
1111
1124
|
"""
|
|
1112
1125
|
Fire-and-forget async execution.
|
|
@@ -1125,9 +1138,10 @@ class AiNamespace(GatewayMixin):
|
|
|
1125
1138
|
tool_allowlist: Optional list of tool names
|
|
1126
1139
|
guardrails: Optional guardrails config
|
|
1127
1140
|
output_schema: Optional JSON Schema
|
|
1128
|
-
model: Model to use
|
|
1141
|
+
model: Model to use (provider default when omitted)
|
|
1129
1142
|
tool_endpoint: Optional custom tool endpoint URL
|
|
1130
1143
|
webhook_url: Optional webhook for completion notification
|
|
1144
|
+
provider: Agent provider to use (default: claude)
|
|
1131
1145
|
|
|
1132
1146
|
Returns:
|
|
1133
1147
|
Dict with "result_key" for polling
|
|
@@ -1138,8 +1152,12 @@ class AiNamespace(GatewayMixin):
|
|
|
1138
1152
|
"user_prompt": user_prompt,
|
|
1139
1153
|
"result_key": result_key,
|
|
1140
1154
|
"history_source": history_source,
|
|
1141
|
-
"
|
|
1155
|
+
"provider": provider
|
|
1142
1156
|
}
|
|
1157
|
+
if model:
|
|
1158
|
+
body["model"] = model
|
|
1159
|
+
elif provider == "claude":
|
|
1160
|
+
body["model"] = "claude-sonnet-4-5"
|
|
1143
1161
|
if inline_history:
|
|
1144
1162
|
body["inline_history"] = inline_history
|
|
1145
1163
|
if preloaded_context:
|
|
@@ -624,11 +624,22 @@ class ArtifactsNamespace:
|
|
|
624
624
|
async def unwatch(self, *, watcher_name: str, **selector: Any) -> Dict[str, Any]:
|
|
625
625
|
return await self.unwatch_v2(watcher_name=watcher_name, **selector)
|
|
626
626
|
|
|
627
|
-
async def list_v2(
|
|
627
|
+
async def list_v2(
|
|
628
|
+
self,
|
|
629
|
+
limit: int = 100,
|
|
630
|
+
include_tombstoned: bool = False,
|
|
631
|
+
cursor: Optional[str] = None,
|
|
632
|
+
include_versions: bool = False,
|
|
633
|
+
**selector: Any,
|
|
634
|
+
) -> Dict[str, Any]:
|
|
628
635
|
body = self._selector_body(selector)
|
|
629
636
|
body["limit"] = limit
|
|
630
637
|
if include_tombstoned:
|
|
631
638
|
body["include_tombstoned"] = True
|
|
639
|
+
if cursor:
|
|
640
|
+
body["cursor"] = cursor
|
|
641
|
+
if include_versions:
|
|
642
|
+
body["include_versions"] = True
|
|
632
643
|
return await self._api("/api/artifact/v2/list", body=body)
|
|
633
644
|
|
|
634
645
|
async def delete_v2(self, *, tombstone: bool = False, delete_bookmarks: bool = False, actor_context: Optional[Dict[str, str]] = None, **selector: Any) -> Dict[str, Any]:
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Usage Namespace - Dominus consumption and provider-cost reporting.
|
|
3
|
+
|
|
4
|
+
Canonical SDK entry point for the dominus-usage service's authorized read
|
|
5
|
+
surface: summary, evidence, coverage, and explain. All four route through
|
|
6
|
+
the gateway (``use_gateway=True``), which maps ``/api/usage/*`` ->
|
|
7
|
+
``/svc/usage/*`` upstream to dominus-usage.
|
|
8
|
+
|
|
9
|
+
This namespace is a thin pass-through: it never converts quantities/amounts
|
|
10
|
+
to float, never infers a default window or grouping (the service applies
|
|
11
|
+
last-7-days / meter+project+environment+logical_service when omitted), and
|
|
12
|
+
never adds a token of its own. An explicitly minted human bearer reaches
|
|
13
|
+
Usage unchanged via the existing ``Dominus(gateway_user_token=...)``
|
|
14
|
+
constructor behavior, exactly like ``dominus.authority.*``.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any, Dict, Optional, Sequence, TYPE_CHECKING, Union
|
|
19
|
+
from urllib.parse import urlencode
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..start import Dominus
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _query_string(params: Dict[str, Any]) -> str:
|
|
26
|
+
"""Build a ``?...`` query string, repeating the key for list/tuple values."""
|
|
27
|
+
pairs: list[tuple[str, str]] = []
|
|
28
|
+
for key, value in params.items():
|
|
29
|
+
if value is None or value == "":
|
|
30
|
+
continue
|
|
31
|
+
if isinstance(value, (list, tuple)):
|
|
32
|
+
for item in value:
|
|
33
|
+
if item is None or item == "":
|
|
34
|
+
continue
|
|
35
|
+
pairs.append((key, str(item)))
|
|
36
|
+
else:
|
|
37
|
+
pairs.append((key, str(value)))
|
|
38
|
+
if not pairs:
|
|
39
|
+
return ""
|
|
40
|
+
return "?" + urlencode(pairs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class UsageNamespace:
|
|
44
|
+
"""
|
|
45
|
+
Usage service namespace: typed consumption and provider-cost reads.
|
|
46
|
+
|
|
47
|
+
Usage::
|
|
48
|
+
|
|
49
|
+
await dominus.usage.summary(since="2026-09-01T00:00:00Z", meter="ai.tokens.input")
|
|
50
|
+
await dominus.usage.evidence(project_id="proj_123", limit=100)
|
|
51
|
+
await dominus.usage.coverage()
|
|
52
|
+
await dominus.usage.explain(observation_id="obs_123")
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, client: "Dominus"):
|
|
56
|
+
self._client = client
|
|
57
|
+
|
|
58
|
+
async def _get(self, endpoint: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
59
|
+
return await self._client._request(
|
|
60
|
+
endpoint=endpoint,
|
|
61
|
+
method="GET",
|
|
62
|
+
use_gateway=True,
|
|
63
|
+
timeout=timeout,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
async def summary(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
since: Optional[str] = None,
|
|
70
|
+
until: Optional[str] = None,
|
|
71
|
+
group_by: Optional[Union[str, Sequence[str]]] = None,
|
|
72
|
+
project_id: Optional[str] = None,
|
|
73
|
+
environment: Optional[str] = None,
|
|
74
|
+
logical_service: Optional[str] = None,
|
|
75
|
+
family_root_id: Optional[str] = None,
|
|
76
|
+
meter: Optional[str] = None,
|
|
77
|
+
provider: Optional[str] = None,
|
|
78
|
+
timeout: float = 30.0,
|
|
79
|
+
) -> Dict[str, Any]:
|
|
80
|
+
"""Unit-aware usage rows plus distinct provider-reported/estimated cost.
|
|
81
|
+
|
|
82
|
+
``GET /api/usage/summary``. Omitting ``since``/``until`` uses the
|
|
83
|
+
service's last-7-days default; omitting ``group_by`` uses its default
|
|
84
|
+
grouping. Quantities/amounts are returned as decimal strings, never
|
|
85
|
+
converted to float.
|
|
86
|
+
"""
|
|
87
|
+
qs = _query_string(
|
|
88
|
+
{
|
|
89
|
+
"since": since,
|
|
90
|
+
"until": until,
|
|
91
|
+
"group_by": group_by,
|
|
92
|
+
"project_id": project_id,
|
|
93
|
+
"environment": environment,
|
|
94
|
+
"logical_service": logical_service,
|
|
95
|
+
"family_root_id": family_root_id,
|
|
96
|
+
"meter": meter,
|
|
97
|
+
"provider": provider,
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
return await self._get(f"/api/usage/summary{qs}", timeout=timeout)
|
|
101
|
+
|
|
102
|
+
async def evidence(
|
|
103
|
+
self,
|
|
104
|
+
*,
|
|
105
|
+
since: Optional[str] = None,
|
|
106
|
+
until: Optional[str] = None,
|
|
107
|
+
project_id: Optional[str] = None,
|
|
108
|
+
environment: Optional[str] = None,
|
|
109
|
+
logical_service: Optional[str] = None,
|
|
110
|
+
family_root_id: Optional[str] = None,
|
|
111
|
+
meter: Optional[str] = None,
|
|
112
|
+
provider: Optional[str] = None,
|
|
113
|
+
limit: Optional[int] = None,
|
|
114
|
+
cursor: Optional[str] = None,
|
|
115
|
+
timeout: float = 30.0,
|
|
116
|
+
) -> Dict[str, Any]:
|
|
117
|
+
"""Cursor-paginated observation/provider-source evidence.
|
|
118
|
+
|
|
119
|
+
``GET /api/usage/evidence``. ``limit`` defaults to 100 and is bounded
|
|
120
|
+
to 500 server-side; pass the response's ``next_cursor`` back in to
|
|
121
|
+
page forward.
|
|
122
|
+
"""
|
|
123
|
+
qs = _query_string(
|
|
124
|
+
{
|
|
125
|
+
"since": since,
|
|
126
|
+
"until": until,
|
|
127
|
+
"project_id": project_id,
|
|
128
|
+
"environment": environment,
|
|
129
|
+
"logical_service": logical_service,
|
|
130
|
+
"family_root_id": family_root_id,
|
|
131
|
+
"meter": meter,
|
|
132
|
+
"provider": provider,
|
|
133
|
+
"limit": limit,
|
|
134
|
+
"cursor": cursor,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
return await self._get(f"/api/usage/evidence{qs}", timeout=timeout)
|
|
138
|
+
|
|
139
|
+
async def coverage(
|
|
140
|
+
self,
|
|
141
|
+
*,
|
|
142
|
+
since: Optional[str] = None,
|
|
143
|
+
until: Optional[str] = None,
|
|
144
|
+
project_id: Optional[str] = None,
|
|
145
|
+
environment: Optional[str] = None,
|
|
146
|
+
logical_service: Optional[str] = None,
|
|
147
|
+
family_root_id: Optional[str] = None,
|
|
148
|
+
meter: Optional[str] = None,
|
|
149
|
+
provider: Optional[str] = None,
|
|
150
|
+
timeout: float = 30.0,
|
|
151
|
+
) -> Dict[str, Any]:
|
|
152
|
+
"""Registered meter/source coverage, freshness, and gap reasons.
|
|
153
|
+
|
|
154
|
+
``GET /api/usage/coverage``. Never reports unsupported capture as
|
|
155
|
+
complete; ``gaps`` names each unavailable reason explicitly.
|
|
156
|
+
"""
|
|
157
|
+
qs = _query_string(
|
|
158
|
+
{
|
|
159
|
+
"since": since,
|
|
160
|
+
"until": until,
|
|
161
|
+
"project_id": project_id,
|
|
162
|
+
"environment": environment,
|
|
163
|
+
"logical_service": logical_service,
|
|
164
|
+
"family_root_id": family_root_id,
|
|
165
|
+
"meter": meter,
|
|
166
|
+
"provider": provider,
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
return await self._get(f"/api/usage/coverage{qs}", timeout=timeout)
|
|
170
|
+
|
|
171
|
+
async def explain(
|
|
172
|
+
self,
|
|
173
|
+
*,
|
|
174
|
+
observation_id: Optional[str] = None,
|
|
175
|
+
operation_id: Optional[str] = None,
|
|
176
|
+
since: Optional[str] = None,
|
|
177
|
+
until: Optional[str] = None,
|
|
178
|
+
project_id: Optional[str] = None,
|
|
179
|
+
environment: Optional[str] = None,
|
|
180
|
+
logical_service: Optional[str] = None,
|
|
181
|
+
family_root_id: Optional[str] = None,
|
|
182
|
+
meter: Optional[str] = None,
|
|
183
|
+
timeout: float = 30.0,
|
|
184
|
+
) -> Dict[str, Any]:
|
|
185
|
+
"""Explanation linking permitted observations, attempts, and cost basis.
|
|
186
|
+
|
|
187
|
+
``GET /api/usage/explain``. Exactly one of ``observation_id`` or
|
|
188
|
+
``operation_id`` must be supplied; supplying both (or neither) is
|
|
189
|
+
rejected locally before the request is sent, matching the service's
|
|
190
|
+
own ambiguous-selector rejection. The remaining filters narrow which
|
|
191
|
+
observations can match an ``operation_id`` lookup; they are inert for
|
|
192
|
+
an ``observation_id`` lookup, which the service resolves unfiltered.
|
|
193
|
+
"""
|
|
194
|
+
if bool(observation_id) == bool(operation_id):
|
|
195
|
+
raise ValueError(
|
|
196
|
+
"usage.explain requires exactly one of observation_id or operation_id"
|
|
197
|
+
)
|
|
198
|
+
qs = _query_string(
|
|
199
|
+
{
|
|
200
|
+
"observation_id": observation_id,
|
|
201
|
+
"operation_id": operation_id,
|
|
202
|
+
"since": since,
|
|
203
|
+
"until": until,
|
|
204
|
+
"project_id": project_id,
|
|
205
|
+
"environment": environment,
|
|
206
|
+
"logical_service": logical_service,
|
|
207
|
+
"family_root_id": family_root_id,
|
|
208
|
+
"meter": meter,
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
return await self._get(f"/api/usage/explain{qs}", timeout=timeout)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
__all__ = ["UsageNamespace"]
|
|
@@ -196,6 +196,10 @@ class Dominus:
|
|
|
196
196
|
from .namespaces.recipes import RecipesNamespace
|
|
197
197
|
self.recipes = RecipesNamespace(self)
|
|
198
198
|
|
|
199
|
+
# Usage service (dominus-usage; consumption and provider-cost reports)
|
|
200
|
+
from .namespaces.usage import UsageNamespace
|
|
201
|
+
self.usage = UsageNamespace(self)
|
|
202
|
+
|
|
199
203
|
# Cache for JWT public key
|
|
200
204
|
self._public_key_cache = None
|
|
201
205
|
|
{dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.12
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License-Expression: LicenseRef-Proprietary
|
|
@@ -38,8 +38,8 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
38
38
|
## Agent Guide
|
|
39
39
|
|
|
40
40
|
Start with `docs/agent-guide/INDEX.md`. The current snapshot is
|
|
41
|
-
`docs/agent-guide/2026-09-
|
|
42
|
-
cleanup audit is `docs/janitor/2026-09-
|
|
41
|
+
`docs/agent-guide/2026-09-14-1300-sdk-python/00-reading-order.md`; the latest
|
|
42
|
+
cleanup audit is `docs/janitor/2026-09-14-1300-sdk-python-cleanup-audit.md`.
|
|
43
43
|
|
|
44
44
|
## What This Repo Ships
|
|
45
45
|
|
|
@@ -48,7 +48,7 @@ cleanup audit is `docs/janitor/2026-09-10-1002-sdk-python-cleanup-audit.md`.
|
|
|
48
48
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
49
49
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
50
50
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
51
|
-
- Current package version: `9.0.
|
|
51
|
+
- Current package version: `9.0.12`
|
|
52
52
|
|
|
53
53
|
## Install
|
|
54
54
|
|
{dominus_sdk_python-9.0.10 → dominus_sdk_python-9.0.12}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
@@ -44,6 +44,7 @@ dominus/namespaces/secrets.py
|
|
|
44
44
|
dominus/namespaces/secure.py
|
|
45
45
|
dominus/namespaces/stash.py
|
|
46
46
|
dominus/namespaces/sync.py
|
|
47
|
+
dominus/namespaces/usage.py
|
|
47
48
|
dominus/namespaces/warden.py
|
|
48
49
|
dominus/namespaces/workflow.py
|
|
49
50
|
dominus/services/__init__.py
|
|
@@ -52,6 +53,8 @@ dominus_sdk_python.egg-info/SOURCES.txt
|
|
|
52
53
|
dominus_sdk_python.egg-info/dependency_links.txt
|
|
53
54
|
dominus_sdk_python.egg-info/requires.txt
|
|
54
55
|
dominus_sdk_python.egg-info/top_level.txt
|
|
56
|
+
tests/test_ai_namespace.py
|
|
57
|
+
tests/test_artifacts_namespace.py
|
|
55
58
|
tests/test_auth.py
|
|
56
59
|
tests/test_authority_public_vocabulary.py
|
|
57
60
|
tests/test_browser_namespace.py
|
|
@@ -74,7 +77,9 @@ tests/test_recipes_stash_routing.py
|
|
|
74
77
|
tests/test_secrets_presence_contract.py
|
|
75
78
|
tests/test_stash_artifact_facade.py
|
|
76
79
|
tests/test_stash_tables_namespace.py
|
|
80
|
+
tests/test_token_precedence.py
|
|
77
81
|
tests/test_transport_compat.py
|
|
82
|
+
tests/test_usage_namespace.py
|
|
78
83
|
tests/test_version_consistency.py
|
|
79
84
|
tests/test_workflow_lifecycle.py
|
|
80
85
|
tests/test_workflow_refs.py
|