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.
Files changed (28) hide show
  1. digitalkin/__version__.py +1 -1
  2. digitalkin/grpc_servers/module_servicer.py +9 -7
  3. digitalkin/models/module/__init__.py +6 -6
  4. digitalkin/models/module/module_types.py +76 -14
  5. digitalkin/modules/_base_module.py +71 -46
  6. digitalkin/modules/archetype_module.py +0 -2
  7. digitalkin/modules/job_manager/base_job_manager.py +8 -7
  8. digitalkin/modules/job_manager/single_job_manager.py +19 -10
  9. digitalkin/modules/job_manager/taskiq_broker.py +6 -4
  10. digitalkin/modules/job_manager/taskiq_job_manager.py +9 -5
  11. digitalkin/modules/tool_module.py +1 -2
  12. digitalkin/services/base_strategy.py +3 -1
  13. digitalkin/services/cost/cost_strategy.py +9 -2
  14. digitalkin/services/cost/default_cost.py +3 -2
  15. digitalkin/services/cost/grpc_cost.py +2 -1
  16. digitalkin/services/filesystem/default_filesystem.py +9 -7
  17. digitalkin/services/filesystem/filesystem_strategy.py +10 -3
  18. digitalkin/services/filesystem/grpc_filesystem.py +10 -8
  19. digitalkin/services/services_config.py +3 -2
  20. digitalkin/services/storage/default_storage.py +2 -1
  21. digitalkin/services/storage/grpc_storage.py +6 -5
  22. digitalkin/services/storage/storage_strategy.py +9 -2
  23. digitalkin/utils/package_discover.py +2 -2
  24. {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/METADATA +13 -13
  25. {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/RECORD +28 -28
  26. {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/WHEEL +0 -0
  27. {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/licenses/LICENSE +0 -0
  28. {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 ConfigSetupModelT, InputModelT, OutputModelT, SetupModelT
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, ConfigSetupModelT]):
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: ConfigSetupModelT,
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
- setup_version_id: The setup ID associated with the module.
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(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
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
- await self.job_specific_callback(self.add_to_queue, job_id),
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 ConfigSetupModelT, InputModelT, SetupModelT
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, ConfigSetupModelT]):
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: ConfigSetupModelT,
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,ConfigSetupModelT,], ABC):
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__(self, mission_id: str, setup_version_id: str, config: dict[str, CostConfig]) -> None:
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.mission_id)
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.mission_id,
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=file.status if hasattr(file, "status") and file.status else "ACTIVE",
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.mission_id)
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__(self, mission_id: str, setup_version_id: str, config: dict[str, Any] | None = None) -> None:
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
- ) -> tuple[FilesystemRecord, bytes | None]:
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.mission_id,
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.mission_id,
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.mission_id,
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.mission_id,
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.mission_id,
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.mission_id,
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.mission_id,
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.exception("gRPC ReadRecord failed for %s:%s", collection, record_id)
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.exception(
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.exception("gRPC ListRecords failed for %s", collection)
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.exception("gRPC RemoveCollection failed for %s", collection)
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__(self, mission_id: str, setup_version_id: str, config: dict[str, type[BaseModel]]) -> None:
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 InputTrigger
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: InputTrigger) -> TriggerHandler:
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: