digitalkin 0.2.25rc0__py3-none-any.whl → 0.3.1.dev2__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.
- 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 +137 -31
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/{modules → core}/job_manager/taskiq_broker.py +116 -22
- 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 +406 -0
- digitalkin/grpc_servers/__init__.py +1 -19
- digitalkin/grpc_servers/_base_server.py +3 -3
- digitalkin/grpc_servers/module_server.py +27 -43
- digitalkin/grpc_servers/module_servicer.py +51 -36
- digitalkin/grpc_servers/registry_server.py +2 -2
- digitalkin/grpc_servers/registry_servicer.py +4 -4
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +0 -8
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +4 -4
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -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/core/__init__.py +1 -0
- digitalkin/{modules/job_manager → models/core}/job_manager_models.py +3 -3
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/{grpc_servers/utils → models/grpc_servers}/models.py +5 -5
- digitalkin/models/module/__init__.py +2 -0
- digitalkin/models/module/module.py +9 -1
- digitalkin/models/module/module_context.py +122 -6
- digitalkin/models/module/module_types.py +307 -19
- digitalkin/models/services/__init__.py +9 -0
- digitalkin/models/services/cost.py +1 -0
- digitalkin/models/services/storage.py +39 -5
- digitalkin/modules/_base_module.py +123 -118
- digitalkin/modules/tool_module.py +10 -2
- digitalkin/modules/trigger_handler.py +7 -6
- 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/setup/default_setup.py +5 -6
- digitalkin/services/setup/grpc_setup.py +52 -15
- digitalkin/services/storage/grpc_storage.py +4 -4
- digitalkin/services/user_profile/__init__.py +1 -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 +40 -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-0.2.25rc0.dist-info → digitalkin-0.3.1.dev2.dist-info}/METADATA +11 -30
- digitalkin-0.3.1.dev2.dist-info/RECORD +119 -0
- modules/dynamic_setup_module.py +362 -0
- 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.1.dev2.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.1.dev2.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.25rc0.dist-info → digitalkin-0.3.1.dev2.dist-info}/top_level.txt +0 -0
|
@@ -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 digitalkin_proto.
|
|
5
|
+
from digitalkin_proto.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),
|
|
@@ -7,8 +7,7 @@ from typing import Any
|
|
|
7
7
|
from pydantic import ValidationError
|
|
8
8
|
|
|
9
9
|
from digitalkin.logger import logger
|
|
10
|
-
from digitalkin.services.setup.
|
|
11
|
-
from digitalkin.services.setup.setup_strategy import SetupServiceError, SetupStrategy
|
|
10
|
+
from digitalkin.services.setup.setup_strategy import SetupData, SetupServiceError, SetupStrategy, SetupVersionData
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class DefaultSetup(SetupStrategy):
|
|
@@ -47,7 +46,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
47
46
|
)
|
|
48
47
|
valid_data.id = setup_id
|
|
49
48
|
self.setups[setup_id] = valid_data
|
|
50
|
-
logger.debug("CREATE SETUP DATA %s:%s
|
|
49
|
+
logger.debug("CREATE SETUP DATA %s:%s successful", setup_id, valid_data)
|
|
51
50
|
return setup_id
|
|
52
51
|
|
|
53
52
|
def get_setup(self, setup_dict: dict[str, Any]) -> SetupData:
|
|
@@ -131,7 +130,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
131
130
|
if setup_version_dict["setup_id"] not in self.setup_versions:
|
|
132
131
|
self.setup_versions[setup_version_dict["setup_id"]] = {}
|
|
133
132
|
self.setup_versions[setup_version_dict["setup_id"]][valid_data.version] = valid_data
|
|
134
|
-
logger.debug("CREATE SETUP VERSION DATA %s:%s
|
|
133
|
+
logger.debug("CREATE SETUP VERSION DATA %s:%s successful", setup_version_dict["setup_id"], valid_data)
|
|
135
134
|
return valid_data.version
|
|
136
135
|
|
|
137
136
|
def get_setup_version(self, setup_version_dict: dict[str, Any]) -> SetupVersionData:
|
|
@@ -173,7 +172,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
173
172
|
|
|
174
173
|
return [
|
|
175
174
|
value
|
|
176
|
-
for value in setup_version_dict["setup_id"].values()
|
|
175
|
+
for value in self.setup_versions[setup_version_dict["setup_id"]].values()
|
|
177
176
|
if setup_version_dict["query_versions"] in value.version or setup_version_dict["name"] in value.name
|
|
178
177
|
]
|
|
179
178
|
|
|
@@ -190,7 +189,7 @@ class DefaultSetup(SetupStrategy):
|
|
|
190
189
|
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
|
|
191
190
|
return False
|
|
192
191
|
|
|
193
|
-
if setup_version_dict["version"] not in self.setup_versions["setup_id"]:
|
|
192
|
+
if setup_version_dict["version"] not in self.setup_versions[setup_version_dict["setup_id"]]:
|
|
194
193
|
logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
|
|
195
194
|
return False
|
|
196
195
|
|
|
@@ -5,7 +5,7 @@ from contextlib import contextmanager
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
import grpc
|
|
8
|
-
from digitalkin_proto.
|
|
8
|
+
from digitalkin_proto.agentic_mesh_protocol.setup.v1 import (
|
|
9
9
|
setup_pb2,
|
|
10
10
|
setup_service_pb2_grpc,
|
|
11
11
|
)
|
|
@@ -15,8 +15,8 @@ from pydantic import ValidationError
|
|
|
15
15
|
|
|
16
16
|
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
17
17
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
18
|
-
from digitalkin.grpc_servers.utils.models import ClientConfig
|
|
19
18
|
from digitalkin.logger import logger
|
|
19
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
20
20
|
from digitalkin.services.setup.setup_strategy import SetupData, SetupServiceError, SetupStrategy, SetupVersionData
|
|
21
21
|
|
|
22
22
|
|
|
@@ -30,10 +30,10 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
30
30
|
"""
|
|
31
31
|
channel = self._init_channel(config)
|
|
32
32
|
self.stub = setup_service_pb2_grpc.SetupServiceStub(channel)
|
|
33
|
-
logger.debug("Channel client 'setup' initialized
|
|
33
|
+
logger.debug("Channel client 'setup' initialized successfully")
|
|
34
34
|
|
|
35
35
|
@contextmanager
|
|
36
|
-
def
|
|
36
|
+
def handle_grpc_errors(self, operation: str) -> Generator[Any, Any, Any]: # noqa: PLR6301
|
|
37
37
|
"""Context manager for consistent gRPC error handling.
|
|
38
38
|
|
|
39
39
|
Yields:
|
|
@@ -76,7 +76,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
76
76
|
ServerError: If gRPC operation fails.
|
|
77
77
|
SetupServiceError: For any unexpected internal error.
|
|
78
78
|
"""
|
|
79
|
-
with self.
|
|
79
|
+
with self.handle_grpc_errors("Setup Creation"):
|
|
80
80
|
valid_data = SetupData.model_validate(setup_dict)
|
|
81
81
|
|
|
82
82
|
request = setup_pb2.CreateSetupRequest(
|
|
@@ -104,7 +104,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
104
104
|
ServerError: If gRPC operation fails.
|
|
105
105
|
SetupServiceError: For any unexpected internal error.
|
|
106
106
|
"""
|
|
107
|
-
with self.
|
|
107
|
+
with self.handle_grpc_errors("Get Setup"):
|
|
108
108
|
if "setup_id" not in setup_dict:
|
|
109
109
|
msg = "Setup name is required"
|
|
110
110
|
raise ValidationError(msg)
|
|
@@ -132,7 +132,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
132
132
|
"""
|
|
133
133
|
current_setup_version = None
|
|
134
134
|
|
|
135
|
-
with self.
|
|
135
|
+
with self.handle_grpc_errors("Setup Update"):
|
|
136
136
|
valid_data = SetupData.model_validate(setup_dict)
|
|
137
137
|
|
|
138
138
|
if valid_data.current_setup_version is not None:
|
|
@@ -162,7 +162,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
162
162
|
ServerError: If gRPC operation fails.
|
|
163
163
|
SetupServiceError: For any unexpected internal error.
|
|
164
164
|
"""
|
|
165
|
-
with self.
|
|
165
|
+
with self.handle_grpc_errors("Setup Deletion"):
|
|
166
166
|
setup_id = setup_dict.get("setup_id")
|
|
167
167
|
if not setup_id:
|
|
168
168
|
msg = "Setup name is required for deletion"
|
|
@@ -186,7 +186,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
186
186
|
ServerError: If gRPC operation fails.
|
|
187
187
|
SetupServiceError: For any unexpected internal error.
|
|
188
188
|
"""
|
|
189
|
-
with self.
|
|
189
|
+
with self.handle_grpc_errors("Setup Version Creation"):
|
|
190
190
|
valid_data = SetupVersionData.model_validate(setup_version_dict)
|
|
191
191
|
content_struct = Struct()
|
|
192
192
|
content_struct.update(valid_data.content)
|
|
@@ -216,14 +216,16 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
216
216
|
ServerError: If gRPC operation fails.
|
|
217
217
|
SetupServiceError: For any unexpected internal error.
|
|
218
218
|
"""
|
|
219
|
-
with self.
|
|
219
|
+
with self.handle_grpc_errors("Get Setup Version"):
|
|
220
220
|
setup_version_id = setup_version_dict.get("setup_version_id")
|
|
221
221
|
if not setup_version_id:
|
|
222
222
|
msg = "Setup version id is required"
|
|
223
223
|
raise ValidationError(msg)
|
|
224
224
|
request = setup_pb2.GetSetupVersionRequest(setup_version_id=setup_version_id)
|
|
225
225
|
response = self.exec_grpc_query("GetSetupVersion", request)
|
|
226
|
-
return SetupVersionData(
|
|
226
|
+
return SetupVersionData(
|
|
227
|
+
**json_format.MessageToDict(response.setup_version, preserving_proto_field_name=True)
|
|
228
|
+
)
|
|
227
229
|
|
|
228
230
|
def search_setup_versions(self, setup_version_dict: dict[str, Any]) -> list[SetupVersionData]:
|
|
229
231
|
"""Search for setup versions based on filters.
|
|
@@ -239,7 +241,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
239
241
|
SetupServiceError: For any unexpected internal error.
|
|
240
242
|
ValidationError: If both name and version are not provided.
|
|
241
243
|
"""
|
|
242
|
-
with self.
|
|
244
|
+
with self.handle_grpc_errors("Search Setup Versions"):
|
|
243
245
|
if "name" not in setup_version_dict and "version" not in setup_version_dict:
|
|
244
246
|
msg = "Either name or version must be provided"
|
|
245
247
|
raise ValidationError(msg)
|
|
@@ -248,7 +250,10 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
248
250
|
version=setup_version_dict.get("version", ""),
|
|
249
251
|
)
|
|
250
252
|
response = self.exec_grpc_query("SearchSetupVersions", request)
|
|
251
|
-
return [
|
|
253
|
+
return [
|
|
254
|
+
SetupVersionData(**json_format.MessageToDict(sv, preserving_proto_field_name=True))
|
|
255
|
+
for sv in response.setup_versions
|
|
256
|
+
]
|
|
252
257
|
|
|
253
258
|
def update_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
|
|
254
259
|
"""Update an existing setup version.
|
|
@@ -264,7 +269,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
264
269
|
ServerError: If gRPC operation fails.
|
|
265
270
|
SetupServiceError: For any unexpected internal error.
|
|
266
271
|
"""
|
|
267
|
-
with self.
|
|
272
|
+
with self.handle_grpc_errors("Setup Version Update"):
|
|
268
273
|
valid_data = SetupVersionData.model_validate(setup_version_dict)
|
|
269
274
|
content_struct = Struct()
|
|
270
275
|
content_struct.update(valid_data.content)
|
|
@@ -295,7 +300,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
295
300
|
ServerError: If gRPC operation fails.
|
|
296
301
|
SetupServiceError: For any unexpected internal error.
|
|
297
302
|
"""
|
|
298
|
-
with self.
|
|
303
|
+
with self.handle_grpc_errors("Setup Version Deletion"):
|
|
299
304
|
setup_version_id = setup_version_dict.get("setup_version_id")
|
|
300
305
|
if not setup_version_id:
|
|
301
306
|
msg = "Setup version id is required for deletion"
|
|
@@ -304,3 +309,35 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
|
|
|
304
309
|
response = self.exec_grpc_query("DeleteSetupVersion", request)
|
|
305
310
|
logger.debug("Setup Version '%s' query sent successfully", setup_version_id)
|
|
306
311
|
return getattr(response, "success", False)
|
|
312
|
+
|
|
313
|
+
def list_setups(self, list_dict: dict[str, Any]) -> dict[str, Any]:
|
|
314
|
+
"""List setups with optional filtering and pagination.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
list_dict: Dictionary with optional filters:
|
|
318
|
+
- organisation_id: Filter by organisation
|
|
319
|
+
- owner_id: Filter by owner
|
|
320
|
+
- limit: Maximum number of results
|
|
321
|
+
- offset: Number of results to skip
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
dict[str, Any]: Dictionary with 'setups' list and 'total_count'.
|
|
325
|
+
|
|
326
|
+
Raises:
|
|
327
|
+
ServerError: If gRPC operation fails.
|
|
328
|
+
SetupServiceError: For any unexpected internal error.
|
|
329
|
+
"""
|
|
330
|
+
with self.handle_grpc_errors("List Setups"):
|
|
331
|
+
request = setup_pb2.ListSetupsRequest(
|
|
332
|
+
organisation_id=list_dict.get("organisation_id", ""),
|
|
333
|
+
owner_id=list_dict.get("owner_id", ""),
|
|
334
|
+
limit=list_dict.get("limit", 0),
|
|
335
|
+
offset=list_dict.get("offset", 0),
|
|
336
|
+
)
|
|
337
|
+
response = self.exec_grpc_query("ListSetups", request)
|
|
338
|
+
return {
|
|
339
|
+
"setups": [
|
|
340
|
+
json_format.MessageToDict(setup, preserving_proto_field_name=True) for setup in response.setups
|
|
341
|
+
],
|
|
342
|
+
"total_count": response.total_count,
|
|
343
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""This module implements the default storage strategy."""
|
|
2
2
|
|
|
3
|
-
from digitalkin_proto.
|
|
3
|
+
from digitalkin_proto.agentic_mesh_protocol.storage.v1 import data_pb2, storage_service_pb2_grpc
|
|
4
4
|
from google.protobuf import json_format
|
|
5
5
|
from google.protobuf.struct_pb2 import Struct
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
|
|
8
8
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
9
|
-
from digitalkin.grpc_servers.utils.models import ClientConfig
|
|
10
9
|
from digitalkin.logger import logger
|
|
10
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
11
11
|
from digitalkin.services.storage.storage_strategy import (
|
|
12
12
|
DataType,
|
|
13
13
|
StorageRecord,
|
|
@@ -128,7 +128,7 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
128
128
|
resp = self.exec_grpc_query("UpdateRecord", req)
|
|
129
129
|
return self._build_record_from_proto(resp.stored_data)
|
|
130
130
|
except Exception:
|
|
131
|
-
logger.
|
|
131
|
+
logger.warning("gRPC UpdateRecord failed for %s:%s", collection, record_id)
|
|
132
132
|
return None
|
|
133
133
|
|
|
134
134
|
def _remove(self, collection: str, record_id: str) -> bool:
|
|
@@ -211,4 +211,4 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
|
|
|
211
211
|
|
|
212
212
|
channel = self._init_channel(client_config)
|
|
213
213
|
self.stub = storage_service_pb2_grpc.StorageServiceStub(channel)
|
|
214
|
-
logger.debug("Channel client 'storage' initialized
|
|
214
|
+
logger.debug("Channel client 'storage' initialized successfully")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""UserProfile service package."""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Default user profile implementation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from digitalkin.logger import logger
|
|
6
|
+
from digitalkin.services.user_profile.user_profile_strategy import (
|
|
7
|
+
UserProfileServiceError,
|
|
8
|
+
UserProfileStrategy,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DefaultUserProfile(UserProfileStrategy):
|
|
13
|
+
"""Default user profile strategy with in-memory storage."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
mission_id: str,
|
|
18
|
+
setup_id: str,
|
|
19
|
+
setup_version_id: str,
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Initialize the strategy.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
mission_id: The ID of the mission this strategy is associated with
|
|
25
|
+
setup_id: The ID of the setup
|
|
26
|
+
setup_version_id: The ID of the setup version
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
29
|
+
self.db: dict[str, dict[str, Any]] = {}
|
|
30
|
+
|
|
31
|
+
def get_user_profile(self) -> dict[str, Any]:
|
|
32
|
+
"""Get user profile from in-memory storage.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
dict[str, Any]: User profile data
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
UserProfileServiceError: If the user profile is not found
|
|
39
|
+
"""
|
|
40
|
+
if self.mission_id not in self.db:
|
|
41
|
+
msg = f"User profile for mission {self.mission_id} not found in the database."
|
|
42
|
+
logger.warning(msg)
|
|
43
|
+
raise UserProfileServiceError(msg)
|
|
44
|
+
|
|
45
|
+
logger.debug(f"Retrieved user profile for mission_id: {self.mission_id}")
|
|
46
|
+
return self.db[self.mission_id]
|
|
47
|
+
|
|
48
|
+
def add_user_profile(self, user_profile_data: dict[str, Any]) -> None:
|
|
49
|
+
"""Add a user profile to the in-memory database (helper for testing).
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
user_profile_data: Dictionary containing user profile data
|
|
53
|
+
"""
|
|
54
|
+
self.db[self.mission_id] = user_profile_data
|
|
55
|
+
logger.debug(f"Added user profile for mission_id: {self.mission_id}")
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Digital Kin UserProfile Service gRPC Client."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from digitalkin_proto.agentic_mesh_protocol.user_profile.v1 import (
|
|
6
|
+
user_profile_pb2,
|
|
7
|
+
user_profile_service_pb2_grpc,
|
|
8
|
+
)
|
|
9
|
+
from google.protobuf import json_format
|
|
10
|
+
|
|
11
|
+
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
12
|
+
from digitalkin.grpc_servers.utils.grpc_error_handler import GrpcErrorHandlerMixin
|
|
13
|
+
from digitalkin.logger import logger
|
|
14
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
15
|
+
from digitalkin.services.user_profile.user_profile_strategy import UserProfileServiceError, UserProfileStrategy
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class GrpcUserProfile(UserProfileStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
|
|
19
|
+
"""This class implements the gRPC user profile service."""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
mission_id: str,
|
|
24
|
+
setup_id: str,
|
|
25
|
+
setup_version_id: str,
|
|
26
|
+
client_config: ClientConfig,
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Initialize the user profile service.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
mission_id: The ID of the mission this strategy is associated with
|
|
32
|
+
setup_id: The ID of the setup
|
|
33
|
+
setup_version_id: The ID of the setup version
|
|
34
|
+
client_config: Client configuration for gRPC connection
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(mission_id=mission_id, setup_id=setup_id, setup_version_id=setup_version_id)
|
|
37
|
+
channel = self._init_channel(client_config)
|
|
38
|
+
self.stub = user_profile_service_pb2_grpc.UserProfileServiceStub(channel)
|
|
39
|
+
logger.debug("Channel client 'UserProfile' initialized successfully")
|
|
40
|
+
|
|
41
|
+
def get_user_profile(self) -> dict[str, Any]:
|
|
42
|
+
"""Get user profile by mission_id (which maps to user_id).
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
dict[str, Any]: User profile data
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
UserProfileServiceError: If the user profile cannot be retrieved
|
|
49
|
+
ServerError: If gRPC operation fails
|
|
50
|
+
"""
|
|
51
|
+
with self.handle_grpc_errors("GetUserProfile", UserProfileServiceError):
|
|
52
|
+
# mission_id maps to the user context in the proto request
|
|
53
|
+
request = user_profile_pb2.GetUserProfileRequest(mission_id=self.mission_id)
|
|
54
|
+
response = self.exec_grpc_query("GetUserProfile", request)
|
|
55
|
+
|
|
56
|
+
if not response.success:
|
|
57
|
+
msg = f"Failed to get user profile for mission_id: {self.mission_id}"
|
|
58
|
+
logger.error(msg)
|
|
59
|
+
raise UserProfileServiceError(msg)
|
|
60
|
+
|
|
61
|
+
# Convert proto to dict
|
|
62
|
+
user_profile_dict = json_format.MessageToDict(
|
|
63
|
+
response.user_profile,
|
|
64
|
+
preserving_proto_field_name=True,
|
|
65
|
+
always_print_fields_with_no_presence=True,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
logger.debug(f"Retrieved user profile for mission_id: {self.mission_id}")
|
|
69
|
+
return user_profile_dict
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""This module contains the abstract base class for UserProfile strategies."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from digitalkin.services.base_strategy import BaseStrategy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UserProfileServiceError(Exception):
|
|
10
|
+
"""Base exception for UserProfile service errors."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UserProfileStrategy(BaseStrategy, ABC):
|
|
14
|
+
"""Abstract base class for UserProfile strategies."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
mission_id: str,
|
|
19
|
+
setup_id: str,
|
|
20
|
+
setup_version_id: str,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Initialize the strategy.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
mission_id: The ID of the mission this strategy is associated with
|
|
26
|
+
setup_id: The ID of the setup
|
|
27
|
+
setup_version_id: The ID of the setup version this strategy is associated with
|
|
28
|
+
"""
|
|
29
|
+
super().__init__(mission_id, setup_id, setup_version_id)
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_user_profile(self) -> dict[str, Any]:
|
|
33
|
+
"""Get user profile data.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
dict[str, Any]: User profile data
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
UserProfileServiceError: If the user profile cannot be retrieved
|
|
40
|
+
"""
|
digitalkin/utils/__init__.py
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
1
|
"""General utils folder."""
|
|
2
|
+
|
|
3
|
+
from digitalkin.utils.dynamic_schema import (
|
|
4
|
+
DEFAULT_TIMEOUT,
|
|
5
|
+
DynamicField,
|
|
6
|
+
Fetcher,
|
|
7
|
+
ResolveResult,
|
|
8
|
+
get_dynamic_metadata,
|
|
9
|
+
get_fetchers,
|
|
10
|
+
has_dynamic,
|
|
11
|
+
resolve,
|
|
12
|
+
resolve_safe,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Alias for cleaner API: `Dynamic` is shorter than `DynamicField`
|
|
16
|
+
Dynamic = DynamicField
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"DEFAULT_TIMEOUT",
|
|
20
|
+
"Dynamic",
|
|
21
|
+
"DynamicField",
|
|
22
|
+
"Fetcher",
|
|
23
|
+
"ResolveResult",
|
|
24
|
+
"get_dynamic_metadata",
|
|
25
|
+
"get_fetchers",
|
|
26
|
+
"has_dynamic",
|
|
27
|
+
"resolve",
|
|
28
|
+
"resolve_safe",
|
|
29
|
+
]
|
digitalkin/utils/arg_parser.py
CHANGED
|
@@ -13,7 +13,7 @@ logger.setLevel(logging.INFO)
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class DevelopmentModeMappingAction(Action):
|
|
16
|
-
"""."""
|
|
16
|
+
"""ArgParse Action to map an environment variable to a ServicesMode enum."""
|
|
17
17
|
|
|
18
18
|
def __init__(
|
|
19
19
|
self,
|
|
@@ -22,7 +22,7 @@ class DevelopmentModeMappingAction(Action):
|
|
|
22
22
|
default: str | None = None,
|
|
23
23
|
**kwargs: dict[str, Any],
|
|
24
24
|
) -> None:
|
|
25
|
-
"""."""
|
|
25
|
+
"""Initialize the DevelopmentModeMappingAction."""
|
|
26
26
|
default = ServicesMode(os.environ.get(env_var, default))
|
|
27
27
|
|
|
28
28
|
if required and default:
|