digitalkin 0.3.1.dev1__py3-none-any.whl → 0.3.2a2__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.
Files changed (87) hide show
  1. base_server/server_async_insecure.py +6 -5
  2. base_server/server_async_secure.py +6 -5
  3. base_server/server_sync_insecure.py +5 -4
  4. base_server/server_sync_secure.py +5 -4
  5. digitalkin/__version__.py +1 -1
  6. digitalkin/core/job_manager/base_job_manager.py +1 -1
  7. digitalkin/core/job_manager/single_job_manager.py +78 -36
  8. digitalkin/core/job_manager/taskiq_broker.py +8 -7
  9. digitalkin/core/job_manager/taskiq_job_manager.py +9 -5
  10. digitalkin/core/task_manager/base_task_manager.py +3 -1
  11. digitalkin/core/task_manager/surrealdb_repository.py +13 -7
  12. digitalkin/core/task_manager/task_executor.py +27 -10
  13. digitalkin/core/task_manager/task_session.py +133 -101
  14. digitalkin/grpc_servers/module_server.py +95 -171
  15. digitalkin/grpc_servers/module_servicer.py +133 -27
  16. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +36 -10
  17. digitalkin/grpc_servers/utils/utility_schema_extender.py +106 -0
  18. digitalkin/models/__init__.py +1 -1
  19. digitalkin/models/core/job_manager_models.py +0 -8
  20. digitalkin/models/core/task_monitor.py +23 -1
  21. digitalkin/models/grpc_servers/models.py +95 -8
  22. digitalkin/models/module/__init__.py +26 -13
  23. digitalkin/models/module/base_types.py +61 -0
  24. digitalkin/models/module/module_context.py +279 -13
  25. digitalkin/models/module/module_types.py +29 -109
  26. digitalkin/models/module/setup_types.py +547 -0
  27. digitalkin/models/module/tool_cache.py +230 -0
  28. digitalkin/models/module/tool_reference.py +160 -0
  29. digitalkin/models/module/utility.py +167 -0
  30. digitalkin/models/services/cost.py +22 -1
  31. digitalkin/models/services/registry.py +77 -0
  32. digitalkin/modules/__init__.py +5 -1
  33. digitalkin/modules/_base_module.py +253 -90
  34. digitalkin/modules/archetype_module.py +6 -1
  35. digitalkin/modules/tool_module.py +6 -1
  36. digitalkin/modules/triggers/__init__.py +8 -0
  37. digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  38. digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  39. digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  40. digitalkin/services/__init__.py +4 -0
  41. digitalkin/services/communication/__init__.py +7 -0
  42. digitalkin/services/communication/communication_strategy.py +87 -0
  43. digitalkin/services/communication/default_communication.py +104 -0
  44. digitalkin/services/communication/grpc_communication.py +264 -0
  45. digitalkin/services/cost/cost_strategy.py +36 -14
  46. digitalkin/services/cost/default_cost.py +61 -1
  47. digitalkin/services/cost/grpc_cost.py +98 -2
  48. digitalkin/services/filesystem/grpc_filesystem.py +9 -2
  49. digitalkin/services/registry/__init__.py +22 -1
  50. digitalkin/services/registry/default_registry.py +156 -4
  51. digitalkin/services/registry/exceptions.py +47 -0
  52. digitalkin/services/registry/grpc_registry.py +382 -0
  53. digitalkin/services/registry/registry_models.py +15 -0
  54. digitalkin/services/registry/registry_strategy.py +106 -4
  55. digitalkin/services/services_config.py +25 -3
  56. digitalkin/services/services_models.py +5 -1
  57. digitalkin/services/setup/default_setup.py +1 -1
  58. digitalkin/services/setup/grpc_setup.py +1 -1
  59. digitalkin/services/storage/grpc_storage.py +1 -1
  60. digitalkin/services/user_profile/__init__.py +11 -0
  61. digitalkin/services/user_profile/grpc_user_profile.py +2 -2
  62. digitalkin/services/user_profile/user_profile_strategy.py +0 -15
  63. digitalkin/utils/__init__.py +40 -0
  64. digitalkin/utils/conditional_schema.py +260 -0
  65. digitalkin/utils/dynamic_schema.py +487 -0
  66. digitalkin/utils/schema_splitter.py +290 -0
  67. {digitalkin-0.3.1.dev1.dist-info → digitalkin-0.3.2a2.dist-info}/METADATA +13 -13
  68. digitalkin-0.3.2a2.dist-info/RECORD +144 -0
  69. {digitalkin-0.3.1.dev1.dist-info → digitalkin-0.3.2a2.dist-info}/WHEEL +1 -1
  70. {digitalkin-0.3.1.dev1.dist-info → digitalkin-0.3.2a2.dist-info}/top_level.txt +1 -0
  71. modules/archetype_with_tools_module.py +232 -0
  72. modules/cpu_intensive_module.py +1 -1
  73. modules/dynamic_setup_module.py +338 -0
  74. modules/minimal_llm_module.py +1 -1
  75. modules/text_transform_module.py +1 -1
  76. monitoring/digitalkin_observability/__init__.py +46 -0
  77. monitoring/digitalkin_observability/http_server.py +150 -0
  78. monitoring/digitalkin_observability/interceptors.py +176 -0
  79. monitoring/digitalkin_observability/metrics.py +201 -0
  80. monitoring/digitalkin_observability/prometheus.py +137 -0
  81. monitoring/tests/test_metrics.py +172 -0
  82. services/filesystem_module.py +7 -5
  83. services/storage_module.py +4 -2
  84. digitalkin/grpc_servers/registry_server.py +0 -65
  85. digitalkin/grpc_servers/registry_servicer.py +0 -456
  86. digitalkin-0.3.1.dev1.dist-info/RECORD +0 -117
  87. {digitalkin-0.3.1.dev1.dist-info → digitalkin-0.3.2a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,23 @@
1
1
  """Define the module context used in the triggers."""
2
2
 
3
+ import os
4
+ from collections.abc import AsyncGenerator, Callable, Coroutine
5
+ from datetime import tzinfo
3
6
  from types import SimpleNamespace
4
7
  from typing import Any
8
+ from zoneinfo import ZoneInfo
5
9
 
10
+ from digitalkin.logger import logger
11
+ from digitalkin.models.module.tool_cache import ToolCache, ToolDefinition, ToolModuleInfo, ToolParameter
6
12
  from digitalkin.services.agent.agent_strategy import AgentStrategy
13
+ from digitalkin.services.communication.communication_strategy import CommunicationStrategy
7
14
  from digitalkin.services.cost.cost_strategy import CostStrategy
8
15
  from digitalkin.services.filesystem.filesystem_strategy import FilesystemStrategy
9
16
  from digitalkin.services.identity.identity_strategy import IdentityStrategy
10
17
  from digitalkin.services.registry.registry_strategy import RegistryStrategy
11
18
  from digitalkin.services.snapshot.snapshot_strategy import SnapshotStrategy
12
19
  from digitalkin.services.storage.storage_strategy import StorageStrategy
20
+ from digitalkin.services.user_profile.user_profile_strategy import UserProfileStrategy
13
21
 
14
22
 
15
23
  class Session(SimpleNamespace):
@@ -19,6 +27,7 @@ class Session(SimpleNamespace):
19
27
  mission_id: str
20
28
  setup_id: str
21
29
  setup_version_id: str
30
+ timezone: tzinfo
22
31
 
23
32
  def __init__(
24
33
  self,
@@ -26,37 +35,32 @@ class Session(SimpleNamespace):
26
35
  mission_id: str,
27
36
  setup_id: str,
28
37
  setup_version_id: str,
38
+ timezone: tzinfo | None = None,
29
39
  **kwargs: dict[str, Any],
30
40
  ) -> None:
31
41
  """Init Module Session.
32
42
 
33
- Args:
34
- job_id: current job_id.
35
- mission_id: current mission_id.
36
- setup_id: used setup config.
37
- setup_version_id: used setup config.
38
- kwargs: user defined session variables.
39
-
40
43
  Raises:
41
- ValueError: If mandatory args are missing
44
+ ValueError: If mandatory args are missing.
42
45
  """
43
46
  if not setup_id:
44
- msg = "setup_id is mandatory and cannot be empty"
47
+ msg = "setup_id is mandatory"
45
48
  raise ValueError(msg)
46
49
  if not setup_version_id:
47
- msg = "setup_version_id is mandatory and cannot be empty"
50
+ msg = "setup_version_id is mandatory"
48
51
  raise ValueError(msg)
49
52
  if not mission_id:
50
- msg = "mission_id is mandatory and cannot be empty"
53
+ msg = "mission_id is mandatory"
51
54
  raise ValueError(msg)
52
55
  if not job_id:
53
- msg = "job_id is mandatory and cannot be empty"
56
+ msg = "job_id is mandatory"
54
57
  raise ValueError(msg)
55
58
 
56
59
  self.job_id = job_id
57
60
  self.mission_id = mission_id
58
61
  self.setup_id = setup_id
59
62
  self.setup_version_id = setup_version_id
63
+ self.timezone = timezone or ZoneInfo(os.environ.get("DIGITALKIN_TIMEZONE", "Europe/Paris"))
60
64
 
61
65
  super().__init__(**kwargs)
62
66
 
@@ -83,58 +87,320 @@ class ModuleContext:
83
87
 
84
88
  # services list
85
89
  agent: AgentStrategy
90
+ communication: CommunicationStrategy
86
91
  cost: CostStrategy
87
92
  filesystem: FilesystemStrategy
88
93
  identity: IdentityStrategy
89
94
  registry: RegistryStrategy
90
95
  snapshot: SnapshotStrategy
91
96
  storage: StorageStrategy
97
+ user_profile: UserProfileStrategy
92
98
 
93
99
  session: Session
94
100
  callbacks: SimpleNamespace
95
101
  metadata: SimpleNamespace
96
102
  helpers: SimpleNamespace
97
103
  state: SimpleNamespace = SimpleNamespace()
104
+ tool_cache: ToolCache
98
105
 
99
106
  def __init__( # noqa: PLR0913, PLR0917
100
107
  self,
101
108
  agent: AgentStrategy,
109
+ communication: CommunicationStrategy,
102
110
  cost: CostStrategy,
103
111
  filesystem: FilesystemStrategy,
104
112
  identity: IdentityStrategy,
105
113
  registry: RegistryStrategy,
106
114
  snapshot: SnapshotStrategy,
107
115
  storage: StorageStrategy,
116
+ user_profile: UserProfileStrategy,
108
117
  session: dict[str, Any],
109
118
  metadata: dict[str, Any] = {},
110
119
  helpers: dict[str, Any] = {},
111
120
  callbacks: dict[str, Any] = {},
121
+ tool_cache: ToolCache | None = None,
112
122
  ) -> None:
113
123
  """Register mandatory services, session, metadata and callbacks.
114
124
 
115
125
  Args:
116
126
  agent: AgentStrategy.
127
+ communication: CommunicationStrategy.
117
128
  cost: CostStrategy.
118
129
  filesystem: FilesystemStrategy.
119
130
  identity: IdentityStrategy.
120
131
  registry: RegistryStrategy.
121
132
  snapshot: SnapshotStrategy.
122
133
  storage: StorageStrategy.
134
+ user_profile: UserProfileStrategy.
123
135
  metadata: dict defining differents Module metadata.
124
136
  helpers: dict different user defined helpers.
125
137
  session: dict referring the session IDs or informations.
126
138
  callbacks: Functions allowing user to agent interaction.
139
+ tool_cache: ToolCache with pre-resolved tool references from setup.
127
140
  """
128
- # Core services
129
141
  self.agent = agent
142
+ self.communication = communication
130
143
  self.cost = cost
131
144
  self.filesystem = filesystem
132
145
  self.identity = identity
133
146
  self.registry = registry
134
147
  self.snapshot = snapshot
135
148
  self.storage = storage
149
+ self.user_profile = user_profile
136
150
 
137
151
  self.metadata = SimpleNamespace(**metadata)
138
152
  self.session = Session(**session)
139
153
  self.helpers = SimpleNamespace(**helpers)
140
154
  self.callbacks = SimpleNamespace(**callbacks)
155
+ self.tool_cache = tool_cache or ToolCache()
156
+
157
+ async def call_module_by_id(
158
+ self,
159
+ module_id: str,
160
+ input_data: dict,
161
+ setup_id: str,
162
+ mission_id: str,
163
+ callback: Callable[[dict], Coroutine[Any, Any, None]] | None = None,
164
+ ) -> AsyncGenerator[dict, None]:
165
+ """Call a module by ID, discovering address/port from registry.
166
+
167
+ Args:
168
+ module_id: Module identifier to look up in registry.
169
+ input_data: Input data as dictionary.
170
+ setup_id: Setup configuration ID.
171
+ mission_id: Mission context ID.
172
+ callback: Optional callback for each response.
173
+
174
+ Yields:
175
+ Streaming responses from module as dictionaries.
176
+ """
177
+ module_info = self.registry.discover_by_id(module_id)
178
+
179
+ logger.debug(
180
+ "Calling module by ID",
181
+ extra={
182
+ "module_id": module_id,
183
+ "address": module_info.address,
184
+ "port": module_info.port,
185
+ },
186
+ )
187
+
188
+ async for response in self.communication.call_module(
189
+ module_address=module_info.address,
190
+ module_port=module_info.port,
191
+ input_data=input_data,
192
+ setup_id=setup_id,
193
+ mission_id=mission_id,
194
+ callback=callback,
195
+ ):
196
+ yield response
197
+
198
+ async def get_module_schemas_by_id(
199
+ self,
200
+ module_id: str,
201
+ *,
202
+ llm_format: bool = False,
203
+ ) -> dict[str, dict]:
204
+ """Get module schemas by ID, discovering address/port from registry.
205
+
206
+ Args:
207
+ module_id: Module identifier to look up in registry.
208
+ llm_format: If True, return LLM-optimized schema format.
209
+
210
+ Returns:
211
+ Dictionary containing schemas: {"input": ..., "output": ..., "setup": ..., "secret": ...}
212
+ """
213
+ module_info = self.registry.discover_by_id(module_id)
214
+
215
+ logger.debug(
216
+ "Getting module schemas by ID",
217
+ extra={
218
+ "module_id": module_id,
219
+ "address": module_info.address,
220
+ "port": module_info.port,
221
+ },
222
+ )
223
+
224
+ return await self.communication.get_module_schemas(
225
+ module_address=module_info.address,
226
+ module_port=module_info.port,
227
+ llm_format=llm_format,
228
+ )
229
+
230
+ def create_openai_style_tools(self, slug: str) -> list[dict[str, Any]]:
231
+ """Create OpenAI-style function calling schemas for a tool module.
232
+
233
+ Uses tool cache (fast path) with registry fallback. Returns one schema
234
+ per ToolDefinition (protocol) in the module. Includes cost information
235
+ both in the description and as separate metadata.
236
+
237
+ Args:
238
+ slug: Module ID to look up (checks cache first, then registry).
239
+
240
+ Returns:
241
+ List of OpenAI-style tool schemas, one per protocol. Empty if not found.
242
+ """
243
+ tool_module_info = self.tool_cache.get(slug)
244
+ if not tool_module_info:
245
+ return []
246
+
247
+ cost_info = ModuleContext._build_cost_info(tool_module_info.cost_config)
248
+ cost_description = ModuleContext._build_cost_description(tool_module_info.cost_config)
249
+
250
+ return [
251
+ {
252
+ "type": "function",
253
+ "function": {
254
+ "module_id": tool_module_info.module_id,
255
+ "toolkit_name": tool_module_info.tool_name or "undefined",
256
+ "name": tool_module_info.slug + "_" + tool_def.name,
257
+ "description": tool_def.description + cost_description,
258
+ "parameters": ModuleContext._build_parameters_schema(tool_def.parameters),
259
+ },
260
+ "cost_info": cost_info,
261
+ }
262
+ for tool_def in tool_module_info.tools
263
+ ]
264
+
265
+ @staticmethod
266
+ def _build_parameters_schema(params: list[ToolParameter]) -> dict[str, Any]:
267
+ """Convert ToolParameter list to JSON Schema.
268
+
269
+ Args:
270
+ params: List of tool parameters.
271
+
272
+ Returns:
273
+ JSON Schema object with properties and required fields.
274
+ """
275
+ return {
276
+ "type": "object",
277
+ "properties": {p.name: {"type": p.type, "description": p.description or ""} for p in params},
278
+ "required": [p.name for p in params if p.required],
279
+ }
280
+
281
+ @staticmethod
282
+ def _build_cost_info(cost_config: dict[str, Any]) -> dict[str, Any]:
283
+ """Build cost information structure for tool metadata.
284
+
285
+ Args:
286
+ cost_config: Cost configuration dictionary from tool module.
287
+
288
+ Returns:
289
+ Structured cost information for LLM consumption.
290
+ """
291
+ if not cost_config:
292
+ return {}
293
+
294
+ costs = cost_config.get("costs", cost_config)
295
+ return {
296
+ "costs": {
297
+ name: {
298
+ "type": config.get("type", ""),
299
+ "unit": config.get("unit", ""),
300
+ "rate": config.get("rate", 0),
301
+ "description": config.get("description", ""),
302
+ }
303
+ for name, config in costs.items()
304
+ }
305
+ }
306
+
307
+ @staticmethod
308
+ def _build_cost_description(cost_config: dict[str, Any]) -> str:
309
+ """Build human-readable cost summary for LLM tool description.
310
+
311
+ Args:
312
+ cost_config: Cost configuration dictionary from tool module.
313
+
314
+ Returns:
315
+ Human-readable cost summary string.
316
+ """
317
+ if not cost_config:
318
+ return ""
319
+
320
+ costs = cost_config.get("costs", cost_config)
321
+ parts = []
322
+ for name, config in costs.items():
323
+ rate = config.get("rate", 0)
324
+ unit = config.get("unit", "unit")
325
+ cost_type = config.get("type", "")
326
+ parts.append(f"{name}: ${rate}/{unit} ({cost_type})")
327
+
328
+ return f" [Cost: {', '.join(parts)}]" if parts else ""
329
+
330
+ def create_tool_functions(
331
+ self,
332
+ slug: str,
333
+ ) -> list[tuple[ToolDefinition, Callable[..., AsyncGenerator[dict, None]]]]:
334
+ """Create tool functions for all protocols in a tool setup.
335
+
336
+ Returns an async generator per ToolDefinition that calls the remote tool
337
+ module via gRPC with the protocol auto-injected.
338
+
339
+ This method only uses the tool cache (no registry fallback). Use this
340
+ in sync contexts like __init__ methods.
341
+
342
+ Args:
343
+ slug: Setup ID to look up in cache.
344
+
345
+ Returns:
346
+ List of (ToolDefinition, async_generator_function) tuples. Empty if not found.
347
+ """
348
+ tool_module_info = self.tool_cache.entries.get(slug)
349
+ if not tool_module_info:
350
+ return []
351
+
352
+ communication = self.communication
353
+ session = self.session
354
+
355
+ result = []
356
+ for tool_def in tool_module_info.tools:
357
+ # Capture tool_def in closure via separate method
358
+ fn = ModuleContext._create_single_tool_function(communication, session, tool_module_info, tool_def)
359
+ result.append((tool_def, fn))
360
+
361
+ return result
362
+
363
+ @staticmethod
364
+ def _create_single_tool_function(
365
+ communication: CommunicationStrategy,
366
+ session: Session,
367
+ tool_module_info: ToolModuleInfo,
368
+ tool_def: ToolDefinition,
369
+ ) -> Callable[..., AsyncGenerator[dict, None]]:
370
+ """Create a single tool function for a specific protocol.
371
+
372
+ Args:
373
+ communication: Communication strategy for gRPC calls.
374
+ session: Current session with setup_id and mission_id.
375
+ tool_module_info: Tool module information containing address and port.
376
+ tool_def: Tool definition with protocol name.
377
+
378
+ Returns:
379
+ Async generator function that calls the module with protocol injected.
380
+ """
381
+ protocol = tool_def.name
382
+
383
+ async def tool_function(**kwargs: Any) -> AsyncGenerator[dict, None]: # noqa: ANN401
384
+ kwargs["protocol"] = protocol
385
+ wrapped_input = {"root": kwargs}
386
+ async for response in communication.call_module(
387
+ module_address=tool_module_info.address,
388
+ module_port=tool_module_info.port,
389
+ input_data=wrapped_input,
390
+ setup_id=tool_module_info.setup_id,
391
+ mission_id=session.mission_id,
392
+ ):
393
+ yield response
394
+
395
+ tool_function.__name__ = tool_def.name
396
+ tool_function.__doc__ = tool_def.description
397
+
398
+ return tool_function
399
+
400
+ async def cleanup(self) -> None:
401
+ """Clean up all service resources.
402
+
403
+ Currently cleans up communication service (gRPC channel pool).
404
+ """
405
+ if self.communication is not None:
406
+ await self.communication.cleanup()
@@ -1,109 +1,29 @@
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/output model exposing the protocol.
13
-
14
- The mandatory protocol is important to define the module beahvior following the user or agent input/output.
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 = Field(
28
- default_factory=lambda: datetime.now(tz=timezone.utc).isoformat(),
29
- title="Created At",
30
- description="Timestamp when the payload was created.",
31
- )
32
-
33
-
34
- DataTriggerT = TypeVar("DataTriggerT", bound=DataTrigger)
35
-
36
-
37
- class DataModel(BaseModel, Generic[DataTriggerT]):
38
- """Base definition of input/output model showing mandatory root fields.
39
-
40
- The Model define the Module Input/output, usually referring to multiple input/output type defined by an union.
41
-
42
- Example:
43
- class ModuleInput(DataModel):
44
- root: FileInput | MessageInput
45
- """
46
-
47
- root: DataTriggerT
48
- annotations: dict[str, str] = Field(
49
- default={},
50
- title="Annotations",
51
- description="Additional metadata or annotations related to the output. ex {'role': 'user'}",
52
- )
53
-
54
-
55
- InputModelT = TypeVar("InputModelT", bound=DataModel)
56
- OutputModelT = TypeVar("OutputModelT", bound=DataModel)
57
- SecretModelT = TypeVar("SecretModelT", bound=BaseModel)
58
- SetupModelT = TypeVar("SetupModelT", bound="SetupModel")
59
-
60
-
61
- class SetupModel(BaseModel):
62
- """Base definition of setup model showing mandatory root fields.
63
-
64
- Optionally, the setup model can define a config option in json_schema_extra to be used to initialize the Kin.
65
-
66
- Example:
67
- class MySetup(SetupModel):
68
- name: str = Field()
69
- number: int = Field(..., json_schema_extra={"config": True})
70
- """
71
-
72
- @classmethod
73
- def get_clean_model(cls, *, config_fields: bool, hidden_fields: bool) -> type[SetupModelT]: # type: ignore
74
- """Dynamically builds and returns a new BaseModel subclass.
75
-
76
- containing only those fields where json_schema_extra["config"] == True.
77
-
78
- Returns:
79
- Type[BaseModel]: A new BaseModel subclass with the filtered fields.
80
-
81
- Raises:
82
- ValueError: If both config_fields and hidden_fields are set to True.
83
- """
84
- clean_fields: dict[str, Any] = {}
85
- for name, field_info in cls.model_fields.items():
86
- extra = getattr(field_info, "json_schema_extra", {}) or {}
87
- is_config = bool(extra.get("config", False))
88
- is_hidden = bool(extra.get("hidden", False))
89
-
90
- # Skip config unless explicitly included
91
- if is_config and not config_fields:
92
- logger.debug("Skipping '%s' (config-only)", name)
93
- continue
94
-
95
- # Skip hidden unless explicitly included
96
- if is_hidden and not hidden_fields:
97
- logger.debug("Skipping '%s' (hidden-only)", name)
98
- continue
99
-
100
- clean_fields[name] = (field_info.annotation, field_info)
101
-
102
- # Dynamically create a model e.g. "SetupModel"
103
- m = create_model(
104
- f"{cls.__name__}",
105
- __base__=BaseModel,
106
- __config__=ConfigDict(arbitrary_types_allowed=True),
107
- **clean_fields,
108
- )
109
- return cast("type[SetupModelT]", m) # type: ignore
1
+ """Types for module models - backward compatibility re-exports.
2
+
3
+ This module re-exports types from their new locations for backward compatibility.
4
+ New code should import directly from the specific modules:
5
+ - digitalkin.models.module.base_types for DataTrigger, DataModel, TypeVars
6
+ - digitalkin.models.module.setup_types for SetupModel
7
+ """
8
+
9
+ from digitalkin.models.module.base_types import (
10
+ DataModel,
11
+ DataTrigger,
12
+ DataTriggerT,
13
+ InputModelT,
14
+ OutputModelT,
15
+ SecretModelT,
16
+ SetupModelT,
17
+ )
18
+ from digitalkin.models.module.setup_types import SetupModel
19
+
20
+ __all__ = [
21
+ "DataModel",
22
+ "DataTrigger",
23
+ "DataTriggerT",
24
+ "InputModelT",
25
+ "OutputModelT",
26
+ "SecretModelT",
27
+ "SetupModel",
28
+ "SetupModelT",
29
+ ]