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.
Files changed (78) hide show
  1. digitalkin/__version__.py +1 -1
  2. digitalkin/core/__init__.py +1 -0
  3. digitalkin/core/common/__init__.py +9 -0
  4. digitalkin/core/common/factories.py +156 -0
  5. digitalkin/core/job_manager/__init__.py +1 -0
  6. digitalkin/{modules → core}/job_manager/base_job_manager.py +137 -31
  7. digitalkin/core/job_manager/single_job_manager.py +354 -0
  8. digitalkin/{modules → core}/job_manager/taskiq_broker.py +116 -22
  9. digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
  10. digitalkin/core/task_manager/__init__.py +1 -0
  11. digitalkin/core/task_manager/base_task_manager.py +539 -0
  12. digitalkin/core/task_manager/local_task_manager.py +108 -0
  13. digitalkin/core/task_manager/remote_task_manager.py +87 -0
  14. digitalkin/core/task_manager/surrealdb_repository.py +266 -0
  15. digitalkin/core/task_manager/task_executor.py +249 -0
  16. digitalkin/core/task_manager/task_session.py +406 -0
  17. digitalkin/grpc_servers/__init__.py +1 -19
  18. digitalkin/grpc_servers/_base_server.py +3 -3
  19. digitalkin/grpc_servers/module_server.py +27 -43
  20. digitalkin/grpc_servers/module_servicer.py +51 -36
  21. digitalkin/grpc_servers/registry_server.py +2 -2
  22. digitalkin/grpc_servers/registry_servicer.py +4 -4
  23. digitalkin/grpc_servers/utils/__init__.py +1 -0
  24. digitalkin/grpc_servers/utils/exceptions.py +0 -8
  25. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +4 -4
  26. digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
  27. digitalkin/logger.py +73 -24
  28. digitalkin/mixins/__init__.py +19 -0
  29. digitalkin/mixins/base_mixin.py +10 -0
  30. digitalkin/mixins/callback_mixin.py +24 -0
  31. digitalkin/mixins/chat_history_mixin.py +110 -0
  32. digitalkin/mixins/cost_mixin.py +76 -0
  33. digitalkin/mixins/file_history_mixin.py +93 -0
  34. digitalkin/mixins/filesystem_mixin.py +46 -0
  35. digitalkin/mixins/logger_mixin.py +51 -0
  36. digitalkin/mixins/storage_mixin.py +79 -0
  37. digitalkin/models/core/__init__.py +1 -0
  38. digitalkin/{modules/job_manager → models/core}/job_manager_models.py +3 -3
  39. digitalkin/models/core/task_monitor.py +70 -0
  40. digitalkin/models/grpc_servers/__init__.py +1 -0
  41. digitalkin/{grpc_servers/utils → models/grpc_servers}/models.py +5 -5
  42. digitalkin/models/module/__init__.py +2 -0
  43. digitalkin/models/module/module.py +9 -1
  44. digitalkin/models/module/module_context.py +122 -6
  45. digitalkin/models/module/module_types.py +307 -19
  46. digitalkin/models/services/__init__.py +9 -0
  47. digitalkin/models/services/cost.py +1 -0
  48. digitalkin/models/services/storage.py +39 -5
  49. digitalkin/modules/_base_module.py +123 -118
  50. digitalkin/modules/tool_module.py +10 -2
  51. digitalkin/modules/trigger_handler.py +7 -6
  52. digitalkin/services/cost/__init__.py +9 -2
  53. digitalkin/services/cost/grpc_cost.py +9 -42
  54. digitalkin/services/filesystem/default_filesystem.py +0 -2
  55. digitalkin/services/filesystem/grpc_filesystem.py +10 -39
  56. digitalkin/services/setup/default_setup.py +5 -6
  57. digitalkin/services/setup/grpc_setup.py +52 -15
  58. digitalkin/services/storage/grpc_storage.py +4 -4
  59. digitalkin/services/user_profile/__init__.py +1 -0
  60. digitalkin/services/user_profile/default_user_profile.py +55 -0
  61. digitalkin/services/user_profile/grpc_user_profile.py +69 -0
  62. digitalkin/services/user_profile/user_profile_strategy.py +40 -0
  63. digitalkin/utils/__init__.py +28 -0
  64. digitalkin/utils/arg_parser.py +1 -1
  65. digitalkin/utils/development_mode_action.py +2 -2
  66. digitalkin/utils/dynamic_schema.py +483 -0
  67. digitalkin/utils/package_discover.py +1 -2
  68. {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/METADATA +11 -30
  69. digitalkin-0.3.1.dev2.dist-info/RECORD +119 -0
  70. modules/dynamic_setup_module.py +362 -0
  71. digitalkin/grpc_servers/utils/factory.py +0 -180
  72. digitalkin/modules/job_manager/single_job_manager.py +0 -294
  73. digitalkin/modules/job_manager/taskiq_job_manager.py +0 -290
  74. digitalkin-0.2.23.dist-info/RECORD +0 -89
  75. /digitalkin/{grpc_servers/utils → models/grpc_servers}/types.py +0 -0
  76. {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/WHEEL +0 -0
  77. {digitalkin-0.2.23.dist-info → digitalkin-0.3.1.dev2.dist-info}/licenses/LICENSE +0 -0
  78. {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.modules.job_manager.base_job_manager import BaseJobManager
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.modules.job_manager.single_job_manager import SingleJobManager # noqa: PLC0415
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.modules.job_manager.taskiq_job_manager import TaskiqJobManager # noqa: PLC0415
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", 50 * 1024 * 1024), # 50MB
179
- ("grpc.max_send_message_length", 50 * 1024 * 1024), # 50MB
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", 50 * 1024 * 1024), # 50MB
227
- ("grpc.max_send_message_length", 50 * 1024 * 1024), # 50MB
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 | None = Field(None, description="Address of the registry server")
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 ModuleContext(SimpleNamespace):
11
- """ModuleContext provides a container for strategies and resources used by a module.
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
- Attributes:
14
- cost (CostStrategy): The strategy used to calculate or manage costs within the module.
15
- filesystem (FilesystemStrategy): The strategy for interacting with the filesystem.
16
- storage (StorageStrategy): The strategy for handling storage operations.
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)