dominus-sdk-python 2.14.0__tar.gz → 2.15.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.14.0 → dominus_sdk_python-2.15.1}/PKG-INFO +25 -6
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/README.md +24 -5
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/__init__.py +1 -1
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/config/endpoints.py +2 -2
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/cache.py +1 -1
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/core.py +12 -12
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/ai.py +284 -20
- dominus_sdk_python-2.15.1/dominus/namespaces/courier.py +247 -0
- dominus_sdk_python-2.15.1/dominus/namespaces/db.py +392 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/redis.py +16 -16
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/workflow.py +291 -69
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/start.py +1 -1
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/PKG-INFO +25 -6
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/SOURCES.txt +3 -1
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/pyproject.toml +1 -1
- dominus_sdk_python-2.15.1/tests/test_workflow_lifecycle.py +259 -0
- dominus_sdk_python-2.15.1/tests/test_workflow_refs.py +97 -0
- dominus_sdk_python-2.14.0/dominus/namespaces/courier.py +0 -251
- dominus_sdk_python-2.14.0/dominus/namespaces/db.py +0 -292
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/errors.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/open.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/__init__.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/audio_capture.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/session.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/types.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/vad_gate.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dominus-sdk-python
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.15.1
|
|
4
4
|
Summary: Python SDK for the Dominus Orchestrator Platform
|
|
5
5
|
Author-email: CareBridge Systems <dev@carebridge.io>
|
|
6
6
|
License: Proprietary
|
|
@@ -49,7 +49,7 @@ Async Python SDK for CareBridge Dominus platform services. Routes calls through
|
|
|
49
49
|
- Server-side, asyncio-first Python SDK (3.9+)
|
|
50
50
|
- Namespace API with root-level shortcuts for common operations
|
|
51
51
|
- Targets production Cloudflare Workers (gateway, JWT, logs) and Cloud Run (orchestrator)
|
|
52
|
-
- Version: 2.
|
|
52
|
+
- Version: 2.15.1
|
|
53
53
|
|
|
54
54
|
## Quick Start
|
|
55
55
|
|
|
@@ -89,8 +89,20 @@ async for chunk in dominus.ai.stream_agent(
|
|
|
89
89
|
):
|
|
90
90
|
print(chunk.get("content", ""), end="", flush=True)
|
|
91
91
|
|
|
92
|
-
#
|
|
93
|
-
|
|
92
|
+
# Saved workflow lifecycle through workflow-manager
|
|
93
|
+
run = await dominus.workflow.create_run(
|
|
94
|
+
workflow_id="wf_saved_123",
|
|
95
|
+
context={"report_ref": {"report_id": "r-1", "accession": "a-1", "session_number": "2"}},
|
|
96
|
+
)
|
|
97
|
+
await dominus.workflow.validate_run(run["run_id"])
|
|
98
|
+
result = await dominus.workflow.start_run(run["run_id"])
|
|
99
|
+
|
|
100
|
+
# Raw orchestration lifecycle with inline workflow definition
|
|
101
|
+
raw_run = await dominus.ai.workflow.create_run(
|
|
102
|
+
workflow_definition={"workflow": {"name": "raw"}, "agents": {}, "artifacts": {}},
|
|
103
|
+
)
|
|
104
|
+
await dominus.ai.workflow.validate_run(raw_run["run_id"])
|
|
105
|
+
raw_result = await dominus.ai.workflow.start_run(raw_run["run_id"])
|
|
94
106
|
```
|
|
95
107
|
|
|
96
108
|
## Architecture (SDK View)
|
|
@@ -116,7 +128,7 @@ DOMINUS_TOKEN (PSK) --> Gateway /jwt/mint --> JWT cached (14min TTL)
|
|
|
116
128
|
| `secrets` | Warden | Secrets CRUD |
|
|
117
129
|
| `db` | Scribe | Database CRUD with optional secure-table audit |
|
|
118
130
|
| `secure` / `db_secure` | Scribe | Audit-logged data access (requires reason/actor) |
|
|
119
|
-
| `redis` |
|
|
131
|
+
| `redis` | Redis Worker | Cache, locks, counters, hashes |
|
|
120
132
|
| `files` | Archivist | Object storage with compliance mode |
|
|
121
133
|
| `auth` | Guardian | Users, roles, scopes, tenants, clients, pages, nav, subtypes, secure tables (147 methods) |
|
|
122
134
|
| `ddl` | Smith | Schema DDL, migrations, tenant provisioning, schema builder |
|
|
@@ -127,7 +139,7 @@ DOMINUS_TOKEN (PSK) --> Gateway /jwt/mint --> JWT cached (14min TTL)
|
|
|
127
139
|
| `health` | Orchestrator | Health/ping/warmup |
|
|
128
140
|
| `admin` | Admin | Reseed/reset admin category |
|
|
129
141
|
| `ai` | Agent Runtime | Agent execution, LLM completions, RAG, artifacts, results, orchestration, STT/TTS |
|
|
130
|
-
| `workflow` | Workflow Manager | Workflow CRUD,
|
|
142
|
+
| `workflow` | Workflow Manager | Workflow CRUD, pipelines/templates, saved-workflow run lifecycle, native pipeline runner |
|
|
131
143
|
| `oracle` / `stt` | Oracle | Real-time streaming STT with VAD (WebSocket, requires `oracle` extras) |
|
|
132
144
|
| `fastapi` | Local | FastAPI route auth decorators (`@jwt`, `@psk`, `@scopes`) |
|
|
133
145
|
|
|
@@ -173,6 +185,13 @@ dominus.release_console() # stop capturing
|
|
|
173
185
|
- [Architecture](docs/architecture.md) -- Request flow, resilience, JWT verification
|
|
174
186
|
- [Services Reference](docs/services.md) -- Complete namespace and method inventory
|
|
175
187
|
- [Development](docs/development.md) -- Setup, testing, adding APIs
|
|
188
|
+
- [Workflow hard-cut release notes](docs/workflow-hard-cut-release.md) -- cutover notes and operational checks
|
|
189
|
+
|
|
190
|
+
## Workflow Surfaces
|
|
191
|
+
|
|
192
|
+
- `dominus.workflow.*` is the saved-workflow facade through `dominus-workflow-manager`. Use it for stored workflow IDs and the artifact-aware run lifecycle: `create_run -> register_artifact -> validate_run -> start_run`.
|
|
193
|
+
- `dominus.ai.workflow.*` is the raw orchestration surface. It requires inline `workflow_definition` data. It does not support saved-workflow IDs directly.
|
|
194
|
+
- `dominus.workflow.execute_pipeline()` runs stored pipelines through workflow-manager's native orchestration-backed runner.
|
|
176
195
|
|
|
177
196
|
## License
|
|
178
197
|
|
|
@@ -7,7 +7,7 @@ Async Python SDK for CareBridge Dominus platform services. Routes calls through
|
|
|
7
7
|
- Server-side, asyncio-first Python SDK (3.9+)
|
|
8
8
|
- Namespace API with root-level shortcuts for common operations
|
|
9
9
|
- Targets production Cloudflare Workers (gateway, JWT, logs) and Cloud Run (orchestrator)
|
|
10
|
-
- Version: 2.
|
|
10
|
+
- Version: 2.15.1
|
|
11
11
|
|
|
12
12
|
## Quick Start
|
|
13
13
|
|
|
@@ -47,8 +47,20 @@ async for chunk in dominus.ai.stream_agent(
|
|
|
47
47
|
):
|
|
48
48
|
print(chunk.get("content", ""), end="", flush=True)
|
|
49
49
|
|
|
50
|
-
#
|
|
51
|
-
|
|
50
|
+
# Saved workflow lifecycle through workflow-manager
|
|
51
|
+
run = await dominus.workflow.create_run(
|
|
52
|
+
workflow_id="wf_saved_123",
|
|
53
|
+
context={"report_ref": {"report_id": "r-1", "accession": "a-1", "session_number": "2"}},
|
|
54
|
+
)
|
|
55
|
+
await dominus.workflow.validate_run(run["run_id"])
|
|
56
|
+
result = await dominus.workflow.start_run(run["run_id"])
|
|
57
|
+
|
|
58
|
+
# Raw orchestration lifecycle with inline workflow definition
|
|
59
|
+
raw_run = await dominus.ai.workflow.create_run(
|
|
60
|
+
workflow_definition={"workflow": {"name": "raw"}, "agents": {}, "artifacts": {}},
|
|
61
|
+
)
|
|
62
|
+
await dominus.ai.workflow.validate_run(raw_run["run_id"])
|
|
63
|
+
raw_result = await dominus.ai.workflow.start_run(raw_run["run_id"])
|
|
52
64
|
```
|
|
53
65
|
|
|
54
66
|
## Architecture (SDK View)
|
|
@@ -74,7 +86,7 @@ DOMINUS_TOKEN (PSK) --> Gateway /jwt/mint --> JWT cached (14min TTL)
|
|
|
74
86
|
| `secrets` | Warden | Secrets CRUD |
|
|
75
87
|
| `db` | Scribe | Database CRUD with optional secure-table audit |
|
|
76
88
|
| `secure` / `db_secure` | Scribe | Audit-logged data access (requires reason/actor) |
|
|
77
|
-
| `redis` |
|
|
89
|
+
| `redis` | Redis Worker | Cache, locks, counters, hashes |
|
|
78
90
|
| `files` | Archivist | Object storage with compliance mode |
|
|
79
91
|
| `auth` | Guardian | Users, roles, scopes, tenants, clients, pages, nav, subtypes, secure tables (147 methods) |
|
|
80
92
|
| `ddl` | Smith | Schema DDL, migrations, tenant provisioning, schema builder |
|
|
@@ -85,7 +97,7 @@ DOMINUS_TOKEN (PSK) --> Gateway /jwt/mint --> JWT cached (14min TTL)
|
|
|
85
97
|
| `health` | Orchestrator | Health/ping/warmup |
|
|
86
98
|
| `admin` | Admin | Reseed/reset admin category |
|
|
87
99
|
| `ai` | Agent Runtime | Agent execution, LLM completions, RAG, artifacts, results, orchestration, STT/TTS |
|
|
88
|
-
| `workflow` | Workflow Manager | Workflow CRUD,
|
|
100
|
+
| `workflow` | Workflow Manager | Workflow CRUD, pipelines/templates, saved-workflow run lifecycle, native pipeline runner |
|
|
89
101
|
| `oracle` / `stt` | Oracle | Real-time streaming STT with VAD (WebSocket, requires `oracle` extras) |
|
|
90
102
|
| `fastapi` | Local | FastAPI route auth decorators (`@jwt`, `@psk`, `@scopes`) |
|
|
91
103
|
|
|
@@ -131,6 +143,13 @@ dominus.release_console() # stop capturing
|
|
|
131
143
|
- [Architecture](docs/architecture.md) -- Request flow, resilience, JWT verification
|
|
132
144
|
- [Services Reference](docs/services.md) -- Complete namespace and method inventory
|
|
133
145
|
- [Development](docs/development.md) -- Setup, testing, adding APIs
|
|
146
|
+
- [Workflow hard-cut release notes](docs/workflow-hard-cut-release.md) -- cutover notes and operational checks
|
|
147
|
+
|
|
148
|
+
## Workflow Surfaces
|
|
149
|
+
|
|
150
|
+
- `dominus.workflow.*` is the saved-workflow facade through `dominus-workflow-manager`. Use it for stored workflow IDs and the artifact-aware run lifecycle: `create_run -> register_artifact -> validate_run -> start_run`.
|
|
151
|
+
- `dominus.ai.workflow.*` is the raw orchestration surface. It requires inline `workflow_definition` data. It does not support saved-workflow IDs directly.
|
|
152
|
+
- `dominus.workflow.execute_pipeline()` runs stored pipelines through workflow-manager's native orchestration-backed runner.
|
|
134
153
|
|
|
135
154
|
## License
|
|
136
155
|
|
|
@@ -37,7 +37,7 @@ LOGS_URL = os.environ.get("DOMINUS_LOGS_URL", _DEFAULT_LOGS_URL)
|
|
|
37
37
|
BASE_URL = os.environ.get("DOMINUS_BASE_URL", _DEFAULT_BASE_URL)
|
|
38
38
|
|
|
39
39
|
# Legacy aliases (all point to orchestrator now) - DEPRECATED
|
|
40
|
-
SOVEREIGN_URL = BASE_URL
|
|
40
|
+
SOVEREIGN_URL = BASE_URL # Deprecated: use BASE_URL or get_gateway_url()
|
|
41
41
|
ARCHITECT_URL = BASE_URL
|
|
42
42
|
ORCHESTRATOR_URL = BASE_URL
|
|
43
43
|
WARDEN_URL = BASE_URL
|
|
@@ -107,7 +107,7 @@ def get_base_url() -> str:
|
|
|
107
107
|
|
|
108
108
|
# DEPRECATED - use get_base_url()
|
|
109
109
|
def get_sovereign_url(environment: str = None) -> str:
|
|
110
|
-
"""Deprecated: Use get_base_url() instead."""
|
|
110
|
+
"""Deprecated: Use get_base_url() instead. 'Sovereign' is the legacy name for the gateway."""
|
|
111
111
|
return BASE_URL
|
|
112
112
|
|
|
113
113
|
|
|
@@ -11,7 +11,7 @@ import json
|
|
|
11
11
|
import time
|
|
12
12
|
from typing import Any, Optional
|
|
13
13
|
|
|
14
|
-
from .cache import dominus_cache,
|
|
14
|
+
from .cache import dominus_cache, gateway_circuit_breaker, exponential_backoff_with_jitter
|
|
15
15
|
|
|
16
16
|
# Max retries for HTTP requests (reduced from implicit to explicit)
|
|
17
17
|
MAX_RETRIES = 3
|
|
@@ -339,14 +339,14 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
339
339
|
proxy_config = get_proxy_config()
|
|
340
340
|
|
|
341
341
|
# Circuit breaker check
|
|
342
|
-
if not
|
|
342
|
+
if not gateway_circuit_breaker.can_execute():
|
|
343
343
|
raise RuntimeError(
|
|
344
344
|
f"Circuit breaker OPEN for auth - "
|
|
345
345
|
f"too many recent failures. Will retry after recovery timeout."
|
|
346
346
|
)
|
|
347
347
|
|
|
348
|
-
if
|
|
349
|
-
|
|
348
|
+
if gateway_circuit_breaker.state == gateway_circuit_breaker.HALF_OPEN:
|
|
349
|
+
gateway_circuit_breaker.record_half_open_call()
|
|
350
350
|
|
|
351
351
|
headers = {
|
|
352
352
|
"Authorization": f"Bearer {psk_token}",
|
|
@@ -384,17 +384,17 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
384
384
|
|
|
385
385
|
if not result.get("success"):
|
|
386
386
|
error_msg = result.get("error", "Unknown auth error")
|
|
387
|
-
|
|
387
|
+
gateway_circuit_breaker.record_failure()
|
|
388
388
|
raise RuntimeError(f"Auth error: {error_msg}")
|
|
389
389
|
|
|
390
390
|
data = result.get("data", {})
|
|
391
391
|
jwt = data.get("access_token") or data.get("token")
|
|
392
392
|
if not jwt:
|
|
393
|
-
|
|
393
|
+
gateway_circuit_breaker.record_failure()
|
|
394
394
|
raise RuntimeError("No JWT token in auth response")
|
|
395
395
|
|
|
396
396
|
# Success - record it
|
|
397
|
-
|
|
397
|
+
gateway_circuit_breaker.record_success()
|
|
398
398
|
return jwt
|
|
399
399
|
|
|
400
400
|
except httpx.TimeoutException as e:
|
|
@@ -407,7 +407,7 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
407
407
|
)
|
|
408
408
|
await asyncio.sleep(delay)
|
|
409
409
|
continue
|
|
410
|
-
|
|
410
|
+
gateway_circuit_breaker.record_failure()
|
|
411
411
|
raise RuntimeError(f"Failed to get JWT: {e}") from e
|
|
412
412
|
|
|
413
413
|
except httpx.NetworkError as e:
|
|
@@ -420,21 +420,21 @@ async def _get_service_jwt(psk_token: str, base_url: str) -> str:
|
|
|
420
420
|
)
|
|
421
421
|
await asyncio.sleep(delay)
|
|
422
422
|
continue
|
|
423
|
-
|
|
423
|
+
gateway_circuit_breaker.record_failure()
|
|
424
424
|
raise RuntimeError(f"Failed to get JWT: {e}") from e
|
|
425
425
|
|
|
426
426
|
except httpx.HTTPStatusError as e:
|
|
427
427
|
last_error = e
|
|
428
428
|
# 4xx errors (except 401 which is retried above) should not be retried
|
|
429
429
|
if 400 <= e.response.status_code < 500 and e.response.status_code != 401:
|
|
430
|
-
|
|
430
|
+
gateway_circuit_breaker.record_failure()
|
|
431
431
|
raise RuntimeError(f"Failed to get JWT: {e}") from e
|
|
432
432
|
# For other errors, we've already handled retries in the status check above
|
|
433
|
-
|
|
433
|
+
gateway_circuit_breaker.record_failure()
|
|
434
434
|
raise RuntimeError(f"Failed to get JWT: {e}") from e
|
|
435
435
|
|
|
436
436
|
# Should not reach here, but just in case
|
|
437
|
-
|
|
437
|
+
gateway_circuit_breaker.record_failure()
|
|
438
438
|
raise RuntimeError(f"Failed to get JWT after {JWT_MINT_RETRIES} retries: {last_error}")
|
|
439
439
|
|
|
440
440
|
|
|
@@ -565,32 +565,95 @@ class WorkflowSubNamespace(GatewayMixin):
|
|
|
565
565
|
def __init__(self, client: "Dominus"):
|
|
566
566
|
self._client = client
|
|
567
567
|
|
|
568
|
+
@staticmethod
|
|
569
|
+
def _require_inline_workflow_definition(
|
|
570
|
+
workflow_definition: Optional[Dict[str, Any]],
|
|
571
|
+
workflow_id: Optional[str],
|
|
572
|
+
method_name: str,
|
|
573
|
+
) -> Dict[str, Any]:
|
|
574
|
+
if isinstance(workflow_definition, dict):
|
|
575
|
+
return workflow_definition
|
|
576
|
+
if workflow_id:
|
|
577
|
+
raise ValueError(
|
|
578
|
+
f"dominus.ai.workflow.{method_name}() does not support saved workflow IDs. "
|
|
579
|
+
"Pass workflow_definition for raw orchestration or use dominus.workflow.create_run() "
|
|
580
|
+
"for saved-workflow execution."
|
|
581
|
+
)
|
|
582
|
+
raise ValueError(
|
|
583
|
+
f"dominus.ai.workflow.{method_name}() requires workflow_definition for raw orchestration execution."
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
@staticmethod
|
|
587
|
+
def _build_start_config(
|
|
588
|
+
*,
|
|
589
|
+
mode: Optional[str] = None,
|
|
590
|
+
timeout_seconds: Optional[int] = None,
|
|
591
|
+
save_events: Optional[bool] = None,
|
|
592
|
+
webhook_url: Optional[str] = None,
|
|
593
|
+
environment: Optional[str] = None,
|
|
594
|
+
) -> Dict[str, Any]:
|
|
595
|
+
config: Dict[str, Any] = {}
|
|
596
|
+
if mode:
|
|
597
|
+
config["mode"] = mode
|
|
598
|
+
if timeout_seconds is not None:
|
|
599
|
+
config["timeout_seconds"] = timeout_seconds
|
|
600
|
+
if save_events is not None:
|
|
601
|
+
config["save_events"] = save_events
|
|
602
|
+
if webhook_url:
|
|
603
|
+
config["webhook_url"] = webhook_url
|
|
604
|
+
if environment:
|
|
605
|
+
config["environment"] = environment
|
|
606
|
+
return config
|
|
607
|
+
|
|
568
608
|
async def execute(
|
|
569
609
|
self,
|
|
570
|
-
|
|
571
|
-
|
|
610
|
+
workflow_definition: Optional[Dict[str, Any]] = None,
|
|
611
|
+
*,
|
|
612
|
+
workflow_id: Optional[str] = None,
|
|
613
|
+
initial_artifacts: Optional[Dict[str, Any]] = None,
|
|
614
|
+
input: Optional[Dict[str, Any]] = None,
|
|
572
615
|
conversation_id: Optional[str] = None,
|
|
573
616
|
mode: str = "blocking",
|
|
574
|
-
webhook_url: Optional[str] = None
|
|
617
|
+
webhook_url: Optional[str] = None,
|
|
618
|
+
timeout_seconds: Optional[int] = None,
|
|
619
|
+
save_events: Optional[bool] = None,
|
|
620
|
+
environment: Optional[str] = None,
|
|
575
621
|
) -> Dict[str, Any]:
|
|
576
622
|
"""
|
|
577
|
-
Execute a
|
|
623
|
+
Execute a raw orchestration workflow definition.
|
|
578
624
|
|
|
579
625
|
Args:
|
|
580
|
-
|
|
581
|
-
|
|
626
|
+
workflow_definition: Inline orchestration workflow definition
|
|
627
|
+
workflow_id: Unsupported for raw orchestration; use dominus.workflow.create_run()
|
|
628
|
+
initial_artifacts: Initial artifact key -> artifact ID mapping
|
|
629
|
+
input: Legacy alias for initial_artifacts
|
|
582
630
|
conversation_id: Optional conversation ID for context
|
|
583
631
|
mode: Execution mode ("blocking", "streaming", "async")
|
|
584
632
|
webhook_url: Optional webhook for completion notification
|
|
633
|
+
timeout_seconds: Optional execution timeout
|
|
634
|
+
save_events: Whether to persist execution events
|
|
635
|
+
environment: Optional execution environment override
|
|
585
636
|
|
|
586
637
|
Returns:
|
|
587
638
|
Dict with "execution_id", "status", "output" (if blocking)
|
|
588
639
|
"""
|
|
589
640
|
body: Dict[str, Any] = {
|
|
590
|
-
"
|
|
591
|
-
|
|
592
|
-
|
|
641
|
+
"workflow_definition": self._require_inline_workflow_definition(
|
|
642
|
+
workflow_definition,
|
|
643
|
+
workflow_id,
|
|
644
|
+
"execute",
|
|
645
|
+
),
|
|
593
646
|
}
|
|
647
|
+
if initial_artifacts:
|
|
648
|
+
body["initial_artifacts"] = initial_artifacts
|
|
649
|
+
elif input:
|
|
650
|
+
body["initial_artifacts"] = input
|
|
651
|
+
body["config"] = self._build_start_config(
|
|
652
|
+
mode=mode,
|
|
653
|
+
timeout_seconds=timeout_seconds,
|
|
654
|
+
save_events=save_events,
|
|
655
|
+
environment=environment,
|
|
656
|
+
)
|
|
594
657
|
if conversation_id:
|
|
595
658
|
body["conversation_id"] = conversation_id
|
|
596
659
|
if webhook_url:
|
|
@@ -601,6 +664,50 @@ class WorkflowSubNamespace(GatewayMixin):
|
|
|
601
664
|
body=body
|
|
602
665
|
)
|
|
603
666
|
|
|
667
|
+
async def stream_execute(
|
|
668
|
+
self,
|
|
669
|
+
workflow_definition: Optional[Dict[str, Any]] = None,
|
|
670
|
+
*,
|
|
671
|
+
workflow_id: Optional[str] = None,
|
|
672
|
+
initial_artifacts: Optional[Dict[str, Any]] = None,
|
|
673
|
+
input: Optional[Dict[str, Any]] = None,
|
|
674
|
+
conversation_id: Optional[str] = None,
|
|
675
|
+
timeout_seconds: Optional[int] = None,
|
|
676
|
+
save_events: Optional[bool] = None,
|
|
677
|
+
environment: Optional[str] = None,
|
|
678
|
+
on_chunk: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
679
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
680
|
+
"""
|
|
681
|
+
Execute a raw orchestration workflow definition with SSE streaming.
|
|
682
|
+
"""
|
|
683
|
+
body: Dict[str, Any] = {
|
|
684
|
+
"workflow_definition": self._require_inline_workflow_definition(
|
|
685
|
+
workflow_definition,
|
|
686
|
+
workflow_id,
|
|
687
|
+
"stream_execute",
|
|
688
|
+
),
|
|
689
|
+
"config": self._build_start_config(
|
|
690
|
+
mode="streaming",
|
|
691
|
+
timeout_seconds=timeout_seconds,
|
|
692
|
+
save_events=save_events,
|
|
693
|
+
environment=environment,
|
|
694
|
+
),
|
|
695
|
+
}
|
|
696
|
+
if initial_artifacts:
|
|
697
|
+
body["initial_artifacts"] = initial_artifacts
|
|
698
|
+
elif input:
|
|
699
|
+
body["initial_artifacts"] = input
|
|
700
|
+
if conversation_id:
|
|
701
|
+
body["conversation_id"] = conversation_id
|
|
702
|
+
|
|
703
|
+
async for chunk in self._api_stream(
|
|
704
|
+
endpoint="/api/orchestration/execute",
|
|
705
|
+
body=body,
|
|
706
|
+
on_chunk=on_chunk,
|
|
707
|
+
timeout=float(timeout_seconds or 600),
|
|
708
|
+
):
|
|
709
|
+
yield chunk
|
|
710
|
+
|
|
604
711
|
async def validate(
|
|
605
712
|
self,
|
|
606
713
|
workflow_definition: Dict[str, Any]
|
|
@@ -616,35 +723,40 @@ class WorkflowSubNamespace(GatewayMixin):
|
|
|
616
723
|
"""
|
|
617
724
|
return await self._api(
|
|
618
725
|
endpoint="/api/orchestration/validate",
|
|
619
|
-
body={"
|
|
726
|
+
body={"workflow_definition": workflow_definition}
|
|
620
727
|
)
|
|
621
728
|
|
|
622
729
|
async def messages(
|
|
623
730
|
self,
|
|
624
731
|
execution_id: str,
|
|
625
|
-
|
|
626
|
-
offset: int = 0
|
|
732
|
+
count: int = 50,
|
|
627
733
|
) -> List[Dict[str, Any]]:
|
|
628
734
|
"""Get messages from an execution."""
|
|
629
735
|
result = await self._api(
|
|
630
|
-
endpoint=f"/api/orchestration/messages/{execution_id}",
|
|
631
|
-
|
|
736
|
+
endpoint=f"/api/orchestration/messages/{execution_id}?count={count}",
|
|
737
|
+
method="GET",
|
|
632
738
|
)
|
|
633
739
|
return result.get("messages", result) if isinstance(result, dict) else result
|
|
634
740
|
|
|
635
741
|
async def events(
|
|
636
742
|
self,
|
|
637
743
|
execution_id: str,
|
|
638
|
-
|
|
744
|
+
from_id: Optional[str] = None,
|
|
745
|
+
count: Optional[int] = None,
|
|
639
746
|
) -> List[Dict[str, Any]]:
|
|
640
747
|
"""Replay events from an execution."""
|
|
641
|
-
|
|
642
|
-
if
|
|
643
|
-
|
|
748
|
+
query = []
|
|
749
|
+
if from_id:
|
|
750
|
+
query.append(f"from_id={from_id}")
|
|
751
|
+
if count is not None:
|
|
752
|
+
query.append(f"count={count}")
|
|
753
|
+
endpoint = f"/api/orchestration/events/{execution_id}"
|
|
754
|
+
if query:
|
|
755
|
+
endpoint += f"?{'&'.join(query)}"
|
|
644
756
|
|
|
645
757
|
result = await self._api(
|
|
646
|
-
endpoint=
|
|
647
|
-
|
|
758
|
+
endpoint=endpoint,
|
|
759
|
+
method="GET",
|
|
648
760
|
)
|
|
649
761
|
return result.get("events", result) if isinstance(result, dict) else result
|
|
650
762
|
|
|
@@ -662,6 +774,158 @@ class WorkflowSubNamespace(GatewayMixin):
|
|
|
662
774
|
method="GET"
|
|
663
775
|
)
|
|
664
776
|
|
|
777
|
+
async def cancel(self, execution_id: str) -> Dict[str, Any]:
|
|
778
|
+
"""Cancel a running workflow execution."""
|
|
779
|
+
return await self._api(
|
|
780
|
+
endpoint=f"/api/orchestration/cancel/{execution_id}",
|
|
781
|
+
body={},
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
async def create_run(
|
|
785
|
+
self,
|
|
786
|
+
workflow_definition: Optional[Dict[str, Any]] = None,
|
|
787
|
+
*,
|
|
788
|
+
workflow_id: Optional[str] = None,
|
|
789
|
+
run_id: Optional[str] = None,
|
|
790
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
791
|
+
environment: Optional[str] = None,
|
|
792
|
+
) -> Dict[str, Any]:
|
|
793
|
+
"""
|
|
794
|
+
Create a raw orchestration run for the two-phase lifecycle.
|
|
795
|
+
"""
|
|
796
|
+
body: Dict[str, Any] = {
|
|
797
|
+
"workflow_definition": self._require_inline_workflow_definition(
|
|
798
|
+
workflow_definition,
|
|
799
|
+
workflow_id,
|
|
800
|
+
"create_run",
|
|
801
|
+
),
|
|
802
|
+
}
|
|
803
|
+
if run_id:
|
|
804
|
+
body["run_id"] = run_id
|
|
805
|
+
if metadata:
|
|
806
|
+
body["metadata"] = metadata
|
|
807
|
+
if environment:
|
|
808
|
+
body["environment"] = environment
|
|
809
|
+
|
|
810
|
+
return await self._api(
|
|
811
|
+
endpoint="/api/orchestration/runs",
|
|
812
|
+
body=body,
|
|
813
|
+
)
|
|
814
|
+
|
|
815
|
+
async def get_run(self, run_id: str) -> Dict[str, Any]:
|
|
816
|
+
"""Get raw orchestration run state."""
|
|
817
|
+
return await self._api(
|
|
818
|
+
endpoint=f"/api/orchestration/runs/{run_id}",
|
|
819
|
+
method="GET",
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
async def get_manifest(self, run_id: str) -> Dict[str, Any]:
|
|
823
|
+
"""Get the required-artifact manifest for a raw orchestration run."""
|
|
824
|
+
return await self._api(
|
|
825
|
+
endpoint=f"/api/orchestration/runs/{run_id}/manifest",
|
|
826
|
+
method="GET",
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
async def register_artifact(
|
|
830
|
+
self,
|
|
831
|
+
run_id: str,
|
|
832
|
+
artifact_key: str,
|
|
833
|
+
artifact_id: str,
|
|
834
|
+
*,
|
|
835
|
+
source_namespace: str = "conversation",
|
|
836
|
+
source_conversation_id: Optional[str] = None,
|
|
837
|
+
source_project: Optional[str] = None,
|
|
838
|
+
source_env: Optional[str] = None,
|
|
839
|
+
source_project_id: Optional[str] = None,
|
|
840
|
+
source_tenant: Optional[str] = None,
|
|
841
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
842
|
+
metadata_trusted: Optional[bool] = None,
|
|
843
|
+
) -> Dict[str, Any]:
|
|
844
|
+
"""
|
|
845
|
+
Register an uploaded artifact with a raw orchestration run.
|
|
846
|
+
"""
|
|
847
|
+
body: Dict[str, Any] = {
|
|
848
|
+
"artifact_key": artifact_key,
|
|
849
|
+
"artifact_id": artifact_id,
|
|
850
|
+
}
|
|
851
|
+
if source_namespace:
|
|
852
|
+
body["source_namespace"] = source_namespace
|
|
853
|
+
if source_conversation_id:
|
|
854
|
+
body["source_conversation_id"] = source_conversation_id
|
|
855
|
+
if source_project:
|
|
856
|
+
body["source_project"] = source_project
|
|
857
|
+
if source_env:
|
|
858
|
+
body["source_env"] = source_env
|
|
859
|
+
if source_project_id:
|
|
860
|
+
body["source_project_id"] = source_project_id
|
|
861
|
+
if source_tenant:
|
|
862
|
+
body["source_tenant"] = source_tenant
|
|
863
|
+
if metadata:
|
|
864
|
+
body["metadata"] = metadata
|
|
865
|
+
if metadata_trusted is not None:
|
|
866
|
+
body["metadata_trusted"] = metadata_trusted
|
|
867
|
+
|
|
868
|
+
return await self._api(
|
|
869
|
+
endpoint=f"/api/orchestration/runs/{run_id}/artifacts",
|
|
870
|
+
body=body,
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
async def validate_run(self, run_id: str) -> Dict[str, Any]:
|
|
874
|
+
"""Validate that a raw orchestration run is ready to start."""
|
|
875
|
+
return await self._api(
|
|
876
|
+
endpoint=f"/api/orchestration/runs/{run_id}/validate",
|
|
877
|
+
body={},
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
async def start_run(
|
|
881
|
+
self,
|
|
882
|
+
run_id: str,
|
|
883
|
+
*,
|
|
884
|
+
mode: str = "blocking",
|
|
885
|
+
timeout_seconds: Optional[int] = None,
|
|
886
|
+
save_events: Optional[bool] = None,
|
|
887
|
+
webhook_url: Optional[str] = None,
|
|
888
|
+
environment: Optional[str] = None,
|
|
889
|
+
) -> Dict[str, Any]:
|
|
890
|
+
"""Start a raw orchestration run after artifacts are registered."""
|
|
891
|
+
return await self._api(
|
|
892
|
+
endpoint=f"/api/orchestration/runs/{run_id}/start",
|
|
893
|
+
body={
|
|
894
|
+
"config": self._build_start_config(
|
|
895
|
+
mode=mode,
|
|
896
|
+
timeout_seconds=timeout_seconds,
|
|
897
|
+
save_events=save_events,
|
|
898
|
+
webhook_url=webhook_url,
|
|
899
|
+
environment=environment,
|
|
900
|
+
)
|
|
901
|
+
},
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
async def stream_start_run(
|
|
905
|
+
self,
|
|
906
|
+
run_id: str,
|
|
907
|
+
*,
|
|
908
|
+
timeout_seconds: Optional[int] = None,
|
|
909
|
+
save_events: Optional[bool] = None,
|
|
910
|
+
environment: Optional[str] = None,
|
|
911
|
+
on_chunk: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
912
|
+
) -> AsyncGenerator[Dict[str, Any], None]:
|
|
913
|
+
"""Start a raw orchestration run with SSE streaming."""
|
|
914
|
+
async for chunk in self._api_stream(
|
|
915
|
+
endpoint=f"/api/orchestration/runs/{run_id}/start",
|
|
916
|
+
body={
|
|
917
|
+
"config": self._build_start_config(
|
|
918
|
+
mode="streaming",
|
|
919
|
+
timeout_seconds=timeout_seconds,
|
|
920
|
+
save_events=save_events,
|
|
921
|
+
environment=environment,
|
|
922
|
+
)
|
|
923
|
+
},
|
|
924
|
+
on_chunk=on_chunk,
|
|
925
|
+
timeout=float(timeout_seconds or 600),
|
|
926
|
+
):
|
|
927
|
+
yield chunk
|
|
928
|
+
|
|
665
929
|
|
|
666
930
|
# ========================================
|
|
667
931
|
# Main AI Namespace
|