digitalkin 0.3.2.dev7__py3-none-any.whl → 0.3.2.dev10__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 (30) hide show
  1. digitalkin/__version__.py +1 -1
  2. digitalkin/grpc_servers/module_servicer.py +0 -11
  3. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +2 -2
  4. digitalkin/grpc_servers/utils/utility_schema_extender.py +2 -1
  5. digitalkin/models/grpc_servers/models.py +91 -6
  6. digitalkin/models/module/module_context.py +136 -23
  7. digitalkin/models/module/setup_types.py +177 -260
  8. digitalkin/models/module/tool_cache.py +27 -187
  9. digitalkin/models/module/tool_reference.py +42 -45
  10. digitalkin/models/services/registry.py +0 -7
  11. digitalkin/modules/_base_module.py +85 -58
  12. digitalkin/services/registry/__init__.py +1 -1
  13. digitalkin/services/registry/default_registry.py +1 -1
  14. digitalkin/services/registry/grpc_registry.py +1 -1
  15. digitalkin/services/registry/registry_models.py +1 -29
  16. digitalkin/services/registry/registry_strategy.py +1 -1
  17. digitalkin/utils/schema_splitter.py +207 -0
  18. {digitalkin-0.3.2.dev7.dist-info → digitalkin-0.3.2.dev10.dist-info}/METADATA +1 -1
  19. {digitalkin-0.3.2.dev7.dist-info → digitalkin-0.3.2.dev10.dist-info}/RECORD +29 -22
  20. {digitalkin-0.3.2.dev7.dist-info → digitalkin-0.3.2.dev10.dist-info}/top_level.txt +1 -0
  21. modules/archetype_with_tools_module.py +244 -0
  22. monitoring/digitalkin_observability/__init__.py +46 -0
  23. monitoring/digitalkin_observability/http_server.py +150 -0
  24. monitoring/digitalkin_observability/interceptors.py +176 -0
  25. monitoring/digitalkin_observability/metrics.py +201 -0
  26. monitoring/digitalkin_observability/prometheus.py +137 -0
  27. monitoring/tests/test_metrics.py +172 -0
  28. digitalkin/models/module/module_helpers.py +0 -189
  29. {digitalkin-0.3.2.dev7.dist-info → digitalkin-0.3.2.dev10.dist-info}/WHEEL +0 -0
  30. {digitalkin-0.3.2.dev7.dist-info → digitalkin-0.3.2.dev10.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,6 @@
2
2
 
3
3
  from digitalkin.models.services.registry import (
4
4
  ModuleInfo,
5
- ModuleStatusInfo,
6
5
  RegistryModuleStatus,
7
6
  RegistryModuleType,
8
7
  )
@@ -12,6 +11,7 @@ from digitalkin.services.registry.exceptions import (
12
11
  RegistryServiceError,
13
12
  )
14
13
  from digitalkin.services.registry.grpc_registry import GrpcRegistry
14
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
15
15
  from digitalkin.services.registry.registry_strategy import RegistryStrategy
16
16
 
17
17
  __all__ = [
@@ -4,11 +4,11 @@ from typing import ClassVar
4
4
 
5
5
  from digitalkin.models.services.registry import (
6
6
  ModuleInfo,
7
- ModuleStatusInfo,
8
7
  RegistryModuleStatus,
9
8
  RegistryModuleType,
10
9
  )
11
10
  from digitalkin.services.registry.exceptions import RegistryModuleNotFoundError
11
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
12
12
  from digitalkin.services.registry.registry_strategy import RegistryStrategy
13
13
 
14
14
 
@@ -20,7 +20,6 @@ from digitalkin.logger import logger
20
20
  from digitalkin.models.grpc_servers.models import ClientConfig
21
21
  from digitalkin.models.services.registry import (
22
22
  ModuleInfo,
23
- ModuleStatusInfo,
24
23
  RegistryModuleStatus,
25
24
  RegistryModuleType,
26
25
  )
@@ -28,6 +27,7 @@ from digitalkin.services.registry.exceptions import (
28
27
  RegistryModuleNotFoundError,
29
28
  RegistryServiceError,
30
29
  )
30
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
31
31
  from digitalkin.services.registry.registry_strategy import RegistryStrategy
32
32
 
33
33
 
@@ -3,37 +3,9 @@
3
3
  This module contains Pydantic models for registry service data structures.
4
4
  """
5
5
 
6
- from enum import IntEnum
7
-
8
6
  from pydantic import BaseModel
9
7
 
10
-
11
- class RegistryModuleStatus(IntEnum):
12
- """Module status in the registry.
13
-
14
- Maps to proto ModuleStatus enum values.
15
- """
16
-
17
- UNKNOWN = 0
18
- READY = 1
19
- ACTIVE = 2
20
- OFFLINE = 3
21
-
22
-
23
- class ModuleInfo(BaseModel):
24
- """Complete module information from registry.
25
-
26
- Maps to proto ModuleDescriptor message.
27
- """
28
-
29
- module_id: str
30
- module_type: str
31
- address: str
32
- port: int
33
- version: str
34
- name: str = ""
35
- documentation: str | None = None
36
- status: RegistryModuleStatus | None = None
8
+ from digitalkin.models.services.registry import RegistryModuleStatus
37
9
 
38
10
 
39
11
  class ModuleStatusInfo(BaseModel):
@@ -5,10 +5,10 @@ from typing import Any
5
5
 
6
6
  from digitalkin.models.services.registry import (
7
7
  ModuleInfo,
8
- ModuleStatusInfo,
9
8
  RegistryModuleStatus,
10
9
  )
11
10
  from digitalkin.services.base_strategy import BaseStrategy
11
+ from digitalkin.services.registry.registry_models import ModuleStatusInfo
12
12
 
13
13
 
14
14
  class RegistryStrategy(BaseStrategy, ABC):
@@ -0,0 +1,207 @@
1
+ """Schema splitter for react-jsonschema-form."""
2
+
3
+ from typing import Any
4
+
5
+
6
+ class SchemaSplitter:
7
+ """Splits a combined JSON schema into jsonschema and uischema for react-jsonschema-form."""
8
+
9
+ @classmethod
10
+ def split(cls, combined_schema: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
11
+ """Split schema into (jsonschema, uischema).
12
+
13
+ Args:
14
+ combined_schema: Combined JSON schema with ui:* properties.
15
+
16
+ Returns:
17
+ Tuple of (jsonschema, uischema).
18
+ """
19
+ defs_ui: dict[str, dict[str, Any]] = {}
20
+ if "$defs" in combined_schema:
21
+ for def_name, def_value in combined_schema["$defs"].items():
22
+ if isinstance(def_value, dict):
23
+ defs_ui[def_name] = {}
24
+ cls._extract_ui_properties(def_value, defs_ui[def_name])
25
+
26
+ json_schema: dict[str, Any] = {}
27
+ ui_schema: dict[str, Any] = {}
28
+ cls._process_object(combined_schema, json_schema, ui_schema, defs_ui)
29
+ return json_schema, ui_schema
30
+
31
+ @classmethod
32
+ def _extract_ui_properties(cls, source: dict[str, Any], ui_target: dict[str, Any]) -> None: # noqa: C901
33
+ """Extract ui:* properties from source into ui_target recursively.
34
+
35
+ Args:
36
+ source: Source dict to extract from.
37
+ ui_target: Target dict for ui properties.
38
+ """
39
+ for key, value in source.items():
40
+ if key.startswith("ui:"):
41
+ ui_target[key] = value
42
+ elif key == "properties" and isinstance(value, dict):
43
+ for prop_name, prop_value in value.items():
44
+ if isinstance(prop_value, dict):
45
+ prop_ui: dict[str, Any] = {}
46
+ cls._extract_ui_properties(prop_value, prop_ui)
47
+ if prop_ui:
48
+ ui_target[prop_name] = prop_ui
49
+ elif key == "items" and isinstance(value, dict):
50
+ items_ui: dict[str, Any] = {}
51
+ cls._extract_ui_properties(value, items_ui)
52
+ if items_ui:
53
+ ui_target["items"] = items_ui
54
+ elif key == "allOf" and isinstance(value, list):
55
+ for item in value:
56
+ if isinstance(item, dict):
57
+ cls._extract_ui_properties(item, ui_target)
58
+
59
+ @classmethod
60
+ def _process_object( # noqa: C901, PLR0912
61
+ cls,
62
+ source: dict[str, Any],
63
+ json_target: dict[str, Any],
64
+ ui_target: dict[str, Any],
65
+ defs_ui: dict[str, dict[str, Any]],
66
+ ) -> None:
67
+ """Process an object, splitting json and ui properties.
68
+
69
+ Args:
70
+ source: Source object to process.
71
+ json_target: Target dict for json schema.
72
+ ui_target: Target dict for ui schema.
73
+ defs_ui: Pre-extracted UI properties from $defs.
74
+ """
75
+ for key, value in source.items():
76
+ if key.startswith("ui:"):
77
+ ui_target[key] = value
78
+ elif key == "properties" and isinstance(value, dict):
79
+ json_target["properties"] = {}
80
+ for prop_name, prop_value in value.items():
81
+ if isinstance(prop_value, dict):
82
+ json_target["properties"][prop_name] = {}
83
+ prop_ui: dict[str, Any] = {}
84
+ cls._process_property(prop_value, json_target["properties"][prop_name], prop_ui, defs_ui)
85
+ if prop_ui:
86
+ ui_target[prop_name] = prop_ui
87
+ else:
88
+ json_target["properties"][prop_name] = prop_value
89
+ elif key == "$defs" and isinstance(value, dict):
90
+ json_target["$defs"] = {}
91
+ for def_name, def_value in value.items():
92
+ if isinstance(def_value, dict):
93
+ json_target["$defs"][def_name] = {}
94
+ cls._strip_ui_properties(def_value, json_target["$defs"][def_name])
95
+ else:
96
+ json_target["$defs"][def_name] = def_value
97
+ elif key == "items" and isinstance(value, dict):
98
+ json_target["items"] = {}
99
+ items_ui: dict[str, Any] = {}
100
+ cls._process_property(value, json_target["items"], items_ui, defs_ui)
101
+ if items_ui:
102
+ ui_target["items"] = items_ui
103
+ elif key == "allOf" and isinstance(value, list):
104
+ json_target["allOf"] = []
105
+ for item in value:
106
+ if isinstance(item, dict):
107
+ item_json: dict[str, Any] = {}
108
+ cls._strip_ui_properties(item, item_json)
109
+ json_target["allOf"].append(item_json)
110
+ else:
111
+ json_target["allOf"].append(item)
112
+ elif key in {"if", "then", "else"} and isinstance(value, dict):
113
+ json_target[key] = {}
114
+ cls._strip_ui_properties(value, json_target[key])
115
+ else:
116
+ json_target[key] = value
117
+
118
+ @classmethod
119
+ def _process_property( # noqa: C901, PLR0912
120
+ cls,
121
+ source: dict[str, Any],
122
+ json_target: dict[str, Any],
123
+ ui_target: dict[str, Any],
124
+ defs_ui: dict[str, dict[str, Any]],
125
+ ) -> None:
126
+ """Process a property, resolving $ref for UI properties.
127
+
128
+ Args:
129
+ source: Source property dict.
130
+ json_target: Target dict for json schema.
131
+ ui_target: Target dict for ui schema.
132
+ defs_ui: Pre-extracted UI properties from $defs.
133
+ """
134
+ if "$ref" in source:
135
+ ref_path = source["$ref"]
136
+ if ref_path.startswith("#/$defs/"):
137
+ def_name = ref_path[8:]
138
+ if def_name in defs_ui:
139
+ ui_target.update(defs_ui[def_name])
140
+
141
+ for key, value in source.items():
142
+ if key.startswith("ui:"):
143
+ ui_target[key] = value
144
+ elif key == "properties" and isinstance(value, dict):
145
+ json_target["properties"] = {}
146
+ for prop_name, prop_value in value.items():
147
+ if isinstance(prop_value, dict):
148
+ json_target["properties"][prop_name] = {}
149
+ prop_ui: dict[str, Any] = {}
150
+ cls._process_property(prop_value, json_target["properties"][prop_name], prop_ui, defs_ui)
151
+ if prop_ui:
152
+ ui_target[prop_name] = prop_ui
153
+ else:
154
+ json_target["properties"][prop_name] = prop_value
155
+ elif key == "items" and isinstance(value, dict):
156
+ json_target["items"] = {}
157
+ items_ui: dict[str, Any] = {}
158
+ cls._process_property(value, json_target["items"], items_ui, defs_ui)
159
+ if items_ui:
160
+ ui_target["items"] = items_ui
161
+ else:
162
+ json_target[key] = value
163
+
164
+ @classmethod
165
+ def _strip_ui_properties(cls, source: dict[str, Any], json_target: dict[str, Any]) -> None: # noqa: C901, PLR0912
166
+ """Copy source to json_target, stripping ui:* properties.
167
+
168
+ Args:
169
+ source: Source dict.
170
+ json_target: Target dict without ui:* properties.
171
+ """
172
+ for key, value in source.items():
173
+ if key.startswith("ui:"):
174
+ continue
175
+ if key == "properties" and isinstance(value, dict):
176
+ json_target["properties"] = {}
177
+ for prop_name, prop_value in value.items():
178
+ if isinstance(prop_value, dict):
179
+ json_target["properties"][prop_name] = {}
180
+ cls._strip_ui_properties(prop_value, json_target["properties"][prop_name])
181
+ else:
182
+ json_target["properties"][prop_name] = prop_value
183
+ elif key == "$defs" and isinstance(value, dict):
184
+ json_target["$defs"] = {}
185
+ for def_name, def_value in value.items():
186
+ if isinstance(def_value, dict):
187
+ json_target["$defs"][def_name] = {}
188
+ cls._strip_ui_properties(def_value, json_target["$defs"][def_name])
189
+ else:
190
+ json_target["$defs"][def_name] = def_value
191
+ elif key == "items" and isinstance(value, dict):
192
+ json_target["items"] = {}
193
+ cls._strip_ui_properties(value, json_target["items"])
194
+ elif key == "allOf" and isinstance(value, list):
195
+ json_target["allOf"] = []
196
+ for item in value:
197
+ if isinstance(item, dict):
198
+ item_json: dict[str, Any] = {}
199
+ cls._strip_ui_properties(item, item_json)
200
+ json_target["allOf"].append(item_json)
201
+ else:
202
+ json_target["allOf"].append(item)
203
+ elif key in {"if", "then", "else"} and isinstance(value, dict):
204
+ json_target[key] = {}
205
+ cls._strip_ui_properties(value, json_target[key])
206
+ else:
207
+ json_target[key] = value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.3.2.dev7
3
+ Version: 0.3.2.dev10
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
@@ -7,7 +7,7 @@ base_server/mock/__init__.py,sha256=YZFT-F1l_TpvJYuIPX-7kTeE1CfOjhx9YmNRXVoi-jQ,
7
7
  base_server/mock/mock_pb2.py,sha256=sETakcS3PAAm4E-hTCV1jIVaQTPEAIoVVHupB8Z_k7Y,1843
8
8
  base_server/mock/mock_pb2_grpc.py,sha256=BbOT70H6q3laKgkHfOx1QdfmCS_HxCY4wCOX84YAdG4,3180
9
9
  digitalkin/__init__.py,sha256=7LLBAba0th-3SGqcpqFO-lopWdUkVLKzLZiMtB-mW3M,162
10
- digitalkin/__version__.py,sha256=CcSqodNkINfd0uyPHVCwE5GCsEYQVveGr3ohUqS3x6A,195
10
+ digitalkin/__version__.py,sha256=HFosF_nDLUB58s-K9x_90emrDL2e-FS3aqanQgLZcDM,196
11
11
  digitalkin/logger.py,sha256=8ze_tjt2G6mDTuQcsf7-UTXWP3UHZ7LZVSs_iqF4rX4,4685
12
12
  digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  digitalkin/core/__init__.py,sha256=FJRcJ-B1Viyn-38L8XpOpZ8KOnf1I7PCDOAmKXLQhqc,71
@@ -28,12 +28,12 @@ digitalkin/core/task_manager/task_session.py,sha256=5jw21bT_SPXUzWE7tk6YG62EXqlR
28
28
  digitalkin/grpc_servers/__init__.py,sha256=ZIRMJ1Lcas8yQ106GCup6hn2UBOsx1sNk8ap0lpEDnY,72
29
29
  digitalkin/grpc_servers/_base_server.py,sha256=ZVeCDwI7w7fFbPTXPkeJb_SOuLfd2T7za3T4oCu2UWY,18680
30
30
  digitalkin/grpc_servers/module_server.py,sha256=Ec3izzV2YpdN8rGs_cX-iVulQ00FkLR5dBflHlQ8a6Y,7849
31
- digitalkin/grpc_servers/module_servicer.py,sha256=a5qVOd0beAwXlFAch9VfLNR8PdaYj_fF2O8rXcwInSU,21087
31
+ digitalkin/grpc_servers/module_servicer.py,sha256=7GQOyAPYMxHVaJGplgDNiVoKr1oaAIL-zdZpyDpznTA,20530
32
32
  digitalkin/grpc_servers/utils/__init__.py,sha256=ZnAIb_F8z4NhtPypqkdmzgRSzolKnJTk3oZx5GfWH5Y,38
33
33
  digitalkin/grpc_servers/utils/exceptions.py,sha256=LtaDtlqXCeT6iqApogs4pbtezotOVeg4fhnFzGBvFsY,692
34
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=ElGvp6evY5q-EBmDVyQZaDJktfShtMsptMmq16jfgxA,3285
34
+ digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=nGG8QdKnBH0UG9qbKrlPwIvcvPgW3osw7O3cImxisPE,3279
35
35
  digitalkin/grpc_servers/utils/grpc_error_handler.py,sha256=0wPEU4713_ZlgIilaeXJV2bi90tHwYO1myDrSLeenKk,1848
36
- digitalkin/grpc_servers/utils/utility_schema_extender.py,sha256=qTnGrRcze2qe_W61FOfcXGWRILNctidm7z_1GKlCM1E,3719
36
+ digitalkin/grpc_servers/utils/utility_schema_extender.py,sha256=UCJR5YAKA_Wg5Q9feInf4AZW6AVtSQZYRZuMsaEgZVo,3775
37
37
  digitalkin/mixins/__init__.py,sha256=d6ljaoyJZJT9XxOrXZG5FVNvbLURb3_CZrkp4GPZWYM,590
38
38
  digitalkin/mixins/base_mixin.py,sha256=uLkg6MbDtVc9DysjdfNIGKahxQLnnjuL3DYpuyNLbk8,486
39
39
  digitalkin/mixins/callback_mixin.py,sha256=90nHm9-pbKT14GAy3CB3fsBtpYu5IH0woOQdNLM2e_Y,836
@@ -48,24 +48,23 @@ digitalkin/models/core/__init__.py,sha256=jOMDmPX0uSfGA9zUi0u_kOvYJ46VdIssoIhVYv
48
48
  digitalkin/models/core/job_manager_models.py,sha256=wvf2dzRzAu0-zzzAXQe6XTC36cNA10sXRLt2p_TFqjk,1003
49
49
  digitalkin/models/core/task_monitor.py,sha256=CW-jydSgXMV464W0pqfar0HpgqlSxqdujmC-f8p9EQc,2639
50
50
  digitalkin/models/grpc_servers/__init__.py,sha256=0tA71nPSXgRrh9DoLvx-TSwZXdYIRUEItoadpTL1cTo,42
51
- digitalkin/models/grpc_servers/models.py,sha256=unV1Wo0u3Efm7ddgYyYZZYUC_W6F0S5BQYH3xsOmXjw,8965
51
+ digitalkin/models/grpc_servers/models.py,sha256=gRX94eL71a5mLIie-lCOwE7a0As_AuGduxPPzTHbAe4,13797
52
52
  digitalkin/models/grpc_servers/types.py,sha256=rQ78s4nAet2jy-NIDj_PUWriT0kuGHr_w6ELjmjgBao,539
53
53
  digitalkin/models/module/__init__.py,sha256=N55wan3rAUVPEGLIDjXoFM_-DYY_zxvbQOZHzNDfwoY,751
54
54
  digitalkin/models/module/base_types.py,sha256=oIylVNqo0idTFj4dRgCt7P19daNZ-AlvgCPpL9TJvto,1850
55
55
  digitalkin/models/module/module.py,sha256=k0W8vfJJFth8XdDzkHm32SyTuSf3h2qF0hSrxAfGF1s,956
56
- digitalkin/models/module/module_context.py,sha256=cjMP3cYPC4ZjTWXY3JhF_77PMT72ZglhHfq2I4JIl6I,6430
57
- digitalkin/models/module/module_helpers.py,sha256=n5lx25c5zRmuivcThC-HdumsjyWDv31xC6tI6NMLR7U,6438
56
+ digitalkin/models/module/module_context.py,sha256=qpjyMYgTCyQS0lW2RhTKZsoQKwFpv6l08FIM0sqC6DQ,10326
58
57
  digitalkin/models/module/module_types.py,sha256=C9azCNBk76xMa-Mww8_6AiwQR8MLAsEyUOvBYxytovI,739
59
- digitalkin/models/module/setup_types.py,sha256=e9CG8x75Mkh7dDIQ66UWU99VcWmyk-JAqHCpUtcgyns,21323
60
- digitalkin/models/module/tool_cache.py,sha256=WFoPFtHLs1Q3jdeQZpu-ZycnJwhocyYyeAo9k0JXj5o,7434
61
- digitalkin/models/module/tool_reference.py,sha256=A4EewKMlfhdI2hW7F3kgPc6_UinUrN-hhOweM0KzSa0,4302
58
+ digitalkin/models/module/setup_types.py,sha256=XMKyDm-7n_Za9tDBv1AkgXUowlUj46SMxWANa_yg_LU,18311
59
+ digitalkin/models/module/tool_cache.py,sha256=RP3JwASV8dFUZEKudyALbu0_tq81lstuEc4QHMQpmvM,2073
60
+ digitalkin/models/module/tool_reference.py,sha256=1AyNK-8McsSTEb62aaSou5AAXkKUOJqrFtJ5YWrFbmw,3512
62
61
  digitalkin/models/module/utility.py,sha256=gnbYfWpXGbomUI0fWf7T-Qm_VvT-LXDv1OuA9zObwVg,5589
63
62
  digitalkin/models/services/__init__.py,sha256=jhfVw6egq0OcHmos_fypH9XFehbHTBw09wluVFVFEyw,226
64
63
  digitalkin/models/services/cost.py,sha256=9PXvd5RrIk9vCrRjcUGQ9ZyAokEbwLg4s0RfnE-aLP4,1616
65
- digitalkin/models/services/registry.py,sha256=0qMAIi52ovofZ4XTV7UkI4i7sR5C3_VFLp-kw9AjQ24,829
64
+ digitalkin/models/services/registry.py,sha256=hz_r03-K633XHu2fOb5HWsE59EPFusBBipy0Gz6OBvI,705
66
65
  digitalkin/models/services/storage.py,sha256=wp7F-AvTsU46ujGPcguqM5kUKRZx4399D4EGAAJt2zs,1143
67
66
  digitalkin/modules/__init__.py,sha256=vTQk8DWopxQSJ17BjE5dNhq247Rou55iQLJdBxoPUmo,296
68
- digitalkin/modules/_base_module.py,sha256=0bbiPOcLSiKqjrVYaWdmc72jbpaPa8dOzozEcQO0zMg,21285
67
+ digitalkin/modules/_base_module.py,sha256=4-fS376f9ti3xSgM946yRccHcEGttdo2QYKaxdqk1kY,22451
69
68
  digitalkin/modules/archetype_module.py,sha256=XC9tl1Yr6QlbPn_x0eov6UUZwQgwW--BYPPMYVJH_NU,505
70
69
  digitalkin/modules/tool_module.py,sha256=GBis7bKCkvWFCYLRvaS9oZVmLBBve1w8BhVnKOU2sCc,506
71
70
  digitalkin/modules/trigger_handler.py,sha256=qPNMi-8NHqscOxciHeaXtpwjXApT3YzjMF23zQAjaZY,1770
@@ -95,12 +94,12 @@ digitalkin/services/filesystem/grpc_filesystem.py,sha256=9Cjp4ie-mJ2EMMZ8DjCqeV6
95
94
  digitalkin/services/identity/__init__.py,sha256=InkeyLgFYYwItx8mePA8HpfacOMWZwwuc0G4pWtKq9s,270
96
95
  digitalkin/services/identity/default_identity.py,sha256=Y2auZHrGSZTIN5D8HyjLvLcNbYFM1CNUE23x7p5VIGw,386
97
96
  digitalkin/services/identity/identity_strategy.py,sha256=skappBbds1_qa0Gr24FGrNX1N0_OYhYT1Lh7dUaAirE,429
98
- digitalkin/services/registry/__init__.py,sha256=4C3bJzOa7gv7fe3etxOg0653wDOwEt6NrA-pBcwFr2s,783
99
- digitalkin/services/registry/default_registry.py,sha256=b6Y0TVffC1_nYlPpJ0kcfn11Gj2s7I5II5oPypD6cIM,4206
97
+ digitalkin/services/registry/__init__.py,sha256=WPGQM3U-QvMXhsaOy9BN0kVMU3QkPFwAMT3lGmTR-Ko,835
98
+ digitalkin/services/registry/default_registry.py,sha256=tOqw9Ve9w_BzhqrZmHuUl5Ps-J_KTEwYg3tu1gNIHmw,4258
100
99
  digitalkin/services/registry/exceptions.py,sha256=tAcVXioCzDqfBvxB_P0uQpaK_LDLrFb0KpymROuqs-8,1371
101
- digitalkin/services/registry/grpc_registry.py,sha256=TOcwa0b0dkdQh7V8Jm7XajWiIL9MQ0ReXcAlm4SJxfQ,10770
102
- digitalkin/services/registry/registry_models.py,sha256=9ypU68K2IMIKwt7QAJERc0cvXJeGcfh8iIfo5sOim5M,805
103
- digitalkin/services/registry/registry_strategy.py,sha256=qeHLY_T8NEFRaeBt7QfaJxezA2TVvFR1ysK863pI5j0,2693
100
+ digitalkin/services/registry/grpc_registry.py,sha256=2_He-I9I4SXdjYA9QYxFWQrr_xBao6LIYXsgiICkeHY,10822
101
+ digitalkin/services/registry/registry_models.py,sha256=DJEwMJg5_BewpgHDtY8xIGWj9jA9H07iYgHLCv81giY,331
102
+ digitalkin/services/registry/registry_strategy.py,sha256=N6oLxMwnYkM_XVW7WDxZoSfFafjUtsFDig3F8VQZj7Q,2745
104
103
  digitalkin/services/setup/__init__.py,sha256=t6xcvEWqTbcRZstBFK9cESEqaZKvpW14VtYygxIqfYQ,65
105
104
  digitalkin/services/setup/default_setup.py,sha256=zTG9Nvw3Tqy9qctSEnjWE4Mlqol321AUaTlIm6Jl1k0,8195
106
105
  digitalkin/services/setup/grpc_setup.py,sha256=aEAHiG0WSzNSQtJjn7xAGQwj70b0HrFO8GBKFXRx3aI,13925
@@ -122,15 +121,23 @@ digitalkin/utils/development_mode_action.py,sha256=2hznh0ajW_4ZTysfoc0Y49161f_PQ
122
121
  digitalkin/utils/dynamic_schema.py,sha256=5-B3dBGlCYYv6uRJkgudtc0ZpBOTYxl0yKedDGsteZQ,15184
123
122
  digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
124
123
  digitalkin/utils/package_discover.py,sha256=sa6Zp5Kape1Zr4iYiNrnZxiHDnqM06ODk6yfWHom53w,13465
125
- digitalkin-0.3.2.dev7.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
124
+ digitalkin/utils/schema_splitter.py,sha256=KMvYRHDHlwdhh_c6FJxkWvLStZo9Kbj-jd3pIGPZfxk,9317
125
+ digitalkin-0.3.2.dev10.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
126
126
  modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
+ modules/archetype_with_tools_module.py,sha256=PXTS6IXmC_OjxTmVrL_pYVI0MKwXjD5I1UJO_2xa10Q,7632
127
128
  modules/cpu_intensive_module.py,sha256=GZlirQDZdYuXrI46sv1q4RNAHZjL4EptHVQTvgK9zz8,8363
128
129
  modules/dynamic_setup_module.py,sha256=tKvUWZdlYZkfAgKR0mLuFcLiFGKpVgpsz10LeJ6B2QI,11410
129
130
  modules/minimal_llm_module.py,sha256=N9aIzZQI-miyH4AB4xTmGHpMvdSLnYyXNOD4Z3YFzis,11216
130
131
  modules/text_transform_module.py,sha256=MfhI_Ki1U6qk379ne6oazNEu4PhO4R3cRezEcr0nGPw,7251
132
+ monitoring/digitalkin_observability/__init__.py,sha256=PXG4xbMbmiVNkIaVG899H9CHvcld7c0bSbaLHqKeDeo,1247
133
+ monitoring/digitalkin_observability/http_server.py,sha256=kFbWFQvaqq2VVwwqDozV0pRPe7RkAXsa9xCWruMlOyk,4527
134
+ monitoring/digitalkin_observability/interceptors.py,sha256=Sx6o0s8FSMfIGKVAtjVl7l-sIygP45od6eITl7DroBU,6219
135
+ monitoring/digitalkin_observability/metrics.py,sha256=XhVRE8y6tbzJqxIzEHa5MT3I0k3hgpxjzZf_R6SyjbQ,7553
136
+ monitoring/digitalkin_observability/prometheus.py,sha256=gDmM9ySaVwPAe7Yg84pLxmEKu0Cudezi8pK3RtqWs6g,5457
137
+ monitoring/tests/test_metrics.py,sha256=ugnYfAwqBPO6zA8z4afKTlyBWECTivacYSN-URQCn2E,5856
131
138
  services/filesystem_module.py,sha256=U4dgqtuDadaXz8PJ1d_uQ_1EPncBqudAQCLUICF9yL4,7421
132
139
  services/storage_module.py,sha256=Wz2MzLvqs2D_bnBBgtnujYcAKK2V2KFMk8K21RoepSE,6972
133
- digitalkin-0.3.2.dev7.dist-info/METADATA,sha256=NvyE2xOIuPXWTJBeqXd_ZMVI-EfKf6Ho-LZFTDVkIuc,29724
134
- digitalkin-0.3.2.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
135
- digitalkin-0.3.2.dev7.dist-info/top_level.txt,sha256=gcjqlyrZuLjIyxrOIavCQM_olpr6ND5kPKkZd2j0xGo,40
136
- digitalkin-0.3.2.dev7.dist-info/RECORD,,
140
+ digitalkin-0.3.2.dev10.dist-info/METADATA,sha256=qExgKNDQPoWMU4v-k-tRwJyJ6on7nFhA83U4pqCgrwE,29725
141
+ digitalkin-0.3.2.dev10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
142
+ digitalkin-0.3.2.dev10.dist-info/top_level.txt,sha256=AYVIesKrO0jnedQ-Muog9JBehG81WeTCNeOFoJgwsgE,51
143
+ digitalkin-0.3.2.dev10.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  base_server
2
2
  digitalkin
3
3
  modules
4
+ monitoring
4
5
  services
@@ -0,0 +1,244 @@
1
+ """Example archetype module with tool cache integration."""
2
+
3
+ import logging
4
+ from typing import Any, ClassVar, Literal
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from digitalkin.models.grpc_servers.models import ClientConfig, SecurityMode, ServerMode
9
+ from digitalkin.models.module.module_context import ModuleContext
10
+ from digitalkin.models.module.setup_types import SetupModel
11
+ from digitalkin.models.module.tool_reference import (
12
+ ToolReference,
13
+ ToolReferenceConfig,
14
+ ToolSelectionMode,
15
+ )
16
+ from digitalkin.modules._base_module import BaseModule # noqa: PLC2701
17
+ from digitalkin.services.services_models import ServicesStrategy
18
+
19
+ logging.basicConfig(
20
+ level=logging.DEBUG,
21
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
22
+ )
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class MessageInputPayload(BaseModel):
27
+ """Message input payload."""
28
+
29
+ payload_type: Literal["message"] = "message"
30
+ user_prompt: str
31
+
32
+
33
+ class ArchetypeInput(BaseModel):
34
+ """Archetype input."""
35
+
36
+ payload: MessageInputPayload = Field(discriminator="payload_type")
37
+
38
+
39
+ class MessageOutputPayload(BaseModel):
40
+ """Message output payload."""
41
+
42
+ payload_type: Literal["message"] = "message"
43
+ response: str
44
+ tools_used: list[str] = Field(default_factory=list)
45
+
46
+
47
+ class ArchetypeOutput(BaseModel):
48
+ """Archetype output."""
49
+
50
+ payload: MessageOutputPayload = Field(discriminator="payload_type")
51
+
52
+
53
+ class ArchetypeSetup(SetupModel):
54
+ """Setup with tool references resolved during config setup."""
55
+
56
+ model_name: str = Field(
57
+ default="gpt-4",
58
+ json_schema_extra={"config": True},
59
+ )
60
+ temperature: float = Field(
61
+ default=0.7,
62
+ json_schema_extra={"config": True},
63
+ )
64
+
65
+ search_tool: ToolReference = Field(
66
+ default_factory=lambda: ToolReference(
67
+ config=ToolReferenceConfig(
68
+ mode=ToolSelectionMode.FIXED,
69
+ module_id="search-tool-v1",
70
+ )
71
+ ),
72
+ json_schema_extra={"config": True},
73
+ )
74
+
75
+ calculator_tool: ToolReference = Field(
76
+ default_factory=lambda: ToolReference(
77
+ config=ToolReferenceConfig(
78
+ mode=ToolSelectionMode.TAG,
79
+ tag="math-calculator",
80
+ )
81
+ ),
82
+ json_schema_extra={"config": True},
83
+ )
84
+
85
+ dynamic_tool: ToolReference = Field(
86
+ default_factory=lambda: ToolReference(
87
+ config=ToolReferenceConfig(
88
+ mode=ToolSelectionMode.DISCOVERABLE,
89
+ )
90
+ ),
91
+ json_schema_extra={"config": True},
92
+ )
93
+
94
+ system_prompt: str = Field(
95
+ default="You are a helpful assistant with access to tools.",
96
+ json_schema_extra={"hidden": True},
97
+ )
98
+
99
+
100
+ class ArchetypeConfigSetup(BaseModel):
101
+ """Config setup model."""
102
+
103
+ additional_instructions: str | None = None
104
+
105
+
106
+ class ArchetypeSecret(BaseModel):
107
+ """Secrets model."""
108
+
109
+
110
+ client_config = ClientConfig(
111
+ host="[::]",
112
+ port=50152,
113
+ mode=ServerMode.ASYNC,
114
+ security=SecurityMode.INSECURE,
115
+ credentials=None,
116
+ )
117
+
118
+
119
+ class ArchetypeWithToolsModule(
120
+ BaseModule[
121
+ ArchetypeInput,
122
+ ArchetypeOutput,
123
+ ArchetypeSetup,
124
+ ArchetypeSecret,
125
+ ]
126
+ ):
127
+ """Archetype module demonstrating tool cache usage."""
128
+
129
+ name = "ArchetypeWithToolsModule"
130
+ description = "Archetype with tool cache integration"
131
+
132
+ config_setup_format = ArchetypeConfigSetup
133
+ input_format = ArchetypeInput
134
+ output_format = ArchetypeOutput
135
+ setup_format = ArchetypeSetup
136
+ secret_format = ArchetypeSecret
137
+
138
+ metadata: ClassVar[dict[str, Any]] = {
139
+ "name": "ArchetypeWithToolsModule",
140
+ "version": "1.0.0",
141
+ "tags": ["archetype", "tools"],
142
+ }
143
+
144
+ services_config_strategies: ClassVar[dict[str, ServicesStrategy | None]] = {}
145
+ services_config_params: ClassVar[dict[str, dict[str, Any | None] | None]] = {
146
+ "registry": {
147
+ "config": {},
148
+ "client_config": client_config,
149
+ },
150
+ }
151
+
152
+ async def run_config_setup(
153
+ self,
154
+ context: ModuleContext, # noqa: ARG002
155
+ config_setup_data: ArchetypeSetup,
156
+ ) -> ArchetypeSetup:
157
+ """Custom config setup logic, runs in parallel with tool resolution.
158
+
159
+ Args:
160
+ context: Module context with services.
161
+ config_setup_data: Setup data being configured.
162
+
163
+ Returns:
164
+ Configured setup data.
165
+ """
166
+ logger.info("Running config setup for %s", self.name)
167
+ return config_setup_data
168
+
169
+ async def initialize(self, context: ModuleContext, setup_data: ArchetypeSetup) -> None: # noqa: ARG002
170
+ """Initialize module.
171
+
172
+ Args:
173
+ context: Module context with services and tool cache.
174
+ setup_data: Setup data for the module.
175
+ """
176
+ logger.info("Initializing %s", self.name)
177
+ if context.tool_cache:
178
+ logger.info("Available tools: %s", context.tool_cache.list_tools())
179
+
180
+ async def run(
181
+ self,
182
+ input_data: ArchetypeInput,
183
+ setup_data: ArchetypeSetup, # noqa: ARG002
184
+ ) -> None:
185
+ """Run module with tool cache lookups and call_module_by_id.
186
+
187
+ Args:
188
+ input_data: Input data to process.
189
+ setup_data: Setup configuration.
190
+ """
191
+ logger.info("Running %s", self.name)
192
+
193
+ tools_used: list[str] = []
194
+ tool_results: list[str] = []
195
+
196
+ # Get search tool from cache and call via call_module_by_id
197
+ search_info = self.context.tool_cache.get("search_tool")
198
+ if search_info:
199
+ tools_used.append(f"search:{search_info.module_id}")
200
+ async for response in self.context.call_module_by_id(
201
+ module_id=search_info.module_id,
202
+ input_data={"query": input_data.payload.user_prompt},
203
+ setup_id=self.context.session.setup_id,
204
+ mission_id=self.context.session.mission_id,
205
+ ):
206
+ tool_results.append(f"search_result: {response}")
207
+
208
+ # Get calculator tool from cache
209
+ calc_info = self.context.tool_cache.get("calculator_tool")
210
+ if calc_info:
211
+ tools_used.append(f"calculator:{calc_info.module_id}")
212
+ async for response in self.context.call_module_by_id(
213
+ module_id=calc_info.module_id,
214
+ input_data={"expression": "2 + 2"},
215
+ setup_id=self.context.session.setup_id,
216
+ mission_id=self.context.session.mission_id,
217
+ ):
218
+ tool_results.append(f"calc_result: {response}")
219
+
220
+ # Dynamic discovery via registry fallback for tools not in cache
221
+ dynamic_info = self.context.tool_cache.get(
222
+ "some_dynamic_tool",
223
+ registry=self.context.registry,
224
+ )
225
+ if dynamic_info:
226
+ tools_used.append(f"dynamic:{dynamic_info.module_id}")
227
+ async for response in self.context.call_module_by_id(
228
+ module_id=dynamic_info.module_id,
229
+ input_data={"prompt": input_data.payload.user_prompt},
230
+ setup_id=self.context.session.setup_id,
231
+ mission_id=self.context.session.mission_id,
232
+ ):
233
+ tool_results.append(f"dynamic_result: {response}")
234
+
235
+ response = MessageOutputPayload(
236
+ response=f"Processed: {input_data.payload.user_prompt} | Results: {len(tool_results)}",
237
+ tools_used=tools_used,
238
+ )
239
+
240
+ await self.context.callbacks.send_message(ArchetypeOutput(payload=response))
241
+
242
+ async def cleanup(self) -> None:
243
+ """Clean up resources."""
244
+ logger.info("Cleaning up %s", self.name)