digitalkin 0.2.12__py3-none-any.whl → 0.2.14__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 (42) hide show
  1. digitalkin/__version__.py +1 -1
  2. digitalkin/grpc_servers/_base_server.py +15 -17
  3. digitalkin/grpc_servers/module_server.py +9 -10
  4. digitalkin/grpc_servers/module_servicer.py +199 -85
  5. digitalkin/grpc_servers/registry_server.py +3 -6
  6. digitalkin/grpc_servers/registry_servicer.py +18 -19
  7. digitalkin/grpc_servers/utils/exceptions.py +4 -0
  8. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +3 -5
  9. digitalkin/logger.py +45 -1
  10. digitalkin/models/module/__init__.py +2 -1
  11. digitalkin/models/module/module.py +1 -0
  12. digitalkin/models/module/module_types.py +1 -0
  13. digitalkin/modules/_base_module.py +124 -7
  14. digitalkin/modules/archetype_module.py +11 -1
  15. digitalkin/modules/job_manager/base_job_manager.py +181 -0
  16. digitalkin/modules/job_manager/job_manager_models.py +44 -0
  17. digitalkin/modules/job_manager/single_job_manager.py +285 -0
  18. digitalkin/modules/job_manager/taskiq_broker.py +214 -0
  19. digitalkin/modules/job_manager/taskiq_job_manager.py +286 -0
  20. digitalkin/modules/tool_module.py +2 -1
  21. digitalkin/modules/trigger_module.py +3 -1
  22. digitalkin/services/cost/default_cost.py +8 -4
  23. digitalkin/services/cost/grpc_cost.py +15 -7
  24. digitalkin/services/filesystem/default_filesystem.py +2 -4
  25. digitalkin/services/filesystem/grpc_filesystem.py +8 -5
  26. digitalkin/services/setup/__init__.py +1 -0
  27. digitalkin/services/setup/default_setup.py +10 -12
  28. digitalkin/services/setup/grpc_setup.py +8 -10
  29. digitalkin/services/storage/default_storage.py +11 -5
  30. digitalkin/services/storage/grpc_storage.py +23 -8
  31. digitalkin/utils/arg_parser.py +5 -48
  32. digitalkin/utils/development_mode_action.py +51 -0
  33. {digitalkin-0.2.12.dist-info → digitalkin-0.2.14.dist-info}/METADATA +46 -15
  34. {digitalkin-0.2.12.dist-info → digitalkin-0.2.14.dist-info}/RECORD +41 -34
  35. {digitalkin-0.2.12.dist-info → digitalkin-0.2.14.dist-info}/WHEEL +1 -1
  36. modules/cpu_intensive_module.py +281 -0
  37. modules/minimal_llm_module.py +240 -58
  38. modules/storage_module.py +5 -6
  39. modules/text_transform_module.py +1 -1
  40. digitalkin/modules/job_manager.py +0 -177
  41. {digitalkin-0.2.12.dist-info → digitalkin-0.2.14.dist-info}/licenses/LICENSE +0 -0
  42. {digitalkin-0.2.12.dist-info → digitalkin-0.2.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,214 @@
1
+ """Taskiq broker & RSTREAM producer for the job manager."""
2
+
3
+ import asyncio
4
+ import json
5
+ import logging
6
+ import os
7
+ import pickle # noqa: S403
8
+
9
+ from rstream import Producer
10
+ from rstream.exceptions import PreconditionFailed
11
+ from taskiq import Context, TaskiqDepends, TaskiqMessage
12
+ from taskiq.abc.formatter import TaskiqFormatter
13
+ from taskiq.compat import model_validate
14
+ from taskiq.message import BrokerMessage
15
+ from taskiq_aio_pika import AioPikaBroker
16
+
17
+ from digitalkin.logger import logger
18
+ from digitalkin.models.module.module_types import OutputModelT
19
+ from digitalkin.modules._base_module import BaseModule
20
+ from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
21
+ from digitalkin.modules.job_manager.job_manager_models import StreamCodeModel
22
+ from digitalkin.services.services_config import ServicesConfig
23
+ from digitalkin.services.services_models import ServicesMode
24
+
25
+ logging.getLogger("taskiq").setLevel(logging.INFO)
26
+ logging.getLogger("aiormq").setLevel(logging.INFO)
27
+ logging.getLogger("aio_pika").setLevel(logging.INFO)
28
+ logging.getLogger("rstream").setLevel(logging.INFO)
29
+
30
+
31
+ class PickleFormatter(TaskiqFormatter):
32
+ """Formatter that pickles the JSON-dumped TaskiqMessage.
33
+
34
+ This lets you send arbitrary Python objects (classes, functions, etc.)
35
+ by first converting to JSON-safe primitives, then pickling that string.
36
+ """
37
+
38
+ def dumps(self, message: TaskiqMessage) -> BrokerMessage: # noqa: PLR6301
39
+ """Dumps message from python complex object to JSON.
40
+
41
+ Args:
42
+ message: TaskIQ message
43
+
44
+ Returns:
45
+ BrokerMessage with mandatory information for TaskIQ
46
+ """
47
+ payload: bytes = pickle.dumps(message)
48
+
49
+ return BrokerMessage(
50
+ task_id=message.task_id,
51
+ task_name=message.task_name,
52
+ message=payload,
53
+ labels=message.labels,
54
+ )
55
+
56
+ def loads(self, message: bytes) -> TaskiqMessage: # noqa: PLR6301
57
+ """Recreate Python object from bytes.
58
+
59
+ Args:
60
+ message: Broker message from bytes.
61
+
62
+ Returns:
63
+ message with TaskIQ format
64
+ """
65
+ json_str = pickle.loads(message) # noqa: S301
66
+ return model_validate(TaskiqMessage, json_str)
67
+
68
+
69
+ def define_producer() -> Producer:
70
+ """Get from the env the connection parameter to RabbitMQ.
71
+
72
+ Returns:
73
+ Producer
74
+ """
75
+ host: str = os.environ.get("RABBITMQ_RSTREAM_HOST", "localhost")
76
+ port: str = os.environ.get("RABBITMQ_RSTREAM_PORT", "5552")
77
+ username: str = os.environ.get("RABBITMQ_RSTREAM_USERNAME", "guest")
78
+ password: str = os.environ.get("RABBITMQ_RSTREAM_PASSWORD", "guest")
79
+
80
+ logger.info("Connection to RabbitMQ: %s:%s.", host, port)
81
+ return Producer(host=host, port=int(port), username=username, password=password)
82
+
83
+
84
+ async def init_rstream() -> None:
85
+ """Init a stream for every tasks."""
86
+ try:
87
+ await RSTREAM_PRODUCER.create_stream(
88
+ STREAM,
89
+ exists_ok=True,
90
+ arguments={"max-length-bytes": STREAM_RETENTION},
91
+ )
92
+ except PreconditionFailed:
93
+ logger.warning("stream already exist")
94
+
95
+
96
+ def define_broker() -> AioPikaBroker:
97
+ """Define broker with from env paramter.
98
+
99
+ Returns:
100
+ Broker: connected to RabbitMQ and with custom formatter.
101
+ """
102
+ host: str = os.environ.get("RABBITMQ_BROKER_HOST", "localhost")
103
+ port: str = os.environ.get("RABBITMQ_BROKER_PORT", "5672")
104
+ username: str = os.environ.get("RABBITMQ_BROKER_USERNAME", "guest")
105
+ password: str = os.environ.get("RABBITMQ_BROKER_PASSWORD", "guest")
106
+
107
+ broker = AioPikaBroker(
108
+ f"amqp://{username}:{password}@{host}:{port}",
109
+ startup=[init_rstream],
110
+ )
111
+ broker.formatter = PickleFormatter()
112
+ return broker
113
+
114
+
115
+ STREAM = "taskiq_data"
116
+ STREAM_RETENTION = 200_000
117
+ RSTREAM_PRODUCER = define_producer()
118
+ TASKIQ_BROKER = define_broker()
119
+
120
+
121
+ async def send_message_to_stream(job_id: str, output_data: OutputModelT) -> None: # type: ignore
122
+ """Callback define to add a message frame to the Rstream.
123
+
124
+ Args:
125
+ job_id: id of the job that sent the message
126
+ output_data: message body as a OutputModelT or error / stream_code
127
+ """
128
+ body = json.dumps({"job_id": job_id, "output_data": output_data.model_dump()}).encode("utf-8")
129
+ await RSTREAM_PRODUCER.send(stream=STREAM, message=body)
130
+
131
+
132
+ @TASKIQ_BROKER.task
133
+ async def run_start_module(
134
+ mission_id: str,
135
+ setup_version_id: str,
136
+ module_class: type[BaseModule],
137
+ services_mode: ServicesMode,
138
+ input_data: dict,
139
+ setup_data: dict,
140
+ context: Context = TaskiqDepends(),
141
+ ) -> None:
142
+ """TaskIQ task allowing a module to compute in the background asynchronously.
143
+
144
+ Args:
145
+ mission_id: str,
146
+ setup_version_id: The setup ID associated with the module.
147
+ module_class: type[BaseModule],
148
+ services_mode: ServicesMode,
149
+ input_data: dict,
150
+ setup_data: dict,
151
+ context: Allow TaskIQ context access
152
+ """
153
+ logger.warning("%s", services_mode)
154
+ services_config = ServicesConfig(
155
+ services_config_strategies=module_class.services_config_strategies,
156
+ services_config_params=module_class.services_config_params,
157
+ mode=services_mode,
158
+ )
159
+ setattr(module_class, "services_config", services_config)
160
+ logger.warning("%s | %s", services_config, module_class.services_config)
161
+
162
+ job_id = context.message.task_id
163
+ 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)
165
+
166
+ await module.start(
167
+ input_data,
168
+ setup_data,
169
+ callback,
170
+ # ensure that the callback is called when the task is done + allow asyncio to run
171
+ # TODO: should define a BaseModel for stream code / error
172
+ done_callback=lambda _: asyncio.create_task(callback(StreamCodeModel(code="__END_OF_STREAM__"))),
173
+ )
174
+
175
+
176
+ @TASKIQ_BROKER.task
177
+ async def run_config_module(
178
+ mission_id: str,
179
+ setup_version_id: str,
180
+ module_class: type[BaseModule],
181
+ services_mode: ServicesMode,
182
+ config_setup_data: dict,
183
+ setup_data: dict,
184
+ context: Context = TaskiqDepends(),
185
+ ) -> None:
186
+ """TaskIQ task allowing a module to compute in the background asynchronously.
187
+
188
+ Args:
189
+ mission_id: str,
190
+ setup_version_id: The setup ID associated with the module.
191
+ module_class: type[BaseModule],
192
+ services_mode: ServicesMode,
193
+ config_setup_data: dict,
194
+ setup_data: dict,
195
+ context: Allow TaskIQ context access
196
+ """
197
+ logger.warning("%s", services_mode)
198
+ services_config = ServicesConfig(
199
+ services_config_strategies=module_class.services_config_strategies,
200
+ services_config_params=module_class.services_config_params,
201
+ mode=services_mode,
202
+ )
203
+ setattr(module_class, "services_config", services_config)
204
+ logger.warning("%s | %s", services_config, module_class.services_config)
205
+
206
+ job_id = context.message.task_id
207
+ 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)
209
+
210
+ await module.start_config_setup(
211
+ module_class.create_config_setup_model(config_setup_data),
212
+ module_class.create_setup_model(setup_data),
213
+ callback,
214
+ )
@@ -0,0 +1,286 @@
1
+ """Taskiq job manager module."""
2
+
3
+ try:
4
+ import taskiq # noqa: F401
5
+
6
+ except ImportError:
7
+ msg = "Install digitalkin[taskiq] to use this functionality\n$ uv pip install digitalkin[taskiq]."
8
+ raise ImportError(msg)
9
+
10
+ import asyncio
11
+ import contextlib
12
+ import json
13
+ import os
14
+ from collections.abc import AsyncGenerator, AsyncIterator
15
+ from contextlib import asynccontextmanager
16
+ from typing import TYPE_CHECKING, Any, Generic
17
+
18
+ from rstream import Consumer, ConsumerOffsetSpecification, MessageContext, OffsetType
19
+
20
+ from digitalkin.logger import logger
21
+ from digitalkin.models.module import ConfigSetupModelT, InputModelT, SetupModelT
22
+ from digitalkin.models.module.module import ModuleStatus
23
+ from digitalkin.modules._base_module import BaseModule
24
+ from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
25
+ from digitalkin.modules.job_manager.taskiq_broker import STREAM, STREAM_RETENTION, TASKIQ_BROKER
26
+ from digitalkin.services.services_models import ServicesMode
27
+
28
+ if TYPE_CHECKING:
29
+ from taskiq.task import AsyncTaskiqTask
30
+
31
+
32
+ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigSetupModelT]):
33
+ """Taskiq job manager for running modules in Taskiq tasks."""
34
+
35
+ services_mode: ServicesMode
36
+
37
+ @staticmethod
38
+ def _define_consumer() -> Consumer:
39
+ """Get from the env the connection parameter to RabbitMQ.
40
+
41
+ Returns:
42
+ Consumer
43
+ """
44
+ host: str = os.environ.get("RABBITMQ_RSTREAM_HOST", "localhost")
45
+ port: str = os.environ.get("RABBITMQ_RSTREAM_PORT", "5552")
46
+ username: str = os.environ.get("RABBITMQ_RSTREAM_USERNAME", "guest")
47
+ password: str = os.environ.get("RABBITMQ_RSTREAM_PASSWORD", "guest")
48
+
49
+ logger.info("Connection to RabbitMQ: %s:%s.", host, port)
50
+ return Consumer(host=host, port=int(port), username=username, password=password)
51
+
52
+ async def _on_message(self, message: bytes, message_context: MessageContext) -> None: # noqa: ARG002
53
+ """Internal callback: parse JSON and route to the correct job queue."""
54
+ try:
55
+ data = json.loads(message.decode("utf-8"))
56
+ except json.JSONDecodeError:
57
+ return
58
+ job_id = data.get("job_id")
59
+ if not job_id:
60
+ return
61
+ queue = self.job_queues.get(job_id)
62
+ if queue:
63
+ await queue.put(data.get("output_data"))
64
+
65
+ async def _start(self) -> None:
66
+ await TASKIQ_BROKER.startup()
67
+
68
+ self.stream_consumer = self._define_consumer()
69
+
70
+ await self.stream_consumer.create_stream(
71
+ STREAM,
72
+ exists_ok=True,
73
+ arguments={"max-length-bytes": STREAM_RETENTION},
74
+ )
75
+ await self.stream_consumer.start()
76
+
77
+ start_spec = ConsumerOffsetSpecification(OffsetType.LAST)
78
+ # on_message use bytes instead of AMQPMessage
79
+ await self.stream_consumer.subscribe(
80
+ stream=STREAM,
81
+ subscriber_name=f"""subscriber_{os.environ.get("SERVER_NAME", "module_servicer")}""",
82
+ callback=self._on_message, # type: ignore
83
+ offset_specification=start_spec,
84
+ )
85
+ self.stream_consumer_task = asyncio.create_task(
86
+ self.stream_consumer.run(),
87
+ name="stream_consumer_task",
88
+ )
89
+
90
+ async def _stop(self) -> None:
91
+ # Signal the consumer to stop
92
+ await self.stream_consumer.close()
93
+ # Cancel the background task
94
+ self.stream_consumer_task.cancel()
95
+ with contextlib.suppress(asyncio.CancelledError):
96
+ await self.stream_consumer_task
97
+
98
+ def __init__(
99
+ self,
100
+ module_class: type[BaseModule],
101
+ services_mode: ServicesMode,
102
+ ) -> None:
103
+ """Initialize the Taskiq job manager."""
104
+ super().__init__(module_class, services_mode)
105
+
106
+ logger.warning("TaskiqJobManager initialized with app: %s", TASKIQ_BROKER)
107
+ self.services_mode = services_mode
108
+ self.job_queues: dict[str, asyncio.Queue] = {}
109
+ self.max_queue_size = 1000
110
+
111
+ async def generate_config_setup_module_response(self, job_id: str) -> SetupModelT:
112
+ """Generate a stream consumer for a module's output data.
113
+
114
+ This method creates an asynchronous generator that streams output data
115
+ from a specific module job. If the module does not exist, it generates
116
+ an error message.
117
+
118
+ Args:
119
+ job_id: The unique identifier of the job.
120
+
121
+ Returns:
122
+ SetupModelT: the SetupModelT object fully processed.
123
+ """
124
+ queue: asyncio.Queue = asyncio.Queue(maxsize=self.max_queue_size)
125
+ self.job_queues[job_id] = queue
126
+
127
+ try:
128
+ item = await queue.get()
129
+ queue.task_done()
130
+ return item
131
+ finally:
132
+ logger.info(f"generate_config_setup_module_response: {job_id=}: {self.job_queues[job_id].empty()}")
133
+ self.job_queues.pop(job_id, None)
134
+
135
+ async def create_config_setup_instance_job(
136
+ self,
137
+ config_setup_data: ConfigSetupModelT,
138
+ setup_data: SetupModelT,
139
+ mission_id: str,
140
+ setup_version_id: str,
141
+ ) -> str:
142
+ """Create and start a new module setup configuration job.
143
+
144
+ This method initializes a new module job, assigns it a unique job ID,
145
+ and starts the config setup it in the background.
146
+
147
+ Args:
148
+ config_setup_data: The input data required to start the job.
149
+ setup_data: The setup configuration for the module.
150
+ mission_id: The mission ID associated with the job.
151
+ setup_version_id: The setup ID.
152
+
153
+ Returns:
154
+ str: The unique identifier (job ID) of the created job.
155
+
156
+ Raises:
157
+ TypeError: If the function is called with bad data type.
158
+ ValueError: If the module fails to start.
159
+ """
160
+ task = TASKIQ_BROKER.find_task("digitalkin.modules.job_manager.taskiq_broker:run_config_module")
161
+
162
+ if task is None:
163
+ msg = "Task not found"
164
+ raise ValueError(msg)
165
+
166
+ if config_setup_data is None:
167
+ msg = "config_setup_data must be a valid model with model_dump method"
168
+ raise TypeError(msg)
169
+
170
+ running_task: AsyncTaskiqTask[Any] = await task.kiq(
171
+ mission_id,
172
+ setup_version_id,
173
+ self.module_class,
174
+ self.services_mode,
175
+ config_setup_data.model_dump(), # type: ignore
176
+ setup_data.model_dump(),
177
+ )
178
+
179
+ job_id = running_task.task_id
180
+ result = await running_task.wait_result(timeout=10)
181
+ logger.info("Job %s with data %s", job_id, result)
182
+ return job_id
183
+
184
+ @asynccontextmanager # type: ignore
185
+ async def generate_stream_consumer(self, job_id: str) -> AsyncIterator[AsyncGenerator[dict[str, Any], None]]: # type: ignore
186
+ """Generate a stream consumer for the RStream stream.
187
+
188
+ Args:
189
+ job_id: The job ID to filter messages.
190
+
191
+ Yields:
192
+ messages: The stream messages from the associated module.
193
+ """
194
+ queue: asyncio.Queue = asyncio.Queue(maxsize=self.max_queue_size)
195
+ self.job_queues[job_id] = queue
196
+
197
+ async def _stream() -> AsyncGenerator[dict[str, Any], Any]:
198
+ """Generate the stream allowing flowless communication.
199
+
200
+ Yields:
201
+ dict: generated object from the module
202
+ """
203
+ while True:
204
+ item = await queue.get()
205
+ queue.task_done()
206
+ yield item
207
+
208
+ while True:
209
+ try:
210
+ item = queue.get_nowait()
211
+ except asyncio.QueueEmpty:
212
+ break
213
+ queue.task_done()
214
+ yield item
215
+
216
+ try:
217
+ yield _stream()
218
+ finally:
219
+ self.job_queues.pop(job_id, None)
220
+
221
+ async def create_module_instance_job(
222
+ self,
223
+ input_data: InputModelT,
224
+ setup_data: SetupModelT,
225
+ mission_id: str,
226
+ setup_version_id: str,
227
+ ) -> str:
228
+ """Launches the module_task in Taskiq, returns the Taskiq task id as job_id.
229
+
230
+ Args:
231
+ input_data: Input data for the module
232
+ setup_data: Setup data for the module
233
+ mission_id: Mission ID for the module
234
+ setup_version_id: The setup ID associated with the module.
235
+
236
+ Returns:
237
+ job_id: The Taskiq task id.
238
+
239
+ Raises:
240
+ ValueError: If the task is not found.
241
+ """
242
+ task = TASKIQ_BROKER.find_task("digitalkin.modules.job_manager.taskiq_broker:run_start_module")
243
+
244
+ if task is None:
245
+ msg = "Task not found"
246
+ raise ValueError(msg)
247
+
248
+ running_task: AsyncTaskiqTask[Any] = await task.kiq(
249
+ mission_id,
250
+ setup_version_id,
251
+ self.module_class,
252
+ self.services_mode,
253
+ input_data.model_dump(),
254
+ setup_data.model_dump(),
255
+ )
256
+ job_id = running_task.task_id
257
+ result = await running_task.wait_result(timeout=10)
258
+ logger.debug("Job %s with data %s", job_id, result)
259
+ return job_id
260
+
261
+ async def stop_module(self, job_id: str) -> bool:
262
+ """Revoke (terminate) the Taskiq task with id.
263
+
264
+ Args:
265
+ job_id: The Taskiq task id to stop.
266
+
267
+ Raises:
268
+ bool: True if the task was successfully revoked, False otherwise.
269
+ """
270
+ msg = "stop_module not implemented in TaskiqJobManager"
271
+ raise NotImplementedError(msg)
272
+
273
+ async def stop_all_modules(self) -> None:
274
+ """Stop all running modules."""
275
+ msg = "stop_all_modules not implemented in TaskiqJobManager"
276
+ raise NotImplementedError(msg)
277
+
278
+ async def get_module_status(self, job_id: str) -> ModuleStatus | None:
279
+ """Query a module status."""
280
+ msg = "get_module_status not implemented in TaskiqJobManager"
281
+ raise NotImplementedError(msg)
282
+
283
+ async def list_modules(self) -> dict[str, dict[str, Any]]:
284
+ """List all modules."""
285
+ msg = "list_modules not implemented in TaskiqJobManager"
286
+ raise NotImplementedError(msg)
@@ -3,8 +3,9 @@
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
6
7
  from digitalkin.modules._base_module import BaseModule # type: ignore
7
8
 
8
9
 
9
- class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
10
+ class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,ConfigSetupModelT,], ABC):
10
11
  """ToolModule extends BaseModule to implement specific module types."""
@@ -2,8 +2,10 @@
2
2
 
3
3
  from abc import ABC
4
4
 
5
+ from digitalkin.models.module.module_types import ConfigSetupModelT
5
6
  from digitalkin.modules._base_module import BaseModule, InputModelT, OutputModelT, SecretModelT, SetupModelT
6
7
 
7
8
 
8
- class TriggerModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
9
+ class TriggerModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,
10
+ ConfigSetupModelT,], ABC):
9
11
  """TriggerModule extends BaseModule to implement specific module types."""
@@ -1,11 +1,15 @@
1
1
  """Default cost."""
2
2
 
3
- import logging
4
3
  from typing import Literal
5
4
 
6
- from digitalkin.services.cost.cost_strategy import CostConfig, CostData, CostServiceError, CostStrategy, CostType
7
-
8
- logger = logging.getLogger(__name__)
5
+ from digitalkin.logger import logger
6
+ from digitalkin.services.cost.cost_strategy import (
7
+ CostConfig,
8
+ CostData,
9
+ CostServiceError,
10
+ CostStrategy,
11
+ CostType,
12
+ )
9
13
 
10
14
 
11
15
  class DefaultCost(CostStrategy):
@@ -1,6 +1,5 @@
1
- """This module implements the default Cost strategy."""
1
+ """This module implements the gRPC Cost strategy."""
2
2
 
3
- import logging
4
3
  from collections.abc import Generator
5
4
  from contextlib import contextmanager
6
5
  from typing import Any, Literal
@@ -11,9 +10,14 @@ from google.protobuf import json_format
11
10
  from digitalkin.grpc_servers.utils.exceptions import ServerError
12
11
  from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
13
12
  from digitalkin.grpc_servers.utils.models import ClientConfig
14
- from digitalkin.services.cost.cost_strategy import CostConfig, CostData, CostServiceError, CostStrategy, CostType
15
-
16
- logger = logging.getLogger(__name__)
13
+ from digitalkin.logger import logger
14
+ from digitalkin.services.cost.cost_strategy import (
15
+ CostConfig,
16
+ CostData,
17
+ CostServiceError,
18
+ CostStrategy,
19
+ CostType,
20
+ )
17
21
 
18
22
 
19
23
  class GrpcCost(CostStrategy, GrpcClientWrapper):
@@ -51,13 +55,17 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
51
55
  raise CostServiceError(msg) from e
52
56
 
53
57
  def __init__(
54
- self, mission_id: str, setup_version_id: str, config: dict[str, CostConfig], client_config: ClientConfig
58
+ self,
59
+ mission_id: str,
60
+ setup_version_id: str,
61
+ config: dict[str, CostConfig],
62
+ client_config: ClientConfig,
55
63
  ) -> None:
56
64
  """Initialize the cost."""
57
65
  super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
58
66
  channel = self._init_channel(client_config)
59
67
  self.stub = cost_service_pb2_grpc.CostServiceStub(channel)
60
- logger.info("Channel client 'Cost' initialized succesfully")
68
+ logger.debug("Channel client 'Cost' initialized succesfully")
61
69
 
62
70
  def add(
63
71
  self,
@@ -1,10 +1,10 @@
1
1
  """Default filesystem."""
2
2
 
3
- import logging
4
3
  import os
5
4
  import tempfile
6
5
  from pathlib import Path
7
6
 
7
+ from digitalkin.logger import logger
8
8
  from digitalkin.services.filesystem.filesystem_strategy import (
9
9
  FilesystemData,
10
10
  FilesystemServiceError,
@@ -12,8 +12,6 @@ from digitalkin.services.filesystem.filesystem_strategy import (
12
12
  FileType,
13
13
  )
14
14
 
15
- logger = logging.getLogger(__name__)
16
-
17
15
 
18
16
  class DefaultFilesystem(FilesystemStrategy):
19
17
  """Default state filesystem strategy."""
@@ -177,7 +175,7 @@ class DefaultFilesystem(FilesystemStrategy):
177
175
  try:
178
176
  os.remove(file_path)
179
177
  del self.db[name]
180
- logger.info("File %s successfully deleted.", name)
178
+ logger.debug("File %s successfully deleted.", name)
181
179
 
182
180
  except OSError:
183
181
  msg = f"Error deleting file {name} from filesystem"
@@ -1,16 +1,21 @@
1
1
  """Grpc filesystem."""
2
2
 
3
- import logging
4
3
  from collections.abc import Generator
5
4
  from contextlib import contextmanager
6
5
  from typing import Any
7
6
 
8
- from digitalkin_proto.digitalkin.filesystem.v2 import filesystem_pb2, filesystem_service_pb2_grpc
9
- from digitalkin_proto.digitalkin.filesystem.v2.filesystem_pb2 import FileType as FileTypeProto
7
+ from digitalkin_proto.digitalkin.filesystem.v2 import (
8
+ filesystem_pb2,
9
+ filesystem_service_pb2_grpc,
10
+ )
11
+ from digitalkin_proto.digitalkin.filesystem.v2.filesystem_pb2 import (
12
+ FileType as FileTypeProto,
13
+ )
10
14
 
11
15
  from digitalkin.grpc_servers.utils.exceptions import ServerError
12
16
  from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
13
17
  from digitalkin.grpc_servers.utils.models import ClientConfig
18
+ from digitalkin.logger import logger
14
19
  from digitalkin.services.filesystem.filesystem_strategy import (
15
20
  FilesystemData,
16
21
  FilesystemServiceError,
@@ -18,8 +23,6 @@ from digitalkin.services.filesystem.filesystem_strategy import (
18
23
  FileType,
19
24
  )
20
25
 
21
- logger = logging.getLogger(__name__)
22
-
23
26
 
24
27
  class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
25
28
  """Default state filesystem strategy."""
@@ -0,0 +1 @@
1
+ """This module is responsible for handling the setup service."""