stabilize 0.9.2__py3-none-any.whl
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.
- stabilize/__init__.py +29 -0
- stabilize/cli.py +1193 -0
- stabilize/context/__init__.py +7 -0
- stabilize/context/stage_context.py +170 -0
- stabilize/dag/__init__.py +15 -0
- stabilize/dag/graph.py +215 -0
- stabilize/dag/topological.py +199 -0
- stabilize/examples/__init__.py +1 -0
- stabilize/examples/docker-example.py +759 -0
- stabilize/examples/golden-standard-expected-result.txt +1 -0
- stabilize/examples/golden-standard.py +488 -0
- stabilize/examples/http-example.py +606 -0
- stabilize/examples/llama-example.py +662 -0
- stabilize/examples/python-example.py +731 -0
- stabilize/examples/shell-example.py +399 -0
- stabilize/examples/ssh-example.py +603 -0
- stabilize/handlers/__init__.py +53 -0
- stabilize/handlers/base.py +226 -0
- stabilize/handlers/complete_stage.py +209 -0
- stabilize/handlers/complete_task.py +75 -0
- stabilize/handlers/complete_workflow.py +150 -0
- stabilize/handlers/run_task.py +369 -0
- stabilize/handlers/start_stage.py +262 -0
- stabilize/handlers/start_task.py +74 -0
- stabilize/handlers/start_workflow.py +136 -0
- stabilize/launcher.py +307 -0
- stabilize/migrations/01KDQ4N9QPJ6Q4MCV3V9GHWPV4_initial_schema.sql +97 -0
- stabilize/migrations/01KDRK3TXW4R2GERC1WBCQYJGG_rag_embeddings.sql +25 -0
- stabilize/migrations/__init__.py +1 -0
- stabilize/models/__init__.py +15 -0
- stabilize/models/stage.py +389 -0
- stabilize/models/status.py +146 -0
- stabilize/models/task.py +125 -0
- stabilize/models/workflow.py +317 -0
- stabilize/orchestrator.py +113 -0
- stabilize/persistence/__init__.py +28 -0
- stabilize/persistence/connection.py +185 -0
- stabilize/persistence/factory.py +136 -0
- stabilize/persistence/memory.py +214 -0
- stabilize/persistence/postgres.py +655 -0
- stabilize/persistence/sqlite.py +674 -0
- stabilize/persistence/store.py +235 -0
- stabilize/queue/__init__.py +59 -0
- stabilize/queue/messages.py +377 -0
- stabilize/queue/processor.py +312 -0
- stabilize/queue/queue.py +526 -0
- stabilize/queue/sqlite_queue.py +354 -0
- stabilize/rag/__init__.py +19 -0
- stabilize/rag/assistant.py +459 -0
- stabilize/rag/cache.py +294 -0
- stabilize/stages/__init__.py +11 -0
- stabilize/stages/builder.py +253 -0
- stabilize/tasks/__init__.py +19 -0
- stabilize/tasks/interface.py +335 -0
- stabilize/tasks/registry.py +255 -0
- stabilize/tasks/result.py +283 -0
- stabilize-0.9.2.dist-info/METADATA +301 -0
- stabilize-0.9.2.dist-info/RECORD +61 -0
- stabilize-0.9.2.dist-info/WHEEL +4 -0
- stabilize-0.9.2.dist-info/entry_points.txt +2 -0
- stabilize-0.9.2.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WorkflowStore interface.
|
|
3
|
+
|
|
4
|
+
This module defines the abstract interface for execution persistence.
|
|
5
|
+
All storage backends must implement this interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from stabilize.models.status import WorkflowStatus
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from stabilize.models.stage import StageExecution
|
|
19
|
+
from stabilize.models.workflow import Workflow
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkflowNotFoundError(Exception):
|
|
23
|
+
"""Raised when an execution cannot be found."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, execution_id: str):
|
|
26
|
+
self.execution_id = execution_id
|
|
27
|
+
super().__init__(f"Execution not found: {execution_id}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class WorkflowCriteria:
|
|
32
|
+
"""Criteria for querying executions."""
|
|
33
|
+
|
|
34
|
+
page_size: int = 20
|
|
35
|
+
statuses: set[WorkflowStatus] | None = None
|
|
36
|
+
start_time_before: int | None = None
|
|
37
|
+
start_time_after: int | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class WorkflowStore(ABC):
|
|
41
|
+
"""Abstract interface for execution persistence."""
|
|
42
|
+
|
|
43
|
+
# ========== Execution Operations ==========
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def store(self, execution: Workflow) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Store a complete execution.
|
|
49
|
+
|
|
50
|
+
Creates the execution and all its stages.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
execution: The execution to store
|
|
54
|
+
"""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def retrieve(self, execution_id: str) -> Workflow:
|
|
59
|
+
"""
|
|
60
|
+
Retrieve an execution by ID.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
execution_id: The execution ID
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The execution
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
WorkflowNotFoundError: If not found
|
|
70
|
+
"""
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def update_status(self, execution: Workflow) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Update the status of an execution.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
execution: The execution with updated status
|
|
80
|
+
"""
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def delete(self, execution_id: str) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Delete an execution and all its stages.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
execution_id: The execution ID
|
|
90
|
+
"""
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
# ========== Stage Operations ==========
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def store_stage(self, stage: StageExecution) -> None:
|
|
97
|
+
"""
|
|
98
|
+
Store or update a stage.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
stage: The stage to store
|
|
102
|
+
"""
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
@abstractmethod
|
|
106
|
+
def add_stage(self, stage: StageExecution) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Add a new stage to an execution.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
stage: The stage to add
|
|
112
|
+
"""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
@abstractmethod
|
|
116
|
+
def remove_stage(
|
|
117
|
+
self,
|
|
118
|
+
execution: Workflow,
|
|
119
|
+
stage_id: str,
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Remove a stage from an execution.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
execution: The execution
|
|
126
|
+
stage_id: The stage ID to remove
|
|
127
|
+
"""
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
# ========== Query Operations ==========
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def retrieve_by_pipeline_config_id(
|
|
134
|
+
self,
|
|
135
|
+
pipeline_config_id: str,
|
|
136
|
+
criteria: WorkflowCriteria | None = None,
|
|
137
|
+
) -> Iterator[Workflow]:
|
|
138
|
+
"""
|
|
139
|
+
Retrieve executions by pipeline config ID.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
pipeline_config_id: The pipeline config ID
|
|
143
|
+
criteria: Optional query criteria
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Iterator of matching executions
|
|
147
|
+
"""
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
@abstractmethod
|
|
151
|
+
def retrieve_by_application(
|
|
152
|
+
self,
|
|
153
|
+
application: str,
|
|
154
|
+
criteria: WorkflowCriteria | None = None,
|
|
155
|
+
) -> Iterator[Workflow]:
|
|
156
|
+
"""
|
|
157
|
+
Retrieve executions by application.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
application: The application name
|
|
161
|
+
criteria: Optional query criteria
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Iterator of matching executions
|
|
165
|
+
"""
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
# ========== Pause/Resume Operations ==========
|
|
169
|
+
|
|
170
|
+
@abstractmethod
|
|
171
|
+
def pause(
|
|
172
|
+
self,
|
|
173
|
+
execution_id: str,
|
|
174
|
+
paused_by: str,
|
|
175
|
+
) -> None:
|
|
176
|
+
"""
|
|
177
|
+
Pause an execution.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
execution_id: The execution ID
|
|
181
|
+
paused_by: Who paused it
|
|
182
|
+
"""
|
|
183
|
+
pass
|
|
184
|
+
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def resume(self, execution_id: str) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Resume a paused execution.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
execution_id: The execution ID
|
|
192
|
+
"""
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
# ========== Cancel Operations ==========
|
|
196
|
+
|
|
197
|
+
@abstractmethod
|
|
198
|
+
def cancel(
|
|
199
|
+
self,
|
|
200
|
+
execution_id: str,
|
|
201
|
+
canceled_by: str,
|
|
202
|
+
reason: str,
|
|
203
|
+
) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Cancel an execution.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
execution_id: The execution ID
|
|
209
|
+
canceled_by: Who canceled it
|
|
210
|
+
reason: Cancellation reason
|
|
211
|
+
"""
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
# ========== Optional Methods ==========
|
|
215
|
+
|
|
216
|
+
def is_healthy(self) -> bool:
|
|
217
|
+
"""
|
|
218
|
+
Check if the repository is healthy.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
True if healthy
|
|
222
|
+
"""
|
|
223
|
+
return True
|
|
224
|
+
|
|
225
|
+
def count_by_application(self, application: str) -> int:
|
|
226
|
+
"""
|
|
227
|
+
Count executions for an application.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
application: The application name
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Number of executions
|
|
234
|
+
"""
|
|
235
|
+
return sum(1 for _ in self.retrieve_by_application(application))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Message queue system for pipeline execution."""
|
|
2
|
+
|
|
3
|
+
from stabilize.queue.messages import (
|
|
4
|
+
CancelStage,
|
|
5
|
+
CancelWorkflow,
|
|
6
|
+
CompleteStage,
|
|
7
|
+
CompleteTask,
|
|
8
|
+
CompleteWorkflow,
|
|
9
|
+
ContinueParentStage,
|
|
10
|
+
InvalidStageId,
|
|
11
|
+
InvalidTaskId,
|
|
12
|
+
InvalidTaskType,
|
|
13
|
+
InvalidWorkflowId,
|
|
14
|
+
Message,
|
|
15
|
+
PauseTask,
|
|
16
|
+
RestartStage,
|
|
17
|
+
ResumeStage,
|
|
18
|
+
RunTask,
|
|
19
|
+
SkipStage,
|
|
20
|
+
StageLevel,
|
|
21
|
+
StartStage,
|
|
22
|
+
StartTask,
|
|
23
|
+
StartWorkflow,
|
|
24
|
+
TaskLevel,
|
|
25
|
+
WorkflowLevel,
|
|
26
|
+
)
|
|
27
|
+
from stabilize.queue.queue import InMemoryQueue, PostgresQueue, Queue
|
|
28
|
+
from stabilize.queue.sqlite_queue import SqliteQueue
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
# Message types
|
|
32
|
+
"Message",
|
|
33
|
+
"WorkflowLevel",
|
|
34
|
+
"StageLevel",
|
|
35
|
+
"TaskLevel",
|
|
36
|
+
"StartWorkflow",
|
|
37
|
+
"StartStage",
|
|
38
|
+
"StartTask",
|
|
39
|
+
"RunTask",
|
|
40
|
+
"CompleteTask",
|
|
41
|
+
"CompleteStage",
|
|
42
|
+
"CompleteWorkflow",
|
|
43
|
+
"CancelWorkflow",
|
|
44
|
+
"CancelStage",
|
|
45
|
+
"SkipStage",
|
|
46
|
+
"PauseTask",
|
|
47
|
+
"ResumeStage",
|
|
48
|
+
"ContinueParentStage",
|
|
49
|
+
"RestartStage",
|
|
50
|
+
"InvalidWorkflowId",
|
|
51
|
+
"InvalidStageId",
|
|
52
|
+
"InvalidTaskId",
|
|
53
|
+
"InvalidTaskType",
|
|
54
|
+
# Queue implementations
|
|
55
|
+
"Queue",
|
|
56
|
+
"InMemoryQueue",
|
|
57
|
+
"PostgresQueue",
|
|
58
|
+
"SqliteQueue",
|
|
59
|
+
]
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Message types for the queue-based execution engine.
|
|
3
|
+
|
|
4
|
+
This module defines all message types used in the pipeline execution queue.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from stabilize.models.stage import SyntheticStageOwner
|
|
14
|
+
from stabilize.models.status import WorkflowStatus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Message:
|
|
19
|
+
"""
|
|
20
|
+
Base class for all queue messages.
|
|
21
|
+
|
|
22
|
+
Each message includes metadata for tracking and debugging.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
# Message metadata
|
|
26
|
+
message_id: str | None = field(default=None, repr=False)
|
|
27
|
+
created_at: datetime = field(default_factory=datetime.now, repr=False)
|
|
28
|
+
attempts: int = field(default=0, repr=False)
|
|
29
|
+
max_attempts: int = field(default=10, repr=False)
|
|
30
|
+
|
|
31
|
+
def copy_with_attempts(self, attempts: int) -> Message:
|
|
32
|
+
"""Create a copy with updated attempt count."""
|
|
33
|
+
import copy
|
|
34
|
+
|
|
35
|
+
new_msg = copy.copy(self)
|
|
36
|
+
new_msg.attempts = attempts
|
|
37
|
+
return new_msg
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ============================================================================
|
|
41
|
+
# Execution-level messages
|
|
42
|
+
# ============================================================================
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class WorkflowLevel(Message):
|
|
47
|
+
"""
|
|
48
|
+
Base class for execution-level messages.
|
|
49
|
+
|
|
50
|
+
These messages target a specific pipeline execution.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
execution_type: str = "PIPELINE"
|
|
54
|
+
execution_id: str = ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class StartWorkflow(WorkflowLevel):
|
|
59
|
+
"""
|
|
60
|
+
Message to start a pipeline execution.
|
|
61
|
+
|
|
62
|
+
Triggers the beginning of pipeline execution, starting initial stages.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class CompleteWorkflow(WorkflowLevel):
|
|
70
|
+
"""
|
|
71
|
+
Message to complete a pipeline execution.
|
|
72
|
+
|
|
73
|
+
Sent when all stages have completed or execution should be finalized.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class CancelWorkflow(WorkflowLevel):
|
|
81
|
+
"""
|
|
82
|
+
Message to cancel a pipeline execution.
|
|
83
|
+
|
|
84
|
+
Marks the execution as canceled and stops all running stages.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
user: str = ""
|
|
88
|
+
reason: str = ""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class StartWaitingWorkflows(Message):
|
|
93
|
+
"""
|
|
94
|
+
Message to start any queued/waiting executions for a pipeline config.
|
|
95
|
+
|
|
96
|
+
Sent after an execution completes when concurrent execution limits are enabled.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
pipeline_config_id: str = ""
|
|
100
|
+
purge_queue: bool = False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ============================================================================
|
|
104
|
+
# Stage-level messages
|
|
105
|
+
# ============================================================================
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class StageLevel(WorkflowLevel):
|
|
110
|
+
"""
|
|
111
|
+
Base class for stage-level messages.
|
|
112
|
+
|
|
113
|
+
These messages target a specific stage within an execution.
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
stage_id: str = ""
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_execution_level(cls, msg: WorkflowLevel, stage_id: str) -> StageLevel:
|
|
120
|
+
"""Create a stage-level message from an execution-level message."""
|
|
121
|
+
return cls(
|
|
122
|
+
execution_type=msg.execution_type,
|
|
123
|
+
execution_id=msg.execution_id,
|
|
124
|
+
stage_id=stage_id,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class StartStage(StageLevel):
|
|
130
|
+
"""
|
|
131
|
+
Message to start a stage.
|
|
132
|
+
|
|
133
|
+
Checks if upstream stages are complete, then plans and starts the stage.
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass
|
|
140
|
+
class CompleteStage(StageLevel):
|
|
141
|
+
"""
|
|
142
|
+
Message to complete a stage.
|
|
143
|
+
|
|
144
|
+
Determines stage status, plans after stages, and triggers downstream.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class SkipStage(StageLevel):
|
|
152
|
+
"""
|
|
153
|
+
Message to skip a stage.
|
|
154
|
+
|
|
155
|
+
Sets stage status to SKIPPED and triggers downstream stages.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass
|
|
162
|
+
class CancelStage(StageLevel):
|
|
163
|
+
"""
|
|
164
|
+
Message to cancel a stage.
|
|
165
|
+
|
|
166
|
+
Cancels any running tasks and marks stage as canceled.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass
|
|
173
|
+
class RestartStage(StageLevel):
|
|
174
|
+
"""
|
|
175
|
+
Message to restart a stage.
|
|
176
|
+
|
|
177
|
+
Resets stage status and re-executes from the beginning.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass
|
|
184
|
+
class ResumeStage(StageLevel):
|
|
185
|
+
"""
|
|
186
|
+
Message to resume a paused stage.
|
|
187
|
+
|
|
188
|
+
Continues execution from where it was paused.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class ContinueParentStage(StageLevel):
|
|
196
|
+
"""
|
|
197
|
+
Message to continue parent stage after synthetic stage completes.
|
|
198
|
+
|
|
199
|
+
Sent when a synthetic stage completes to notify its parent.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
phase: SyntheticStageOwner = SyntheticStageOwner.STAGE_AFTER
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# ============================================================================
|
|
206
|
+
# Task-level messages
|
|
207
|
+
# ============================================================================
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass
|
|
211
|
+
class TaskLevel(StageLevel):
|
|
212
|
+
"""
|
|
213
|
+
Base class for task-level messages.
|
|
214
|
+
|
|
215
|
+
These messages target a specific task within a stage.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
task_id: str = ""
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def from_stage_level(cls, msg: StageLevel, task_id: str) -> TaskLevel:
|
|
222
|
+
"""Create a task-level message from a stage-level message."""
|
|
223
|
+
return cls(
|
|
224
|
+
execution_type=msg.execution_type,
|
|
225
|
+
execution_id=msg.execution_id,
|
|
226
|
+
stage_id=msg.stage_id,
|
|
227
|
+
task_id=task_id,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@dataclass
|
|
232
|
+
class StartTask(TaskLevel):
|
|
233
|
+
"""
|
|
234
|
+
Message to start a task.
|
|
235
|
+
|
|
236
|
+
Sets task status to RUNNING and triggers RunTask.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@dataclass
|
|
243
|
+
class RunTask(TaskLevel):
|
|
244
|
+
"""
|
|
245
|
+
Message to execute a task.
|
|
246
|
+
|
|
247
|
+
Runs the task implementation and handles the result.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
task_type: str = ""
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@dataclass
|
|
254
|
+
class CompleteTask(TaskLevel):
|
|
255
|
+
"""
|
|
256
|
+
Message to complete a task.
|
|
257
|
+
|
|
258
|
+
Updates task status and triggers next task or stage completion.
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
status: WorkflowStatus = WorkflowStatus.SUCCEEDED
|
|
262
|
+
original_status: WorkflowStatus | None = None
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@dataclass
|
|
266
|
+
class PauseTask(TaskLevel):
|
|
267
|
+
"""
|
|
268
|
+
Message to pause a task.
|
|
269
|
+
|
|
270
|
+
Used when execution is paused - task will resume when execution resumes.
|
|
271
|
+
"""
|
|
272
|
+
|
|
273
|
+
pass
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ============================================================================
|
|
277
|
+
# Error messages
|
|
278
|
+
# ============================================================================
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dataclass
|
|
282
|
+
class InvalidWorkflowId(WorkflowLevel):
|
|
283
|
+
"""
|
|
284
|
+
Message indicating an invalid execution ID was referenced.
|
|
285
|
+
|
|
286
|
+
Logged and dropped - no further processing.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
pass
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@dataclass
|
|
293
|
+
class InvalidStageId(StageLevel):
|
|
294
|
+
"""
|
|
295
|
+
Message indicating an invalid stage ID was referenced.
|
|
296
|
+
|
|
297
|
+
Logged and dropped - no further processing.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
pass
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@dataclass
|
|
304
|
+
class InvalidTaskId(TaskLevel):
|
|
305
|
+
"""
|
|
306
|
+
Message indicating an invalid task ID was referenced.
|
|
307
|
+
|
|
308
|
+
Logged and dropped - no further processing.
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
pass
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@dataclass
|
|
315
|
+
class InvalidTaskType(TaskLevel):
|
|
316
|
+
"""
|
|
317
|
+
Message indicating an unknown task type was referenced.
|
|
318
|
+
|
|
319
|
+
Logged and dropped - no further processing.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
task_type_name: str = ""
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# ============================================================================
|
|
326
|
+
# Message Registry
|
|
327
|
+
# ============================================================================
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# Map of message type names to classes for deserialization
|
|
331
|
+
MESSAGE_TYPES: dict[str, type[Message]] = {
|
|
332
|
+
"StartWorkflow": StartWorkflow,
|
|
333
|
+
"CompleteWorkflow": CompleteWorkflow,
|
|
334
|
+
"CancelWorkflow": CancelWorkflow,
|
|
335
|
+
"StartWaitingWorkflows": StartWaitingWorkflows,
|
|
336
|
+
"StartStage": StartStage,
|
|
337
|
+
"CompleteStage": CompleteStage,
|
|
338
|
+
"SkipStage": SkipStage,
|
|
339
|
+
"CancelStage": CancelStage,
|
|
340
|
+
"RestartStage": RestartStage,
|
|
341
|
+
"ResumeStage": ResumeStage,
|
|
342
|
+
"ContinueParentStage": ContinueParentStage,
|
|
343
|
+
"StartTask": StartTask,
|
|
344
|
+
"RunTask": RunTask,
|
|
345
|
+
"CompleteTask": CompleteTask,
|
|
346
|
+
"PauseTask": PauseTask,
|
|
347
|
+
"InvalidWorkflowId": InvalidWorkflowId,
|
|
348
|
+
"InvalidStageId": InvalidStageId,
|
|
349
|
+
"InvalidTaskId": InvalidTaskId,
|
|
350
|
+
"InvalidTaskType": InvalidTaskType,
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def get_message_type_name(message: Message) -> str:
|
|
355
|
+
"""Get the type name for a message."""
|
|
356
|
+
return message.__class__.__name__
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def create_message_from_dict(type_name: str, data: dict[str, Any]) -> Message:
|
|
360
|
+
"""
|
|
361
|
+
Create a message from a dictionary representation.
|
|
362
|
+
|
|
363
|
+
Args:
|
|
364
|
+
type_name: The message type name
|
|
365
|
+
data: Dictionary of message fields
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
A Message instance
|
|
369
|
+
|
|
370
|
+
Raises:
|
|
371
|
+
ValueError: If type_name is unknown
|
|
372
|
+
"""
|
|
373
|
+
if type_name not in MESSAGE_TYPES:
|
|
374
|
+
raise ValueError(f"Unknown message type: {type_name}")
|
|
375
|
+
|
|
376
|
+
message_class = MESSAGE_TYPES[type_name]
|
|
377
|
+
return message_class(**data)
|