digitalkin 0.2.6__py3-none-any.whl → 0.2.7__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.
digitalkin/__version__.py CHANGED
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.2.6"
8
+ __version__ = "0.2.7"
@@ -244,7 +244,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
244
244
  # Get input schema if available
245
245
  try:
246
246
  # Convert schema to proto format
247
- input_schema_proto = self.module_class.get_input_format(request.llm_format)
247
+ input_schema_proto = self.module_class.get_input_format(llm_format=request.llm_format)
248
248
  input_format_struct = json_format.Parse(
249
249
  text=input_schema_proto,
250
250
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -280,7 +280,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
280
280
  # Get output schema if available
281
281
  try:
282
282
  # Convert schema to proto format
283
- output_schema_proto = self.module_class.get_output_format(request.llm_format)
283
+ output_schema_proto = self.module_class.get_output_format(llm_format=request.llm_format)
284
284
  output_format_struct = json_format.Parse(
285
285
  text=output_schema_proto,
286
286
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -316,7 +316,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
316
316
  # Get setup schema if available
317
317
  try:
318
318
  # Convert schema to proto format
319
- setup_schema_proto = self.module_class.get_setup_format(request.llm_format)
319
+ setup_schema_proto = self.module_class.get_setup_format(llm_format=request.llm_format)
320
320
  setup_format_struct = json_format.Parse(
321
321
  text=setup_schema_proto,
322
322
  message=struct_pb2.Struct(), # pylint: disable=no-member
@@ -332,3 +332,39 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer):
332
332
  success=True,
333
333
  setup_schema=setup_format_struct,
334
334
  )
335
+
336
+ def GetModuleSecret( # noqa: N802
337
+ self,
338
+ request: information_pb2.GetModuleSecretRequest,
339
+ context: grpc.ServicerContext,
340
+ ) -> information_pb2.GetModuleSecretResponse:
341
+ """Get information about the module's secrets.
342
+
343
+ Args:
344
+ request: The get module secret request.
345
+ context: The gRPC context.
346
+
347
+ Returns:
348
+ A response with the module's secret schema.
349
+ """
350
+ logger.info("GetModuleSecret called for module: '%s'", self.module_class.__name__)
351
+
352
+ # Get secret schema if available
353
+ try:
354
+ # Convert schema to proto format
355
+ secret_schema_proto = self.module_class.get_secret_format(llm_format=request.llm_format)
356
+ secret_format_struct = json_format.Parse(
357
+ text=secret_schema_proto,
358
+ message=struct_pb2.Struct(), # pylint: disable=no-member
359
+ ignore_unknown_fields=True,
360
+ )
361
+ except NotImplementedError as e:
362
+ logger.warning(e)
363
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
364
+ context.set_details(e)
365
+ return information_pb2.GetModuleSecretResponse()
366
+
367
+ return information_pb2.GetModuleSecretResponse(
368
+ success=True,
369
+ secret_schema=secret_format_struct,
370
+ )
@@ -17,6 +17,7 @@ from digitalkin.services.registry.registry_strategy import RegistryStrategy
17
17
  from digitalkin.services.services_config import ServicesConfig, ServicesStrategy
18
18
  from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
19
19
  from digitalkin.services.storage.storage_strategy import StorageStrategy
20
+ from digitalkin.utils.llm_ready_schema import llm_ready_schema
20
21
 
21
22
 
22
23
  class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretModelT]):
@@ -73,7 +74,7 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
73
74
  return self._status
74
75
 
75
76
  @classmethod
76
- def get_secret_format(cls, llm_format: bool) -> str: # noqa: FBT001
77
+ def get_secret_format(cls, *, llm_format: bool) -> str:
77
78
  """Get the JSON schema of the secret format model.
78
79
 
79
80
  Raises:
@@ -84,13 +85,13 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
84
85
  """
85
86
  if cls.secret_format is not None:
86
87
  if llm_format:
87
- return json.dumps(cls.secret_format, indent=2)
88
+ return json.dumps(llm_ready_schema(cls.secret_format), indent=2)
88
89
  return json.dumps(cls.secret_format.model_json_schema(), indent=2)
89
90
  msg = f"{cls.__name__}' class does not define a 'secret_format'."
90
91
  raise NotImplementedError(msg)
91
92
 
92
93
  @classmethod
93
- def get_input_format(cls, llm_format: bool) -> str: # noqa: FBT001
94
+ def get_input_format(cls, *, llm_format: bool) -> str:
94
95
  """Get the JSON schema of the input format model.
95
96
 
96
97
  Raises:
@@ -101,13 +102,13 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
101
102
  """
102
103
  if cls.input_format is not None:
103
104
  if llm_format:
104
- return json.dumps(cls.input_format, indent=2)
105
+ return json.dumps(llm_ready_schema(cls.input_format), indent=2)
105
106
  return json.dumps(cls.input_format.model_json_schema(), indent=2)
106
107
  msg = f"{cls.__name__}' class does not define an 'input_format'."
107
108
  raise NotImplementedError(msg)
108
109
 
109
110
  @classmethod
110
- def get_output_format(cls, llm_format: bool) -> str: # noqa: FBT001
111
+ def get_output_format(cls, *, llm_format: bool) -> str:
111
112
  """Get the JSON schema of the output format model.
112
113
 
113
114
  Raises:
@@ -118,13 +119,13 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
118
119
  """
119
120
  if cls.output_format is not None:
120
121
  if llm_format:
121
- return json.dumps(cls.output_format, indent=2)
122
+ return json.dumps(llm_ready_schema(cls.output_format), indent=2)
122
123
  return json.dumps(cls.output_format.model_json_schema(), indent=2)
123
124
  msg = "'%s' class does not define an 'output_format'."
124
125
  raise NotImplementedError(msg)
125
126
 
126
127
  @classmethod
127
- def get_setup_format(cls, llm_format: bool) -> str: # noqa: FBT001
128
+ def get_setup_format(cls, *, llm_format: bool) -> str:
128
129
  """Gets the JSON schema of the setup format model.
129
130
 
130
131
  Raises:
@@ -135,7 +136,7 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
135
136
  """
136
137
  if cls.setup_format is not None:
137
138
  if llm_format:
138
- return json.dumps(cls.setup_format, indent=2)
139
+ return json.dumps(llm_ready_schema(cls.setup_format), indent=2)
139
140
  return json.dumps(cls.setup_format.model_json_schema(), indent=2)
140
141
  msg = "'%s' class does not define an 'setup_format'."
141
142
  raise NotImplementedError(msg)
@@ -0,0 +1,75 @@
1
+ """LLM format schema for Pydantic models.
2
+
3
+ This module provides functionality to generate JSON schemas for Pydantic models ready for LLMs.
4
+ """
5
+
6
+ import copy
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel
10
+ from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
11
+
12
+
13
+ class CustomOrderSchema(GenerateJsonSchema):
14
+ """Custom schema generator to sort keys in a specific order."""
15
+
16
+ def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: # noqa: ARG002
17
+ """Sort the keys of the schema in a specific order.
18
+
19
+ Args:
20
+ value: The schema value to sort.
21
+ parent_key: The parent key of the schema value.
22
+
23
+ Returns:
24
+ The sorted schema value.
25
+ """
26
+ if isinstance(value, dict):
27
+ # Define your preferred order
28
+ preferred = ["title", "description", "type", "examples", "properties"]
29
+ # Collect all keys, putting preferred ones first
30
+ keys = preferred + [k for k in value if k not in preferred]
31
+ # Recurse for each value
32
+ return {k: self.sort(value[k], k) for k in keys if k in value}
33
+ if isinstance(value, list):
34
+ return [self.sort(v) for v in value]
35
+ return value
36
+
37
+
38
+ def inline_refs(schema: dict) -> dict:
39
+ """Recursively resolve and inline all $ref in the schema.
40
+
41
+ Args:
42
+ schema: The JSON schema to inline.
43
+
44
+ Returns:
45
+ The inlined JSON schema.
46
+ """
47
+ schema = copy.deepcopy(schema)
48
+ defs = schema.pop("$defs", {})
49
+
50
+ def _resolve(obj: Any) -> Any: # noqa: ANN401
51
+ if isinstance(obj, dict):
52
+ if "$ref" in obj:
53
+ ref = obj["$ref"]
54
+ if ref.startswith("#/$defs/"):
55
+ key = ref.split("/")[-1]
56
+ return _resolve(defs[key])
57
+ return {k: _resolve(v) for k, v in obj.items()}
58
+ if isinstance(obj, list):
59
+ return [_resolve(item) for item in obj]
60
+ return obj
61
+
62
+ return _resolve(schema)
63
+
64
+
65
+ def llm_ready_schema(model: type[BaseModel]) -> dict:
66
+ """Convert a Pydantic model to a JSON schema ready for LLMs.
67
+
68
+ Args:
69
+ model: The Pydantic model to convert.
70
+
71
+ Returns:
72
+ The JSON schema as a dictionary.
73
+ """
74
+ schema = model.model_json_schema(schema_generator=CustomOrderSchema)
75
+ return inline_refs(schema)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.2.6
3
+ Version: 0.2.7
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,13 +7,13 @@ 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=oM4jNCAYWlE0yUk_rpWiJCYE3Pyq4cZrXpraEmj9p-E,190
10
+ digitalkin/__version__.py,sha256=sq3ZYGvIa5rczwcukAFcGJdR0MZ8h0cIPYmDlhbjOP4,190
11
11
  digitalkin/logger.py,sha256=9cDgyJV2QXXT8F--xRODFlZyDgjuTTXNdpCU3GdqCsk,382
12
12
  digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  digitalkin/grpc_servers/__init__.py,sha256=0cJBlwipSmFdXkyH3T0i6OJ1WpAtNsZgYX7JaSnkbtg,804
14
14
  digitalkin/grpc_servers/_base_server.py,sha256=ec4xmgAuOMVg45a63O_PEa2T7mI4tJ6boxcXauFyZ5g,18649
15
15
  digitalkin/grpc_servers/module_server.py,sha256=0jC5MN-H4MGA9O9J87gnlM8EHcbrkb0chuCm8yZh7gI,10232
16
- digitalkin/grpc_servers/module_servicer.py,sha256=byqRpp9m8HS3NUUtKBWFXOncpUWGDW_xsyddA2qwaW4,12294
16
+ digitalkin/grpc_servers/module_servicer.py,sha256=gm1dhmHhBGqY2xlhAp5i15O6waOA1mDHbFJxsT2RUT8,13662
17
17
  digitalkin/grpc_servers/registry_server.py,sha256=PmWaH4Xmg5Sj7NtFVLBNTOzkOfqo7dw_qyVBnaW4jy4,2238
18
18
  digitalkin/grpc_servers/registry_servicer.py,sha256=mCAjNhdMq5DozZMEPsJK__DIxePEYxSWV-gAq-Xctk4,16469
19
19
  digitalkin/grpc_servers/utils/exceptions.py,sha256=I00OM8p8up20He4dU1fiHsvdLj1DymjR_UmoeUm2MSA,785
@@ -29,7 +29,7 @@ digitalkin/models/services/__init__.py,sha256=HsW7MUGFPvH7Ri28WN4BHHBfEQk5dzU_9F
29
29
  digitalkin/models/services/cost.py,sha256=QTEuFD6xz62nob0z4ksE-INJWcZ-iFiuNW5mvXhpFes,1599
30
30
  digitalkin/models/services/storage.py,sha256=cYTVIriGKiprF9OerhSxmc_jM6fUTVwmeon1yQCinkE,143
31
31
  digitalkin/modules/__init__.py,sha256=ppYARmhvdVi55ofC0QZerIempSlcJYDeCXhcl4qXObw,278
32
- digitalkin/modules/_base_module.py,sha256=iVPRlooAGIW4_3lN9loUPe1q6uf62-vSsEE5THvn8eU,9007
32
+ digitalkin/modules/_base_module.py,sha256=WU2oUW4-FjFtieuyRfXv3R3P4N1xT78zzMWrSTdhmAg,9090
33
33
  digitalkin/modules/archetype_module.py,sha256=T2Ehj7EpAC2MO9WQbJv39hqRw7rh3exhVZTEL3JPM8U,421
34
34
  digitalkin/modules/job_manager.py,sha256=QHcrm3F99I9tlNzXefkyhkkwPITf_H1bEb_obMDbUic,6177
35
35
  digitalkin/modules/tool_module.py,sha256=86g0M1wHZ1ReIc7AkKfyjnlGN2QYJBGxrEQpKVlyrZI,421
@@ -67,12 +67,13 @@ digitalkin/services/storage/grpc_storage.py,sha256=eSadiI3JuveItx8LdiC3GbjbjnB8G
67
67
  digitalkin/services/storage/storage_strategy.py,sha256=vGo4aYkEp_GZV11m7vd-xY_Z3gVa5K0gMTzbj2Au_3o,6600
68
68
  digitalkin/utils/__init__.py,sha256=sJnY-ZUgsjMfojAjONC1VN14mhgIDnzyOlGkw21rRnM,28
69
69
  digitalkin/utils/arg_parser.py,sha256=3YyI6oZhhrlTmPTrzlwpQzbCNWDFAT3pggcLxNtJoc0,4388
70
- digitalkin-0.2.6.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
70
+ digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
71
+ digitalkin-0.2.7.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
71
72
  modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
73
  modules/minimal_llm_module.py,sha256=W-E3OrRbAsRJ6hvSeTU8pzmacdJC_PbcWfDapRv5A1A,5617
73
74
  modules/storage_module.py,sha256=bu52lW4RFcWB8VFDhrpBFfCaTSkVL6so3zrkfW4LO9E,6270
74
75
  modules/text_transform_module.py,sha256=1KaA7abwxltKKtbmiW1rkkIK3BTYFPegUq54px0LOQs,7277
75
- digitalkin-0.2.6.dist-info/METADATA,sha256=MSrxM2ATU0easkm4lkbVJdPbUzoH0xDgbmQ622ACwfM,29125
76
- digitalkin-0.2.6.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
77
- digitalkin-0.2.6.dist-info/top_level.txt,sha256=5_5e35inSM5YfWNZE21p5wGBojiVtQQML_WzbEk4BRU,31
78
- digitalkin-0.2.6.dist-info/RECORD,,
76
+ digitalkin-0.2.7.dist-info/METADATA,sha256=1-9MQvIcY6e4S5Cfzea9Nk2lHFx7coGQZ935DzJ00zg,29125
77
+ digitalkin-0.2.7.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
78
+ digitalkin-0.2.7.dist-info/top_level.txt,sha256=5_5e35inSM5YfWNZE21p5wGBojiVtQQML_WzbEk4BRU,31
79
+ digitalkin-0.2.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5