digitalkin 0.3.2.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.
- 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 +125 -0
- base_server/server_async_secure.py +143 -0
- base_server/server_sync_insecure.py +103 -0
- base_server/server_sync_secure.py +122 -0
- digitalkin/__init__.py +8 -0
- digitalkin/__version__.py +8 -0
- 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/core/job_manager/base_job_manager.py +288 -0
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/core/job_manager/taskiq_broker.py +311 -0
- 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 -0
- digitalkin/grpc_servers/_base_server.py +486 -0
- digitalkin/grpc_servers/module_server.py +208 -0
- digitalkin/grpc_servers/module_servicer.py +516 -0
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +29 -0
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
- digitalkin/logger.py +157 -0
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/__init__.py +8 -0
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/models/core/job_manager_models.py +36 -0
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/models/grpc_servers/models.py +275 -0
- digitalkin/models/grpc_servers/types.py +24 -0
- digitalkin/models/module/__init__.py +25 -0
- digitalkin/models/module/module.py +40 -0
- digitalkin/models/module/module_context.py +149 -0
- digitalkin/models/module/module_types.py +393 -0
- digitalkin/models/module/utility.py +146 -0
- digitalkin/models/services/__init__.py +10 -0
- digitalkin/models/services/cost.py +54 -0
- digitalkin/models/services/registry.py +42 -0
- digitalkin/models/services/storage.py +44 -0
- digitalkin/modules/__init__.py +11 -0
- digitalkin/modules/_base_module.py +517 -0
- digitalkin/modules/archetype_module.py +23 -0
- digitalkin/modules/tool_module.py +23 -0
- digitalkin/modules/trigger_handler.py +48 -0
- digitalkin/modules/triggers/__init__.py +12 -0
- digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
- digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
- digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
- digitalkin/py.typed +0 -0
- digitalkin/services/__init__.py +30 -0
- digitalkin/services/agent/__init__.py +6 -0
- digitalkin/services/agent/agent_strategy.py +19 -0
- digitalkin/services/agent/default_agent.py +13 -0
- digitalkin/services/base_strategy.py +22 -0
- digitalkin/services/communication/__init__.py +7 -0
- digitalkin/services/communication/communication_strategy.py +76 -0
- digitalkin/services/communication/default_communication.py +101 -0
- digitalkin/services/communication/grpc_communication.py +223 -0
- digitalkin/services/cost/__init__.py +14 -0
- digitalkin/services/cost/cost_strategy.py +100 -0
- digitalkin/services/cost/default_cost.py +114 -0
- digitalkin/services/cost/grpc_cost.py +138 -0
- digitalkin/services/filesystem/__init__.py +7 -0
- digitalkin/services/filesystem/default_filesystem.py +417 -0
- digitalkin/services/filesystem/filesystem_strategy.py +252 -0
- digitalkin/services/filesystem/grpc_filesystem.py +317 -0
- digitalkin/services/identity/__init__.py +6 -0
- digitalkin/services/identity/default_identity.py +15 -0
- digitalkin/services/identity/identity_strategy.py +14 -0
- digitalkin/services/registry/__init__.py +27 -0
- digitalkin/services/registry/default_registry.py +141 -0
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +43 -0
- digitalkin/services/registry/registry_strategy.py +98 -0
- digitalkin/services/services_config.py +200 -0
- digitalkin/services/services_models.py +65 -0
- digitalkin/services/setup/__init__.py +1 -0
- digitalkin/services/setup/default_setup.py +219 -0
- digitalkin/services/setup/grpc_setup.py +343 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +6 -0
- digitalkin/services/snapshot/default_snapshot.py +39 -0
- digitalkin/services/snapshot/snapshot_strategy.py +30 -0
- digitalkin/services/storage/__init__.py +7 -0
- digitalkin/services/storage/default_storage.py +228 -0
- digitalkin/services/storage/grpc_storage.py +214 -0
- digitalkin/services/storage/storage_strategy.py +273 -0
- digitalkin/services/user_profile/__init__.py +12 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +40 -0
- digitalkin/utils/__init__.py +29 -0
- digitalkin/utils/arg_parser.py +92 -0
- digitalkin/utils/development_mode_action.py +51 -0
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/llm_ready_schema.py +75 -0
- digitalkin/utils/package_discover.py +357 -0
- digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
- digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
- digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
- digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
- digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
- modules/__init__.py +0 -0
- modules/cpu_intensive_module.py +280 -0
- modules/dynamic_setup_module.py +338 -0
- modules/minimal_llm_module.py +347 -0
- modules/text_transform_module.py +203 -0
- services/filesystem_module.py +200 -0
- services/storage_module.py +206 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""Example module implementation to test ArchetypeModule functionality."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from digitalkin.logger import logger
|
|
11
|
+
from digitalkin.models.module import ModuleStatus
|
|
12
|
+
from digitalkin.modules.archetype_module import ArchetypeModule
|
|
13
|
+
from digitalkin.services.filesystem.filesystem_strategy import FileFilter, UploadFileData
|
|
14
|
+
from digitalkin.services.services_config import ServicesConfig
|
|
15
|
+
from digitalkin.services.services_models import ServicesMode
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExampleInput(BaseModel):
|
|
19
|
+
"""Input model for example module."""
|
|
20
|
+
|
|
21
|
+
message: str = Field(description="Message to process")
|
|
22
|
+
number: int = Field(description="Number to process")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ExampleOutput(BaseModel):
|
|
26
|
+
"""Output model for example module."""
|
|
27
|
+
|
|
28
|
+
processed_message: str = Field(description="The processed message")
|
|
29
|
+
processed_number: int = Field(description="The processed number")
|
|
30
|
+
timestamp: datetime.datetime = Field(description="When the processing was done")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ExampleSetup(BaseModel):
|
|
34
|
+
"""Setup model for example module."""
|
|
35
|
+
|
|
36
|
+
processing_mode: str = Field(description="Mode to process data in", default="default")
|
|
37
|
+
multiply_factor: int = Field(description="Factor to multiply number by", default=1)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ExampleSecret(BaseModel):
|
|
41
|
+
"""Secret model for example module."""
|
|
42
|
+
|
|
43
|
+
api_key: str = Field(description="API key for external service")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ExampleStorage(BaseModel):
|
|
47
|
+
"""Secret model for example module."""
|
|
48
|
+
|
|
49
|
+
test_key: str = Field(description="Test value for storage")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ExampleModule(ArchetypeModule[ExampleInput, ExampleOutput, ExampleSetup, ExampleSecret, None]):
|
|
53
|
+
"""Example module that demonstrates ArchetypeModule functionality."""
|
|
54
|
+
|
|
55
|
+
name = "ExampleModule"
|
|
56
|
+
description = "An example module for testing purposes"
|
|
57
|
+
input_format = ExampleInput
|
|
58
|
+
output_format = ExampleOutput
|
|
59
|
+
setup_format = ExampleSetup
|
|
60
|
+
secret_format = ExampleSecret
|
|
61
|
+
metadata = {"name": "ExampleModule", "description": "A module for testing ArchetypeModule functionality"}
|
|
62
|
+
|
|
63
|
+
# Define services_config_params with default values
|
|
64
|
+
services_config_strategies = {}
|
|
65
|
+
services_config_params = {
|
|
66
|
+
"cost": {"config": {}},
|
|
67
|
+
"storage": {"config": {}},
|
|
68
|
+
} # Filesystem has no config but it's enabled
|
|
69
|
+
|
|
70
|
+
def __init__(self, job_id: str, mission_id: str, setup_version_id: str) -> None:
|
|
71
|
+
"""Initialize the example module.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
job_id: Unique identifier for the job
|
|
75
|
+
name: Optional name for the module
|
|
76
|
+
"""
|
|
77
|
+
# Initialize services configuration using the class attribute before the instance is created
|
|
78
|
+
self.services_config = ServicesConfig(
|
|
79
|
+
services_config_strategies=self.services_config_strategies,
|
|
80
|
+
services_config_params=self.services_config_params,
|
|
81
|
+
mode=ServicesMode.LOCAL,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
super().__init__(job_id, mission_id, setup_version_id)
|
|
85
|
+
|
|
86
|
+
async def initialize(self, setup_data: ExampleSetup) -> None:
|
|
87
|
+
"""Initialize the module.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
setup_data: Setup data for the module
|
|
91
|
+
"""
|
|
92
|
+
logger.info("Initializing ExampleModule with setup data: %s", setup_data)
|
|
93
|
+
self.setup = self.setup_format.model_validate(setup_data)
|
|
94
|
+
logger.info("Initialization complete, using processing mode: [%s]", self.setup.processing_mode)
|
|
95
|
+
|
|
96
|
+
async def run(
|
|
97
|
+
self,
|
|
98
|
+
input_data: dict[str, Any],
|
|
99
|
+
setup_data: ExampleSetup,
|
|
100
|
+
callback: Callable,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Run the module.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
input_data: Input data for the module
|
|
106
|
+
setup_data: Setup data for the module
|
|
107
|
+
callback: Callback function to report progress
|
|
108
|
+
"""
|
|
109
|
+
# Validate the input data
|
|
110
|
+
input_model = self.input_format.model_validate(input_data)
|
|
111
|
+
logger.info("Running with input data: %s", input_model)
|
|
112
|
+
|
|
113
|
+
# Process the data
|
|
114
|
+
processed_message = f"Processed: {input_model.message}"
|
|
115
|
+
processed_number = input_model.number * self.setup.multiply_factor
|
|
116
|
+
|
|
117
|
+
# Create output model
|
|
118
|
+
file = UploadFileData(
|
|
119
|
+
content=b"%s\n%s" % (processed_message.encode(), str(processed_number).encode()),
|
|
120
|
+
name="example_output.txt",
|
|
121
|
+
file_type="text/plain",
|
|
122
|
+
content_type="text/plain",
|
|
123
|
+
metadata={"example_key": "example_value"},
|
|
124
|
+
replace_if_exists=True,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
records, uploaded, failed = self.filesystem.upload_files(files=[file])
|
|
128
|
+
for record in records:
|
|
129
|
+
logger.info("Uploaded file: %s, uploaded: %d, failed: %d", record, uploaded, failed)
|
|
130
|
+
logger.info("Stored file with ID: %s", record.id)
|
|
131
|
+
callback(record.model_dump())
|
|
132
|
+
# Call the callback with the output data
|
|
133
|
+
|
|
134
|
+
# Wait a bit to simulate processing time
|
|
135
|
+
await asyncio.sleep(1)
|
|
136
|
+
|
|
137
|
+
async def run_config_setup(
|
|
138
|
+
self,
|
|
139
|
+
setup_data: ExampleSetup,
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Run the configuration setup for the module.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
setup_data: Setup data for the module
|
|
145
|
+
"""
|
|
146
|
+
logger.info("Running config setup with data: %s", setup_data)
|
|
147
|
+
# Here we could implement any additional configuration logic if needed
|
|
148
|
+
|
|
149
|
+
async def cleanup(self) -> None:
|
|
150
|
+
"""Clean up the module."""
|
|
151
|
+
logger.info("Cleaning up ExampleModule")
|
|
152
|
+
# Nothing to clean up in this example
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def test_module() -> None:
|
|
156
|
+
"""Test the example module."""
|
|
157
|
+
# Create the module
|
|
158
|
+
module = ExampleModule(job_id="test-job-123", mission_id="test-mission-123", setup_version_id="test-setup-123")
|
|
159
|
+
|
|
160
|
+
# Define input and setup data
|
|
161
|
+
input_data = ExampleInput(message="Hello, world!", number=42)
|
|
162
|
+
|
|
163
|
+
setup_data = ExampleSetup(processing_mode="test", multiply_factor=10)
|
|
164
|
+
|
|
165
|
+
# Define a callback function
|
|
166
|
+
def callback(result) -> None:
|
|
167
|
+
logger.info(f"callback {result}")
|
|
168
|
+
|
|
169
|
+
# Start the module
|
|
170
|
+
await module.start(input_data, setup_data, callback)
|
|
171
|
+
|
|
172
|
+
# Wait for the module to complete
|
|
173
|
+
while module.status not in {ModuleStatus.STOPPED, ModuleStatus.FAILED}:
|
|
174
|
+
await asyncio.sleep(0.5)
|
|
175
|
+
|
|
176
|
+
# Check the storage
|
|
177
|
+
if module.status == ModuleStatus.STOPPED:
|
|
178
|
+
files, _nb_results = module.filesystem.get_files(
|
|
179
|
+
filters=FileFilter(name="example_output.txt", context="test-mission-123"),
|
|
180
|
+
)
|
|
181
|
+
for file in files:
|
|
182
|
+
module.filesystem.update_file(file.id, file_type="updated")
|
|
183
|
+
# module.filesystem.delete_files(filters=FileFilter(name="example_output.txt", context="test-mission-123"), permanent=True)
|
|
184
|
+
|
|
185
|
+
logger.info("Retrieved file: %s with ID: %s", file.name, file.id)
|
|
186
|
+
try:
|
|
187
|
+
file_record = module.filesystem.get_file(file_id=file.id, include_content=True)
|
|
188
|
+
if file_record:
|
|
189
|
+
logger.info("File ID: %s", file_record.id)
|
|
190
|
+
logger.info("File name: %s", file_record.name)
|
|
191
|
+
logger.info("File type: %s", file_record.file_type)
|
|
192
|
+
logger.info("File status: %s", file_record.status)
|
|
193
|
+
logger.info("File content: %s", file_record.content.decode())
|
|
194
|
+
except Exception:
|
|
195
|
+
logger.error("No file retrieved")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
# Run the module test
|
|
200
|
+
asyncio.run(test_module())
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""Example module implementation to test ArchetypeModule functionality."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from digitalkin.logger import logger
|
|
11
|
+
from digitalkin.models.module import ModuleStatus
|
|
12
|
+
from digitalkin.modules.archetype_module import ArchetypeModule
|
|
13
|
+
from digitalkin.services.services_config import ServicesConfig
|
|
14
|
+
from digitalkin.services.services_models import ServicesMode
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from digitalkin.services.storage.storage_strategy import StorageRecord
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExampleInput(BaseModel):
|
|
21
|
+
"""Input model for example module."""
|
|
22
|
+
|
|
23
|
+
message: str = Field(description="Message to process")
|
|
24
|
+
number: int = Field(description="Number to process")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ExampleOutput(BaseModel):
|
|
28
|
+
"""Output model for example module."""
|
|
29
|
+
|
|
30
|
+
processed_message: str = Field(description="The processed message")
|
|
31
|
+
processed_number: int = Field(description="The processed number")
|
|
32
|
+
timestamp: datetime.datetime = Field(description="When the processing was done")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ExampleSetup(BaseModel):
|
|
36
|
+
"""Setup model for example module."""
|
|
37
|
+
|
|
38
|
+
processing_mode: str = Field(description="Mode to process data in", default="default")
|
|
39
|
+
multiply_factor: int = Field(description="Factor to multiply number by", default=1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ExampleSecret(BaseModel):
|
|
43
|
+
"""Secret model for example module."""
|
|
44
|
+
|
|
45
|
+
api_key: str = Field(description="API key for external service")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ExampleStorage(BaseModel):
|
|
49
|
+
"""Secret model for example module."""
|
|
50
|
+
|
|
51
|
+
test_key: str = Field(description="Test value for storage")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ExampleModule(ArchetypeModule[ExampleInput, ExampleOutput, ExampleSetup, ExampleSecret, None]):
|
|
55
|
+
"""Example module that demonstrates ArchetypeModule functionality."""
|
|
56
|
+
|
|
57
|
+
name = "ExampleModule"
|
|
58
|
+
description = "An example module for testing purposes"
|
|
59
|
+
input_format = ExampleInput
|
|
60
|
+
output_format = ExampleOutput
|
|
61
|
+
setup_format = ExampleSetup
|
|
62
|
+
secret_format = ExampleSecret
|
|
63
|
+
metadata = {"name": "ExampleModule", "description": "A module for testing ArchetypeModule functionality"}
|
|
64
|
+
|
|
65
|
+
# Define services_config_params with default values
|
|
66
|
+
services_config_strategies = {}
|
|
67
|
+
services_config_params = {"storage": {"config": {"example": ExampleOutput}}, "cost": {"config": {}}}
|
|
68
|
+
|
|
69
|
+
def __init__(self, job_id: str, mission_id: str, setup_version_id: str) -> None:
|
|
70
|
+
"""Initialize the example module.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
job_id: Unique identifier for the job
|
|
74
|
+
mission_id: Unique identifier for the mission
|
|
75
|
+
setup_version_id: Unique identifier for the setup version
|
|
76
|
+
"""
|
|
77
|
+
# Initialize services configuration using the class attribute before the instance is created
|
|
78
|
+
self.services_config = ServicesConfig(
|
|
79
|
+
services_config_strategies=self.services_config_strategies,
|
|
80
|
+
services_config_params=self.services_config_params,
|
|
81
|
+
mode=ServicesMode.LOCAL,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
super().__init__(job_id, mission_id, setup_version_id)
|
|
85
|
+
|
|
86
|
+
async def initialize(self, setup_data: ExampleSetup) -> None:
|
|
87
|
+
"""Initialize the module.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
setup_data: Setup data for the module
|
|
91
|
+
"""
|
|
92
|
+
logger.info("Initializing ExampleModule with setup data: %s", setup_data)
|
|
93
|
+
self.setup = self.setup_format.model_validate(setup_data)
|
|
94
|
+
logger.info("Initialization complete, using processing mode: [%s]", self.setup.processing_mode)
|
|
95
|
+
|
|
96
|
+
async def run_config_setup(
|
|
97
|
+
self,
|
|
98
|
+
setup_data: ExampleSetup,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Run the configuration setup for the module.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
setup_data: Setup data for the module
|
|
104
|
+
"""
|
|
105
|
+
logger.info("Running config setup with data: %s", setup_data)
|
|
106
|
+
# Here we could implement any additional configuration logic if needed
|
|
107
|
+
|
|
108
|
+
async def run(
|
|
109
|
+
self,
|
|
110
|
+
input_data: dict[str, Any],
|
|
111
|
+
setup_data: ExampleSetup,
|
|
112
|
+
callback: Callable,
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Run the module.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
input_data: Input data for the module
|
|
118
|
+
setup_data: Setup data for the module
|
|
119
|
+
callback: Callback function to report progress
|
|
120
|
+
"""
|
|
121
|
+
# Validate the input data
|
|
122
|
+
input_model = self.input_format.model_validate(input_data)
|
|
123
|
+
logger.info("Running with input data: %s", input_model)
|
|
124
|
+
|
|
125
|
+
# Process the data
|
|
126
|
+
processed_message = f"Processed: {input_model.message}"
|
|
127
|
+
processed_number = input_model.number * self.setup.multiply_factor
|
|
128
|
+
|
|
129
|
+
# Create output model
|
|
130
|
+
output_data = self.output_format(
|
|
131
|
+
processed_message=processed_message,
|
|
132
|
+
processed_number=processed_number,
|
|
133
|
+
timestamp=datetime.datetime.now(),
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Store the output data in storage
|
|
137
|
+
storage_id = self.storage.store(
|
|
138
|
+
collection="example", record_id="example_outputs", data=output_data.model_dump(), data_type="OUTPUT"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
logger.info("Stored output data with ID: %s", storage_id)
|
|
142
|
+
|
|
143
|
+
# Call the callback with the output data
|
|
144
|
+
callback(output_data.model_dump())
|
|
145
|
+
|
|
146
|
+
# Wait a bit to simulate processing time
|
|
147
|
+
await asyncio.sleep(1)
|
|
148
|
+
|
|
149
|
+
async def cleanup(self) -> None:
|
|
150
|
+
"""Clean up the module."""
|
|
151
|
+
logger.info("Cleaning up ExampleModule")
|
|
152
|
+
# Nothing to clean up in this example
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
async def test_module() -> None:
|
|
156
|
+
"""Test the example module."""
|
|
157
|
+
# Create the module
|
|
158
|
+
module = ExampleModule(job_id="test-job-123", mission_id="test-mission-123", setup_version_id="test-setup-123")
|
|
159
|
+
|
|
160
|
+
# Define input and setup data
|
|
161
|
+
input_data = ExampleInput(message="Hello, world!", number=42)
|
|
162
|
+
|
|
163
|
+
setup_data = ExampleSetup(processing_mode="test", multiply_factor=10)
|
|
164
|
+
|
|
165
|
+
# Define a callback function
|
|
166
|
+
def callback(result) -> None:
|
|
167
|
+
for key, value in result.items():
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
# Start the module
|
|
171
|
+
await module.start(input_data, setup_data, callback)
|
|
172
|
+
|
|
173
|
+
# Wait for the module to complete
|
|
174
|
+
while module.status not in {ModuleStatus.STOPPED, ModuleStatus.FAILED}:
|
|
175
|
+
await asyncio.sleep(0.5)
|
|
176
|
+
|
|
177
|
+
# Check the storage
|
|
178
|
+
if module.status == ModuleStatus.STOPPED:
|
|
179
|
+
result: StorageRecord = module.storage.read("example", "example_outputs")
|
|
180
|
+
if result:
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_storage_directly() -> None:
|
|
185
|
+
"""Test the storage service directly."""
|
|
186
|
+
# Initialize storage service
|
|
187
|
+
storage = ServicesConfig().storage(
|
|
188
|
+
mission_id="test-mission", setup_version_id="test-setup-123", config={"example": ExampleStorage}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Create a test record
|
|
192
|
+
storage.store("example", "test_table", {"test_key": "test_value"}, "OUTPUT")
|
|
193
|
+
|
|
194
|
+
# Retrieve the record
|
|
195
|
+
retrieved = storage.read("example", "test_table")
|
|
196
|
+
|
|
197
|
+
if retrieved:
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
# Run the module test
|
|
203
|
+
asyncio.run(test_module())
|
|
204
|
+
|
|
205
|
+
# Test storage directly
|
|
206
|
+
test_storage_directly()
|