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.
Files changed (131) hide show
  1. base_server/__init__.py +1 -0
  2. base_server/mock/__init__.py +5 -0
  3. base_server/mock/mock_pb2.py +39 -0
  4. base_server/mock/mock_pb2_grpc.py +102 -0
  5. base_server/server_async_insecure.py +125 -0
  6. base_server/server_async_secure.py +143 -0
  7. base_server/server_sync_insecure.py +103 -0
  8. base_server/server_sync_secure.py +122 -0
  9. digitalkin/__init__.py +8 -0
  10. digitalkin/__version__.py +8 -0
  11. digitalkin/core/__init__.py +1 -0
  12. digitalkin/core/common/__init__.py +9 -0
  13. digitalkin/core/common/factories.py +156 -0
  14. digitalkin/core/job_manager/__init__.py +1 -0
  15. digitalkin/core/job_manager/base_job_manager.py +288 -0
  16. digitalkin/core/job_manager/single_job_manager.py +354 -0
  17. digitalkin/core/job_manager/taskiq_broker.py +311 -0
  18. digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
  19. digitalkin/core/task_manager/__init__.py +1 -0
  20. digitalkin/core/task_manager/base_task_manager.py +539 -0
  21. digitalkin/core/task_manager/local_task_manager.py +108 -0
  22. digitalkin/core/task_manager/remote_task_manager.py +87 -0
  23. digitalkin/core/task_manager/surrealdb_repository.py +266 -0
  24. digitalkin/core/task_manager/task_executor.py +249 -0
  25. digitalkin/core/task_manager/task_session.py +406 -0
  26. digitalkin/grpc_servers/__init__.py +1 -0
  27. digitalkin/grpc_servers/_base_server.py +486 -0
  28. digitalkin/grpc_servers/module_server.py +208 -0
  29. digitalkin/grpc_servers/module_servicer.py +516 -0
  30. digitalkin/grpc_servers/utils/__init__.py +1 -0
  31. digitalkin/grpc_servers/utils/exceptions.py +29 -0
  32. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
  33. digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
  34. digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
  35. digitalkin/logger.py +157 -0
  36. digitalkin/mixins/__init__.py +19 -0
  37. digitalkin/mixins/base_mixin.py +10 -0
  38. digitalkin/mixins/callback_mixin.py +24 -0
  39. digitalkin/mixins/chat_history_mixin.py +110 -0
  40. digitalkin/mixins/cost_mixin.py +76 -0
  41. digitalkin/mixins/file_history_mixin.py +93 -0
  42. digitalkin/mixins/filesystem_mixin.py +46 -0
  43. digitalkin/mixins/logger_mixin.py +51 -0
  44. digitalkin/mixins/storage_mixin.py +79 -0
  45. digitalkin/models/__init__.py +8 -0
  46. digitalkin/models/core/__init__.py +1 -0
  47. digitalkin/models/core/job_manager_models.py +36 -0
  48. digitalkin/models/core/task_monitor.py +70 -0
  49. digitalkin/models/grpc_servers/__init__.py +1 -0
  50. digitalkin/models/grpc_servers/models.py +275 -0
  51. digitalkin/models/grpc_servers/types.py +24 -0
  52. digitalkin/models/module/__init__.py +25 -0
  53. digitalkin/models/module/module.py +40 -0
  54. digitalkin/models/module/module_context.py +149 -0
  55. digitalkin/models/module/module_types.py +393 -0
  56. digitalkin/models/module/utility.py +146 -0
  57. digitalkin/models/services/__init__.py +10 -0
  58. digitalkin/models/services/cost.py +54 -0
  59. digitalkin/models/services/registry.py +42 -0
  60. digitalkin/models/services/storage.py +44 -0
  61. digitalkin/modules/__init__.py +11 -0
  62. digitalkin/modules/_base_module.py +517 -0
  63. digitalkin/modules/archetype_module.py +23 -0
  64. digitalkin/modules/tool_module.py +23 -0
  65. digitalkin/modules/trigger_handler.py +48 -0
  66. digitalkin/modules/triggers/__init__.py +12 -0
  67. digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  68. digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  69. digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  70. digitalkin/py.typed +0 -0
  71. digitalkin/services/__init__.py +30 -0
  72. digitalkin/services/agent/__init__.py +6 -0
  73. digitalkin/services/agent/agent_strategy.py +19 -0
  74. digitalkin/services/agent/default_agent.py +13 -0
  75. digitalkin/services/base_strategy.py +22 -0
  76. digitalkin/services/communication/__init__.py +7 -0
  77. digitalkin/services/communication/communication_strategy.py +76 -0
  78. digitalkin/services/communication/default_communication.py +101 -0
  79. digitalkin/services/communication/grpc_communication.py +223 -0
  80. digitalkin/services/cost/__init__.py +14 -0
  81. digitalkin/services/cost/cost_strategy.py +100 -0
  82. digitalkin/services/cost/default_cost.py +114 -0
  83. digitalkin/services/cost/grpc_cost.py +138 -0
  84. digitalkin/services/filesystem/__init__.py +7 -0
  85. digitalkin/services/filesystem/default_filesystem.py +417 -0
  86. digitalkin/services/filesystem/filesystem_strategy.py +252 -0
  87. digitalkin/services/filesystem/grpc_filesystem.py +317 -0
  88. digitalkin/services/identity/__init__.py +6 -0
  89. digitalkin/services/identity/default_identity.py +15 -0
  90. digitalkin/services/identity/identity_strategy.py +14 -0
  91. digitalkin/services/registry/__init__.py +27 -0
  92. digitalkin/services/registry/default_registry.py +141 -0
  93. digitalkin/services/registry/exceptions.py +47 -0
  94. digitalkin/services/registry/grpc_registry.py +306 -0
  95. digitalkin/services/registry/registry_models.py +43 -0
  96. digitalkin/services/registry/registry_strategy.py +98 -0
  97. digitalkin/services/services_config.py +200 -0
  98. digitalkin/services/services_models.py +65 -0
  99. digitalkin/services/setup/__init__.py +1 -0
  100. digitalkin/services/setup/default_setup.py +219 -0
  101. digitalkin/services/setup/grpc_setup.py +343 -0
  102. digitalkin/services/setup/setup_strategy.py +145 -0
  103. digitalkin/services/snapshot/__init__.py +6 -0
  104. digitalkin/services/snapshot/default_snapshot.py +39 -0
  105. digitalkin/services/snapshot/snapshot_strategy.py +30 -0
  106. digitalkin/services/storage/__init__.py +7 -0
  107. digitalkin/services/storage/default_storage.py +228 -0
  108. digitalkin/services/storage/grpc_storage.py +214 -0
  109. digitalkin/services/storage/storage_strategy.py +273 -0
  110. digitalkin/services/user_profile/__init__.py +12 -0
  111. digitalkin/services/user_profile/default_user_profile.py +55 -0
  112. digitalkin/services/user_profile/grpc_user_profile.py +69 -0
  113. digitalkin/services/user_profile/user_profile_strategy.py +40 -0
  114. digitalkin/utils/__init__.py +29 -0
  115. digitalkin/utils/arg_parser.py +92 -0
  116. digitalkin/utils/development_mode_action.py +51 -0
  117. digitalkin/utils/dynamic_schema.py +483 -0
  118. digitalkin/utils/llm_ready_schema.py +75 -0
  119. digitalkin/utils/package_discover.py +357 -0
  120. digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
  121. digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
  122. digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
  123. digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
  124. digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
  125. modules/__init__.py +0 -0
  126. modules/cpu_intensive_module.py +280 -0
  127. modules/dynamic_setup_module.py +338 -0
  128. modules/minimal_llm_module.py +347 -0
  129. modules/text_transform_module.py +203 -0
  130. services/filesystem_module.py +200 -0
  131. 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()