digitalkin 0.2.18__py3-none-any.whl → 0.2.20__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/grpc_servers/module_servicer.py +9 -7
- digitalkin/models/module/__init__.py +6 -6
- digitalkin/models/module/module_types.py +76 -14
- digitalkin/modules/_base_module.py +71 -46
- digitalkin/modules/archetype_module.py +0 -2
- digitalkin/modules/job_manager/base_job_manager.py +8 -7
- digitalkin/modules/job_manager/single_job_manager.py +19 -10
- digitalkin/modules/job_manager/taskiq_broker.py +6 -4
- digitalkin/modules/job_manager/taskiq_job_manager.py +9 -5
- digitalkin/modules/tool_module.py +1 -2
- digitalkin/services/base_strategy.py +3 -1
- digitalkin/services/cost/cost_strategy.py +9 -2
- digitalkin/services/cost/default_cost.py +3 -2
- digitalkin/services/cost/grpc_cost.py +2 -1
- digitalkin/services/filesystem/default_filesystem.py +9 -7
- digitalkin/services/filesystem/filesystem_strategy.py +10 -3
- digitalkin/services/filesystem/grpc_filesystem.py +10 -8
- digitalkin/services/services_config.py +3 -2
- digitalkin/services/storage/default_storage.py +2 -1
- digitalkin/services/storage/grpc_storage.py +6 -5
- digitalkin/services/storage/storage_strategy.py +9 -2
- digitalkin/utils/package_discover.py +2 -2
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/METADATA +13 -13
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/RECORD +28 -28
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/WHEEL +0 -0
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.2.18.dist-info → digitalkin-0.2.20.dist-info}/top_level.txt +0 -0
digitalkin/__version__.py
CHANGED
|
@@ -105,7 +105,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
105
105
|
setup_version = request.setup_version
|
|
106
106
|
config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
|
|
107
107
|
setup_version_data = self.module_class.create_setup_model(
|
|
108
|
-
json_format.MessageToDict(request.setup_version.content)
|
|
108
|
+
json_format.MessageToDict(request.setup_version.content),
|
|
109
|
+
config_fields=True,
|
|
109
110
|
)
|
|
110
111
|
|
|
111
112
|
if not setup_version_data:
|
|
@@ -119,8 +120,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
119
120
|
# create a task to run the module in background
|
|
120
121
|
job_id = await self.job_manager.create_config_setup_instance_job(
|
|
121
122
|
config_setup_data,
|
|
122
|
-
setup_version_data,
|
|
123
123
|
request.mission_id,
|
|
124
|
+
setup_version.setup_id,
|
|
124
125
|
setup_version.id,
|
|
125
126
|
)
|
|
126
127
|
|
|
@@ -177,6 +178,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
177
178
|
input_data,
|
|
178
179
|
setup_data,
|
|
179
180
|
mission_id=request.mission_id,
|
|
181
|
+
setup_id=setup_data_class.current_setup_version.setup_id,
|
|
180
182
|
setup_version_id=setup_data_class.current_setup_version.id,
|
|
181
183
|
)
|
|
182
184
|
|
|
@@ -333,7 +335,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
333
335
|
except NotImplementedError as e:
|
|
334
336
|
logger.warning(e)
|
|
335
337
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
336
|
-
context.set_details(e)
|
|
338
|
+
context.set_details(str(e))
|
|
337
339
|
return information_pb2.GetModuleInputResponse()
|
|
338
340
|
|
|
339
341
|
return information_pb2.GetModuleInputResponse(
|
|
@@ -369,7 +371,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
369
371
|
except NotImplementedError as e:
|
|
370
372
|
logger.warning(e)
|
|
371
373
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
372
|
-
context.set_details(e)
|
|
374
|
+
context.set_details(str(e))
|
|
373
375
|
return information_pb2.GetModuleOutputResponse()
|
|
374
376
|
|
|
375
377
|
return information_pb2.GetModuleOutputResponse(
|
|
@@ -405,7 +407,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
405
407
|
except NotImplementedError as e:
|
|
406
408
|
logger.warning(e)
|
|
407
409
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
408
|
-
context.set_details(e)
|
|
410
|
+
context.set_details(str(e))
|
|
409
411
|
return information_pb2.GetModuleSetupResponse()
|
|
410
412
|
|
|
411
413
|
return information_pb2.GetModuleSetupResponse(
|
|
@@ -441,7 +443,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
441
443
|
except NotImplementedError as e:
|
|
442
444
|
logger.warning(e)
|
|
443
445
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
444
|
-
context.set_details(e)
|
|
446
|
+
context.set_details(str(e))
|
|
445
447
|
return information_pb2.GetModuleSecretResponse()
|
|
446
448
|
|
|
447
449
|
return information_pb2.GetModuleSecretResponse(
|
|
@@ -477,7 +479,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
477
479
|
except NotImplementedError as e:
|
|
478
480
|
logger.warning(e)
|
|
479
481
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
480
|
-
context.set_details(e)
|
|
482
|
+
context.set_details(str(e))
|
|
481
483
|
return information_pb2.GetConfigSetupModuleResponse()
|
|
482
484
|
|
|
483
485
|
return information_pb2.GetConfigSetupModuleResponse(
|
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
from digitalkin.models.module.module import Module, ModuleStatus
|
|
4
4
|
from digitalkin.models.module.module_context import ModuleContext
|
|
5
5
|
from digitalkin.models.module.module_types import (
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
DataModel,
|
|
7
|
+
DataTrigger,
|
|
8
8
|
InputModelT,
|
|
9
|
-
InputTrigger,
|
|
10
9
|
OutputModelT,
|
|
11
10
|
SecretModelT,
|
|
11
|
+
SetupModel,
|
|
12
12
|
SetupModelT,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
__all__ = [
|
|
16
|
-
"
|
|
17
|
-
"
|
|
16
|
+
"DataModel",
|
|
17
|
+
"DataTrigger",
|
|
18
18
|
"InputModelT",
|
|
19
|
-
"InputTrigger",
|
|
20
19
|
"Module",
|
|
21
20
|
"ModuleContext",
|
|
22
21
|
"ModuleStatus",
|
|
23
22
|
"OutputModelT",
|
|
24
23
|
"SecretModelT",
|
|
24
|
+
"SetupModel",
|
|
25
25
|
"SetupModelT",
|
|
26
26
|
]
|
|
@@ -1,43 +1,105 @@
|
|
|
1
1
|
"""Types for module models."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Any, ClassVar, Generic, TypeVar, cast
|
|
4
5
|
|
|
5
|
-
from pydantic import BaseModel
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, create_model
|
|
6
7
|
|
|
8
|
+
from digitalkin.logger import logger
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
class DataTrigger(BaseModel):
|
|
9
12
|
"""Defines the root input model exposing the protocol.
|
|
10
13
|
|
|
11
14
|
The mandatory protocol is important to define the module beahvior following the user or agent input.
|
|
12
15
|
|
|
13
16
|
Example:
|
|
14
|
-
class MyInput(
|
|
15
|
-
root:
|
|
17
|
+
class MyInput(DataModel):
|
|
18
|
+
root: DataTrigger
|
|
16
19
|
user_define_data: Any
|
|
17
20
|
|
|
18
21
|
# Usage
|
|
19
|
-
my_input = MyInput(root=
|
|
22
|
+
my_input = MyInput(root=DataTrigger(protocol="message"))
|
|
20
23
|
print(my_input.root.protocol) # Output: message
|
|
21
24
|
"""
|
|
22
25
|
|
|
23
|
-
protocol: str
|
|
26
|
+
protocol: ClassVar[str]
|
|
27
|
+
created_at: str = datetime.now(tz=timezone.utc).isoformat()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
DataTriggerT = TypeVar("DataTriggerT", bound=DataTrigger)
|
|
24
31
|
|
|
25
32
|
|
|
26
|
-
class
|
|
33
|
+
class DataModel(BaseModel, Generic[DataTriggerT]):
|
|
27
34
|
"""Base definition of input model showing mandatory root fields.
|
|
28
35
|
|
|
29
36
|
The Model define the Module Input, usually referring to multiple input type defined by an union.
|
|
30
37
|
|
|
31
38
|
Example:
|
|
32
|
-
class ModuleInput(
|
|
39
|
+
class ModuleInput(DataModel):
|
|
33
40
|
root: FileInput | MessageInput
|
|
34
41
|
"""
|
|
35
42
|
|
|
36
|
-
root:
|
|
43
|
+
root: DataTriggerT
|
|
44
|
+
annotations: dict[str, str] = Field(
|
|
45
|
+
default={},
|
|
46
|
+
title="Annotations",
|
|
47
|
+
description="Additional metadata or annotations related to the output. ex {'role': 'user'}",
|
|
48
|
+
)
|
|
37
49
|
|
|
38
50
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
OutputModelT = TypeVar("OutputModelT", bound=BaseModel)
|
|
42
|
-
SetupModelT = TypeVar("SetupModelT", bound=BaseModel)
|
|
51
|
+
InputModelT = TypeVar("InputModelT", bound=DataModel)
|
|
52
|
+
OutputModelT = TypeVar("OutputModelT", bound=DataModel)
|
|
43
53
|
SecretModelT = TypeVar("SecretModelT", bound=BaseModel)
|
|
54
|
+
SetupModelT = TypeVar("SetupModelT", bound="SetupModel")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SetupModel(BaseModel):
|
|
58
|
+
"""Base definition of setup model showing mandatory root fields.
|
|
59
|
+
|
|
60
|
+
Optionally, the setup model can define a config option in json_schema_extra to be used to initialize the Kin.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
class MySetup(SetupModel):
|
|
64
|
+
name: str = Field()
|
|
65
|
+
number: int = Field(..., json_schema_extra={"config": True})
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def get_clean_model(cls, *, config_fields: bool, hidden_fields: bool) -> type[SetupModelT]: # type: ignore
|
|
70
|
+
"""Dynamically builds and returns a new BaseModel subclass.
|
|
71
|
+
|
|
72
|
+
containing only those fields where json_schema_extra["config"] == True.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Type[BaseModel]: A new BaseModel subclass with the filtered fields.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValueError: If both config_fields and hidden_fields are set to True.
|
|
79
|
+
"""
|
|
80
|
+
clean_fields: dict[str, Any] = {}
|
|
81
|
+
for name, field_info in cls.model_fields.items():
|
|
82
|
+
extra = getattr(field_info, "json_schema_extra", {}) or {}
|
|
83
|
+
is_config = bool(extra.get("config", False))
|
|
84
|
+
is_hidden = bool(extra.get("hidden", False))
|
|
85
|
+
|
|
86
|
+
# Skip config unless explicitly included
|
|
87
|
+
if is_config and not config_fields:
|
|
88
|
+
logger.debug("Skipping '%s' (config-only)", name)
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
# Skip hidden unless explicitly included
|
|
92
|
+
if is_hidden and not hidden_fields:
|
|
93
|
+
logger.debug("Skipping '%s' (hidden-only)", name)
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
clean_fields[name] = (field_info.annotation, field_info)
|
|
97
|
+
|
|
98
|
+
# Dynamically create a model e.g. "SetupModel"
|
|
99
|
+
m = create_model(
|
|
100
|
+
f"{cls.__name__}",
|
|
101
|
+
__base__=BaseModel,
|
|
102
|
+
__config__=ConfigDict(arbitrary_types_allowed=True),
|
|
103
|
+
**clean_fields,
|
|
104
|
+
)
|
|
105
|
+
return cast("type[SetupModelT]", m)
|
|
@@ -9,10 +9,8 @@ from typing import Any, ClassVar, Generic
|
|
|
9
9
|
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
|
|
12
|
-
from digitalkin.grpc_servers.utils.exceptions import OptionalFeatureNotImplementedError
|
|
13
12
|
from digitalkin.logger import logger
|
|
14
13
|
from digitalkin.models.module import (
|
|
15
|
-
ConfigSetupModelT,
|
|
16
14
|
InputModelT,
|
|
17
15
|
ModuleStatus,
|
|
18
16
|
OutputModelT,
|
|
@@ -33,11 +31,11 @@ from digitalkin.utils.llm_ready_schema import llm_ready_schema
|
|
|
33
31
|
from digitalkin.utils.package_discover import ModuleDiscoverer
|
|
34
32
|
|
|
35
33
|
|
|
36
|
-
class
|
|
34
|
+
class ModuleCodeModel(BaseModel):
|
|
37
35
|
"""typed error/code model."""
|
|
38
36
|
|
|
39
37
|
code: str
|
|
40
|
-
|
|
38
|
+
message: str
|
|
41
39
|
short_description: str
|
|
42
40
|
|
|
43
41
|
|
|
@@ -48,7 +46,6 @@ class BaseModule( # noqa: PLR0904
|
|
|
48
46
|
OutputModelT,
|
|
49
47
|
SetupModelT,
|
|
50
48
|
SecretModelT,
|
|
51
|
-
ConfigSetupModelT,
|
|
52
49
|
],
|
|
53
50
|
):
|
|
54
51
|
"""BaseModule is the abstract base for all modules in the DigitalKin SDK."""
|
|
@@ -56,10 +53,9 @@ class BaseModule( # noqa: PLR0904
|
|
|
56
53
|
name: str
|
|
57
54
|
description: str
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
setup_format: type[SetupModelT]
|
|
60
57
|
input_format: type[InputModelT]
|
|
61
58
|
output_format: type[OutputModelT]
|
|
62
|
-
setup_format: type[SetupModelT]
|
|
63
59
|
secret_format: type[SecretModelT]
|
|
64
60
|
metadata: ClassVar[dict[str, Any]]
|
|
65
61
|
|
|
@@ -80,21 +76,38 @@ class BaseModule( # noqa: PLR0904
|
|
|
80
76
|
snapshot: SnapshotStrategy
|
|
81
77
|
storage: StorageStrategy
|
|
82
78
|
|
|
79
|
+
# runtime params
|
|
80
|
+
job_id: str
|
|
81
|
+
mission_id: str
|
|
82
|
+
setup_id: str
|
|
83
|
+
setup_version_id: str
|
|
84
|
+
_status: ModuleStatus
|
|
85
|
+
_task: asyncio.Task | None
|
|
86
|
+
|
|
83
87
|
def _init_strategies(self) -> None:
|
|
84
88
|
"""Initialize the services configuration."""
|
|
85
89
|
for service_name in self.services_config.valid_strategy_names():
|
|
86
|
-
service = self.services_config.init_strategy(
|
|
90
|
+
service = self.services_config.init_strategy(
|
|
91
|
+
service_name,
|
|
92
|
+
self.mission_id,
|
|
93
|
+
self.setup_id,
|
|
94
|
+
self.setup_version_id,
|
|
95
|
+
)
|
|
87
96
|
setattr(self, service_name, service)
|
|
88
97
|
|
|
89
98
|
def __init__(
|
|
90
99
|
self,
|
|
91
100
|
job_id: str,
|
|
92
101
|
mission_id: str,
|
|
102
|
+
setup_id: str,
|
|
93
103
|
setup_version_id: str,
|
|
94
104
|
) -> None:
|
|
95
105
|
"""Initialize the module."""
|
|
96
106
|
self.job_id: str = job_id
|
|
97
107
|
self.mission_id: str = mission_id
|
|
108
|
+
# Setup reference needed for the overall Kin scope as the filesystem context
|
|
109
|
+
self.setup_id: str = setup_id
|
|
110
|
+
# SetupVersion reference needed for the precise Kin scope as the cost
|
|
98
111
|
self.setup_version_id: str = setup_version_id
|
|
99
112
|
self._status = ModuleStatus.CREATED
|
|
100
113
|
self._task: asyncio.Task | None = None
|
|
@@ -172,20 +185,22 @@ class BaseModule( # noqa: PLR0904
|
|
|
172
185
|
def get_config_setup_format(cls, *, llm_format: bool) -> str:
|
|
173
186
|
"""Gets the JSON schema of the config setup format model.
|
|
174
187
|
|
|
188
|
+
The config setup format is used only to initialize the module with configuration data.
|
|
189
|
+
The setup format is used to initialize an run the module with setup data.
|
|
190
|
+
|
|
175
191
|
Raises:
|
|
176
|
-
|
|
192
|
+
NotImplementedError: If the `setup_format` is not defined.
|
|
177
193
|
|
|
178
194
|
Returns:
|
|
179
195
|
The JSON schema of the config setup format as a string.
|
|
180
196
|
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if config_setup_format is not None:
|
|
197
|
+
if cls.setup_format is not None:
|
|
198
|
+
setup_format = cls.setup_format.get_clean_model(config_fields=True, hidden_fields=False)
|
|
184
199
|
if llm_format:
|
|
185
|
-
return json.dumps(llm_ready_schema(
|
|
186
|
-
return json.dumps(
|
|
200
|
+
return json.dumps(llm_ready_schema(setup_format), indent=2)
|
|
201
|
+
return json.dumps(setup_format.model_json_schema(), indent=2)
|
|
187
202
|
msg = "'%s' class does not define an 'config_setup_format'."
|
|
188
|
-
raise
|
|
203
|
+
raise NotImplementedError(msg)
|
|
189
204
|
|
|
190
205
|
@classmethod
|
|
191
206
|
def get_setup_format(cls, *, llm_format: bool) -> str:
|
|
@@ -198,14 +213,15 @@ class BaseModule( # noqa: PLR0904
|
|
|
198
213
|
The JSON schema of the setup format as a string.
|
|
199
214
|
"""
|
|
200
215
|
if cls.setup_format is not None:
|
|
216
|
+
setup_format = cls.setup_format.get_clean_model(config_fields=False, hidden_fields=True)
|
|
201
217
|
if llm_format:
|
|
202
|
-
return json.dumps(llm_ready_schema(
|
|
203
|
-
return json.dumps(
|
|
218
|
+
return json.dumps(llm_ready_schema(setup_format), indent=2)
|
|
219
|
+
return json.dumps(setup_format.model_json_schema(), indent=2)
|
|
204
220
|
msg = "'%s' class does not define an 'setup_format'."
|
|
205
221
|
raise NotImplementedError(msg)
|
|
206
222
|
|
|
207
223
|
@classmethod
|
|
208
|
-
def create_config_setup_model(cls, config_setup_data: dict[str, Any]) ->
|
|
224
|
+
def create_config_setup_model(cls, config_setup_data: dict[str, Any]) -> SetupModelT:
|
|
209
225
|
"""Create the setup model from the setup data.
|
|
210
226
|
|
|
211
227
|
Args:
|
|
@@ -214,7 +230,7 @@ class BaseModule( # noqa: PLR0904
|
|
|
214
230
|
Returns:
|
|
215
231
|
The setup model.
|
|
216
232
|
"""
|
|
217
|
-
return cls.
|
|
233
|
+
return cls.setup_format(**config_setup_data)
|
|
218
234
|
|
|
219
235
|
@classmethod
|
|
220
236
|
def create_input_model(cls, input_data: dict[str, Any]) -> InputModelT:
|
|
@@ -229,16 +245,17 @@ class BaseModule( # noqa: PLR0904
|
|
|
229
245
|
return cls.input_format(**input_data)
|
|
230
246
|
|
|
231
247
|
@classmethod
|
|
232
|
-
def create_setup_model(cls, setup_data: dict[str, Any]) -> SetupModelT:
|
|
248
|
+
def create_setup_model(cls, setup_data: dict[str, Any], *, config_fields: bool = False) -> SetupModelT:
|
|
233
249
|
"""Create the setup model from the setup data.
|
|
234
250
|
|
|
235
251
|
Args:
|
|
236
252
|
setup_data: The setup data to create the model from.
|
|
253
|
+
config_fields: If True, include only fields with json_schema_extra["config"] == True.
|
|
237
254
|
|
|
238
255
|
Returns:
|
|
239
256
|
The setup model.
|
|
240
257
|
"""
|
|
241
|
-
return cls.setup_format(**setup_data)
|
|
258
|
+
return cls.setup_format.get_clean_model(config_fields=config_fields, hidden_fields=True)(**setup_data)
|
|
242
259
|
|
|
243
260
|
@classmethod
|
|
244
261
|
def create_secret_model(cls, secret_data: dict[str, Any]) -> SecretModelT:
|
|
@@ -290,20 +307,20 @@ class BaseModule( # noqa: PLR0904
|
|
|
290
307
|
"""
|
|
291
308
|
return cls.triggers_discoverer.register_trigger(handler_cls)
|
|
292
309
|
|
|
293
|
-
|
|
294
|
-
async def run_config_setup(
|
|
295
|
-
self,
|
|
296
|
-
config_setup_data: ConfigSetupModelT,
|
|
297
|
-
setup_data: SetupModelT,
|
|
298
|
-
callback: Callable,
|
|
299
|
-
) -> None:
|
|
310
|
+
async def run_config_setup(self, config_setup_data: SetupModelT) -> SetupModelT: # noqa: PLR6301
|
|
300
311
|
"""Run config setup the module.
|
|
301
312
|
|
|
302
|
-
|
|
303
|
-
|
|
313
|
+
The config setup is used to initialize the setup with configuration data.
|
|
314
|
+
This method is typically used to set up the module with necessary configuration before running it,
|
|
315
|
+
especially for processing data like files.
|
|
316
|
+
The function needs to save the setup in the storage.
|
|
317
|
+
The module will be initialize with the setup and not the config setup.
|
|
318
|
+
This method is optional, the config setup and setup can be the same.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
The updated setup model after running the config setup.
|
|
304
322
|
"""
|
|
305
|
-
|
|
306
|
-
raise OptionalFeatureNotImplementedError(msg)
|
|
323
|
+
return config_setup_data
|
|
307
324
|
|
|
308
325
|
@abstractmethod
|
|
309
326
|
async def initialize(self, setup_data: SetupModelT) -> None:
|
|
@@ -364,9 +381,9 @@ class BaseModule( # noqa: PLR0904
|
|
|
364
381
|
asyncio.CancelledError: If the module is cancelled
|
|
365
382
|
"""
|
|
366
383
|
try:
|
|
367
|
-
logger.
|
|
384
|
+
logger.info("Starting module %s", self.name)
|
|
368
385
|
await self.run(input_data, setup_data, callback)
|
|
369
|
-
logger.
|
|
386
|
+
logger.info("Module %s finished", self.name)
|
|
370
387
|
except asyncio.CancelledError:
|
|
371
388
|
self._status = ModuleStatus.CANCELLED
|
|
372
389
|
logger.error(f"Module {self.name} cancelled")
|
|
@@ -374,7 +391,7 @@ class BaseModule( # noqa: PLR0904
|
|
|
374
391
|
self._status = ModuleStatus.FAILED
|
|
375
392
|
logger.exception("Error inside module %s", self.name)
|
|
376
393
|
else:
|
|
377
|
-
self._status = ModuleStatus.
|
|
394
|
+
self._status = ModuleStatus.STOPPING
|
|
378
395
|
finally:
|
|
379
396
|
await self.stop()
|
|
380
397
|
|
|
@@ -382,22 +399,22 @@ class BaseModule( # noqa: PLR0904
|
|
|
382
399
|
self,
|
|
383
400
|
input_data: InputModelT,
|
|
384
401
|
setup_data: SetupModelT,
|
|
385
|
-
callback: Callable[[OutputModelT |
|
|
402
|
+
callback: Callable[[OutputModelT | ModuleCodeModel], Coroutine[Any, Any, None]],
|
|
386
403
|
done_callback: Callable | None = None,
|
|
387
404
|
) -> None:
|
|
388
405
|
"""Start the module."""
|
|
389
406
|
try:
|
|
390
|
-
logger.
|
|
407
|
+
logger.debug("Inititalize module")
|
|
391
408
|
await self.initialize(setup_data=setup_data)
|
|
392
409
|
except Exception as e:
|
|
393
410
|
self._status = ModuleStatus.FAILED
|
|
394
411
|
short_description = "Error initializing module"
|
|
395
412
|
logger.exception("%s: %s", short_description, e)
|
|
396
413
|
await callback(
|
|
397
|
-
|
|
414
|
+
ModuleCodeModel(
|
|
398
415
|
code=str(self._status),
|
|
399
416
|
short_description=short_description,
|
|
400
|
-
|
|
417
|
+
message=str(e),
|
|
401
418
|
)
|
|
402
419
|
)
|
|
403
420
|
if done_callback is not None:
|
|
@@ -406,9 +423,9 @@ class BaseModule( # noqa: PLR0904
|
|
|
406
423
|
return
|
|
407
424
|
|
|
408
425
|
try:
|
|
409
|
-
logger.
|
|
426
|
+
logger.debug("Init the discovered input handlers.")
|
|
410
427
|
self.triggers_discoverer.init_handlers(self.context)
|
|
411
|
-
logger.
|
|
428
|
+
logger.debug("Run lifecycle")
|
|
412
429
|
self._status = ModuleStatus.RUNNING
|
|
413
430
|
self._task = asyncio.create_task(
|
|
414
431
|
self._run_lifecycle(input_data, setup_data, callback),
|
|
@@ -422,7 +439,8 @@ class BaseModule( # noqa: PLR0904
|
|
|
422
439
|
|
|
423
440
|
async def stop(self) -> None:
|
|
424
441
|
"""Stop the module."""
|
|
425
|
-
|
|
442
|
+
logger.info("Stopping module %s with status %s", self.name, self._status)
|
|
443
|
+
if self._status not in {ModuleStatus.RUNNING, ModuleStatus.STOPPING}:
|
|
426
444
|
return
|
|
427
445
|
|
|
428
446
|
try:
|
|
@@ -431,22 +449,29 @@ class BaseModule( # noqa: PLR0904
|
|
|
431
449
|
self._task.cancel()
|
|
432
450
|
with contextlib.suppress(asyncio.CancelledError):
|
|
433
451
|
await self._task
|
|
452
|
+
logger.debug("Module %s stopped", self.name)
|
|
434
453
|
await self.cleanup()
|
|
454
|
+
self._status = ModuleStatus.STOPPED
|
|
455
|
+
logger.debug("Module %s cleaned", self.name)
|
|
435
456
|
except Exception:
|
|
436
457
|
self._status = ModuleStatus.FAILED
|
|
437
458
|
logger.exception("Error stopping module")
|
|
438
459
|
|
|
439
460
|
async def start_config_setup(
|
|
440
461
|
self,
|
|
441
|
-
config_setup_data:
|
|
442
|
-
|
|
443
|
-
callback: Callable[[OutputModelT | ModuleErrorModel], Coroutine[Any, Any, None]],
|
|
462
|
+
config_setup_data: SetupModelT,
|
|
463
|
+
callback: Callable[[SetupModelT | ModuleCodeModel], Coroutine[Any, Any, None]],
|
|
444
464
|
) -> None:
|
|
445
465
|
"""Start the module."""
|
|
446
466
|
try:
|
|
447
467
|
logger.info("Run Config Setup lifecycle")
|
|
448
468
|
self._status = ModuleStatus.RUNNING
|
|
449
|
-
await self.run_config_setup(config_setup_data
|
|
469
|
+
content = await self.run_config_setup(config_setup_data)
|
|
470
|
+
|
|
471
|
+
wrapper = config_setup_data.model_dump()
|
|
472
|
+
wrapper["content"] = content.model_dump()
|
|
473
|
+
await callback(self.create_setup_model(wrapper))
|
|
474
|
+
self._status = ModuleStatus.STOPPING
|
|
450
475
|
except Exception:
|
|
451
476
|
self._status = ModuleStatus.FAILED
|
|
452
477
|
logger.exception("Error during module lifecyle")
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
|
|
5
5
|
from digitalkin.models.module import InputModelT, OutputModelT, SecretModelT, SetupModelT
|
|
6
|
-
from digitalkin.models.module.module_types import ConfigSetupModelT
|
|
7
6
|
from digitalkin.modules._base_module import BaseModule
|
|
8
7
|
|
|
9
8
|
|
|
@@ -13,7 +12,6 @@ class ArchetypeModule(
|
|
|
13
12
|
OutputModelT,
|
|
14
13
|
SetupModelT,
|
|
15
14
|
SecretModelT,
|
|
16
|
-
ConfigSetupModelT,
|
|
17
15
|
],
|
|
18
16
|
ABC,
|
|
19
17
|
):
|
|
@@ -7,13 +7,12 @@ from typing import Any, Generic
|
|
|
7
7
|
|
|
8
8
|
from digitalkin.models import ModuleStatus
|
|
9
9
|
from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
|
|
10
|
-
from digitalkin.models.module.module_types import ConfigSetupModelT
|
|
11
10
|
from digitalkin.modules._base_module import BaseModule
|
|
12
11
|
from digitalkin.services.services_config import ServicesConfig
|
|
13
12
|
from digitalkin.services.services_models import ServicesMode
|
|
14
13
|
|
|
15
14
|
|
|
16
|
-
class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT
|
|
15
|
+
class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
|
|
17
16
|
"""Abstract base class for managing background module jobs."""
|
|
18
17
|
|
|
19
18
|
async def _start(self) -> None:
|
|
@@ -88,6 +87,7 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
|
|
|
88
87
|
input_data: InputModelT,
|
|
89
88
|
setup_data: SetupModelT,
|
|
90
89
|
mission_id: str,
|
|
90
|
+
setup_id: str,
|
|
91
91
|
setup_version_id: str,
|
|
92
92
|
) -> str:
|
|
93
93
|
"""Create and start a new job for the module's instance.
|
|
@@ -96,7 +96,8 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
|
|
|
96
96
|
input_data: The input data required to start the job.
|
|
97
97
|
setup_data: The setup configuration for the module.
|
|
98
98
|
mission_id: The mission ID associated with the job.
|
|
99
|
-
|
|
99
|
+
setup_id: The setup ID.
|
|
100
|
+
setup_version_id: The setup version ID associated with the module.
|
|
100
101
|
|
|
101
102
|
Returns:
|
|
102
103
|
str: The unique identifier (job ID) of the created job.
|
|
@@ -120,9 +121,9 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
|
|
|
120
121
|
@abc.abstractmethod
|
|
121
122
|
async def create_config_setup_instance_job(
|
|
122
123
|
self,
|
|
123
|
-
config_setup_data:
|
|
124
|
-
setup_data: SetupModelT,
|
|
124
|
+
config_setup_data: SetupModelT,
|
|
125
125
|
mission_id: str,
|
|
126
|
+
setup_id: str,
|
|
126
127
|
setup_version_id: str,
|
|
127
128
|
) -> str:
|
|
128
129
|
"""Create and start a new module job.
|
|
@@ -132,9 +133,9 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
|
|
|
132
133
|
|
|
133
134
|
Args:
|
|
134
135
|
config_setup_data: The input data required to start the job.
|
|
135
|
-
setup_data: The setup configuration for the module.
|
|
136
136
|
mission_id: The mission ID associated with the job.
|
|
137
|
-
|
|
137
|
+
setup_id: The setup ID.
|
|
138
|
+
setup_version_id: The setup version ID.
|
|
138
139
|
|
|
139
140
|
Returns:
|
|
140
141
|
str: The unique identifier (job ID) of the created job.
|