dominus-sdk-python 5.1.0__tar.gz → 6.0.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-5.1.0 → dominus_sdk_python-6.0.0}/PKG-INFO +3 -9
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/README.md +2 -8
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/__init__.py +1 -1
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/authority.py +33 -12
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/recipes.py +24 -8
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/workflow.py +26 -35
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/PKG-INFO +3 -9
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/pyproject.toml +1 -1
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_authority_public_vocabulary.py +43 -3
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_recipes_namespace.py +36 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_workflow_lifecycle.py +6 -6
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_workflow_refs.py +13 -16
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/errors.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/browser.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/deployer.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/stash.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/namespaces/warden.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus/start.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/setup.cfg +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_auth.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_browser_namespace.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_control_plane_namespaces.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_errors.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_flat_commands.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_health.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_logs.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_provisioning_parity.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_public_exports.py +0 -0
- {dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_transport_compat.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -43,7 +43,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
43
43
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
44
44
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
45
45
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
46
|
-
- Current package version: `
|
|
46
|
+
- Current package version: `6.0.0`
|
|
47
47
|
|
|
48
48
|
## Install
|
|
49
49
|
|
|
@@ -66,12 +66,6 @@ users = await dominus.db.query("users", filters={"status": "active"})
|
|
|
66
66
|
await dominus.redis.set("session:123", {"user": "john"}, ttl=3600)
|
|
67
67
|
|
|
68
68
|
run = await dominus.workflow.ensure(
|
|
69
|
-
"wf://carebridge/report-cycle",
|
|
70
|
-
subject="PCM47474562",
|
|
71
|
-
company="summit-radiology",
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
recipe_run = await dominus.workflow.ensure(
|
|
75
69
|
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@v3",
|
|
76
70
|
subject="PCM47474562",
|
|
77
71
|
company="summit-radiology",
|
|
@@ -199,7 +193,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
199
193
|
| `health` | Gateway | Health and ping helpers |
|
|
200
194
|
| `admin` | Admin Worker | Admin category reseed/reset |
|
|
201
195
|
| `ai` | Agent Runtime | Agent, completion, RAG, artifacts, results, raw orchestration |
|
|
202
|
-
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and
|
|
196
|
+
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and recipe-backed run lifecycle |
|
|
203
197
|
| `artifacts` | Artifact Worker | Addressed V2 artifacts, bookmarks, watches |
|
|
204
198
|
| `jobs` | Job Worker | Enqueue, poll, dead-letter management |
|
|
205
199
|
| `processor` | Processor | Batch and single-job processing |
|
|
@@ -9,7 +9,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
9
9
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
10
10
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
11
11
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
12
|
-
- Current package version: `
|
|
12
|
+
- Current package version: `6.0.0`
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -32,12 +32,6 @@ users = await dominus.db.query("users", filters={"status": "active"})
|
|
|
32
32
|
await dominus.redis.set("session:123", {"user": "john"}, ttl=3600)
|
|
33
33
|
|
|
34
34
|
run = await dominus.workflow.ensure(
|
|
35
|
-
"wf://carebridge/report-cycle",
|
|
36
|
-
subject="PCM47474562",
|
|
37
|
-
company="summit-radiology",
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
recipe_run = await dominus.workflow.ensure(
|
|
41
35
|
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@v3",
|
|
42
36
|
subject="PCM47474562",
|
|
43
37
|
company="summit-radiology",
|
|
@@ -165,7 +159,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
165
159
|
| `health` | Gateway | Health and ping helpers |
|
|
166
160
|
| `admin` | Admin Worker | Admin category reseed/reset |
|
|
167
161
|
| `ai` | Agent Runtime | Agent, completion, RAG, artifacts, results, raw orchestration |
|
|
168
|
-
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and
|
|
162
|
+
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and recipe-backed run lifecycle |
|
|
169
163
|
| `artifacts` | Artifact Worker | Addressed V2 artifacts, bookmarks, watches |
|
|
170
164
|
| `jobs` | Job Worker | Enqueue, poll, dead-letter management |
|
|
171
165
|
| `processor` | Processor | Batch and single-job processing |
|
|
@@ -12,9 +12,9 @@ All methods route through the gateway (``use_gateway=True``) which maps
|
|
|
12
12
|
|
|
13
13
|
Run launch is also exposed as ``dominus.workflow.ensure(...)``. Both call
|
|
14
14
|
``/api/authority/runs/ensure``. Use ``dominus.workflow.ensure`` for the
|
|
15
|
-
canonical
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
canonical recipe-backed launch path; use ``dominus.authority.ensure_run`` when
|
|
16
|
+
you want to think in Authority terms (run lifecycle, run dossiers, retries,
|
|
17
|
+
cancels).
|
|
18
18
|
"""
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
@@ -48,6 +48,14 @@ def _is_provisioning_bootstrap_run_kind(run_kind: Optional[str]) -> bool:
|
|
|
48
48
|
return _normalize_run_kind_token(run_kind) == "provisioning_bootstrap"
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _is_workflow_recipe_ref(value: Optional[str]) -> bool:
|
|
52
|
+
return str(value or "").strip().startswith("recipe://workflow-recipe-v1/")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _is_pipeline_recipe_ref(value: Optional[str]) -> bool:
|
|
56
|
+
return str(value or "").strip().startswith("recipe://pipeline-recipe-v1/")
|
|
57
|
+
|
|
58
|
+
|
|
51
59
|
def _query_string(params: Mapping[str, Any]) -> str:
|
|
52
60
|
cleaned = {k: v for k, v in params.items() if v is not None and v != ""}
|
|
53
61
|
if not cleaned:
|
|
@@ -61,7 +69,10 @@ class AuthorityNamespace:
|
|
|
61
69
|
|
|
62
70
|
Usage::
|
|
63
71
|
|
|
64
|
-
await dominus.authority.ensure_run(
|
|
72
|
+
await dominus.authority.ensure_run(
|
|
73
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@head",
|
|
74
|
+
subject="PCM47474562",
|
|
75
|
+
)
|
|
65
76
|
await dominus.authority.list_provisioning_targets()
|
|
66
77
|
await dominus.authority.get_run_dossier(run_id)
|
|
67
78
|
"""
|
|
@@ -167,8 +178,8 @@ class AuthorityNamespace:
|
|
|
167
178
|
async def ensure_run(
|
|
168
179
|
self,
|
|
169
180
|
*,
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
workflow_recipe_ref: Optional[str] = None,
|
|
182
|
+
pipeline_recipe_ref: Optional[str] = None,
|
|
172
183
|
run_kind: Optional[str] = None,
|
|
173
184
|
bootstrap_profile_ref: Optional[str] = None,
|
|
174
185
|
provisioning_target_slug: Optional[str] = None,
|
|
@@ -213,11 +224,23 @@ class AuthorityNamespace:
|
|
|
213
224
|
``bootstrap_provisioning_target`` (``execution_mode``, ``region``, ``bootstrap_profile_ref``, ...).
|
|
214
225
|
"""
|
|
215
226
|
bootstrap = _is_provisioning_bootstrap_run_kind(run_kind)
|
|
216
|
-
|
|
227
|
+
recipe_sources = [
|
|
228
|
+
value
|
|
229
|
+
for value in (
|
|
230
|
+
str(workflow_recipe_ref or "").strip(),
|
|
231
|
+
str(pipeline_recipe_ref or "").strip(),
|
|
232
|
+
)
|
|
233
|
+
if value
|
|
234
|
+
]
|
|
235
|
+
if not bootstrap and len(recipe_sources) != 1:
|
|
217
236
|
raise ValueError(
|
|
218
|
-
"ensure_run requires
|
|
237
|
+
"ensure_run requires exactly one of workflow_recipe_ref or pipeline_recipe_ref unless run_kind is "
|
|
219
238
|
"provisioning.bootstrap"
|
|
220
239
|
)
|
|
240
|
+
if workflow_recipe_ref and not _is_workflow_recipe_ref(workflow_recipe_ref):
|
|
241
|
+
raise ValueError("workflow_recipe_ref must start with recipe://workflow-recipe-v1/")
|
|
242
|
+
if pipeline_recipe_ref and not _is_pipeline_recipe_ref(pipeline_recipe_ref):
|
|
243
|
+
raise ValueError("pipeline_recipe_ref must start with recipe://pipeline-recipe-v1/")
|
|
221
244
|
if bootstrap:
|
|
222
245
|
target_slug = (provisioning_target_slug or "").strip()
|
|
223
246
|
if not target_slug:
|
|
@@ -236,8 +259,6 @@ class AuthorityNamespace:
|
|
|
236
259
|
"run_kind": run_kind,
|
|
237
260
|
"bootstrap_profile_ref": bootstrap_profile_ref,
|
|
238
261
|
"provisioning_target_slug": provisioning_target_slug,
|
|
239
|
-
"workflow_id": workflow_id,
|
|
240
|
-
"workflow_ref": workflow_ref,
|
|
241
262
|
"instance_id": instance_id,
|
|
242
263
|
"instance_key": instance_key,
|
|
243
264
|
"group": group,
|
|
@@ -262,8 +283,8 @@ class AuthorityNamespace:
|
|
|
262
283
|
})
|
|
263
284
|
else:
|
|
264
285
|
body = _compact({
|
|
265
|
-
"
|
|
266
|
-
"
|
|
286
|
+
"workflow_recipe_ref": workflow_recipe_ref,
|
|
287
|
+
"pipeline_recipe_ref": pipeline_recipe_ref,
|
|
267
288
|
"instance_id": instance_id,
|
|
268
289
|
"instance_key": instance_key,
|
|
269
290
|
"group": group,
|
|
@@ -41,14 +41,24 @@ class RecipesNamespace:
|
|
|
41
41
|
def __init__(self, client: "Dominus"):
|
|
42
42
|
self._client = client
|
|
43
43
|
|
|
44
|
-
async def _post(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
async def _post(
|
|
45
|
+
self,
|
|
46
|
+
endpoint: str,
|
|
47
|
+
body: Optional[Dict[str, Any]] = None,
|
|
48
|
+
*,
|
|
49
|
+
timeout: float = 30.0,
|
|
50
|
+
actor_context: Optional[Dict[str, str]] = None,
|
|
51
|
+
) -> Dict[str, Any]:
|
|
52
|
+
kwargs: Dict[str, Any] = {
|
|
53
|
+
"endpoint": endpoint,
|
|
54
|
+
"method": "POST",
|
|
55
|
+
"body": body or {},
|
|
56
|
+
"use_gateway": True,
|
|
57
|
+
"timeout": timeout,
|
|
58
|
+
}
|
|
59
|
+
if actor_context is not None:
|
|
60
|
+
kwargs["actor"] = actor_context
|
|
61
|
+
return await self._client._request(**kwargs)
|
|
52
62
|
|
|
53
63
|
async def _get(self, endpoint: str, *, timeout: float = 30.0) -> Dict[str, Any]:
|
|
54
64
|
return await self._client._request(
|
|
@@ -75,6 +85,7 @@ class RecipesNamespace:
|
|
|
75
85
|
name: str,
|
|
76
86
|
body: str,
|
|
77
87
|
description: Optional[str] = None,
|
|
88
|
+
actor_context: Optional[Dict[str, str]] = None,
|
|
78
89
|
timeout: float = 30.0,
|
|
79
90
|
) -> Dict[str, Any]:
|
|
80
91
|
"""Publish a recipe. ``POST /api/recipe/recipes/publish``."""
|
|
@@ -87,6 +98,7 @@ class RecipesNamespace:
|
|
|
87
98
|
"body": body,
|
|
88
99
|
"description": description,
|
|
89
100
|
}),
|
|
101
|
+
actor_context=actor_context,
|
|
90
102
|
timeout=timeout,
|
|
91
103
|
)
|
|
92
104
|
|
|
@@ -112,6 +124,7 @@ class RecipesNamespace:
|
|
|
112
124
|
name: str,
|
|
113
125
|
version: int,
|
|
114
126
|
reason: Optional[str] = None,
|
|
127
|
+
actor_context: Optional[Dict[str, str]] = None,
|
|
115
128
|
timeout: float = 30.0,
|
|
116
129
|
) -> Dict[str, Any]:
|
|
117
130
|
"""Mark the current head recipe version deprecated. ``POST /api/recipe/recipes/deprecate``."""
|
|
@@ -124,6 +137,7 @@ class RecipesNamespace:
|
|
|
124
137
|
"version": version,
|
|
125
138
|
"reason": reason,
|
|
126
139
|
}),
|
|
140
|
+
actor_context=actor_context,
|
|
127
141
|
timeout=timeout,
|
|
128
142
|
)
|
|
129
143
|
|
|
@@ -170,6 +184,7 @@ class RecipesNamespace:
|
|
|
170
184
|
owning_worker: str,
|
|
171
185
|
schema_version: int,
|
|
172
186
|
json_schema: Dict[str, Any],
|
|
187
|
+
actor_context: Optional[Dict[str, str]] = None,
|
|
173
188
|
timeout: float = 15.0,
|
|
174
189
|
) -> Dict[str, Any]:
|
|
175
190
|
"""Register a recipe type. ``POST /api/recipe/types/register``.
|
|
@@ -184,5 +199,6 @@ class RecipesNamespace:
|
|
|
184
199
|
"schema_version": schema_version,
|
|
185
200
|
"json_schema": json_schema,
|
|
186
201
|
},
|
|
202
|
+
actor_context=actor_context,
|
|
187
203
|
timeout=timeout,
|
|
188
204
|
)
|
|
@@ -33,9 +33,9 @@ Usage:
|
|
|
33
33
|
tools = await dominus.workflow.list_tools(limit=50)
|
|
34
34
|
await dominus.workflow.register_tool({"slug": "my-tool", "name": "My Tool", "tool_type": "http"})
|
|
35
35
|
|
|
36
|
-
# Canonical
|
|
36
|
+
# Canonical recipe-backed launch (Authority POST /api/authority/runs/ensure)
|
|
37
37
|
execution = await dominus.workflow.ensure(
|
|
38
|
-
"my-workflow
|
|
38
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/my-workflow@head",
|
|
39
39
|
subject="PCM47474562",
|
|
40
40
|
company="acme",
|
|
41
41
|
inputs={"key": "value"},
|
|
@@ -58,7 +58,7 @@ class WorkflowNamespace:
|
|
|
58
58
|
Workflow management namespace.
|
|
59
59
|
|
|
60
60
|
Provides operations for workflow CRUD, pipelines, templates,
|
|
61
|
-
and the
|
|
61
|
+
and the recipe-backed run lifecycle via Authority.
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
64
|
def __init__(self, client: "Dominus"):
|
|
@@ -91,19 +91,6 @@ class WorkflowNamespace:
|
|
|
91
91
|
endpoint += "?include_content=true"
|
|
92
92
|
return endpoint
|
|
93
93
|
|
|
94
|
-
@staticmethod
|
|
95
|
-
def _workflow_identifier_body(
|
|
96
|
-
workflow_id: Optional[str] = None,
|
|
97
|
-
workflow_ref: Optional[str] = None,
|
|
98
|
-
) -> Dict[str, Any]:
|
|
99
|
-
if workflow_ref and str(workflow_ref).strip():
|
|
100
|
-
return {"workflow_ref": str(workflow_ref).strip()}
|
|
101
|
-
if WorkflowNamespace._is_workflow_ref(workflow_id):
|
|
102
|
-
return {"workflow_ref": str(workflow_id).strip()}
|
|
103
|
-
if workflow_id and str(workflow_id).strip():
|
|
104
|
-
return {"workflow_id": str(workflow_id).strip()}
|
|
105
|
-
raise ValueError("workflow_id or workflow_ref is required")
|
|
106
|
-
|
|
107
94
|
@staticmethod
|
|
108
95
|
def _is_workflow_recipe_ref(value: Optional[str]) -> bool:
|
|
109
96
|
return str(value or "").strip().startswith("recipe://workflow-recipe-v1/")
|
|
@@ -218,13 +205,14 @@ class WorkflowNamespace:
|
|
|
218
205
|
|
|
219
206
|
def _build_authority_ensure_body(
|
|
220
207
|
self,
|
|
221
|
-
|
|
208
|
+
recipe_ref: Optional[str] = None,
|
|
222
209
|
*,
|
|
223
|
-
workflow_ref: Optional[str] = None,
|
|
224
210
|
workflow_recipe_ref: Optional[str] = None,
|
|
225
211
|
pipeline_recipe_ref: Optional[str] = None,
|
|
226
212
|
subject: Optional[str] = None,
|
|
227
213
|
company: Optional[str] = None,
|
|
214
|
+
group: Optional[str] = None,
|
|
215
|
+
owner: Optional[str] = None,
|
|
228
216
|
inputs: Optional[Dict[str, Any]] = None,
|
|
229
217
|
context: Optional[Dict[str, Any]] = None,
|
|
230
218
|
bindings: Optional[Dict[str, Any]] = None,
|
|
@@ -238,26 +226,25 @@ class WorkflowNamespace:
|
|
|
238
226
|
) -> Dict[str, Any]:
|
|
239
227
|
body: Dict[str, Any] = {"mode": mode}
|
|
240
228
|
launch_sources: List[tuple[str, str]] = []
|
|
241
|
-
|
|
242
|
-
if workflow_ref and str(workflow_ref).strip():
|
|
243
|
-
launch_sources.append(("workflow_ref", str(workflow_ref).strip()))
|
|
229
|
+
normalized_recipe_ref = str(recipe_ref or "").strip()
|
|
244
230
|
if workflow_recipe_ref and str(workflow_recipe_ref).strip():
|
|
245
231
|
launch_sources.append(("workflow_recipe_ref", str(workflow_recipe_ref).strip()))
|
|
246
232
|
if pipeline_recipe_ref and str(pipeline_recipe_ref).strip():
|
|
247
233
|
launch_sources.append(("pipeline_recipe_ref", str(pipeline_recipe_ref).strip()))
|
|
248
|
-
if
|
|
249
|
-
if self.
|
|
250
|
-
launch_sources.append(("
|
|
251
|
-
elif self.
|
|
252
|
-
launch_sources.append(("
|
|
253
|
-
elif self._is_pipeline_recipe_ref(normalized_workflow_id):
|
|
254
|
-
launch_sources.append(("pipeline_recipe_ref", normalized_workflow_id))
|
|
234
|
+
if normalized_recipe_ref:
|
|
235
|
+
if self._is_workflow_recipe_ref(normalized_recipe_ref):
|
|
236
|
+
launch_sources.append(("workflow_recipe_ref", normalized_recipe_ref))
|
|
237
|
+
elif self._is_pipeline_recipe_ref(normalized_recipe_ref):
|
|
238
|
+
launch_sources.append(("pipeline_recipe_ref", normalized_recipe_ref))
|
|
255
239
|
else:
|
|
256
|
-
|
|
240
|
+
raise ValueError(
|
|
241
|
+
"recipe_ref must start with recipe://workflow-recipe-v1/ "
|
|
242
|
+
"or recipe://pipeline-recipe-v1/"
|
|
243
|
+
)
|
|
257
244
|
if len(launch_sources) != 1:
|
|
258
245
|
raise ValueError(
|
|
259
|
-
"ensure requires exactly one of
|
|
260
|
-
"
|
|
246
|
+
"ensure requires exactly one of recipe_ref, workflow_recipe_ref, "
|
|
247
|
+
"or pipeline_recipe_ref"
|
|
261
248
|
)
|
|
262
249
|
field, value = launch_sources[0]
|
|
263
250
|
body[field] = value
|
|
@@ -265,6 +252,10 @@ class WorkflowNamespace:
|
|
|
265
252
|
body["subject"] = subject
|
|
266
253
|
if company is not None:
|
|
267
254
|
body["company"] = company
|
|
255
|
+
if group is not None:
|
|
256
|
+
body["group"] = group
|
|
257
|
+
if owner is not None:
|
|
258
|
+
body["owner"] = owner
|
|
268
259
|
if inputs:
|
|
269
260
|
body["inputs"] = inputs
|
|
270
261
|
if context:
|
|
@@ -883,7 +874,7 @@ class WorkflowNamespace:
|
|
|
883
874
|
|
|
884
875
|
async def ensure(
|
|
885
876
|
self,
|
|
886
|
-
|
|
877
|
+
recipe_ref: Optional[str] = None,
|
|
887
878
|
*,
|
|
888
879
|
instance_id: Optional[str] = None,
|
|
889
880
|
group: Optional[str] = None,
|
|
@@ -893,7 +884,6 @@ class WorkflowNamespace:
|
|
|
893
884
|
bindings: Optional[Dict[str, Any]] = None,
|
|
894
885
|
mode: str = "blocking",
|
|
895
886
|
context: Optional[Dict[str, Any]] = None,
|
|
896
|
-
workflow_ref: Optional[str] = None,
|
|
897
887
|
workflow_recipe_ref: Optional[str] = None,
|
|
898
888
|
pipeline_recipe_ref: Optional[str] = None,
|
|
899
889
|
subject: Optional[str] = None,
|
|
@@ -914,12 +904,13 @@ class WorkflowNamespace:
|
|
|
914
904
|
return await self._api(
|
|
915
905
|
endpoint="/api/authority/runs/ensure",
|
|
916
906
|
body=self._build_authority_ensure_body(
|
|
917
|
-
|
|
918
|
-
workflow_ref=workflow_ref,
|
|
907
|
+
recipe_ref,
|
|
919
908
|
workflow_recipe_ref=workflow_recipe_ref,
|
|
920
909
|
pipeline_recipe_ref=pipeline_recipe_ref,
|
|
921
910
|
subject=subject,
|
|
922
911
|
company=company,
|
|
912
|
+
group=group,
|
|
913
|
+
owner=owner,
|
|
923
914
|
inputs=inputs,
|
|
924
915
|
context=context,
|
|
925
916
|
bindings=bindings,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: Python SDK for the Dominus gateway-first platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -43,7 +43,7 @@ Async Python SDK for the Dominus gateway-first service plane.
|
|
|
43
43
|
- Gateway-scoped client mode for MCP and other user-JWT sessions
|
|
44
44
|
- Transport compatibility for wrapped `{success,data}` responses and unwrapped Warden/control-plane success objects
|
|
45
45
|
- Local helpers for JWT verification, trace propagation, retries, and console capture
|
|
46
|
-
- Current package version: `
|
|
46
|
+
- Current package version: `6.0.0`
|
|
47
47
|
|
|
48
48
|
## Install
|
|
49
49
|
|
|
@@ -66,12 +66,6 @@ users = await dominus.db.query("users", filters={"status": "active"})
|
|
|
66
66
|
await dominus.redis.set("session:123", {"user": "john"}, ttl=3600)
|
|
67
67
|
|
|
68
68
|
run = await dominus.workflow.ensure(
|
|
69
|
-
"wf://carebridge/report-cycle",
|
|
70
|
-
subject="PCM47474562",
|
|
71
|
-
company="summit-radiology",
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
recipe_run = await dominus.workflow.ensure(
|
|
75
69
|
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@v3",
|
|
76
70
|
subject="PCM47474562",
|
|
77
71
|
company="summit-radiology",
|
|
@@ -199,7 +193,7 @@ JWT and selected scope headers directly through Gateway.
|
|
|
199
193
|
| `health` | Gateway | Health and ping helpers |
|
|
200
194
|
| `admin` | Admin Worker | Admin category reseed/reset |
|
|
201
195
|
| `ai` | Agent Runtime | Agent, completion, RAG, artifacts, results, raw orchestration |
|
|
202
|
-
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and
|
|
196
|
+
| `workflow` | Workflow Manager + Authority | Saved workflow CRUD and recipe-backed run lifecycle |
|
|
203
197
|
| `artifacts` | Artifact Worker | Addressed V2 artifacts, bookmarks, watches |
|
|
204
198
|
| `jobs` | Job Worker | Enqueue, poll, dead-letter management |
|
|
205
199
|
| `processor` | Processor | Batch and single-job processing |
|
{dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_authority_public_vocabulary.py
RENAMED
|
@@ -86,10 +86,29 @@ def test_authority_scope_signatures_use_new_public_vocabulary():
|
|
|
86
86
|
schedule_create_sig = inspect.signature(AuthorityNamespace.create_schedule)
|
|
87
87
|
mint_sig = inspect.signature(core_helpers.mint_selected_scope_jwt)
|
|
88
88
|
|
|
89
|
-
for name in (
|
|
89
|
+
for name in (
|
|
90
|
+
"app_slug",
|
|
91
|
+
"env",
|
|
92
|
+
"target_org_id",
|
|
93
|
+
"target_env",
|
|
94
|
+
"run_kind",
|
|
95
|
+
"target_app_slug",
|
|
96
|
+
"bootstrap_profile_ref",
|
|
97
|
+
"provisioning_target_slug",
|
|
98
|
+
"workflow_recipe_ref",
|
|
99
|
+
"pipeline_recipe_ref",
|
|
100
|
+
):
|
|
90
101
|
assert name in ensure_sig.parameters
|
|
91
102
|
old_profile_key = "bootstrap" + "_profile"
|
|
92
|
-
for legacy in (
|
|
103
|
+
for legacy in (
|
|
104
|
+
"project_slug",
|
|
105
|
+
"environment",
|
|
106
|
+
"target_project_id",
|
|
107
|
+
"target_environment",
|
|
108
|
+
"workflow_id",
|
|
109
|
+
"workflow_ref",
|
|
110
|
+
old_profile_key,
|
|
111
|
+
):
|
|
93
112
|
assert legacy not in ensure_sig.parameters
|
|
94
113
|
|
|
95
114
|
for name in ("shared_app_slug", "target_app_slug", "target_env", "bootstrap_profile_ref"):
|
|
@@ -135,7 +154,7 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
|
|
|
135
154
|
namespace = AuthorityNamespace(client)
|
|
136
155
|
|
|
137
156
|
await namespace.ensure_run(
|
|
138
|
-
|
|
157
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@head",
|
|
139
158
|
app_slug="carebridge",
|
|
140
159
|
env="production",
|
|
141
160
|
target_org_id="org-123",
|
|
@@ -255,7 +274,10 @@ async def test_authority_scope_methods_use_canonical_context_wire_names():
|
|
|
255
274
|
assert ensure_body["env"] == "production"
|
|
256
275
|
assert ensure_body["target_org_id"] == "org-123"
|
|
257
276
|
assert ensure_body["target_env"] == "production"
|
|
277
|
+
assert ensure_body["workflow_recipe_ref"] == "recipe://workflow-recipe-v1/report-cycle@head"
|
|
258
278
|
assert "bootstrap_profile_ref" not in ensure_body
|
|
279
|
+
assert "workflow_id" not in ensure_body
|
|
280
|
+
assert "workflow_ref" not in ensure_body
|
|
259
281
|
|
|
260
282
|
bootstrap_body = client.calls[1]["body"]
|
|
261
283
|
assert bootstrap_body["shared_app_slug"] == "shared-core"
|
|
@@ -478,6 +500,24 @@ async def test_authority_ensure_run_provisioning_bootstrap_omits_workflow_mode()
|
|
|
478
500
|
assert "mode" not in body
|
|
479
501
|
|
|
480
502
|
|
|
503
|
+
@pytest.mark.asyncio
|
|
504
|
+
async def test_authority_ensure_run_requires_recipe_launch_ref():
|
|
505
|
+
client = FakeClient()
|
|
506
|
+
namespace = AuthorityNamespace(client)
|
|
507
|
+
|
|
508
|
+
with pytest.raises(ValueError, match="workflow_recipe_ref or pipeline_recipe_ref"):
|
|
509
|
+
await namespace.ensure_run(app_slug="carebridge", env="production")
|
|
510
|
+
|
|
511
|
+
with pytest.raises(ValueError, match="recipe://workflow-recipe-v1/"):
|
|
512
|
+
await namespace.ensure_run(
|
|
513
|
+
workflow_recipe_ref="wf://carebridge/report-cycle",
|
|
514
|
+
app_slug="carebridge",
|
|
515
|
+
env="production",
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
assert client.calls == []
|
|
519
|
+
|
|
520
|
+
|
|
481
521
|
@pytest.mark.asyncio
|
|
482
522
|
async def test_authority_ensure_run_bootstrap_requires_provisioning_target_slug():
|
|
483
523
|
client = FakeClient()
|
|
@@ -47,3 +47,39 @@ async def test_recipes_namespace_maps_deprecate_to_gateway_route(monkeypatch, sd
|
|
|
47
47
|
"timeout": 30.0,
|
|
48
48
|
}
|
|
49
49
|
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.asyncio
|
|
53
|
+
async def test_recipes_namespace_passes_actor_context_on_publish(monkeypatch, sdk):
|
|
54
|
+
calls = []
|
|
55
|
+
|
|
56
|
+
async def fake_request(**kwargs):
|
|
57
|
+
calls.append(kwargs)
|
|
58
|
+
return {"ok": True, "recipe": {"version": 1}}
|
|
59
|
+
|
|
60
|
+
monkeypatch.setattr(sdk, "_request", fake_request)
|
|
61
|
+
|
|
62
|
+
actor = {"type": "user", "id": "user-1"}
|
|
63
|
+
await sdk.recipes.publish(
|
|
64
|
+
type="browser-recipe",
|
|
65
|
+
tier="project",
|
|
66
|
+
name="cms-login",
|
|
67
|
+
body="version: browser-recipe-v1\nsteps: []\n",
|
|
68
|
+
actor_context=actor,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
assert calls == [
|
|
72
|
+
{
|
|
73
|
+
"endpoint": "/api/recipe/recipes/publish",
|
|
74
|
+
"method": "POST",
|
|
75
|
+
"body": {
|
|
76
|
+
"type": "browser-recipe",
|
|
77
|
+
"tier": "project",
|
|
78
|
+
"name": "cms-login",
|
|
79
|
+
"body": "version: browser-recipe-v1\nsteps: []\n",
|
|
80
|
+
},
|
|
81
|
+
"use_gateway": True,
|
|
82
|
+
"timeout": 30.0,
|
|
83
|
+
"actor": actor,
|
|
84
|
+
}
|
|
85
|
+
]
|
|
@@ -255,7 +255,7 @@ async def test_workflow_ensure_supports_authority_one_call_lifecycle():
|
|
|
255
255
|
namespace = WorkflowNamespace(client)
|
|
256
256
|
|
|
257
257
|
await namespace.ensure(
|
|
258
|
-
"
|
|
258
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@head",
|
|
259
259
|
subject="PCM47474562",
|
|
260
260
|
company="summit-radiology",
|
|
261
261
|
inputs={"report_snapshot": "ar://artifact"},
|
|
@@ -265,7 +265,7 @@ async def test_workflow_ensure_supports_authority_one_call_lifecycle():
|
|
|
265
265
|
|
|
266
266
|
assert client.calls[0]["endpoint"] == "/api/authority/runs/ensure"
|
|
267
267
|
body = client.calls[0]["body"]
|
|
268
|
-
assert body["
|
|
268
|
+
assert body["workflow_recipe_ref"] == "recipe://workflow-recipe-v1/report-cycle@head"
|
|
269
269
|
assert body["subject"] == "PCM47474562"
|
|
270
270
|
assert body["company"] == "summit-radiology"
|
|
271
271
|
assert body["inputs"] == {"report_snapshot": "ar://artifact"}
|
|
@@ -433,24 +433,24 @@ async def test_saved_workflow_instance_ensure_contract_matches_workflow_manager(
|
|
|
433
433
|
|
|
434
434
|
|
|
435
435
|
@pytest.mark.asyncio
|
|
436
|
-
async def
|
|
436
|
+
async def test_recipe_workflow_ensure_is_authority_only_and_stream_guard():
|
|
437
437
|
client = FakeClient()
|
|
438
438
|
namespace = WorkflowNamespace(client)
|
|
439
439
|
|
|
440
440
|
await namespace.ensure(
|
|
441
|
-
"
|
|
441
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@head",
|
|
442
442
|
mode="ensure_only",
|
|
443
443
|
)
|
|
444
444
|
|
|
445
445
|
call = client.calls[0]
|
|
446
446
|
assert call["endpoint"] == "/api/authority/runs/ensure"
|
|
447
|
-
assert call["body"]["
|
|
447
|
+
assert call["body"]["workflow_recipe_ref"] == "recipe://workflow-recipe-v1/report-cycle@head"
|
|
448
448
|
assert call["body"]["mode"] == "ensure_only"
|
|
449
449
|
assert isinstance(call["body"]["idempotency_key"], str)
|
|
450
450
|
|
|
451
451
|
with pytest.raises(ValueError, match="Authority-only and supports blocking, async, or ensure_only"):
|
|
452
452
|
await namespace.ensure(
|
|
453
|
-
"
|
|
453
|
+
workflow_recipe_ref="recipe://workflow-recipe-v1/report-cycle@head",
|
|
454
454
|
mode="streaming",
|
|
455
455
|
)
|
|
456
456
|
|
|
@@ -56,24 +56,21 @@ async def test_workflow_get_accepts_workflow_ref():
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
@pytest.mark.asyncio
|
|
59
|
-
async def
|
|
59
|
+
async def test_workflow_ensure_rejects_legacy_workflow_ref():
|
|
60
60
|
client = FakeClient()
|
|
61
61
|
namespace = WorkflowNamespace(client)
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
with pytest.raises(ValueError, match="recipe_ref"):
|
|
64
|
+
await namespace.ensure(
|
|
65
|
+
"wf://carebridge-platform/production/shared/patient-intake",
|
|
66
|
+
subject="sub-1",
|
|
67
|
+
company="co-1",
|
|
68
|
+
instance_id="run-123",
|
|
69
|
+
context={"report_ref": {"report_id": "r-1"}},
|
|
70
|
+
mode="async",
|
|
71
|
+
)
|
|
71
72
|
|
|
72
|
-
assert client.calls
|
|
73
|
-
body = client.calls[0]["body"]
|
|
74
|
-
assert body["workflow_ref"] == "wf://carebridge-platform/production/shared/patient-intake"
|
|
75
|
-
assert body["instance_id"] == "run-123"
|
|
76
|
-
assert body["context"] == {"report_ref": {"report_id": "r-1"}}
|
|
73
|
+
assert client.calls == []
|
|
77
74
|
|
|
78
75
|
|
|
79
76
|
@pytest.mark.asyncio
|
|
@@ -104,13 +101,13 @@ async def test_workflow_ensure_accepts_recipe_refs():
|
|
|
104
101
|
|
|
105
102
|
|
|
106
103
|
@pytest.mark.asyncio
|
|
107
|
-
async def
|
|
104
|
+
async def test_workflow_ensure_rejects_mixed_recipe_refs():
|
|
108
105
|
client = FakeClient()
|
|
109
106
|
namespace = WorkflowNamespace(client)
|
|
110
107
|
|
|
111
108
|
with pytest.raises(ValueError, match="exactly one"):
|
|
112
109
|
await namespace.ensure(
|
|
113
|
-
"
|
|
110
|
+
"recipe://pipeline-recipe-v1/intake-pipeline@v2",
|
|
114
111
|
workflow_recipe_ref="recipe://workflow-recipe-v1/patient-intake@v4",
|
|
115
112
|
)
|
|
116
113
|
|
|
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
|
|
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-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-5.1.0 → dominus_sdk_python-6.0.0}/tests/test_control_plane_namespaces.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
|