digitalkin 0.2.11__py3-none-any.whl → 0.2.13__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 (41) hide show
  1. digitalkin/__version__.py +1 -1
  2. digitalkin/grpc_servers/_base_server.py +15 -17
  3. digitalkin/grpc_servers/module_server.py +9 -10
  4. digitalkin/grpc_servers/module_servicer.py +108 -85
  5. digitalkin/grpc_servers/registry_server.py +3 -6
  6. digitalkin/grpc_servers/registry_servicer.py +18 -19
  7. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +3 -5
  8. digitalkin/logger.py +45 -1
  9. digitalkin/models/module/module.py +1 -0
  10. digitalkin/modules/_base_module.py +47 -6
  11. digitalkin/modules/job_manager/base_job_manager.py +139 -0
  12. digitalkin/modules/job_manager/job_manager_models.py +44 -0
  13. digitalkin/modules/job_manager/single_job_manager.py +218 -0
  14. digitalkin/modules/job_manager/taskiq_broker.py +173 -0
  15. digitalkin/modules/job_manager/taskiq_job_manager.py +213 -0
  16. digitalkin/services/base_strategy.py +3 -1
  17. digitalkin/services/cost/cost_strategy.py +64 -16
  18. digitalkin/services/cost/default_cost.py +95 -12
  19. digitalkin/services/cost/grpc_cost.py +149 -60
  20. digitalkin/services/filesystem/default_filesystem.py +5 -6
  21. digitalkin/services/filesystem/filesystem_strategy.py +3 -2
  22. digitalkin/services/filesystem/grpc_filesystem.py +31 -26
  23. digitalkin/services/services_config.py +6 -5
  24. digitalkin/services/setup/__init__.py +1 -0
  25. digitalkin/services/setup/default_setup.py +10 -12
  26. digitalkin/services/setup/grpc_setup.py +8 -10
  27. digitalkin/services/storage/default_storage.py +13 -6
  28. digitalkin/services/storage/grpc_storage.py +25 -9
  29. digitalkin/services/storage/storage_strategy.py +3 -2
  30. digitalkin/utils/arg_parser.py +5 -48
  31. digitalkin/utils/development_mode_action.py +51 -0
  32. {digitalkin-0.2.11.dist-info → digitalkin-0.2.13.dist-info}/METADATA +43 -12
  33. {digitalkin-0.2.11.dist-info → digitalkin-0.2.13.dist-info}/RECORD +40 -33
  34. {digitalkin-0.2.11.dist-info → digitalkin-0.2.13.dist-info}/WHEEL +1 -1
  35. modules/cpu_intensive_module.py +271 -0
  36. modules/minimal_llm_module.py +200 -56
  37. modules/storage_module.py +5 -6
  38. modules/text_transform_module.py +1 -1
  39. digitalkin/modules/job_manager.py +0 -176
  40. {digitalkin-0.2.11.dist-info → digitalkin-0.2.13.dist-info}/licenses/LICENSE +0 -0
  41. {digitalkin-0.2.11.dist-info → digitalkin-0.2.13.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,15 @@
1
1
  """This module contains the abstract base class for setup strategies."""
2
2
 
3
- import logging
4
3
  import secrets
5
4
  import string
6
5
  from typing import Any
7
6
 
8
7
  from pydantic import ValidationError
9
8
 
9
+ from digitalkin.logger import logger
10
10
  from digitalkin.services.setup.grpc_setup import SetupData, SetupVersionData
11
11
  from digitalkin.services.setup.setup_strategy import SetupServiceError, SetupStrategy
12
12
 
13
- logger = logging.getLogger(__name__)
14
-
15
13
 
16
14
  class DefaultSetup(SetupStrategy):
17
15
  """Abstract base class for setup strategies."""
@@ -49,7 +47,7 @@ class DefaultSetup(SetupStrategy):
49
47
  )
50
48
  valid_data.id = setup_id
51
49
  self.setups[setup_id] = valid_data
52
- logger.info("CREATE SETUP DATA %s:%s succesfull", setup_id, valid_data)
50
+ logger.debug("CREATE SETUP DATA %s:%s succesfull", setup_id, valid_data)
53
51
  return setup_id
54
52
 
55
53
  def get_setup(self, setup_dict: dict[str, Any]) -> SetupData:
@@ -64,7 +62,7 @@ class DefaultSetup(SetupStrategy):
64
62
  Returns:
65
63
  Dict[str, Any]: Setup details including optional setup version.
66
64
  """
67
- logger.info("GET setup_id = %s", setup_dict["setup_id"])
65
+ logger.debug("GET setup_id = %s", setup_dict["setup_id"])
68
66
  if setup_dict["setup_id"] not in self.setups:
69
67
  msg = f"GET setup_id = {setup_dict['setup_id']}: setup_id DOESN'T EXIST"
70
68
  logger.error(msg)
@@ -84,7 +82,7 @@ class DefaultSetup(SetupStrategy):
84
82
  bool: Success status of the update operation.
85
83
  """
86
84
  if setup_dict["setup_id"] not in self.setups:
87
- logger.info("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
85
+ logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
88
86
  return False
89
87
 
90
88
  try:
@@ -106,7 +104,7 @@ class DefaultSetup(SetupStrategy):
106
104
  bool: Success status of deletion.
107
105
  """
108
106
  if setup_dict["setup_id"] not in self.setups:
109
- logger.info("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
107
+ logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_dict["setup_id"])
110
108
  return False
111
109
  del self.setups[setup_dict["setup_id"]]
112
110
  return True
@@ -133,7 +131,7 @@ class DefaultSetup(SetupStrategy):
133
131
  if setup_version_dict["setup_id"] not in self.setup_versions:
134
132
  self.setup_versions[setup_version_dict["setup_id"]] = {}
135
133
  self.setup_versions[setup_version_dict["setup_id"]][valid_data.version] = valid_data
136
- logger.info("CREATE SETUP VERSION DATA %s:%s succesfull", setup_version_dict["setup_id"], valid_data)
134
+ logger.debug("CREATE SETUP VERSION DATA %s:%s succesfull", setup_version_dict["setup_id"], valid_data)
137
135
  return valid_data.version
138
136
 
139
137
  def get_setup_version(self, setup_version_dict: dict[str, Any]) -> SetupVersionData:
@@ -148,7 +146,7 @@ class DefaultSetup(SetupStrategy):
148
146
  Returns:
149
147
  Dict[str, Any]: Setup version details.
150
148
  """
151
- logger.info("GET setup_id = %s: version = %s", setup_version_dict["setup_id"], setup_version_dict["version"])
149
+ logger.debug("GET setup_id = %s: version = %s", setup_version_dict["setup_id"], setup_version_dict["version"])
152
150
  if setup_version_dict["setup_id"] not in self.setup_versions:
153
151
  msg = f"GET setup_id = {setup_version_dict['setup_id']}: setup_id DOESN'T EXIST"
154
152
  logger.error(msg)
@@ -189,11 +187,11 @@ class DefaultSetup(SetupStrategy):
189
187
  bool: Success status of the update operation.
190
188
  """
191
189
  if setup_version_dict["setup_id"] not in self.setup_versions:
192
- logger.info("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
190
+ logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
193
191
  return False
194
192
 
195
193
  if setup_version_dict["version"] not in self.setup_versions["setup_id"]:
196
- logger.info("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
194
+ logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
197
195
  return False
198
196
 
199
197
  try:
@@ -215,7 +213,7 @@ class DefaultSetup(SetupStrategy):
215
213
  bool: Success status of version deletion.
216
214
  """
217
215
  if setup_version_dict["setup_id"] not in self.setup_versions:
218
- logger.info("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
216
+ logger.debug("UPDATE setup_id = %s: setup_id DOESN'T EXIST", setup_version_dict["setup_id"])
219
217
  return False
220
218
 
221
219
  del self.setup_versions[setup_version_dict["setup_id"]][setup_version_dict["version"]]
@@ -1,6 +1,5 @@
1
1
  """Digital Kin Setup Service gRPC Client."""
2
2
 
3
- import logging
4
3
  from collections.abc import Generator
5
4
  from contextlib import contextmanager
6
5
  from typing import Any
@@ -17,10 +16,9 @@ from pydantic import ValidationError
17
16
  from digitalkin.grpc_servers.utils.exceptions import ServerError
18
17
  from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
19
18
  from digitalkin.grpc_servers.utils.models import ClientConfig
19
+ from digitalkin.logger import logger
20
20
  from digitalkin.services.setup.setup_strategy import SetupData, SetupServiceError, SetupStrategy, SetupVersionData
21
21
 
22
- logger = logging.getLogger(__name__)
23
-
24
22
 
25
23
  class GrpcSetup(SetupStrategy, GrpcClientWrapper):
26
24
  """This class implements the gRPC setup service."""
@@ -32,7 +30,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
32
30
  """
33
31
  channel = self._init_channel(config)
34
32
  self.stub = setup_service_pb2_grpc.SetupServiceStub(channel)
35
- logger.info("Channel client 'setup' initialized succesfully")
33
+ logger.debug("Channel client 'setup' initialized succesfully")
36
34
 
37
35
  @contextmanager
38
36
  def _handle_grpc_errors(self, operation: str) -> Generator[Any, Any, Any]: # noqa: PLR6301
@@ -89,7 +87,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
89
87
  current_setup_version=setup_pb2.SetupVersion(**valid_data.current_setup_version.model_dump()),
90
88
  )
91
89
  response = self.exec_grpc_query("CreateSetup", request)
92
- logger.info("Setup '%s' query sent successfully", valid_data.name)
90
+ logger.debug("Setup '%s' query sent successfully", valid_data.name)
93
91
  return response
94
92
 
95
93
  def get_setup(self, setup_dict: dict[str, Any]) -> SetupData:
@@ -146,7 +144,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
146
144
  current_setup_version=current_setup_version,
147
145
  )
148
146
  response = self.exec_grpc_query("UpdateSetup", request)
149
- logger.info("Setup '%s' query sent successfully", valid_data.name)
147
+ logger.debug("Setup '%s' query sent successfully", valid_data.name)
150
148
  return getattr(response, "success", False)
151
149
 
152
150
  def delete_setup(self, setup_dict: dict[str, Any]) -> bool:
@@ -170,7 +168,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
170
168
  raise ValidationError(msg)
171
169
  request = setup_pb2.DeleteSetupRequest(setup_id=setup_id)
172
170
  response = self.exec_grpc_query("DeleteSetup", request)
173
- logger.info("Setup '%s' query sent successfully", setup_id)
171
+ logger.debug("Setup '%s' query sent successfully", setup_id)
174
172
  return getattr(response, "success", False)
175
173
 
176
174
  def create_setup_version(self, setup_version_dict: dict[str, Any]) -> str:
@@ -196,7 +194,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
196
194
  version=valid_data.version,
197
195
  content=content_struct,
198
196
  )
199
- logger.info(
197
+ logger.debug(
200
198
  "Setup Version '%s' for setup '%s' query sent successfully",
201
199
  valid_data.version,
202
200
  valid_data.setup_id,
@@ -275,7 +273,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
275
273
  content=content_struct,
276
274
  )
277
275
  response = self.exec_grpc_query("UpdateSetupVersion", request)
278
- logger.info(
276
+ logger.debug(
279
277
  "Setup Version '%s' for setup '%s' query sent successfully",
280
278
  valid_data.id,
281
279
  valid_data.setup_id,
@@ -303,5 +301,5 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
303
301
  raise ValidationError(msg)
304
302
  request = setup_pb2.DeleteSetupVersionRequest(setup_version_id=setup_version_id)
305
303
  response = self.exec_grpc_query("DeleteSetupVersion", request)
306
- logger.info("Setup Version '%s' query sent successfully", setup_version_id)
304
+ logger.debug("Setup Version '%s' query sent successfully", setup_version_id)
307
305
  return getattr(response, "success", False)
@@ -2,16 +2,18 @@
2
2
 
3
3
  import datetime
4
4
  import json
5
- import logging
6
5
  import tempfile
7
6
  from pathlib import Path
8
7
  from typing import Any
9
8
 
10
9
  from pydantic import BaseModel
11
10
 
12
- from digitalkin.services.storage.storage_strategy import DataType, StorageRecord, StorageStrategy
13
-
14
- logger = logging.getLogger(__name__)
11
+ from digitalkin.logger import logger
12
+ from digitalkin.services.storage.storage_strategy import (
13
+ DataType,
14
+ StorageRecord,
15
+ StorageStrategy,
16
+ )
15
17
 
16
18
 
17
19
  class DefaultStorage(StorageStrategy):
@@ -80,7 +82,11 @@ class DefaultStorage(StorageStrategy):
80
82
  """Atomically write `self.storage` back to disk as JSON."""
81
83
  self.storage_file.parent.mkdir(parents=True, exist_ok=True)
82
84
  with tempfile.NamedTemporaryFile(
83
- mode="w", encoding="utf-8", delete=False, dir=str(self.storage_file.parent), suffix=".tmp"
85
+ mode="w",
86
+ encoding="utf-8",
87
+ delete=False,
88
+ dir=str(self.storage_file.parent),
89
+ suffix=".tmp",
84
90
  ) as temp:
85
91
  try:
86
92
  # Convert storage to a serializable format
@@ -209,12 +215,13 @@ class DefaultStorage(StorageStrategy):
209
215
  def __init__(
210
216
  self,
211
217
  mission_id: str,
218
+ setup_version_id: str,
212
219
  config: dict[str, type[BaseModel]],
213
220
  storage_file_path: str = "local_storage",
214
221
  **kwargs, # noqa: ANN003, ARG002
215
222
  ) -> None:
216
223
  """Initialize the storage."""
217
- super().__init__(mission_id=mission_id, config=config)
224
+ super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
218
225
  self.storage_file_path = f"{self.mission_id}_{storage_file_path}.json"
219
226
  self.storage_file = Path(self.storage_file_path)
220
227
  self.storage = self._load_from_file()
@@ -1,7 +1,5 @@
1
1
  """This module implements the default storage strategy."""
2
2
 
3
- import logging
4
-
5
3
  from digitalkin_proto.digitalkin.storage.v2 import data_pb2, storage_service_pb2_grpc
6
4
  from google.protobuf import json_format
7
5
  from google.protobuf.struct_pb2 import Struct
@@ -9,9 +7,13 @@ from pydantic import BaseModel
9
7
 
10
8
  from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
11
9
  from digitalkin.grpc_servers.utils.models import ClientConfig
12
- from digitalkin.services.storage.storage_strategy import DataType, StorageRecord, StorageServiceError, StorageStrategy
13
-
14
- logger = logging.getLogger(__name__)
10
+ from digitalkin.logger import logger
11
+ from digitalkin.services.storage.storage_strategy import (
12
+ DataType,
13
+ StorageRecord,
14
+ StorageServiceError,
15
+ StorageStrategy,
16
+ )
15
17
 
16
18
 
17
19
  class GrpcStorage(StorageStrategy, GrpcClientWrapper):
@@ -73,7 +75,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
73
75
  resp = self.exec_grpc_query("StoreRecord", req)
74
76
  return self._build_record_from_proto(resp.stored_data)
75
77
  except Exception as e:
76
- logger.exception("gRPC StoreRecord failed for %s:%s", record.collection, record.record_id)
78
+ logger.exception(
79
+ "gRPC StoreRecord failed for %s:%s",
80
+ record.collection,
81
+ record.record_id,
82
+ )
77
83
  raise StorageServiceError(str(e)) from e
78
84
 
79
85
  def _read(self, collection: str, record_id: str) -> StorageRecord | None:
@@ -94,7 +100,12 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
94
100
  logger.exception("gRPC ReadRecord failed for %s:%s", collection, record_id)
95
101
  return None
96
102
 
97
- def _update(self, collection: str, record_id: str, data: BaseModel) -> StorageRecord | None:
103
+ def _update(
104
+ self,
105
+ collection: str,
106
+ record_id: str,
107
+ data: BaseModel,
108
+ ) -> StorageRecord | None:
98
109
  """Overwrite a document via gRPC.
99
110
 
100
111
  Args:
@@ -138,7 +149,11 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
138
149
  )
139
150
  self.exec_grpc_query("RemoveRecord", req)
140
151
  except Exception:
141
- logger.exception("gRPC RemoveRecord failed for %s:%s", collection, record_id)
152
+ logger.exception(
153
+ "gRPC RemoveRecord failed for %s:%s",
154
+ collection,
155
+ record_id,
156
+ )
142
157
  return False
143
158
  return True
144
159
 
@@ -185,12 +200,13 @@ class GrpcStorage(StorageStrategy, GrpcClientWrapper):
185
200
  def __init__(
186
201
  self,
187
202
  mission_id: str,
203
+ setup_version_id: str,
188
204
  config: dict[str, type[BaseModel]],
189
205
  client_config: ClientConfig,
190
206
  **kwargs, # noqa: ANN003, ARG002
191
207
  ) -> None:
192
208
  """Initialize the storage."""
193
- super().__init__(mission_id=mission_id, config=config)
209
+ super().__init__(mission_id=mission_id, setup_version_id=setup_version_id, config=config)
194
210
 
195
211
  channel = self._init_channel(client_config)
196
212
  self.stub = storage_service_pb2_grpc.StorageServiceStub(channel)
@@ -163,14 +163,15 @@ class StorageStrategy(BaseStrategy, ABC):
163
163
  True if the deletion was successful, False otherwise
164
164
  """
165
165
 
166
- def __init__(self, mission_id: str, config: dict[str, type[BaseModel]]) -> None:
166
+ def __init__(self, mission_id: str, setup_version_id: str, config: dict[str, type[BaseModel]]) -> None:
167
167
  """Initialize the storage strategy.
168
168
 
169
169
  Args:
170
170
  mission_id: The ID of the mission this strategy is associated with
171
+ setup_version_id: The ID of the setup version
171
172
  config: A dictionary mapping names to Pydantic model classes
172
173
  """
173
- super().__init__(mission_id)
174
+ super().__init__(mission_id, setup_version_id)
174
175
  # Schema configuration mapping keys to model classes
175
176
  self.config: dict[str, type[BaseModel]] = config
176
177
 
@@ -1,15 +1,13 @@
1
1
  """ArgParser and Action classes to ease command lines arguments settings."""
2
2
 
3
3
  import logging
4
- import os
5
- from argparse import Action, ArgumentParser, Namespace, _HelpAction, _SubParsersAction # noqa: PLC2701
4
+ from argparse import ArgumentParser, Namespace, _HelpAction, _SubParsersAction # noqa: PLC2701
6
5
  from collections.abc import Sequence
7
6
  from typing import Any
8
7
 
9
- from digitalkin.services.services_models import ServicesMode
8
+ from digitalkin.logger import logger
10
9
 
11
- logging.getLogger().setLevel(logging.INFO)
12
- logger = logging.getLogger(__name__)
10
+ logger.setLevel(logging.INFO)
13
11
 
14
12
 
15
13
  class ArgParser:
@@ -81,11 +79,9 @@ class ArgParser:
81
79
  def _add_parser_args(self, parser: ArgumentParser) -> None:
82
80
  parser.add_argument("-h", "--help", action=self.HelpAction, help="help usage")
83
81
 
84
- @staticmethod
85
- def _add_exclusive_args(parser: ArgumentParser) -> None: ...
82
+ def _add_exclusive_args(self, parser: ArgumentParser) -> None: ...
86
83
 
87
- @staticmethod
88
- def _add_subparser_args(parser: ArgumentParser) -> None: ...
84
+ def _add_subparser_args(self, parser: ArgumentParser) -> None: ...
89
85
 
90
86
  def __init__(self, prog: str = "PROG") -> None:
91
87
  """Create prser and call abstract methods."""
@@ -94,42 +90,3 @@ class ArgParser:
94
90
  self._add_exclusive_args(self.parser)
95
91
  self._add_subparser_args(self.parser)
96
92
  self.args, _ = self.parser.parse_known_args()
97
-
98
-
99
- class DevelopmentModeMappingAction(Action):
100
- """."""
101
-
102
- def __init__(
103
- self,
104
- env_var: str,
105
- required: bool = True, # noqa: FBT001, FBT002
106
- default: str | None = None,
107
- **kwargs: dict[str, Any],
108
- ) -> None:
109
- """."""
110
- default = ServicesMode(os.environ.get(env_var, default))
111
-
112
- if required and default:
113
- required = False
114
- super().__init__(default=default, required=required, **kwargs) # type: ignore
115
-
116
- def __call__(
117
- self,
118
- parser: ArgumentParser, # noqa: ARG002
119
- namespace: Namespace,
120
- values: str | Sequence[Any] | None,
121
- option_string: str | None = None, # noqa: ARG002
122
- ) -> None:
123
- """Set the attribute to the corresponding class.
124
-
125
- Raises:
126
- TypeError: if the value is not a string.
127
- """
128
- # Check if the value is a string and convert it to lowercase
129
- if isinstance(values, str):
130
- values = values.lower()
131
- else:
132
- msg = "values must be a string"
133
- raise TypeError(msg)
134
- mode = ServicesMode(values)
135
- setattr(namespace, self.dest, mode)
@@ -0,0 +1,51 @@
1
+ """ArgParser and Action classes to ease command lines arguments settings."""
2
+
3
+ import logging
4
+ import os
5
+ from argparse import Action, ArgumentParser, Namespace
6
+ from collections.abc import Sequence
7
+ from typing import Any
8
+
9
+ from digitalkin.logger import logger
10
+ from digitalkin.services.services_models import ServicesMode
11
+
12
+ logger.setLevel(logging.INFO)
13
+
14
+
15
+ class DevelopmentModeMappingAction(Action):
16
+ """."""
17
+
18
+ def __init__(
19
+ self,
20
+ env_var: str,
21
+ required: bool = True, # noqa: FBT001, FBT002
22
+ default: str | None = None,
23
+ **kwargs: dict[str, Any],
24
+ ) -> None:
25
+ """."""
26
+ default = ServicesMode(os.environ.get(env_var, default))
27
+
28
+ if required and default:
29
+ required = False
30
+ super().__init__(default=default, required=required, **kwargs) # type: ignore
31
+
32
+ def __call__(
33
+ self,
34
+ parser: ArgumentParser, # noqa: ARG002
35
+ namespace: Namespace,
36
+ values: str | Sequence[Any] | None,
37
+ option_string: str | None = None, # noqa: ARG002
38
+ ) -> None:
39
+ """Set the attribute to the corresponding class.
40
+
41
+ Raises:
42
+ TypeError: if the value is not a string.
43
+ """
44
+ # Check if the value is a string and convert it to lowercase
45
+ if isinstance(values, str):
46
+ values = values.lower()
47
+ else:
48
+ msg = "values must be a string"
49
+ raise TypeError(msg)
50
+ mode = ServicesMode(values)
51
+ setattr(namespace, self.dest, mode)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: SDK to build kin used in DigitalKin
5
5
  Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
6
6
  License: Attribution-NonCommercial-ShareAlike 4.0 International
@@ -452,29 +452,36 @@ Classifier: License :: Other/Proprietary License
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: digitalkin-proto>=0.1.8
455
+ Requires-Dist: digitalkin-proto>=0.1.10
456
456
  Requires-Dist: grpcio-health-checking>=1.71.0
457
457
  Requires-Dist: grpcio-reflection>=1.71.0
458
458
  Requires-Dist: grpcio-status>=1.71.0
459
- Requires-Dist: openai>=1.76.2
460
459
  Requires-Dist: pydantic>=2.11.4
461
460
  Provides-Extra: dev
462
- Requires-Dist: pytest>=8.3.4; extra == "dev"
463
- Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
464
- Requires-Dist: pytest-cov>=6.1.0; extra == "dev"
465
- Requires-Dist: typos>=1.31.2; extra == "dev"
466
- Requires-Dist: ruff>=0.11.7; extra == "dev"
461
+ Requires-Dist: typos>=1.32.0; extra == "dev"
462
+ Requires-Dist: ruff>=0.11.9; extra == "dev"
467
463
  Requires-Dist: mypy>=1.15.0; extra == "dev"
468
464
  Requires-Dist: pyright>=1.1.400; extra == "dev"
469
465
  Requires-Dist: pre-commit>=4.2.0; extra == "dev"
470
466
  Requires-Dist: bump2version>=1.0.1; extra == "dev"
471
467
  Requires-Dist: build>=1.2.2; extra == "dev"
472
468
  Requires-Dist: twine>=6.1.0; extra == "dev"
473
- Requires-Dist: cryptography>=44.0.2; extra == "dev"
474
- Requires-Dist: grpcio-testing>=1.71.0; extra == "dev"
475
- Requires-Dist: freezegun>=1.5.1; extra == "dev"
469
+ Requires-Dist: cryptography>=44.0.3; extra == "dev"
470
+ Requires-Dist: taskiq[reload]>=0.11.17; extra == "dev"
476
471
  Provides-Extra: examples
477
- Requires-Dist: openai>=1.66.3; extra == "examples"
472
+ Requires-Dist: openai>=1.75.0; extra == "examples"
473
+ Provides-Extra: tests
474
+ Requires-Dist: freezegun>=1.5.1; extra == "tests"
475
+ Requires-Dist: hdrhistogram>=0.10.3; extra == "tests"
476
+ Requires-Dist: grpcio-testing>=1.71.0; extra == "tests"
477
+ Requires-Dist: psutil>=7.0.0; extra == "tests"
478
+ Requires-Dist: pytest>=8.3.4; extra == "tests"
479
+ Requires-Dist: pytest-asyncio>=0.26.0; extra == "tests"
480
+ Requires-Dist: pytest-cov>=6.1.0; extra == "tests"
481
+ Provides-Extra: taskiq
482
+ Requires-Dist: rstream>=0.20.9; extra == "taskiq"
483
+ Requires-Dist: taskiq-aio-pika>=0.4.2; extra == "taskiq"
484
+ Requires-Dist: taskiq-redis>=1.0.8; extra == "taskiq"
478
485
  Dynamic: license-file
479
486
 
480
487
  # DigitalKin Python SDK
@@ -509,6 +516,16 @@ To install the DigitalKin SDK, simply run:
509
516
  pip install digitalkin
510
517
  ```
511
518
 
519
+ **Optional Taskiq Integration**: Asynchronous task execution powered by Taskiq, backed by RabbitMQ and Redis
520
+ To enable the Rabbitmq streaming capabilities, run:
521
+
522
+ ```sh
523
+ sudo rabbitmq-plugins enable rabbitmq_stream
524
+
525
+ # Core + Taskiq integration (RabbitMQ broker)
526
+ pip install digitalkin[taskiq]
527
+ ```
528
+
512
529
  ## 🛠️ Usage
513
530
 
514
531
  ### Basic Import
@@ -519,6 +536,20 @@ Start by importing the necessary modules:
519
536
  import digitalkin
520
537
  ```
521
538
 
539
+ ## Features
540
+
541
+ ### Taskiq with RabbitMQ
542
+
543
+ TaskIQ intergration allows the module to scale for heavy CPU tasks by having the request's stateless module in a new instance.
544
+
545
+ - **Decoupled Scalability**: RabbitMQ brokers messages, letting producers and consumers scale independently.
546
+ - **Reliability**: Durable queues, acknowledgements, and dead-lettering ensure tasks aren’t lost.
547
+ - **Concurrency Control**: Taskiq’s worker pool manages parallel execution without custom schedulers.
548
+ - **Flexibility**: Built-in retries, exponential backoff, and Redis result-backend for resilient workflows.
549
+ - **Ecosystem**: Battle-tested `aio-pika` AMQP client plus Taskiq’s decorator-based API.
550
+
551
+ By combining Taskiq’s async API with RabbitMQ’s guarantees, you get a robust, production-ready queue with minimal boilerplate.
552
+
522
553
  ## 👷‍♂️ Development
523
554
 
524
555
  ### Prerequisites