digitalkin 0.2.17__tar.gz → 0.2.19__tar.gz

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 (95) hide show
  1. {digitalkin-0.2.17 → digitalkin-0.2.19}/PKG-INFO +12 -12
  2. {digitalkin-0.2.17 → digitalkin-0.2.19}/pyproject.toml +12 -12
  3. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/module_servicer.py +7 -7
  5. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/module/__init__.py +6 -6
  6. digitalkin-0.2.19/src/digitalkin/models/module/module_types.py +105 -0
  7. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/_base_module.py +54 -45
  8. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/archetype_module.py +0 -2
  9. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/job_manager/base_job_manager.py +2 -5
  10. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/job_manager/single_job_manager.py +7 -7
  11. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/job_manager/taskiq_broker.py +0 -2
  12. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/job_manager/taskiq_job_manager.py +3 -5
  13. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/tool_module.py +1 -2
  14. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/filesystem/default_filesystem.py +7 -6
  15. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/filesystem/filesystem_strategy.py +2 -1
  16. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/filesystem/grpc_filesystem.py +2 -1
  17. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/storage/grpc_storage.py +4 -4
  18. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/utils/package_discover.py +2 -2
  19. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin.egg-info/PKG-INFO +12 -12
  20. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin.egg-info/requires.txt +11 -11
  21. digitalkin-0.2.17/src/digitalkin/models/module/module_types.py +0 -43
  22. {digitalkin-0.2.17 → digitalkin-0.2.19}/LICENSE +0 -0
  23. {digitalkin-0.2.17 → digitalkin-0.2.19}/README.md +0 -0
  24. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/__init__.py +0 -0
  25. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/mock/__init__.py +0 -0
  26. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/mock/mock_pb2.py +0 -0
  27. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  28. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/server_async_insecure.py +0 -0
  29. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/server_async_secure.py +0 -0
  30. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/server_sync_insecure.py +0 -0
  31. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/base_server/server_sync_secure.py +0 -0
  32. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/modules/__init__.py +0 -0
  33. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/modules/cpu_intensive_module.py +0 -0
  34. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/modules/minimal_llm_module.py +0 -0
  35. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/modules/text_transform_module.py +0 -0
  36. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/services/filesystem_module.py +0 -0
  37. {digitalkin-0.2.17 → digitalkin-0.2.19}/examples/services/storage_module.py +0 -0
  38. {digitalkin-0.2.17 → digitalkin-0.2.19}/setup.cfg +0 -0
  39. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/__init__.py +0 -0
  40. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/__init__.py +0 -0
  41. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  42. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/module_server.py +0 -0
  43. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/registry_server.py +0 -0
  44. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/registry_servicer.py +0 -0
  45. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/utils/exceptions.py +0 -0
  46. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/utils/factory.py +0 -0
  47. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
  48. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/utils/models.py +0 -0
  49. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/grpc_servers/utils/types.py +0 -0
  50. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/logger.py +0 -0
  51. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/__init__.py +0 -0
  52. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/module/module.py +0 -0
  53. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/module/module_context.py +0 -0
  54. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/services/__init__.py +0 -0
  55. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/services/cost.py +0 -0
  56. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/models/services/storage.py +0 -0
  57. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/__init__.py +0 -0
  58. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/job_manager/job_manager_models.py +0 -0
  59. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/modules/trigger_handler.py +0 -0
  60. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/py.typed +0 -0
  61. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/__init__.py +0 -0
  62. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/agent/__init__.py +0 -0
  63. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/agent/agent_strategy.py +0 -0
  64. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/agent/default_agent.py +0 -0
  65. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/base_strategy.py +0 -0
  66. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/cost/__init__.py +0 -0
  67. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  68. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/cost/default_cost.py +0 -0
  69. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  70. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/filesystem/__init__.py +0 -0
  71. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/identity/__init__.py +0 -0
  72. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/identity/default_identity.py +0 -0
  73. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  74. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/registry/__init__.py +0 -0
  75. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/registry/default_registry.py +0 -0
  76. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  77. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/services_config.py +0 -0
  78. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/services_models.py +0 -0
  79. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/setup/__init__.py +0 -0
  80. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/setup/default_setup.py +0 -0
  81. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  82. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  83. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/snapshot/__init__.py +0 -0
  84. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
  85. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
  86. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/storage/__init__.py +0 -0
  87. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/storage/default_storage.py +0 -0
  88. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  89. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/utils/__init__.py +0 -0
  90. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/utils/arg_parser.py +0 -0
  91. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/utils/development_mode_action.py +0 -0
  92. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  93. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin.egg-info/SOURCES.txt +0 -0
  94. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  95. {digitalkin-0.2.17 → digitalkin-0.2.19}/src/digitalkin.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.2.17
3
+ Version: 0.2.19
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,36 +452,36 @@ Classifier: License :: Other/Proprietary License
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: digitalkin-proto>=0.1.15
455
+ Requires-Dist: digitalkin-proto>=0.1.16
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
459
  Requires-Dist: pydantic>=2.11.5
460
460
  Provides-Extra: dev
461
- Requires-Dist: typos>=1.33.1; extra == "dev"
462
- Requires-Dist: ruff>=0.11.13; extra == "dev"
463
- Requires-Dist: mypy>=1.16.0; extra == "dev"
464
- Requires-Dist: pyright>=1.1.401; extra == "dev"
461
+ Requires-Dist: typos>=1.34.0; extra == "dev"
462
+ Requires-Dist: ruff>=0.12.5; extra == "dev"
463
+ Requires-Dist: mypy>=1.17.0; extra == "dev"
464
+ Requires-Dist: pyright>=1.1.403; extra == "dev"
465
465
  Requires-Dist: pre-commit>=4.2.0; extra == "dev"
466
466
  Requires-Dist: bump2version>=1.0.1; extra == "dev"
467
467
  Requires-Dist: build>=1.2.2; extra == "dev"
468
468
  Requires-Dist: twine>=6.1.0; extra == "dev"
469
- Requires-Dist: cryptography>=45.0.4; extra == "dev"
469
+ Requires-Dist: cryptography>=45.0.5; extra == "dev"
470
470
  Provides-Extra: examples
471
471
  Requires-Dist: openai>=1.75.0; extra == "examples"
472
472
  Provides-Extra: tests
473
- Requires-Dist: freezegun>=1.5.2; extra == "tests"
473
+ Requires-Dist: freezegun>=1.5.3; extra == "tests"
474
474
  Requires-Dist: hdrhistogram>=0.10.3; extra == "tests"
475
475
  Requires-Dist: grpcio-testing>=1.71.0; extra == "tests"
476
476
  Requires-Dist: psutil>=7.0.0; extra == "tests"
477
477
  Requires-Dist: pytest>=8.4.0; extra == "tests"
478
- Requires-Dist: pytest-asyncio>=1.0.0; extra == "tests"
478
+ Requires-Dist: pytest-asyncio>=1.1.0; extra == "tests"
479
479
  Requires-Dist: pytest-cov>=6.1.0; extra == "tests"
480
480
  Provides-Extra: taskiq
481
481
  Requires-Dist: rstream>=0.30.0; extra == "taskiq"
482
- Requires-Dist: taskiq-aio-pika>=0.4.2; extra == "taskiq"
483
- Requires-Dist: taskiq-redis>=1.0.9; extra == "taskiq"
484
- Requires-Dist: taskiq[reload]>=0.11.17; extra == "taskiq"
482
+ Requires-Dist: taskiq-aio-pika>=0.4.3; extra == "taskiq"
483
+ Requires-Dist: taskiq-redis>=1.1.0; extra == "taskiq"
484
+ Requires-Dist: taskiq[reload]>=0.11.18; extra == "taskiq"
485
485
  Dynamic: license-file
486
486
 
487
487
  # DigitalKin Python SDK
@@ -12,7 +12,7 @@
12
12
 
13
13
  keywords = [ "digitalkin", "kin", "agent", "gprc", "sdk" ]
14
14
  # Version of the package automatically updated by bump2version (that is why it is separated)
15
- version = "0.2.17"
15
+ version = "0.2.19"
16
16
 
17
17
  classifiers = [
18
18
  "Development Status :: 3 - Alpha",
@@ -29,7 +29,7 @@
29
29
  ]
30
30
 
31
31
  dependencies = [
32
- "digitalkin-proto>=0.1.15",
32
+ "digitalkin-proto>=0.1.16",
33
33
  "grpcio-health-checking>=1.71.0",
34
34
  "grpcio-reflection>=1.71.0",
35
35
  "grpcio-status>=1.71.0",
@@ -38,31 +38,31 @@
38
38
 
39
39
  [project.optional-dependencies]
40
40
  dev = [
41
- "typos>=1.33.1",
42
- "ruff>=0.11.13",
43
- "mypy>=1.16.0",
44
- "pyright>=1.1.401",
41
+ "typos>=1.34.0",
42
+ "ruff>=0.12.5",
43
+ "mypy>=1.17.0",
44
+ "pyright>=1.1.403",
45
45
  "pre-commit>=4.2.0",
46
46
  "bump2version>=1.0.1",
47
47
  "build>=1.2.2",
48
48
  "twine>=6.1.0",
49
- "cryptography>=45.0.4",
49
+ "cryptography>=45.0.5",
50
50
  ]
51
51
  examples = [ "openai>=1.75.0" ]
52
52
  tests = [
53
- "freezegun>=1.5.2",
53
+ "freezegun>=1.5.3",
54
54
  "hdrhistogram>=0.10.3",
55
55
  "grpcio-testing>=1.71.0",
56
56
  "psutil>=7.0.0",
57
57
  "pytest>=8.4.0",
58
- "pytest-asyncio>=1.0.0",
58
+ "pytest-asyncio>=1.1.0",
59
59
  "pytest-cov>=6.1.0",
60
60
  ]
61
61
  taskiq = [
62
62
  "rstream>=0.30.0",
63
- "taskiq-aio-pika>=0.4.2",
64
- "taskiq-redis>=1.0.9",
65
- "taskiq[reload]>=0.11.17",
63
+ "taskiq-aio-pika>=0.4.3",
64
+ "taskiq-redis>=1.1.0",
65
+ "taskiq[reload]>=0.11.18",
66
66
  ]
67
67
 
68
68
  [project.urls]
@@ -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.17"
8
+ __version__ = "0.2.19"
@@ -105,7 +105,8 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
105
105
  setup_version = request.setup_version
106
106
  config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
107
107
  setup_version_data = self.module_class.create_setup_model(
108
- json_format.MessageToDict(request.setup_version.content)
108
+ json_format.MessageToDict(request.setup_version.content),
109
+ config_fields=True,
109
110
  )
110
111
 
111
112
  if not setup_version_data:
@@ -119,7 +120,6 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
119
120
  # create a task to run the module in background
120
121
  job_id = await self.job_manager.create_config_setup_instance_job(
121
122
  config_setup_data,
122
- setup_version_data,
123
123
  request.mission_id,
124
124
  setup_version.id,
125
125
  )
@@ -333,7 +333,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
333
333
  except NotImplementedError as e:
334
334
  logger.warning(e)
335
335
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
336
- context.set_details(e)
336
+ context.set_details(str(e))
337
337
  return information_pb2.GetModuleInputResponse()
338
338
 
339
339
  return information_pb2.GetModuleInputResponse(
@@ -369,7 +369,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
369
369
  except NotImplementedError as e:
370
370
  logger.warning(e)
371
371
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
372
- context.set_details(e)
372
+ context.set_details(str(e))
373
373
  return information_pb2.GetModuleOutputResponse()
374
374
 
375
375
  return information_pb2.GetModuleOutputResponse(
@@ -405,7 +405,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
405
405
  except NotImplementedError as e:
406
406
  logger.warning(e)
407
407
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
408
- context.set_details(e)
408
+ context.set_details(str(e))
409
409
  return information_pb2.GetModuleSetupResponse()
410
410
 
411
411
  return information_pb2.GetModuleSetupResponse(
@@ -441,7 +441,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
441
441
  except NotImplementedError as e:
442
442
  logger.warning(e)
443
443
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
444
- context.set_details(e)
444
+ context.set_details(str(e))
445
445
  return information_pb2.GetModuleSecretResponse()
446
446
 
447
447
  return information_pb2.GetModuleSecretResponse(
@@ -477,7 +477,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
477
477
  except NotImplementedError as e:
478
478
  logger.warning(e)
479
479
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
480
- context.set_details(e)
480
+ context.set_details(str(e))
481
481
  return information_pb2.GetConfigSetupModuleResponse()
482
482
 
483
483
  return information_pb2.GetConfigSetupModuleResponse(
@@ -3,24 +3,24 @@
3
3
  from digitalkin.models.module.module import Module, ModuleStatus
4
4
  from digitalkin.models.module.module_context import ModuleContext
5
5
  from digitalkin.models.module.module_types import (
6
- ConfigSetupModelT,
7
- InputModel,
6
+ DataModel,
7
+ DataTrigger,
8
8
  InputModelT,
9
- InputTrigger,
10
9
  OutputModelT,
11
10
  SecretModelT,
11
+ SetupModel,
12
12
  SetupModelT,
13
13
  )
14
14
 
15
15
  __all__ = [
16
- "ConfigSetupModelT",
17
- "InputModel",
16
+ "DataModel",
17
+ "DataTrigger",
18
18
  "InputModelT",
19
- "InputTrigger",
20
19
  "Module",
21
20
  "ModuleContext",
22
21
  "ModuleStatus",
23
22
  "OutputModelT",
24
23
  "SecretModelT",
24
+ "SetupModel",
25
25
  "SetupModelT",
26
26
  ]
@@ -0,0 +1,105 @@
1
+ """Types for module models."""
2
+
3
+ from datetime import datetime, timezone
4
+ from typing import Any, ClassVar, Generic, TypeVar, cast
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field, create_model
7
+
8
+ from digitalkin.logger import logger
9
+
10
+
11
+ class DataTrigger(BaseModel):
12
+ """Defines the root input model exposing the protocol.
13
+
14
+ The mandatory protocol is important to define the module beahvior following the user or agent input.
15
+
16
+ Example:
17
+ class MyInput(DataModel):
18
+ root: DataTrigger
19
+ user_define_data: Any
20
+
21
+ # Usage
22
+ my_input = MyInput(root=DataTrigger(protocol="message"))
23
+ print(my_input.root.protocol) # Output: message
24
+ """
25
+
26
+ protocol: ClassVar[str]
27
+ created_at: str = datetime.now(tz=timezone.utc).isoformat()
28
+
29
+
30
+ DataTriggerT = TypeVar("DataTriggerT", bound=DataTrigger)
31
+
32
+
33
+ class DataModel(BaseModel, Generic[DataTriggerT]):
34
+ """Base definition of input model showing mandatory root fields.
35
+
36
+ The Model define the Module Input, usually referring to multiple input type defined by an union.
37
+
38
+ Example:
39
+ class ModuleInput(DataModel):
40
+ root: FileInput | MessageInput
41
+ """
42
+
43
+ root: DataTriggerT
44
+ annotations: dict[str, str] = Field(
45
+ default={},
46
+ title="Annotations",
47
+ description="Additional metadata or annotations related to the output. ex {'role': 'user'}",
48
+ )
49
+
50
+
51
+ InputModelT = TypeVar("InputModelT", bound=DataModel)
52
+ OutputModelT = TypeVar("OutputModelT", bound=DataModel)
53
+ SecretModelT = TypeVar("SecretModelT", bound=BaseModel)
54
+ SetupModelT = TypeVar("SetupModelT", bound="SetupModel")
55
+
56
+
57
+ class SetupModel(BaseModel):
58
+ """Base definition of setup model showing mandatory root fields.
59
+
60
+ Optionally, the setup model can define a config option in json_schema_extra to be used to initialize the Kin.
61
+
62
+ Example:
63
+ class MySetup(SetupModel):
64
+ name: str = Field()
65
+ number: int = Field(..., json_schema_extra={"config": True})
66
+ """
67
+
68
+ @classmethod
69
+ def get_clean_model(cls, *, config_fields: bool, hidden_fields: bool) -> type[SetupModelT]: # type: ignore
70
+ """Dynamically builds and returns a new BaseModel subclass.
71
+
72
+ containing only those fields where json_schema_extra["config"] == True.
73
+
74
+ Returns:
75
+ Type[BaseModel]: A new BaseModel subclass with the filtered fields.
76
+
77
+ Raises:
78
+ ValueError: If both config_fields and hidden_fields are set to True.
79
+ """
80
+ clean_fields: dict[str, Any] = {}
81
+ for name, field_info in cls.model_fields.items():
82
+ extra = getattr(field_info, "json_schema_extra", {}) or {}
83
+ is_config = bool(extra.get("config", False))
84
+ is_hidden = bool(extra.get("hidden", False))
85
+
86
+ # Skip config unless explicitly included
87
+ if is_config and not config_fields:
88
+ logger.debug("Skipping '%s' (config-only)", name)
89
+ continue
90
+
91
+ # Skip hidden unless explicitly included
92
+ if is_hidden and not hidden_fields:
93
+ logger.debug("Skipping '%s' (hidden-only)", name)
94
+ continue
95
+
96
+ clean_fields[name] = (field_info.annotation, field_info)
97
+
98
+ # Dynamically create a model e.g. "SetupModel"
99
+ m = create_model(
100
+ f"{cls.__name__}",
101
+ __base__=BaseModel,
102
+ __config__=ConfigDict(arbitrary_types_allowed=True),
103
+ **clean_fields,
104
+ )
105
+ return cast("type[SetupModelT]", m)
@@ -9,10 +9,8 @@ from typing import Any, ClassVar, Generic
9
9
 
10
10
  from pydantic import BaseModel
11
11
 
12
- from digitalkin.grpc_servers.utils.exceptions import OptionalFeatureNotImplementedError
13
12
  from digitalkin.logger import logger
14
13
  from digitalkin.models.module import (
15
- ConfigSetupModelT,
16
14
  InputModelT,
17
15
  ModuleStatus,
18
16
  OutputModelT,
@@ -33,11 +31,11 @@ from digitalkin.utils.llm_ready_schema import llm_ready_schema
33
31
  from digitalkin.utils.package_discover import ModuleDiscoverer
34
32
 
35
33
 
36
- class ModuleErrorModel(BaseModel):
34
+ class ModuleCodeModel(BaseModel):
37
35
  """typed error/code model."""
38
36
 
39
37
  code: str
40
- exception: str
38
+ message: str
41
39
  short_description: str
42
40
 
43
41
 
@@ -48,7 +46,6 @@ class BaseModule( # noqa: PLR0904
48
46
  OutputModelT,
49
47
  SetupModelT,
50
48
  SecretModelT,
51
- ConfigSetupModelT,
52
49
  ],
53
50
  ):
54
51
  """BaseModule is the abstract base for all modules in the DigitalKin SDK."""
@@ -56,10 +53,10 @@ class BaseModule( # noqa: PLR0904
56
53
  name: str
57
54
  description: str
58
55
 
59
- config_setup_format: type[ConfigSetupModelT]
56
+ setup_format: type[SetupModelT]
57
+
60
58
  input_format: type[InputModelT]
61
59
  output_format: type[OutputModelT]
62
- setup_format: type[SetupModelT]
63
60
  secret_format: type[SecretModelT]
64
61
  metadata: ClassVar[dict[str, Any]]
65
62
 
@@ -172,20 +169,22 @@ class BaseModule( # noqa: PLR0904
172
169
  def get_config_setup_format(cls, *, llm_format: bool) -> str:
173
170
  """Gets the JSON schema of the config setup format model.
174
171
 
172
+ The config setup format is used only to initialize the module with configuration data.
173
+ The setup format is used to initialize an run the module with setup data.
174
+
175
175
  Raises:
176
- OptionalFeatureNotImplementedError: If the `config_setup_format` is not defined.
176
+ NotImplementedError: If the `setup_format` is not defined.
177
177
 
178
178
  Returns:
179
179
  The JSON schema of the config setup format as a string.
180
180
  """
181
- config_setup_format = getattr(cls, "config_setup_format", None)
182
-
183
- if config_setup_format is not None:
181
+ if cls.setup_format is not None:
182
+ setup_format = cls.setup_format.get_clean_model(config_fields=True, hidden_fields=False)
184
183
  if llm_format:
185
- return json.dumps(llm_ready_schema(config_setup_format), indent=2)
186
- return json.dumps(config_setup_format.model_json_schema(), indent=2)
184
+ return json.dumps(llm_ready_schema(setup_format), indent=2)
185
+ return json.dumps(setup_format.model_json_schema(), indent=2)
187
186
  msg = "'%s' class does not define an 'config_setup_format'."
188
- raise OptionalFeatureNotImplementedError(msg)
187
+ raise NotImplementedError(msg)
189
188
 
190
189
  @classmethod
191
190
  def get_setup_format(cls, *, llm_format: bool) -> str:
@@ -198,14 +197,15 @@ class BaseModule( # noqa: PLR0904
198
197
  The JSON schema of the setup format as a string.
199
198
  """
200
199
  if cls.setup_format is not None:
200
+ setup_format = cls.setup_format.get_clean_model(config_fields=False, hidden_fields=True)
201
201
  if llm_format:
202
- return json.dumps(llm_ready_schema(cls.setup_format), indent=2)
203
- return json.dumps(cls.setup_format.model_json_schema(), indent=2)
202
+ return json.dumps(llm_ready_schema(setup_format), indent=2)
203
+ return json.dumps(setup_format.model_json_schema(), indent=2)
204
204
  msg = "'%s' class does not define an 'setup_format'."
205
205
  raise NotImplementedError(msg)
206
206
 
207
207
  @classmethod
208
- def create_config_setup_model(cls, config_setup_data: dict[str, Any]) -> ConfigSetupModelT:
208
+ def create_config_setup_model(cls, config_setup_data: dict[str, Any]) -> SetupModelT:
209
209
  """Create the setup model from the setup data.
210
210
 
211
211
  Args:
@@ -214,7 +214,7 @@ class BaseModule( # noqa: PLR0904
214
214
  Returns:
215
215
  The setup model.
216
216
  """
217
- return cls.config_setup_format(**config_setup_data)
217
+ return cls.setup_format(**config_setup_data)
218
218
 
219
219
  @classmethod
220
220
  def create_input_model(cls, input_data: dict[str, Any]) -> InputModelT:
@@ -229,16 +229,17 @@ class BaseModule( # noqa: PLR0904
229
229
  return cls.input_format(**input_data)
230
230
 
231
231
  @classmethod
232
- def create_setup_model(cls, setup_data: dict[str, Any]) -> SetupModelT:
232
+ def create_setup_model(cls, setup_data: dict[str, Any], *, config_fields: bool = False) -> SetupModelT:
233
233
  """Create the setup model from the setup data.
234
234
 
235
235
  Args:
236
236
  setup_data: The setup data to create the model from.
237
+ config_fields: If True, include only fields with json_schema_extra["config"] == True.
237
238
 
238
239
  Returns:
239
240
  The setup model.
240
241
  """
241
- return cls.setup_format(**setup_data)
242
+ return cls.setup_format.get_clean_model(config_fields=config_fields, hidden_fields=True)(**setup_data)
242
243
 
243
244
  @classmethod
244
245
  def create_secret_model(cls, secret_data: dict[str, Any]) -> SecretModelT:
@@ -290,20 +291,20 @@ class BaseModule( # noqa: PLR0904
290
291
  """
291
292
  return cls.triggers_discoverer.register_trigger(handler_cls)
292
293
 
293
- @abstractmethod
294
- async def run_config_setup(
295
- self,
296
- config_setup_data: ConfigSetupModelT,
297
- setup_data: SetupModelT,
298
- callback: Callable,
299
- ) -> None:
294
+ async def run_config_setup(self, config_setup_data: SetupModelT) -> SetupModelT: # noqa: PLR6301
300
295
  """Run config setup the module.
301
296
 
302
- Raises:
303
- OptionalFeatureNotImplementedError: If the config setup feature is not implemented.
297
+ The config setup is used to initialize the setup with configuration data.
298
+ This method is typically used to set up the module with necessary configuration before running it,
299
+ especially for processing data like files.
300
+ The function needs to save the setup in the storage.
301
+ The module will be initialize with the setup and not the config setup.
302
+ This method is optional, the config setup and setup can be the same.
303
+
304
+ Returns:
305
+ The updated setup model after running the config setup.
304
306
  """
305
- msg = f"'{self}' class does not define an optional 'run_config_setup' attribute."
306
- raise OptionalFeatureNotImplementedError(msg)
307
+ return config_setup_data
307
308
 
308
309
  @abstractmethod
309
310
  async def initialize(self, setup_data: SetupModelT) -> None:
@@ -364,9 +365,9 @@ class BaseModule( # noqa: PLR0904
364
365
  asyncio.CancelledError: If the module is cancelled
365
366
  """
366
367
  try:
367
- logger.warning("Starting module %s", self.name)
368
+ logger.info("Starting module %s", self.name)
368
369
  await self.run(input_data, setup_data, callback)
369
- logger.warning("Module %s finished", self.name)
370
+ logger.info("Module %s finished", self.name)
370
371
  except asyncio.CancelledError:
371
372
  self._status = ModuleStatus.CANCELLED
372
373
  logger.error(f"Module {self.name} cancelled")
@@ -374,7 +375,7 @@ class BaseModule( # noqa: PLR0904
374
375
  self._status = ModuleStatus.FAILED
375
376
  logger.exception("Error inside module %s", self.name)
376
377
  else:
377
- self._status = ModuleStatus.STOPPED
378
+ self._status = ModuleStatus.STOPPING
378
379
  finally:
379
380
  await self.stop()
380
381
 
@@ -382,22 +383,22 @@ class BaseModule( # noqa: PLR0904
382
383
  self,
383
384
  input_data: InputModelT,
384
385
  setup_data: SetupModelT,
385
- callback: Callable[[OutputModelT | ModuleErrorModel], Coroutine[Any, Any, None]],
386
+ callback: Callable[[OutputModelT | ModuleCodeModel], Coroutine[Any, Any, None]],
386
387
  done_callback: Callable | None = None,
387
388
  ) -> None:
388
389
  """Start the module."""
389
390
  try:
390
- logger.info("Inititalize module")
391
+ logger.debug("Inititalize module")
391
392
  await self.initialize(setup_data=setup_data)
392
393
  except Exception as e:
393
394
  self._status = ModuleStatus.FAILED
394
395
  short_description = "Error initializing module"
395
396
  logger.exception("%s: %s", short_description, e)
396
397
  await callback(
397
- ModuleErrorModel(
398
+ ModuleCodeModel(
398
399
  code=str(self._status),
399
400
  short_description=short_description,
400
- exception=str(e),
401
+ message=str(e),
401
402
  )
402
403
  )
403
404
  if done_callback is not None:
@@ -406,9 +407,9 @@ class BaseModule( # noqa: PLR0904
406
407
  return
407
408
 
408
409
  try:
409
- logger.info("Init the discod input handlers.")
410
+ logger.debug("Init the discovered input handlers.")
410
411
  self.triggers_discoverer.init_handlers(self.context)
411
- logger.info("Run lifecycle")
412
+ logger.debug("Run lifecycle")
412
413
  self._status = ModuleStatus.RUNNING
413
414
  self._task = asyncio.create_task(
414
415
  self._run_lifecycle(input_data, setup_data, callback),
@@ -422,7 +423,8 @@ class BaseModule( # noqa: PLR0904
422
423
 
423
424
  async def stop(self) -> None:
424
425
  """Stop the module."""
425
- if self._status != ModuleStatus.RUNNING:
426
+ logger.info("Stopping module %s with status %s", self.name, self._status)
427
+ if self._status not in {ModuleStatus.RUNNING, ModuleStatus.STOPPING}:
426
428
  return
427
429
 
428
430
  try:
@@ -431,22 +433,29 @@ class BaseModule( # noqa: PLR0904
431
433
  self._task.cancel()
432
434
  with contextlib.suppress(asyncio.CancelledError):
433
435
  await self._task
436
+ logger.debug("Module %s stopped", self.name)
434
437
  await self.cleanup()
438
+ self._status = ModuleStatus.STOPPED
439
+ logger.debug("Module %s cleaned", self.name)
435
440
  except Exception:
436
441
  self._status = ModuleStatus.FAILED
437
442
  logger.exception("Error stopping module")
438
443
 
439
444
  async def start_config_setup(
440
445
  self,
441
- config_setup_data: ConfigSetupModelT,
442
- setup_data: SetupModelT,
443
- callback: Callable[[OutputModelT | ModuleErrorModel], Coroutine[Any, Any, None]],
446
+ config_setup_data: SetupModelT,
447
+ callback: Callable[[SetupModelT | ModuleCodeModel], Coroutine[Any, Any, None]],
444
448
  ) -> None:
445
449
  """Start the module."""
446
450
  try:
447
451
  logger.info("Run Config Setup lifecycle")
448
452
  self._status = ModuleStatus.RUNNING
449
- await self.run_config_setup(config_setup_data, setup_data, callback)
453
+ content = await self.run_config_setup(config_setup_data)
454
+
455
+ wrapper = config_setup_data.model_dump()
456
+ wrapper["content"] = content.model_dump()
457
+ await callback(self.create_setup_model(wrapper))
458
+ self._status = ModuleStatus.STOPPING
450
459
  except Exception:
451
460
  self._status = ModuleStatus.FAILED
452
461
  logger.exception("Error during module lifecyle")
@@ -3,7 +3,6 @@
3
3
  from abc import ABC
4
4
 
5
5
  from digitalkin.models.module import InputModelT, OutputModelT, SecretModelT, SetupModelT
6
- from digitalkin.models.module.module_types import ConfigSetupModelT
7
6
  from digitalkin.modules._base_module import BaseModule
8
7
 
9
8
 
@@ -13,7 +12,6 @@ class ArchetypeModule(
13
12
  OutputModelT,
14
13
  SetupModelT,
15
14
  SecretModelT,
16
- ConfigSetupModelT,
17
15
  ],
18
16
  ABC,
19
17
  ):
@@ -7,13 +7,12 @@ from typing import Any, Generic
7
7
 
8
8
  from digitalkin.models import ModuleStatus
9
9
  from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
10
- from digitalkin.models.module.module_types import ConfigSetupModelT
11
10
  from digitalkin.modules._base_module import BaseModule
12
11
  from digitalkin.services.services_config import ServicesConfig
13
12
  from digitalkin.services.services_models import ServicesMode
14
13
 
15
14
 
16
- class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModelT]):
15
+ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
17
16
  """Abstract base class for managing background module jobs."""
18
17
 
19
18
  async def _start(self) -> None:
@@ -120,8 +119,7 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
120
119
  @abc.abstractmethod
121
120
  async def create_config_setup_instance_job(
122
121
  self,
123
- config_setup_data: ConfigSetupModelT,
124
- setup_data: SetupModelT,
122
+ config_setup_data: SetupModelT,
125
123
  mission_id: str,
126
124
  setup_version_id: str,
127
125
  ) -> str:
@@ -132,7 +130,6 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModel
132
130
 
133
131
  Args:
134
132
  config_setup_data: The input data required to start the job.
135
- setup_data: The setup configuration for the module.
136
133
  mission_id: The mission ID associated with the job.
137
134
  setup_version_id: The setup ID.
138
135