digitalkin 0.2.13__tar.gz → 0.2.14__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 (91) hide show
  1. {digitalkin-0.2.13 → digitalkin-0.2.14}/PKG-INFO +14 -14
  2. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/modules/cpu_intensive_module.py +11 -1
  3. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/modules/minimal_llm_module.py +41 -3
  4. {digitalkin-0.2.13 → digitalkin-0.2.14}/pyproject.toml +15 -15
  5. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/__version__.py +1 -1
  6. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/module_servicer.py +93 -1
  7. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/utils/exceptions.py +4 -0
  8. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/module/__init__.py +2 -1
  9. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/module/module_types.py +1 -0
  10. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/_base_module.py +80 -2
  11. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/archetype_module.py +11 -1
  12. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/job_manager/base_job_manager.py +45 -3
  13. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/job_manager/single_job_manager.py +70 -3
  14. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/job_manager/taskiq_broker.py +42 -1
  15. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/job_manager/taskiq_job_manager.py +90 -17
  16. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/tool_module.py +2 -1
  17. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/trigger_module.py +3 -1
  18. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin.egg-info/PKG-INFO +14 -14
  19. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin.egg-info/requires.txt +13 -13
  20. {digitalkin-0.2.13 → digitalkin-0.2.14}/LICENSE +0 -0
  21. {digitalkin-0.2.13 → digitalkin-0.2.14}/README.md +0 -0
  22. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/__init__.py +0 -0
  23. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/mock/__init__.py +0 -0
  24. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/mock/mock_pb2.py +0 -0
  25. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  26. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/server_async_insecure.py +0 -0
  27. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/server_async_secure.py +0 -0
  28. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/server_sync_insecure.py +0 -0
  29. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/base_server/server_sync_secure.py +0 -0
  30. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/modules/__init__.py +0 -0
  31. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/modules/storage_module.py +0 -0
  32. {digitalkin-0.2.13 → digitalkin-0.2.14}/examples/modules/text_transform_module.py +0 -0
  33. {digitalkin-0.2.13 → digitalkin-0.2.14}/setup.cfg +0 -0
  34. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/__init__.py +0 -0
  35. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/__init__.py +0 -0
  36. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  37. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/module_server.py +0 -0
  38. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/registry_server.py +0 -0
  39. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/registry_servicer.py +0 -0
  40. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/utils/factory.py +0 -0
  41. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
  42. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/utils/models.py +0 -0
  43. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/grpc_servers/utils/types.py +0 -0
  44. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/logger.py +0 -0
  45. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/__init__.py +0 -0
  46. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/module/module.py +0 -0
  47. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/services/__init__.py +0 -0
  48. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/services/cost.py +0 -0
  49. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/models/services/storage.py +0 -0
  50. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/__init__.py +0 -0
  51. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/modules/job_manager/job_manager_models.py +0 -0
  52. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/py.typed +0 -0
  53. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/__init__.py +0 -0
  54. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/agent/__init__.py +0 -0
  55. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/agent/agent_strategy.py +0 -0
  56. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/agent/default_agent.py +0 -0
  57. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/base_strategy.py +0 -0
  58. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/cost/__init__.py +0 -0
  59. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  60. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/cost/default_cost.py +0 -0
  61. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  62. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/filesystem/__init__.py +0 -0
  63. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  64. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  65. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  66. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/identity/__init__.py +0 -0
  67. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/identity/default_identity.py +0 -0
  68. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  69. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/registry/__init__.py +0 -0
  70. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/registry/default_registry.py +0 -0
  71. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  72. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/services_config.py +0 -0
  73. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/services_models.py +0 -0
  74. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/setup/__init__.py +0 -0
  75. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/setup/default_setup.py +0 -0
  76. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/setup/grpc_setup.py +0 -0
  77. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/setup/setup_strategy.py +0 -0
  78. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/snapshot/__init__.py +0 -0
  79. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/snapshot/default_snapshot.py +0 -0
  80. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/snapshot/snapshot_strategy.py +0 -0
  81. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/storage/__init__.py +0 -0
  82. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/storage/default_storage.py +0 -0
  83. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/storage/grpc_storage.py +0 -0
  84. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  85. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/utils/__init__.py +0 -0
  86. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/utils/arg_parser.py +0 -0
  87. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/utils/development_mode_action.py +0 -0
  88. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  89. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin.egg-info/SOURCES.txt +0 -0
  90. {digitalkin-0.2.13 → digitalkin-0.2.14}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  91. {digitalkin-0.2.13 → digitalkin-0.2.14}/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.13
3
+ Version: 0.2.14
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.10
455
+ Requires-Dist: digitalkin-proto>=0.1.15
456
456
  Requires-Dist: grpcio-health-checking>=1.71.0
457
457
  Requires-Dist: grpcio-reflection>=1.71.0
458
458
  Requires-Dist: grpcio-status>=1.71.0
459
- Requires-Dist: pydantic>=2.11.4
459
+ Requires-Dist: pydantic>=2.11.5
460
460
  Provides-Extra: dev
461
- Requires-Dist: typos>=1.32.0; extra == "dev"
462
- Requires-Dist: ruff>=0.11.9; extra == "dev"
463
- Requires-Dist: mypy>=1.15.0; extra == "dev"
464
- Requires-Dist: pyright>=1.1.400; 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"
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>=44.0.3; extra == "dev"
470
- Requires-Dist: taskiq[reload]>=0.11.17; extra == "dev"
469
+ Requires-Dist: cryptography>=45.0.4; extra == "dev"
471
470
  Provides-Extra: examples
472
471
  Requires-Dist: openai>=1.75.0; extra == "examples"
473
472
  Provides-Extra: tests
474
- Requires-Dist: freezegun>=1.5.1; extra == "tests"
473
+ Requires-Dist: freezegun>=1.5.2; extra == "tests"
475
474
  Requires-Dist: hdrhistogram>=0.10.3; extra == "tests"
476
475
  Requires-Dist: grpcio-testing>=1.71.0; extra == "tests"
477
476
  Requires-Dist: psutil>=7.0.0; extra == "tests"
478
- Requires-Dist: pytest>=8.3.4; extra == "tests"
479
- Requires-Dist: pytest-asyncio>=0.26.0; extra == "tests"
477
+ Requires-Dist: pytest>=8.4.0; extra == "tests"
478
+ Requires-Dist: pytest-asyncio>=1.0.0; extra == "tests"
480
479
  Requires-Dist: pytest-cov>=6.1.0; extra == "tests"
481
480
  Provides-Extra: taskiq
482
- Requires-Dist: rstream>=0.20.9; extra == "taskiq"
481
+ Requires-Dist: rstream>=0.30.0; extra == "taskiq"
483
482
  Requires-Dist: taskiq-aio-pika>=0.4.2; extra == "taskiq"
484
- Requires-Dist: taskiq-redis>=1.0.8; extra == "taskiq"
483
+ Requires-Dist: taskiq-redis>=1.0.9; extra == "taskiq"
484
+ Requires-Dist: taskiq[reload]>=0.11.17; extra == "taskiq"
485
485
  Dynamic: license-file
486
486
 
487
487
  # DigitalKin Python SDK
@@ -127,6 +127,16 @@ class CPUOutput(BaseModel):
127
127
  )
128
128
 
129
129
 
130
+ class CPUConfigSetup(BaseModel):
131
+ """Config Setup model definining data that will be pre-computed for each setup and module instance."""
132
+
133
+ files: list[str] = Field(
134
+ ...,
135
+ title="Files to embed",
136
+ description="List of files to embed in the setup lifecycle.",
137
+ )
138
+
139
+
130
140
  class CPUSetup(BaseModel):
131
141
  """Setup model defining module configuration parameters."""
132
142
 
@@ -175,7 +185,7 @@ client_config = ClientConfig(
175
185
  )
176
186
 
177
187
 
178
- class CPUIntensiveModule(BaseModule[CPUInput, CPUOutput, CPUSetup, CPUToolSecret]):
188
+ class CPUIntensiveModule(BaseModule[CPUInput, CPUOutput, CPUSetup, CPUToolSecret, None]):
179
189
  """A CPU endpoint tool module module."""
180
190
 
181
191
  name = "CPUIntensiveModule"
@@ -11,7 +11,6 @@ from pydantic import BaseModel, Field
11
11
  from digitalkin.grpc_servers.utils.models import ClientConfig, SecurityMode, ServerMode
12
12
  from digitalkin.modules._base_module import BaseModule
13
13
  from digitalkin.services.services_models import ServicesStrategy
14
- from digitalkin.services.setup.setup_strategy import SetupData
15
14
 
16
15
  # Configure logging with clear formatting
17
16
  logging.basicConfig(
@@ -154,6 +153,16 @@ class OpenAISetup(BaseModel):
154
153
  )
155
154
 
156
155
 
156
+ class OpenAIConfigSetup(BaseModel):
157
+ """Setup model defining module configuration parameters."""
158
+
159
+ rag_files: list[bytes] = Field(
160
+ ...,
161
+ title="RAG Files",
162
+ description="Files used for retrieval-augmented generation (RAG) with the OpenAI module.",
163
+ )
164
+
165
+
157
166
  class OpenAIToolSecret(BaseModel):
158
167
  """Secret model defining module configuration parameters."""
159
168
 
@@ -167,13 +176,22 @@ client_config = ClientConfig(
167
176
  )
168
177
 
169
178
 
170
- class OpenAIToolModule(BaseModule[OpenAIInput, OpenAIOutput, OpenAISetup, OpenAIToolSecret]):
179
+ class OpenAIToolModule(
180
+ BaseModule[
181
+ OpenAIInput,
182
+ OpenAIOutput,
183
+ OpenAISetup,
184
+ OpenAIToolSecret,
185
+ OpenAIConfigSetup,
186
+ ]
187
+ ):
171
188
  """A openAI endpoint tool module module."""
172
189
 
173
190
  name = "OpenAIToolModule"
174
191
  description = "A module that interacts with OpenAI API to process text"
175
192
 
176
193
  # Define the schema formats for the module
194
+ config_setup_format = OpenAIConfigSetup
177
195
  input_format = OpenAIInput
178
196
  output_format = OpenAIOutput
179
197
  setup_format = OpenAISetup
@@ -205,7 +223,27 @@ class OpenAIToolModule(BaseModule[OpenAIInput, OpenAIOutput, OpenAISetup, OpenAI
205
223
  },
206
224
  }
207
225
 
208
- async def initialize(self, setup_data: SetupData) -> None:
226
+ async def run_config_setup(
227
+ self,
228
+ config_setup_data: OpenAIConfigSetup,
229
+ setup_data: OpenAISetup,
230
+ callback: Callable,
231
+ ) -> None:
232
+ """Configure the module with additional setup data.
233
+
234
+ Args:
235
+ config_setup_data: Additional configuration content.
236
+ setup_data: Initial setup data for the module.
237
+ callback: Function to send output data back to the client.
238
+ """
239
+ logger.info("Configuring OpenAIToolModule with additional setup data. %s", config_setup_data)
240
+
241
+ # Here you can process config_content and update setup_data as needed
242
+ # For now, we just return the original setup_data
243
+ setup_data.developer_prompt = "| + |".join(f.decode("utf-8") for f in config_setup_data.rag_files)
244
+ await callback(setup_data)
245
+
246
+ async def initialize(self, setup_data: OpenAISetup) -> None:
209
247
  """Initialize the module capabilities.
210
248
 
211
249
  This method is called when the module is loaded by the server.
@@ -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.13"
15
+ version = "0.2.14"
16
16
 
17
17
  classifiers = [
18
18
  "Development Status :: 3 - Alpha",
@@ -29,41 +29,41 @@
29
29
  ]
30
30
 
31
31
  dependencies = [
32
- "digitalkin-proto>=0.1.10",
32
+ "digitalkin-proto>=0.1.15",
33
33
  "grpcio-health-checking>=1.71.0",
34
34
  "grpcio-reflection>=1.71.0",
35
35
  "grpcio-status>=1.71.0",
36
- "pydantic>=2.11.4",
36
+ "pydantic>=2.11.5",
37
37
  ]
38
38
 
39
39
  [project.optional-dependencies]
40
40
  dev = [
41
- "typos>=1.32.0",
42
- "ruff>=0.11.9",
43
- "mypy>=1.15.0",
44
- "pyright>=1.1.400",
41
+ "typos>=1.33.1",
42
+ "ruff>=0.11.13",
43
+ "mypy>=1.16.0",
44
+ "pyright>=1.1.401",
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>=44.0.3",
50
- "taskiq[reload]>=0.11.17",
49
+ "cryptography>=45.0.4",
51
50
  ]
52
51
  examples = [ "openai>=1.75.0" ]
53
52
  tests = [
54
- "freezegun>=1.5.1",
53
+ "freezegun>=1.5.2",
55
54
  "hdrhistogram>=0.10.3",
56
55
  "grpcio-testing>=1.71.0",
57
56
  "psutil>=7.0.0",
58
- "pytest>=8.3.4",
59
- "pytest-asyncio>=0.26.0",
57
+ "pytest>=8.4.0",
58
+ "pytest-asyncio>=1.0.0",
60
59
  "pytest-cov>=6.1.0",
61
60
  ]
62
61
  taskiq = [
63
- "rstream>=0.20.9",
62
+ "rstream>=0.30.0",
64
63
  "taskiq-aio-pika>=0.4.2",
65
- "taskiq-redis>=1.0.8",
66
- ]
64
+ "taskiq-redis>=1.0.9",
65
+ "taskiq[reload]>=0.11.17",
66
+ ]
67
67
 
68
68
  [project.urls]
69
69
  Homepage = "https://github.com/DigitalKin-ai/digitalkin"
@@ -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.13"
8
+ __version__ = "0.2.14"
@@ -81,6 +81,62 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
81
81
  )
82
82
  self.setup = GrpcSetup() if self.args.services_mode == ServicesMode.REMOTE else DefaultSetup()
83
83
 
84
+ async def ConfigSetupModule( # noqa: N802
85
+ self,
86
+ request: lifecycle_pb2.ConfigSetupModuleRequest,
87
+ context: grpc.aio.ServicerContext,
88
+ ) -> lifecycle_pb2.ConfigSetupModuleResponse:
89
+ """Configure the module setup.
90
+
91
+ Args:
92
+ request: The configuration request.
93
+ context: The gRPC context.
94
+
95
+ Returns:
96
+ A response indicating success or failure.
97
+
98
+ Raises:
99
+ ServicerError: if the setup data is not returned or job creation fails.
100
+ """
101
+ logger.info("ConfigSetupVersion called for module: '%s'", self.module_class.__name__)
102
+ # Process the module input
103
+ # TODO: Secret should be used here as well
104
+ setup_version = request.setup_version
105
+ config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
106
+ setup_version_data = self.module_class.create_setup_model(
107
+ json_format.MessageToDict(request.setup_version.content)
108
+ )
109
+
110
+ if not setup_version_data:
111
+ msg = "No setup data returned."
112
+ raise ServicerError(msg)
113
+
114
+ if not config_setup_data:
115
+ msg = "No config setup data returned."
116
+ raise ServicerError(msg)
117
+
118
+ # create a task to run the module in background
119
+ job_id = await self.job_manager.create_config_setup_instance_job(
120
+ config_setup_data,
121
+ setup_version_data,
122
+ request.mission_id,
123
+ setup_version.id,
124
+ )
125
+
126
+ if job_id is None:
127
+ context.set_code(grpc.StatusCode.NOT_FOUND)
128
+ context.set_details("Failed to create module instance")
129
+ return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
130
+
131
+ updated_setup_data = await self.job_manager.generate_config_setup_module_response(job_id)
132
+ logger.warning(f"Updated setup data: {updated_setup_data=}")
133
+ setup_version.content = json_format.ParseDict(
134
+ updated_setup_data,
135
+ struct_pb2.Struct(),
136
+ ignore_unknown_fields=True,
137
+ )
138
+ return lifecycle_pb2.ConfigSetupModuleResponse(success=True, setup_version=setup_version)
139
+
84
140
  async def StartModule( # noqa: N802
85
141
  self,
86
142
  request: lifecycle_pb2.StartModuleRequest,
@@ -116,7 +172,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
116
172
  setup_data = self.module_class.create_setup_model(setup_data_class.current_setup_version.content)
117
173
 
118
174
  # create a task to run the module in background
119
- job_id = await self.job_manager.create_job(
175
+ job_id = await self.job_manager.create_module_instance_job(
120
176
  input_data,
121
177
  setup_data,
122
178
  mission_id=request.mission_id,
@@ -391,3 +447,39 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
391
447
  success=True,
392
448
  secret_schema=secret_format_struct,
393
449
  )
450
+
451
+ async def GetConfigSetupModule( # noqa: N802
452
+ self,
453
+ request: information_pb2.GetConfigSetupModuleRequest,
454
+ context: grpc.ServicerContext,
455
+ ) -> information_pb2.GetConfigSetupModuleResponse:
456
+ """Get information about the module's setup and configuration.
457
+
458
+ Args:
459
+ request: The get module setup request.
460
+ context: The gRPC context.
461
+
462
+ Returns:
463
+ A response with the module's setup information.
464
+ """
465
+ logger.debug("GetConfigSetupModule called for module: '%s'", self.module_class.__name__)
466
+
467
+ # Get setup schema if available
468
+ try:
469
+ # Convert schema to proto format
470
+ config_setup_schema_proto = self.module_class.get_config_setup_format(llm_format=request.llm_format)
471
+ config_setup_format_struct = json_format.Parse(
472
+ text=config_setup_schema_proto,
473
+ message=struct_pb2.Struct(), # pylint: disable=no-member
474
+ ignore_unknown_fields=True,
475
+ )
476
+ except NotImplementedError as e:
477
+ logger.warning(e)
478
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
479
+ context.set_details(e)
480
+ return information_pb2.GetConfigSetupModuleResponse()
481
+
482
+ return information_pb2.GetConfigSetupModuleResponse(
483
+ success=True,
484
+ config_setup_schema=config_setup_format_struct,
485
+ )
@@ -31,3 +31,7 @@ class ReflectionError(ServerError):
31
31
 
32
32
  class HealthCheckError(ServerError):
33
33
  """Error related to gRPC health check service."""
34
+
35
+
36
+ class OptionalFeatureNotImplementedError(NotImplementedError):
37
+ """Raised when an optional feature is not implemented, but was requested."""
@@ -2,10 +2,11 @@
2
2
 
3
3
  from digitalkin.models.module.module import Module, ModuleStatus
4
4
  from digitalkin.models.module.module_types import (
5
+ ConfigSetupModelT,
5
6
  InputModelT,
6
7
  OutputModelT,
7
8
  SecretModelT,
8
9
  SetupModelT,
9
10
  )
10
11
 
11
- __all__ = ["InputModelT", "Module", "ModuleStatus", "OutputModelT", "SecretModelT", "SetupModelT"]
12
+ __all__ = ["ConfigSetupModelT", "InputModelT", "Module", "ModuleStatus", "OutputModelT", "SecretModelT", "SetupModelT"]
@@ -4,6 +4,7 @@ from typing import TypeVar
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
+ ConfigSetupModelT = TypeVar("ConfigSetupModelT", bound=BaseModel | None)
7
8
  InputModelT = TypeVar("InputModelT", bound=BaseModel)
8
9
  OutputModelT = TypeVar("OutputModelT", bound=BaseModel)
9
10
  SetupModelT = TypeVar("SetupModelT", bound=BaseModel)
@@ -9,8 +9,16 @@ 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
12
13
  from digitalkin.logger import logger
13
- from digitalkin.models.module import InputModelT, ModuleStatus, OutputModelT, SecretModelT, SetupModelT
14
+ from digitalkin.models.module import (
15
+ ConfigSetupModelT,
16
+ InputModelT,
17
+ ModuleStatus,
18
+ OutputModelT,
19
+ SecretModelT,
20
+ SetupModelT,
21
+ )
14
22
  from digitalkin.services.agent.agent_strategy import AgentStrategy
15
23
  from digitalkin.services.cost.cost_strategy import CostStrategy
16
24
  from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
@@ -30,11 +38,22 @@ class ModuleErrorModel(BaseModel):
30
38
  short_description: str
31
39
 
32
40
 
33
- class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretModelT]):
41
+ class BaseModule(
42
+ ABC,
43
+ Generic[
44
+ InputModelT,
45
+ OutputModelT,
46
+ SetupModelT,
47
+ SecretModelT,
48
+ ConfigSetupModelT,
49
+ ],
50
+ ):
34
51
  """BaseModule is the abstract base for all modules in the DigitalKin SDK."""
35
52
 
36
53
  name: str
37
54
  description: str
55
+
56
+ config_setup_format: type[ConfigSetupModelT]
38
57
  input_format: type[InputModelT]
39
58
  output_format: type[OutputModelT]
40
59
  setup_format: type[SetupModelT]
@@ -136,6 +155,23 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
136
155
  msg = "'%s' class does not define an 'output_format'."
137
156
  raise NotImplementedError(msg)
138
157
 
158
+ @classmethod
159
+ def get_config_setup_format(cls, *, llm_format: bool) -> str:
160
+ """Gets the JSON schema of the config setup format model.
161
+
162
+ Raises:
163
+ OptionalFeatureNotImplementedError: If the `config_setup_format` is not defined.
164
+
165
+ Returns:
166
+ The JSON schema of the config setup format as a string.
167
+ """
168
+ if cls.config_setup_format is not None:
169
+ if llm_format:
170
+ return json.dumps(llm_ready_schema(cls.config_setup_format), indent=2)
171
+ return json.dumps(cls.config_setup_format.model_json_schema(), indent=2)
172
+ msg = "'%s' class does not define an 'config_setup_format'."
173
+ raise OptionalFeatureNotImplementedError(msg)
174
+
139
175
  @classmethod
140
176
  def get_setup_format(cls, *, llm_format: bool) -> str:
141
177
  """Gets the JSON schema of the setup format model.
@@ -153,6 +189,18 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
153
189
  msg = "'%s' class does not define an 'setup_format'."
154
190
  raise NotImplementedError(msg)
155
191
 
192
+ @classmethod
193
+ def create_config_setup_model(cls, config_setup_data: dict[str, Any]) -> ConfigSetupModelT:
194
+ """Create the setup model from the setup data.
195
+
196
+ Args:
197
+ config_setup_data: The setup data to create the model from.
198
+
199
+ Returns:
200
+ The setup model.
201
+ """
202
+ return cls.config_setup_format(**config_setup_data)
203
+
156
204
  @classmethod
157
205
  def create_input_model(cls, input_data: dict[str, Any]) -> InputModelT:
158
206
  """Create the input model from the input data.
@@ -201,6 +249,21 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
201
249
  """
202
250
  return cls.output_format(**output_data)
203
251
 
252
+ @abstractmethod
253
+ async def run_config_setup(
254
+ self,
255
+ config_setup_data: ConfigSetupModelT,
256
+ setup_data: SetupModelT,
257
+ callback: Callable,
258
+ ) -> None:
259
+ """Run config setup the module.
260
+
261
+ Raises:
262
+ OptionalFeatureNotImplementedError: If the config setup feature is not implemented.
263
+ """
264
+ msg = f"'{self}' class does not define an optional 'run_config_setup' attribute."
265
+ raise OptionalFeatureNotImplementedError(msg)
266
+
204
267
  @abstractmethod
205
268
  async def initialize(self, setup_data: SetupModelT) -> None:
206
269
  """Initialize the module."""
@@ -302,3 +365,18 @@ class BaseModule(ABC, Generic[InputModelT, OutputModelT, SetupModelT, SecretMode
302
365
  except Exception:
303
366
  self._status = ModuleStatus.FAILED
304
367
  logger.exception("Error stopping module")
368
+
369
+ async def start_config_setup(
370
+ self,
371
+ config_setup_data: ConfigSetupModelT,
372
+ setup_data: SetupModelT,
373
+ callback: Callable[[OutputModelT | ModuleErrorModel], Coroutine[Any, Any, None]],
374
+ ) -> None:
375
+ """Start the module."""
376
+ try:
377
+ logger.info("Run Config Setup lifecycle")
378
+ self._status = ModuleStatus.RUNNING
379
+ await self.run_config_setup(config_setup_data, setup_data, callback)
380
+ except Exception:
381
+ self._status = ModuleStatus.FAILED
382
+ logger.exception("Error during module lifecyle")
@@ -3,8 +3,18 @@
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
6
7
  from digitalkin.modules._base_module import BaseModule
7
8
 
8
9
 
9
- class ArchetypeModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
10
+ class ArchetypeModule(
11
+ BaseModule[
12
+ InputModelT,
13
+ OutputModelT,
14
+ SetupModelT,
15
+ SecretModelT,
16
+ ConfigSetupModelT,
17
+ ],
18
+ ABC,
19
+ ):
10
20
  """ArchetypeModule extends BaseModule to implement specific module types."""
@@ -7,12 +7,13 @@ 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
10
11
  from digitalkin.modules._base_module import BaseModule
11
12
  from digitalkin.services.services_config import ServicesConfig
12
13
  from digitalkin.services.services_models import ServicesMode
13
14
 
14
15
 
15
- class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
16
+ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT, ConfigSetupModelT]):
16
17
  """Abstract base class for managing background module jobs."""
17
18
 
18
19
  async def _start(self) -> None:
@@ -82,14 +83,14 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
82
83
  """
83
84
 
84
85
  @abc.abstractmethod
85
- async def create_job(
86
+ async def create_module_instance_job(
86
87
  self,
87
88
  input_data: InputModelT,
88
89
  setup_data: SetupModelT,
89
90
  mission_id: str,
90
91
  setup_version_id: str,
91
92
  ) -> str:
92
- """Create and start a new job for the module.
93
+ """Create and start a new job for the module's instance.
93
94
 
94
95
  Args:
95
96
  input_data: The input data required to start the job.
@@ -101,6 +102,47 @@ class BaseJobManager(abc.ABC, Generic[InputModelT, SetupModelT]):
101
102
  str: The unique identifier (job ID) of the created job.
102
103
  """
103
104
 
105
+ @abc.abstractmethod
106
+ async def generate_config_setup_module_response(self, job_id: str) -> SetupModelT:
107
+ """Generate a stream consumer for a module's output data.
108
+
109
+ This method creates an asynchronous generator that streams output data
110
+ from a specific module job. If the module does not exist, it generates
111
+ an error message.
112
+
113
+ Args:
114
+ job_id: The unique identifier of the job.
115
+
116
+ Returns:
117
+ SetupModelT: the SetupModelT object fully processed.
118
+ """
119
+
120
+ @abc.abstractmethod
121
+ async def create_config_setup_instance_job(
122
+ self,
123
+ config_setup_data: ConfigSetupModelT,
124
+ setup_data: SetupModelT,
125
+ mission_id: str,
126
+ setup_version_id: str,
127
+ ) -> str:
128
+ """Create and start a new module job.
129
+
130
+ This method initializes a new module job, assigns it a unique job ID,
131
+ and starts it in the background.
132
+
133
+ Args:
134
+ config_setup_data: The input data required to start the job.
135
+ setup_data: The setup configuration for the module.
136
+ mission_id: The mission ID associated with the job.
137
+ setup_version_id: The setup ID.
138
+
139
+ Returns:
140
+ str: The unique identifier (job ID) of the created job.
141
+
142
+ Raises:
143
+ Exception: If the module fails to start.
144
+ """
145
+
104
146
  @abc.abstractmethod
105
147
  async def stop_module(self, job_id: str) -> bool:
106
148
  """Stop a running module job.