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.
Files changed (53) hide show
  1. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/PKG-INFO +25 -6
  2. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/README.md +24 -5
  3. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/__init__.py +1 -1
  4. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/config/endpoints.py +2 -2
  5. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/cache.py +1 -1
  6. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/core.py +12 -12
  7. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/ai.py +284 -20
  8. dominus_sdk_python-2.15.1/dominus/namespaces/courier.py +247 -0
  9. dominus_sdk_python-2.15.1/dominus/namespaces/db.py +392 -0
  10. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/redis.py +16 -16
  11. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/workflow.py +291 -69
  12. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/start.py +1 -1
  13. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/PKG-INFO +25 -6
  14. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/SOURCES.txt +3 -1
  15. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/pyproject.toml +1 -1
  16. dominus_sdk_python-2.15.1/tests/test_workflow_lifecycle.py +259 -0
  17. dominus_sdk_python-2.15.1/tests/test_workflow_refs.py +97 -0
  18. dominus_sdk_python-2.14.0/dominus/namespaces/courier.py +0 -251
  19. dominus_sdk_python-2.14.0/dominus/namespaces/db.py +0 -292
  20. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/config/__init__.py +0 -0
  21. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/errors.py +0 -0
  22. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/__init__.py +0 -0
  23. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/auth.py +0 -0
  24. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/console_capture.py +0 -0
  25. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/crypto.py +0 -0
  26. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/helpers/sse.py +0 -0
  27. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/__init__.py +0 -0
  28. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/admin.py +0 -0
  29. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/artifacts.py +0 -0
  30. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/auth.py +0 -0
  31. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/ddl.py +0 -0
  32. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/fastapi.py +0 -0
  33. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/files.py +0 -0
  34. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/health.py +0 -0
  35. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/jobs.py +0 -0
  36. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/logs.py +0 -0
  37. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/open.py +0 -0
  38. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/__init__.py +0 -0
  39. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/audio_capture.py +0 -0
  40. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
  41. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/session.py +0 -0
  42. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/types.py +0 -0
  43. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/oracle/vad_gate.py +0 -0
  44. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/portal.py +0 -0
  45. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/processor.py +0 -0
  46. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/secrets.py +0 -0
  47. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/secure.py +0 -0
  48. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/namespaces/sync.py +0 -0
  49. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus/services/__init__.py +0 -0
  50. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
  51. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/requires.txt +0 -0
  52. {dominus_sdk_python-2.14.0 → dominus_sdk_python-2.15.1}/dominus_sdk_python.egg-info/top_level.txt +0 -0
  53. {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.14.0
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.12.0
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
- # Workflow execution
93
- result = await dominus.workflow.execute(workflow_id, context={"key": "value"})
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` | Whisperer | Cache, locks, counters, hashes |
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, categories/pipelines, templates, execution |
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.12.0
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
- # Workflow execution
51
- result = await dominus.workflow.execute(workflow_id, context={"key": "value"})
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` | Whisperer | Cache, locks, counters, hashes |
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, categories/pipelines, templates, execution |
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
 
@@ -152,7 +152,7 @@ from .errors import (
152
152
  TimeoutError as DominusTimeoutError,
153
153
  )
154
154
 
155
- __version__ = "2.14.0"
155
+ __version__ = "2.15.1"
156
156
  __all__ = [
157
157
  # Main SDK instance
158
158
  "dominus",
@@ -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
 
@@ -197,4 +197,4 @@ orchestrator_circuit_breaker = CircuitBreaker(
197
197
  )
198
198
 
199
199
  # Backward compatibility alias
200
- sovereign_circuit_breaker = orchestrator_circuit_breaker
200
+ gateway_circuit_breaker = orchestrator_circuit_breaker
@@ -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, sovereign_circuit_breaker, exponential_backoff_with_jitter
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 sovereign_circuit_breaker.can_execute():
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 sovereign_circuit_breaker.state == sovereign_circuit_breaker.HALF_OPEN:
349
- sovereign_circuit_breaker.record_half_open_call()
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
- sovereign_circuit_breaker.record_failure()
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
- sovereign_circuit_breaker.record_failure()
393
+ gateway_circuit_breaker.record_failure()
394
394
  raise RuntimeError("No JWT token in auth response")
395
395
 
396
396
  # Success - record it
397
- sovereign_circuit_breaker.record_success()
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
- sovereign_circuit_breaker.record_failure()
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
- sovereign_circuit_breaker.record_failure()
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
- sovereign_circuit_breaker.record_failure()
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
- sovereign_circuit_breaker.record_failure()
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
- sovereign_circuit_breaker.record_failure()
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
- workflow_id: str,
571
- input: Dict[str, Any],
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 multi-agent workflow.
623
+ Execute a raw orchestration workflow definition.
578
624
 
579
625
  Args:
580
- workflow_id: Workflow identifier
581
- input: Workflow input data
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
- "workflow_id": workflow_id,
591
- "input": input,
592
- "mode": mode
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={"definition": workflow_definition}
726
+ body={"workflow_definition": workflow_definition}
620
727
  )
621
728
 
622
729
  async def messages(
623
730
  self,
624
731
  execution_id: str,
625
- limit: int = 100,
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
- body={"limit": limit, "offset": offset}
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
- from_timestamp: Optional[str] = None
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
- body: Dict[str, Any] = {}
642
- if from_timestamp:
643
- body["from"] = from_timestamp
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=f"/api/orchestration/events/{execution_id}",
647
- body=body if body else None
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