dominus-sdk-python 2.16.0__tar.gz → 2.17.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-2.16.0 → dominus_sdk_python-2.17.0}/PKG-INFO +1 -1
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/__init__.py +1 -1
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/workflow.py +343 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/PKG-INFO +1 -1
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/pyproject.toml +1 -1
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/tests/test_workflow_lifecycle.py +225 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/README.md +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/config/__init__.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/config/endpoints.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/errors.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/__init__.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/auth.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/cache.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/console_capture.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/core.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/crypto.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/sse.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/helpers/trace.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/__init__.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/admin.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/ai.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/artifacts.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/auth.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/courier.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/db.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/ddl.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/fastapi.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/files.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/health.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/jobs.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/logs.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/open.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/__init__.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/audio_capture.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/oracle_websocket.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/session.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/types.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/vad_gate.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/portal.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/processor.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/redis.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/secrets.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/secure.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/sync.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/services/__init__.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/start.py +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/SOURCES.txt +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/dependency_links.txt +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/requires.txt +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/top_level.txt +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/setup.cfg +0 -0
- {dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/tests/test_workflow_refs.py +0 -0
|
@@ -86,6 +86,74 @@ class WorkflowNamespace:
|
|
|
86
86
|
return {"workflow_id": str(workflow_id).strip()}
|
|
87
87
|
raise ValueError("workflow_id or workflow_ref is required")
|
|
88
88
|
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _instance_base_endpoint(workflow_id: str) -> str:
|
|
91
|
+
from urllib.parse import quote
|
|
92
|
+
|
|
93
|
+
return f"/api/workflow/workflows/{quote(workflow_id, safe='')}/instances"
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _build_instance_nudge_policy(
|
|
97
|
+
nudge_policy: Optional[Dict[str, Any]] = None,
|
|
98
|
+
) -> Optional[Dict[str, Any]]:
|
|
99
|
+
if not nudge_policy:
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
policy = dict(nudge_policy)
|
|
103
|
+
result: Dict[str, Any] = {}
|
|
104
|
+
if "mode" in policy and policy["mode"] is not None:
|
|
105
|
+
result["mode"] = policy["mode"]
|
|
106
|
+
if "max_rerun_count" in policy and policy["max_rerun_count"] is not None:
|
|
107
|
+
result["max_rerun_count"] = policy["max_rerun_count"]
|
|
108
|
+
elif "maxRerunCount" in policy and policy["maxRerunCount"] is not None:
|
|
109
|
+
result["max_rerun_count"] = policy["maxRerunCount"]
|
|
110
|
+
if "rerun_window_seconds" in policy and policy["rerun_window_seconds"] is not None:
|
|
111
|
+
result["rerun_window_seconds"] = policy["rerun_window_seconds"]
|
|
112
|
+
elif "rerunWindowSeconds" in policy and policy["rerunWindowSeconds"] is not None:
|
|
113
|
+
result["rerun_window_seconds"] = policy["rerunWindowSeconds"]
|
|
114
|
+
|
|
115
|
+
for key, value in policy.items():
|
|
116
|
+
if value is None:
|
|
117
|
+
continue
|
|
118
|
+
if key in {"mode", "max_rerun_count", "maxRerunCount", "rerun_window_seconds", "rerunWindowSeconds"}:
|
|
119
|
+
continue
|
|
120
|
+
result[key] = value
|
|
121
|
+
|
|
122
|
+
return result or None
|
|
123
|
+
|
|
124
|
+
def _build_ensure_instance_body(
|
|
125
|
+
self,
|
|
126
|
+
*,
|
|
127
|
+
group: str,
|
|
128
|
+
owner: str,
|
|
129
|
+
instance_id: Optional[str] = None,
|
|
130
|
+
environment: Optional[str] = None,
|
|
131
|
+
nudge_policy: Optional[Dict[str, Any]] = None,
|
|
132
|
+
bindings: Optional[Dict[str, Any]] = None,
|
|
133
|
+
mode: Optional[str] = None,
|
|
134
|
+
context: Optional[Dict[str, Any]] = None,
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
body: Dict[str, Any] = {
|
|
137
|
+
"params": {
|
|
138
|
+
"group": group,
|
|
139
|
+
"owner": owner,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if instance_id:
|
|
143
|
+
body["instance_id"] = instance_id
|
|
144
|
+
if environment:
|
|
145
|
+
body["params"]["env"] = environment
|
|
146
|
+
policy = self._build_instance_nudge_policy(nudge_policy)
|
|
147
|
+
if policy:
|
|
148
|
+
body["params"]["nudge_policy"] = policy
|
|
149
|
+
if bindings:
|
|
150
|
+
body["bindings"] = bindings
|
|
151
|
+
if mode:
|
|
152
|
+
body["mode"] = mode
|
|
153
|
+
if context:
|
|
154
|
+
body["context"] = context
|
|
155
|
+
return body
|
|
156
|
+
|
|
89
157
|
async def _api(
|
|
90
158
|
self,
|
|
91
159
|
endpoint: str,
|
|
@@ -620,6 +688,281 @@ class WorkflowNamespace:
|
|
|
620
688
|
body=body,
|
|
621
689
|
)
|
|
622
690
|
|
|
691
|
+
async def ensure(
|
|
692
|
+
self,
|
|
693
|
+
workflow_id: str,
|
|
694
|
+
*,
|
|
695
|
+
instance_id: Optional[str] = None,
|
|
696
|
+
group: str,
|
|
697
|
+
owner: str,
|
|
698
|
+
environment: Optional[str] = None,
|
|
699
|
+
nudge_policy: Optional[Dict[str, Any]] = None,
|
|
700
|
+
bindings: Optional[Dict[str, Any]] = None,
|
|
701
|
+
mode: str = "blocking",
|
|
702
|
+
context: Optional[Dict[str, Any]] = None,
|
|
703
|
+
) -> Dict[str, Any]:
|
|
704
|
+
"""Ensure a workflow instance exists and optionally launch execution."""
|
|
705
|
+
return await self.ensure_instance(
|
|
706
|
+
workflow_id,
|
|
707
|
+
instance_id=instance_id,
|
|
708
|
+
group=group,
|
|
709
|
+
owner=owner,
|
|
710
|
+
environment=environment,
|
|
711
|
+
nudge_policy=nudge_policy,
|
|
712
|
+
bindings=bindings,
|
|
713
|
+
mode=mode,
|
|
714
|
+
context=context,
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
async def ensure_instance(
|
|
718
|
+
self,
|
|
719
|
+
workflow_id: str,
|
|
720
|
+
*,
|
|
721
|
+
instance_id: Optional[str] = None,
|
|
722
|
+
group: str,
|
|
723
|
+
owner: str,
|
|
724
|
+
environment: Optional[str] = None,
|
|
725
|
+
nudge_policy: Optional[Dict[str, Any]] = None,
|
|
726
|
+
bindings: Optional[Dict[str, Any]] = None,
|
|
727
|
+
mode: str = "blocking",
|
|
728
|
+
context: Optional[Dict[str, Any]] = None,
|
|
729
|
+
) -> Dict[str, Any]:
|
|
730
|
+
"""Ensure a workflow instance exists and optionally launch execution."""
|
|
731
|
+
if mode == "streaming":
|
|
732
|
+
raise ValueError("Use stream_ensure_instance() for streaming instance execution")
|
|
733
|
+
|
|
734
|
+
return await self._api(
|
|
735
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/ensure",
|
|
736
|
+
body=self._build_ensure_instance_body(
|
|
737
|
+
instance_id=instance_id,
|
|
738
|
+
group=group,
|
|
739
|
+
owner=owner,
|
|
740
|
+
environment=environment,
|
|
741
|
+
nudge_policy=nudge_policy,
|
|
742
|
+
bindings=bindings,
|
|
743
|
+
mode=mode,
|
|
744
|
+
context=context,
|
|
745
|
+
),
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
async def stream_ensure_instance(
|
|
749
|
+
self,
|
|
750
|
+
workflow_id: str,
|
|
751
|
+
*,
|
|
752
|
+
instance_id: Optional[str] = None,
|
|
753
|
+
group: str,
|
|
754
|
+
owner: str,
|
|
755
|
+
environment: Optional[str] = None,
|
|
756
|
+
nudge_policy: Optional[Dict[str, Any]] = None,
|
|
757
|
+
bindings: Optional[Dict[str, Any]] = None,
|
|
758
|
+
context: Optional[Dict[str, Any]] = None,
|
|
759
|
+
on_chunk: Optional[Any] = None,
|
|
760
|
+
):
|
|
761
|
+
"""Ensure a workflow instance and stream execution events."""
|
|
762
|
+
async for chunk in self._client._stream_request(
|
|
763
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/ensure",
|
|
764
|
+
body=self._build_ensure_instance_body(
|
|
765
|
+
instance_id=instance_id,
|
|
766
|
+
group=group,
|
|
767
|
+
owner=owner,
|
|
768
|
+
environment=environment,
|
|
769
|
+
nudge_policy=nudge_policy,
|
|
770
|
+
bindings=bindings,
|
|
771
|
+
mode="streaming",
|
|
772
|
+
context=context,
|
|
773
|
+
),
|
|
774
|
+
on_chunk=on_chunk,
|
|
775
|
+
use_gateway=True,
|
|
776
|
+
):
|
|
777
|
+
yield chunk
|
|
778
|
+
|
|
779
|
+
async def get_instance_status(self, workflow_id: str, instance_id: str) -> Dict[str, Any]:
|
|
780
|
+
"""Get workflow instance status."""
|
|
781
|
+
from urllib.parse import quote
|
|
782
|
+
|
|
783
|
+
return await self._api(
|
|
784
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/status",
|
|
785
|
+
method="GET",
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
async def list_instances(
|
|
789
|
+
self,
|
|
790
|
+
*,
|
|
791
|
+
workflow_id: Optional[str] = None,
|
|
792
|
+
group: Optional[str] = None,
|
|
793
|
+
owner: Optional[str] = None,
|
|
794
|
+
environment: Optional[str] = None,
|
|
795
|
+
status: Optional[str] = None,
|
|
796
|
+
limit: int = 50,
|
|
797
|
+
offset: int = 0,
|
|
798
|
+
) -> List[Dict[str, Any]]:
|
|
799
|
+
"""List workflow instances."""
|
|
800
|
+
params = [f"limit={limit}", f"offset={offset}"]
|
|
801
|
+
if workflow_id:
|
|
802
|
+
params.append(f"workflow_id={workflow_id}")
|
|
803
|
+
if group:
|
|
804
|
+
params.append(f"group={group}")
|
|
805
|
+
if owner:
|
|
806
|
+
params.append(f"owner={owner}")
|
|
807
|
+
if environment:
|
|
808
|
+
params.append(f"environment={environment}")
|
|
809
|
+
if status:
|
|
810
|
+
params.append(f"status={status}")
|
|
811
|
+
|
|
812
|
+
result = await self._api(
|
|
813
|
+
endpoint=f"/api/workflow/instances?{'&'.join(params)}",
|
|
814
|
+
method="GET",
|
|
815
|
+
)
|
|
816
|
+
return result.get("instances", result) if isinstance(result, dict) else result
|
|
817
|
+
|
|
818
|
+
async def get_instance_artifacts(self, workflow_id: str, instance_id: str) -> Dict[str, Any]:
|
|
819
|
+
"""Get workflow instance artifact addresses."""
|
|
820
|
+
from urllib.parse import quote
|
|
821
|
+
|
|
822
|
+
return await self._api(
|
|
823
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/artifacts",
|
|
824
|
+
method="GET",
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
async def get_instance_outputs(self, workflow_id: str, instance_id: str) -> Dict[str, Any]:
|
|
828
|
+
"""Get workflow instance accepted outputs."""
|
|
829
|
+
from urllib.parse import quote
|
|
830
|
+
|
|
831
|
+
return await self._api(
|
|
832
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/outputs",
|
|
833
|
+
method="GET",
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
async def get_instance_history(
|
|
837
|
+
self,
|
|
838
|
+
workflow_id: str,
|
|
839
|
+
instance_id: str,
|
|
840
|
+
*,
|
|
841
|
+
limit: int = 50,
|
|
842
|
+
offset: int = 0,
|
|
843
|
+
) -> Dict[str, Any]:
|
|
844
|
+
"""Get workflow instance execution history."""
|
|
845
|
+
from urllib.parse import quote
|
|
846
|
+
|
|
847
|
+
return await self._api(
|
|
848
|
+
endpoint=(
|
|
849
|
+
f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/history"
|
|
850
|
+
f"?limit={limit}&offset={offset}"
|
|
851
|
+
),
|
|
852
|
+
method="GET",
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
async def nudge(
|
|
856
|
+
self,
|
|
857
|
+
workflow_id: str,
|
|
858
|
+
instance_id: str,
|
|
859
|
+
*,
|
|
860
|
+
reason: str,
|
|
861
|
+
trigger_artifact: Optional[str] = None,
|
|
862
|
+
mode: str = "async",
|
|
863
|
+
) -> Dict[str, Any]:
|
|
864
|
+
"""Nudge a workflow instance into a new execution."""
|
|
865
|
+
return await self.nudge_instance(
|
|
866
|
+
workflow_id,
|
|
867
|
+
instance_id,
|
|
868
|
+
reason=reason,
|
|
869
|
+
trigger_artifact=trigger_artifact,
|
|
870
|
+
mode=mode,
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
async def nudge_instance(
|
|
874
|
+
self,
|
|
875
|
+
workflow_id: str,
|
|
876
|
+
instance_id: str,
|
|
877
|
+
*,
|
|
878
|
+
reason: str,
|
|
879
|
+
trigger_artifact: Optional[str] = None,
|
|
880
|
+
mode: str = "async",
|
|
881
|
+
) -> Dict[str, Any]:
|
|
882
|
+
"""Nudge a workflow instance into a new execution."""
|
|
883
|
+
from urllib.parse import quote
|
|
884
|
+
|
|
885
|
+
if mode == "streaming":
|
|
886
|
+
raise ValueError("Use stream_nudge_instance() for streaming instance execution")
|
|
887
|
+
|
|
888
|
+
body: Dict[str, Any] = {"reason": reason, "mode": mode}
|
|
889
|
+
if trigger_artifact:
|
|
890
|
+
body["trigger_artifact"] = trigger_artifact
|
|
891
|
+
|
|
892
|
+
return await self._api(
|
|
893
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/nudge",
|
|
894
|
+
body=body,
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
async def stream_nudge_instance(
|
|
898
|
+
self,
|
|
899
|
+
workflow_id: str,
|
|
900
|
+
instance_id: str,
|
|
901
|
+
*,
|
|
902
|
+
reason: str,
|
|
903
|
+
trigger_artifact: Optional[str] = None,
|
|
904
|
+
on_chunk: Optional[Any] = None,
|
|
905
|
+
):
|
|
906
|
+
"""Nudge a workflow instance and stream execution events."""
|
|
907
|
+
from urllib.parse import quote
|
|
908
|
+
|
|
909
|
+
body: Dict[str, Any] = {"reason": reason, "mode": "streaming"}
|
|
910
|
+
if trigger_artifact:
|
|
911
|
+
body["trigger_artifact"] = trigger_artifact
|
|
912
|
+
|
|
913
|
+
async for chunk in self._client._stream_request(
|
|
914
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/nudge",
|
|
915
|
+
body=body,
|
|
916
|
+
on_chunk=on_chunk,
|
|
917
|
+
use_gateway=True,
|
|
918
|
+
):
|
|
919
|
+
yield chunk
|
|
920
|
+
|
|
921
|
+
async def retry(
|
|
922
|
+
self,
|
|
923
|
+
workflow_id: str,
|
|
924
|
+
instance_id: str,
|
|
925
|
+
*,
|
|
926
|
+
reason: str = "retry",
|
|
927
|
+
trigger_artifact: Optional[str] = None,
|
|
928
|
+
mode: str = "async",
|
|
929
|
+
) -> Dict[str, Any]:
|
|
930
|
+
"""Retry a workflow instance. Alias for nudge with a default retry reason."""
|
|
931
|
+
return await self.retry_instance(
|
|
932
|
+
workflow_id,
|
|
933
|
+
instance_id,
|
|
934
|
+
reason=reason,
|
|
935
|
+
trigger_artifact=trigger_artifact,
|
|
936
|
+
mode=mode,
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
async def retry_instance(
|
|
940
|
+
self,
|
|
941
|
+
workflow_id: str,
|
|
942
|
+
instance_id: str,
|
|
943
|
+
*,
|
|
944
|
+
reason: str = "retry",
|
|
945
|
+
trigger_artifact: Optional[str] = None,
|
|
946
|
+
mode: str = "async",
|
|
947
|
+
) -> Dict[str, Any]:
|
|
948
|
+
"""Retry a workflow instance. Alias for nudge with a default retry reason."""
|
|
949
|
+
return await self.nudge_instance(
|
|
950
|
+
workflow_id,
|
|
951
|
+
instance_id,
|
|
952
|
+
reason=reason,
|
|
953
|
+
trigger_artifact=trigger_artifact,
|
|
954
|
+
mode=mode,
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
async def cancel_instance(self, workflow_id: str, instance_id: str) -> Dict[str, Any]:
|
|
958
|
+
"""Cancel a running workflow instance."""
|
|
959
|
+
from urllib.parse import quote
|
|
960
|
+
|
|
961
|
+
return await self._api(
|
|
962
|
+
endpoint=f"{self._instance_base_endpoint(workflow_id)}/{quote(instance_id, safe='')}/cancel",
|
|
963
|
+
body={},
|
|
964
|
+
)
|
|
965
|
+
|
|
623
966
|
async def get_run(self, run_id: str) -> Dict[str, Any]:
|
|
624
967
|
"""Get a saved-workflow run."""
|
|
625
968
|
return await self._api(
|
|
@@ -314,3 +314,228 @@ async def test_execute_pipeline_forwards_resume_execution_id():
|
|
|
314
314
|
|
|
315
315
|
call = client.calls[0]
|
|
316
316
|
assert call["body"]["config"]["resume_execution_id"] == "resume-1"
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@pytest.mark.asyncio
|
|
320
|
+
async def test_saved_workflow_instance_ensure_contract_matches_workflow_manager():
|
|
321
|
+
client = FakeClient()
|
|
322
|
+
namespace = WorkflowNamespace(client)
|
|
323
|
+
|
|
324
|
+
await namespace.ensure_instance(
|
|
325
|
+
"wf-instance",
|
|
326
|
+
instance_id="inst-1",
|
|
327
|
+
group="reports",
|
|
328
|
+
owner="company:acme",
|
|
329
|
+
environment="production",
|
|
330
|
+
nudge_policy={
|
|
331
|
+
"mode": "coalesce",
|
|
332
|
+
"maxRerunCount": 3,
|
|
333
|
+
"rerunWindowSeconds": 120,
|
|
334
|
+
"cooldown_seconds": 15,
|
|
335
|
+
},
|
|
336
|
+
bindings={"subject": "PCM47474562"},
|
|
337
|
+
mode="async",
|
|
338
|
+
context={"company": "acme"},
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
call = client.calls[0]
|
|
342
|
+
assert call["endpoint"] == "/api/workflow/workflows/wf-instance/instances/ensure"
|
|
343
|
+
assert call["body"] == {
|
|
344
|
+
"instance_id": "inst-1",
|
|
345
|
+
"params": {
|
|
346
|
+
"group": "reports",
|
|
347
|
+
"owner": "company:acme",
|
|
348
|
+
"env": "production",
|
|
349
|
+
"nudge_policy": {
|
|
350
|
+
"mode": "coalesce",
|
|
351
|
+
"max_rerun_count": 3,
|
|
352
|
+
"rerun_window_seconds": 120,
|
|
353
|
+
"cooldown_seconds": 15,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
"bindings": {"subject": "PCM47474562"},
|
|
357
|
+
"mode": "async",
|
|
358
|
+
"context": {"company": "acme"},
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
@pytest.mark.asyncio
|
|
363
|
+
async def test_saved_workflow_instance_ensure_alias_and_stream_guard():
|
|
364
|
+
client = FakeClient()
|
|
365
|
+
namespace = WorkflowNamespace(client)
|
|
366
|
+
|
|
367
|
+
await namespace.ensure(
|
|
368
|
+
"wf-instance",
|
|
369
|
+
group="reports",
|
|
370
|
+
owner="company:acme",
|
|
371
|
+
mode="ensure_only",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
call = client.calls[0]
|
|
375
|
+
assert call["endpoint"] == "/api/workflow/workflows/wf-instance/instances/ensure"
|
|
376
|
+
assert call["body"] == {
|
|
377
|
+
"params": {
|
|
378
|
+
"group": "reports",
|
|
379
|
+
"owner": "company:acme",
|
|
380
|
+
},
|
|
381
|
+
"mode": "ensure_only",
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
with pytest.raises(ValueError, match="Use stream_ensure_instance\\(\\) for streaming instance execution"):
|
|
385
|
+
await namespace.ensure_instance(
|
|
386
|
+
"wf-instance",
|
|
387
|
+
group="reports",
|
|
388
|
+
owner="company:acme",
|
|
389
|
+
mode="streaming",
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
@pytest.mark.asyncio
|
|
394
|
+
async def test_stream_ensure_instance_uses_streaming_mode():
|
|
395
|
+
client = FakeClient(stream_chunks=[{"_event": "instance_ensured"}, {"_event": "workflow_complete"}])
|
|
396
|
+
namespace = WorkflowNamespace(client)
|
|
397
|
+
|
|
398
|
+
streamed = [
|
|
399
|
+
chunk
|
|
400
|
+
async for chunk in namespace.stream_ensure_instance(
|
|
401
|
+
"wf-instance",
|
|
402
|
+
group="reports",
|
|
403
|
+
owner="company:acme",
|
|
404
|
+
bindings={"subject": "PCM47474562"},
|
|
405
|
+
)
|
|
406
|
+
]
|
|
407
|
+
|
|
408
|
+
assert client.stream_calls == [
|
|
409
|
+
{
|
|
410
|
+
"endpoint": "/api/workflow/workflows/wf-instance/instances/ensure",
|
|
411
|
+
"body": {
|
|
412
|
+
"params": {
|
|
413
|
+
"group": "reports",
|
|
414
|
+
"owner": "company:acme",
|
|
415
|
+
},
|
|
416
|
+
"bindings": {"subject": "PCM47474562"},
|
|
417
|
+
"mode": "streaming",
|
|
418
|
+
},
|
|
419
|
+
"timeout": 300.0,
|
|
420
|
+
"use_gateway": True,
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
assert streamed == [{"_event": "instance_ensured"}, {"_event": "workflow_complete"}]
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@pytest.mark.asyncio
|
|
427
|
+
async def test_workflow_instance_read_routes_match_contract():
|
|
428
|
+
client = FakeClient()
|
|
429
|
+
namespace = WorkflowNamespace(client)
|
|
430
|
+
|
|
431
|
+
await namespace.get_instance_status("wf-instance", "inst/1")
|
|
432
|
+
await namespace.list_instances(
|
|
433
|
+
workflow_id="wf-instance",
|
|
434
|
+
group="reports",
|
|
435
|
+
owner="company:acme",
|
|
436
|
+
environment="production",
|
|
437
|
+
status="idle",
|
|
438
|
+
limit=25,
|
|
439
|
+
offset=5,
|
|
440
|
+
)
|
|
441
|
+
await namespace.get_instance_artifacts("wf-instance", "inst-1")
|
|
442
|
+
await namespace.get_instance_outputs("wf-instance", "inst-1")
|
|
443
|
+
await namespace.get_instance_history("wf-instance", "inst-1", limit=10, offset=2)
|
|
444
|
+
|
|
445
|
+
assert client.calls[0]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst%2F1/status"
|
|
446
|
+
assert client.calls[0]["method"] == "GET"
|
|
447
|
+
assert client.calls[1]["endpoint"] == (
|
|
448
|
+
"/api/workflow/instances"
|
|
449
|
+
"?limit=25&offset=5&workflow_id=wf-instance&group=reports&owner=company:acme"
|
|
450
|
+
"&environment=production&status=idle"
|
|
451
|
+
)
|
|
452
|
+
assert client.calls[1]["method"] == "GET"
|
|
453
|
+
assert client.calls[2]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-1/artifacts"
|
|
454
|
+
assert client.calls[2]["method"] == "GET"
|
|
455
|
+
assert client.calls[3]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-1/outputs"
|
|
456
|
+
assert client.calls[3]["method"] == "GET"
|
|
457
|
+
assert client.calls[4]["endpoint"] == (
|
|
458
|
+
"/api/workflow/workflows/wf-instance/instances/inst-1/history?limit=10&offset=2"
|
|
459
|
+
)
|
|
460
|
+
assert client.calls[4]["method"] == "GET"
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@pytest.mark.asyncio
|
|
464
|
+
async def test_workflow_instance_nudge_retry_and_cancel_contract():
|
|
465
|
+
client = FakeClient(stream_chunks=[{"_event": "instance_nudged"}])
|
|
466
|
+
namespace = WorkflowNamespace(client)
|
|
467
|
+
|
|
468
|
+
await namespace.nudge_instance(
|
|
469
|
+
"wf-instance",
|
|
470
|
+
"inst-1",
|
|
471
|
+
reason="artifact-updated",
|
|
472
|
+
trigger_artifact="report_snapshot",
|
|
473
|
+
mode="blocking",
|
|
474
|
+
)
|
|
475
|
+
await namespace.nudge(
|
|
476
|
+
"wf-instance",
|
|
477
|
+
"inst-2",
|
|
478
|
+
reason="manual-retry",
|
|
479
|
+
)
|
|
480
|
+
await namespace.retry_instance(
|
|
481
|
+
"wf-instance",
|
|
482
|
+
"inst-3",
|
|
483
|
+
trigger_artifact="report_snapshot",
|
|
484
|
+
mode="async",
|
|
485
|
+
)
|
|
486
|
+
await namespace.cancel_instance("wf-instance", "inst-4")
|
|
487
|
+
|
|
488
|
+
streamed = [
|
|
489
|
+
chunk
|
|
490
|
+
async for chunk in namespace.stream_nudge_instance(
|
|
491
|
+
"wf-instance",
|
|
492
|
+
"inst-5",
|
|
493
|
+
reason="artifact-updated",
|
|
494
|
+
trigger_artifact="report_snapshot",
|
|
495
|
+
)
|
|
496
|
+
]
|
|
497
|
+
|
|
498
|
+
assert client.calls[0]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-1/nudge"
|
|
499
|
+
assert client.calls[0]["body"] == {
|
|
500
|
+
"reason": "artifact-updated",
|
|
501
|
+
"trigger_artifact": "report_snapshot",
|
|
502
|
+
"mode": "blocking",
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
assert client.calls[1]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-2/nudge"
|
|
506
|
+
assert client.calls[1]["body"] == {
|
|
507
|
+
"reason": "manual-retry",
|
|
508
|
+
"mode": "async",
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
assert client.calls[2]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-3/nudge"
|
|
512
|
+
assert client.calls[2]["body"] == {
|
|
513
|
+
"reason": "retry",
|
|
514
|
+
"trigger_artifact": "report_snapshot",
|
|
515
|
+
"mode": "async",
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
assert client.calls[3]["endpoint"] == "/api/workflow/workflows/wf-instance/instances/inst-4/cancel"
|
|
519
|
+
assert client.calls[3]["body"] == {}
|
|
520
|
+
|
|
521
|
+
assert client.stream_calls == [
|
|
522
|
+
{
|
|
523
|
+
"endpoint": "/api/workflow/workflows/wf-instance/instances/inst-5/nudge",
|
|
524
|
+
"body": {
|
|
525
|
+
"reason": "artifact-updated",
|
|
526
|
+
"trigger_artifact": "report_snapshot",
|
|
527
|
+
"mode": "streaming",
|
|
528
|
+
},
|
|
529
|
+
"timeout": 300.0,
|
|
530
|
+
"use_gateway": True,
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
assert streamed == [{"_event": "instance_nudged"}]
|
|
534
|
+
|
|
535
|
+
with pytest.raises(ValueError, match="Use stream_nudge_instance\\(\\) for streaming instance execution"):
|
|
536
|
+
await namespace.nudge_instance(
|
|
537
|
+
"wf-instance",
|
|
538
|
+
"inst-1",
|
|
539
|
+
reason="artifact-updated",
|
|
540
|
+
mode="streaming",
|
|
541
|
+
)
|
|
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-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/__init__.py
RENAMED
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/audio_capture.py
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/session.py
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus/namespaces/oracle/vad_gate.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{dominus_sdk_python-2.16.0 → dominus_sdk_python-2.17.0}/dominus_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|