digitalkin 0.2.23__py3-none-any.whl → 0.3.1.dev2__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.
- digitalkin/__version__.py +1 -1
- digitalkin/core/__init__.py +1 -0
- digitalkin/core/common/__init__.py +9 -0
- digitalkin/core/common/factories.py +156 -0
- digitalkin/core/job_manager/__init__.py +1 -0
- digitalkin/{modules → core}/job_manager/base_job_manager.py +137 -31
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/{modules → core}/job_manager/taskiq_broker.py +116 -22
- digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
- digitalkin/core/task_manager/__init__.py +1 -0
- digitalkin/core/task_manager/base_task_manager.py +539 -0
- digitalkin/core/task_manager/local_task_manager.py +108 -0
- digitalkin/core/task_manager/remote_task_manager.py +87 -0
- digitalkin/core/task_manager/surrealdb_repository.py +266 -0
- digitalkin/core/task_manager/task_executor.py +249 -0
- digitalkin/core/task_manager/task_session.py +406 -0
- digitalkin/grpc_servers/__init__.py +1 -19
- digitalkin/grpc_servers/_base_server.py +3 -3
- digitalkin/grpc_servers/module_server.py +27 -43
- digitalkin/grpc_servers/module_servicer.py +51 -36
- digitalkin/grpc_servers/registry_server.py +2 -2
- digitalkin/grpc_servers/registry_servicer.py +4 -4
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +0 -8
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +4 -4
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/logger.py +73 -24
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/{modules/job_manager → models/core}/job_manager_models.py +3 -3
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/{grpc_servers/utils → models/grpc_servers}/models.py +5 -5
- digitalkin/models/module/__init__.py +2 -0
- digitalkin/models/module/module.py +9 -1
- digitalkin/models/module/module_context.py +122 -6
- digitalkin/models/module/module_types.py +307 -19
- digitalkin/models/services/__init__.py +9 -0
- digitalkin/models/services/cost.py +1 -0
- digitalkin/models/services/storage.py +39 -5
- digitalkin/modules/_base_module.py +123 -118
- digitalkin/modules/tool_module.py +10 -2
- digitalkin/modules/trigger_handler.py +7 -6
- digitalkin/services/cost/__init__.py +9 -2
- digitalkin/services/cost/grpc_cost.py +9 -42
- digitalkin/services/filesystem/default_filesystem.py +0 -2
- digitalkin/services/filesystem/grpc_filesystem.py +10 -39
- digitalkin/services/setup/default_setup.py +5 -6
- digitalkin/services/setup/grpc_setup.py +52 -15
- digitalkin/services/storage/grpc_storage.py +4 -4
- digitalkin/services/user_profile/__init__.py +1 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +40 -0
- digitalkin/utils/__init__.py +28 -0
- digitalkin/utils/arg_parser.py +1 -1
- digitalkin/utils/development_mode_action.py +2 -2
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/package_discover.py +1 -2
- {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/METADATA +11 -30
- digitalkin-0.3.1.dev2.dist-info/RECORD +119 -0
- modules/dynamic_setup_module.py +362 -0
- digitalkin/grpc_servers/utils/factory.py +0 -180
- digitalkin/modules/job_manager/single_job_manager.py +0 -294
- digitalkin/modules/job_manager/taskiq_job_manager.py +0 -290
- digitalkin-0.2.23.dist-info/RECORD +0 -89
- /digitalkin/{grpc_servers/utils → models/grpc_servers}/types.py +0 -0
- {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Cost Mixin to ease trigger deveolpment."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
6
|
+
from digitalkin.services.cost.cost_strategy import CostData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CostMixin:
|
|
10
|
+
"""Mixin providing cost tracking operations through the cost strategy.
|
|
11
|
+
|
|
12
|
+
This mixin wraps cost strategy calls to provide a cleaner API
|
|
13
|
+
for cost tracking in trigger handlers.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def add_cost(context: ModuleContext, name: str, cost_config_name: str, quantity: float) -> None:
|
|
18
|
+
"""Add a cost entry using the cost strategy.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
context: Module context containing the cost strategy
|
|
22
|
+
name: Name/identifier for this cost entry
|
|
23
|
+
cost_config_name: Name of the cost configuration to use
|
|
24
|
+
quantity: Quantity of units consumed
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
CostServiceError: If cost addition fails
|
|
28
|
+
"""
|
|
29
|
+
return context.cost.add(name, cost_config_name, quantity)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def get_cost(context: ModuleContext, name: str) -> list[CostData]:
|
|
33
|
+
"""Get cost entries for a specific name.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
context: Module context containing the cost strategy
|
|
37
|
+
name: Name/identifier to get costs for
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
List of cost data entries
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
CostServiceError: If cost retrieval fails
|
|
44
|
+
"""
|
|
45
|
+
return context.cost.get(name)
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def get_costs(
|
|
49
|
+
context: ModuleContext,
|
|
50
|
+
names: list[str] | None = None,
|
|
51
|
+
cost_types: list[
|
|
52
|
+
Literal[
|
|
53
|
+
"TOKEN_INPUT",
|
|
54
|
+
"TOKEN_OUTPUT",
|
|
55
|
+
"API_CALL",
|
|
56
|
+
"STORAGE",
|
|
57
|
+
"TIME",
|
|
58
|
+
"OTHER",
|
|
59
|
+
]
|
|
60
|
+
]
|
|
61
|
+
| None = None,
|
|
62
|
+
) -> list[CostData]:
|
|
63
|
+
"""Get filtered cost entries.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
context: Module context containing the cost strategy
|
|
67
|
+
names: Optional list of names to filter by
|
|
68
|
+
cost_types: Optional list of cost types to filter by
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
List of filtered cost data entries
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
CostServiceError: If cost retrieval fails
|
|
75
|
+
"""
|
|
76
|
+
return context.cost.get_filtered(names, cost_types)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Context mixins providing ergonomic access to service strategies.
|
|
2
|
+
|
|
3
|
+
This module provides mixins that wrap service strategy calls with cleaner APIs,
|
|
4
|
+
following Django/FastAPI patterns where context is passed explicitly to each method.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from digitalkin.mixins.logger_mixin import LoggerMixin
|
|
8
|
+
from digitalkin.mixins.storage_mixin import StorageMixin
|
|
9
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
10
|
+
from digitalkin.models.services.storage import FileHistory, FileModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FileHistoryMixin(StorageMixin, LoggerMixin):
|
|
14
|
+
"""Mixin providing File history operations through storage strategy.
|
|
15
|
+
|
|
16
|
+
This mixin provides a higher-level API for managing File history,
|
|
17
|
+
using the storage strategy as the underlying persistence mechanism.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
file_history_front: FileHistory = FileHistory(files=[])
|
|
21
|
+
FILE_HISTORY_COLLECTION = "file_history"
|
|
22
|
+
FILE_HISTORY_RECORD_ID = "full_file_history"
|
|
23
|
+
|
|
24
|
+
def _get_history_key(self, context: ModuleContext) -> str:
|
|
25
|
+
"""Get session-specific history key.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
context: Module context containing session information
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Unique history key for the current session
|
|
32
|
+
"""
|
|
33
|
+
# TODO: define mission-specific chat history key not dependant on mission_id
|
|
34
|
+
# or need customization by user
|
|
35
|
+
mission_id = getattr(context.session, "mission_id", None) or "default"
|
|
36
|
+
return f"{self.FILE_HISTORY_RECORD_ID}_{mission_id}"
|
|
37
|
+
|
|
38
|
+
def load_file_history(self, context: ModuleContext) -> FileHistory:
|
|
39
|
+
"""Load File history for the current session.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
context: Module context containing storage strategy
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
File history object, empty if none exists or loading fails
|
|
46
|
+
"""
|
|
47
|
+
history_key = self._get_history_key(context)
|
|
48
|
+
|
|
49
|
+
if self.file_history_front is None:
|
|
50
|
+
try:
|
|
51
|
+
record = self.read_storage(
|
|
52
|
+
context,
|
|
53
|
+
self.FILE_HISTORY_COLLECTION,
|
|
54
|
+
history_key,
|
|
55
|
+
)
|
|
56
|
+
if record and record.data:
|
|
57
|
+
return FileHistory.model_validate(record.data)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
self.log_warning(context, f"Failed to load File history: {e}")
|
|
60
|
+
return self.file_history_front
|
|
61
|
+
|
|
62
|
+
def append_files_history(self, context: ModuleContext, files: list[FileModel]) -> None:
|
|
63
|
+
"""Append a message to File history.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
context: Module context containing storage strategy
|
|
67
|
+
files: list of files model
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
StorageServiceError: If history update fails
|
|
71
|
+
"""
|
|
72
|
+
history_key = self._get_history_key(context)
|
|
73
|
+
file_history = self.load_file_history(context)
|
|
74
|
+
|
|
75
|
+
file_history.files.extend(files)
|
|
76
|
+
if len(file_history.files) == len(files):
|
|
77
|
+
# Create new record
|
|
78
|
+
self.log_debug(context, f"Creating new file history for session: {history_key}")
|
|
79
|
+
self.store_storage(
|
|
80
|
+
context,
|
|
81
|
+
self.FILE_HISTORY_COLLECTION,
|
|
82
|
+
history_key,
|
|
83
|
+
file_history.model_dump(),
|
|
84
|
+
data_type="OUTPUT",
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
self.log_debug(context, f"Updating file history for session: {history_key}")
|
|
88
|
+
self.update_storage(
|
|
89
|
+
context,
|
|
90
|
+
self.FILE_HISTORY_COLLECTION,
|
|
91
|
+
history_key,
|
|
92
|
+
file_history.model_dump(),
|
|
93
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Filesystem Mixin to ease filesystem use."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
6
|
+
from digitalkin.services.filesystem.filesystem_strategy import FilesystemRecord
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FilesystemMixin:
|
|
10
|
+
"""Mixin providing filesystem operations through the filesystem strategy.
|
|
11
|
+
|
|
12
|
+
This mixin wraps filesystem strategy calls to provide a cleaner API
|
|
13
|
+
for file operations in trigger handlers.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def upload_files(context: ModuleContext, files: list[Any]) -> tuple[list[FilesystemRecord], int, int]:
|
|
18
|
+
"""Upload files using the filesystem strategy.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
context: Module context containing the filesystem strategy
|
|
22
|
+
files: List of files to upload
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Tuple of (all_files, succeeded_files, failed_files)
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
FilesystemServiceError: If upload operation fails
|
|
29
|
+
"""
|
|
30
|
+
return context.filesystem.upload_files(files)
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def get_file(context: ModuleContext, file_id: str) -> FilesystemRecord:
|
|
34
|
+
"""Retrieve a file by ID with the content.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
context: Module context containing the filesystem strategy
|
|
38
|
+
file_id: Unique identifier for the file
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
File object with metadata and optionally content
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
FilesystemServiceError: If file retrieval fails
|
|
45
|
+
"""
|
|
46
|
+
return context.filesystem.get_file(file_id, include_content=True)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Logger Mixin to ease and merge every logs."""
|
|
2
|
+
|
|
3
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LoggerMixin:
|
|
7
|
+
"""Mixin providing callback operations through the callbacks strategy.
|
|
8
|
+
|
|
9
|
+
This mixin wraps callback strategy calls to provide a cleaner API
|
|
10
|
+
for logging and messaging in trigger handlers.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def log_debug(context: ModuleContext, message: str) -> None:
|
|
15
|
+
"""Log debug message using the callbacks strategy.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
context: Module context containing the callbacks strategy
|
|
19
|
+
message: Debug message to log
|
|
20
|
+
"""
|
|
21
|
+
return context.callbacks.logger.debug(message, extra=context.session.current_ids())
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def log_info(context: ModuleContext, message: str) -> None:
|
|
25
|
+
"""Log info message using the callbacks strategy.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
context: Module context containing the callbacks strategy
|
|
29
|
+
message: Info message to log
|
|
30
|
+
"""
|
|
31
|
+
return context.callbacks.logger.info(message, extra=context.session.current_ids())
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def log_warning(context: ModuleContext, message: str) -> None:
|
|
35
|
+
"""Log warning message using the callbacks strategy.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
context: Module context containing the callbacks strategy
|
|
39
|
+
message: Warning message to log
|
|
40
|
+
"""
|
|
41
|
+
return context.callbacks.logger.warning(message, extra=context.session.current_ids())
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def log_error(context: ModuleContext, message: str) -> None:
|
|
45
|
+
"""Log error message using the callbacks strategy.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
context: Module context containing the callbacks strategy
|
|
49
|
+
message: Error message to log
|
|
50
|
+
"""
|
|
51
|
+
return context.callbacks.logger.error(message, extra=context.session.current_ids())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Storage Mixin to ease storage access in Triggers."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from digitalkin.models.module.module_context import ModuleContext
|
|
6
|
+
from digitalkin.services.storage.storage_strategy import StorageRecord
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StorageMixin:
|
|
10
|
+
"""Mixin providing storage operations through the storage strategy.
|
|
11
|
+
|
|
12
|
+
This mixin wraps storage strategy calls to provide a cleaner API
|
|
13
|
+
for trigger handlers.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def store_storage(
|
|
18
|
+
context: ModuleContext,
|
|
19
|
+
collection: str,
|
|
20
|
+
record_id: str | None,
|
|
21
|
+
data: dict[str, Any],
|
|
22
|
+
data_type: Literal["OUTPUT", "VIEW", "LOGS", "OTHER"] = "OUTPUT",
|
|
23
|
+
) -> StorageRecord:
|
|
24
|
+
"""Store data using the storage strategy.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
context: Module context containing the storage strategy
|
|
28
|
+
collection: Collection name for the data
|
|
29
|
+
record_id: Optional record identifier
|
|
30
|
+
data: Data to store
|
|
31
|
+
data_type: Type of data being stored
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Result from the storage strategy
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
StorageServiceError: If storage operation fails
|
|
38
|
+
"""
|
|
39
|
+
return context.storage.store(collection, record_id, data, data_type=data_type)
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def read_storage(context: ModuleContext, collection: str, record_id: str) -> StorageRecord | None:
|
|
43
|
+
"""Read data from storage.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
context: Module context containing the storage strategy
|
|
47
|
+
collection: Collection name
|
|
48
|
+
record_id: Record identifier
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Retrieved data
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
StorageServiceError: If read operation fails
|
|
55
|
+
"""
|
|
56
|
+
return context.storage.read(collection, record_id)
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def update_storage(
|
|
60
|
+
context: ModuleContext,
|
|
61
|
+
collection: str,
|
|
62
|
+
record_id: str,
|
|
63
|
+
data: dict[str, Any],
|
|
64
|
+
) -> StorageRecord | None:
|
|
65
|
+
"""Update existing data in storage.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
context: Module context containing the storage strategy
|
|
69
|
+
collection: Collection name
|
|
70
|
+
record_id: Record identifier
|
|
71
|
+
data: Updated data
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Result from the storage strategy
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
StorageServiceError: If update operation fails
|
|
78
|
+
"""
|
|
79
|
+
return context.storage.update(collection, record_id, data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core models."""
|
|
@@ -4,7 +4,7 @@ from enum import Enum
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
-
from digitalkin.
|
|
7
|
+
from digitalkin.core.job_manager.base_job_manager import BaseJobManager
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class StreamCodeModel(BaseModel):
|
|
@@ -35,10 +35,10 @@ class JobManagerMode(Enum):
|
|
|
35
35
|
"""
|
|
36
36
|
match self:
|
|
37
37
|
case JobManagerMode.SINGLE:
|
|
38
|
-
from digitalkin.
|
|
38
|
+
from digitalkin.core.job_manager.single_job_manager import SingleJobManager # noqa: PLC0415
|
|
39
39
|
|
|
40
40
|
return SingleJobManager
|
|
41
41
|
case JobManagerMode.TASKIQ:
|
|
42
|
-
from digitalkin.
|
|
42
|
+
from digitalkin.core.job_manager.taskiq_job_manager import TaskiqJobManager # noqa: PLC0415
|
|
43
43
|
|
|
44
44
|
return TaskiqJobManager
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Task monitoring models for signaling and heartbeat messages."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TaskStatus(Enum):
|
|
11
|
+
"""Task status enumeration."""
|
|
12
|
+
|
|
13
|
+
PENDING = "pending"
|
|
14
|
+
RUNNING = "running"
|
|
15
|
+
CANCELLED = "cancelled"
|
|
16
|
+
COMPLETED = "completed"
|
|
17
|
+
FAILED = "failed"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CancellationReason(Enum):
|
|
21
|
+
"""Reason for task cancellation - helps distinguish cleanup vs real cancellation."""
|
|
22
|
+
|
|
23
|
+
# Cleanup cancellations (not errors)
|
|
24
|
+
SUCCESS_CLEANUP = "success_cleanup" # Main task completed, cleaning up helper tasks
|
|
25
|
+
FAILURE_CLEANUP = "failure_cleanup" # Main task failed, cleaning up helper tasks
|
|
26
|
+
|
|
27
|
+
# Real cancellations
|
|
28
|
+
SIGNAL = "signal" # External signal requested cancellation
|
|
29
|
+
HEARTBEAT_FAILURE = "heartbeat_failure" # Heartbeat stopped working
|
|
30
|
+
TIMEOUT = "timeout" # Task timed out
|
|
31
|
+
SHUTDOWN = "shutdown" # Manager is shutting down
|
|
32
|
+
|
|
33
|
+
# Unknown/unset
|
|
34
|
+
UNKNOWN = "unknown" # Reason not determined
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SignalType(Enum):
|
|
38
|
+
"""Signal type enumeration."""
|
|
39
|
+
|
|
40
|
+
START = "start"
|
|
41
|
+
STOP = "stop"
|
|
42
|
+
CANCEL = "cancel"
|
|
43
|
+
PAUSE = "pause"
|
|
44
|
+
RESUME = "resume"
|
|
45
|
+
STATUS = "status"
|
|
46
|
+
|
|
47
|
+
ACK_CANCEL = "ack_cancel"
|
|
48
|
+
ACK_PAUSE = "ack_pause"
|
|
49
|
+
ACK_RESUME = "ack_resume"
|
|
50
|
+
ACK_STATUS = "ack_status"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SignalMessage(BaseModel):
|
|
54
|
+
"""Signal message model for task monitoring."""
|
|
55
|
+
|
|
56
|
+
task_id: str = Field(..., description="Unique identifier for the task")
|
|
57
|
+
mission_id: str = Field(..., description="Identifier for the mission")
|
|
58
|
+
status: TaskStatus = Field(..., description="Current status of the task")
|
|
59
|
+
action: SignalType = Field(..., description="Type of signal action")
|
|
60
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
61
|
+
payload: dict[str, Any] = Field(default={}, description="Optional payload for the signal")
|
|
62
|
+
model_config = {"use_enum_values": True}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class HeartbeatMessage(BaseModel):
|
|
66
|
+
"""Heartbeat message model for task monitoring."""
|
|
67
|
+
|
|
68
|
+
task_id: str = Field(..., description="Unique identifier for the task")
|
|
69
|
+
mission_id: str = Field(..., description="Identifier for the mission")
|
|
70
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Base gRPC server and client models."""
|
|
@@ -175,8 +175,8 @@ class ClientConfig(ChannelConfig):
|
|
|
175
175
|
credentials: ClientCredentials | None = Field(None, description="Client credentials for secure mode")
|
|
176
176
|
channel_options: list[tuple[str, Any]] = Field(
|
|
177
177
|
default_factory=lambda: [
|
|
178
|
-
("grpc.max_receive_message_length",
|
|
179
|
-
("grpc.max_send_message_length",
|
|
178
|
+
("grpc.max_receive_message_length", 100 * 1024 * 1024), # 100MB
|
|
179
|
+
("grpc.max_send_message_length", 100 * 1024 * 1024), # 100MB
|
|
180
180
|
],
|
|
181
181
|
description="Additional channel options",
|
|
182
182
|
)
|
|
@@ -223,8 +223,8 @@ class ServerConfig(ChannelConfig):
|
|
|
223
223
|
credentials: ServerCredentials | None = Field(None, description="Server credentials for secure mode")
|
|
224
224
|
server_options: list[tuple[str, Any]] = Field(
|
|
225
225
|
default_factory=lambda: [
|
|
226
|
-
("grpc.max_receive_message_length",
|
|
227
|
-
("grpc.max_send_message_length",
|
|
226
|
+
("grpc.max_receive_message_length", 100 * 1024 * 1024), # 100MB
|
|
227
|
+
("grpc.max_send_message_length", 100 * 1024 * 1024), # 100MB
|
|
228
228
|
],
|
|
229
229
|
description="Additional server options",
|
|
230
230
|
)
|
|
@@ -262,7 +262,7 @@ class ModuleServerConfig(ServerConfig):
|
|
|
262
262
|
registry_address: Address of the registry server
|
|
263
263
|
"""
|
|
264
264
|
|
|
265
|
-
registry_address: str
|
|
265
|
+
registry_address: str = Field(..., description="Address of the registry server")
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
class RegistryServerConfig(ServerConfig):
|
|
@@ -5,6 +5,7 @@ from digitalkin.models.module.module_context import ModuleContext
|
|
|
5
5
|
from digitalkin.models.module.module_types import (
|
|
6
6
|
DataModel,
|
|
7
7
|
DataTrigger,
|
|
8
|
+
DataTriggerT,
|
|
8
9
|
InputModelT,
|
|
9
10
|
OutputModelT,
|
|
10
11
|
SecretModelT,
|
|
@@ -15,6 +16,7 @@ from digitalkin.models.module.module_types import (
|
|
|
15
16
|
__all__ = [
|
|
16
17
|
"DataModel",
|
|
17
18
|
"DataTrigger",
|
|
19
|
+
"DataTriggerT",
|
|
18
20
|
"InputModelT",
|
|
19
21
|
"Module",
|
|
20
22
|
"ModuleContext",
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from enum import Enum, auto
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ModuleCodeModel(BaseModel):
|
|
9
|
+
"""typed error/code model."""
|
|
10
|
+
|
|
11
|
+
code: str = Field(...)
|
|
12
|
+
message: str | None = Field(default=None)
|
|
13
|
+
short_description: str | None = Field(default=None)
|
|
6
14
|
|
|
7
15
|
|
|
8
16
|
class ModuleStatus(Enum):
|
|
@@ -1,24 +1,140 @@
|
|
|
1
1
|
"""Define the module context used in the triggers."""
|
|
2
2
|
|
|
3
3
|
from types import SimpleNamespace
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
6
|
+
from digitalkin.services.agent.agent_strategy import AgentStrategy
|
|
5
7
|
from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
6
8
|
from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
|
|
9
|
+
from digitalkin.services.identity.identity_strategy import IdentityStrategy
|
|
10
|
+
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
11
|
+
from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
|
|
7
12
|
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
8
13
|
|
|
9
14
|
|
|
10
|
-
class
|
|
11
|
-
"""
|
|
15
|
+
class Session(SimpleNamespace):
|
|
16
|
+
"""Session data container with mandatory setup_id and mission_id."""
|
|
17
|
+
|
|
18
|
+
job_id: str
|
|
19
|
+
mission_id: str
|
|
20
|
+
setup_id: str
|
|
21
|
+
setup_version_id: str
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
job_id: str,
|
|
26
|
+
mission_id: str,
|
|
27
|
+
setup_id: str,
|
|
28
|
+
setup_version_id: str,
|
|
29
|
+
**kwargs: dict[str, Any],
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Init Module Session.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
job_id: current job_id.
|
|
35
|
+
mission_id: current mission_id.
|
|
36
|
+
setup_id: used setup config.
|
|
37
|
+
setup_version_id: used setup config.
|
|
38
|
+
kwargs: user defined session variables.
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If mandatory args are missing
|
|
42
|
+
"""
|
|
43
|
+
if not setup_id:
|
|
44
|
+
msg = "setup_id is mandatory and cannot be empty"
|
|
45
|
+
raise ValueError(msg)
|
|
46
|
+
if not setup_version_id:
|
|
47
|
+
msg = "setup_version_id is mandatory and cannot be empty"
|
|
48
|
+
raise ValueError(msg)
|
|
49
|
+
if not mission_id:
|
|
50
|
+
msg = "mission_id is mandatory and cannot be empty"
|
|
51
|
+
raise ValueError(msg)
|
|
52
|
+
if not job_id:
|
|
53
|
+
msg = "job_id is mandatory and cannot be empty"
|
|
54
|
+
raise ValueError(msg)
|
|
55
|
+
|
|
56
|
+
self.job_id = job_id
|
|
57
|
+
self.mission_id = mission_id
|
|
58
|
+
self.setup_id = setup_id
|
|
59
|
+
self.setup_version_id = setup_version_id
|
|
60
|
+
|
|
61
|
+
super().__init__(**kwargs)
|
|
62
|
+
|
|
63
|
+
def current_ids(self) -> dict[str, str]:
|
|
64
|
+
"""Return current session ids as a dictionary.
|
|
12
65
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
66
|
+
Returns:
|
|
67
|
+
A dictionary containing the current session ids.
|
|
68
|
+
"""
|
|
69
|
+
return {
|
|
70
|
+
"job_id": self.job_id,
|
|
71
|
+
"mission_id": self.mission_id,
|
|
72
|
+
"setup_id": self.setup_id,
|
|
73
|
+
"setup_version_id": self.setup_version_id,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ModuleContext:
|
|
78
|
+
"""ModuleContext provides a container for strategies and resources used by a module.
|
|
17
79
|
|
|
18
80
|
This context object is designed to be passed to module components, providing them with
|
|
19
81
|
access to shared strategies and resources. Additional attributes may be set dynamically.
|
|
20
82
|
"""
|
|
21
83
|
|
|
84
|
+
# services list
|
|
85
|
+
agent: AgentStrategy
|
|
22
86
|
cost: CostStrategy
|
|
23
87
|
filesystem: FilesystemStrategy
|
|
88
|
+
identity: IdentityStrategy
|
|
89
|
+
registry: RegistryStrategy
|
|
90
|
+
snapshot: SnapshotStrategy
|
|
24
91
|
storage: StorageStrategy
|
|
92
|
+
|
|
93
|
+
session: Session
|
|
94
|
+
callbacks: SimpleNamespace
|
|
95
|
+
metadata: SimpleNamespace
|
|
96
|
+
helpers: SimpleNamespace
|
|
97
|
+
state: SimpleNamespace = SimpleNamespace()
|
|
98
|
+
|
|
99
|
+
def __init__( # noqa: PLR0913, PLR0917
|
|
100
|
+
self,
|
|
101
|
+
agent: AgentStrategy,
|
|
102
|
+
cost: CostStrategy,
|
|
103
|
+
filesystem: FilesystemStrategy,
|
|
104
|
+
identity: IdentityStrategy,
|
|
105
|
+
registry: RegistryStrategy,
|
|
106
|
+
snapshot: SnapshotStrategy,
|
|
107
|
+
storage: StorageStrategy,
|
|
108
|
+
session: dict[str, Any],
|
|
109
|
+
metadata: dict[str, Any] = {},
|
|
110
|
+
helpers: dict[str, Any] = {},
|
|
111
|
+
callbacks: dict[str, Any] = {},
|
|
112
|
+
) -> None:
|
|
113
|
+
"""Register mandatory services, session, metadata and callbacks.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
agent: AgentStrategy.
|
|
117
|
+
cost: CostStrategy.
|
|
118
|
+
filesystem: FilesystemStrategy.
|
|
119
|
+
identity: IdentityStrategy.
|
|
120
|
+
registry: RegistryStrategy.
|
|
121
|
+
snapshot: SnapshotStrategy.
|
|
122
|
+
storage: StorageStrategy.
|
|
123
|
+
metadata: dict defining differents Module metadata.
|
|
124
|
+
helpers: dict different user defined helpers.
|
|
125
|
+
session: dict referring the session IDs or informations.
|
|
126
|
+
callbacks: Functions allowing user to agent interaction.
|
|
127
|
+
"""
|
|
128
|
+
# Core services
|
|
129
|
+
self.agent = agent
|
|
130
|
+
self.cost = cost
|
|
131
|
+
self.filesystem = filesystem
|
|
132
|
+
self.identity = identity
|
|
133
|
+
self.registry = registry
|
|
134
|
+
self.snapshot = snapshot
|
|
135
|
+
self.storage = storage
|
|
136
|
+
|
|
137
|
+
self.metadata = SimpleNamespace(**metadata)
|
|
138
|
+
self.session = Session(**session)
|
|
139
|
+
self.helpers = SimpleNamespace(**helpers)
|
|
140
|
+
self.callbacks = SimpleNamespace(**callbacks)
|