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
digitalkin/__version__.py CHANGED
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.2.12"
8
+ __version__ = "0.2.14"
@@ -2,7 +2,6 @@
2
2
 
3
3
  import abc
4
4
  import asyncio
5
- import logging
6
5
  from collections.abc import Callable
7
6
  from concurrent import futures
8
7
  from pathlib import Path
@@ -20,8 +19,7 @@ from digitalkin.grpc_servers.utils.exceptions import (
20
19
  )
21
20
  from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
22
21
  from digitalkin.grpc_servers.utils.types import GrpcServer, ServiceDescriptor, T
23
-
24
- logger = logging.getLogger(__name__)
22
+ from digitalkin.logger import logger
25
23
 
26
24
 
27
25
  class BaseServer(abc.ABC):
@@ -132,7 +130,7 @@ class BaseServer(abc.ABC):
132
130
  # reflection queries with detailed service information
133
131
  reflection.enable_server_reflection(service_names, self.server)
134
132
 
135
- logger.info("Added gRPC reflection service with services: %s", service_names)
133
+ logger.debug("Added gRPC reflection service with services: %s", service_names)
136
134
  except ImportError:
137
135
  logger.warning("Could not enable reflection: grpcio-reflection package not installed")
138
136
  except Exception as e:
@@ -165,7 +163,7 @@ class BaseServer(abc.ABC):
165
163
  if service_name not in self._service_names:
166
164
  self._service_names.append(service_name)
167
165
 
168
- logger.info("Added gRPC health checking service")
166
+ logger.debug("Added gRPC health checking service")
169
167
 
170
168
  # Set all services as SERVING
171
169
  for service_name in self._service_names:
@@ -255,7 +253,7 @@ class BaseServer(abc.ABC):
255
253
  sync_server = cast("grpc.Server", server)
256
254
  sync_server.add_secure_port(self.config.address, server_credentials)
257
255
 
258
- logger.info("Added secure port %s", self.config.address)
256
+ logger.debug("Added secure port %s", self.config.address)
259
257
  except Exception as e:
260
258
  msg = f"Failed to configure secure port: {e}"
261
259
  raise SecurityError(msg) from e
@@ -277,7 +275,7 @@ class BaseServer(abc.ABC):
277
275
  sync_server = cast("grpc.Server", server)
278
276
  sync_server.add_insecure_port(self.config.address)
279
277
 
280
- logger.info("Added insecure port %s", self.config.address)
278
+ logger.debug("Added insecure port %s", self.config.address)
281
279
  except Exception as e:
282
280
  msg = f"Failed to add insecure port: {e}"
283
281
  raise ConfigurationError(msg) from e
@@ -301,7 +299,7 @@ class BaseServer(abc.ABC):
301
299
  self._add_reflection()
302
300
 
303
301
  # Start the server
304
- logger.info("Starting gRPC server on %s", self.config.address)
302
+ logger.debug("Starting gRPC server on %s", self.config.address)
305
303
  try:
306
304
  if self.config.mode == ServerMode.ASYNC:
307
305
  # For async server, use the event loop
@@ -314,7 +312,7 @@ class BaseServer(abc.ABC):
314
312
  # For sync server, directly call start
315
313
  sync_server = cast("grpc.Server", self.server)
316
314
  sync_server.start()
317
- logger.info("✅ gRPC server started on %s", self.config.address)
315
+ logger.debug("✅ gRPC server started on %s", self.config.address)
318
316
  except Exception as e:
319
317
  logger.exception("❎ Error starting server")
320
318
  msg = f"Failed to start server: {e}"
@@ -351,7 +349,7 @@ class BaseServer(abc.ABC):
351
349
  self._add_reflection()
352
350
 
353
351
  # Start the server
354
- logger.info("Starting gRPC server on %s", self.config.address)
352
+ logger.debug("Starting gRPC server on %s", self.config.address)
355
353
  try:
356
354
  if self.config.mode == ServerMode.ASYNC:
357
355
  await self._start_async()
@@ -359,7 +357,7 @@ class BaseServer(abc.ABC):
359
357
  # For sync server in async context
360
358
  sync_server = cast("grpc.Server", self.server)
361
359
  sync_server.start()
362
- logger.info("✅ gRPC server started on %s", self.config.address)
360
+ logger.debug("✅ gRPC server started on %s", self.config.address)
363
361
  except Exception as e:
364
362
  logger.exception("❎ Error starting server")
365
363
  msg = f"Failed to start server: {e}"
@@ -375,7 +373,7 @@ class BaseServer(abc.ABC):
375
373
  logger.warning("Attempted to stop server, but no server is running")
376
374
  return
377
375
 
378
- logger.info("Stopping gRPC server...")
376
+ logger.debug("Stopping gRPC server...")
379
377
  if self.config.mode == ServerMode.ASYNC:
380
378
  # We'll use a different approach that works whether we're in a running event loop or not
381
379
  try:
@@ -392,13 +390,13 @@ class BaseServer(abc.ABC):
392
390
  )
393
391
  # Set server to None to avoid further operations
394
392
  self.server = None
395
- logger.info("✅ gRPC server marked as stopped")
393
+ logger.debug("✅ gRPC server marked as stopped")
396
394
  return
397
395
  # If not in a running event loop, use run_until_complete
398
396
  loop.run_until_complete(self._stop_async(grace))
399
397
  except RuntimeError:
400
398
  # Event loop issues - try with a new loop
401
- logger.info("Creating new event loop for shutdown")
399
+ logger.debug("Creating new event loop for shutdown")
402
400
  try:
403
401
  new_loop = asyncio.new_event_loop()
404
402
  asyncio.set_event_loop(new_loop)
@@ -410,7 +408,7 @@ class BaseServer(abc.ABC):
410
408
  sync_server = cast("grpc.Server", self.server)
411
409
  sync_server.stop(grace=grace)
412
410
 
413
- logger.info("✅ gRPC server stopped")
411
+ logger.debug("✅ gRPC server stopped")
414
412
  self.server = None
415
413
 
416
414
  async def _stop_async(self, grace: float | None = None) -> None:
@@ -437,7 +435,7 @@ class BaseServer(abc.ABC):
437
435
  logger.warning("Attempted to stop server, but no server is running")
438
436
  return
439
437
 
440
- logger.info("Stopping gRPC server asynchronously...")
438
+ logger.debug("Stopping gRPC server asynchronously...")
441
439
  if self.config.mode == ServerMode.ASYNC:
442
440
  await self._stop_async(grace)
443
441
  else:
@@ -445,7 +443,7 @@ class BaseServer(abc.ABC):
445
443
  sync_server = cast("grpc.Server", self.server)
446
444
  sync_server.stop(grace=grace)
447
445
 
448
- logger.info("✅ gRPC server stopped")
446
+ logger.debug("✅ gRPC server stopped")
449
447
  self.server = None
450
448
 
451
449
  def wait_for_termination(self) -> None:
@@ -1,6 +1,5 @@
1
1
  """Module gRPC server implementation for DigitalKin."""
2
2
 
3
- import logging
4
3
  from pathlib import Path
5
4
  import uuid
6
5
 
@@ -25,8 +24,7 @@ from digitalkin_proto.digitalkin.module_registry.v2 import (
25
24
  module_registry_service_pb2_grpc,
26
25
  registration_pb2,
27
26
  )
28
-
29
- logger = logging.getLogger(__name__)
27
+ from digitalkin.logger import logger
30
28
 
31
29
 
32
30
  class ModuleServer(BaseServer):
@@ -70,14 +68,14 @@ class ModuleServer(BaseServer):
70
68
  msg = "Server must be created before registering servicers"
71
69
  raise RuntimeError(msg)
72
70
 
73
- logger.info("Registering module servicer for %s", self.module_class.__name__)
71
+ logger.debug("Registering module servicer for %s", self.module_class.__name__)
74
72
  self.module_servicer = ModuleServicer(self.module_class)
75
73
  self.register_servicer(
76
74
  self.module_servicer,
77
75
  module_service_pb2_grpc.add_ModuleServiceServicer_to_server,
78
76
  service_descriptor=module_service_pb2.DESCRIPTOR,
79
77
  )
80
- logger.info("Registered Module servicer")
78
+ logger.debug("Registered Module servicer")
81
79
 
82
80
  def start(self) -> None:
83
81
  """Start the module server and register with the registry if configured."""
@@ -115,6 +113,7 @@ class ModuleServer(BaseServer):
115
113
  logger.critical(
116
114
  "Setup post init started with config: %s", self.client_config
117
115
  )
116
+ await self.module_servicer.job_manager._start()
118
117
  self.module_servicer.setup.__post_init__(self.client_config)
119
118
 
120
119
  def stop(self, grace: float | None = None) -> None:
@@ -134,7 +133,7 @@ class ModuleServer(BaseServer):
134
133
  Raises:
135
134
  ServerError: If communication with the registry server fails.
136
135
  """
137
- logger.info(
136
+ logger.debug(
138
137
  "Registering module with registry at %s",
139
138
  self.server_config.registry_address,
140
139
  )
@@ -172,7 +171,7 @@ class ModuleServer(BaseServer):
172
171
 
173
172
  try:
174
173
  # Call the register method
175
- logger.info(
174
+ logger.debug(
176
175
  "Request sent to registry for module: %s:%s",
177
176
  self.module_class.metadata["name"],
178
177
  self.module_class.metadata["module_id"],
@@ -180,7 +179,7 @@ class ModuleServer(BaseServer):
180
179
  response = stub.RegisterModule(request)
181
180
 
182
181
  if response.success:
183
- logger.info("Module registered successfully")
182
+ logger.debug("Module registered successfully")
184
183
  else:
185
184
  logger.error("Module registration failed")
186
185
  except grpc.RpcError:
@@ -193,7 +192,7 @@ class ModuleServer(BaseServer):
193
192
  Raises:
194
193
  ServerError: If communication with the registry server fails.
195
194
  """
196
- logger.info(
195
+ logger.debug(
197
196
  "Deregistering module from registry at %s",
198
197
  self.server_config.registry_address,
199
198
  )
@@ -214,7 +213,7 @@ class ModuleServer(BaseServer):
214
213
  response = stub.DeregisterModule(request)
215
214
 
216
215
  if response.success:
217
- logger.info("Module deregistered successfull")
216
+ logger.debug("Module deregistered successfull")
218
217
  else:
219
218
  logger.error("Module deregistration failed")
220
219
  except grpc.RpcError:
@@ -1,7 +1,6 @@
1
1
  """Module servicer implementation for DigitalKin."""
2
2
 
3
- import asyncio
4
- import logging
3
+ from argparse import ArgumentParser, Namespace
5
4
  from collections.abc import AsyncGenerator
6
5
  from typing import Any
7
6
 
@@ -15,19 +14,20 @@ from digitalkin_proto.digitalkin.module.v2 import (
15
14
  from google.protobuf import json_format, struct_pb2
16
15
 
17
16
  from digitalkin.grpc_servers.utils.exceptions import ServicerError
18
- from digitalkin.models.module import OutputModelT
17
+ from digitalkin.logger import logger
19
18
  from digitalkin.models.module.module import ModuleStatus
20
19
  from digitalkin.modules._base_module import BaseModule
21
- from digitalkin.modules.job_manager import JobManager
20
+ from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
21
+ from digitalkin.modules.job_manager.job_manager_models import JobManagerMode
22
22
  from digitalkin.services.services_models import ServicesMode
23
23
  from digitalkin.services.setup.default_setup import DefaultSetup
24
24
  from digitalkin.services.setup.grpc_setup import GrpcSetup
25
25
  from digitalkin.services.setup.setup_strategy import SetupStrategy
26
+ from digitalkin.utils.arg_parser import ArgParser
27
+ from digitalkin.utils.development_mode_action import DevelopmentModeMappingAction
26
28
 
27
- logger = logging.getLogger(__name__)
28
29
 
29
-
30
- class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
30
+ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
31
31
  """Implementation of the ModuleService.
32
32
 
33
33
  This servicer handles interactions with a DigitalKin module.
@@ -37,7 +37,31 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
37
37
  active_jobs: Dictionary tracking active module jobs.
38
38
  """
39
39
 
40
+ args: Namespace
40
41
  setup: SetupStrategy
42
+ job_manager: BaseJobManager
43
+
44
+ def _add_parser_args(self, parser: ArgumentParser) -> None:
45
+ super()._add_parser_args(parser)
46
+ parser.add_argument(
47
+ "-d",
48
+ "--dev-mode",
49
+ env_var="SERVICE_MODE",
50
+ choices=ServicesMode.__members__,
51
+ default="local",
52
+ action=DevelopmentModeMappingAction,
53
+ dest="services_mode",
54
+ help="Define Module Service configurations for endpoints",
55
+ )
56
+ parser.add_argument(
57
+ "-jm",
58
+ "--job-manager",
59
+ type=JobManagerMode,
60
+ choices=list(JobManagerMode),
61
+ default=JobManagerMode.SINGLE,
62
+ dest="job_manager_mode",
63
+ help="Define Module job manager configurations for load balancing",
64
+ )
41
65
 
42
66
  def __init__(self, module_class: type[BaseModule]) -> None:
43
67
  """Initialize the module servicer.
@@ -46,15 +70,72 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
46
70
  module_class: The module type to serve.
47
71
  """
48
72
  super().__init__()
49
- self.queue: asyncio.Queue = asyncio.Queue()
50
73
  self.module_class = module_class
51
- self.job_manager = JobManager(module_class)
52
- self.setup = GrpcSetup() if self.job_manager.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
74
+ job_manager_class = self.args.job_manager_mode.get_manager_class()
75
+ self.job_manager = job_manager_class(module_class, self.args.services_mode)
76
+
77
+ logger.debug(
78
+ "ModuleServicer initialized with job manager: %s | %s",
79
+ self.args.job_manager_mode,
80
+ self.job_manager,
81
+ )
82
+ self.setup = GrpcSetup() if self.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
83
+
84
+ async def ConfigSetupModule( # noqa: N802
85
+ self,
86
+ request: lifecycle_pb2.ConfigSetupModuleRequest,
87
+ context: grpc.aio.ServicerContext,
88
+ ) -> lifecycle_pb2.ConfigSetupModuleResponse:
89
+ """Configure the module setup.
90
+
91
+ Args:
92
+ request: The configuration request.
93
+ context: The gRPC context.
94
+
95
+ Returns:
96
+ A response indicating success or failure.
53
97
 
54
- async def add_to_queue(self, job_id: str, output_data: OutputModelT) -> None: # type: ignore
55
- """Callback used to add the output data to the queue of messages."""
56
- logger.info("JOB: %s added an output_data: %s", job_id, output_data)
57
- await self.queue.put({job_id: output_data})
98
+ Raises:
99
+ ServicerError: if the setup data is not returned or job creation fails.
100
+ """
101
+ logger.info("ConfigSetupVersion called for module: '%s'", self.module_class.__name__)
102
+ # Process the module input
103
+ # TODO: Secret should be used here as well
104
+ setup_version = request.setup_version
105
+ config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
106
+ setup_version_data = self.module_class.create_setup_model(
107
+ json_format.MessageToDict(request.setup_version.content)
108
+ )
109
+
110
+ if not setup_version_data:
111
+ msg = "No setup data returned."
112
+ raise ServicerError(msg)
113
+
114
+ if not config_setup_data:
115
+ msg = "No config setup data returned."
116
+ raise ServicerError(msg)
117
+
118
+ # create a task to run the module in background
119
+ job_id = await self.job_manager.create_config_setup_instance_job(
120
+ config_setup_data,
121
+ setup_version_data,
122
+ request.mission_id,
123
+ setup_version.id,
124
+ )
125
+
126
+ if job_id is None:
127
+ context.set_code(grpc.StatusCode.NOT_FOUND)
128
+ context.set_details("Failed to create module instance")
129
+ return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
130
+
131
+ updated_setup_data = await self.job_manager.generate_config_setup_module_response(job_id)
132
+ logger.warning(f"Updated setup data: {updated_setup_data=}")
133
+ setup_version.content = json_format.ParseDict(
134
+ updated_setup_data,
135
+ struct_pb2.Struct(),
136
+ ignore_unknown_fields=True,
137
+ )
138
+ return lifecycle_pb2.ConfigSetupModuleResponse(success=True, setup_version=setup_version)
58
139
 
59
140
  async def StartModule( # noqa: N802
60
141
  self,
@@ -87,47 +168,45 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
87
168
  if not setup_data_class:
88
169
  msg = "No setup data returned."
89
170
  raise ServicerError(msg)
90
- # TODO: Check failure of setup data format
171
+
91
172
  setup_data = self.module_class.create_setup_model(setup_data_class.current_setup_version.content)
92
173
 
93
- # setup_id should be use to request a precise setup from the module
94
- # Create a job for this execution
95
- result: tuple[str, BaseModule] = await self.job_manager.create_job(
174
+ # create a task to run the module in background
175
+ job_id = await self.job_manager.create_module_instance_job(
96
176
  input_data,
97
177
  setup_data,
98
178
  mission_id=request.mission_id,
99
179
  setup_version_id=setup_data_class.current_setup_version.id,
100
- callback=self.add_to_queue,
101
180
  )
102
- job_id, module = result
103
-
104
- while module.status == ModuleStatus.RUNNING or not self.queue.empty():
105
- output_data: dict = await self.queue.get()
106
-
107
- if job_id not in output_data or job_id not in self.job_manager.modules:
108
- message = f"Job {job_id} not found"
109
- logger.warning(message)
110
- context.set_code(grpc.StatusCode.NOT_FOUND)
111
- context.set_details(message)
112
- yield lifecycle_pb2.StartModuleResponse(success=False)
113
- return
114
-
115
- if output_data[job_id].get("error", None) is not None:
116
- context.set_code(output_data[job_id]["error"]["code"])
117
- context.set_details(output_data[job_id]["error"]["error_message"])
118
- yield lifecycle_pb2.StartModuleResponse(success=False)
119
- return
120
-
121
- output_proto = json_format.ParseDict(
122
- output_data[job_id],
123
- struct_pb2.Struct(),
124
- ignore_unknown_fields=True,
125
- )
126
- yield lifecycle_pb2.StartModuleResponse(
127
- success=True,
128
- output=output_proto,
129
- job_id=job_id,
130
- )
181
+
182
+ if job_id is None:
183
+ context.set_code(grpc.StatusCode.NOT_FOUND)
184
+ context.set_details("Failed to create module instance")
185
+ yield lifecycle_pb2.StartModuleResponse(success=False)
186
+ return
187
+
188
+ async with self.job_manager.generate_stream_consumer(job_id) as stream: # type: ignore
189
+ async for message in stream:
190
+ if message.get("error", None) is not None:
191
+ context.set_code(message["error"]["code"])
192
+ context.set_details(message["error"]["error_message"])
193
+ yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
194
+ break
195
+
196
+ if message.get("exception", None) is not None:
197
+ logger.error("Error in output_data")
198
+ context.set_code(message["short_description"])
199
+ context.set_details(message["exception"])
200
+ yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
201
+ break
202
+
203
+ if message.get("code", None) is not None and message.get("code") == "__END_OF_STREAM__":
204
+ yield lifecycle_pb2.StartModuleResponse(success=True, job_id=job_id)
205
+ break
206
+
207
+ proto = json_format.ParseDict(message, struct_pb2.Struct(), ignore_unknown_fields=True)
208
+ yield lifecycle_pb2.StartModuleResponse(success=True, output=proto, job_id=job_id)
209
+ logger.info("Job %s finished", job_id)
131
210
 
132
211
  async def StopModule( # noqa: N802
133
212
  self,
@@ -143,23 +222,20 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
143
222
  Returns:
144
223
  A response indicating success or failure.
145
224
  """
146
- logger.info("StopModule called for module: '%s'", self.module_class.__name__)
225
+ logger.debug("StopModule called for module: '%s'", self.module_class.__name__)
147
226
 
148
- job_id = request.job_id
149
- if job_id not in self.job_manager.modules:
150
- message = f"Job {job_id} not found"
227
+ response: bool = await self.job_manager.stop_module(request.job_id)
228
+ if not response:
229
+ message = f"Job {request.job_id} not found"
151
230
  logger.warning(message)
152
231
  context.set_code(grpc.StatusCode.NOT_FOUND)
153
232
  context.set_details(message)
154
233
  return lifecycle_pb2.StopModuleResponse(success=False)
155
234
 
156
- # Update the job status
157
- await self.job_manager.modules[job_id].stop()
158
-
159
- logger.info("Job %s stopped successfully", job_id)
235
+ logger.debug("Job %s stopped successfully", request.job_id)
160
236
  return lifecycle_pb2.StopModuleResponse(success=True)
161
237
 
162
- def GetModuleStatus( # noqa: N802
238
+ async def GetModuleStatus( # noqa: N802
163
239
  self,
164
240
  request: monitoring_pb2.GetModuleStatusRequest,
165
241
  context: grpc.ServicerContext,
@@ -173,33 +249,33 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
173
249
  Returns:
174
250
  A response with the module status.
175
251
  """
176
- logger.info("GetModuleStatus called for module: '%s'", self.module_class.__name__)
177
-
178
- # If job_id is specified, get status for that job
179
- if request.job_id:
180
- if request.job_id not in self.job_manager.modules:
181
- message = f"Job {request.job_id} not found"
182
- logger.warning(message)
183
- context.set_code(grpc.StatusCode.NOT_FOUND)
184
- context.set_details(message)
185
- return monitoring_pb2.GetModuleStatusResponse()
186
-
187
- status = self.job_manager.modules[request.job_id].status
188
- logger.info("Job %s status: '%s'", request.job_id, status)
252
+ logger.debug("GetModuleStatus called for module: '%s'", self.module_class.__name__)
253
+
254
+ if not request.job_id:
255
+ logger.debug("Job %s status: '%s'", request.job_id, ModuleStatus.NOT_FOUND)
189
256
  return monitoring_pb2.GetModuleStatusResponse(
190
- success=True,
191
- status=status.name,
257
+ success=False,
258
+ status=ModuleStatus.NOT_FOUND.name,
192
259
  job_id=request.job_id,
193
260
  )
194
261
 
195
- logger.info("Job %s status: '%s'", request.job_id, ModuleStatus.NOT_FOUND)
262
+ status = await self.job_manager.get_module_status(request.job_id)
263
+
264
+ if status is None:
265
+ message = f"Job {request.job_id} not found"
266
+ logger.warning(message)
267
+ context.set_code(grpc.StatusCode.NOT_FOUND)
268
+ context.set_details(message)
269
+ return monitoring_pb2.GetModuleStatusResponse()
270
+
271
+ logger.debug("Job %s status: '%s'", request.job_id, status)
196
272
  return monitoring_pb2.GetModuleStatusResponse(
197
- success=False,
198
- status=ModuleStatus.NOT_FOUND.name,
273
+ success=True,
274
+ status=status.name,
199
275
  job_id=request.job_id,
200
276
  )
201
277
 
202
- def GetModuleJobs( # noqa: N802
278
+ async def GetModuleJobs( # noqa: N802
203
279
  self,
204
280
  request: monitoring_pb2.GetModuleJobsRequest, # noqa: ARG002
205
281
  context: grpc.ServicerContext, # noqa: ARG002
@@ -213,20 +289,22 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
213
289
  Returns:
214
290
  A response with information about active jobs.
215
291
  """
216
- logger.info("GetModuleJobs called for module: '%s'", self.module_class.__name__)
292
+ logger.debug("GetModuleJobs called for module: '%s'", self.module_class.__name__)
293
+
294
+ modules = await self.job_manager.list_modules()
217
295
 
218
296
  # Create job info objects for each active job
219
297
  return monitoring_pb2.GetModuleJobsResponse(
220
298
  jobs=[
221
299
  monitoring_pb2.JobInfo(
222
300
  job_id=job_id,
223
- job_status=job_data.status.name,
301
+ job_status=job_data["status"].name,
224
302
  )
225
- for job_id, job_data in self.job_manager.modules.items()
303
+ for job_id, job_data in modules.items()
226
304
  ],
227
305
  )
228
306
 
229
- def GetModuleInput( # noqa: N802
307
+ async def GetModuleInput( # noqa: N802
230
308
  self,
231
309
  request: information_pb2.GetModuleInputRequest,
232
310
  context: grpc.ServicerContext,
@@ -240,7 +318,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
240
318
  Returns:
241
319
  A response with the module's input schema.
242
320
  """
243
- logger.info("GetModuleInput called for module: '%s'", self.module_class.__name__)
321
+ logger.debug("GetModuleInput called for module: '%s'", self.module_class.__name__)
244
322
 
245
323
  # Get input schema if available
246
324
  try:
@@ -262,7 +340,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
262
340
  input_schema=input_format_struct,
263
341
  )
264
342
 
265
- def GetModuleOutput( # noqa: N802
343
+ async def GetModuleOutput( # noqa: N802
266
344
  self,
267
345
  request: information_pb2.GetModuleOutputRequest,
268
346
  context: grpc.ServicerContext,
@@ -276,7 +354,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
276
354
  Returns:
277
355
  A response with the module's output schema.
278
356
  """
279
- logger.info("GetModuleOutput called for module: '%s'", self.module_class.__name__)
357
+ logger.debug("GetModuleOutput called for module: '%s'", self.module_class.__name__)
280
358
 
281
359
  # Get output schema if available
282
360
  try:
@@ -298,7 +376,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
298
376
  output_schema=output_format_struct,
299
377
  )
300
378
 
301
- def GetModuleSetup( # noqa: N802
379
+ async def GetModuleSetup( # noqa: N802
302
380
  self,
303
381
  request: information_pb2.GetModuleSetupRequest,
304
382
  context: grpc.ServicerContext,
@@ -312,7 +390,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
312
390
  Returns:
313
391
  A response with the module's setup information.
314
392
  """
315
- logger.info("GetModuleSetup called for module: '%s'", self.module_class.__name__)
393
+ logger.debug("GetModuleSetup called for module: '%s'", self.module_class.__name__)
316
394
 
317
395
  # Get setup schema if available
318
396
  try:
@@ -369,3 +447,39 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
369
447
  success=True,
370
448
  secret_schema=secret_format_struct,
371
449
  )
450
+
451
+ async def GetConfigSetupModule( # noqa: N802
452
+ self,
453
+ request: information_pb2.GetConfigSetupModuleRequest,
454
+ context: grpc.ServicerContext,
455
+ ) -> information_pb2.GetConfigSetupModuleResponse:
456
+ """Get information about the module's setup and configuration.
457
+
458
+ Args:
459
+ request: The get module setup request.
460
+ context: The gRPC context.
461
+
462
+ Returns:
463
+ A response with the module's setup information.
464
+ """
465
+ logger.debug("GetConfigSetupModule called for module: '%s'", self.module_class.__name__)
466
+
467
+ # Get setup schema if available
468
+ try:
469
+ # Convert schema to proto format
470
+ config_setup_schema_proto = self.module_class.get_config_setup_format(llm_format=request.llm_format)
471
+ config_setup_format_struct = json_format.Parse(
472
+ text=config_setup_schema_proto,
473
+ message=struct_pb2.Struct(), # pylint: disable=no-member
474
+ ignore_unknown_fields=True,
475
+ )
476
+ except NotImplementedError as e:
477
+ logger.warning(e)
478
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
479
+ context.set_details(e)
480
+ return information_pb2.GetConfigSetupModuleResponse()
481
+
482
+ return information_pb2.GetConfigSetupModuleResponse(
483
+ success=True,
484
+ config_setup_schema=config_setup_format_struct,
485
+ )