digitalkin 0.3.2.dev2__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 (131) hide show
  1. base_server/__init__.py +1 -0
  2. base_server/mock/__init__.py +5 -0
  3. base_server/mock/mock_pb2.py +39 -0
  4. base_server/mock/mock_pb2_grpc.py +102 -0
  5. base_server/server_async_insecure.py +125 -0
  6. base_server/server_async_secure.py +143 -0
  7. base_server/server_sync_insecure.py +103 -0
  8. base_server/server_sync_secure.py +122 -0
  9. digitalkin/__init__.py +8 -0
  10. digitalkin/__version__.py +8 -0
  11. digitalkin/core/__init__.py +1 -0
  12. digitalkin/core/common/__init__.py +9 -0
  13. digitalkin/core/common/factories.py +156 -0
  14. digitalkin/core/job_manager/__init__.py +1 -0
  15. digitalkin/core/job_manager/base_job_manager.py +288 -0
  16. digitalkin/core/job_manager/single_job_manager.py +354 -0
  17. digitalkin/core/job_manager/taskiq_broker.py +311 -0
  18. digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
  19. digitalkin/core/task_manager/__init__.py +1 -0
  20. digitalkin/core/task_manager/base_task_manager.py +539 -0
  21. digitalkin/core/task_manager/local_task_manager.py +108 -0
  22. digitalkin/core/task_manager/remote_task_manager.py +87 -0
  23. digitalkin/core/task_manager/surrealdb_repository.py +266 -0
  24. digitalkin/core/task_manager/task_executor.py +249 -0
  25. digitalkin/core/task_manager/task_session.py +406 -0
  26. digitalkin/grpc_servers/__init__.py +1 -0
  27. digitalkin/grpc_servers/_base_server.py +486 -0
  28. digitalkin/grpc_servers/module_server.py +208 -0
  29. digitalkin/grpc_servers/module_servicer.py +516 -0
  30. digitalkin/grpc_servers/utils/__init__.py +1 -0
  31. digitalkin/grpc_servers/utils/exceptions.py +29 -0
  32. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
  33. digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
  34. digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
  35. digitalkin/logger.py +157 -0
  36. digitalkin/mixins/__init__.py +19 -0
  37. digitalkin/mixins/base_mixin.py +10 -0
  38. digitalkin/mixins/callback_mixin.py +24 -0
  39. digitalkin/mixins/chat_history_mixin.py +110 -0
  40. digitalkin/mixins/cost_mixin.py +76 -0
  41. digitalkin/mixins/file_history_mixin.py +93 -0
  42. digitalkin/mixins/filesystem_mixin.py +46 -0
  43. digitalkin/mixins/logger_mixin.py +51 -0
  44. digitalkin/mixins/storage_mixin.py +79 -0
  45. digitalkin/models/__init__.py +8 -0
  46. digitalkin/models/core/__init__.py +1 -0
  47. digitalkin/models/core/job_manager_models.py +36 -0
  48. digitalkin/models/core/task_monitor.py +70 -0
  49. digitalkin/models/grpc_servers/__init__.py +1 -0
  50. digitalkin/models/grpc_servers/models.py +275 -0
  51. digitalkin/models/grpc_servers/types.py +24 -0
  52. digitalkin/models/module/__init__.py +25 -0
  53. digitalkin/models/module/module.py +40 -0
  54. digitalkin/models/module/module_context.py +149 -0
  55. digitalkin/models/module/module_types.py +393 -0
  56. digitalkin/models/module/utility.py +146 -0
  57. digitalkin/models/services/__init__.py +10 -0
  58. digitalkin/models/services/cost.py +54 -0
  59. digitalkin/models/services/registry.py +42 -0
  60. digitalkin/models/services/storage.py +44 -0
  61. digitalkin/modules/__init__.py +11 -0
  62. digitalkin/modules/_base_module.py +517 -0
  63. digitalkin/modules/archetype_module.py +23 -0
  64. digitalkin/modules/tool_module.py +23 -0
  65. digitalkin/modules/trigger_handler.py +48 -0
  66. digitalkin/modules/triggers/__init__.py +12 -0
  67. digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  68. digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  69. digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  70. digitalkin/py.typed +0 -0
  71. digitalkin/services/__init__.py +30 -0
  72. digitalkin/services/agent/__init__.py +6 -0
  73. digitalkin/services/agent/agent_strategy.py +19 -0
  74. digitalkin/services/agent/default_agent.py +13 -0
  75. digitalkin/services/base_strategy.py +22 -0
  76. digitalkin/services/communication/__init__.py +7 -0
  77. digitalkin/services/communication/communication_strategy.py +76 -0
  78. digitalkin/services/communication/default_communication.py +101 -0
  79. digitalkin/services/communication/grpc_communication.py +223 -0
  80. digitalkin/services/cost/__init__.py +14 -0
  81. digitalkin/services/cost/cost_strategy.py +100 -0
  82. digitalkin/services/cost/default_cost.py +114 -0
  83. digitalkin/services/cost/grpc_cost.py +138 -0
  84. digitalkin/services/filesystem/__init__.py +7 -0
  85. digitalkin/services/filesystem/default_filesystem.py +417 -0
  86. digitalkin/services/filesystem/filesystem_strategy.py +252 -0
  87. digitalkin/services/filesystem/grpc_filesystem.py +317 -0
  88. digitalkin/services/identity/__init__.py +6 -0
  89. digitalkin/services/identity/default_identity.py +15 -0
  90. digitalkin/services/identity/identity_strategy.py +14 -0
  91. digitalkin/services/registry/__init__.py +27 -0
  92. digitalkin/services/registry/default_registry.py +141 -0
  93. digitalkin/services/registry/exceptions.py +47 -0
  94. digitalkin/services/registry/grpc_registry.py +306 -0
  95. digitalkin/services/registry/registry_models.py +43 -0
  96. digitalkin/services/registry/registry_strategy.py +98 -0
  97. digitalkin/services/services_config.py +200 -0
  98. digitalkin/services/services_models.py +65 -0
  99. digitalkin/services/setup/__init__.py +1 -0
  100. digitalkin/services/setup/default_setup.py +219 -0
  101. digitalkin/services/setup/grpc_setup.py +343 -0
  102. digitalkin/services/setup/setup_strategy.py +145 -0
  103. digitalkin/services/snapshot/__init__.py +6 -0
  104. digitalkin/services/snapshot/default_snapshot.py +39 -0
  105. digitalkin/services/snapshot/snapshot_strategy.py +30 -0
  106. digitalkin/services/storage/__init__.py +7 -0
  107. digitalkin/services/storage/default_storage.py +228 -0
  108. digitalkin/services/storage/grpc_storage.py +214 -0
  109. digitalkin/services/storage/storage_strategy.py +273 -0
  110. digitalkin/services/user_profile/__init__.py +12 -0
  111. digitalkin/services/user_profile/default_user_profile.py +55 -0
  112. digitalkin/services/user_profile/grpc_user_profile.py +69 -0
  113. digitalkin/services/user_profile/user_profile_strategy.py +40 -0
  114. digitalkin/utils/__init__.py +29 -0
  115. digitalkin/utils/arg_parser.py +92 -0
  116. digitalkin/utils/development_mode_action.py +51 -0
  117. digitalkin/utils/dynamic_schema.py +483 -0
  118. digitalkin/utils/llm_ready_schema.py +75 -0
  119. digitalkin/utils/package_discover.py +357 -0
  120. digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
  121. digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
  122. digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
  123. digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
  124. digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
  125. modules/__init__.py +0 -0
  126. modules/cpu_intensive_module.py +280 -0
  127. modules/dynamic_setup_module.py +338 -0
  128. modules/minimal_llm_module.py +347 -0
  129. modules/text_transform_module.py +203 -0
  130. services/filesystem_module.py +200 -0
  131. services/storage_module.py +206 -0
@@ -0,0 +1,23 @@
1
+ """ToolModule extends BaseModule to implement specific module types."""
2
+
3
+ from abc import ABC
4
+
5
+ from digitalkin.models.module.module_types import (
6
+ InputModelT,
7
+ OutputModelT,
8
+ SecretModelT,
9
+ SetupModelT,
10
+ )
11
+ from digitalkin.modules._base_module import BaseModule # type: ignore
12
+
13
+
14
+ class ToolModule(
15
+ BaseModule[
16
+ InputModelT,
17
+ OutputModelT,
18
+ SetupModelT,
19
+ SecretModelT,
20
+ ],
21
+ ABC,
22
+ ):
23
+ """ToolModule extends BaseModule to implement specific module types."""
@@ -0,0 +1,48 @@
1
+ """Definition of the Trigger type."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import ClassVar, Generic
5
+
6
+ from digitalkin.mixins import BaseMixin
7
+ from digitalkin.models.module.module_context import ModuleContext
8
+ from digitalkin.models.module.module_types import InputModelT, OutputModelT, SetupModelT
9
+
10
+
11
+ class TriggerHandler(ABC, BaseMixin, Generic[InputModelT, SetupModelT, OutputModelT]):
12
+ """Base class for all input-trigger handlers.
13
+
14
+ Each handler declares:
15
+ - protocol_key: the Literal value this handler processes
16
+ - handle(): logic to process the validated payload
17
+ """
18
+
19
+ protocol: ClassVar[str]
20
+ input_format: type[InputModelT]
21
+ output_format: type[OutputModelT]
22
+
23
+ def __init__(self, context: ModuleContext) -> None:
24
+ """Initialize the TriggerHandler with the given context."""
25
+
26
+ @abstractmethod
27
+ async def handle(
28
+ self,
29
+ input_data: InputModelT,
30
+ setup_data: SetupModelT,
31
+ context: ModuleContext,
32
+ ) -> None:
33
+ """Asynchronously processes the input data specific to Handler and streams results via the provided callback.
34
+
35
+ Args:
36
+ input_data (InputModelT): The input data to be processed by the handler.
37
+ setup_data (SetupModelT): The setup or configuration data required for processing.
38
+ context (ModuleContext): The context object containing module-specific information and resources.
39
+
40
+ Returns:
41
+ Any: The result of the processing, if applicable.
42
+
43
+ Note:
44
+ self.send_message: : callback used to stream results.
45
+ (Callable[[OutputMdodelT], Coroutine[Any, Any, None]])
46
+
47
+ The callback must be awaited to ensure results are streamed correctly during processing.
48
+ """
@@ -0,0 +1,12 @@
1
+ """Built-in SDK triggers.
2
+
3
+ These triggers are automatically registered without requiring discovery.
4
+ They provide standard functionality available to all modules.
5
+
6
+ Note: These are internal triggers. External code should not import them directly.
7
+ Use UtilityRegistry.get_builtin_triggers() to access the trigger classes.
8
+ """
9
+
10
+ # No public exports - all triggers are internal
11
+ # Access via: UtilityRegistry.get_builtin_triggers()
12
+ __all__: list[str] = []
@@ -0,0 +1,45 @@
1
+ """Healthcheck ping trigger - simple alive check."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any, ClassVar
5
+
6
+ from digitalkin.mixins import BaseMixin
7
+ from digitalkin.models.module.module_context import ModuleContext
8
+ from digitalkin.models.module.utility import (
9
+ HealthcheckPingInput,
10
+ HealthcheckPingOutput,
11
+ )
12
+ from digitalkin.modules.trigger_handler import TriggerHandler
13
+
14
+
15
+ class HealthcheckPingTrigger(TriggerHandler, BaseMixin):
16
+ """Handler for simple ping healthcheck.
17
+
18
+ Responds immediately with "pong" status to verify the module is responsive.
19
+ """
20
+
21
+ protocol: ClassVar[str] = "healthcheck_ping"
22
+ input_format = HealthcheckPingInput
23
+ _request_time: datetime
24
+
25
+ def __init__(self, context: ModuleContext) -> None:
26
+ """Initialize the handler."""
27
+ self._request_time = datetime.now(tz=context.session.timezone)
28
+
29
+ async def handle(
30
+ self,
31
+ input_data: HealthcheckPingInput, # noqa: ARG002
32
+ setup_data: Any, # noqa: ANN401, ARG002
33
+ context: ModuleContext,
34
+ ) -> None:
35
+ """Handle ping healthcheck request.
36
+
37
+ Args:
38
+ input_data: The input trigger data (unused for healthcheck).
39
+ setup_data: The setup configuration (unused for healthcheck).
40
+ context: The module context.
41
+ """
42
+ elapsed = datetime.now(tz=context.session.timezone) - self._request_time
43
+ latency_ms = elapsed.total_seconds() * 1000
44
+ output = HealthcheckPingOutput(latency_ms=latency_ms)
45
+ await self.send_message(context, output)
@@ -0,0 +1,63 @@
1
+ """Healthcheck services trigger - reports service health."""
2
+
3
+ from typing import Any, ClassVar
4
+
5
+ from digitalkin.mixins import BaseMixin
6
+ from digitalkin.models.module.module_context import ModuleContext
7
+ from digitalkin.models.module.utility import (
8
+ HealthcheckServicesInput,
9
+ HealthcheckServicesOutput,
10
+ ServiceHealthStatus,
11
+ )
12
+ from digitalkin.modules.trigger_handler import TriggerHandler
13
+
14
+
15
+ class HealthcheckServicesTrigger(TriggerHandler, BaseMixin):
16
+ """Handler for services healthcheck.
17
+
18
+ Reports the health status of all configured services (storage, cost, filesystem, etc.).
19
+ """
20
+
21
+ protocol: ClassVar[str] = "healthcheck_services"
22
+ input_format = HealthcheckServicesInput
23
+
24
+ def __init__(self, context: ModuleContext) -> None:
25
+ """Initialize the handler."""
26
+
27
+ async def handle(
28
+ self,
29
+ input_data: HealthcheckServicesInput, # noqa: ARG002
30
+ setup_data: Any, # noqa: ANN401, ARG002
31
+ context: ModuleContext,
32
+ ) -> None:
33
+ """Handle services healthcheck request.
34
+
35
+ Args:
36
+ input_data: The input trigger data (unused for healthcheck).
37
+ setup_data: The setup configuration (unused for healthcheck).
38
+ context: The module context.
39
+ """
40
+ service_names = ["storage", "cost", "filesystem", "registry", "user_profile"]
41
+ services_status: list[ServiceHealthStatus] = []
42
+
43
+ for name in service_names:
44
+ service = getattr(context, name, None)
45
+ if service is not None:
46
+ services_status.append(ServiceHealthStatus(name=name, status="healthy"))
47
+ else:
48
+ services_status.append(ServiceHealthStatus(name=name, status="unknown", message="Not configured"))
49
+
50
+ # Determine overall status
51
+ healthy_count = sum(1 for s in services_status if s.status == "healthy")
52
+ if healthy_count == len(services_status):
53
+ overall_status = "healthy"
54
+ elif healthy_count > 0:
55
+ overall_status = "degraded"
56
+ else:
57
+ overall_status = "unhealthy"
58
+
59
+ output = HealthcheckServicesOutput(
60
+ services=services_status,
61
+ overall_status=overall_status, # type: ignore[arg-type]
62
+ )
63
+ await self.send_message(context, output)
@@ -0,0 +1,52 @@
1
+ """Healthcheck status trigger - comprehensive module status."""
2
+
3
+ import time
4
+ from typing import Any, ClassVar
5
+
6
+ from digitalkin.mixins import BaseMixin
7
+ from digitalkin.models.module.module_context import ModuleContext
8
+ from digitalkin.models.module.utility import (
9
+ HealthcheckStatusInput,
10
+ HealthcheckStatusOutput,
11
+ )
12
+ from digitalkin.modules.trigger_handler import TriggerHandler
13
+
14
+
15
+ class HealthcheckStatusTrigger(TriggerHandler, BaseMixin):
16
+ """Handler for comprehensive status healthcheck.
17
+
18
+ Reports detailed module status including uptime, active jobs, and metadata.
19
+ """
20
+
21
+ protocol: ClassVar[str] = "healthcheck_status"
22
+ input_format = HealthcheckStatusInput
23
+ _start_time: ClassVar[float] = time.time()
24
+
25
+ def __init__(self, context: ModuleContext) -> None:
26
+ """Initialize the handler."""
27
+
28
+ async def handle(
29
+ self,
30
+ input_data: HealthcheckStatusInput, # noqa: ARG002
31
+ setup_data: Any, # noqa: ANN401, ARG002
32
+ context: ModuleContext,
33
+ ) -> None:
34
+ """Handle status healthcheck request.
35
+
36
+ Args:
37
+ input_data: The input trigger data (unused for healthcheck).
38
+ setup_data: The setup configuration (unused for healthcheck).
39
+ context: The module context.
40
+ """
41
+ output = HealthcheckStatusOutput(
42
+ module_name=context.session.setup_id,
43
+ module_status="RUNNING",
44
+ uptime_seconds=time.time() - self._start_time,
45
+ active_jobs=1,
46
+ metadata={
47
+ "job_id": context.session.job_id,
48
+ "mission_id": context.session.mission_id,
49
+ "setup_version_id": context.session.setup_version_id,
50
+ },
51
+ )
52
+ await self.send_message(context, output)
digitalkin/py.typed ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ """This package contains the abstract base class for all services."""
2
+
3
+ from digitalkin.services.agent import AgentStrategy, DefaultAgent
4
+ from digitalkin.services.communication import CommunicationStrategy, DefaultCommunication, GrpcCommunication
5
+ from digitalkin.services.cost import CostStrategy, DefaultCost
6
+ from digitalkin.services.filesystem import DefaultFilesystem, FilesystemStrategy
7
+ from digitalkin.services.identity import DefaultIdentity, IdentityStrategy
8
+ from digitalkin.services.registry import DefaultRegistry, RegistryStrategy
9
+ from digitalkin.services.snapshot import DefaultSnapshot, SnapshotStrategy
10
+ from digitalkin.services.storage import DefaultStorage, StorageStrategy
11
+
12
+ __all__ = [
13
+ "AgentStrategy",
14
+ "CommunicationStrategy",
15
+ "CostStrategy",
16
+ "DefaultAgent",
17
+ "DefaultCommunication",
18
+ "DefaultCost",
19
+ "DefaultFilesystem",
20
+ "DefaultIdentity",
21
+ "DefaultRegistry",
22
+ "DefaultSnapshot",
23
+ "DefaultStorage",
24
+ "FilesystemStrategy",
25
+ "GrpcCommunication",
26
+ "IdentityStrategy",
27
+ "RegistryStrategy",
28
+ "SnapshotStrategy",
29
+ "StorageStrategy",
30
+ ]
@@ -0,0 +1,6 @@
1
+ """This module is responsible for handling the agent services."""
2
+
3
+ from digitalkin.services.agent.agent_strategy import AgentStrategy
4
+ from digitalkin.services.agent.default_agent import DefaultAgent
5
+
6
+ __all__ = ["AgentStrategy", "DefaultAgent"]
@@ -0,0 +1,19 @@
1
+ """This module contains the abstract base class for agent strategies."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ from digitalkin.services.base_strategy import BaseStrategy
6
+
7
+
8
+ class AgentStrategy(BaseStrategy, ABC):
9
+ """Abstract base class for agent strategies."""
10
+
11
+ @abstractmethod
12
+ def start(self) -> None:
13
+ """Start the agent."""
14
+ raise NotImplementedError
15
+
16
+ @abstractmethod
17
+ def stop(self) -> None:
18
+ """Stop the agent."""
19
+ raise NotImplementedError
@@ -0,0 +1,13 @@
1
+ """Default agent implementation for the agent service."""
2
+
3
+ from digitalkin.services.agent.agent_strategy import AgentStrategy
4
+
5
+
6
+ class DefaultAgent(AgentStrategy):
7
+ """Default agent implementation for the agent service."""
8
+
9
+ def start(self) -> None:
10
+ """Start the agent."""
11
+
12
+ def stop(self) -> None:
13
+ """Stop the agent."""
@@ -0,0 +1,22 @@
1
+ """This module contains the abstract base class for storage strategies."""
2
+
3
+ from abc import ABC
4
+
5
+
6
+ class BaseStrategy(ABC):
7
+ """Abstract base class for all strategies.
8
+
9
+ This class defines the interface for all strategies.
10
+ """
11
+
12
+ def __init__(self, mission_id: str, setup_id: str, setup_version_id: str) -> None:
13
+ """Initialize the strategy.
14
+
15
+ Args:
16
+ mission_id: The ID of the mission this strategy is associated with
17
+ setup_id: The ID of the setup this strategy is associated with
18
+ setup_version_id: The ID of the setup version this strategy is associated with
19
+ """
20
+ self.mission_id: str = mission_id
21
+ self.setup_id: str = setup_id
22
+ self.setup_version_id: str = setup_version_id
@@ -0,0 +1,7 @@
1
+ """Communication service for module-to-module interaction."""
2
+
3
+ from digitalkin.services.communication.communication_strategy import CommunicationStrategy
4
+ from digitalkin.services.communication.default_communication import DefaultCommunication
5
+ from digitalkin.services.communication.grpc_communication import GrpcCommunication
6
+
7
+ __all__ = ["CommunicationStrategy", "DefaultCommunication", "GrpcCommunication"]
@@ -0,0 +1,76 @@
1
+ """Abstract base class for communication strategies."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from collections.abc import AsyncGenerator, Awaitable, Callable
5
+
6
+ from digitalkin.services.base_strategy import BaseStrategy
7
+
8
+
9
+ class CommunicationStrategy(BaseStrategy, ABC):
10
+ """Abstract base class for module-to-module communication.
11
+
12
+ This service enables:
13
+ - Archetype → Tool communication
14
+ - Archetype → Archetype communication
15
+ - Tool → Tool communication
16
+ - Any module → Any module communication
17
+
18
+ The service wraps the Module Service protocol from agentic-mesh-protocol.
19
+ """
20
+
21
+ @abstractmethod
22
+ async def get_module_schemas(
23
+ self,
24
+ module_address: str,
25
+ module_port: int,
26
+ *,
27
+ llm_format: bool = False,
28
+ ) -> dict[str, dict]:
29
+ """Get module schemas (input/output/setup/secret).
30
+
31
+ Args:
32
+ module_address: Target module address
33
+ module_port: Target module port
34
+ llm_format: Return LLM-friendly format (simplified schema)
35
+
36
+ Returns:
37
+ Dictionary containing schemas:
38
+ {
39
+ "input": {...},
40
+ "output": {...},
41
+ "setup": {...},
42
+ "secret": {...}
43
+ }
44
+ """
45
+ raise NotImplementedError
46
+
47
+ @abstractmethod
48
+ async def call_module(
49
+ self,
50
+ module_address: str,
51
+ module_port: int,
52
+ input_data: dict,
53
+ setup_id: str,
54
+ mission_id: str,
55
+ callback: Callable[[dict], Awaitable[None]] | None = None,
56
+ ) -> AsyncGenerator[dict, None]:
57
+ """Call a module and stream responses.
58
+
59
+ Uses Module Service StartModule RPC to execute the module.
60
+ Streams responses as they are generated by the module.
61
+
62
+ Args:
63
+ module_address: Target module address
64
+ module_port: Target module port
65
+ input_data: Input data as dictionary
66
+ setup_id: Setup configuration ID
67
+ mission_id: Mission context ID
68
+ callback: Optional callback for each response
69
+
70
+ Yields:
71
+ Streaming responses from module as dictionaries
72
+ """
73
+ # Make this an actual async generator to satisfy type checkers
74
+ if False: # pragma: no cover
75
+ yield {}
76
+ raise NotImplementedError
@@ -0,0 +1,101 @@
1
+ """Default communication implementation (local, for testing)."""
2
+
3
+ from collections.abc import AsyncGenerator, Awaitable, Callable
4
+
5
+ from digitalkin.logger import logger
6
+ from digitalkin.services.communication.communication_strategy import CommunicationStrategy
7
+
8
+
9
+ class DefaultCommunication(CommunicationStrategy):
10
+ """Default communication strategy (local implementation).
11
+
12
+ This implementation is primarily for testing and development.
13
+ For production, use GrpcCommunication to connect to remote modules.
14
+ """
15
+
16
+ def __init__(
17
+ self,
18
+ mission_id: str,
19
+ setup_id: str,
20
+ setup_version_id: str,
21
+ ) -> None:
22
+ """Initialize the default communication service.
23
+
24
+ Args:
25
+ mission_id: Mission identifier
26
+ setup_id: Setup identifier
27
+ setup_version_id: Setup version identifier
28
+ """
29
+ super().__init__(mission_id, setup_id, setup_version_id)
30
+ logger.debug("Initialized DefaultCommunication (local)")
31
+
32
+ async def get_module_schemas( # noqa: PLR6301
33
+ self,
34
+ module_address: str,
35
+ module_port: int,
36
+ *,
37
+ llm_format: bool = False,
38
+ ) -> dict[str, dict]:
39
+ """Get module schemas (local implementation returns empty schemas).
40
+
41
+ Args:
42
+ module_address: Target module address
43
+ module_port: Target module port
44
+ llm_format: Return LLM-friendly format
45
+
46
+ Returns:
47
+ Empty schemas dictionary
48
+ """
49
+ logger.debug(
50
+ "DefaultCommunication.get_module_schemas called (returns empty)",
51
+ extra={
52
+ "module_address": module_address,
53
+ "module_port": module_port,
54
+ "llm_format": llm_format,
55
+ },
56
+ )
57
+ return {
58
+ "input": {},
59
+ "output": {},
60
+ "setup": {},
61
+ "secret": {},
62
+ }
63
+
64
+ async def call_module( # noqa: PLR6301
65
+ self,
66
+ module_address: str,
67
+ module_port: int,
68
+ input_data: dict, # noqa: ARG002
69
+ setup_id: str,
70
+ mission_id: str,
71
+ callback: Callable[[dict], Awaitable[None]] | None = None,
72
+ ) -> AsyncGenerator[dict, None]:
73
+ """Call module (local implementation yields empty response).
74
+
75
+ Args:
76
+ module_address: Target module address
77
+ module_port: Target module port
78
+ input_data: Input data
79
+ setup_id: Setup ID
80
+ mission_id: Mission ID
81
+ callback: Optional callback
82
+
83
+ Yields:
84
+ Empty response dictionary
85
+ """
86
+ logger.debug(
87
+ "DefaultCommunication.call_module called (returns empty)",
88
+ extra={
89
+ "module_address": module_address,
90
+ "module_port": module_port,
91
+ "setup_id": setup_id,
92
+ "mission_id": mission_id,
93
+ },
94
+ )
95
+
96
+ # Yield empty response
97
+ response = {"status": "error", "message": "Local communication not implemented"}
98
+ if callback:
99
+ await callback(response)
100
+ yield response
101
+ return # Explicit return for async generator