digitalkin 0.2.13__py3-none-any.whl → 0.2.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
digitalkin/__version__.py CHANGED
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "0.2.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.
@@ -10,13 +10,13 @@ import grpc
10
10
 
11
11
  from digitalkin.logger import logger
12
12
  from digitalkin.models import ModuleStatus
13
- from digitalkin.models.module import InputModelT, OutputModelT, SetupModelT
13
+ from digitalkin.models.module import ConfigSetupModelT, InputModelT, OutputModelT, SetupModelT
14
14
  from digitalkin.modules._base_module import BaseModule
15
15
  from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
16
16
  from digitalkin.services.services_models import ServicesMode
17
17
 
18
18
 
19
- class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
19
+ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigSetupModelT]):
20
20
  """Manages a single instance of a module job.
21
21
 
22
22
  This class ensures that only one instance of a module job is active at a time.
@@ -44,6 +44,73 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
44
44
  self.modules: dict[str, BaseModule] = {}
45
45
  self.queues: dict[str, asyncio.Queue] = {}
46
46
 
47
+ async def generate_config_setup_module_response(self, job_id: str) -> SetupModelT:
48
+ """Generate a stream consumer for a module's output data.
49
+
50
+ This method creates an asynchronous generator that streams output data
51
+ from a specific module job. If the module does not exist, it generates
52
+ an error message.
53
+
54
+ Args:
55
+ job_id: The unique identifier of the job.
56
+
57
+ Returns:
58
+ SetupModelT: the SetupModelT object fully processed.
59
+ """
60
+ module = self.modules.get(job_id, None)
61
+ logger.debug("Module %s found: %s", job_id, module)
62
+
63
+ try:
64
+ return await self.queues[job_id].get()
65
+ finally:
66
+ logger.info(f"{job_id=}: {self.queues[job_id].empty()}")
67
+ del self.queues[job_id]
68
+
69
+ async def create_config_setup_instance_job(
70
+ self,
71
+ config_setup_data: ConfigSetupModelT,
72
+ setup_data: SetupModelT,
73
+ mission_id: str,
74
+ setup_version_id: str,
75
+ ) -> str:
76
+ """Create and start a new module setup configuration job.
77
+
78
+ This method initializes a new module job, assigns it a unique job ID,
79
+ and starts the config setup it in the background.
80
+
81
+ Args:
82
+ config_setup_data: The input data required to start the job.
83
+ setup_data: The setup configuration for the module.
84
+ mission_id: The mission ID associated with the job.
85
+ setup_version_id: The setup ID.
86
+
87
+ Returns:
88
+ str: The unique identifier (job ID) of the created job.
89
+
90
+ Raises:
91
+ Exception: If the module fails to start.
92
+ """
93
+ job_id = str(uuid.uuid4())
94
+ # TODO: Ensure the job_id is unique.
95
+ module = self.module_class(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
96
+ self.modules[job_id] = module
97
+ self.queues[job_id] = asyncio.Queue()
98
+
99
+ try:
100
+ await module.start_config_setup(
101
+ config_setup_data,
102
+ setup_data,
103
+ await self.job_specific_callback(self.add_to_queue, job_id),
104
+ )
105
+ logger.debug("Module %s (%s) started successfully", job_id, module.name)
106
+ except Exception:
107
+ # Remove the module from the manager in case of an error.
108
+ del self.modules[job_id]
109
+ logger.exception("Failed to start module %s: %s", job_id)
110
+ raise
111
+ else:
112
+ return job_id
113
+
47
114
  async def add_to_queue(self, job_id: str, output_data: OutputModelT) -> None: # type: ignore
48
115
  """Add output data to the queue for a specific job.
49
116
 
@@ -106,7 +173,7 @@ class SingleJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
106
173
 
107
174
  yield _stream()
108
175
 
109
- async def create_job(
176
+ async def create_module_instance_job(
110
177
  self,
111
178
  input_data: InputModelT,
112
179
  setup_data: SetupModelT,
@@ -130,7 +130,7 @@ async def send_message_to_stream(job_id: str, output_data: OutputModelT) -> None
130
130
 
131
131
 
132
132
  @TASKIQ_BROKER.task
133
- async def run_task(
133
+ async def run_start_module(
134
134
  mission_id: str,
135
135
  setup_version_id: str,
136
136
  module_class: type[BaseModule],
@@ -171,3 +171,44 @@ async def run_task(
171
171
  # TODO: should define a BaseModel for stream code / error
172
172
  done_callback=lambda _: asyncio.create_task(callback(StreamCodeModel(code="__END_OF_STREAM__"))),
173
173
  )
174
+
175
+
176
+ @TASKIQ_BROKER.task
177
+ async def run_config_module(
178
+ mission_id: str,
179
+ setup_version_id: str,
180
+ module_class: type[BaseModule],
181
+ services_mode: ServicesMode,
182
+ config_setup_data: dict,
183
+ setup_data: dict,
184
+ context: Context = TaskiqDepends(),
185
+ ) -> None:
186
+ """TaskIQ task allowing a module to compute in the background asynchronously.
187
+
188
+ Args:
189
+ mission_id: str,
190
+ setup_version_id: The setup ID associated with the module.
191
+ module_class: type[BaseModule],
192
+ services_mode: ServicesMode,
193
+ config_setup_data: dict,
194
+ setup_data: dict,
195
+ context: Allow TaskIQ context access
196
+ """
197
+ logger.warning("%s", services_mode)
198
+ services_config = ServicesConfig(
199
+ services_config_strategies=module_class.services_config_strategies,
200
+ services_config_params=module_class.services_config_params,
201
+ mode=services_mode,
202
+ )
203
+ setattr(module_class, "services_config", services_config)
204
+ logger.warning("%s | %s", services_config, module_class.services_config)
205
+
206
+ job_id = context.message.task_id
207
+ callback = await BaseJobManager.job_specific_callback(send_message_to_stream, job_id)
208
+ module = module_class(job_id, mission_id=mission_id, setup_version_id=setup_version_id)
209
+
210
+ await module.start_config_setup(
211
+ module_class.create_config_setup_model(config_setup_data),
212
+ module_class.create_setup_model(setup_data),
213
+ callback,
214
+ )
@@ -18,7 +18,7 @@ from typing import TYPE_CHECKING, Any, Generic
18
18
  from rstream import Consumer, ConsumerOffsetSpecification, MessageContext, OffsetType
19
19
 
20
20
  from digitalkin.logger import logger
21
- from digitalkin.models.module import InputModelT, SetupModelT
21
+ from digitalkin.models.module import ConfigSetupModelT, InputModelT, SetupModelT
22
22
  from digitalkin.models.module.module import ModuleStatus
23
23
  from digitalkin.modules._base_module import BaseModule
24
24
  from digitalkin.modules.job_manager.base_job_manager import BaseJobManager
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
29
29
  from taskiq.task import AsyncTaskiqTask
30
30
 
31
31
 
32
- class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
32
+ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT, ConfigSetupModelT]):
33
33
  """Taskiq job manager for running modules in Taskiq tasks."""
34
34
 
35
35
  services_mode: ServicesMode
@@ -95,6 +95,92 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
95
95
  with contextlib.suppress(asyncio.CancelledError):
96
96
  await self.stream_consumer_task
97
97
 
98
+ def __init__(
99
+ self,
100
+ module_class: type[BaseModule],
101
+ services_mode: ServicesMode,
102
+ ) -> None:
103
+ """Initialize the Taskiq job manager."""
104
+ super().__init__(module_class, services_mode)
105
+
106
+ logger.warning("TaskiqJobManager initialized with app: %s", TASKIQ_BROKER)
107
+ self.services_mode = services_mode
108
+ self.job_queues: dict[str, asyncio.Queue] = {}
109
+ self.max_queue_size = 1000
110
+
111
+ async def generate_config_setup_module_response(self, job_id: str) -> SetupModelT:
112
+ """Generate a stream consumer for a module's output data.
113
+
114
+ This method creates an asynchronous generator that streams output data
115
+ from a specific module job. If the module does not exist, it generates
116
+ an error message.
117
+
118
+ Args:
119
+ job_id: The unique identifier of the job.
120
+
121
+ Returns:
122
+ SetupModelT: the SetupModelT object fully processed.
123
+ """
124
+ queue: asyncio.Queue = asyncio.Queue(maxsize=self.max_queue_size)
125
+ self.job_queues[job_id] = queue
126
+
127
+ try:
128
+ item = await queue.get()
129
+ queue.task_done()
130
+ return item
131
+ finally:
132
+ logger.info(f"generate_config_setup_module_response: {job_id=}: {self.job_queues[job_id].empty()}")
133
+ self.job_queues.pop(job_id, None)
134
+
135
+ async def create_config_setup_instance_job(
136
+ self,
137
+ config_setup_data: ConfigSetupModelT,
138
+ setup_data: SetupModelT,
139
+ mission_id: str,
140
+ setup_version_id: str,
141
+ ) -> str:
142
+ """Create and start a new module setup configuration job.
143
+
144
+ This method initializes a new module job, assigns it a unique job ID,
145
+ and starts the config setup it in the background.
146
+
147
+ Args:
148
+ config_setup_data: The input data required to start the job.
149
+ setup_data: The setup configuration for the module.
150
+ mission_id: The mission ID associated with the job.
151
+ setup_version_id: The setup ID.
152
+
153
+ Returns:
154
+ str: The unique identifier (job ID) of the created job.
155
+
156
+ Raises:
157
+ TypeError: If the function is called with bad data type.
158
+ ValueError: If the module fails to start.
159
+ """
160
+ task = TASKIQ_BROKER.find_task("digitalkin.modules.job_manager.taskiq_broker:run_config_module")
161
+
162
+ if task is None:
163
+ msg = "Task not found"
164
+ raise ValueError(msg)
165
+
166
+ if config_setup_data is None:
167
+ msg = "config_setup_data must be a valid model with model_dump method"
168
+ raise TypeError(msg)
169
+
170
+ running_task: AsyncTaskiqTask[Any] = await task.kiq(
171
+ mission_id,
172
+ setup_version_id,
173
+ self.module_class,
174
+ self.services_mode,
175
+ config_setup_data.model_dump(), # type: ignore
176
+ setup_data.model_dump(),
177
+ )
178
+
179
+ job_id = running_task.task_id
180
+ result = await running_task.wait_result(timeout=10)
181
+ logger.info("Job %s with data %s", job_id, result)
182
+ return job_id
183
+
98
184
  @asynccontextmanager # type: ignore
99
185
  async def generate_stream_consumer(self, job_id: str) -> AsyncIterator[AsyncGenerator[dict[str, Any], None]]: # type: ignore
100
186
  """Generate a stream consumer for the RStream stream.
@@ -132,20 +218,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
132
218
  finally:
133
219
  self.job_queues.pop(job_id, None)
134
220
 
135
- def __init__(
136
- self,
137
- module_class: type[BaseModule],
138
- services_mode: ServicesMode,
139
- ) -> None:
140
- """Initialize the Taskiq job manager."""
141
- super().__init__(module_class, services_mode)
142
-
143
- logger.warning("TaskiqJobManager initialized with app: %s", TASKIQ_BROKER)
144
- self.services_mode = services_mode
145
- self.job_queues: dict[str, asyncio.Queue] = {}
146
- self.max_queue_size = 1000
147
-
148
- async def create_job(
221
+ async def create_module_instance_job(
149
222
  self,
150
223
  input_data: InputModelT,
151
224
  setup_data: SetupModelT,
@@ -166,7 +239,7 @@ class TaskiqJobManager(BaseJobManager, Generic[InputModelT, SetupModelT]):
166
239
  Raises:
167
240
  ValueError: If the task is not found.
168
241
  """
169
- task = TASKIQ_BROKER.find_task("digitalkin.modules.job_manager.taskiq_broker:run_task")
242
+ task = TASKIQ_BROKER.find_task("digitalkin.modules.job_manager.taskiq_broker:run_start_module")
170
243
 
171
244
  if task is None:
172
245
  msg = "Task not found"
@@ -3,8 +3,9 @@
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 # type: ignore
7
8
 
8
9
 
9
- class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
10
+ class ToolModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,ConfigSetupModelT,], ABC):
10
11
  """ToolModule extends BaseModule to implement specific module types."""
@@ -2,8 +2,10 @@
2
2
 
3
3
  from abc import ABC
4
4
 
5
+ from digitalkin.models.module.module_types import ConfigSetupModelT
5
6
  from digitalkin.modules._base_module import BaseModule, InputModelT, OutputModelT, SecretModelT, SetupModelT
6
7
 
7
8
 
8
- class TriggerModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT], ABC):
9
+ class TriggerModule(BaseModule[InputModelT, OutputModelT, SetupModelT, SecretModelT,
10
+ ConfigSetupModelT,], ABC):
9
11
  """TriggerModule extends BaseModule to implement specific module types."""
@@ -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
@@ -7,37 +7,37 @@ 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=FYm4fUSZE0epsu7qiHq0IkVIvAs5ZzTEDWDQWZeryGs,191
10
+ digitalkin/__version__.py,sha256=yRvTSc47o9ijGrVlx1OEwjguXmUPtnbjcgpjhitwyMw,191
11
11
  digitalkin/logger.py,sha256=cFbIAZHOFx3nddOssRNYLXyqUPzR4CgDR_c-5wmB-og,1685
12
12
  digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  digitalkin/grpc_servers/__init__.py,sha256=0cJBlwipSmFdXkyH3T0i6OJ1WpAtNsZgYX7JaSnkbtg,804
14
14
  digitalkin/grpc_servers/_base_server.py,sha256=NXnnZPjJqUDWoumhEbb7EOEWB7d8XYwpQs-l97NTe4k,18647
15
15
  digitalkin/grpc_servers/module_server.py,sha256=tc4n8fu7bcMkwCI3CKkAKR4cZqyP_JTEW0lGtqsc5hs,10283
16
- digitalkin/grpc_servers/module_servicer.py,sha256=I6S8qDhKOwDD1tofoUhAfhN8VH6A7hAM5UAlwu91_Is,14747
16
+ digitalkin/grpc_servers/module_servicer.py,sha256=DZ1c_3XAXt-m5I6-CGqmrQ8TAFrYCUQj8R5rqMDKeQc,18383
17
17
  digitalkin/grpc_servers/registry_server.py,sha256=StY18DKYoPKQIU1SIzgito6D4_QA1aMVddZ8O2WGlHY,2223
18
18
  digitalkin/grpc_servers/registry_servicer.py,sha256=dqsKGHZ0LnaIvGt4ipaAuigd37sbJBndT4MAT029GsY,16471
19
- digitalkin/grpc_servers/utils/exceptions.py,sha256=I00OM8p8up20He4dU1fiHsvdLj1DymjR_UmoeUm2MSA,785
19
+ digitalkin/grpc_servers/utils/exceptions.py,sha256=SyOgvjggaUECYmSiqy8KJLHwHVt5IClSTxslHM-IZzI,931
20
20
  digitalkin/grpc_servers/utils/factory.py,sha256=jm6rFjiqmtSv7BIHNAOxsG9xXtSvWpx9TfzSQiX97MQ,5899
21
21
  digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=GXRqCTCufwWd8sVMoClnl-JpeBjgmdlSCD0a3YMU9D0,2594
22
22
  digitalkin/grpc_servers/utils/models.py,sha256=80F5oHiv8MOqMoDZJBXmJSRoVYJRyhaVcijQ2LinAig,8428
23
23
  digitalkin/grpc_servers/utils/types.py,sha256=rQ78s4nAet2jy-NIDj_PUWriT0kuGHr_w6ELjmjgBao,539
24
24
  digitalkin/models/__init__.py,sha256=hDHtUfswaNh8wo4NZaBItg9JqC0uNSRqXArNWSrGynY,163
25
- digitalkin/models/module/__init__.py,sha256=ihvRlemJuFvU4armZIL-Vq-zaJx9UrjDoJAVPbEG8jw,345
25
+ digitalkin/models/module/__init__.py,sha256=iQJgq3OVvxoY6gkNCVdVz5bSGgLCy0TMY5H1FFxs-0Q,389
26
26
  digitalkin/models/module/module.py,sha256=NWOwCeD_NH0NL89AF_NJ0SwE7G1n-HTcRalodLCDmpM,749
27
- digitalkin/models/module/module_types.py,sha256=SxwzfDDnkyuQe50glVrxN6VPuJCkbB313FRyVZugIMw,312
27
+ digitalkin/models/module/module_types.py,sha256=rlT6prjBN-h1sLKstSwjxWXiyFgtxEn1mjOSLgfI6uA,385
28
28
  digitalkin/models/services/__init__.py,sha256=HsW7MUGFPvH7Ri28WN4BHHBfEQk5dzU_9FOWAc-0lSE,56
29
29
  digitalkin/models/services/cost.py,sha256=QTEuFD6xz62nob0z4ksE-INJWcZ-iFiuNW5mvXhpFes,1599
30
30
  digitalkin/models/services/storage.py,sha256=cYTVIriGKiprF9OerhSxmc_jM6fUTVwmeon1yQCinkE,143
31
31
  digitalkin/modules/__init__.py,sha256=ppYARmhvdVi55ofC0QZerIempSlcJYDeCXhcl4qXObw,278
32
- digitalkin/modules/_base_module.py,sha256=EwfCCIy3N1eBdBUj6KAdx0WzNRFJsyQDVCdA8Lwvtn4,10463
33
- digitalkin/modules/archetype_module.py,sha256=T2Ehj7EpAC2MO9WQbJv39hqRw7rh3exhVZTEL3JPM8U,421
34
- digitalkin/modules/tool_module.py,sha256=86g0M1wHZ1ReIc7AkKfyjnlGN2QYJBGxrEQpKVlyrZI,421
35
- digitalkin/modules/trigger_module.py,sha256=kVoI4Gdkw7WWUP5T6hSCNqw5FxibTxL6Tpq9KP7gg78,379
36
- digitalkin/modules/job_manager/base_job_manager.py,sha256=I3bduyGDjCJ6RsbwQvLp_NA6ucP8MJJVoIKpEbaP9ss,4706
32
+ digitalkin/modules/_base_module.py,sha256=wFnGGN97RNu3TJMdgMktOKdYPWmJL9WnwR120bPRXQg,12922
33
+ digitalkin/modules/archetype_module.py,sha256=7rleVOkZfFR7TQeaq6oZXLBuRWixKsUJMOr9HfDV0T8,566
34
+ digitalkin/modules/tool_module.py,sha256=7eWem70dEJiyoCtkGIJP4Ak2G8MzSruOjrByQMqW9-I,508
35
+ digitalkin/modules/trigger_module.py,sha256=FNiJmG3nTG1nDqKC1XdJI8PmFNZNPs1_QrH6RX7ev2Y,475
36
+ digitalkin/modules/job_manager/base_job_manager.py,sha256=Ku_MT7Cuf1R_qJU7SV-yHkzUhhMtmDOa8t5DyetvMxw,6177
37
37
  digitalkin/modules/job_manager/job_manager_models.py,sha256=onHy-DfInLZQveniMIWIKwTKSQjojz500JvHB54x93c,1129
38
- digitalkin/modules/job_manager/single_job_manager.py,sha256=Dekcnn8wb4epqGrWFAZ86YvvII553Hw0xjipgAINl4A,7745
39
- digitalkin/modules/job_manager/taskiq_broker.py,sha256=OnHZy-hAtVDU5FU4W9folZZKCrDrUk16VfHlfoECuiQ,5930
40
- digitalkin/modules/job_manager/taskiq_job_manager.py,sha256=ODqC7Snf-Flo0ugotqicrTgjFu4wgll6bUu7CM3l7ZE,7459
38
+ digitalkin/modules/job_manager/single_job_manager.py,sha256=DkQV3-E9XqgmuC6hoYKolTX2iv9lUJ8BSCde3zk1OU8,10264
39
+ digitalkin/modules/job_manager/taskiq_broker.py,sha256=sMXIgheOBshMiBUWUKph_5rxBHkip4rKusrrxFoa7uY,7379
40
+ digitalkin/modules/job_manager/taskiq_job_manager.py,sha256=evcE3o51DPWzW2y4ri6DFQZNZbFJXyvFb65SibExJH8,10152
41
41
  digitalkin/services/__init__.py,sha256=LqGk_5DJy8Bzz62ajIq9jCeYNKQUIgtSCpafZk15FLc,910
42
42
  digitalkin/services/base_strategy.py,sha256=QAQnJw1BbqcYMSzwlFyhHP5juBH2WKrZzWxqDr_sDHI,638
43
43
  digitalkin/services/services_config.py,sha256=4hc7-rHgSigoS3SuV0V9FReD2Dz7XoMXcD6iMBP2KKM,7391
@@ -74,13 +74,13 @@ digitalkin/utils/__init__.py,sha256=sJnY-ZUgsjMfojAjONC1VN14mhgIDnzyOlGkw21rRnM,
74
74
  digitalkin/utils/arg_parser.py,sha256=nvjI1pKDY1HfS0oGcMQPtdTQcggXLtpxXMbnMxNEKRU,3109
75
75
  digitalkin/utils/development_mode_action.py,sha256=TqRuAF_A7bDD4twRB4PnZcRoNeaiAnEdxM5kvy4aoaA,1511
76
76
  digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
77
- digitalkin-0.2.13.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
77
+ digitalkin-0.2.14.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
78
78
  modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- modules/cpu_intensive_module.py,sha256=LUfgd1qFJ0HfsWMapSXUivgf5KGDpANKPORbcQBUjc4,8089
80
- modules/minimal_llm_module.py,sha256=6cygPliUb4mroOrUo5YirZfQQ1IeOT3xUHzPpDC_0Tc,10045
79
+ modules/cpu_intensive_module.py,sha256=Nd_dHcnOKwhhJOLdqlfGklrxZkABV-iG-2g8wTsPUpg,8389
80
+ modules/minimal_llm_module.py,sha256=Ijld__ZnhzfLwpXD1XVkLZ7jyKZKyOFZczOpiPttJZc,11216
81
81
  modules/storage_module.py,sha256=KJA75oxXDUxZrLfoY5KxeyI8Gm1b3DxFuFj7LGOkeaQ,6389
82
82
  modules/text_transform_module.py,sha256=Db_WbJi7_gWyoNvQEVhHQf5VurDsU-jd9LMAalG0oZY,7277
83
- digitalkin-0.2.13.dist-info/METADATA,sha256=CUpDclUxsamv7icPsWN-ImippAec3_EQZUGm1cQGHFQ,30577
84
- digitalkin-0.2.13.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
85
- digitalkin-0.2.13.dist-info/top_level.txt,sha256=5_5e35inSM5YfWNZE21p5wGBojiVtQQML_WzbEk4BRU,31
86
- digitalkin-0.2.13.dist-info/RECORD,,
83
+ digitalkin-0.2.14.dist-info/METADATA,sha256=2srdCp7b3ieTRigCaUMqFJc2c3Q0tQq1LLBZgSoBUVw,30580
84
+ digitalkin-0.2.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
+ digitalkin-0.2.14.dist-info/top_level.txt,sha256=5_5e35inSM5YfWNZE21p5wGBojiVtQQML_WzbEk4BRU,31
86
+ digitalkin-0.2.14.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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.