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
@@ -0,0 +1,306 @@
1
+ """gRPC Registry client implementation.
2
+
3
+ This module provides a gRPC-based registry client that communicates with
4
+ the Service Provider's Registry service.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from agentic_mesh_protocol.registry.v1 import (
10
+ registry_enums_pb2,
11
+ registry_models_pb2,
12
+ registry_requests_pb2,
13
+ registry_service_pb2_grpc,
14
+ )
15
+
16
+ from digitalkin.grpc_servers.utils.exceptions import ServerError
17
+ from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
18
+ from digitalkin.grpc_servers.utils.grpc_error_handler import GrpcErrorHandlerMixin
19
+ from digitalkin.logger import logger
20
+ from digitalkin.models.grpc_servers.models import ClientConfig
21
+ from digitalkin.models.services.registry import (
22
+ ModuleInfo,
23
+ RegistryModuleStatus,
24
+ RegistryModuleType,
25
+ )
26
+ from digitalkin.services.registry.exceptions import (
27
+ RegistryModuleNotFoundError,
28
+ RegistryServiceError,
29
+ )
30
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
31
+ from digitalkin.services.registry.registry_strategy import RegistryStrategy
32
+
33
+
34
+ class GrpcRegistry(RegistryStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
35
+ """gRPC-based registry client.
36
+
37
+ This client communicates with the Service Provider's Registry service
38
+ to perform module discovery, registration, and status management operations.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ mission_id: str,
44
+ setup_id: str,
45
+ setup_version_id: str,
46
+ client_config: ClientConfig,
47
+ config: dict[str, Any] | None = None,
48
+ ) -> None:
49
+ """Initialize the gRPC registry client."""
50
+ RegistryStrategy.__init__(self, mission_id, setup_id, setup_version_id, config)
51
+ self.service_name = "RegistryService"
52
+ self.stub = registry_service_pb2_grpc.RegistryServiceStub(self._init_channel(client_config))
53
+ logger.debug("Channel client 'Registry' initialized successfully")
54
+
55
+ @staticmethod
56
+ def _proto_to_module_info(
57
+ descriptor: registry_models_pb2.ModuleDescriptor,
58
+ ) -> ModuleInfo:
59
+ """Convert proto ModuleDescriptor to ModuleInfo.
60
+
61
+ Args:
62
+ descriptor: Proto ModuleDescriptor message.
63
+
64
+ Returns:
65
+ ModuleInfo with mapped fields.
66
+ """
67
+ type_name = registry_enums_pb2.ModuleType.Name(descriptor.module_type).removeprefix("MODULE_TYPE_")
68
+ return ModuleInfo(
69
+ module_id=descriptor.id,
70
+ module_type=RegistryModuleType[type_name],
71
+ address=descriptor.address,
72
+ port=descriptor.port,
73
+ version=descriptor.version,
74
+ name=descriptor.name,
75
+ documentation=descriptor.documentation or None,
76
+ )
77
+
78
+ def discover_by_id(self, module_id: str) -> ModuleInfo:
79
+ """Get module info by ID.
80
+
81
+ Args:
82
+ module_id: The module identifier.
83
+
84
+ Returns:
85
+ ModuleInfo with module details.
86
+
87
+ Raises:
88
+ RegistryModuleNotFoundError: If module not found.
89
+ RegistryServiceError: If gRPC call fails.
90
+ """
91
+ logger.debug("Discovering module by ID", extra={"module_id": module_id})
92
+
93
+ with self.handle_grpc_errors("GetModule", RegistryServiceError):
94
+ try:
95
+ response = self.exec_grpc_query(
96
+ "GetModule",
97
+ registry_requests_pb2.GetModuleRequest(module_id=module_id),
98
+ )
99
+ except ServerError as e:
100
+ msg = f"Failed to discover module '{module_id}': {e}"
101
+ logger.error(msg)
102
+ raise RegistryServiceError(msg) from e
103
+
104
+ if not response.id:
105
+ logger.warning("Module not found in registry", extra={"module_id": module_id})
106
+ raise RegistryModuleNotFoundError(module_id)
107
+
108
+ logger.debug(
109
+ "Module discovered",
110
+ extra={
111
+ "module_id": response.id,
112
+ "address": response.address,
113
+ "port": response.port,
114
+ },
115
+ )
116
+ return self._proto_to_module_info(response)
117
+
118
+ def search(
119
+ self,
120
+ name: str | None = None,
121
+ module_type: str | None = None,
122
+ organization_id: str | None = None,
123
+ ) -> list[ModuleInfo]:
124
+ """Search for modules by criteria.
125
+
126
+ Args:
127
+ name: Filter by name (partial match via query).
128
+ module_type: Filter by type (archetype, tool).
129
+ organization_id: Filter by organization.
130
+
131
+ Returns:
132
+ List of matching modules.
133
+
134
+ Raises:
135
+ RegistryServiceError: If gRPC call fails.
136
+ """
137
+ logger.debug(
138
+ "Searching modules",
139
+ extra={
140
+ "name": name,
141
+ "module_type": module_type,
142
+ "organization_id": organization_id,
143
+ },
144
+ )
145
+
146
+ with self.handle_grpc_errors("DiscoverModules", RegistryServiceError):
147
+ module_types = []
148
+ if module_type:
149
+ enum_val = RegistryModuleType[module_type.upper()]
150
+ module_types.append(getattr(registry_enums_pb2, f"MODULE_TYPE_{enum_val.name}"))
151
+
152
+ try:
153
+ response = self.exec_grpc_query(
154
+ "DiscoverModules",
155
+ registry_requests_pb2.DiscoverModulesRequest(
156
+ query=name or "",
157
+ organization_id=organization_id or "",
158
+ module_types=module_types,
159
+ ),
160
+ )
161
+ except ServerError as e:
162
+ msg = f"Failed to search modules: {e}"
163
+ logger.error(msg)
164
+ raise RegistryServiceError(msg) from e
165
+
166
+ logger.debug("Search returned %d modules", len(response.modules))
167
+ return [self._proto_to_module_info(m) for m in response.modules]
168
+
169
+ def get_status(self, module_id: str) -> ModuleStatusInfo:
170
+ """Get module status by fetching the module.
171
+
172
+ Args:
173
+ module_id: The module identifier.
174
+
175
+ Returns:
176
+ ModuleStatusInfo with current status.
177
+
178
+ Raises:
179
+ RegistryModuleNotFoundError: If module not found.
180
+ RegistryServiceError: If gRPC call fails.
181
+ """
182
+ logger.debug("Getting module status", extra={"module_id": module_id})
183
+
184
+ with self.handle_grpc_errors("GetModule", RegistryServiceError):
185
+ try:
186
+ response = self.exec_grpc_query(
187
+ "GetModule",
188
+ registry_requests_pb2.GetModuleRequest(module_id=module_id),
189
+ )
190
+ except ServerError as e:
191
+ msg = f"Failed to get module status for '{module_id}': {e}"
192
+ logger.error(msg)
193
+ raise RegistryServiceError(msg) from e
194
+
195
+ if not response.id:
196
+ logger.warning("Module not found in registry", extra={"module_id": module_id})
197
+ raise RegistryModuleNotFoundError(module_id)
198
+
199
+ status_name = registry_enums_pb2.ModuleStatus.Name(response.status).removeprefix("MODULE_STATUS_")
200
+ logger.debug(
201
+ "Module status retrieved",
202
+ extra={"module_id": response.id, "status": status_name},
203
+ )
204
+ return ModuleStatusInfo(
205
+ module_id=response.id,
206
+ status=RegistryModuleStatus[status_name],
207
+ )
208
+
209
+ def register(
210
+ self,
211
+ module_id: str,
212
+ address: str,
213
+ port: int,
214
+ version: str,
215
+ ) -> ModuleInfo | None:
216
+ """Register a module with the registry.
217
+
218
+ Note: The new proto only updates address/port/version for an existing module.
219
+ The module must already exist in the registry database.
220
+
221
+ Args:
222
+ module_id: Unique module identifier.
223
+ address: Network address.
224
+ port: Network port.
225
+ version: Module version.
226
+
227
+ Returns:
228
+ ModuleInfo if successful, None if module not found.
229
+
230
+ Raises:
231
+ RegistryServiceError: If gRPC call fails.
232
+ """
233
+ logger.info(
234
+ "Registering module with registry",
235
+ extra={
236
+ "module_id": module_id,
237
+ "address": address,
238
+ "port": port,
239
+ "version": version,
240
+ },
241
+ )
242
+
243
+ with self.handle_grpc_errors("RegisterModule", RegistryServiceError):
244
+ try:
245
+ response = self.exec_grpc_query(
246
+ "RegisterModule",
247
+ registry_requests_pb2.RegisterModuleRequest(
248
+ module_id=module_id,
249
+ address=address,
250
+ port=port,
251
+ version=version,
252
+ ),
253
+ )
254
+ except ServerError as e:
255
+ msg = f"Failed to register module '{module_id}': {e}"
256
+ logger.error(msg)
257
+ raise RegistryServiceError(msg) from e
258
+
259
+ if not response.module or not response.module.id:
260
+ logger.warning(
261
+ "Registry returned empty response for module registration",
262
+ extra={"module_id": module_id},
263
+ )
264
+ return None
265
+
266
+ logger.info(
267
+ "Module registered successfully",
268
+ extra={
269
+ "module_id": response.module.id,
270
+ "address": response.module.address,
271
+ "port": response.module.port,
272
+ },
273
+ )
274
+ return self._proto_to_module_info(response.module)
275
+
276
+ def heartbeat(self, module_id: str) -> RegistryModuleStatus:
277
+ """Send heartbeat to keep module active.
278
+
279
+ Args:
280
+ module_id: The module identifier.
281
+
282
+ Returns:
283
+ Current module status after heartbeat.
284
+
285
+ Raises:
286
+ RegistryServiceError: If gRPC call fails.
287
+ """
288
+ logger.debug("Sending heartbeat", extra={"module_id": module_id})
289
+
290
+ with self.handle_grpc_errors("Heartbeat", RegistryServiceError):
291
+ try:
292
+ response = self.exec_grpc_query(
293
+ "Heartbeat",
294
+ registry_requests_pb2.HeartbeatRequest(module_id=module_id),
295
+ )
296
+ except ServerError as e:
297
+ msg = f"Failed to send heartbeat for '{module_id}': {e}"
298
+ logger.error(msg)
299
+ raise RegistryServiceError(msg) from e
300
+
301
+ status_name = registry_enums_pb2.ModuleStatus.Name(response.status).removeprefix("MODULE_STATUS_")
302
+ logger.debug(
303
+ "Heartbeat response",
304
+ extra={"module_id": module_id, "status": status_name},
305
+ )
306
+ return RegistryModuleStatus[status_name]
@@ -0,0 +1,15 @@
1
+ """Registry data models.
2
+
3
+ This module contains Pydantic models for registry service data structures.
4
+ """
5
+
6
+ from pydantic import BaseModel
7
+
8
+ from digitalkin.models.services.registry import RegistryModuleStatus
9
+
10
+
11
+ class ModuleStatusInfo(BaseModel):
12
+ """Module status response."""
13
+
14
+ module_id: str
15
+ status: RegistryModuleStatus
@@ -1,14 +1,98 @@
1
- """This module contains the abstract base class for registry strategies."""
1
+ """Abstract base class for registry strategies."""
2
2
 
3
3
  from abc import ABC, abstractmethod
4
+ from typing import Any
4
5
 
6
+ from digitalkin.models.services.registry import (
7
+ ModuleInfo,
8
+ RegistryModuleStatus,
9
+ )
5
10
  from digitalkin.services.base_strategy import BaseStrategy
11
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
6
12
 
7
13
 
8
14
  class RegistryStrategy(BaseStrategy, ABC):
9
- """Abstract base class for registry strategies."""
15
+ """Abstract base class for registry strategies.
16
+
17
+ Defines the interface for registry operations including module discovery,
18
+ registration, and status management.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ mission_id: str,
24
+ setup_id: str,
25
+ setup_version_id: str,
26
+ config: dict[str, Any] | None = None,
27
+ ) -> None:
28
+ """Initialize the strategy."""
29
+ super().__init__(mission_id, setup_id, setup_version_id)
30
+ self.config = config
31
+
32
+ @abstractmethod
33
+ def discover_by_id(self, module_id: str) -> ModuleInfo:
34
+ """Get module info by ID."""
35
+ raise NotImplementedError
36
+
37
+ @abstractmethod
38
+ def search(
39
+ self,
40
+ name: str | None = None,
41
+ module_type: str | None = None,
42
+ organization_id: str | None = None,
43
+ ) -> list[ModuleInfo]:
44
+ """Search for modules by criteria.
45
+
46
+ Args:
47
+ name: Filter by name (partial match via query).
48
+ module_type: Filter by type (archetype, tool).
49
+ organization_id: Filter by organization.
50
+
51
+ Returns:
52
+ List of matching modules.
53
+ """
54
+ raise NotImplementedError
55
+
56
+ @abstractmethod
57
+ def get_status(self, module_id: str) -> ModuleStatusInfo:
58
+ """Get module status."""
59
+ raise NotImplementedError
10
60
 
11
61
  @abstractmethod
12
- def get_by_id(self, module_id: str) -> None:
13
- """Get services from the registry."""
62
+ def register(
63
+ self,
64
+ module_id: str,
65
+ address: str,
66
+ port: int,
67
+ version: str,
68
+ ) -> ModuleInfo | None:
69
+ """Register a module with the registry.
70
+
71
+ Note: The new proto only updates address/port/version for an existing module.
72
+ The module must already exist in the registry database.
73
+
74
+ Args:
75
+ module_id: Unique module identifier.
76
+ address: Network address.
77
+ port: Network port.
78
+ version: Module version.
79
+
80
+ Returns:
81
+ ModuleInfo if successful, None otherwise.
82
+ """
83
+ raise NotImplementedError
84
+
85
+ @abstractmethod
86
+ def heartbeat(self, module_id: str) -> RegistryModuleStatus:
87
+ """Send heartbeat to keep module active.
88
+
89
+ Args:
90
+ module_id: The module identifier.
91
+
92
+ Returns:
93
+ Current module status after heartbeat.
94
+
95
+ Raises:
96
+ RegistryModuleNotFoundError: If module not found.
97
+ """
14
98
  raise NotImplementedError
@@ -5,13 +5,15 @@ from typing import Any, ClassVar
5
5
  from pydantic import BaseModel, Field, PrivateAttr
6
6
 
7
7
  from digitalkin.services.agent import AgentStrategy, DefaultAgent
8
+ from digitalkin.services.communication import CommunicationStrategy, DefaultCommunication, GrpcCommunication
8
9
  from digitalkin.services.cost import CostStrategy, DefaultCost, GrpcCost
9
10
  from digitalkin.services.filesystem import DefaultFilesystem, FilesystemStrategy, GrpcFilesystem
10
11
  from digitalkin.services.identity import DefaultIdentity, IdentityStrategy
11
- from digitalkin.services.registry import DefaultRegistry, RegistryStrategy
12
+ from digitalkin.services.registry import DefaultRegistry, GrpcRegistry, RegistryStrategy
12
13
  from digitalkin.services.services_models import ServicesMode, ServicesStrategy
13
14
  from digitalkin.services.snapshot import DefaultSnapshot, SnapshotStrategy
14
15
  from digitalkin.services.storage import DefaultStorage, GrpcStorage, StorageStrategy
16
+ from digitalkin.services.user_profile import DefaultUserProfile, GrpcUserProfile, UserProfileStrategy
15
17
 
16
18
 
17
19
  class ServicesConfig(BaseModel):
@@ -38,9 +40,9 @@ class ServicesConfig(BaseModel):
38
40
  )
39
41
  _config_snapshot: dict[str, Any | None] = PrivateAttr(default_factory=dict)
40
42
  _registry: ServicesStrategy[RegistryStrategy] = PrivateAttr(
41
- default_factory=lambda: ServicesStrategy(local=DefaultRegistry, remote=DefaultRegistry)
43
+ default_factory=lambda: ServicesStrategy(local=DefaultRegistry, remote=GrpcRegistry)
42
44
  )
43
- _config__registry: dict[str, Any | None] = PrivateAttr(default_factory=dict)
45
+ _config_registry: dict[str, Any | None] = PrivateAttr(default_factory=dict)
44
46
  _filesystem: ServicesStrategy[FilesystemStrategy] = PrivateAttr(
45
47
  default_factory=lambda: ServicesStrategy(local=DefaultFilesystem, remote=GrpcFilesystem)
46
48
  )
@@ -53,6 +55,14 @@ class ServicesConfig(BaseModel):
53
55
  default_factory=lambda: ServicesStrategy(local=DefaultIdentity, remote=DefaultIdentity)
54
56
  )
55
57
  _config_identity: dict[str, Any | None] = PrivateAttr(default_factory=dict)
58
+ _communication: ServicesStrategy[CommunicationStrategy] = PrivateAttr(
59
+ default_factory=lambda: ServicesStrategy(local=DefaultCommunication, remote=GrpcCommunication)
60
+ )
61
+ _config_communication: dict[str, Any | None] = PrivateAttr(default_factory=dict)
62
+ _user_profile: ServicesStrategy[UserProfileStrategy] = PrivateAttr(
63
+ default_factory=lambda: ServicesStrategy(local=DefaultUserProfile, remote=GrpcUserProfile)
64
+ )
65
+ _config_user_profile: dict[str, Any | None] = PrivateAttr(default_factory=dict)
56
66
 
57
67
  # List of valid strategy names for validation
58
68
  _valid_strategy_names: ClassVar[set[str]] = {
@@ -63,6 +73,8 @@ class ServicesConfig(BaseModel):
63
73
  "filesystem",
64
74
  "agent",
65
75
  "identity",
76
+ "communication",
77
+ "user_profile",
66
78
  }
67
79
 
68
80
  def __init__(
@@ -169,6 +181,16 @@ class ServicesConfig(BaseModel):
169
181
  """Get the identity service strategy class based on the current mode."""
170
182
  return self._identity[self.mode.value]
171
183
 
184
+ @property
185
+ def communication(self) -> type[CommunicationStrategy]:
186
+ """Get the communication service strategy class based on the current mode."""
187
+ return self._communication[self.mode.value]
188
+
189
+ @property
190
+ def user_profile(self) -> type[UserProfileStrategy]:
191
+ """Get the user_profile service strategy class based on the current mode."""
192
+ return self._user_profile[self.mode.value]
193
+
172
194
  def update_mode(self, mode: ServicesMode) -> None:
173
195
  """Update the strategy mode.
174
196
 
@@ -7,23 +7,27 @@ from pydantic import BaseModel
7
7
 
8
8
  from digitalkin.logger import logger
9
9
  from digitalkin.services.agent import AgentStrategy
10
+ from digitalkin.services.communication import CommunicationStrategy
10
11
  from digitalkin.services.cost import CostStrategy
11
12
  from digitalkin.services.filesystem import FilesystemStrategy
12
13
  from digitalkin.services.identity import IdentityStrategy
13
14
  from digitalkin.services.registry import RegistryStrategy
14
15
  from digitalkin.services.snapshot import SnapshotStrategy
15
16
  from digitalkin.services.storage import StorageStrategy
17
+ from digitalkin.services.user_profile import UserProfileStrategy
16
18
 
17
19
  # Define type variables
18
20
  T = TypeVar(
19
21
  "T",
20
22
  bound=AgentStrategy
23
+ | CommunicationStrategy
21
24
  | CostStrategy
22
25
  | FilesystemStrategy
23
26
  | IdentityStrategy
24
27
  | RegistryStrategy
25
28
  | SnapshotStrategy
26
- | StorageStrategy,
29
+ | StorageStrategy
30
+ | UserProfileStrategy,
27
31
  )
28
32
 
29
33
 
@@ -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.grpc_setup import SetupData, SetupVersionData
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 succesfull", setup_id, valid_data)
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 succesfull", setup_version_dict["setup_id"], valid_data)
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,8 +172,8 @@ class DefaultSetup(SetupStrategy):
173
172
 
174
173
  return [
175
174
  value
176
- for value in setup_version_dict["setup_id"].values()
177
- if setup_version_dict["query_versions"] in value.version or setup_version_dict["name"] in value.name
175
+ for value in self.setup_versions[setup_version_dict["setup_id"]].values()
176
+ if setup_version_dict["query_versions"] in value.version
178
177
  ]
179
178
 
180
179
  def update_setup_version(self, setup_version_dict: dict[str, Any]) -> bool:
@@ -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