digitalkin 0.2.18__py3-none-any.whl → 0.2.20__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/grpc_servers/module_servicer.py +9 -7
- digitalkin/models/module/__init__.py +6 -6
- digitalkin/models/module/module_types.py +76 -14
- digitalkin/modules/_base_module.py +71 -46
- digitalkin/modules/archetype_module.py +0 -2
- digitalkin/modules/job_manager/base_job_manager.py +8 -7
- digitalkin/modules/job_manager/single_job_manager.py +19 -10
- digitalkin/modules/job_manager/taskiq_broker.py +6 -4
- digitalkin/modules/job_manager/taskiq_job_manager.py +9 -5
- digitalkin/modules/tool_module.py +1 -2
- digitalkin/services/base_strategy.py +3 -1
- digitalkin/services/cost/cost_strategy.py +9 -2
- digitalkin/services/cost/default_cost.py +3 -2
- digitalkin/services/cost/grpc_cost.py +2 -1
- digitalkin/services/filesystem/default_filesystem.py +9 -7
- digitalkin/services/filesystem/filesystem_strategy.py +10 -3
- digitalkin/services/filesystem/grpc_filesystem.py +10 -8
- digitalkin/services/services_config.py +3 -2
- digitalkin/services/storage/default_storage.py +2 -1
- digitalkin/services/storage/grpc_storage.py +6 -5
- digitalkin/services/storage/storage_strategy.py +9 -2
- digitalkin/utils/package_discover.py +2 -2
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/METADATA +13 -13
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/RECORD +28 -28
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/top_level.txt +0 -0
|
@@ -10,13 +10,14 @@ import grpc
|
|
|
10
10
|
|
|
11
11
|
from digitalkin.logger import logger
|
|
12
12
|
from digitalkin.models import ModuleStatus
|
|
13
|
-
from digitalkin.models.module import
|
|
13
|
+
from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
|
|
14
14
|
from digitalkin.modules._base_module import BaseModule
|
|
15
15
|
from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
|
|
16
|
+
from digitalkin.modules.job_manager.job_manager_models import StreamCodeModel
|
|
16
17
|
from digitalkin.services.services_models import ServicesMode
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT
|
|
20
|
+
class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
|
|
20
21
|
"""Manages a single instance of a module job.
|
|
21
22
|
|
|
22
23
|
This class ensures that only one instance of a module job is active at a time.
|
|
@@ -68,9 +69,9 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
68
69
|
|
|
69
70
|
async def create_config_setup_instance_job(
|
|
70
71
|
self,
|
|
71
|
-
config_setup_data:
|
|
72
|
-
setup_data: SetupModelT,
|
|
72
|
+
config_setup_data: SetupModelT,
|
|
73
73
|
mission_id: str,
|
|
74
|
+
setup_id: str,
|
|
74
75
|
setup_version_id: str,
|
|
75
76
|
) -> str:
|
|
76
77
|
"""Create and start a new module setup configuration job.
|
|
@@ -82,6 +83,7 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
82
83
|
config_setup_data: The input data required to start the job.
|
|
83
84
|
setup_data: The setup configuration for the module.
|
|
84
85
|
mission_id: The mission ID associated with the job.
|
|
86
|
+
setup_id: The setup ID associated with the module.
|
|
85
87
|
setup_version_id: The setup ID.
|
|
86
88
|
|
|
87
89
|
Returns:
|
|
@@ -92,14 +94,13 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
92
94
|
"""
|
|
93
95
|
job_id = str(uuid.uuid4())
|
|
94
96
|
# TODO: Ensure the job_id is unique.
|
|
95
|
-
module = self.module_class(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
|
|
97
|
+
module = self.module_class(job_id, mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
96
98
|
self.modules[job_id] = module
|
|
97
99
|
self.queues[job_id] = asyncio.Queue()
|
|
98
100
|
|
|
99
101
|
try:
|
|
100
102
|
await module.start_config_setup(
|
|
101
103
|
config_setup_data,
|
|
102
|
-
setup_data,
|
|
103
104
|
await self.job_specific_callback(self.add_to_queue, job_id),
|
|
104
105
|
)
|
|
105
106
|
logger.debug("Module %s (%s) started successfully", job_id, module.name)
|
|
@@ -166,7 +167,6 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
166
167
|
):
|
|
167
168
|
logger.info(f"{job_id=}: {module.status=}")
|
|
168
169
|
yield await self.queues[job_id].get()
|
|
169
|
-
logger.info(f"{job_id=}: {module.status=} | {self.queues[job_id].empty()}")
|
|
170
170
|
|
|
171
171
|
finally:
|
|
172
172
|
del self.queues[job_id]
|
|
@@ -178,6 +178,7 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
178
178
|
input_data: InputModelT,
|
|
179
179
|
setup_data: SetupModelT,
|
|
180
180
|
mission_id: str,
|
|
181
|
+
setup_id: str,
|
|
181
182
|
setup_version_id: str,
|
|
182
183
|
) -> str:
|
|
183
184
|
"""Create and start a new module job.
|
|
@@ -189,7 +190,8 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
189
190
|
input_data: The input data required to start the job.
|
|
190
191
|
setup_data: The setup configuration for the module.
|
|
191
192
|
mission_id: The mission ID associated with the job.
|
|
192
|
-
|
|
193
|
+
setup_id: The setup ID associated with the module.
|
|
194
|
+
setup_version_id: The setup Version ID associated with the module.
|
|
193
195
|
|
|
194
196
|
Returns:
|
|
195
197
|
str: The unique identifier (job ID) of the created job.
|
|
@@ -199,15 +201,22 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
199
201
|
"""
|
|
200
202
|
job_id = str(uuid.uuid4())
|
|
201
203
|
# TODO: Ensure the job_id is unique.
|
|
202
|
-
module = self.module_class(
|
|
204
|
+
module = self.module_class(
|
|
205
|
+
job_id,
|
|
206
|
+
mission_id=mission_id,
|
|
207
|
+
setup_id=setup_id,
|
|
208
|
+
setup_version_id=setup_version_id,
|
|
209
|
+
)
|
|
203
210
|
self.modules[job_id] = module
|
|
204
211
|
self.queues[job_id] = asyncio.Queue()
|
|
212
|
+
callback = await self.job_specific_callback(self.add_to_queue, job_id)
|
|
205
213
|
|
|
206
214
|
try:
|
|
207
215
|
await module.start(
|
|
208
216
|
input_data,
|
|
209
217
|
setup_data,
|
|
210
|
-
|
|
218
|
+
callback,
|
|
219
|
+
done_callback=lambda _: asyncio.create_task(callback(StreamCodeModel(code="__END_OF_STREAM__"))),
|
|
211
220
|
)
|
|
212
221
|
logger.debug("Module %s (%s) started successfully", job_id, module.name)
|
|
213
222
|
except Exception:
|
|
@@ -132,6 +132,7 @@ async def send_message_to_stream(job_id: str, output_data: OutputModelT) -> None
|
|
|
132
132
|
@TASKIQ_BROKER.task
|
|
133
133
|
async def run_start_module(
|
|
134
134
|
mission_id: str,
|
|
135
|
+
setup_id: str,
|
|
135
136
|
setup_version_id: str,
|
|
136
137
|
module_class: type[BaseModule],
|
|
137
138
|
services_mode: ServicesMode,
|
|
@@ -143,6 +144,7 @@ async def run_start_module(
|
|
|
143
144
|
|
|
144
145
|
Args:
|
|
145
146
|
mission_id: str,
|
|
147
|
+
setup_id: The setup ID associated with the module.
|
|
146
148
|
setup_version_id: The setup ID associated with the module.
|
|
147
149
|
module_class: type[BaseModule],
|
|
148
150
|
services_mode: ServicesMode,
|
|
@@ -161,7 +163,7 @@ async def run_start_module(
|
|
|
161
163
|
|
|
162
164
|
job_id = context.message.task_id
|
|
163
165
|
callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id)
|
|
164
|
-
module = module_class(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
|
|
166
|
+
module = module_class(job_id, mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
165
167
|
|
|
166
168
|
await module.start(
|
|
167
169
|
input_data,
|
|
@@ -176,17 +178,18 @@ async def run_start_module(
|
|
|
176
178
|
@TASKIQ_BROKER.task
|
|
177
179
|
async def run_config_module(
|
|
178
180
|
mission_id: str,
|
|
181
|
+
setup_id: str,
|
|
179
182
|
setup_version_id: str,
|
|
180
183
|
module_class: type[BaseModule],
|
|
181
184
|
services_mode: ServicesMode,
|
|
182
185
|
config_setup_data: dict,
|
|
183
|
-
setup_data: dict,
|
|
184
186
|
context: Context = TaskiqDepends(),
|
|
185
187
|
) -> None:
|
|
186
188
|
"""TaskIQ task allowing a module to compute in the background asynchronously.
|
|
187
189
|
|
|
188
190
|
Args:
|
|
189
191
|
mission_id: str,
|
|
192
|
+
setup_id: The setup ID associated with the module.
|
|
190
193
|
setup_version_id: The setup ID associated with the module.
|
|
191
194
|
module_class: type[BaseModule],
|
|
192
195
|
services_mode: ServicesMode,
|
|
@@ -205,10 +208,9 @@ async def run_config_module(
|
|
|
205
208
|
|
|
206
209
|
job_id = context.message.task_id
|
|
207
210
|
callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id)
|
|
208
|
-
module = module_class(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
|
|
211
|
+
module = module_class(job_id, mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
209
212
|
|
|
210
213
|
await module.start_config_setup(
|
|
211
214
|
module_class.create_config_setup_model(config_setup_data),
|
|
212
|
-
module_class.create_setup_model(setup_data),
|
|
213
215
|
callback,
|
|
214
216
|
)
|
|
@@ -18,7 +18,7 @@ from typing import TYPE_CHECKING, Any, Generic
|
|
|
18
18
|
from rstream import Consumer, ConsumerOffsetSpecification, MessageContext, OffsetType
|
|
19
19
|
|
|
20
20
|
from digitalkin.logger import logger
|
|
21
|
-
from digitalkin.models.module import
|
|
21
|
+
from digitalkin.models.module import InputModelT, SetupModelT
|
|
22
22
|
from digitalkin.models.module.module import ModuleStatus
|
|
23
23
|
from digitalkin.modules._base_module import BaseModule
|
|
24
24
|
from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
|
|
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
|
|
|
29
29
|
from taskiq.task import AsyncTaskiqTask
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT
|
|
32
|
+
class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
|
|
33
33
|
"""Taskiq job manager for running modules in Taskiq tasks."""
|
|
34
34
|
|
|
35
35
|
services_mode: ServicesMode
|
|
@@ -134,9 +134,9 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
134
134
|
|
|
135
135
|
async def create_config_setup_instance_job(
|
|
136
136
|
self,
|
|
137
|
-
config_setup_data:
|
|
138
|
-
setup_data: SetupModelT,
|
|
137
|
+
config_setup_data: SetupModelT,
|
|
139
138
|
mission_id: str,
|
|
139
|
+
setup_id: str,
|
|
140
140
|
setup_version_id: str,
|
|
141
141
|
) -> str:
|
|
142
142
|
"""Create and start a new module setup configuration job.
|
|
@@ -148,6 +148,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
148
148
|
config_setup_data: The input data required to start the job.
|
|
149
149
|
setup_data: The setup configuration for the module.
|
|
150
150
|
mission_id: The mission ID associated with the job.
|
|
151
|
+
setup_id: The setup ID associated with the module.
|
|
151
152
|
setup_version_id: The setup ID.
|
|
152
153
|
|
|
153
154
|
Returns:
|
|
@@ -169,11 +170,11 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
169
170
|
|
|
170
171
|
running_task: AsyncTaskiqTask[Any] = await task.kiq(
|
|
171
172
|
mission_id,
|
|
173
|
+
setup_id,
|
|
172
174
|
setup_version_id,
|
|
173
175
|
self.module_class,
|
|
174
176
|
self.services_mode,
|
|
175
177
|
config_setup_data.model_dump(), # type: ignore
|
|
176
|
-
setup_data.model_dump(),
|
|
177
178
|
)
|
|
178
179
|
|
|
179
180
|
job_id = running_task.task_id
|
|
@@ -223,6 +224,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
223
224
|
input_data: InputModelT,
|
|
224
225
|
setup_data: SetupModelT,
|
|
225
226
|
mission_id: str,
|
|
227
|
+
setup_id: str,
|
|
226
228
|
setup_version_id: str,
|
|
227
229
|
) -> str:
|
|
228
230
|
"""Launches the module_task in Taskiq, returns the Taskiq task id as job_id.
|
|
@@ -231,6 +233,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
231
233
|
input_data: Input data for the module
|
|
232
234
|
setup_data: Setup data for the module
|
|
233
235
|
mission_id: Mission ID for the module
|
|
236
|
+
setup_id: The setup ID associated with the module.
|
|
234
237
|
setup_version_id: The setup ID associated with the module.
|
|
235
238
|
|
|
236
239
|
Returns:
|
|
@@ -247,6 +250,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigS
|
|
|
247
250
|
|
|
248
251
|
running_task: AsyncTaskiqTask[Any] = await task.kiq(
|
|
249
252
|
mission_id,
|
|
253
|
+
setup_id,
|
|
250
254
|
setup_version_id,
|
|
251
255
|
self.module_class,
|
|
252
256
|
self.services_mode,
|
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
|
|
5
5
|
from digitalkin.models.module import InputModelT, OutputModelT, SecretModelT, SetupModelT
|
|
6
|
-
from digitalkin.models.module.module_types import ConfigSetupModelT
|
|
7
6
|
from digitalkin.modules._base_module import BaseModule # type: ignore
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,
|
|
9
|
+
class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,], ABC):
|
|
11
10
|
"""ToolModule extends BaseModule to implement specific module types."""
|
|
@@ -9,12 +9,14 @@ class BaseStrategy(ABC):
|
|
|
9
9
|
This class defines the interface for all strategies.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
def __init__(self, mission_id: str, setup_version_id: str) -> None:
|
|
12
|
+
def __init__(self, mission_id: str, setup_id: str, setup_version_id: str) -> None:
|
|
13
13
|
"""Initialize the strategy.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
16
|
mission_id: The ID of the mission this strategy is associated with
|
|
17
|
+
setup_id: The ID of the setup this strategy is associated with
|
|
17
18
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
18
19
|
"""
|
|
19
20
|
self.mission_id: str = mission_id
|
|
21
|
+
self.setup_id: str = setup_id
|
|
20
22
|
self.setup_version_id: str = setup_version_id
|
|
@@ -57,15 +57,22 @@ class CostServiceError(Exception):
|
|
|
57
57
|
class CostStrategy(BaseStrategy, ABC):
|
|
58
58
|
"""Abstract base class for cost strategies."""
|
|
59
59
|
|
|
60
|
-
def __init__(
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
mission_id: str,
|
|
63
|
+
setup_id: str,
|
|
64
|
+
setup_version_id: str,
|
|
65
|
+
config: dict[str, CostConfig],
|
|
66
|
+
) -> None:
|
|
61
67
|
"""Initialize the strategy.
|
|
62
68
|
|
|
63
69
|
Args:
|
|
64
70
|
mission_id: The ID of the mission this strategy is associated with
|
|
71
|
+
setup_id: The ID of the setup
|
|
65
72
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
66
73
|
config: Configuration dictionary for the strategy
|
|
67
74
|
"""
|
|
68
|
-
super().__init__(mission_id, setup_version_id)
|
|
75
|
+
super().__init__(mission_id, setup_id, setup_version_id)
|
|
69
76
|
self.config = config
|
|
70
77
|
|
|
71
78
|
@abstractmethod
|
|
@@ -15,15 +15,16 @@ from digitalkin.services.cost.cost_strategy import (
|
|
|
15
15
|
class DefaultCost(CostStrategy):
|
|
16
16
|
"""Default cost strategy."""
|
|
17
17
|
|
|
18
|
-
def __init__(self, mission_id: str, setup_version_id: str, config: dict[str, CostConfig]) -> None:
|
|
18
|
+
def __init__(self, mission_id: str, setup_id: str, setup_version_id: str, config: dict[str, CostConfig]) -> None:
|
|
19
19
|
"""Initialize the strategy.
|
|
20
20
|
|
|
21
21
|
Args:
|
|
22
22
|
mission_id: The ID of the mission this strategy is associated with
|
|
23
|
+
setup_id: The ID of the setup
|
|
23
24
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
24
25
|
config: The configuration dictionary for the cost
|
|
25
26
|
"""
|
|
26
|
-
super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
|
|
27
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id, config=config)
|
|
27
28
|
self.db: dict[str, list[CostData]] = {}
|
|
28
29
|
|
|
29
30
|
def add(
|
|
@@ -57,12 +57,13 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
|
57
57
|
def __init__(
|
|
58
58
|
self,
|
|
59
59
|
mission_id: str,
|
|
60
|
+
setup_id: str,
|
|
60
61
|
setup_version_id: str,
|
|
61
62
|
config: dict[str, CostConfig],
|
|
62
63
|
client_config: ClientConfig,
|
|
63
64
|
) -> None:
|
|
64
65
|
"""Initialize the cost."""
|
|
65
|
-
super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
|
|
66
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id, config=config)
|
|
66
67
|
channel = self._init_channel(client_config)
|
|
67
68
|
self.stub = cost_service_pb2_grpc.CostServiceStub(channel)
|
|
68
69
|
logger.debug("Channel client 'Cost' initialized succesfully")
|
|
@@ -25,14 +25,15 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
25
25
|
Files are stored in a temporary directory with proper metadata tracking.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
def __init__(self, mission_id: str, setup_version_id: str) -> None:
|
|
28
|
+
def __init__(self, mission_id: str, setup_id: str, setup_version_id: str) -> None:
|
|
29
29
|
"""Initialize the default filesystem strategy.
|
|
30
30
|
|
|
31
31
|
Args:
|
|
32
32
|
mission_id: The ID of the mission this strategy is associated with
|
|
33
|
+
setup_id: The ID of the setup
|
|
33
34
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
34
35
|
"""
|
|
35
|
-
super().__init__(mission_id, setup_version_id)
|
|
36
|
+
super().__init__(mission_id, setup_id, setup_version_id)
|
|
36
37
|
self.temp_root: str = tempfile.mkdtemp()
|
|
37
38
|
os.makedirs(self.temp_root, exist_ok=True)
|
|
38
39
|
self.db: dict[str, FilesystemRecord] = {}
|
|
@@ -118,7 +119,7 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
118
119
|
for file in files:
|
|
119
120
|
try:
|
|
120
121
|
# Check if file with same name exists in the context
|
|
121
|
-
context_dir = self._get_context_temp_dir(self.
|
|
122
|
+
context_dir = self._get_context_temp_dir(self.setup_id)
|
|
122
123
|
file_path = os.path.join(context_dir, file.name)
|
|
123
124
|
if os.path.exists(file_path) and not file.replace_if_exists:
|
|
124
125
|
msg = f"File with name {file.name} already exists."
|
|
@@ -129,7 +130,7 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
129
130
|
storage_uri = str(Path(file_path).resolve())
|
|
130
131
|
file_data = FilesystemRecord(
|
|
131
132
|
id=str(uuid.uuid4()),
|
|
132
|
-
context=self.
|
|
133
|
+
context=self.setup_id,
|
|
133
134
|
name=file.name,
|
|
134
135
|
file_type=file.file_type,
|
|
135
136
|
content_type=file.content_type or "application/octet-stream",
|
|
@@ -138,14 +139,13 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
138
139
|
metadata=file.metadata,
|
|
139
140
|
storage_uri=storage_uri,
|
|
140
141
|
file_url=storage_uri,
|
|
141
|
-
status=
|
|
142
|
+
status="ACTIVE",
|
|
142
143
|
)
|
|
143
144
|
|
|
144
145
|
self.db[file_data.id] = file_data
|
|
145
146
|
uploaded_files.append(file_data)
|
|
146
147
|
total_uploaded += 1
|
|
147
148
|
logger.debug("Uploaded file %s", file_data)
|
|
148
|
-
|
|
149
149
|
except Exception as e: # noqa: PERF203
|
|
150
150
|
logger.exception("Error uploading file %s: %s", file.name, e)
|
|
151
151
|
total_failed += 1
|
|
@@ -199,6 +199,8 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
199
199
|
end_idx = start_idx + list_size
|
|
200
200
|
paginated_files = filtered_files[start_idx:end_idx]
|
|
201
201
|
|
|
202
|
+
logger.critical(f"{filters=} | {paginated_files=}")
|
|
203
|
+
|
|
202
204
|
if include_content:
|
|
203
205
|
for file in paginated_files:
|
|
204
206
|
file.content = Path(file.storage_uri).read_bytes()
|
|
@@ -306,7 +308,7 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
306
308
|
raise FilesystemServiceError(msg)
|
|
307
309
|
|
|
308
310
|
try:
|
|
309
|
-
context_dir = self._get_context_temp_dir(self.
|
|
311
|
+
context_dir = self._get_context_temp_dir(self.setup_id)
|
|
310
312
|
file_path = os.path.join(context_dir, file_id)
|
|
311
313
|
existing_file = self.db[file_id]
|
|
312
314
|
|
|
@@ -90,15 +90,22 @@ class FilesystemStrategy(BaseStrategy, ABC):
|
|
|
90
90
|
filtering, and pagination.
|
|
91
91
|
"""
|
|
92
92
|
|
|
93
|
-
def __init__(
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
mission_id: str,
|
|
96
|
+
setup_id: str,
|
|
97
|
+
setup_version_id: str,
|
|
98
|
+
config: dict[str, Any] | None = None,
|
|
99
|
+
) -> None:
|
|
94
100
|
"""Initialize the strategy.
|
|
95
101
|
|
|
96
102
|
Args:
|
|
97
103
|
mission_id: The ID of the mission this strategy is associated with
|
|
104
|
+
setup_id: The ID of the setup
|
|
98
105
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
99
106
|
config: Configuration for the filesystem strategy
|
|
100
107
|
"""
|
|
101
|
-
super().__init__(mission_id, setup_version_id)
|
|
108
|
+
super().__init__(mission_id, setup_id, setup_version_id)
|
|
102
109
|
self.config = config
|
|
103
110
|
|
|
104
111
|
@abstractmethod
|
|
@@ -125,7 +132,7 @@ class FilesystemStrategy(BaseStrategy, ABC):
|
|
|
125
132
|
file_id: str,
|
|
126
133
|
*,
|
|
127
134
|
include_content: bool = False,
|
|
128
|
-
) ->
|
|
135
|
+
) -> FilesystemRecord:
|
|
129
136
|
"""Get a specific file by ID or name.
|
|
130
137
|
|
|
131
138
|
This method fetches detailed information about a single file,
|
|
@@ -95,7 +95,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
95
95
|
filesystem_pb2.FileFilter: The converted FileFilter proto message
|
|
96
96
|
"""
|
|
97
97
|
return filesystem_pb2.FileFilter(
|
|
98
|
-
context=self.
|
|
98
|
+
context=self.setup_id,
|
|
99
99
|
**filters.model_dump(exclude={"file_types", "status"}),
|
|
100
100
|
file_types=[self._file_type_to_enum(file_type) for file_type in filters.file_types]
|
|
101
101
|
if filters.file_types
|
|
@@ -114,7 +114,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
114
114
|
"""
|
|
115
115
|
return FilesystemRecord(
|
|
116
116
|
id=file.file_id,
|
|
117
|
-
context=self.
|
|
117
|
+
context=self.setup_id,
|
|
118
118
|
name=file.name,
|
|
119
119
|
file_type=filesystem_pb2.FileType.Name(file.file_type),
|
|
120
120
|
content_type=file.content_type,
|
|
@@ -130,6 +130,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
130
130
|
def __init__(
|
|
131
131
|
self,
|
|
132
132
|
mission_id: str,
|
|
133
|
+
setup_id: str,
|
|
133
134
|
setup_version_id: str,
|
|
134
135
|
client_config: ClientConfig,
|
|
135
136
|
config: dict[str, Any] | None = None,
|
|
@@ -138,11 +139,12 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
138
139
|
|
|
139
140
|
Args:
|
|
140
141
|
mission_id: The ID of the mission this strategy is associated with
|
|
142
|
+
setup_id: The ID of the setup
|
|
141
143
|
setup_version_id: The ID of the setup version this strategy is associated with
|
|
142
144
|
client_config: Configuration for the gRPC client connection
|
|
143
145
|
config: Configuration for the filesystem strategy
|
|
144
146
|
"""
|
|
145
|
-
super().__init__(mission_id, setup_version_id, config)
|
|
147
|
+
super().__init__(mission_id, setup_id, setup_version_id, config)
|
|
146
148
|
self.service_name = "FilesystemService"
|
|
147
149
|
channel = self._init_channel(client_config)
|
|
148
150
|
self.stub = filesystem_service_pb2_grpc.FilesystemServiceStub(channel)
|
|
@@ -170,7 +172,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
170
172
|
metadata_struct.update(file.metadata)
|
|
171
173
|
upload_files.append(
|
|
172
174
|
filesystem_pb2.UploadFileData(
|
|
173
|
-
context=self.
|
|
175
|
+
context=self.setup_id,
|
|
174
176
|
name=file.name,
|
|
175
177
|
file_type=self._file_type_to_enum(file.file_type),
|
|
176
178
|
content_type=file.content_type or "application/octet-stream",
|
|
@@ -206,7 +208,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
206
208
|
"""
|
|
207
209
|
with GrpcFilesystem._handle_grpc_errors("GetFile"):
|
|
208
210
|
request = filesystem_pb2.GetFileRequest(
|
|
209
|
-
context=self.
|
|
211
|
+
context=self.setup_id,
|
|
210
212
|
file_id=file_id,
|
|
211
213
|
include_content=include_content,
|
|
212
214
|
)
|
|
@@ -254,7 +256,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
254
256
|
"""
|
|
255
257
|
with GrpcFilesystem._handle_grpc_errors("UpdateFile"):
|
|
256
258
|
request = filesystem_pb2.UpdateFileRequest(
|
|
257
|
-
context=self.
|
|
259
|
+
context=self.setup_id,
|
|
258
260
|
file_id=file_id,
|
|
259
261
|
content=content,
|
|
260
262
|
file_type=self._file_type_to_enum(file_type) if file_type else None,
|
|
@@ -288,7 +290,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
288
290
|
"""
|
|
289
291
|
with GrpcFilesystem._handle_grpc_errors("DeleteFiles"):
|
|
290
292
|
request = filesystem_pb2.DeleteFilesRequest(
|
|
291
|
-
context=self.
|
|
293
|
+
context=self.setup_id,
|
|
292
294
|
filters=self._filter_to_proto(filters),
|
|
293
295
|
permanent=permanent,
|
|
294
296
|
force=force,
|
|
@@ -320,7 +322,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
320
322
|
"""
|
|
321
323
|
with GrpcFilesystem._handle_grpc_errors("GetFiles"):
|
|
322
324
|
request = filesystem_pb2.GetFilesRequest(
|
|
323
|
-
context=self.
|
|
325
|
+
context=self.setup_id,
|
|
324
326
|
filters=self._filter_to_proto(filters),
|
|
325
327
|
include_content=include_content,
|
|
326
328
|
list_size=list_size,
|
|
@@ -111,12 +111,13 @@ class ServicesConfig(BaseModel):
|
|
|
111
111
|
"""
|
|
112
112
|
return getattr(self, f"_config_{name}", {})
|
|
113
113
|
|
|
114
|
-
def init_strategy(self, name: str, mission_id: str, setup_version_id: str) -> ServicesStrategy:
|
|
114
|
+
def init_strategy(self, name: str, mission_id: str, setup_id: str, setup_version_id: str) -> ServicesStrategy:
|
|
115
115
|
"""Initialize a specific strategy.
|
|
116
116
|
|
|
117
117
|
Args:
|
|
118
118
|
name: The name of the strategy to initialize
|
|
119
119
|
mission_id: The ID of the mission this strategy is associated with
|
|
120
|
+
setup_id: The setup ID for the strategy
|
|
120
121
|
setup_version_id: The setup version ID for the strategy
|
|
121
122
|
|
|
122
123
|
Returns:
|
|
@@ -131,7 +132,7 @@ class ServicesConfig(BaseModel):
|
|
|
131
132
|
raise ValueError(msg)
|
|
132
133
|
|
|
133
134
|
# Instantiate the strategy with the mission ID, setup version ID, and configuration
|
|
134
|
-
return strategy_type(mission_id, setup_version_id, **self.get_strategy_config(name) or {})
|
|
135
|
+
return strategy_type(mission_id, setup_id, setup_version_id, **self.get_strategy_config(name) or {})
|
|
135
136
|
|
|
136
137
|
@property
|
|
137
138
|
def storage(self) -> type[StorageStrategy]:
|
|
@@ -215,13 +215,14 @@ class DefaultStorage(StorageStrategy):
|
|
|
215
215
|
def __init__(
|
|
216
216
|
self,
|
|
217
217
|
mission_id: str,
|
|
218
|
+
setup_id: str,
|
|
218
219
|
setup_version_id: str,
|
|
219
220
|
config: dict[str, type[BaseModel]],
|
|
220
221
|
storage_file_path: str = "local_storage",
|
|
221
222
|
**kwargs, # noqa: ANN003, ARG002
|
|
222
223
|
) -> None:
|
|
223
224
|
"""Initialize the storage."""
|
|
224
|
-
super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
|
|
225
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id, config=config)
|
|
225
226
|
self.storage_file_path = f"{self.mission_id}_{storage_file_path}.json"
|
|
226
227
|
self.storage_file = Path(self.storage_file_path)
|
|
227
228
|
self.storage = self._load_from_file()
|
|
@@ -97,7 +97,7 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
97
97
|
resp = self.exec_grpc_query("ReadRecord", req)
|
|
98
98
|
return self._build_record_from_proto(resp.stored_data)
|
|
99
99
|
except Exception:
|
|
100
|
-
logger.
|
|
100
|
+
logger.warning("gRPC ReadRecord failed for %s:%s", collection, record_id)
|
|
101
101
|
return None
|
|
102
102
|
|
|
103
103
|
def _update(
|
|
@@ -149,7 +149,7 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
149
149
|
)
|
|
150
150
|
self.exec_grpc_query("RemoveRecord", req)
|
|
151
151
|
except Exception:
|
|
152
|
-
logger.
|
|
152
|
+
logger.warning(
|
|
153
153
|
"gRPC RemoveRecord failed for %s:%s",
|
|
154
154
|
collection,
|
|
155
155
|
record_id,
|
|
@@ -174,7 +174,7 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
174
174
|
resp = self.exec_grpc_query("ListRecords", req)
|
|
175
175
|
return [self._build_record_from_proto(r) for r in resp.records]
|
|
176
176
|
except Exception:
|
|
177
|
-
logger.
|
|
177
|
+
logger.warning("gRPC ListRecords failed for %s", collection)
|
|
178
178
|
return []
|
|
179
179
|
|
|
180
180
|
def _remove_collection(self, collection: str) -> bool:
|
|
@@ -193,20 +193,21 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
193
193
|
)
|
|
194
194
|
self.exec_grpc_query("RemoveCollection", req)
|
|
195
195
|
except Exception:
|
|
196
|
-
logger.
|
|
196
|
+
logger.warning("gRPC RemoveCollection failed for %s", collection)
|
|
197
197
|
return False
|
|
198
198
|
return True
|
|
199
199
|
|
|
200
200
|
def __init__(
|
|
201
201
|
self,
|
|
202
202
|
mission_id: str,
|
|
203
|
+
setup_id: str,
|
|
203
204
|
setup_version_id: str,
|
|
204
205
|
config: dict[str, type[BaseModel]],
|
|
205
206
|
client_config: ClientConfig,
|
|
206
207
|
**kwargs, # noqa: ANN003, ARG002
|
|
207
208
|
) -> None:
|
|
208
209
|
"""Initialize the storage."""
|
|
209
|
-
super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
|
|
210
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id, config=config)
|
|
210
211
|
|
|
211
212
|
channel = self._init_channel(client_config)
|
|
212
213
|
self.stub = storage_service_pb2_grpc.StorageServiceStub(channel)
|
|
@@ -163,15 +163,22 @@ class StorageStrategy(BaseStrategy, ABC):
|
|
|
163
163
|
True if the deletion was successful, False otherwise
|
|
164
164
|
"""
|
|
165
165
|
|
|
166
|
-
def __init__(
|
|
166
|
+
def __init__(
|
|
167
|
+
self,
|
|
168
|
+
mission_id: str,
|
|
169
|
+
setup_id: str,
|
|
170
|
+
setup_version_id: str,
|
|
171
|
+
config: dict[str, type[BaseModel]],
|
|
172
|
+
) -> None:
|
|
167
173
|
"""Initialize the storage strategy.
|
|
168
174
|
|
|
169
175
|
Args:
|
|
170
176
|
mission_id: The ID of the mission this strategy is associated with
|
|
177
|
+
setup_id: The ID of the setup
|
|
171
178
|
setup_version_id: The ID of the setup version
|
|
172
179
|
config: A dictionary mapping names to Pydantic model classes
|
|
173
180
|
"""
|
|
174
|
-
super().__init__(mission_id, setup_version_id)
|
|
181
|
+
super().__init__(mission_id, setup_id, setup_version_id)
|
|
175
182
|
# Schema configuration mapping keys to model classes
|
|
176
183
|
self.config: dict[str, type[BaseModel]] = config
|
|
177
184
|
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
from typing import ClassVar
|
|
11
11
|
|
|
12
12
|
from digitalkin.models.module.module_context import ModuleContext
|
|
13
|
-
from digitalkin.models.module.module_types import
|
|
13
|
+
from digitalkin.models.module.module_types import DataTrigger
|
|
14
14
|
from digitalkin.modules.trigger_handler import TriggerHandler
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
@@ -329,7 +329,7 @@ class ModuleDiscoverer:
|
|
|
329
329
|
for protocol, handlers_cls in self._trigger_handlers_cls.items():
|
|
330
330
|
self.trigger_handlers[protocol] = tuple(handler_cls(context) for handler_cls in set(handlers_cls))
|
|
331
331
|
|
|
332
|
-
def get_trigger(self, protocol: str, input_instance:
|
|
332
|
+
def get_trigger(self, protocol: str, input_instance: DataTrigger) -> TriggerHandler:
|
|
333
333
|
"""Retrieve a trigger handler instance based on the provided protocol and input instance type.
|
|
334
334
|
|
|
335
335
|
Args:
|