digitalkin 0.1.1__py3-none-any.whl → 0.2.0__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/__init__.py +1 -0
- base_server/mock/__init__.py +5 -0
- base_server/mock/mock_pb2.py +39 -0
- base_server/mock/mock_pb2_grpc.py +102 -0
- base_server/server_async_insecure.py +124 -0
- base_server/server_async_secure.py +142 -0
- base_server/server_sync_insecure.py +102 -0
- base_server/server_sync_secure.py +121 -0
- digitalkin/__init__.py +1 -11
- digitalkin/__version__.py +1 -4
- digitalkin/{grpc → grpc_servers}/__init__.py +1 -13
- digitalkin/{grpc → grpc_servers}/_base_server.py +3 -3
- digitalkin/{grpc → grpc_servers}/module_server.py +30 -12
- digitalkin/{grpc → grpc_servers}/module_servicer.py +30 -14
- digitalkin/{grpc → grpc_servers}/registry_server.py +6 -4
- digitalkin/{grpc → grpc_servers}/registry_servicer.py +8 -2
- digitalkin/{grpc → grpc_servers}/utils/factory.py +6 -4
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +68 -0
- digitalkin/{grpc → grpc_servers}/utils/models.py +1 -1
- digitalkin/models/__init__.py +1 -4
- digitalkin/models/module/__init__.py +8 -2
- digitalkin/models/module/module_types.py +10 -0
- digitalkin/models/services/__init__.py +0 -5
- digitalkin/modules/__init__.py +3 -3
- digitalkin/modules/_base_module.py +64 -27
- digitalkin/modules/archetype_module.py +2 -6
- digitalkin/modules/job_manager.py +46 -28
- digitalkin/modules/tool_module.py +3 -7
- digitalkin/modules/trigger_module.py +2 -7
- digitalkin/services/__init__.py +7 -9
- digitalkin/services/agent/__init__.py +2 -2
- digitalkin/services/agent/agent_strategy.py +3 -6
- digitalkin/services/agent/default_agent.py +1 -4
- digitalkin/services/base_strategy.py +18 -0
- digitalkin/services/cost/__init__.py +4 -3
- digitalkin/services/cost/cost_strategy.py +35 -5
- digitalkin/services/cost/default_cost.py +22 -5
- digitalkin/services/cost/grpc_cost.py +81 -0
- digitalkin/services/filesystem/__init__.py +4 -3
- digitalkin/services/filesystem/default_filesystem.py +197 -17
- digitalkin/services/filesystem/filesystem_strategy.py +54 -15
- digitalkin/services/filesystem/grpc_filesystem.py +209 -0
- digitalkin/services/identity/__init__.py +2 -2
- digitalkin/services/identity/default_identity.py +1 -1
- digitalkin/services/identity/identity_strategy.py +3 -1
- digitalkin/services/registry/__init__.py +2 -2
- digitalkin/services/registry/default_registry.py +1 -4
- digitalkin/services/registry/registry_strategy.py +3 -6
- digitalkin/services/services_config.py +176 -0
- digitalkin/services/services_models.py +61 -0
- digitalkin/services/setup/default_setup.py +222 -0
- digitalkin/services/setup/grpc_setup.py +307 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +2 -2
- digitalkin/services/snapshot/default_snapshot.py +1 -1
- digitalkin/services/snapshot/snapshot_strategy.py +3 -4
- digitalkin/services/storage/__init__.py +4 -3
- digitalkin/services/storage/default_storage.py +184 -57
- digitalkin/services/storage/grpc_storage.py +76 -170
- digitalkin/services/storage/storage_strategy.py +195 -24
- digitalkin/utils/arg_parser.py +16 -17
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/METADATA +8 -7
- digitalkin-0.2.0.dist-info/RECORD +78 -0
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/WHEEL +1 -1
- digitalkin-0.2.0.dist-info/top_level.txt +3 -0
- modules/__init__.py +0 -0
- modules/minimal_llm_module.py +162 -0
- modules/storage_module.py +187 -0
- modules/text_transform_module.py +201 -0
- digitalkin/services/default_service.py +0 -13
- digitalkin/services/development_service.py +0 -10
- digitalkin/services/service_provider.py +0 -27
- digitalkin-0.1.1.dist-info/RECORD +0 -59
- digitalkin-0.1.1.dist-info/top_level.txt +0 -1
- /digitalkin/{grpc → grpc_servers}/utils/exceptions.py +0 -0
- /digitalkin/{grpc → grpc_servers}/utils/types.py +0 -0
- {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,15 +11,15 @@ from typing import Any, cast
|
|
|
11
11
|
import grpc
|
|
12
12
|
from grpc import aio as grpc_aio
|
|
13
13
|
|
|
14
|
-
from digitalkin.
|
|
14
|
+
from digitalkin.grpc_servers.utils.exceptions import (
|
|
15
15
|
ConfigurationError,
|
|
16
16
|
ReflectionError,
|
|
17
17
|
SecurityError,
|
|
18
18
|
ServerStateError,
|
|
19
19
|
ServicerError,
|
|
20
20
|
)
|
|
21
|
-
from digitalkin.
|
|
22
|
-
from digitalkin.
|
|
21
|
+
from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
|
|
22
|
+
from digitalkin.grpc_servers.utils.types import GrpcServer, ServiceDescriptor, T
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
@@ -5,13 +5,16 @@ import uuid
|
|
|
5
5
|
|
|
6
6
|
import grpc
|
|
7
7
|
|
|
8
|
-
from digitalkin.
|
|
9
|
-
from digitalkin.
|
|
10
|
-
from digitalkin.
|
|
11
|
-
from digitalkin.
|
|
8
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
9
|
+
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 ModuleServerConfig, SecurityMode
|
|
12
12
|
from digitalkin.modules._base_module import BaseModule
|
|
13
13
|
|
|
14
|
-
from digitalkin_proto.digitalkin.module.v2 import
|
|
14
|
+
from digitalkin_proto.digitalkin.module.v2 import (
|
|
15
|
+
module_service_pb2,
|
|
16
|
+
module_service_pb2_grpc,
|
|
17
|
+
)
|
|
15
18
|
from digitalkin_proto.digitalkin.module_registry.v2 import (
|
|
16
19
|
metadata_pb2,
|
|
17
20
|
module_registry_service_pb2_grpc,
|
|
@@ -111,7 +114,9 @@ class ModuleServer(BaseServer):
|
|
|
111
114
|
Raises:
|
|
112
115
|
ServerError: If communication with the registry server fails.
|
|
113
116
|
"""
|
|
114
|
-
logger.info(
|
|
117
|
+
logger.info(
|
|
118
|
+
"Registering module with registry at %s", self.config.registry_address
|
|
119
|
+
)
|
|
115
120
|
|
|
116
121
|
# Create appropriate channel based on security mode
|
|
117
122
|
channel = self._create_registry_channel()
|
|
@@ -125,11 +130,16 @@ class ModuleServer(BaseServer):
|
|
|
125
130
|
|
|
126
131
|
metadata = metadata_pb2.Metadata(
|
|
127
132
|
name=self.module_class.metadata["name"],
|
|
128
|
-
tags=[
|
|
133
|
+
tags=[
|
|
134
|
+
metadata_pb2.Tag(tag=tag)
|
|
135
|
+
for tag in self.module_class.metadata["tags"]
|
|
136
|
+
],
|
|
129
137
|
description=self.module_class.metadata["description"],
|
|
130
138
|
)
|
|
131
139
|
|
|
132
|
-
self.module_class.metadata["module_id"] =
|
|
140
|
+
self.module_class.metadata["module_id"] = (
|
|
141
|
+
f"{self.module_class.metadata['name']}:{uuid.uuid4()}"
|
|
142
|
+
)
|
|
133
143
|
# Create registration request
|
|
134
144
|
request = registration_pb2.RegisterRequest(
|
|
135
145
|
module_id=self.module_class.metadata["module_id"],
|
|
@@ -162,7 +172,9 @@ class ModuleServer(BaseServer):
|
|
|
162
172
|
Raises:
|
|
163
173
|
ServerError: If communication with the registry server fails.
|
|
164
174
|
"""
|
|
165
|
-
logger.info(
|
|
175
|
+
logger.info(
|
|
176
|
+
"Deregistering module from registry at %s", self.config.registry_address
|
|
177
|
+
)
|
|
166
178
|
|
|
167
179
|
# Create appropriate channel based on security mode
|
|
168
180
|
channel = self._create_registry_channel()
|
|
@@ -204,13 +216,19 @@ class ModuleServer(BaseServer):
|
|
|
204
216
|
|
|
205
217
|
root_certificates = None
|
|
206
218
|
if self.config.credentials.root_cert_path:
|
|
207
|
-
with open(
|
|
219
|
+
with open(
|
|
220
|
+
self.config.credentials.root_cert_path, "rb"
|
|
221
|
+
) as root_cert_file: # noqa: FURB101
|
|
208
222
|
root_certificates = root_cert_file.read()
|
|
209
223
|
|
|
210
224
|
# Create channel credentials
|
|
211
|
-
channel_credentials = grpc.ssl_channel_credentials(
|
|
225
|
+
channel_credentials = grpc.ssl_channel_credentials(
|
|
226
|
+
root_certificates=root_certificates or certificate_chain
|
|
227
|
+
)
|
|
212
228
|
|
|
213
|
-
return grpc.secure_channel(
|
|
229
|
+
return grpc.secure_channel(
|
|
230
|
+
self.config.registry_address, channel_credentials
|
|
231
|
+
)
|
|
214
232
|
# Insecure channel
|
|
215
233
|
return grpc.insecure_channel(self.config.registry_address)
|
|
216
234
|
|
|
@@ -14,9 +14,15 @@ from digitalkin_proto.digitalkin.module.v2 import (
|
|
|
14
14
|
)
|
|
15
15
|
from google.protobuf import json_format, struct_pb2
|
|
16
16
|
|
|
17
|
+
from digitalkin.grpc_servers.utils.exceptions import ServicerError
|
|
18
|
+
from digitalkin.models.module import OutputModelT
|
|
17
19
|
from digitalkin.models.module.module import ModuleStatus
|
|
18
20
|
from digitalkin.modules._base_module import BaseModule
|
|
19
21
|
from digitalkin.modules.job_manager import JobManager
|
|
22
|
+
from digitalkin.services.services_models import ServicesMode
|
|
23
|
+
from digitalkin.services.setup.default_setup import DefaultSetup
|
|
24
|
+
from digitalkin.services.setup.grpc_setup import GrpcSetup
|
|
25
|
+
from digitalkin.services.setup.setup_strategy import SetupStrategy
|
|
20
26
|
|
|
21
27
|
logger = logging.getLogger(__name__)
|
|
22
28
|
|
|
@@ -31,6 +37,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
|
|
|
31
37
|
active_jobs: Dictionary tracking active module jobs.
|
|
32
38
|
"""
|
|
33
39
|
|
|
40
|
+
setup: SetupStrategy
|
|
41
|
+
|
|
34
42
|
def __init__(self, module_class: type[BaseModule]) -> None:
|
|
35
43
|
"""Initialize the module servicer.
|
|
36
44
|
|
|
@@ -41,8 +49,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
|
|
|
41
49
|
self.queue: asyncio.Queue = asyncio.Queue()
|
|
42
50
|
self.module_class = module_class
|
|
43
51
|
self.job_manager = JobManager(module_class)
|
|
52
|
+
self.setup = GrpcSetup() if self.job_manager.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
|
|
44
53
|
|
|
45
|
-
async def add_to_queue(self, job_id: str, output_data:
|
|
54
|
+
async def add_to_queue(self, job_id: str, output_data: OutputModelT) -> None:
|
|
46
55
|
"""Callback used to add the output data to the queue of messages."""
|
|
47
56
|
logger.info("JOB: %s added an output_data: %s", job_id, output_data)
|
|
48
57
|
await self.queue.put({job_id: output_data})
|
|
@@ -60,19 +69,26 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
|
|
|
60
69
|
|
|
61
70
|
Yields:
|
|
62
71
|
Responses during module execution.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
ServicerError: the necessary query didn't work.
|
|
63
75
|
"""
|
|
64
76
|
logger.info("StartModule called for module: '%s'", self.module_class.__name__)
|
|
65
77
|
# Process the module input
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
# TODO: Check failure of input data format
|
|
79
|
+
input_data = self.module_class.input_format(**dict(request.input.items()))
|
|
80
|
+
setup_data_class = self.setup.get_setup(
|
|
81
|
+
setup_dict={
|
|
82
|
+
"setup_id": request.setup_id,
|
|
83
|
+
"mission_id": "missions:test_demo",
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if not setup_data_class:
|
|
88
|
+
msg = "No setup data returned."
|
|
89
|
+
raise ServicerError(msg)
|
|
90
|
+
# TODO: Check failure of setup data format
|
|
91
|
+
setup_data = self.module_class.setup_format(**setup_data_class.current_setup_version.content)
|
|
76
92
|
|
|
77
93
|
# setup_id should be use to request a precise setup from the module
|
|
78
94
|
# Create a job for this execution
|
|
@@ -84,9 +100,9 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
|
|
|
84
100
|
|
|
85
101
|
while module.status == ModuleStatus.RUNNING or not self.queue.empty():
|
|
86
102
|
output_data = await self.queue.get()
|
|
87
|
-
if output_data.get("error", None) is not None:
|
|
88
|
-
context.set_code(output_data["error"]["code"])
|
|
89
|
-
context.set_details(output_data["error"]["error_message"])
|
|
103
|
+
if output_data[job_id].get("error", None) is not None:
|
|
104
|
+
context.set_code(output_data[job_id]["error"]["code"])
|
|
105
|
+
context.set_details(output_data[job_id]["error"]["error_message"])
|
|
90
106
|
yield lifecycle_pb2.StartModuleResponse(success=False)
|
|
91
107
|
return
|
|
92
108
|
else:
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
-
from digitalkin_proto.digitalkin.module_registry.v2 import
|
|
5
|
+
from digitalkin_proto.digitalkin.module_registry.v2 import (
|
|
6
|
+
module_registry_service_pb2_grpc,
|
|
7
|
+
)
|
|
6
8
|
|
|
7
|
-
from digitalkin.
|
|
8
|
-
from digitalkin.
|
|
9
|
-
from digitalkin.
|
|
9
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
10
|
+
from digitalkin.grpc_servers.registry_servicer import RegistryModule, RegistryServicer
|
|
11
|
+
from digitalkin.grpc_servers.utils.models import RegistryServerConfig
|
|
10
12
|
|
|
11
13
|
logger = logging.getLogger(__name__)
|
|
12
14
|
|
|
@@ -88,7 +88,9 @@ class Metadata(BaseModel):
|
|
|
88
88
|
metadata_pb2.Metadata: The protobuf representation of this metadata.
|
|
89
89
|
"""
|
|
90
90
|
return metadata_pb2.Metadata(
|
|
91
|
-
name=self.name,
|
|
91
|
+
name=self.name,
|
|
92
|
+
tags=(t.to_proto() for t in self.tags),
|
|
93
|
+
description=self.description,
|
|
92
94
|
)
|
|
93
95
|
|
|
94
96
|
@classmethod
|
|
@@ -361,7 +363,11 @@ class RegistryServicer(module_registry_service_pb2_grpc.ModuleRegistryServiceSer
|
|
|
361
363
|
Returns:
|
|
362
364
|
status_pb2.ListModulesStatusResponse: A response containing a list of module statuses.
|
|
363
365
|
"""
|
|
364
|
-
logger.info(
|
|
366
|
+
logger.info(
|
|
367
|
+
"Getting registered modules with offset %d and limit %d",
|
|
368
|
+
request.offset,
|
|
369
|
+
request.list_size,
|
|
370
|
+
)
|
|
365
371
|
if request.offset > len(self.registered_modules):
|
|
366
372
|
message = f"Out of range {request.offset}"
|
|
367
373
|
logger.warning(message)
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
from digitalkin.
|
|
7
|
-
from digitalkin.
|
|
8
|
-
from digitalkin.
|
|
6
|
+
from digitalkin.grpc_servers.module_server import ModuleServer
|
|
7
|
+
from digitalkin.grpc_servers.registry_server import RegistryServer
|
|
8
|
+
from digitalkin.grpc_servers.utils.models import (
|
|
9
9
|
ModuleServerConfig,
|
|
10
10
|
RegistryServerConfig,
|
|
11
11
|
SecurityMode,
|
|
@@ -167,7 +167,9 @@ def _create_server_config(
|
|
|
167
167
|
# Add credentials if secure mode
|
|
168
168
|
if security == "secure":
|
|
169
169
|
if not server_key_path or not server_cert_path:
|
|
170
|
-
raise ValueError(
|
|
170
|
+
raise ValueError(
|
|
171
|
+
"Server key and certificate paths are required for secure mode"
|
|
172
|
+
)
|
|
171
173
|
|
|
172
174
|
config_params["credentials"] = ServerCredentials(
|
|
173
175
|
server_key_path=Path(server_key_path),
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Client wrapper to ease channel creation with specific ServerConfig."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import grpc
|
|
7
|
+
|
|
8
|
+
from digitalkin.grpc_servers.utils.exceptions import ServerError
|
|
9
|
+
from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GrpcClientWrapper:
|
|
15
|
+
"""gRPC client shared by the different services."""
|
|
16
|
+
|
|
17
|
+
stub: Any
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _init_channel(config: ServerConfig) -> grpc.Channel:
|
|
21
|
+
"""Create an appropriate channel to the registry server.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A gRPC channel for communication with the registry.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If credentials are required but not provided.
|
|
28
|
+
"""
|
|
29
|
+
if config.security == SecurityMode.SECURE and config.credentials:
|
|
30
|
+
# Secure channel
|
|
31
|
+
with open(config.credentials.server_cert_path, "rb") as cert_file: # noqa: FURB101
|
|
32
|
+
certificate_chain = cert_file.read()
|
|
33
|
+
|
|
34
|
+
root_certificates = None
|
|
35
|
+
if config.credentials.root_cert_path:
|
|
36
|
+
with open(config.credentials.root_cert_path, "rb") as root_cert_file: # noqa: FURB101
|
|
37
|
+
root_certificates = root_cert_file.read()
|
|
38
|
+
|
|
39
|
+
# Create channel credentials
|
|
40
|
+
channel_credentials = grpc.ssl_channel_credentials(root_certificates=root_certificates or certificate_chain)
|
|
41
|
+
|
|
42
|
+
return grpc.secure_channel(f"{config.host}:{config.port}", channel_credentials)
|
|
43
|
+
# Insecure channel
|
|
44
|
+
return grpc.insecure_channel(f"{config.host}:{config.port}")
|
|
45
|
+
|
|
46
|
+
def exec_grpc_query(self, query_endpoint: str, request: Any) -> Any: # noqa: ANN401
|
|
47
|
+
"""Execute a gRPC query with from the query's rpc endpoint name.
|
|
48
|
+
|
|
49
|
+
Arguments:
|
|
50
|
+
query_endpoint: rpc query name
|
|
51
|
+
request: gRPC object to match the rpc query
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
corresponding gRPC reponse.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
ServerError: gRPC error catching
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
# Call the register method
|
|
61
|
+
logger.warning("send request to %s", query_endpoint)
|
|
62
|
+
response = getattr(self.stub, query_endpoint)(request)
|
|
63
|
+
logger.warning("recive response from request to registry: %s", response)
|
|
64
|
+
except grpc.RpcError:
|
|
65
|
+
logger.exception("RPC error during registration:")
|
|
66
|
+
raise ServerError
|
|
67
|
+
else:
|
|
68
|
+
return response
|
|
@@ -6,7 +6,7 @@ from typing import Any
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, Field, ValidationInfo, field_validator
|
|
8
8
|
|
|
9
|
-
from digitalkin.
|
|
9
|
+
from digitalkin.grpc_servers.utils.exceptions import ConfigurationError, SecurityError
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class ServerMode(str, Enum):
|
digitalkin/models/__init__.py
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
"""This package contains the models for DigitalKin."""
|
|
2
2
|
|
|
3
|
-
from .module import Module, ModuleStatus
|
|
4
|
-
from .services import CostEvent, StorageModel
|
|
3
|
+
from digitalkin.models.module import Module, ModuleStatus
|
|
5
4
|
|
|
6
5
|
__all__ = [
|
|
7
|
-
"CostEvent",
|
|
8
6
|
"Module",
|
|
9
7
|
"ModuleStatus",
|
|
10
|
-
"StorageModel",
|
|
11
8
|
]
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"""This module contains the models for the modules."""
|
|
2
2
|
|
|
3
|
-
from .module import Module, ModuleStatus
|
|
3
|
+
from digitalkin.models.module.module import Module, ModuleStatus
|
|
4
|
+
from digitalkin.models.module.module_types import (
|
|
5
|
+
InputModelT,
|
|
6
|
+
OutputModelT,
|
|
7
|
+
SecretModelT,
|
|
8
|
+
SetupModelT,
|
|
9
|
+
)
|
|
4
10
|
|
|
5
|
-
__all__ = ["Module", "ModuleStatus"]
|
|
11
|
+
__all__ = ["InputModelT", "Module", "ModuleStatus", "OutputModelT", "SecretModelT", "SetupModelT"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Types for module models."""
|
|
2
|
+
|
|
3
|
+
from typing import TypeVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
InputModelT = TypeVar("InputModelT", bound=BaseModel)
|
|
8
|
+
OutputModelT = TypeVar("OutputModelT", bound=BaseModel)
|
|
9
|
+
SetupModelT = TypeVar("SetupModelT", bound=BaseModel)
|
|
10
|
+
SecretModelT = TypeVar("SecretModelT", bound=BaseModel)
|
digitalkin/modules/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Module package for DigitalKin."""
|
|
2
2
|
|
|
3
|
-
from .archetype_module import ArchetypeModule
|
|
4
|
-
from .tool_module import ToolModule
|
|
5
|
-
from .trigger_module import TriggerModule
|
|
3
|
+
from digitalkin.modules.archetype_module import ArchetypeModule
|
|
4
|
+
from digitalkin.modules.tool_module import ToolModule
|
|
5
|
+
from digitalkin.modules.trigger_module import TriggerModule
|
|
6
6
|
|
|
7
7
|
__all__ = ["ArchetypeModule", "ToolModule", "TriggerModule"]
|
|
@@ -4,44 +4,64 @@ import asyncio
|
|
|
4
4
|
import contextlib
|
|
5
5
|
import json
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from collections.abc import Callable
|
|
8
|
-
from typing import Any, ClassVar, Generic
|
|
9
|
-
|
|
10
|
-
from pydantic import BaseModel
|
|
7
|
+
from collections.abc import Callable, Coroutine
|
|
8
|
+
from typing import Any, ClassVar, Generic
|
|
11
9
|
|
|
12
10
|
from digitalkin.logger import logger
|
|
13
|
-
from digitalkin.models.module import ModuleStatus
|
|
14
|
-
from digitalkin.services.
|
|
11
|
+
from digitalkin.models.module import InputModelT, ModuleStatus, OutputModelT, SecretModelT, SetupModelT
|
|
12
|
+
from digitalkin.services.agent.agent_strategy import AgentStrategy
|
|
13
|
+
from digitalkin.services.cost.cost_strategy import CostStrategy
|
|
14
|
+
from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
|
|
15
|
+
from digitalkin.services.identity.identity_strategy import IdentityStrategy
|
|
16
|
+
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
17
|
+
from digitalkin.services.services_config import ServicesConfig, ServicesStrategy
|
|
18
|
+
from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
|
|
15
19
|
from digitalkin.services.storage.storage_strategy import StorageStrategy
|
|
16
20
|
|
|
17
|
-
InputModelT = TypeVar("InputModelT", bound=BaseModel)
|
|
18
|
-
OutputModelT = TypeVar("OutputModelT", bound=BaseModel)
|
|
19
|
-
SetupModelT = TypeVar("SetupModelT", bound=BaseModel)
|
|
20
|
-
|
|
21
21
|
|
|
22
|
-
class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
22
|
+
class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretModelT]):
|
|
23
23
|
"""BaseModule is the abstract base for all modules in the DigitalKin SDK."""
|
|
24
24
|
|
|
25
|
+
name: str
|
|
26
|
+
description: str
|
|
25
27
|
input_format: type[InputModelT]
|
|
26
28
|
output_format: type[OutputModelT]
|
|
27
29
|
setup_format: type[SetupModelT]
|
|
30
|
+
secret_format: type[SecretModelT]
|
|
28
31
|
metadata: ClassVar[dict[str, Any]]
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
# service config params
|
|
34
|
+
services_config_strategies: ClassVar[dict[str, ServicesStrategy | None]]
|
|
35
|
+
services_config_params: ClassVar[dict[str, dict[str, Any | None] | None]]
|
|
36
|
+
services_config: ServicesConfig
|
|
37
|
+
|
|
38
|
+
# services list
|
|
39
|
+
agent: AgentStrategy
|
|
40
|
+
cost: CostStrategy
|
|
41
|
+
filesystem: FilesystemStrategy
|
|
42
|
+
identity: IdentityStrategy
|
|
43
|
+
registry: RegistryStrategy
|
|
44
|
+
snapshot: SnapshotStrategy
|
|
33
45
|
storage: StorageStrategy
|
|
34
46
|
|
|
47
|
+
def _init_strategies(self) -> None:
|
|
48
|
+
"""Initialize the services configuration."""
|
|
49
|
+
for service_name in self.services_config.valid_strategy_names():
|
|
50
|
+
service = self.services_config.init_strategy(service_name, self.mission_id)
|
|
51
|
+
setattr(self, service_name, service)
|
|
52
|
+
|
|
35
53
|
def __init__(
|
|
36
54
|
self,
|
|
37
55
|
job_id: str,
|
|
38
|
-
|
|
56
|
+
mission_id: str,
|
|
39
57
|
) -> None:
|
|
40
58
|
"""Initialize the module."""
|
|
41
59
|
self.job_id: str = job_id
|
|
42
|
-
self.
|
|
60
|
+
self.mission_id: str = mission_id
|
|
43
61
|
self._status = ModuleStatus.CREATED
|
|
44
62
|
self._task: asyncio.Task | None = None
|
|
63
|
+
# Initialize services configuration
|
|
64
|
+
self._init_strategies()
|
|
45
65
|
|
|
46
66
|
@property
|
|
47
67
|
def status(self) -> ModuleStatus:
|
|
@@ -52,6 +72,23 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
|
52
72
|
"""
|
|
53
73
|
return self._status
|
|
54
74
|
|
|
75
|
+
@classmethod
|
|
76
|
+
def get_secret_format(cls, llm_format: bool) -> str: # noqa: FBT001
|
|
77
|
+
"""Get the JSON schema of the secret format model.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
NotImplementedError: If the `secret_format` is not defined.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The JSON schema of the secret format as a string.
|
|
84
|
+
"""
|
|
85
|
+
if cls.secret_format is not None:
|
|
86
|
+
if llm_format:
|
|
87
|
+
return json.dumps(cls.secret_format, indent=2)
|
|
88
|
+
return json.dumps(cls.secret_format.model_json_schema(), indent=2)
|
|
89
|
+
msg = f"{cls.__name__}' class does not define a 'secret_format'."
|
|
90
|
+
raise NotImplementedError(msg)
|
|
91
|
+
|
|
55
92
|
@classmethod
|
|
56
93
|
def get_input_format(cls, llm_format: bool) -> str: # noqa: FBT001
|
|
57
94
|
"""Get the JSON schema of the input format model.
|
|
@@ -62,7 +99,7 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
|
62
99
|
Returns:
|
|
63
100
|
The JSON schema of the input format as a string.
|
|
64
101
|
"""
|
|
65
|
-
if cls.
|
|
102
|
+
if cls.input_format is not None:
|
|
66
103
|
if llm_format:
|
|
67
104
|
return json.dumps(cls.input_format, indent=2)
|
|
68
105
|
return json.dumps(cls.input_format.model_json_schema(), indent=2)
|
|
@@ -104,16 +141,16 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
|
104
141
|
raise NotImplementedError(msg)
|
|
105
142
|
|
|
106
143
|
@abstractmethod
|
|
107
|
-
async def initialize(self, setup_data:
|
|
144
|
+
async def initialize(self, setup_data: SetupModelT) -> None:
|
|
108
145
|
"""Initialize the module."""
|
|
109
146
|
raise NotImplementedError
|
|
110
147
|
|
|
111
148
|
@abstractmethod
|
|
112
149
|
async def run(
|
|
113
150
|
self,
|
|
114
|
-
input_data:
|
|
115
|
-
setup_data:
|
|
116
|
-
callback: Callable,
|
|
151
|
+
input_data: InputModelT,
|
|
152
|
+
setup_data: SetupModelT,
|
|
153
|
+
callback: Callable[[OutputModelT], Coroutine[Any, Any, None]],
|
|
117
154
|
) -> None:
|
|
118
155
|
"""Run the module."""
|
|
119
156
|
raise NotImplementedError
|
|
@@ -125,9 +162,9 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
|
125
162
|
|
|
126
163
|
async def _run_lifecycle(
|
|
127
164
|
self,
|
|
128
|
-
input_data:
|
|
129
|
-
setup_data:
|
|
130
|
-
callback: Callable,
|
|
165
|
+
input_data: InputModelT,
|
|
166
|
+
setup_data: SetupModelT,
|
|
167
|
+
callback: Callable[[OutputModelT], Coroutine[Any, Any, None]],
|
|
131
168
|
) -> None:
|
|
132
169
|
"""Run the module lifecycle.
|
|
133
170
|
|
|
@@ -147,9 +184,9 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT]):
|
|
|
147
184
|
|
|
148
185
|
async def start(
|
|
149
186
|
self,
|
|
150
|
-
input_data:
|
|
151
|
-
setup_data:
|
|
152
|
-
callback: Callable,
|
|
187
|
+
input_data: InputModelT,
|
|
188
|
+
setup_data: SetupModelT,
|
|
189
|
+
callback: Callable[[OutputModelT], Coroutine[Any, Any, None]],
|
|
153
190
|
) -> None:
|
|
154
191
|
"""Start the module."""
|
|
155
192
|
try:
|
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
|
|
5
|
+
from digitalkin.models.module import InputModelT, OutputModelT, SecretModelT, SetupModelT
|
|
5
6
|
from digitalkin.modules._base_module import BaseModule
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class ArchetypeModule(BaseModule, ABC):
|
|
9
|
+
class ArchetypeModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
|
|
9
10
|
"""ArchetypeModule extends BaseModule to implement specific module types."""
|
|
10
|
-
|
|
11
|
-
def __init__(self, name: str | None = None) -> None:
|
|
12
|
-
"""Initialize the module with the given metadata."""
|
|
13
|
-
super().__init__(self.job_id, name=name)
|
|
14
|
-
self.capabilities = ["archetype"]
|