digitalkin 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) 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 +124 -0
  6. base_server/server_async_secure.py +142 -0
  7. base_server/server_sync_insecure.py +102 -0
  8. base_server/server_sync_secure.py +121 -0
  9. digitalkin/__init__.py +1 -11
  10. digitalkin/__version__.py +1 -4
  11. digitalkin/{grpc → grpc_servers}/__init__.py +1 -13
  12. digitalkin/{grpc → grpc_servers}/_base_server.py +3 -3
  13. digitalkin/{grpc → grpc_servers}/module_server.py +30 -12
  14. digitalkin/{grpc → grpc_servers}/module_servicer.py +30 -14
  15. digitalkin/{grpc → grpc_servers}/registry_server.py +6 -4
  16. digitalkin/{grpc → grpc_servers}/registry_servicer.py +8 -2
  17. digitalkin/{grpc → grpc_servers}/utils/factory.py +6 -4
  18. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +68 -0
  19. digitalkin/{grpc → grpc_servers}/utils/models.py +1 -1
  20. digitalkin/models/__init__.py +1 -4
  21. digitalkin/models/module/__init__.py +8 -2
  22. digitalkin/models/module/module_types.py +10 -0
  23. digitalkin/models/services/__init__.py +0 -5
  24. digitalkin/modules/__init__.py +3 -3
  25. digitalkin/modules/_base_module.py +64 -27
  26. digitalkin/modules/archetype_module.py +2 -6
  27. digitalkin/modules/job_manager.py +46 -28
  28. digitalkin/modules/tool_module.py +3 -7
  29. digitalkin/modules/trigger_module.py +2 -7
  30. digitalkin/services/__init__.py +7 -9
  31. digitalkin/services/agent/__init__.py +2 -2
  32. digitalkin/services/agent/agent_strategy.py +3 -6
  33. digitalkin/services/agent/default_agent.py +1 -4
  34. digitalkin/services/base_strategy.py +18 -0
  35. digitalkin/services/cost/__init__.py +4 -3
  36. digitalkin/services/cost/cost_strategy.py +35 -5
  37. digitalkin/services/cost/default_cost.py +22 -5
  38. digitalkin/services/cost/grpc_cost.py +81 -0
  39. digitalkin/services/filesystem/__init__.py +4 -3
  40. digitalkin/services/filesystem/default_filesystem.py +197 -17
  41. digitalkin/services/filesystem/filesystem_strategy.py +54 -15
  42. digitalkin/services/filesystem/grpc_filesystem.py +209 -0
  43. digitalkin/services/identity/__init__.py +2 -2
  44. digitalkin/services/identity/default_identity.py +1 -1
  45. digitalkin/services/identity/identity_strategy.py +3 -1
  46. digitalkin/services/registry/__init__.py +2 -2
  47. digitalkin/services/registry/default_registry.py +1 -4
  48. digitalkin/services/registry/registry_strategy.py +3 -6
  49. digitalkin/services/services_config.py +176 -0
  50. digitalkin/services/services_models.py +61 -0
  51. digitalkin/services/setup/default_setup.py +222 -0
  52. digitalkin/services/setup/grpc_setup.py +307 -0
  53. digitalkin/services/setup/setup_strategy.py +145 -0
  54. digitalkin/services/snapshot/__init__.py +2 -2
  55. digitalkin/services/snapshot/default_snapshot.py +1 -1
  56. digitalkin/services/snapshot/snapshot_strategy.py +3 -4
  57. digitalkin/services/storage/__init__.py +4 -3
  58. digitalkin/services/storage/default_storage.py +184 -57
  59. digitalkin/services/storage/grpc_storage.py +76 -170
  60. digitalkin/services/storage/storage_strategy.py +195 -24
  61. digitalkin/utils/arg_parser.py +16 -17
  62. {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/METADATA +8 -7
  63. digitalkin-0.2.0.dist-info/RECORD +78 -0
  64. {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/WHEEL +1 -1
  65. digitalkin-0.2.0.dist-info/top_level.txt +3 -0
  66. modules/__init__.py +0 -0
  67. modules/minimal_llm_module.py +162 -0
  68. modules/storage_module.py +187 -0
  69. modules/text_transform_module.py +201 -0
  70. digitalkin/services/default_service.py +0 -13
  71. digitalkin/services/development_service.py +0 -10
  72. digitalkin/services/service_provider.py +0 -27
  73. digitalkin-0.1.1.dist-info/RECORD +0 -59
  74. digitalkin-0.1.1.dist-info/top_level.txt +0 -1
  75. /digitalkin/{grpc → grpc_servers}/utils/exceptions.py +0 -0
  76. /digitalkin/{grpc → grpc_servers}/utils/types.py +0 -0
  77. {digitalkin-0.1.1.dist-info → digitalkin-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,42 +1,213 @@
1
1
  """This module contains the abstract base class for storage strategies."""
2
2
 
3
+ import datetime
3
4
  from abc import ABC, abstractmethod
4
- from typing import Any
5
+ from enum import Enum
6
+ from typing import Any, Literal, TypeGuard
5
7
 
8
+ from pydantic import BaseModel, Field
6
9
 
7
- class StorageStrategy(ABC):
8
- """Abstract base class for storage strategies."""
10
+ from digitalkin.services.base_strategy import BaseStrategy
9
11
 
10
- def __init__(self) -> None:
11
- """Initialize the storage strategy."""
12
12
 
13
- def __post_init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003
14
- """Initialize the storage strategy."""
13
+ class StorageServiceError(Exception):
14
+ """Base exception for Setup service errors."""
15
15
 
16
- @abstractmethod
17
- def connect(self) -> bool:
18
- """Establish connection to the database."""
19
16
 
20
- @abstractmethod
21
- def disconnect(self) -> bool:
22
- """Close connection to the database."""
17
+ class DataType(Enum):
18
+ """Enum defining the types of data that can be stored."""
23
19
 
24
- @abstractmethod
25
- def create(self, table: str, data: dict[str, Any]) -> str:
26
- """Create a new record in the database."""
20
+ OUTPUT = "OUTPUT"
21
+ VIEW = "VIEW"
22
+ LOGS = "LOGS"
23
+ OTHER = "OTHER"
24
+
25
+
26
+ class StorageRecord(BaseModel):
27
+ """Container for stored records with metadata."""
28
+
29
+ # Metadata
30
+ mission_id: str = Field(description="The ID of the mission this record is associated with")
31
+ name: str = Field(description="The name of the record")
32
+ creation_date: datetime.datetime | None = Field(default=None, description="The date the record was created")
33
+ update_date: datetime.datetime | None = Field(default=None, description="The date the record was last updated")
34
+ data_type: DataType = Field(default=DataType.OUTPUT, description="The type of data stored")
35
+ # Actual data payload
36
+ data: BaseModel = Field(description="The data stored in the record")
37
+
38
+
39
+ class StorageStrategy(BaseStrategy, ABC):
40
+ """Abstract base class for storage strategies.
41
+
42
+ This strategy defines how data is stored and retrieved, with
43
+ type validation through registered Pydantic models.
44
+ """
45
+
46
+ def __init__(self, mission_id: str, config: dict[str, type[BaseModel]]) -> None:
47
+ """Initialize the storage strategy.
48
+
49
+ Args:
50
+ mission_id: The ID of the mission this strategy is associated with
51
+ config: A dictionary mapping names to Pydantic model classes
52
+ """
53
+ super().__init__(mission_id)
54
+ # Schema configuration mapping keys to model classes
55
+ self.config: dict[str, type[BaseModel]] = config
56
+
57
+ @staticmethod
58
+ def _is_valid_data_type_name(value: str) -> TypeGuard[str]:
59
+ return value in DataType.__members__
27
60
 
28
61
  @abstractmethod
29
- def get(self, table: str, data: dict[str, Any]) -> list[dict[str, Any]]:
30
- """Get records from the database."""
62
+ def _store(self, record: StorageRecord) -> StorageRecord:
63
+ """Store a new record in the storage.
64
+
65
+ Args:
66
+ record: The record to store
67
+
68
+ Returns:
69
+ The ID of the created record
70
+ """
71
+
72
+ def store(
73
+ self,
74
+ name: str,
75
+ data: dict[str, Any],
76
+ data_type: Literal["OUTPUT", "VIEW", "LOGS", "OTHER"] = "OUTPUT",
77
+ ) -> StorageRecord:
78
+ """Store a new record in the storage.
79
+
80
+ Args:
81
+ name: The unique name to store the data under
82
+ data: The data to store
83
+ data_type: The type of data being stored (default: OUTPUT)
84
+
85
+ Returns:
86
+ The ID of the created record
87
+
88
+ Raises:
89
+ ValueError: If the data type is invalid or if validation fails
90
+ """
91
+ if not self._is_valid_data_type_name(data_type):
92
+ msg = f"Invalid data type '{data_type}'. Must be one of {list(DataType.__members__.keys())}"
93
+ raise ValueError(msg)
94
+ data_type_enum = DataType[data_type]
95
+ validated_data = self._validate_data(name, {**data, "mission_id": self.mission_id})
96
+ record = self._create_storage_record(name, validated_data, data_type_enum)
97
+ return self._store(record)
31
98
 
32
99
  @abstractmethod
33
- def update(self, table: str, data: dict[str, Any]) -> int:
34
- """Update records in the database."""
100
+ def _read(self, name: str) -> StorageRecord | None:
101
+ """Get records from storage by key.
102
+
103
+ Args:
104
+ name: The unique name to retrieve data for
105
+
106
+ Returns:
107
+ A storage record with validated data
108
+ """
109
+
110
+ def read(self, name: str) -> StorageRecord | None:
111
+ """Get records from storage by key.
112
+
113
+ Args:
114
+ name: The unique name to retrieve data for
115
+
116
+ Returns:
117
+ A storage record with validated data
118
+ """
119
+ return self._read(name)
35
120
 
36
121
  @abstractmethod
37
- def delete(self, table: str, data: dict[str, Any]) -> int:
38
- """Delete records from the database."""
122
+ def _modify(self, name: str, data: BaseModel) -> StorageRecord | None:
123
+ """Update a record in the storage.
124
+
125
+ Args:
126
+ name: The unique name for the record type
127
+ data: The new data to store
128
+
129
+ Returns:
130
+ StorageRecord: The modified record
131
+ """
132
+
133
+ def modify(self, name: str, data: dict[str, Any]) -> StorageRecord | None:
134
+ """Update a record in the storage (overwrite all the data).
135
+
136
+ Args:
137
+ name: The unique name for the record type
138
+ data: The new data to store
139
+
140
+ Returns:
141
+ StorageRecord: The modified record
142
+ """
143
+ validated_data = self._validate_data(name, data)
144
+ return self._modify(name, validated_data)
39
145
 
40
146
  @abstractmethod
41
- def get_all(self) -> dict[str, list[dict[str, Any]]]:
42
- """Get all records from the database."""
147
+ def _remove(self, name: str) -> bool:
148
+ """Delete a record from the storage.
149
+
150
+ Args:
151
+ name: The unique name for the record type
152
+
153
+ Returns:
154
+ True if the deletion was successful, False otherwise
155
+ """
156
+
157
+ def remove(self, name: str) -> bool:
158
+ """Delete a record from the storage.
159
+
160
+ Args:
161
+ name: The unique name for the record type
162
+
163
+ Returns:
164
+ True if the deletion was successful, False otherwise
165
+ """
166
+ return self._remove(name)
167
+
168
+ def _validate_data(self, name: str, data: dict[str, Any]) -> BaseModel:
169
+ """Validate data against the model schema for the given key.
170
+
171
+ Args:
172
+ name: The unique name to get the model type for
173
+ data: The data to validate
174
+
175
+ Returns:
176
+ A validated model instance
177
+
178
+ Raises:
179
+ ValueError: If the key has no associated model or validation fails
180
+ """
181
+ model_cls = self.config.get(name)
182
+ if not model_cls:
183
+ msg = f"No model schema defined for name: {name}"
184
+ raise ValueError(msg)
185
+
186
+ try:
187
+ return model_cls.model_validate(data)
188
+ except Exception as e:
189
+ msg = f"Data validation failed for key '{name}': {e!s}"
190
+ raise ValueError(msg) from e
191
+
192
+ def _create_storage_record(
193
+ self,
194
+ name: str,
195
+ validated_data: BaseModel,
196
+ data_type: DataType,
197
+ ) -> StorageRecord:
198
+ """Create a storage record with metadata.
199
+
200
+ Args:
201
+ name: The unique name for the record
202
+ validated_data: The validated data model
203
+ data_type: The type of data
204
+
205
+ Returns:
206
+ A complete storage record with metadata
207
+ """
208
+ return StorageRecord(
209
+ mission_id=self.mission_id,
210
+ name=name,
211
+ data=validated_data,
212
+ data_type=data_type,
213
+ )
@@ -6,7 +6,7 @@ from argparse import Action, ArgumentParser, Namespace, _HelpAction, _SubParsers
6
6
  from collections.abc import Sequence
7
7
  from typing import Any
8
8
 
9
- from digitalkin.services.service_provider import ServiceProvider
9
+ from digitalkin.services.services_models import ServicesMode
10
10
 
11
11
  logging.getLogger().setLevel(logging.INFO)
12
12
  logger = logging.getLogger(__name__)
@@ -99,38 +99,37 @@ class ArgParser:
99
99
  class DevelopmentModeMappingAction(Action):
100
100
  """."""
101
101
 
102
- default: ServiceProvider | None
103
- class_mapping: dict[str, ServiceProvider]
104
-
105
102
  def __init__(
106
103
  self,
107
104
  env_var: str,
108
- class_mapping: dict[str, ServiceProvider],
109
105
  required: bool = True, # noqa: FBT001, FBT002
110
106
  default: str | None = None,
111
107
  **kwargs: dict[str, Any],
112
108
  ) -> None:
113
109
  """."""
114
- default = class_mapping.get(os.environ.get(env_var, default), None) # type: ignore
115
- if default is None:
116
- logger.error("Invalid default value: %s, for the Service Provider in the module", default)
110
+ default = ServicesMode(os.environ.get(env_var, default))
117
111
 
118
112
  if required and default:
119
113
  required = False
120
- self.class_mapping = class_mapping
121
114
  super().__init__(default=default, required=required, **kwargs) # type: ignore
122
115
 
123
116
  def __call__(
124
117
  self,
125
- parser: ArgumentParser,
118
+ parser: ArgumentParser, # noqa: ARG002
126
119
  namespace: Namespace,
127
120
  values: str | Sequence[Any] | None,
128
121
  option_string: str | None = None, # noqa: ARG002
129
122
  ) -> None:
130
- """Set the attribute to the corresponding class."""
131
- if values not in self.class_mapping:
132
- logger.error("Invalid mode: %s, dest: %s not set!", values, self.dest)
133
- parser.error(f"Invalid mode: {values}")
134
-
135
- values = self.class_mapping[values] # type: ignore
136
- setattr(namespace, self.dest, values)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.1.1
3
+ Version: 0.2.0
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,25 +452,26 @@ 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.0.8
455
+ Requires-Dist: digitalkin-proto>=0.1.5
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: pydantic>=2.10.6
459
+ Requires-Dist: pydantic>=2.11.3
460
460
  Provides-Extra: dev
461
461
  Requires-Dist: pytest>=8.3.4; extra == "dev"
462
- Requires-Dist: pytest-asyncio>=0.25.3; extra == "dev"
463
- Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
464
- Requires-Dist: typos>=1.30.2; extra == "dev"
462
+ Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
463
+ Requires-Dist: pytest-cov>=6.1.0; extra == "dev"
464
+ Requires-Dist: typos>=1.31.1; extra == "dev"
465
465
  Requires-Dist: ruff>=0.11.2; extra == "dev"
466
466
  Requires-Dist: mypy>=1.15.0; extra == "dev"
467
- Requires-Dist: pyright>=1.1.397; extra == "dev"
467
+ Requires-Dist: pyright>=1.1.398; extra == "dev"
468
468
  Requires-Dist: pre-commit>=4.2.0; extra == "dev"
469
469
  Requires-Dist: bump2version>=1.0.1; extra == "dev"
470
470
  Requires-Dist: build>=1.2.2; extra == "dev"
471
471
  Requires-Dist: twine>=6.1.0; extra == "dev"
472
472
  Requires-Dist: cryptography>=44.0.2; extra == "dev"
473
473
  Requires-Dist: grpcio-testing>=1.71.0; extra == "dev"
474
+ Requires-Dist: freezegun>=1.5.1; extra == "dev"
474
475
  Provides-Extra: examples
475
476
  Requires-Dist: openai>=1.66.3; extra == "examples"
476
477
  Dynamic: license-file
@@ -0,0 +1,78 @@
1
+ base_server/__init__.py,sha256=gs8t9Dg0dNVHRdYYbEQY8bn8tzEUv2zE6eBhKNPG3kU,88
2
+ base_server/server_async_insecure.py,sha256=Rvj5Xj8tDMglAj6aOGlQdMeD-FL97_OBEhHYHxxQrVo,3887
3
+ base_server/server_async_secure.py,sha256=SV_CqEmD6YNHHP2Y369T2PLPJ-9JG3bvXytchX_Ensk,4684
4
+ base_server/server_sync_insecure.py,sha256=VgSH8YghagK3fiwHhc7d6__zjN6lj_FgBUponFbOoxM,3127
5
+ base_server/server_sync_secure.py,sha256=MMkq67vAZNiDLJSySjoXtHIEIK1pAlRz57n75Egnndk,3939
6
+ base_server/mock/__init__.py,sha256=YZFT-F1l_TpvJYuIPX-7kTeE1CfOjhx9YmNRXVoi-jQ,143
7
+ base_server/mock/mock_pb2.py,sha256=sETakcS3PAAm4E-hTCV1jIVaQTPEAIoVVHupB8Z_k7Y,1843
8
+ base_server/mock/mock_pb2_grpc.py,sha256=BbOT70H6q3laKgkHfOx1QdfmCS_HxCY4wCOX84YAdG4,3180
9
+ digitalkin/__init__.py,sha256=7LLBAba0th-3SGqcpqFO-lopWdUkVLKzLZiMtB-mW3M,162
10
+ digitalkin/__version__.py,sha256=c4f1Pc-TEf7O1UkIJonXwVhP7WicVMKecmjTFvgmDLQ,190
11
+ digitalkin/logger.py,sha256=9cDgyJV2QXXT8F--xRODFlZyDgjuTTXNdpCU3GdqCsk,382
12
+ digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ digitalkin/grpc_servers/__init__.py,sha256=0cJBlwipSmFdXkyH3T0i6OJ1WpAtNsZgYX7JaSnkbtg,804
14
+ digitalkin/grpc_servers/_base_server.py,sha256=ec4xmgAuOMVg45a63O_PEa2T7mI4tJ6boxcXauFyZ5g,18649
15
+ digitalkin/grpc_servers/module_server.py,sha256=0HVOygMGP82WDU8I_04ZrFQ1Tqme0bM5nyqMVmoOL1Y,9072
16
+ digitalkin/grpc_servers/module_servicer.py,sha256=togOyvogAYkBv353S-9X_pP5yI8MvxupOIBSvq39jA4,11816
17
+ digitalkin/grpc_servers/registry_server.py,sha256=AjN-OXLGsJtvddqLKeHosJVdSDqvjfpkQM3JcPl5PSQ,2120
18
+ digitalkin/grpc_servers/registry_servicer.py,sha256=mCAjNhdMq5DozZMEPsJK__DIxePEYxSWV-gAq-Xctk4,16469
19
+ digitalkin/grpc_servers/utils/exceptions.py,sha256=I00OM8p8up20He4dU1fiHsvdLj1DymjR_UmoeUm2MSA,785
20
+ digitalkin/grpc_servers/utils/factory.py,sha256=jm6rFjiqmtSv7BIHNAOxsG9xXtSvWpx9TfzSQiX97MQ,5899
21
+ digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=y9zGzmeESAhrzzQcekA8rE_P9gyPvOjxWwnZAc3fpx4,2435
22
+ digitalkin/grpc_servers/utils/models.py,sha256=AXv4FQ3RRmcr5sZaaZc72ED1LtGJEaxmFIZP0Xlo-PY,5483
23
+ digitalkin/grpc_servers/utils/types.py,sha256=rQ78s4nAet2jy-NIDj_PUWriT0kuGHr_w6ELjmjgBao,539
24
+ digitalkin/models/__init__.py,sha256=hDHtUfswaNh8wo4NZaBItg9JqC0uNSRqXArNWSrGynY,163
25
+ digitalkin/models/module/__init__.py,sha256=ihvRlemJuFvU4armZIL-Vq-zaJx9UrjDoJAVPbEG8jw,345
26
+ digitalkin/models/module/module.py,sha256=vlIeWmH61NVEiJpRx0Crk4iB8hAUV4mG30likSyRFP4,686
27
+ digitalkin/models/module/module_types.py,sha256=SxwzfDDnkyuQe50glVrxN6VPuJCkbB313FRyVZugIMw,312
28
+ digitalkin/models/services/__init__.py,sha256=HsW7MUGFPvH7Ri28WN4BHHBfEQk5dzU_9FOWAc-0lSE,56
29
+ digitalkin/models/services/cost.py,sha256=QTEuFD6xz62nob0z4ksE-INJWcZ-iFiuNW5mvXhpFes,1599
30
+ digitalkin/models/services/storage.py,sha256=cYTVIriGKiprF9OerhSxmc_jM6fUTVwmeon1yQCinkE,143
31
+ digitalkin/modules/__init__.py,sha256=ppYARmhvdVi55ofC0QZerIempSlcJYDeCXhcl4qXObw,278
32
+ digitalkin/modules/_base_module.py,sha256=f4AEZFyl0meT_V6esnEFyUtuN7ZXvhHgIYa1ZOMe7tM,7651
33
+ digitalkin/modules/archetype_module.py,sha256=T2Ehj7EpAC2MO9WQbJv39hqRw7rh3exhVZTEL3JPM8U,421
34
+ digitalkin/modules/job_manager.py,sha256=q48ZJwWmOZ4xJKV2QApI6x_Nfgviej-AoIV8BJQ9B-o,6110
35
+ digitalkin/modules/tool_module.py,sha256=86g0M1wHZ1ReIc7AkKfyjnlGN2QYJBGxrEQpKVlyrZI,421
36
+ digitalkin/modules/trigger_module.py,sha256=kVoI4Gdkw7WWUP5T6hSCNqw5FxibTxL6Tpq9KP7gg78,379
37
+ digitalkin/services/__init__.py,sha256=LqGk_5DJy8Bzz62ajIq9jCeYNKQUIgtSCpafZk15FLc,910
38
+ digitalkin/services/base_strategy.py,sha256=U7J09LkNvTaj4bkX-gF86iU_FcUeHK6AKp4zyUPx6eI,470
39
+ digitalkin/services/services_config.py,sha256=6mNYtw07ZNZbk2b4e3xKEpYfozD_NUZyUY3iiein_10,7256
40
+ digitalkin/services/services_models.py,sha256=5zXkWcfKnXGwQi9sN4OAL3XrgqOcmsTl8ai5Mi4RPsw,1668
41
+ digitalkin/services/agent/__init__.py,sha256=vJc8JN0pdtA8ecypLBeHrwAUIW6H2C8NyW-dk24rTpk,244
42
+ digitalkin/services/agent/agent_strategy.py,sha256=42Q9RciHX6tg3CgDQkbrlIx4h_TX0WIuSpLmCjitVmA,492
43
+ digitalkin/services/agent/default_agent.py,sha256=4N_E_eQxJGOx1KVUUg5jNOje-3ncMxF3ePB-uDuGrJc,345
44
+ digitalkin/services/cost/__init__.py,sha256=Wi9ZB4LSXFsUYgkX-V1UJQkVXYDNDpp8q2dXccR2uRM,303
45
+ digitalkin/services/cost/cost_strategy.py,sha256=KkVLT8X2EY58ul-27Gj6lFx7dsk1SZgg-UunBC0D8Ks,986
46
+ digitalkin/services/cost/default_cost.py,sha256=LaTOBIdGOwh4-9vDlnefm8eKDvSWglJOS7BFLOT_G9Q,811
47
+ digitalkin/services/cost/grpc_cost.py,sha256=k0Df_41dwrowBjtz11eqYQyWCMIjY-wEPEytbo2j4gE,3215
48
+ digitalkin/services/filesystem/__init__.py,sha256=BhwMl_BUvM0d65fmglkp0SVwn3RfYiUOKJgIMnOCaGM,381
49
+ digitalkin/services/filesystem/default_filesystem.py,sha256=tTOsBt0MSjexKgWm657y8BLppMYJtmJIVdqzsSRRaYk,7264
50
+ digitalkin/services/filesystem/filesystem_strategy.py,sha256=VH93jkijFtyVA6XaH_rsfdSyeEpkqea54NAvHFLJDjM,2216
51
+ digitalkin/services/filesystem/grpc_filesystem.py,sha256=rtVfutPoggB1AG6vcxrSMnUOu20h9tlWFfwoY1PMliw,8180
52
+ digitalkin/services/identity/__init__.py,sha256=InkeyLgFYYwItx8mePA8HpfacOMWZwwuc0G4pWtKq9s,270
53
+ digitalkin/services/identity/default_identity.py,sha256=Y2auZHrGSZTIN5D8HyjLvLcNbYFM1CNUE23x7p5VIGw,386
54
+ digitalkin/services/identity/identity_strategy.py,sha256=skappBbds1_qa0Gr24FGrNX1N0_OYhYT1Lh7dUaAirE,429
55
+ digitalkin/services/registry/__init__.py,sha256=Zl4QAkCe9tOmmKGBWVuLQVFepdZiL0ec3VDj27IeyYM,270
56
+ digitalkin/services/registry/default_registry.py,sha256=VnWkF6nHpFxUKuUbZLPqzXqdA6oXmyV_ySpeuOCf_ko,277
57
+ digitalkin/services/registry/registry_strategy.py,sha256=uBXgZIv25jeXbeVO8vWvlNPxxNYu7_KiCw2PoE6AWr8,423
58
+ digitalkin/services/setup/default_setup.py,sha256=x1J6trXhyLC7V2OTe5pRY5mIFkQ4oyi3-aG50a1G1U4,8253
59
+ digitalkin/services/setup/grpc_setup.py,sha256=Bo84gLZSK8DKqHAVSVFOQdtZQkfpp78eNgjJnGhXQfU,12449
60
+ digitalkin/services/setup/setup_strategy.py,sha256=ZnJ_HwWCkHCPrqKekSD5L9y3p8wMwfjQ8sj2hLZq6go,4004
61
+ digitalkin/services/snapshot/__init__.py,sha256=Uzlnzo0CYlSpVsdiI37hW7xQk8hu3YA1fOI6O6MSzB0,270
62
+ digitalkin/services/snapshot/default_snapshot.py,sha256=Mb8QwWRsHh9I_tN0ln_ZiFa1QCZxOVWmuVLemQOTWpc,1058
63
+ digitalkin/services/snapshot/snapshot_strategy.py,sha256=B1TU3V_k9A-OdqBkdyc41-ihnrW5Btcwd1KyQdHT46A,898
64
+ digitalkin/services/storage/__init__.py,sha256=T-ocYLLphudkQgzvG47jBOm5GQsRFRIGA88y7Ur4akg,341
65
+ digitalkin/services/storage/default_storage.py,sha256=bHNPm8nLvytKqKP2ntLkikvqH1qDKOwofrStVJH6PJg,7765
66
+ digitalkin/services/storage/grpc_storage.py,sha256=b9z-2Q7mui8H0yn9GNb_RLu5RoLNFc55Dw2Xe_YklvY,4042
67
+ digitalkin/services/storage/storage_strategy.py,sha256=vGo4aYkEp_GZV11m7vd-xY_Z3gVa5K0gMTzbj2Au_3o,6600
68
+ digitalkin/utils/__init__.py,sha256=sJnY-ZUgsjMfojAjONC1VN14mhgIDnzyOlGkw21rRnM,28
69
+ digitalkin/utils/arg_parser.py,sha256=3YyI6oZhhrlTmPTrzlwpQzbCNWDFAT3pggcLxNtJoc0,4388
70
+ digitalkin-0.2.0.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
71
+ modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
+ modules/minimal_llm_module.py,sha256=JrRPTx7DCYrbmnfkE98xWBU_6x5lr7i8MFP16ATKkfo,5531
73
+ modules/storage_module.py,sha256=HktIy4j8-0w7ugMA7HqcNZymZIGruhchsWKXje9qR4o,6264
74
+ modules/text_transform_module.py,sha256=fAC6r_Ujca1Tz1qdWL4hTPZFn3gFWIVNj5-rytQMObE,7191
75
+ digitalkin-0.2.0.dist-info/METADATA,sha256=692wgNWfg0bqDK5y2cA-UxS3QvpYUEAtBP1XeTno3QE,29095
76
+ digitalkin-0.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
77
+ digitalkin-0.2.0.dist-info/top_level.txt,sha256=5_5e35inSM5YfWNZE21p5wGBojiVtQQML_WzbEk4BRU,31
78
+ digitalkin-0.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.0.2)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,3 @@
1
+ base_server
2
+ digitalkin
3
+ modules
modules/__init__.py ADDED
File without changes
@@ -0,0 +1,162 @@
1
+ """Simple module calling an LLM."""
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+ from typing import Any, ClassVar
6
+
7
+ import grpc
8
+ import openai
9
+ from pydantic import BaseModel
10
+
11
+ from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
12
+ from digitalkin.modules._base_module import BaseModule
13
+ from digitalkin.services.setup.setup_strategy import SetupData
14
+
15
+ # Configure logging with clear formatting
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
19
+ )
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ # Define schema models using Pydantic
24
+ class OpenAIToolInput(BaseModel):
25
+ """Input model defining what data the module expects."""
26
+
27
+ prompt: str
28
+
29
+
30
+ class OpenAIToolOutput(BaseModel):
31
+ """Output model defining what data the module produces."""
32
+
33
+ response: str
34
+
35
+
36
+ class OpenAIToolSetup(BaseModel):
37
+ """Setup model defining module configuration parameters."""
38
+
39
+ openai_key: str
40
+ model_name: str
41
+ dev_prompt: str
42
+
43
+
44
+ class OpenAIToolSecret(BaseModel):
45
+ """Secret model defining module configuration parameters."""
46
+
47
+
48
+ server_config = ServerConfig(
49
+ host="[::]",
50
+ port=50151,
51
+ mode=ServerMode.ASYNC,
52
+ security=SecurityMode.INSECURE,
53
+ max_workers=10,
54
+ credentials=None,
55
+ )
56
+
57
+
58
+ class OpenAIToolModule(BaseModule[OpenAIToolInput, OpenAIToolOutput, OpenAIToolSetup, OpenAIToolSecret]):
59
+ """A openAI endpoint tool module module."""
60
+
61
+ name = "OpenAIToolModule"
62
+ description = "A module that interacts with OpenAI API to process text"
63
+
64
+ # Define the schema formats for the module
65
+ input_format = OpenAIToolInput
66
+ output_format = OpenAIToolOutput
67
+ setup_format = OpenAIToolSetup
68
+ secret_format = OpenAIToolSecret
69
+
70
+ openai_client: openai.OpenAI
71
+
72
+ # Define module metadata for discovery
73
+ metadata: ClassVar[dict[str, Any]] = {
74
+ "name": "Minimal_LLM_Tool",
75
+ "description": "Transforms input text using a streaming LLM response.",
76
+ "version": "1.0.0",
77
+ "tags": ["text", "transformation", "encryption", "streaming"],
78
+ }
79
+ # Define services_config_params with default values
80
+ services_config_strategies = {}
81
+ services_config_params = {
82
+ "storage": {
83
+ "config": {"setups": OpenAIToolSetup},
84
+ "server_config": server_config,
85
+ }
86
+ }
87
+
88
+ async def initialize(self, setup_data: SetupData) -> None:
89
+ """Initialize the module capabilities.
90
+
91
+ This method is called when the module is loaded by the server.
92
+ Use it to set up module-specific resources or configurations.
93
+ """
94
+ self.openai_client = openai.OpenAI(api_key=setup_data.current_setup_version.content["openai_key"])
95
+ # Define what capabilities this module provides
96
+ self.capabilities = ["text-processing", "streaming", "transformation"]
97
+ logger.info(
98
+ "Module %s initialized with capabilities: %s",
99
+ self.metadata["name"],
100
+ self.capabilities,
101
+ )
102
+
103
+ async def run(
104
+ self,
105
+ input_data: dict[str, Any],
106
+ setup_data: SetupData,
107
+ callback: Callable,
108
+ ) -> None:
109
+ """Process input text and stream LLM responses.
110
+
111
+ Args:
112
+ input_data: Contains the text to process.
113
+ setup_data: Contains model configuration and development prompt.
114
+ callback: Function to send output data back to the client.
115
+
116
+ Raises:
117
+ grpc.RpcError: If gRPC communication fails.
118
+ openai.AuthenticationError: If authentication with OpenAI fails.
119
+ openai.APIConnectionError: If an API connection error occurs.
120
+ Exception: For any unexpected runtime errors.
121
+ """
122
+ logger.info(
123
+ "Running job %s with prompt: '%s' on model: %s",
124
+ self.job_id,
125
+ input_data["prompt"],
126
+ setup_data.current_setup_version.content["model_name"],
127
+ )
128
+ try:
129
+ response = self.openai_client.responses.create(
130
+ model=setup_data.current_setup_version.content["model_name"],
131
+ tools=[{"type": "web_search_preview"}],
132
+ instructions=setup_data.current_setup_version.content["dev_prompt"],
133
+ input=input_data["prompt"],
134
+ )
135
+ if not response.output_text:
136
+ raise openai.APIConnectionError
137
+ output_data = OpenAIToolOutput(response=response.output_text).model_dump()
138
+
139
+ except openai.AuthenticationError as _:
140
+ message = "Authentication Error, OPENAI auth token was never set."
141
+ logger.exception(message)
142
+ output_data = {
143
+ "error": {
144
+ "code": grpc.StatusCode.UNAUTHENTICATED,
145
+ "error_message": message,
146
+ }
147
+ }
148
+ except openai.APIConnectionError as _:
149
+ message = "API Error, please try again."
150
+ logger.exception(message)
151
+ output_data = {"error": {"code": grpc.StatusCode.UNAVAILABLE, "error_message": message}}
152
+ await callback(job_id=self.job_id, output_data=output_data)
153
+ logger.info("Job %s completed", self.job_id)
154
+
155
+ async def cleanup(self) -> None:
156
+ """Clean up any resources when the module is stopped.
157
+
158
+ This method is called when the module is being shut down.
159
+ Use it to close connections, free resources, etc.
160
+ """
161
+ logger.info("Cleaning up module %s", self.metadata["name"])
162
+ # Release any resources here if needed.