digitalkin 0.3.2.dev18__py3-none-any.whl → 0.3.2.dev20__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 +1 -1
- digitalkin/grpc_servers/module_servicer.py +27 -2
- digitalkin/models/module/setup_types.py +141 -102
- digitalkin/modules/_base_module.py +1 -15
- digitalkin/utils/__init__.py +14 -0
- digitalkin/utils/conditional_schema.py +260 -0
- digitalkin/utils/schema_splitter.py +16 -1
- {digitalkin-0.3.2.dev18.dist-info → digitalkin-0.3.2.dev20.dist-info}/METADATA +1 -1
- {digitalkin-0.3.2.dev18.dist-info → digitalkin-0.3.2.dev20.dist-info}/RECORD +12 -11
- {digitalkin-0.3.2.dev18.dist-info → digitalkin-0.3.2.dev20.dist-info}/WHEEL +0 -0
- {digitalkin-0.3.2.dev18.dist-info → digitalkin-0.3.2.dev20.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.3.2.dev18.dist-info → digitalkin-0.3.2.dev20.dist-info}/top_level.txt +0 -0
digitalkin/__version__.py
CHANGED
|
@@ -17,7 +17,7 @@ from digitalkin.core.job_manager.base_job_manager import BaseJobManager
|
|
|
17
17
|
from digitalkin.grpc_servers.utils.exceptions import ServicerError
|
|
18
18
|
from digitalkin.logger import logger
|
|
19
19
|
from digitalkin.models.core.job_manager_models import JobManagerMode
|
|
20
|
-
from digitalkin.models.module.module import ModuleStatus
|
|
20
|
+
from digitalkin.models.module.module import ModuleCodeModel, ModuleStatus
|
|
21
21
|
from digitalkin.modules._base_module import BaseModule
|
|
22
22
|
from digitalkin.services.registry import GrpcRegistry, RegistryStrategy
|
|
23
23
|
from digitalkin.services.services_models import ServicesMode
|
|
@@ -159,7 +159,32 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
159
159
|
return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
|
|
160
160
|
|
|
161
161
|
updated_setup_data = await self.job_manager.generate_config_setup_module_response(job_id)
|
|
162
|
-
logger.info("Setup
|
|
162
|
+
logger.info("Setup response received", extra={"job_id": job_id})
|
|
163
|
+
|
|
164
|
+
# Check if response is an error
|
|
165
|
+
if isinstance(updated_setup_data, ModuleCodeModel):
|
|
166
|
+
logger.error(
|
|
167
|
+
"Config setup failed",
|
|
168
|
+
extra={"job_id": job_id, "code": updated_setup_data.code, "message": updated_setup_data.message},
|
|
169
|
+
)
|
|
170
|
+
context.set_code(grpc.StatusCode.INTERNAL)
|
|
171
|
+
context.set_details(updated_setup_data.message or "Config setup failed")
|
|
172
|
+
return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
|
|
173
|
+
|
|
174
|
+
if isinstance(updated_setup_data, dict) and "code" in updated_setup_data:
|
|
175
|
+
# ModuleCodeModel was serialized to dict
|
|
176
|
+
logger.error(
|
|
177
|
+
"Config setup failed",
|
|
178
|
+
extra={
|
|
179
|
+
"job_id": job_id,
|
|
180
|
+
"code": updated_setup_data["code"],
|
|
181
|
+
"message": updated_setup_data.get("message"),
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
context.set_code(grpc.StatusCode.INTERNAL)
|
|
185
|
+
context.set_details(updated_setup_data.get("message") or "Config setup failed")
|
|
186
|
+
return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
|
|
187
|
+
|
|
163
188
|
logger.debug("Updated setup data", extra={"job_id": job_id, "setup_data": updated_setup_data})
|
|
164
189
|
setup_version.content = json_format.ParseDict(
|
|
165
190
|
updated_setup_data,
|
|
@@ -29,65 +29,12 @@ SetupModelT = TypeVar("SetupModelT", bound="SetupModel")
|
|
|
29
29
|
class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
30
30
|
"""Base setup model with dynamic schema and tool cache support."""
|
|
31
31
|
|
|
32
|
+
model_config = ConfigDict(extra="allow")
|
|
32
33
|
_clean_model_cache: ClassVar[dict[tuple[type, bool, bool], type]] = {}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
""
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
**kwargs: Keyword arguments passed to parent.
|
|
39
|
-
"""
|
|
40
|
-
super().__init_subclass__(**kwargs)
|
|
41
|
-
cls._inject_tool_cache_fields()
|
|
42
|
-
|
|
43
|
-
@classmethod
|
|
44
|
-
def _inject_tool_cache_fields(cls) -> None:
|
|
45
|
-
"""Inject hidden companion fields for ToolReference annotations."""
|
|
46
|
-
annotations = getattr(cls, "__annotations__", {})
|
|
47
|
-
new_annotations: dict[str, Any] = {}
|
|
48
|
-
|
|
49
|
-
for field_name, annotation in annotations.items():
|
|
50
|
-
if cls._is_tool_reference_annotation(annotation):
|
|
51
|
-
cache_field_name = f"{field_name}_cache"
|
|
52
|
-
if cache_field_name not in annotations:
|
|
53
|
-
# Check if it's a list type
|
|
54
|
-
origin = get_origin(annotation)
|
|
55
|
-
if origin is list:
|
|
56
|
-
new_annotations[cache_field_name] = list[ToolModuleInfo]
|
|
57
|
-
setattr(
|
|
58
|
-
cls,
|
|
59
|
-
cache_field_name,
|
|
60
|
-
Field(default_factory=list, json_schema_extra={"hidden": True}),
|
|
61
|
-
)
|
|
62
|
-
else:
|
|
63
|
-
new_annotations[cache_field_name] = ToolModuleInfo | None
|
|
64
|
-
setattr(
|
|
65
|
-
cls,
|
|
66
|
-
cache_field_name,
|
|
67
|
-
Field(default=None, json_schema_extra={"hidden": True}),
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
if new_annotations:
|
|
71
|
-
cls.__annotations__ = {**annotations, **new_annotations}
|
|
72
|
-
|
|
73
|
-
@classmethod
|
|
74
|
-
def _is_tool_reference_annotation(cls, annotation: object) -> bool:
|
|
75
|
-
"""Check if annotation is ToolReference or Optional[ToolReference].
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
annotation: Type annotation to check.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
True if annotation is or contains ToolReference.
|
|
82
|
-
"""
|
|
83
|
-
origin = get_origin(annotation)
|
|
84
|
-
if origin is typing.Union or origin is types.UnionType:
|
|
85
|
-
return any(
|
|
86
|
-
arg is ToolReference or (isinstance(arg, type) and issubclass(arg, ToolReference))
|
|
87
|
-
for arg in get_args(annotation)
|
|
88
|
-
if arg is not type(None)
|
|
89
|
-
)
|
|
90
|
-
return annotation is ToolReference or (isinstance(annotation, type) and issubclass(annotation, ToolReference))
|
|
34
|
+
resolved_tools: dict[str, ToolModuleInfo] = Field(
|
|
35
|
+
default_factory=dict,
|
|
36
|
+
json_schema_extra={"hidden": True},
|
|
37
|
+
)
|
|
91
38
|
|
|
92
39
|
@classmethod
|
|
93
40
|
async def get_clean_model(
|
|
@@ -285,7 +232,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
285
232
|
if not has_changes:
|
|
286
233
|
return model_cls
|
|
287
234
|
|
|
288
|
-
root_extra =
|
|
235
|
+
root_extra = model_cls.model_config.get("json_schema_extra", {})
|
|
289
236
|
|
|
290
237
|
return create_model(
|
|
291
238
|
model_cls.__name__,
|
|
@@ -346,7 +293,12 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
346
293
|
communication: Communication service for module schemas.
|
|
347
294
|
"""
|
|
348
295
|
logger.info("Starting resolve_tool_references")
|
|
349
|
-
await self._resolve_tool_references_recursive(
|
|
296
|
+
await self._resolve_tool_references_recursive(
|
|
297
|
+
self,
|
|
298
|
+
registry,
|
|
299
|
+
communication,
|
|
300
|
+
self.resolved_tools,
|
|
301
|
+
)
|
|
350
302
|
logger.info("Finished resolve_tool_references")
|
|
351
303
|
|
|
352
304
|
@classmethod
|
|
@@ -355,6 +307,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
355
307
|
model_instance: BaseModel,
|
|
356
308
|
registry: "RegistryStrategy",
|
|
357
309
|
communication: "CommunicationStrategy",
|
|
310
|
+
resolved_tools: dict[str, ToolModuleInfo],
|
|
358
311
|
) -> None:
|
|
359
312
|
"""Recursively resolve ToolReference fields in a model.
|
|
360
313
|
|
|
@@ -362,11 +315,18 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
362
315
|
model_instance: Model instance to process.
|
|
363
316
|
registry: Registry service for resolution.
|
|
364
317
|
communication: Communication service for module schemas.
|
|
318
|
+
resolved_tools: Cache of already resolved tools.
|
|
365
319
|
"""
|
|
366
320
|
for field_name, field_value in model_instance.__dict__.items():
|
|
367
321
|
if field_value is None:
|
|
368
322
|
continue
|
|
369
|
-
await cls._resolve_field_value(
|
|
323
|
+
await cls._resolve_field_value(
|
|
324
|
+
field_name,
|
|
325
|
+
field_value,
|
|
326
|
+
registry,
|
|
327
|
+
communication,
|
|
328
|
+
resolved_tools,
|
|
329
|
+
)
|
|
370
330
|
|
|
371
331
|
@classmethod
|
|
372
332
|
async def _resolve_field_value(
|
|
@@ -375,6 +335,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
375
335
|
field_value: "BaseModel | ToolReference | list | dict",
|
|
376
336
|
registry: "RegistryStrategy",
|
|
377
337
|
communication: "CommunicationStrategy",
|
|
338
|
+
resolved_tools: dict[str, ToolModuleInfo],
|
|
378
339
|
) -> None:
|
|
379
340
|
"""Resolve a single field value based on its type.
|
|
380
341
|
|
|
@@ -383,15 +344,37 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
383
344
|
field_value: Value to process.
|
|
384
345
|
registry: Registry service for resolution.
|
|
385
346
|
communication: Communication service for module schemas.
|
|
347
|
+
resolved_tools: Cache of already resolved tools.
|
|
386
348
|
"""
|
|
387
349
|
if isinstance(field_value, ToolReference):
|
|
388
|
-
await cls._resolve_single_tool_reference(
|
|
350
|
+
await cls._resolve_single_tool_reference(
|
|
351
|
+
field_name,
|
|
352
|
+
field_value,
|
|
353
|
+
registry,
|
|
354
|
+
communication,
|
|
355
|
+
resolved_tools,
|
|
356
|
+
)
|
|
389
357
|
elif isinstance(field_value, BaseModel):
|
|
390
|
-
await cls._resolve_tool_references_recursive(
|
|
358
|
+
await cls._resolve_tool_references_recursive(
|
|
359
|
+
field_value,
|
|
360
|
+
registry,
|
|
361
|
+
communication,
|
|
362
|
+
resolved_tools,
|
|
363
|
+
)
|
|
391
364
|
elif isinstance(field_value, list):
|
|
392
|
-
await cls._resolve_list_items(
|
|
365
|
+
await cls._resolve_list_items(
|
|
366
|
+
field_value,
|
|
367
|
+
registry,
|
|
368
|
+
communication,
|
|
369
|
+
resolved_tools,
|
|
370
|
+
)
|
|
393
371
|
elif isinstance(field_value, dict):
|
|
394
|
-
await cls._resolve_dict_values(
|
|
372
|
+
await cls._resolve_dict_values(
|
|
373
|
+
field_value,
|
|
374
|
+
registry,
|
|
375
|
+
communication,
|
|
376
|
+
resolved_tools,
|
|
377
|
+
)
|
|
395
378
|
|
|
396
379
|
@classmethod
|
|
397
380
|
async def _resolve_single_tool_reference(
|
|
@@ -400,6 +383,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
400
383
|
tool_ref: ToolReference,
|
|
401
384
|
registry: "RegistryStrategy",
|
|
402
385
|
communication: "CommunicationStrategy",
|
|
386
|
+
resolved_tools: dict[str, ToolModuleInfo],
|
|
403
387
|
) -> None:
|
|
404
388
|
"""Resolve a single ToolReference.
|
|
405
389
|
|
|
@@ -408,17 +392,33 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
408
392
|
tool_ref: ToolReference to resolve.
|
|
409
393
|
registry: Registry service for resolution.
|
|
410
394
|
communication: Communication service for module schemas.
|
|
395
|
+
resolved_tools: Cache of already resolved tools.
|
|
411
396
|
"""
|
|
412
397
|
logger.info("Resolving ToolReference '%s' with setup_id='%s'", field_name, tool_ref.config.setup_id)
|
|
398
|
+
|
|
399
|
+
slug = tool_ref.slug
|
|
400
|
+
if slug:
|
|
401
|
+
cached = resolved_tools.get(slug)
|
|
402
|
+
if cached:
|
|
403
|
+
tool_ref._cached_info = cached # noqa: SLF001
|
|
404
|
+
logger.info("ToolReference '%s' resolved from cache -> %s", field_name, cached)
|
|
405
|
+
return
|
|
406
|
+
|
|
413
407
|
try:
|
|
414
|
-
await tool_ref.resolve(registry, communication)
|
|
408
|
+
info = await tool_ref.resolve(registry, communication)
|
|
409
|
+
if info and info.setup_id:
|
|
410
|
+
resolved_tools[info.setup_id] = info
|
|
415
411
|
logger.info("Resolved ToolReference '%s' -> %s", field_name, tool_ref.tool_module_info)
|
|
416
412
|
except Exception:
|
|
417
413
|
logger.exception("Failed to resolve ToolReference '%s'", field_name)
|
|
418
414
|
|
|
419
415
|
@classmethod
|
|
420
416
|
async def _resolve_list_items(
|
|
421
|
-
cls,
|
|
417
|
+
cls,
|
|
418
|
+
items: list,
|
|
419
|
+
registry: "RegistryStrategy",
|
|
420
|
+
communication: "CommunicationStrategy",
|
|
421
|
+
resolved_tools: dict[str, ToolModuleInfo],
|
|
422
422
|
) -> None:
|
|
423
423
|
"""Resolve ToolReference instances in a list.
|
|
424
424
|
|
|
@@ -426,16 +426,32 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
426
426
|
items: List of items to process.
|
|
427
427
|
registry: Registry service for resolution.
|
|
428
428
|
communication: Communication service for module schemas.
|
|
429
|
+
resolved_tools: Cache of already resolved tools.
|
|
429
430
|
"""
|
|
430
431
|
for item in items:
|
|
431
432
|
if isinstance(item, ToolReference):
|
|
432
|
-
await cls._resolve_single_tool_reference(
|
|
433
|
+
await cls._resolve_single_tool_reference(
|
|
434
|
+
"list_item",
|
|
435
|
+
item,
|
|
436
|
+
registry,
|
|
437
|
+
communication,
|
|
438
|
+
resolved_tools,
|
|
439
|
+
)
|
|
433
440
|
elif isinstance(item, BaseModel):
|
|
434
|
-
await cls._resolve_tool_references_recursive(
|
|
441
|
+
await cls._resolve_tool_references_recursive(
|
|
442
|
+
item,
|
|
443
|
+
registry,
|
|
444
|
+
communication,
|
|
445
|
+
resolved_tools,
|
|
446
|
+
)
|
|
435
447
|
|
|
436
448
|
@classmethod
|
|
437
449
|
async def _resolve_dict_values(
|
|
438
|
-
cls,
|
|
450
|
+
cls,
|
|
451
|
+
mapping: dict,
|
|
452
|
+
registry: "RegistryStrategy",
|
|
453
|
+
communication: "CommunicationStrategy",
|
|
454
|
+
resolved_tools: dict[str, ToolModuleInfo],
|
|
439
455
|
) -> None:
|
|
440
456
|
"""Resolve ToolReference instances in dict values.
|
|
441
457
|
|
|
@@ -443,63 +459,86 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
443
459
|
mapping: Dict to process.
|
|
444
460
|
registry: Registry service for resolution.
|
|
445
461
|
communication: Communication service for module schemas.
|
|
462
|
+
resolved_tools: Cache of already resolved tools.
|
|
446
463
|
"""
|
|
447
464
|
for item in mapping.values():
|
|
448
465
|
if isinstance(item, ToolReference):
|
|
449
|
-
await cls._resolve_single_tool_reference(
|
|
466
|
+
await cls._resolve_single_tool_reference(
|
|
467
|
+
"dict_value",
|
|
468
|
+
item,
|
|
469
|
+
registry,
|
|
470
|
+
communication,
|
|
471
|
+
resolved_tools,
|
|
472
|
+
)
|
|
450
473
|
elif isinstance(item, BaseModel):
|
|
451
|
-
await cls._resolve_tool_references_recursive(
|
|
474
|
+
await cls._resolve_tool_references_recursive(
|
|
475
|
+
item,
|
|
476
|
+
registry,
|
|
477
|
+
communication,
|
|
478
|
+
resolved_tools,
|
|
479
|
+
)
|
|
452
480
|
|
|
453
481
|
def build_tool_cache(self) -> ToolCache:
|
|
454
|
-
"""Build tool cache from resolved ToolReferences
|
|
482
|
+
"""Build tool cache from resolved ToolReferences.
|
|
455
483
|
|
|
456
484
|
Returns:
|
|
457
485
|
ToolCache with field names as keys and ToolModuleInfo as values.
|
|
458
486
|
"""
|
|
459
|
-
logger.info("Building tool cache")
|
|
460
487
|
cache = ToolCache()
|
|
461
488
|
self._build_tool_cache_recursive(self, cache)
|
|
462
489
|
logger.info("Tool cache built: %d entries", len(cache.entries))
|
|
463
490
|
return cache
|
|
464
491
|
|
|
465
|
-
def _build_tool_cache_recursive(self, model_instance: BaseModel, cache: ToolCache) -> None:
|
|
466
|
-
"""Recursively build tool cache
|
|
492
|
+
def _build_tool_cache_recursive(self, model_instance: BaseModel, cache: ToolCache) -> None:
|
|
493
|
+
"""Recursively build tool cache from ToolReferences.
|
|
467
494
|
|
|
468
495
|
Args:
|
|
469
496
|
model_instance: Model instance to process.
|
|
470
497
|
cache: ToolCache to populate.
|
|
471
498
|
"""
|
|
472
|
-
for
|
|
499
|
+
for field_value in model_instance.__dict__.values():
|
|
473
500
|
if field_value is None:
|
|
474
501
|
continue
|
|
475
502
|
if isinstance(field_value, ToolReference):
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
cached_info = getattr(model_instance, cache_field_name, None)
|
|
479
|
-
module_info = field_value.tool_module_info or cached_info
|
|
503
|
+
module_info = self.resolved_tools.get(field_value.slug or "") or field_value.tool_module_info
|
|
480
504
|
if module_info:
|
|
481
|
-
|
|
482
|
-
setattr(model_instance, cache_field_name, module_info)
|
|
505
|
+
self.resolved_tools[module_info.setup_id] = module_info
|
|
483
506
|
cache.add(module_info.module_id, module_info)
|
|
484
|
-
logger.debug("Added tool to cache: %s", module_info.module_id)
|
|
485
507
|
elif isinstance(field_value, BaseModel):
|
|
486
508
|
self._build_tool_cache_recursive(field_value, cache)
|
|
487
509
|
elif isinstance(field_value, list):
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
510
|
+
self._process_list_items(field_value, cache)
|
|
511
|
+
elif isinstance(field_value, dict):
|
|
512
|
+
self._process_dict_values(field_value, cache)
|
|
513
|
+
|
|
514
|
+
def _process_list_items(self, items: list, cache: ToolCache) -> None:
|
|
515
|
+
"""Process list items for ToolReferences.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
items: List to process.
|
|
519
|
+
cache: ToolCache to populate.
|
|
520
|
+
"""
|
|
521
|
+
for item in items:
|
|
522
|
+
if isinstance(item, ToolReference):
|
|
523
|
+
module_info = self.resolved_tools.get(item.slug or "") or item.tool_module_info
|
|
524
|
+
if module_info:
|
|
525
|
+
self.resolved_tools[module_info.setup_id] = module_info
|
|
526
|
+
cache.add(module_info.module_id, module_info)
|
|
527
|
+
elif isinstance(item, BaseModel):
|
|
528
|
+
self._build_tool_cache_recursive(item, cache)
|
|
529
|
+
|
|
530
|
+
def _process_dict_values(self, mapping: dict, cache: ToolCache) -> None:
|
|
531
|
+
"""Process dict values for ToolReferences.
|
|
532
|
+
|
|
533
|
+
Args:
|
|
534
|
+
mapping: Dict to process.
|
|
535
|
+
cache: ToolCache to populate.
|
|
536
|
+
"""
|
|
537
|
+
for item in mapping.values():
|
|
538
|
+
if isinstance(item, ToolReference):
|
|
539
|
+
module_info = self.resolved_tools.get(item.slug or "") or item.tool_module_info
|
|
540
|
+
if module_info:
|
|
541
|
+
self.resolved_tools[module_info.setup_id] = module_info
|
|
542
|
+
cache.add(module_info.module_id, module_info)
|
|
543
|
+
elif isinstance(item, BaseModel):
|
|
544
|
+
self._build_tool_cache_recursive(item, cache)
|
|
@@ -556,21 +556,7 @@ class BaseModule( # noqa: PLR0904
|
|
|
556
556
|
await self._resolve_tools(config_setup_data)
|
|
557
557
|
updated_config = await self.run_config_setup(self.context, config_setup_data)
|
|
558
558
|
|
|
559
|
-
|
|
560
|
-
wrapper = config_setup_data.model_dump()
|
|
561
|
-
wrapper["content"] = updated_config.model_dump()
|
|
562
|
-
|
|
563
|
-
# Debug logging
|
|
564
|
-
content = wrapper.get("content", {})
|
|
565
|
-
logger.info(
|
|
566
|
-
"Config setup wrapper: keys=%s, content_keys=%s, tools_cache=%s",
|
|
567
|
-
list(wrapper.keys()),
|
|
568
|
-
list(content.keys()) if isinstance(content, dict) else "N/A",
|
|
569
|
-
content.get("tools_cache") if isinstance(content, dict) else "N/A",
|
|
570
|
-
extra=self.context.session.current_ids(),
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
setup_model = await self.create_setup_model(wrapper)
|
|
559
|
+
setup_model = await self.create_setup_model(updated_config.model_dump())
|
|
574
560
|
await callback(setup_model)
|
|
575
561
|
self._status = ModuleStatus.STOPPING
|
|
576
562
|
except Exception:
|
digitalkin/utils/__init__.py
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"""General utils folder."""
|
|
2
2
|
|
|
3
|
+
from digitalkin.utils.conditional_schema import (
|
|
4
|
+
Conditional,
|
|
5
|
+
ConditionalField,
|
|
6
|
+
ConditionalSchemaMixin,
|
|
7
|
+
get_conditional_metadata,
|
|
8
|
+
has_conditional,
|
|
9
|
+
)
|
|
3
10
|
from digitalkin.utils.dynamic_schema import (
|
|
4
11
|
DEFAULT_TIMEOUT,
|
|
5
12
|
Dynamic,
|
|
@@ -14,13 +21,20 @@ from digitalkin.utils.dynamic_schema import (
|
|
|
14
21
|
)
|
|
15
22
|
|
|
16
23
|
__all__ = [
|
|
24
|
+
# Dynamic schema
|
|
17
25
|
"DEFAULT_TIMEOUT",
|
|
26
|
+
# Conditional schema
|
|
27
|
+
"Conditional",
|
|
28
|
+
"ConditionalField",
|
|
29
|
+
"ConditionalSchemaMixin",
|
|
18
30
|
"Dynamic",
|
|
19
31
|
"DynamicField",
|
|
20
32
|
"Fetcher",
|
|
21
33
|
"ResolveResult",
|
|
34
|
+
"get_conditional_metadata",
|
|
22
35
|
"get_dynamic_metadata",
|
|
23
36
|
"get_fetchers",
|
|
37
|
+
"has_conditional",
|
|
24
38
|
"has_dynamic",
|
|
25
39
|
"resolve",
|
|
26
40
|
"resolve_safe",
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Conditional field visibility for react-jsonschema-form.
|
|
2
|
+
|
|
3
|
+
This module provides a clean way to mark fields as conditional using Annotated metadata,
|
|
4
|
+
generating JSON Schema with if/then clauses for react-jsonschema-form.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
from typing import Annotated, Literal
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
from digitalkin.utils import Conditional, ConditionalSchemaMixin
|
|
10
|
+
|
|
11
|
+
class Tools(ConditionalSchemaMixin, BaseModel):
|
|
12
|
+
web_search_enabled: bool = Field(...)
|
|
13
|
+
|
|
14
|
+
web_search_engine: Annotated[
|
|
15
|
+
Literal["duckduckgo", "tavily"],
|
|
16
|
+
Conditional(trigger="web_search_enabled", show_when=True),
|
|
17
|
+
] = Field(...)
|
|
18
|
+
|
|
19
|
+
See Also:
|
|
20
|
+
- Documentation: docs/api/conditional_schema.md
|
|
21
|
+
- Tests: tests/utils/test_conditional_schema.py
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from typing import TYPE_CHECKING, Any, ClassVar
|
|
28
|
+
|
|
29
|
+
from pydantic import BaseModel
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from pydantic.annotated_handlers import GetJsonSchemaHandler
|
|
33
|
+
from pydantic.fields import FieldInfo
|
|
34
|
+
from pydantic.json_schema import JsonSchemaValue
|
|
35
|
+
from pydantic_core.core_schema import CoreSchema
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class ConditionalField:
|
|
40
|
+
"""Metadata for conditional field visibility.
|
|
41
|
+
|
|
42
|
+
Use with typing.Annotated to mark fields that should only appear
|
|
43
|
+
when a trigger field has a specific value.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
trigger: Name of the field that controls visibility.
|
|
47
|
+
show_when: Value(s) that trigger field must have to show this field.
|
|
48
|
+
Can be a boolean, string, or list of strings for multiple values.
|
|
49
|
+
required_when_shown: Whether field is required when visible. Defaults to True.
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
# Boolean condition
|
|
53
|
+
web_search_engine: Annotated[
|
|
54
|
+
str,
|
|
55
|
+
Conditional(trigger="web_search_enabled", show_when=True),
|
|
56
|
+
] = Field(...)
|
|
57
|
+
|
|
58
|
+
# Enum condition
|
|
59
|
+
advanced_option: Annotated[
|
|
60
|
+
str,
|
|
61
|
+
Conditional(trigger="mode", show_when="advanced"),
|
|
62
|
+
] = Field(...)
|
|
63
|
+
|
|
64
|
+
# Multiple values condition
|
|
65
|
+
shared_feature: Annotated[
|
|
66
|
+
bool,
|
|
67
|
+
Conditional(trigger="mode", show_when=["standard", "advanced"]),
|
|
68
|
+
] = Field(...)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
trigger: str
|
|
72
|
+
show_when: bool | str | list[str]
|
|
73
|
+
required_when_shown: bool = True
|
|
74
|
+
|
|
75
|
+
def __post_init__(self) -> None:
|
|
76
|
+
"""Normalize single-item lists to scalar values."""
|
|
77
|
+
if isinstance(self.show_when, list) and len(self.show_when) == 1:
|
|
78
|
+
self.show_when = self.show_when[0]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Short alias for cleaner API
|
|
82
|
+
Conditional = ConditionalField
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_conditional_metadata(field_info: FieldInfo) -> ConditionalField | None:
|
|
86
|
+
"""Extract ConditionalField from field metadata.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
field_info: The Pydantic FieldInfo object to inspect.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The ConditionalField metadata instance if found, None otherwise.
|
|
93
|
+
"""
|
|
94
|
+
for meta in field_info.metadata:
|
|
95
|
+
if isinstance(meta, ConditionalField):
|
|
96
|
+
return meta
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def has_conditional(field_info: FieldInfo) -> bool:
|
|
101
|
+
"""Check if field has ConditionalField metadata.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
field_info: The Pydantic FieldInfo object to check.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True if the field has ConditionalField metadata, False otherwise.
|
|
108
|
+
"""
|
|
109
|
+
return get_conditional_metadata(field_info) is not None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _collect_conditions(
|
|
113
|
+
model_fields: dict[str, FieldInfo],
|
|
114
|
+
props: dict[str, Any],
|
|
115
|
+
) -> tuple[dict[tuple[str, Any], list[tuple[str, bool]]], set[str]]:
|
|
116
|
+
"""Collect conditional fields grouped by trigger and show_when value.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
model_fields: The model's field definitions.
|
|
120
|
+
props: The schema properties dict.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Tuple of (conditions dict, fields to remove set).
|
|
124
|
+
"""
|
|
125
|
+
conditions: dict[tuple[str, Any], list[tuple[str, bool]]] = {}
|
|
126
|
+
fields_to_remove: set[str] = set()
|
|
127
|
+
|
|
128
|
+
for field_name, field_info in model_fields.items():
|
|
129
|
+
cond = get_conditional_metadata(field_info)
|
|
130
|
+
if cond is None or field_name not in props:
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
show_key = tuple(cond.show_when) if isinstance(cond.show_when, list) else cond.show_when
|
|
134
|
+
key = (cond.trigger, show_key)
|
|
135
|
+
|
|
136
|
+
if key not in conditions:
|
|
137
|
+
conditions[key] = []
|
|
138
|
+
conditions[key].append((field_name, cond.required_when_shown))
|
|
139
|
+
fields_to_remove.add(field_name)
|
|
140
|
+
|
|
141
|
+
return conditions, fields_to_remove
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _build_if_clause(trigger: str, *, show_when: bool | str | tuple[str, ...]) -> dict[str, Any]:
|
|
145
|
+
"""Build the if clause for a conditional.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
trigger: The trigger field name.
|
|
149
|
+
show_when: The value(s) that trigger visibility.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
The if clause dict.
|
|
153
|
+
"""
|
|
154
|
+
if isinstance(show_when, tuple):
|
|
155
|
+
return {"properties": {trigger: {"enum": list(show_when)}}, "required": [trigger]}
|
|
156
|
+
return {"properties": {trigger: {"const": show_when}}, "required": [trigger]}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _resolve_field_schema(
|
|
160
|
+
field_schema: dict[str, Any],
|
|
161
|
+
handler: GetJsonSchemaHandler,
|
|
162
|
+
) -> dict[str, Any]:
|
|
163
|
+
"""Resolve $ref in field schema if present.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
field_schema: The field's schema dict.
|
|
167
|
+
handler: The JSON schema handler for resolving refs.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
The resolved schema dict.
|
|
171
|
+
"""
|
|
172
|
+
if "$ref" not in field_schema:
|
|
173
|
+
return field_schema
|
|
174
|
+
|
|
175
|
+
resolved = handler.resolve_ref_schema(field_schema)
|
|
176
|
+
extra = {k: v for k, v in field_schema.items() if k != "$ref"}
|
|
177
|
+
return {**resolved, **extra}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class ConditionalSchemaMixin(BaseModel):
|
|
181
|
+
"""Mixin for automatic conditional field processing in JSON schema.
|
|
182
|
+
|
|
183
|
+
Inherit from this mixin to automatically generate JSON Schema with
|
|
184
|
+
if/then clauses for fields marked with ConditionalField metadata.
|
|
185
|
+
|
|
186
|
+
The mixin processes Annotated fields with Conditional metadata and:
|
|
187
|
+
1. Removes conditional fields from main properties
|
|
188
|
+
2. Adds them to allOf with if/then clauses
|
|
189
|
+
3. Groups multiple fields with the same condition together
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
class Config(ConditionalSchemaMixin, BaseModel):
|
|
193
|
+
mode: Literal["basic", "advanced"] = Field(...)
|
|
194
|
+
|
|
195
|
+
advanced_option: Annotated[
|
|
196
|
+
str,
|
|
197
|
+
Conditional(trigger="mode", show_when="advanced"),
|
|
198
|
+
] = Field(...)
|
|
199
|
+
|
|
200
|
+
# Generates schema with:
|
|
201
|
+
# {
|
|
202
|
+
# "properties": {"mode": {...}},
|
|
203
|
+
# "allOf": [{
|
|
204
|
+
# "if": {"properties": {"mode": {"const": "advanced"}}},
|
|
205
|
+
# "then": {"properties": {"advanced_option": {...}}}
|
|
206
|
+
# }]
|
|
207
|
+
# }
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
model_fields: ClassVar[dict[str, FieldInfo]] # type: ignore[misc]
|
|
211
|
+
|
|
212
|
+
@classmethod
|
|
213
|
+
def __get_pydantic_json_schema__( # noqa: PLW3201
|
|
214
|
+
cls,
|
|
215
|
+
core_schema: CoreSchema,
|
|
216
|
+
handler: GetJsonSchemaHandler,
|
|
217
|
+
) -> JsonSchemaValue:
|
|
218
|
+
"""Generate JSON schema with conditional field handling.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
core_schema: The Pydantic core schema.
|
|
222
|
+
handler: The JSON schema handler for resolving refs.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The JSON schema with if/then clauses for conditional fields.
|
|
226
|
+
"""
|
|
227
|
+
schema = handler(core_schema)
|
|
228
|
+
props = schema.get("properties", {})
|
|
229
|
+
if not props:
|
|
230
|
+
return schema
|
|
231
|
+
|
|
232
|
+
conditions, fields_to_remove = _collect_conditions(cls.model_fields, props)
|
|
233
|
+
if not conditions:
|
|
234
|
+
return schema
|
|
235
|
+
|
|
236
|
+
all_of = schema.setdefault("allOf", [])
|
|
237
|
+
|
|
238
|
+
for (trigger, show_when), field_list in conditions.items():
|
|
239
|
+
then_props: dict[str, Any] = {}
|
|
240
|
+
then_required: list[str] = []
|
|
241
|
+
|
|
242
|
+
for field_name, required in field_list:
|
|
243
|
+
then_props[field_name] = _resolve_field_schema(props[field_name], handler)
|
|
244
|
+
if required:
|
|
245
|
+
then_required.append(field_name)
|
|
246
|
+
|
|
247
|
+
if_clause = _build_if_clause(trigger, show_when=show_when)
|
|
248
|
+
then_clause: dict[str, Any] = {"properties": then_props}
|
|
249
|
+
if then_required:
|
|
250
|
+
then_clause["required"] = then_required
|
|
251
|
+
|
|
252
|
+
all_of.append({"if": if_clause, "then": then_clause})
|
|
253
|
+
|
|
254
|
+
for field_name in fields_to_remove:
|
|
255
|
+
del props[field_name]
|
|
256
|
+
|
|
257
|
+
if "required" in schema:
|
|
258
|
+
schema["required"] = [r for r in schema["required"] if r not in fields_to_remove]
|
|
259
|
+
|
|
260
|
+
return schema
|
|
@@ -81,6 +81,9 @@ class SchemaSplitter:
|
|
|
81
81
|
json_target["properties"] = {}
|
|
82
82
|
for prop_name, prop_value in value.items():
|
|
83
83
|
if isinstance(prop_value, dict):
|
|
84
|
+
# Skip hidden fields
|
|
85
|
+
if prop_value.get("hidden") is True:
|
|
86
|
+
continue
|
|
84
87
|
json_target["properties"][prop_name] = {}
|
|
85
88
|
prop_ui: dict[str, Any] = {}
|
|
86
89
|
cls._process_property(prop_value, json_target["properties"][prop_name], prop_ui, defs_ui)
|
|
@@ -118,6 +121,9 @@ class SchemaSplitter:
|
|
|
118
121
|
cls._strip_ui_properties(value, json_target[key])
|
|
119
122
|
# Extract UI properties from conditional
|
|
120
123
|
cls._extract_ui_properties(value, ui_target)
|
|
124
|
+
elif key == "hidden":
|
|
125
|
+
# Strip hidden key from json schema
|
|
126
|
+
continue
|
|
121
127
|
else:
|
|
122
128
|
json_target[key] = value
|
|
123
129
|
|
|
@@ -151,6 +157,9 @@ class SchemaSplitter:
|
|
|
151
157
|
json_target["properties"] = {}
|
|
152
158
|
for prop_name, prop_value in value.items():
|
|
153
159
|
if isinstance(prop_value, dict):
|
|
160
|
+
# Skip hidden fields
|
|
161
|
+
if prop_value.get("hidden") is True:
|
|
162
|
+
continue
|
|
154
163
|
json_target["properties"][prop_name] = {}
|
|
155
164
|
prop_ui: dict[str, Any] = {}
|
|
156
165
|
cls._process_property(prop_value, json_target["properties"][prop_name], prop_ui, defs_ui)
|
|
@@ -164,6 +173,9 @@ class SchemaSplitter:
|
|
|
164
173
|
cls._process_property(value, json_target["items"], items_ui, defs_ui)
|
|
165
174
|
if items_ui:
|
|
166
175
|
ui_target["items"] = items_ui
|
|
176
|
+
elif key == "hidden":
|
|
177
|
+
# Strip hidden key from json schema
|
|
178
|
+
continue
|
|
167
179
|
else:
|
|
168
180
|
json_target[key] = value
|
|
169
181
|
|
|
@@ -176,12 +188,15 @@ class SchemaSplitter:
|
|
|
176
188
|
json_target: Target dict without ui:* properties.
|
|
177
189
|
"""
|
|
178
190
|
for key, value in source.items():
|
|
179
|
-
if key.startswith("ui:"):
|
|
191
|
+
if key.startswith("ui:") or key == "hidden":
|
|
180
192
|
continue
|
|
181
193
|
if key == "properties" and isinstance(value, dict):
|
|
182
194
|
json_target["properties"] = {}
|
|
183
195
|
for prop_name, prop_value in value.items():
|
|
184
196
|
if isinstance(prop_value, dict):
|
|
197
|
+
# Skip hidden fields
|
|
198
|
+
if prop_value.get("hidden") is True:
|
|
199
|
+
continue
|
|
185
200
|
json_target["properties"][prop_name] = {}
|
|
186
201
|
cls._strip_ui_properties(prop_value, json_target["properties"][prop_name])
|
|
187
202
|
else:
|
|
@@ -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=
|
|
10
|
+
digitalkin/__version__.py,sha256=dN3fPmuqWC0wGuJWF7p4H8rHGbT_gGfHvNrei8I6TU8,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,7 +28,7 @@ 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=
|
|
31
|
+
digitalkin/grpc_servers/module_servicer.py,sha256=KsWXPwCQ0s2pygn0cq89KndJleAkjvkvLFs1Z2fWQIk,21705
|
|
32
32
|
digitalkin/grpc_servers/utils/__init__.py,sha256=ZnAIb_F8z4NhtPypqkdmzgRSzolKnJTk3oZx5GfWH5Y,38
|
|
33
33
|
digitalkin/grpc_servers/utils/exceptions.py,sha256=LtaDtlqXCeT6iqApogs4pbtezotOVeg4fhnFzGBvFsY,692
|
|
34
34
|
digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=nGG8QdKnBH0UG9qbKrlPwIvcvPgW3osw7O3cImxisPE,3279
|
|
@@ -55,7 +55,7 @@ digitalkin/models/module/base_types.py,sha256=oIylVNqo0idTFj4dRgCt7P19daNZ-AlvgC
|
|
|
55
55
|
digitalkin/models/module/module.py,sha256=k0W8vfJJFth8XdDzkHm32SyTuSf3h2qF0hSrxAfGF1s,956
|
|
56
56
|
digitalkin/models/module/module_context.py,sha256=QDdjZdhIJpvU_2Tn7kkJsZ1givB4dM1-ksopdF4VySw,12176
|
|
57
57
|
digitalkin/models/module/module_types.py,sha256=C9azCNBk76xMa-Mww8_6AiwQR8MLAsEyUOvBYxytovI,739
|
|
58
|
-
digitalkin/models/module/setup_types.py,sha256=
|
|
58
|
+
digitalkin/models/module/setup_types.py,sha256=Xb_KZ5vKcpLqkbPdQfooqtI6TFp2cklvdXLUBa55c30,19346
|
|
59
59
|
digitalkin/models/module/tool_cache.py,sha256=5e30A_GxT2W-w1LZFmVUqOxDjPcrZ8s_eW7p9impO64,7153
|
|
60
60
|
digitalkin/models/module/tool_reference.py,sha256=eIWJrT6syyEaXAWRXIlWYTst-j0XuvtU_va9m3tj_KU,4470
|
|
61
61
|
digitalkin/models/module/utility.py,sha256=gnbYfWpXGbomUI0fWf7T-Qm_VvT-LXDv1OuA9zObwVg,5589
|
|
@@ -64,7 +64,7 @@ digitalkin/models/services/cost.py,sha256=9PXvd5RrIk9vCrRjcUGQ9ZyAokEbwLg4s0RfnE
|
|
|
64
64
|
digitalkin/models/services/registry.py,sha256=mFehnPAVLGimodHquNrltXbH_aE0jEa-PxfyNm6J38E,1828
|
|
65
65
|
digitalkin/models/services/storage.py,sha256=wp7F-AvTsU46ujGPcguqM5kUKRZx4399D4EGAAJt2zs,1143
|
|
66
66
|
digitalkin/modules/__init__.py,sha256=vTQk8DWopxQSJ17BjE5dNhq247Rou55iQLJdBxoPUmo,296
|
|
67
|
-
digitalkin/modules/_base_module.py,sha256=
|
|
67
|
+
digitalkin/modules/_base_module.py,sha256=0XC0aQAxlNfvz0KK9ut7K0JbZql3cZMU4aeg7ISEsD0,21971
|
|
68
68
|
digitalkin/modules/archetype_module.py,sha256=XC9tl1Yr6QlbPn_x0eov6UUZwQgwW--BYPPMYVJH_NU,505
|
|
69
69
|
digitalkin/modules/tool_module.py,sha256=GBis7bKCkvWFCYLRvaS9oZVmLBBve1w8BhVnKOU2sCc,506
|
|
70
70
|
digitalkin/modules/trigger_handler.py,sha256=qPNMi-8NHqscOxciHeaXtpwjXApT3YzjMF23zQAjaZY,1770
|
|
@@ -115,14 +115,15 @@ digitalkin/services/user_profile/__init__.py,sha256=RKEZCsgCHS7fmswhWgUoQd6vZ_1p
|
|
|
115
115
|
digitalkin/services/user_profile/default_user_profile.py,sha256=46DH_VBCHKXJVyagVcc8kH5sLwRK54Fe_0ahqYJ1maA,1847
|
|
116
116
|
digitalkin/services/user_profile/grpc_user_profile.py,sha256=xDiUC5Ceofa6QtGPmqJV3ik5j8HDHc1zxtpia49rlRw,2780
|
|
117
117
|
digitalkin/services/user_profile/user_profile_strategy.py,sha256=CH8kT__1MUwA21k5djjmB5ZZ6pYg57OWbe_7owBCgwU,681
|
|
118
|
-
digitalkin/utils/__init__.py,sha256=
|
|
118
|
+
digitalkin/utils/__init__.py,sha256=_RXXALotIr_JToSQdhwDi3eizghKz9KrQtvC8Um80H8,808
|
|
119
119
|
digitalkin/utils/arg_parser.py,sha256=wzscRlE1Qp1gGl-lAJlkkwnbU1O2oezj6BwK_BZFBIk,3158
|
|
120
|
+
digitalkin/utils/conditional_schema.py,sha256=ZyGvY-DWkM_d0OuLElUpqnvUeDgZ2hXX3dFnKXbgL5k,8373
|
|
120
121
|
digitalkin/utils/development_mode_action.py,sha256=2hznh0ajW_4ZTysfoc0Y49161f_PQPATRgNk8NAn1_o,1623
|
|
121
122
|
digitalkin/utils/dynamic_schema.py,sha256=y5csxjuqVHjWDpnTUzxbcUuI_wou9-ibRVHQlBs_btY,15275
|
|
122
123
|
digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
|
|
123
124
|
digitalkin/utils/package_discover.py,sha256=sa6Zp5Kape1Zr4iYiNrnZxiHDnqM06ODk6yfWHom53w,13465
|
|
124
|
-
digitalkin/utils/schema_splitter.py,sha256=
|
|
125
|
-
digitalkin-0.3.2.
|
|
125
|
+
digitalkin/utils/schema_splitter.py,sha256=7KFiRUhFE9HsH_Z8p0POSbIhiJlgZEeSxL_7TDY0n1U,10374
|
|
126
|
+
digitalkin-0.3.2.dev20.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
|
|
126
127
|
modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
127
128
|
modules/archetype_with_tools_module.py,sha256=PXTS6IXmC_OjxTmVrL_pYVI0MKwXjD5I1UJO_2xa10Q,7632
|
|
128
129
|
modules/cpu_intensive_module.py,sha256=GZlirQDZdYuXrI46sv1q4RNAHZjL4EptHVQTvgK9zz8,8363
|
|
@@ -137,7 +138,7 @@ monitoring/digitalkin_observability/prometheus.py,sha256=gDmM9ySaVwPAe7Yg84pLxmE
|
|
|
137
138
|
monitoring/tests/test_metrics.py,sha256=ugnYfAwqBPO6zA8z4afKTlyBWECTivacYSN-URQCn2E,5856
|
|
138
139
|
services/filesystem_module.py,sha256=U4dgqtuDadaXz8PJ1d_uQ_1EPncBqudAQCLUICF9yL4,7421
|
|
139
140
|
services/storage_module.py,sha256=Wz2MzLvqs2D_bnBBgtnujYcAKK2V2KFMk8K21RoepSE,6972
|
|
140
|
-
digitalkin-0.3.2.
|
|
141
|
-
digitalkin-0.3.2.
|
|
142
|
-
digitalkin-0.3.2.
|
|
143
|
-
digitalkin-0.3.2.
|
|
141
|
+
digitalkin-0.3.2.dev20.dist-info/METADATA,sha256=EosqD8N82EhYPNyjWKp-_n2az8875Qp8iRfdZph5uB4,29725
|
|
142
|
+
digitalkin-0.3.2.dev20.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
143
|
+
digitalkin-0.3.2.dev20.dist-info/top_level.txt,sha256=AYVIesKrO0jnedQ-Muog9JBehG81WeTCNeOFoJgwsgE,51
|
|
144
|
+
digitalkin-0.3.2.dev20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|