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.
- base_server/server_async_insecure.py +6 -5
- base_server/server_async_secure.py +6 -5
- base_server/server_sync_insecure.py +5 -4
- base_server/server_sync_secure.py +5 -4
- digitalkin/__version__.py +1 -1
- digitalkin/core/__init__.py +1 -0
- digitalkin/core/common/__init__.py +9 -0
- digitalkin/core/common/factories.py +156 -0
- digitalkin/core/job_manager/__init__.py +1 -0
- digitalkin/{modules → core}/job_manager/base_job_manager.py +138 -32
- digitalkin/core/job_manager/single_job_manager.py +373 -0
- digitalkin/{modules → core}/job_manager/taskiq_broker.py +121 -26
- digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
- digitalkin/core/task_manager/__init__.py +1 -0
- digitalkin/core/task_manager/base_task_manager.py +539 -0
- digitalkin/core/task_manager/local_task_manager.py +108 -0
- digitalkin/core/task_manager/remote_task_manager.py +87 -0
- digitalkin/core/task_manager/surrealdb_repository.py +266 -0
- digitalkin/core/task_manager/task_executor.py +249 -0
- digitalkin/core/task_manager/task_session.py +368 -0
- digitalkin/grpc_servers/__init__.py +1 -19
- digitalkin/grpc_servers/_base_server.py +3 -3
- digitalkin/grpc_servers/module_server.py +120 -195
- digitalkin/grpc_servers/module_servicer.py +81 -44
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +0 -8
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +25 -9
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/grpc_servers/utils/utility_schema_extender.py +100 -0
- digitalkin/logger.py +64 -27
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/__init__.py +1 -1
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/{modules/job_manager → models/core}/job_manager_models.py +3 -11
- digitalkin/models/core/task_monitor.py +74 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/{grpc_servers/utils → models/grpc_servers}/models.py +92 -7
- digitalkin/models/module/__init__.py +18 -11
- digitalkin/models/module/base_types.py +61 -0
- digitalkin/models/module/module.py +9 -1
- digitalkin/models/module/module_context.py +282 -6
- digitalkin/models/module/module_types.py +29 -105
- digitalkin/models/module/setup_types.py +490 -0
- digitalkin/models/module/tool_cache.py +68 -0
- digitalkin/models/module/tool_reference.py +117 -0
- digitalkin/models/module/utility.py +167 -0
- digitalkin/models/services/__init__.py +9 -0
- digitalkin/models/services/cost.py +1 -0
- digitalkin/models/services/registry.py +35 -0
- digitalkin/models/services/storage.py +39 -5
- digitalkin/modules/__init__.py +5 -1
- digitalkin/modules/_base_module.py +265 -167
- digitalkin/modules/archetype_module.py +6 -1
- digitalkin/modules/tool_module.py +16 -3
- digitalkin/modules/trigger_handler.py +7 -6
- digitalkin/modules/triggers/__init__.py +8 -0
- digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
- digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
- digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
- digitalkin/services/__init__.py +4 -0
- digitalkin/services/communication/__init__.py +7 -0
- digitalkin/services/communication/communication_strategy.py +76 -0
- digitalkin/services/communication/default_communication.py +101 -0
- digitalkin/services/communication/grpc_communication.py +234 -0
- digitalkin/services/cost/__init__.py +9 -2
- digitalkin/services/cost/grpc_cost.py +9 -42
- digitalkin/services/filesystem/default_filesystem.py +0 -2
- digitalkin/services/filesystem/grpc_filesystem.py +10 -39
- digitalkin/services/registry/__init__.py +22 -1
- digitalkin/services/registry/default_registry.py +135 -4
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +15 -0
- digitalkin/services/registry/registry_strategy.py +88 -4
- digitalkin/services/services_config.py +25 -3
- digitalkin/services/services_models.py +5 -1
- digitalkin/services/setup/default_setup.py +6 -7
- digitalkin/services/setup/grpc_setup.py +52 -15
- digitalkin/services/storage/grpc_storage.py +4 -4
- digitalkin/services/user_profile/__init__.py +12 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +25 -0
- digitalkin/utils/__init__.py +28 -0
- digitalkin/utils/arg_parser.py +1 -1
- digitalkin/utils/development_mode_action.py +2 -2
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/package_discover.py +1 -2
- digitalkin/utils/schema_splitter.py +207 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/METADATA +11 -30
- digitalkin-0.3.2.dev14.dist-info/RECORD +143 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/top_level.txt +1 -0
- modules/archetype_with_tools_module.py +244 -0
- modules/cpu_intensive_module.py +1 -1
- modules/dynamic_setup_module.py +338 -0
- modules/minimal_llm_module.py +1 -1
- modules/text_transform_module.py +1 -1
- monitoring/digitalkin_observability/__init__.py +46 -0
- monitoring/digitalkin_observability/http_server.py +150 -0
- monitoring/digitalkin_observability/interceptors.py +176 -0
- monitoring/digitalkin_observability/metrics.py +201 -0
- monitoring/digitalkin_observability/prometheus.py +137 -0
- monitoring/tests/test_metrics.py +172 -0
- services/filesystem_module.py +7 -5
- services/storage_module.py +4 -2
- digitalkin/grpc_servers/registry_server.py +0 -65
- digitalkin/grpc_servers/registry_servicer.py +0 -456
- digitalkin/grpc_servers/utils/factory.py +0 -180
- digitalkin/modules/job_manager/single_job_manager.py +0 -294
- digitalkin/modules/job_manager/taskiq_job_manager.py +0 -290
- digitalkin-0.2.25rc0.dist-info/RECORD +0 -89
- /digitalkin/{grpc_servers/utils → models/grpc_servers}/types.py +0 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.2.dev14.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
"""This module is responsible for handling the cost services."""
|
|
2
2
|
|
|
3
|
-
from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
3
|
+
from digitalkin.services.cost.cost_strategy import CostConfig, CostData, CostStrategy, CostType
|
|
4
4
|
from digitalkin.services.cost.default_cost import DefaultCost
|
|
5
5
|
from digitalkin.services.cost.grpc_cost import GrpcCost
|
|
6
6
|
|
|
7
|
-
__all__ = [
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CostConfig",
|
|
9
|
+
"CostData",
|
|
10
|
+
"CostStrategy",
|
|
11
|
+
"CostType",
|
|
12
|
+
"DefaultCost",
|
|
13
|
+
"GrpcCost",
|
|
14
|
+
]
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"""This module implements the gRPC Cost strategy."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from contextlib import contextmanager
|
|
5
|
-
from typing import Any, Literal
|
|
3
|
+
from typing import Literal
|
|
6
4
|
|
|
7
|
-
from
|
|
5
|
+
from agentic_mesh_protocol.cost.v1 import cost_pb2, cost_service_pb2_grpc
|
|
8
6
|
from google.protobuf import json_format
|
|
9
7
|
|
|
10
|
-
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
11
8
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
12
|
-
from digitalkin.grpc_servers.utils.
|
|
9
|
+
from digitalkin.grpc_servers.utils.grpc_error_handler import GrpcErrorHandlerMixin
|
|
13
10
|
from digitalkin.logger import logger
|
|
11
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
14
12
|
from digitalkin.services.cost.cost_strategy import (
|
|
15
13
|
CostConfig,
|
|
16
14
|
CostData,
|
|
@@ -20,40 +18,9 @@ from digitalkin.services.cost.cost_strategy import (
|
|
|
20
18
|
)
|
|
21
19
|
|
|
22
20
|
|
|
23
|
-
class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
21
|
+
class GrpcCost(CostStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
|
|
24
22
|
"""This class implements the default Cost strategy."""
|
|
25
23
|
|
|
26
|
-
@staticmethod
|
|
27
|
-
@contextmanager
|
|
28
|
-
def _handle_grpc_errors(operation: str) -> Generator[Any, Any, Any]:
|
|
29
|
-
"""Context manager for consistent gRPC error handling.
|
|
30
|
-
|
|
31
|
-
Yields:
|
|
32
|
-
Allow error handling in context.
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
operation: Description of the operation being performed.
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
ValueError: Error with the model validation.
|
|
39
|
-
ServerError: from gRPC Client.
|
|
40
|
-
CostServiceError: Unexpected error.
|
|
41
|
-
"""
|
|
42
|
-
try:
|
|
43
|
-
yield
|
|
44
|
-
except CostServiceError as e:
|
|
45
|
-
msg = f"CostServiceError in {operation}: {e}"
|
|
46
|
-
logger.exception(msg)
|
|
47
|
-
raise CostServiceError(msg) from e
|
|
48
|
-
except ServerError as e:
|
|
49
|
-
msg = f"gRPC {operation} failed: {e}"
|
|
50
|
-
logger.exception(msg)
|
|
51
|
-
raise ServerError(msg) from e
|
|
52
|
-
except Exception as e:
|
|
53
|
-
msg = f"Unexpected error in {operation}"
|
|
54
|
-
logger.exception(msg)
|
|
55
|
-
raise CostServiceError(msg) from e
|
|
56
|
-
|
|
57
24
|
def __init__(
|
|
58
25
|
self,
|
|
59
26
|
mission_id: str,
|
|
@@ -66,7 +33,7 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
|
66
33
|
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id, config=config)
|
|
67
34
|
channel = self._init_channel(client_config)
|
|
68
35
|
self.stub = cost_service_pb2_grpc.CostServiceStub(channel)
|
|
69
|
-
logger.debug("Channel client 'Cost' initialized
|
|
36
|
+
logger.debug("Channel client 'Cost' initialized successfully")
|
|
70
37
|
|
|
71
38
|
def add(
|
|
72
39
|
self,
|
|
@@ -84,7 +51,7 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
|
84
51
|
Raises:
|
|
85
52
|
CostServiceError: If the cost config is invalid
|
|
86
53
|
"""
|
|
87
|
-
with self.
|
|
54
|
+
with self.handle_grpc_errors("AddCost", CostServiceError):
|
|
88
55
|
cost_config = self.config.get(cost_config_name)
|
|
89
56
|
if cost_config is None:
|
|
90
57
|
msg = f"Cost config {cost_config_name} not found in the configuration."
|
|
@@ -122,7 +89,7 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
|
122
89
|
Returns:
|
|
123
90
|
CostData: The cost data
|
|
124
91
|
"""
|
|
125
|
-
with self.
|
|
92
|
+
with self.handle_grpc_errors("GetCost", CostServiceError):
|
|
126
93
|
request = cost_pb2.GetCostRequest(name=name, mission_id=self.mission_id)
|
|
127
94
|
response: cost_pb2.GetCostResponse = self.exec_grpc_query("GetCost", request)
|
|
128
95
|
cost_data_list = [
|
|
@@ -150,7 +117,7 @@ class GrpcCost(CostStrategy, GrpcClientWrapper):
|
|
|
150
117
|
Returns:
|
|
151
118
|
list[CostData]: The cost data
|
|
152
119
|
"""
|
|
153
|
-
with self.
|
|
120
|
+
with self.handle_grpc_errors("GetCosts", CostServiceError):
|
|
154
121
|
request = cost_pb2.GetCostsRequest(
|
|
155
122
|
mission_id=self.mission_id,
|
|
156
123
|
filter=cost_pb2.CostFilter(
|
|
@@ -198,8 +198,6 @@ class DefaultFilesystem(FilesystemStrategy):
|
|
|
198
198
|
end_idx = start_idx + list_size
|
|
199
199
|
paginated_files = filtered_files[start_idx:end_idx]
|
|
200
200
|
|
|
201
|
-
logger.critical(f"{filters=} | {paginated_files=}")
|
|
202
|
-
|
|
203
201
|
if include_content:
|
|
204
202
|
for file in paginated_files:
|
|
205
203
|
file.content = Path(file.storage_uri).read_bytes()
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"""gRPC filesystem implementation."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import Generator
|
|
4
|
-
from contextlib import contextmanager
|
|
5
3
|
from typing import Any, Literal
|
|
6
4
|
|
|
7
|
-
from
|
|
5
|
+
from agentic_mesh_protocol.filesystem.v1 import filesystem_pb2, filesystem_service_pb2_grpc
|
|
8
6
|
from google.protobuf import struct_pb2
|
|
9
7
|
from google.protobuf.json_format import MessageToDict
|
|
10
8
|
|
|
11
|
-
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
12
9
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
13
|
-
from digitalkin.grpc_servers.utils.
|
|
10
|
+
from digitalkin.grpc_servers.utils.grpc_error_handler import GrpcErrorHandlerMixin
|
|
14
11
|
from digitalkin.logger import logger
|
|
12
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
15
13
|
from digitalkin.services.filesystem.filesystem_strategy import (
|
|
16
14
|
FileFilter,
|
|
17
15
|
FilesystemRecord,
|
|
@@ -21,36 +19,9 @@ from digitalkin.services.filesystem.filesystem_strategy import (
|
|
|
21
19
|
)
|
|
22
20
|
|
|
23
21
|
|
|
24
|
-
class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
22
|
+
class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
|
|
25
23
|
"""Default state filesystem strategy."""
|
|
26
24
|
|
|
27
|
-
@staticmethod
|
|
28
|
-
@contextmanager
|
|
29
|
-
def _handle_grpc_errors(operation: str) -> Generator[Any, Any, Any]:
|
|
30
|
-
"""Context manager for consistent gRPC error handling.
|
|
31
|
-
|
|
32
|
-
Yields:
|
|
33
|
-
Allow error handling in context.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
operation: Description of the operation being performed.
|
|
37
|
-
|
|
38
|
-
Raises:
|
|
39
|
-
ValueError: Error with the model validation.
|
|
40
|
-
ServerError: from gRPC Client.
|
|
41
|
-
FilesystemServiceError: Filesystem service internal.
|
|
42
|
-
"""
|
|
43
|
-
try:
|
|
44
|
-
yield
|
|
45
|
-
except ServerError as e:
|
|
46
|
-
msg = f"gRPC {operation} failed: {e}"
|
|
47
|
-
logger.exception(msg)
|
|
48
|
-
raise ServerError(msg) from e
|
|
49
|
-
except Exception as e:
|
|
50
|
-
msg = f"Unexpected error in {operation}"
|
|
51
|
-
logger.exception(msg)
|
|
52
|
-
raise FilesystemServiceError(msg) from e
|
|
53
|
-
|
|
54
25
|
@staticmethod
|
|
55
26
|
def _file_type_to_enum(file_type: str) -> filesystem_pb2.FileType:
|
|
56
27
|
"""Convert a file type string to a FileType enum.
|
|
@@ -148,7 +119,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
148
119
|
self.service_name = "FilesystemService"
|
|
149
120
|
channel = self._init_channel(client_config)
|
|
150
121
|
self.stub = filesystem_service_pb2_grpc.FilesystemServiceStub(channel)
|
|
151
|
-
logger.debug("Channel client 'Filesystem' initialized
|
|
122
|
+
logger.debug("Channel client 'Filesystem' initialized successfully")
|
|
152
123
|
|
|
153
124
|
def upload_files(
|
|
154
125
|
self,
|
|
@@ -163,7 +134,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
163
134
|
tuple[list[FilesystemRecord], int, int]: List of uploaded files, total uploaded count, total failed count
|
|
164
135
|
"""
|
|
165
136
|
logger.debug("Uploading %d files", len(files))
|
|
166
|
-
with
|
|
137
|
+
with self.handle_grpc_errors("UploadFiles", FilesystemServiceError):
|
|
167
138
|
upload_files: list[filesystem_pb2.UploadFileData] = []
|
|
168
139
|
for file in files:
|
|
169
140
|
metadata_struct: struct_pb2.Struct | None = None
|
|
@@ -213,7 +184,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
213
184
|
context_id = self.setup_id
|
|
214
185
|
case "mission":
|
|
215
186
|
context_id = self.mission_id
|
|
216
|
-
with
|
|
187
|
+
with self.handle_grpc_errors("GetFile", FilesystemServiceError):
|
|
217
188
|
request = filesystem_pb2.GetFileRequest(
|
|
218
189
|
context=context_id,
|
|
219
190
|
file_id=file_id,
|
|
@@ -261,7 +232,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
261
232
|
Raises:
|
|
262
233
|
FilesystemServiceError: If there is an error during update
|
|
263
234
|
"""
|
|
264
|
-
with
|
|
235
|
+
with self.handle_grpc_errors("UpdateFile", FilesystemServiceError):
|
|
265
236
|
request = filesystem_pb2.UpdateFileRequest(
|
|
266
237
|
context=self.mission_id,
|
|
267
238
|
file_id=file_id,
|
|
@@ -295,7 +266,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
295
266
|
Returns:
|
|
296
267
|
tuple[dict[str, bool], int, int]: Results per file, total deleted count, total failed count
|
|
297
268
|
"""
|
|
298
|
-
with
|
|
269
|
+
with self.handle_grpc_errors("DeleteFiles", FilesystemServiceError):
|
|
299
270
|
request = filesystem_pb2.DeleteFilesRequest(
|
|
300
271
|
context=self.mission_id,
|
|
301
272
|
filters=self._filter_to_proto(filters),
|
|
@@ -332,7 +303,7 @@ class GrpcFilesystem(FilesystemStrategy, GrpcClientWrapper):
|
|
|
332
303
|
context_id = self.setup_id
|
|
333
304
|
case "mission":
|
|
334
305
|
context_id = self.mission_id
|
|
335
|
-
with
|
|
306
|
+
with self.handle_grpc_errors("GetFiles", FilesystemServiceError):
|
|
336
307
|
request = filesystem_pb2.GetFilesRequest(
|
|
337
308
|
context=context_id,
|
|
338
309
|
filters=self._filter_to_proto(filters),
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
"""This module is responsible for handling the registry service."""
|
|
2
2
|
|
|
3
|
+
from digitalkin.models.services.registry import (
|
|
4
|
+
ModuleInfo,
|
|
5
|
+
RegistryModuleStatus,
|
|
6
|
+
RegistryModuleType,
|
|
7
|
+
)
|
|
3
8
|
from digitalkin.services.registry.default_registry import DefaultRegistry
|
|
9
|
+
from digitalkin.services.registry.exceptions import (
|
|
10
|
+
RegistryModuleNotFoundError,
|
|
11
|
+
RegistryServiceError,
|
|
12
|
+
)
|
|
13
|
+
from digitalkin.services.registry.grpc_registry import GrpcRegistry
|
|
14
|
+
from digitalkin.services.registry.registry_models import ModuleStatusInfo
|
|
4
15
|
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
5
16
|
|
|
6
|
-
__all__ = [
|
|
17
|
+
__all__ = [
|
|
18
|
+
"DefaultRegistry",
|
|
19
|
+
"GrpcRegistry",
|
|
20
|
+
"ModuleInfo",
|
|
21
|
+
"ModuleStatusInfo",
|
|
22
|
+
"RegistryModuleNotFoundError",
|
|
23
|
+
"RegistryModuleStatus",
|
|
24
|
+
"RegistryModuleType",
|
|
25
|
+
"RegistryServiceError",
|
|
26
|
+
"RegistryStrategy",
|
|
27
|
+
]
|
|
@@ -1,10 +1,141 @@
|
|
|
1
|
-
"""Default registry."""
|
|
1
|
+
"""Default registry implementation."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from digitalkin.models.services.registry import (
|
|
6
|
+
ModuleInfo,
|
|
7
|
+
RegistryModuleStatus,
|
|
8
|
+
RegistryModuleType,
|
|
9
|
+
)
|
|
10
|
+
from digitalkin.services.registry.exceptions import RegistryModuleNotFoundError
|
|
11
|
+
from digitalkin.services.registry.registry_models import ModuleStatusInfo
|
|
3
12
|
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
4
13
|
|
|
5
14
|
|
|
6
15
|
class DefaultRegistry(RegistryStrategy):
|
|
7
|
-
"""Default registry strategy."""
|
|
16
|
+
"""Default registry strategy using in-memory storage."""
|
|
17
|
+
|
|
18
|
+
_modules: ClassVar[dict[str, ModuleInfo]] = {}
|
|
19
|
+
|
|
20
|
+
def discover_by_id(self, module_id: str) -> ModuleInfo:
|
|
21
|
+
"""Get module info by ID.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
module_id: The module identifier.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
ModuleInfo with module details.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
RegistryModuleNotFoundError: If module not found.
|
|
31
|
+
"""
|
|
32
|
+
if module_id not in self._modules:
|
|
33
|
+
raise RegistryModuleNotFoundError(module_id)
|
|
34
|
+
return self._modules[module_id]
|
|
35
|
+
|
|
36
|
+
def search(
|
|
37
|
+
self,
|
|
38
|
+
name: str | None = None,
|
|
39
|
+
module_type: str | None = None,
|
|
40
|
+
organization_id: str | None = None, # noqa: ARG002
|
|
41
|
+
) -> list[ModuleInfo]:
|
|
42
|
+
"""Search for modules by criteria.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
name: Filter by name (partial match).
|
|
46
|
+
module_type: Filter by type (archetype, tool).
|
|
47
|
+
organization_id: Filter by organization (not used in local storage).
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of matching modules.
|
|
51
|
+
"""
|
|
52
|
+
results = list(self._modules.values())
|
|
53
|
+
|
|
54
|
+
if name:
|
|
55
|
+
results = [m for m in results if name in m.name]
|
|
56
|
+
|
|
57
|
+
if module_type:
|
|
58
|
+
results = [m for m in results if m.module_type == module_type]
|
|
59
|
+
|
|
60
|
+
return results
|
|
61
|
+
|
|
62
|
+
def get_status(self, module_id: str) -> ModuleStatusInfo:
|
|
63
|
+
"""Get module status.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
module_id: The module identifier.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
ModuleStatusInfo with current status.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
RegistryModuleNotFoundError: If module not found.
|
|
73
|
+
"""
|
|
74
|
+
if module_id not in self._modules:
|
|
75
|
+
raise RegistryModuleNotFoundError(module_id)
|
|
76
|
+
|
|
77
|
+
module = self._modules[module_id]
|
|
78
|
+
return ModuleStatusInfo(
|
|
79
|
+
module_id=module_id,
|
|
80
|
+
status=module.status or RegistryModuleStatus.UNSPECIFIED,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def register(
|
|
84
|
+
self,
|
|
85
|
+
module_id: str,
|
|
86
|
+
address: str,
|
|
87
|
+
port: int,
|
|
88
|
+
version: str,
|
|
89
|
+
) -> ModuleInfo | None:
|
|
90
|
+
"""Register a module with the registry.
|
|
91
|
+
|
|
92
|
+
Note: Updates existing module or creates new one in local storage.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
module_id: Unique module identifier.
|
|
96
|
+
address: Network address.
|
|
97
|
+
port: Network port.
|
|
98
|
+
version: Module version.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
ModuleInfo if successful, None otherwise.
|
|
102
|
+
"""
|
|
103
|
+
existing = self._modules.get(module_id)
|
|
104
|
+
self._modules[module_id] = ModuleInfo(
|
|
105
|
+
module_id=module_id,
|
|
106
|
+
module_type=existing.module_type if existing else RegistryModuleType.UNSPECIFIED,
|
|
107
|
+
address=address,
|
|
108
|
+
port=port,
|
|
109
|
+
version=version,
|
|
110
|
+
name=existing.name if existing else module_id,
|
|
111
|
+
status=RegistryModuleStatus.ACTIVE,
|
|
112
|
+
)
|
|
113
|
+
return self._modules[module_id]
|
|
114
|
+
|
|
115
|
+
def heartbeat(self, module_id: str) -> RegistryModuleStatus:
|
|
116
|
+
"""Send heartbeat to keep module active.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
module_id: The module identifier.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Current module status after heartbeat.
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
RegistryModuleNotFoundError: If module not found.
|
|
126
|
+
"""
|
|
127
|
+
if module_id not in self._modules:
|
|
128
|
+
raise RegistryModuleNotFoundError(module_id)
|
|
8
129
|
|
|
9
|
-
|
|
10
|
-
|
|
130
|
+
module = self._modules[module_id]
|
|
131
|
+
# Update status to ACTIVE on heartbeat
|
|
132
|
+
self._modules[module_id] = ModuleInfo(
|
|
133
|
+
module_id=module.module_id,
|
|
134
|
+
module_type=module.module_type,
|
|
135
|
+
address=module.address,
|
|
136
|
+
port=module.port,
|
|
137
|
+
version=module.version,
|
|
138
|
+
name=module.name,
|
|
139
|
+
status=RegistryModuleStatus.ACTIVE,
|
|
140
|
+
)
|
|
141
|
+
return RegistryModuleStatus.ACTIVE
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Registry-specific exceptions.
|
|
2
|
+
|
|
3
|
+
This module contains custom exceptions for registry service operations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RegistryServiceError(Exception):
|
|
8
|
+
"""Base exception for registry service errors."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RegistryModuleNotFoundError(RegistryServiceError):
|
|
12
|
+
"""Raised when a module is not found in the registry."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, module_id: str) -> None:
|
|
15
|
+
"""Initialize the exception.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
module_id: The ID of the module that was not found.
|
|
19
|
+
"""
|
|
20
|
+
self.module_id = module_id
|
|
21
|
+
super().__init__(f"Module '{module_id}' not found in registry")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ModuleAlreadyExistsError(RegistryServiceError):
|
|
25
|
+
"""Raised when attempting to register an already-registered module."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, module_id: str) -> None:
|
|
28
|
+
"""Initialize the exception.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
module_id: The ID of the module that already exists.
|
|
32
|
+
"""
|
|
33
|
+
self.module_id = module_id
|
|
34
|
+
super().__init__(f"Module '{module_id}' already registered")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class InvalidStatusError(RegistryServiceError):
|
|
38
|
+
"""Raised when an invalid status is provided."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, status: int) -> None:
|
|
41
|
+
"""Initialize the exception.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
status: The invalid status value.
|
|
45
|
+
"""
|
|
46
|
+
self.status = status
|
|
47
|
+
super().__init__(f"Invalid module status: {status}")
|