digitalkin 0.2.25rc0__py3-none-any.whl → 0.3.2.dev14__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 (122) hide show
  1. base_server/server_async_insecure.py +6 -5
  2. base_server/server_async_secure.py +6 -5
  3. base_server/server_sync_insecure.py +5 -4
  4. base_server/server_sync_secure.py +5 -4
  5. digitalkin/__version__.py +1 -1
  6. digitalkin/core/__init__.py +1 -0
  7. digitalkin/core/common/__init__.py +9 -0
  8. digitalkin/core/common/factories.py +156 -0
  9. digitalkin/core/job_manager/__init__.py +1 -0
  10. digitalkin/{modules → core}/job_manager/base_job_manager.py +138 -32
  11. digitalkin/core/job_manager/single_job_manager.py +373 -0
  12. digitalkin/{modules → core}/job_manager/taskiq_broker.py +121 -26
  13. digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
  14. digitalkin/core/task_manager/__init__.py +1 -0
  15. digitalkin/core/task_manager/base_task_manager.py +539 -0
  16. digitalkin/core/task_manager/local_task_manager.py +108 -0
  17. digitalkin/core/task_manager/remote_task_manager.py +87 -0
  18. digitalkin/core/task_manager/surrealdb_repository.py +266 -0
  19. digitalkin/core/task_manager/task_executor.py +249 -0
  20. digitalkin/core/task_manager/task_session.py +368 -0
  21. digitalkin/grpc_servers/__init__.py +1 -19
  22. digitalkin/grpc_servers/_base_server.py +3 -3
  23. digitalkin/grpc_servers/module_server.py +120 -195
  24. digitalkin/grpc_servers/module_servicer.py +81 -44
  25. digitalkin/grpc_servers/utils/__init__.py +1 -0
  26. digitalkin/grpc_servers/utils/exceptions.py +0 -8
  27. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +25 -9
  28. digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
  29. digitalkin/grpc_servers/utils/utility_schema_extender.py +100 -0
  30. digitalkin/logger.py +64 -27
  31. digitalkin/mixins/__init__.py +19 -0
  32. digitalkin/mixins/base_mixin.py +10 -0
  33. digitalkin/mixins/callback_mixin.py +24 -0
  34. digitalkin/mixins/chat_history_mixin.py +110 -0
  35. digitalkin/mixins/cost_mixin.py +76 -0
  36. digitalkin/mixins/file_history_mixin.py +93 -0
  37. digitalkin/mixins/filesystem_mixin.py +46 -0
  38. digitalkin/mixins/logger_mixin.py +51 -0
  39. digitalkin/mixins/storage_mixin.py +79 -0
  40. digitalkin/models/__init__.py +1 -1
  41. digitalkin/models/core/__init__.py +1 -0
  42. digitalkin/{modules/job_manager → models/core}/job_manager_models.py +3 -11
  43. digitalkin/models/core/task_monitor.py +74 -0
  44. digitalkin/models/grpc_servers/__init__.py +1 -0
  45. digitalkin/{grpc_servers/utils → models/grpc_servers}/models.py +92 -7
  46. digitalkin/models/module/__init__.py +18 -11
  47. digitalkin/models/module/base_types.py +61 -0
  48. digitalkin/models/module/module.py +9 -1
  49. digitalkin/models/module/module_context.py +282 -6
  50. digitalkin/models/module/module_types.py +29 -105
  51. digitalkin/models/module/setup_types.py +490 -0
  52. digitalkin/models/module/tool_cache.py +68 -0
  53. digitalkin/models/module/tool_reference.py +117 -0
  54. digitalkin/models/module/utility.py +167 -0
  55. digitalkin/models/services/__init__.py +9 -0
  56. digitalkin/models/services/cost.py +1 -0
  57. digitalkin/models/services/registry.py +35 -0
  58. digitalkin/models/services/storage.py +39 -5
  59. digitalkin/modules/__init__.py +5 -1
  60. digitalkin/modules/_base_module.py +265 -167
  61. digitalkin/modules/archetype_module.py +6 -1
  62. digitalkin/modules/tool_module.py +16 -3
  63. digitalkin/modules/trigger_handler.py +7 -6
  64. digitalkin/modules/triggers/__init__.py +8 -0
  65. digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  66. digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  67. digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  68. digitalkin/services/__init__.py +4 -0
  69. digitalkin/services/communication/__init__.py +7 -0
  70. digitalkin/services/communication/communication_strategy.py +76 -0
  71. digitalkin/services/communication/default_communication.py +101 -0
  72. digitalkin/services/communication/grpc_communication.py +234 -0
  73. digitalkin/services/cost/__init__.py +9 -2
  74. digitalkin/services/cost/grpc_cost.py +9 -42
  75. digitalkin/services/filesystem/default_filesystem.py +0 -2
  76. digitalkin/services/filesystem/grpc_filesystem.py +10 -39
  77. digitalkin/services/registry/__init__.py +22 -1
  78. digitalkin/services/registry/default_registry.py +135 -4
  79. digitalkin/services/registry/exceptions.py +47 -0
  80. digitalkin/services/registry/grpc_registry.py +306 -0
  81. digitalkin/services/registry/registry_models.py +15 -0
  82. digitalkin/services/registry/registry_strategy.py +88 -4
  83. digitalkin/services/services_config.py +25 -3
  84. digitalkin/services/services_models.py +5 -1
  85. digitalkin/services/setup/default_setup.py +6 -7
  86. digitalkin/services/setup/grpc_setup.py +52 -15
  87. digitalkin/services/storage/grpc_storage.py +4 -4
  88. digitalkin/services/user_profile/__init__.py +12 -0
  89. digitalkin/services/user_profile/default_user_profile.py +55 -0
  90. digitalkin/services/user_profile/grpc_user_profile.py +69 -0
  91. digitalkin/services/user_profile/user_profile_strategy.py +25 -0
  92. digitalkin/utils/__init__.py +28 -0
  93. digitalkin/utils/arg_parser.py +1 -1
  94. digitalkin/utils/development_mode_action.py +2 -2
  95. digitalkin/utils/dynamic_schema.py +483 -0
  96. digitalkin/utils/package_discover.py +1 -2
  97. digitalkin/utils/schema_splitter.py +207 -0
  98. {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/METADATA +11 -30
  99. digitalkin-0.3.2.dev14.dist-info/RECORD +143 -0
  100. {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/top_level.txt +1 -0
  101. modules/archetype_with_tools_module.py +244 -0
  102. modules/cpu_intensive_module.py +1 -1
  103. modules/dynamic_setup_module.py +338 -0
  104. modules/minimal_llm_module.py +1 -1
  105. modules/text_transform_module.py +1 -1
  106. monitoring/digitalkin_observability/__init__.py +46 -0
  107. monitoring/digitalkin_observability/http_server.py +150 -0
  108. monitoring/digitalkin_observability/interceptors.py +176 -0
  109. monitoring/digitalkin_observability/metrics.py +201 -0
  110. monitoring/digitalkin_observability/prometheus.py +137 -0
  111. monitoring/tests/test_metrics.py +172 -0
  112. services/filesystem_module.py +7 -5
  113. services/storage_module.py +4 -2
  114. digitalkin/grpc_servers/registry_server.py +0 -65
  115. digitalkin/grpc_servers/registry_servicer.py +0 -456
  116. digitalkin/grpc_servers/utils/factory.py +0 -180
  117. digitalkin/modules/job_manager/single_job_manager.py +0 -294
  118. digitalkin/modules/job_manager/taskiq_job_manager.py +0 -290
  119. digitalkin-0.2.25rc0.dist-info/RECORD +0 -89
  120. /digitalkin/{grpc_servers/utils → models/grpc_servers}/types.py +0 -0
  121. {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/WHEEL +0 -0
  122. {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/licenses/LICENSE +0 -0
@@ -1,37 +1,32 @@
1
1
  """Module gRPC server implementation for DigitalKin."""
2
2
 
3
- from pathlib import Path
4
- import uuid
3
+ from typing import TYPE_CHECKING
5
4
 
6
- import grpc
5
+ from agentic_mesh_protocol.module.v1 import (
6
+ module_service_pb2,
7
+ module_service_pb2_grpc,
8
+ )
7
9
 
8
10
  from digitalkin.grpc_servers._base_server import BaseServer
9
11
  from digitalkin.grpc_servers.module_servicer import ModuleServicer
10
- from digitalkin.grpc_servers.utils.exceptions import ServerError
11
- from digitalkin.grpc_servers.utils.models import (
12
+ from digitalkin.logger import logger
13
+ from digitalkin.models.grpc_servers.models import (
12
14
  ClientConfig,
13
15
  ModuleServerConfig,
14
16
  SecurityMode,
15
17
  )
16
18
  from digitalkin.modules._base_module import BaseModule
19
+ from digitalkin.services.registry import GrpcRegistry
17
20
 
18
- from digitalkin_proto.digitalkin.module.v2 import (
19
- module_service_pb2,
20
- module_service_pb2_grpc,
21
- )
22
- from digitalkin_proto.digitalkin.module_registry.v2 import (
23
- metadata_pb2,
24
- module_registry_service_pb2_grpc,
25
- registration_pb2,
26
- )
27
- from digitalkin.logger import logger
21
+ if TYPE_CHECKING:
22
+ from digitalkin.services.registry import RegistryStrategy
28
23
 
29
24
 
30
25
  class ModuleServer(BaseServer):
31
26
  """gRPC server for a DigitalKin module.
32
27
 
33
28
  This server exposes the module's functionality through the ModuleService gRPC interface.
34
- It can optionally register itself with a ModuleRegistry server.
29
+ It can optionally register itself with a Registry server.
35
30
 
36
31
  Attributes:
37
32
  module: The module instance being served.
@@ -50,13 +45,20 @@ class ModuleServer(BaseServer):
50
45
 
51
46
  Args:
52
47
  module_class: The module instance to be served.
53
- config: Server configuration including registry address if auto-registration is desired.
48
+ server_config: Server configuration including registry address if auto-registration is desired.
49
+ client_config: Client configuration used by services.
54
50
  """
55
51
  super().__init__(server_config)
56
52
  self.module_class = module_class
57
53
  self.server_config = server_config
58
54
  self.client_config = client_config
59
55
  self.module_servicer: ModuleServicer | None = None
56
+ self.registry: RegistryStrategy | None = None
57
+
58
+ self._registry_client_config: ClientConfig | None = None
59
+ if self.server_config.registry_address:
60
+ self._registry_client_config = self._build_registry_client_config()
61
+ self._prepare_registry_config()
60
62
 
61
63
  def _register_servicers(self) -> None:
62
64
  """Register the module servicer with the gRPC server.
@@ -77,207 +79,130 @@ class ModuleServer(BaseServer):
77
79
  )
78
80
  logger.debug("Registered Module servicer")
79
81
 
82
+ def _prepare_registry_config(self) -> None:
83
+ """Prepare registry client config on module_class before server starts.
84
+
85
+ This ensures ServicesConfig created by JobManager will have registry config,
86
+ allowing spawned module instances to inherit the registry configuration.
87
+ """
88
+ if not self._registry_client_config:
89
+ return
90
+
91
+ self.module_class.services_config_params["registry"] = {"client_config": self._registry_client_config}
92
+
93
+ def _build_registry_client_config(self) -> ClientConfig:
94
+ """Build ClientConfig for registry from server_config.registry_address.
95
+
96
+ Returns:
97
+ ClientConfig configured for registry connection.
98
+ """
99
+ host, port = self.server_config.registry_address.rsplit(":", 1)
100
+ return ClientConfig(
101
+ host=host,
102
+ port=int(port),
103
+ mode=self.server_config.mode,
104
+ security=self.client_config.security if self.client_config else SecurityMode.INSECURE,
105
+ credentials=self.client_config.credentials if self.client_config else None,
106
+ )
107
+
108
+ def _init_registry(self) -> None:
109
+ """Initialize server-level registry client for registration.
110
+
111
+ Note: services_config_params["registry"] is already set in _prepare_registry_config()
112
+ which runs in __init__(). This method only creates the server-level client instance.
113
+ """
114
+ if not self._registry_client_config:
115
+ return
116
+
117
+ self.registry = GrpcRegistry("", "", "", self._registry_client_config)
118
+
80
119
  def start(self) -> None:
81
120
  """Start the module server and register with the registry if configured."""
82
- logger.info("Starting module server",extra={"server_config": self.server_config})
121
+ logger.info("Starting module server", extra={"server_config": self.server_config})
83
122
  super().start()
84
123
 
85
- logger.debug("Starting module server",extra={"server_config": self.server_config})
86
- # If a registry address is provided, register the module
87
- if self.server_config.registry_address:
88
- try:
89
- self._register_with_registry()
90
- except Exception:
91
- logger.exception("Failed to register with registry")
124
+ try:
125
+ self._init_registry()
126
+ self._register_with_registry()
127
+ except Exception:
128
+ logger.exception("Failed to register with registry")
92
129
 
93
130
  if self.module_servicer is not None:
94
- logger.debug(
95
- "Setup post init started",extra={"client_config": self.client_config}
96
- )
131
+ logger.debug("Setup post init started", extra={"client_config": self.client_config})
97
132
  self.module_servicer.setup.__post_init__(self.client_config)
98
133
 
99
134
  async def start_async(self) -> None:
100
135
  """Start the module server and register with the registry if configured."""
101
- logger.info("Starting module server",extra={"server_config": self.server_config})
136
+ logger.info("Starting module server", extra={"server_config": self.server_config})
102
137
  await super().start_async()
103
- # If a registry address is provided, register the module
104
- if self.server_config.registry_address:
105
- try:
106
- self._register_with_registry()
107
- except Exception:
108
- logger.exception("Failed to register with registry")
138
+
139
+ try:
140
+ self._init_registry()
141
+ self._register_with_registry()
142
+ except Exception:
143
+ logger.exception("Failed to register with registry")
109
144
 
110
145
  if self.module_servicer is not None:
111
- logger.info(
112
- "Setup post init started",extra={"client_config": self.client_config}
113
- )
114
- await self.module_servicer.job_manager._start()
146
+ logger.info("Setup post init started", extra={"client_config": self.client_config})
147
+ await self.module_servicer.job_manager.start()
115
148
  self.module_servicer.setup.__post_init__(self.client_config)
116
149
 
117
150
  def stop(self, grace: float | None = None) -> None:
118
- """Stop the module server and deregister from the registry if needed."""
119
- # If registered with a registry, deregister
120
- if self.server_config.registry_address:
121
- try:
122
- self._deregister_from_registry()
123
- except ServerError:
124
- logger.exception("Failed to deregister from registry")
151
+ """Stop the module server.
125
152
 
153
+ Modules become inactive when they stop sending heartbeats
154
+ """
126
155
  super().stop(grace)
127
156
 
128
157
  def _register_with_registry(self) -> None:
129
- """Register this module with the registry server.
130
-
131
- Raises:
132
- ServerError: If communication with the registry server fails.
133
- """
134
- logger.debug(
135
- "Registering module with registry at %s",
136
- self.server_config.registry_address,
137
- )
138
-
139
- # Create appropriate channel based on security mode
140
- channel = self._create_registry_channel()
141
-
142
- with channel:
143
- # Create a stub (client)
144
- stub = module_registry_service_pb2_grpc.ModuleRegistryServiceStub(channel)
145
-
146
- # Determine module type
147
- module_type = self._determine_module_type()
148
-
149
- metadata = metadata_pb2.Metadata(
150
- name=self.module_class.metadata["name"],
151
- tags=[
152
- metadata_pb2.Tag(tag=tag)
153
- for tag in self.module_class.metadata["tags"]
154
- ],
155
- description=self.module_class.metadata["description"],
158
+ """Register this module with the registry server."""
159
+ if not self.registry:
160
+ logger.debug("No registry configured, skipping registration")
161
+ return
162
+
163
+ module_id = self.module_class.get_module_id()
164
+ version = self.module_class.metadata.get("version", "0.0.0")
165
+
166
+ if not module_id or module_id == "unknown":
167
+ logger.warning(
168
+ "Module has no valid module_id, skipping registration",
169
+ extra={"module_class": self.module_class.__name__},
156
170
  )
157
-
158
- self.module_class.metadata["module_id"] = (
159
- f"{self.module_class.metadata['name']}:{uuid.uuid4()}"
160
- )
161
- # Create registration request
162
- request = registration_pb2.RegisterRequest(
163
- module_id=self.module_class.metadata["module_id"],
164
- version=self.module_class.metadata["version"],
165
- module_type=module_type,
166
- address=self.server_config.address,
167
- metadata=metadata,
168
- )
169
-
170
- try:
171
- # Call the register method
172
- logger.debug(
173
- "Request sent to registry for module: %s:%s",
174
- self.module_class.metadata["name"],
175
- self.module_class.metadata["module_id"],
176
- )
177
- response = stub.RegisterModule(request)
178
-
179
- if response.success:
180
- logger.debug("Module registered successfully")
181
- else:
182
- logger.error("Module registration failed")
183
- except grpc.RpcError:
184
- logger.exception("RPC error during registration:")
185
- raise ServerError
186
-
187
- def _deregister_from_registry(self) -> None:
188
- """Deregister this module from the registry server.
189
-
190
- Raises:
191
- ServerError: If communication with the registry server fails.
192
- """
193
- logger.debug(
194
- "Deregistering module from registry at %s",
195
- self.server_config.registry_address,
171
+ return
172
+
173
+ logger.info(
174
+ "Attempting to register module with registry",
175
+ extra={
176
+ "module_id": module_id,
177
+ "address": self.server_config.host,
178
+ "port": self.server_config.port,
179
+ "version": version,
180
+ "registry_address": self.server_config.registry_address,
181
+ },
196
182
  )
197
183
 
198
- # Create appropriate channel based on security mode
199
- channel = self._create_registry_channel()
200
-
201
- with channel:
202
- # Create a stub (client)
203
- stub = module_registry_service_pb2_grpc.ModuleRegistryServiceStub(channel)
204
-
205
- # Create deregistration request
206
- request = registration_pb2.DeregisterRequest(
207
- module_id=self.module_class.metadata["module_id"],
208
- )
209
- try:
210
- # Call the deregister method
211
- response = stub.DeregisterModule(request)
212
-
213
- if response.success:
214
- logger.debug("Module deregistered successfull")
215
- else:
216
- logger.error("Module deregistration failed")
217
- except grpc.RpcError:
218
- logger.exception("RPC error during deregistration")
219
- raise ServerError
220
-
221
- def _create_registry_channel(self) -> grpc.Channel:
222
- """Create an appropriate channel to the registry server.
223
-
224
- Returns:
225
- A gRPC channel for communication with the registry.
184
+ result = self.registry.register(
185
+ module_id=module_id,
186
+ address=self.server_config.host,
187
+ port=self.server_config.port,
188
+ version=version,
189
+ )
226
190
 
227
- Raises:
228
- ValueError: If credentials are required but not provided.
229
- """
230
- if (
231
- self.client_config is not None
232
- and self.client_config.security == SecurityMode.SECURE
233
- and self.client_config.credentials
234
- ):
235
- # Secure channel
236
- # Secure channel
237
- root_certificates = Path(
238
- self.client_config.credentials.root_cert_path
239
- ).read_bytes()
240
-
241
- # mTLS channel
242
- private_key = None
243
- certificate_chain = None
244
- if (
245
- self.client_config.credentials.client_cert_path is not None
246
- and self.client_config.credentials.client_key_path is not None
247
- ):
248
- private_key = Path(
249
- self.client_config.credentials.client_key_path
250
- ).read_bytes()
251
- certificate_chain = Path(
252
- self.client_config.credentials.client_cert_path
253
- ).read_bytes()
254
-
255
- # Create channel credentials
256
- channel_credentials = grpc.ssl_channel_credentials(
257
- root_certificates=root_certificates,
258
- certificate_chain=certificate_chain,
259
- private_key=private_key,
191
+ if result:
192
+ logger.info(
193
+ "Module registered successfully",
194
+ extra={
195
+ "module_id": result.module_id,
196
+ "address": self.server_config.host,
197
+ "port": self.server_config.port,
198
+ "registry_address": self.server_config.registry_address,
199
+ },
260
200
  )
261
- return grpc.secure_channel(
262
- self.server_config.registry_address, channel_credentials
201
+ else:
202
+ logger.warning(
203
+ "Module registration returned None (module may not exist in registry)",
204
+ extra={
205
+ "module_id": module_id,
206
+ "registry_address": self.server_config.registry_address,
207
+ },
263
208
  )
264
- # Insecure channel
265
- return grpc.insecure_channel(self.server_config.registry_address)
266
-
267
- def _determine_module_type(self) -> str:
268
- """Determine the module type based on its class.
269
-
270
- Returns:
271
- A string representing the module type.
272
- """
273
- module_type = "UNKNOWN"
274
- class_name = self.module_class.__name__
275
-
276
- if class_name == "ToolModule":
277
- module_type = "TOOL"
278
- elif class_name == "TriggerModule":
279
- module_type = "TRIGGER"
280
- elif class_name == "ArchetypeModule":
281
- module_type = "KIN"
282
-
283
- return module_type
@@ -5,7 +5,7 @@ from collections.abc import AsyncGenerator
5
5
  from typing import Any
6
6
 
7
7
  import grpc
8
- from digitalkin_proto.digitalkin.module.v2 import (
8
+ from agentic_mesh_protocol.module.v1 import (
9
9
  information_pb2,
10
10
  lifecycle_pb2,
11
11
  module_service_pb2_grpc,
@@ -13,12 +13,13 @@ from digitalkin_proto.digitalkin.module.v2 import (
13
13
  )
14
14
  from google.protobuf import json_format, struct_pb2
15
15
 
16
+ from digitalkin.core.job_manager.base_job_manager import BaseJobManager
16
17
  from digitalkin.grpc_servers.utils.exceptions import ServicerError
17
18
  from digitalkin.logger import logger
19
+ from digitalkin.models.core.job_manager_models import JobManagerMode
18
20
  from digitalkin.models.module.module import ModuleStatus
19
21
  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 JobManagerMode
22
+ from digitalkin.services.registry import GrpcRegistry, RegistryStrategy
22
23
  from digitalkin.services.services_models import ServicesMode
23
24
  from digitalkin.services.setup.default_setup import DefaultSetup
24
25
  from digitalkin.services.setup.grpc_setup import GrpcSetup
@@ -40,6 +41,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
40
41
  args: Namespace
41
42
  setup: SetupStrategy
42
43
  job_manager: BaseJobManager
44
+ _registry_cache: RegistryStrategy | None = None
43
45
 
44
46
  def _add_parser_args(self, parser: ArgumentParser) -> None:
45
47
  super()._add_parser_args(parser)
@@ -76,12 +78,32 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
76
78
  self.job_manager = job_manager_class(module_class, self.args.services_mode)
77
79
 
78
80
  logger.debug(
79
- "ModuleServicer initialized with job manager: %s | %s",
81
+ "ModuleServicer initialized with job manager: %s",
80
82
  self.args.job_manager_mode,
81
- self.job_manager,
83
+ extra={"job_manager": self.job_manager},
82
84
  )
83
85
  self.setup = GrpcSetup() if self.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
84
86
 
87
+ def _get_registry(self) -> RegistryStrategy | None:
88
+ """Get a cached registry instance if configured.
89
+
90
+ Returns:
91
+ Cached GrpcRegistry instance if registry config exists, None otherwise.
92
+ """
93
+ if self._registry_cache is not None:
94
+ return self._registry_cache
95
+
96
+ registry_config = self.module_class.services_config_params.get("registry")
97
+ if not registry_config:
98
+ return None
99
+
100
+ client_config = registry_config.get("client_config")
101
+ if not client_config:
102
+ return None
103
+
104
+ self._registry_cache = GrpcRegistry("", "", "", client_config)
105
+ return self._registry_cache
106
+
85
107
  async def ConfigSetupModule( # noqa: N802
86
108
  self,
87
109
  request: lifecycle_pb2.ConfigSetupModuleRequest,
@@ -108,11 +130,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
108
130
  "mission_id": request.mission_id,
109
131
  },
110
132
  )
111
- # Process the module input
112
- # TODO: Secret should be used here as well
113
133
  setup_version = request.setup_version
114
134
  config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
115
- setup_version_data = self.module_class.create_setup_model(
135
+ setup_version_data = await self.module_class.create_setup_model(
116
136
  json_format.MessageToDict(request.setup_version.content),
117
137
  config_fields=True,
118
138
  )
@@ -139,8 +159,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
139
159
  return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
140
160
 
141
161
  updated_setup_data = await self.job_manager.generate_config_setup_module_response(job_id)
142
- logger.info("Setup updated")
143
- logger.debug(f"Updated setup data: {updated_setup_data=}")
162
+ logger.info("Setup updated", extra={"job_id": job_id})
163
+ logger.debug("Updated setup data", extra={"job_id": job_id, "setup_data": updated_setup_data})
144
164
  setup_version.content = json_format.ParseDict(
145
165
  updated_setup_data,
146
166
  struct_pb2.Struct(),
@@ -172,7 +192,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
172
192
  )
173
193
  # Process the module input
174
194
  # TODO: Check failure of input data format
175
- input_data = self.module_class.create_input_model(dict(request.input.items()))
195
+ input_data = self.module_class.create_input_model(json_format.MessageToDict(request.input))
196
+
176
197
  setup_data_class = self.setup.get_setup(
177
198
  setup_dict={
178
199
  "setup_id": request.setup_id,
@@ -184,7 +205,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
184
205
  msg = "No setup data returned."
185
206
  raise ServicerError(msg)
186
207
 
187
- setup_data = self.module_class.create_setup_model(setup_data_class.current_setup_version.content)
208
+ setup_data = await self.module_class.create_setup_model(setup_data_class.current_setup_version.content)
188
209
 
189
210
  # create a task to run the module in background
190
211
  job_id = await self.job_manager.create_module_instance_job(
@@ -201,27 +222,37 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
201
222
  yield lifecycle_pb2.StartModuleResponse(success=False)
202
223
  return
203
224
 
204
- async with self.job_manager.generate_stream_consumer(job_id) as stream: # type: ignore
205
- async for message in stream:
206
- if message.get("error", None) is not None:
207
- context.set_code(message["error"]["code"])
208
- context.set_details(message["error"]["error_message"])
209
- yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
210
- break
211
-
212
- if message.get("exception", None) is not None:
213
- logger.error("Error in output_data")
214
- context.set_code(message["short_description"])
215
- context.set_details(message["exception"])
216
- yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
217
- break
218
-
219
- if message.get("code", None) is not None and message.get("code") == "__END_OF_STREAM__":
220
- yield lifecycle_pb2.StartModuleResponse(success=True, job_id=job_id)
221
- break
222
-
223
- proto = json_format.ParseDict(message, struct_pb2.Struct(), ignore_unknown_fields=True)
224
- yield lifecycle_pb2.StartModuleResponse(success=True, output=proto, job_id=job_id)
225
+ try:
226
+ async with self.job_manager.generate_stream_consumer(job_id) as stream: # type: ignore
227
+ async for message in stream:
228
+ if message.get("error", None) is not None:
229
+ logger.error("Error in output_data", extra={"message": message})
230
+ context.set_code(message["error"]["code"])
231
+ context.set_details(message["error"]["error_message"])
232
+ yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
233
+ break
234
+
235
+ if message.get("exception", None) is not None:
236
+ logger.error("Exception in output_data", extra={"message": message})
237
+ context.set_code(message["short_description"])
238
+ context.set_details(message["exception"])
239
+ yield lifecycle_pb2.StartModuleResponse(success=False, job_id=job_id)
240
+ break
241
+
242
+ logger.info("Yielding message from job %s: %s", job_id, message)
243
+ proto = json_format.ParseDict(message, struct_pb2.Struct(), ignore_unknown_fields=True)
244
+ yield lifecycle_pb2.StartModuleResponse(success=True, output=proto, job_id=job_id)
245
+
246
+ if message.get("root", {}).get("protocol") == "end_of_stream":
247
+ logger.info(
248
+ "End of stream signal received",
249
+ extra={"job_id": job_id, "mission_id": request.mission_id},
250
+ )
251
+ break
252
+ finally:
253
+ await self.job_manager.wait_for_completion(job_id)
254
+ await self.job_manager.clean_session(job_id, mission_id=request.mission_id)
255
+
225
256
  logger.info("Job %s finished", job_id)
226
257
 
227
258
  async def StopModule( # noqa: N802
@@ -238,17 +269,19 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
238
269
  Returns:
239
270
  A response indicating success or failure.
240
271
  """
241
- logger.debug("StopModule called for module: '%s'", self.module_class.__name__)
272
+ logger.debug(
273
+ "StopModule called",
274
+ extra={"module_class": self.module_class.__name__, "job_id": request.job_id},
275
+ )
242
276
 
243
277
  response: bool = await self.job_manager.stop_module(request.job_id)
244
278
  if not response:
245
- message = f"Job {request.job_id} not found"
246
- logger.warning(message)
279
+ logger.warning("Job not found for stop request", extra={"job_id": request.job_id})
247
280
  context.set_code(grpc.StatusCode.NOT_FOUND)
248
- context.set_details(message)
281
+ context.set_details(f"Job {request.job_id} not found")
249
282
  return lifecycle_pb2.StopModuleResponse(success=False)
250
283
 
251
- logger.debug("Job %s stopped successfully", request.job_id)
284
+ logger.debug("Job stopped successfully", extra={"job_id": request.job_id})
252
285
  return lifecycle_pb2.StopModuleResponse(success=True)
253
286
 
254
287
  async def GetModuleStatus( # noqa: N802
@@ -339,7 +372,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
339
372
  # Get input schema if available
340
373
  try:
341
374
  # Convert schema to proto format
342
- input_schema_proto = self.module_class.get_input_format(llm_format=request.llm_format)
375
+ input_schema_proto = await self.module_class.get_input_format(
376
+ llm_format=request.llm_format,
377
+ )
343
378
  input_format_struct = json_format.Parse(
344
379
  text=input_schema_proto,
345
380
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -375,7 +410,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
375
410
  # Get output schema if available
376
411
  try:
377
412
  # Convert schema to proto format
378
- output_schema_proto = self.module_class.get_output_format(llm_format=request.llm_format)
413
+ output_schema_proto = await self.module_class.get_output_format(
414
+ llm_format=request.llm_format,
415
+ )
379
416
  output_format_struct = json_format.Parse(
380
417
  text=output_schema_proto,
381
418
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -411,7 +448,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
411
448
  # Get setup schema if available
412
449
  try:
413
450
  # Convert schema to proto format
414
- setup_schema_proto = self.module_class.get_setup_format(llm_format=request.llm_format)
451
+ setup_schema_proto = await self.module_class.get_setup_format(llm_format=request.llm_format)
415
452
  setup_format_struct = json_format.Parse(
416
453
  text=setup_schema_proto,
417
454
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -428,7 +465,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
428
465
  setup_schema=setup_format_struct,
429
466
  )
430
467
 
431
- def GetModuleSecret( # noqa: N802
468
+ async def GetModuleSecret( # noqa: N802
432
469
  self,
433
470
  request: information_pb2.GetModuleSecretRequest,
434
471
  context: grpc.ServicerContext,
@@ -447,7 +484,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
447
484
  # Get secret schema if available
448
485
  try:
449
486
  # Convert schema to proto format
450
- secret_schema_proto = self.module_class.get_secret_format(llm_format=request.llm_format)
487
+ secret_schema_proto = await self.module_class.get_secret_format(llm_format=request.llm_format)
451
488
  secret_format_struct = json_format.Parse(
452
489
  text=secret_schema_proto,
453
490
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -483,7 +520,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
483
520
  # Get setup schema if available
484
521
  try:
485
522
  # Convert schema to proto format
486
- config_setup_schema_proto = self.module_class.get_config_setup_format(llm_format=request.llm_format)
523
+ config_setup_schema_proto = await self.module_class.get_config_setup_format(llm_format=request.llm_format)
487
524
  config_setup_format_struct = json_format.Parse(
488
525
  text=config_setup_schema_proto,
489
526
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -0,0 +1 @@
1
+ """gRPC servers utilities package."""
@@ -27,11 +27,3 @@ class ServerStateError(ServerError):
27
27
 
28
28
  class ReflectionError(ServerError):
29
29
  """Error related to gRPC reflection service."""
30
-
31
-
32
- class HealthCheckError(ServerError):
33
- """Error related to gRPC health check service."""
34
-
35
-
36
- class OptionalFeatureNotImplementedError(NotImplementedError):
37
- """Raised when an optional feature is not implemented, but was requested."""