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.
- base_server/__init__.py +1 -0
- base_server/mock/__init__.py +5 -0
- base_server/mock/mock_pb2.py +39 -0
- base_server/mock/mock_pb2_grpc.py +102 -0
- base_server/server_async_insecure.py +125 -0
- base_server/server_async_secure.py +143 -0
- base_server/server_sync_insecure.py +103 -0
- base_server/server_sync_secure.py +122 -0
- digitalkin/__init__.py +8 -0
- digitalkin/__version__.py +8 -0
- digitalkin/core/__init__.py +1 -0
- digitalkin/core/common/__init__.py +9 -0
- digitalkin/core/common/factories.py +156 -0
- digitalkin/core/job_manager/__init__.py +1 -0
- digitalkin/core/job_manager/base_job_manager.py +288 -0
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/core/job_manager/taskiq_broker.py +311 -0
- digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
- digitalkin/core/task_manager/__init__.py +1 -0
- digitalkin/core/task_manager/base_task_manager.py +539 -0
- digitalkin/core/task_manager/local_task_manager.py +108 -0
- digitalkin/core/task_manager/remote_task_manager.py +87 -0
- digitalkin/core/task_manager/surrealdb_repository.py +266 -0
- digitalkin/core/task_manager/task_executor.py +249 -0
- digitalkin/core/task_manager/task_session.py +406 -0
- digitalkin/grpc_servers/__init__.py +1 -0
- digitalkin/grpc_servers/_base_server.py +486 -0
- digitalkin/grpc_servers/module_server.py +208 -0
- digitalkin/grpc_servers/module_servicer.py +516 -0
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +29 -0
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
- digitalkin/logger.py +157 -0
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/__init__.py +8 -0
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/models/core/job_manager_models.py +36 -0
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/models/grpc_servers/models.py +275 -0
- digitalkin/models/grpc_servers/types.py +24 -0
- digitalkin/models/module/__init__.py +25 -0
- digitalkin/models/module/module.py +40 -0
- digitalkin/models/module/module_context.py +149 -0
- digitalkin/models/module/module_types.py +393 -0
- digitalkin/models/module/utility.py +146 -0
- digitalkin/models/services/__init__.py +10 -0
- digitalkin/models/services/cost.py +54 -0
- digitalkin/models/services/registry.py +42 -0
- digitalkin/models/services/storage.py +44 -0
- digitalkin/modules/__init__.py +11 -0
- digitalkin/modules/_base_module.py +517 -0
- digitalkin/modules/archetype_module.py +23 -0
- digitalkin/modules/tool_module.py +23 -0
- digitalkin/modules/trigger_handler.py +48 -0
- digitalkin/modules/triggers/__init__.py +12 -0
- digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
- digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
- digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
- digitalkin/py.typed +0 -0
- digitalkin/services/__init__.py +30 -0
- digitalkin/services/agent/__init__.py +6 -0
- digitalkin/services/agent/agent_strategy.py +19 -0
- digitalkin/services/agent/default_agent.py +13 -0
- digitalkin/services/base_strategy.py +22 -0
- digitalkin/services/communication/__init__.py +7 -0
- digitalkin/services/communication/communication_strategy.py +76 -0
- digitalkin/services/communication/default_communication.py +101 -0
- digitalkin/services/communication/grpc_communication.py +223 -0
- digitalkin/services/cost/__init__.py +14 -0
- digitalkin/services/cost/cost_strategy.py +100 -0
- digitalkin/services/cost/default_cost.py +114 -0
- digitalkin/services/cost/grpc_cost.py +138 -0
- digitalkin/services/filesystem/__init__.py +7 -0
- digitalkin/services/filesystem/default_filesystem.py +417 -0
- digitalkin/services/filesystem/filesystem_strategy.py +252 -0
- digitalkin/services/filesystem/grpc_filesystem.py +317 -0
- digitalkin/services/identity/__init__.py +6 -0
- digitalkin/services/identity/default_identity.py +15 -0
- digitalkin/services/identity/identity_strategy.py +14 -0
- digitalkin/services/registry/__init__.py +27 -0
- digitalkin/services/registry/default_registry.py +141 -0
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +43 -0
- digitalkin/services/registry/registry_strategy.py +98 -0
- digitalkin/services/services_config.py +200 -0
- digitalkin/services/services_models.py +65 -0
- digitalkin/services/setup/__init__.py +1 -0
- digitalkin/services/setup/default_setup.py +219 -0
- digitalkin/services/setup/grpc_setup.py +343 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +6 -0
- digitalkin/services/snapshot/default_snapshot.py +39 -0
- digitalkin/services/snapshot/snapshot_strategy.py +30 -0
- digitalkin/services/storage/__init__.py +7 -0
- digitalkin/services/storage/default_storage.py +228 -0
- digitalkin/services/storage/grpc_storage.py +214 -0
- digitalkin/services/storage/storage_strategy.py +273 -0
- digitalkin/services/user_profile/__init__.py +12 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +40 -0
- digitalkin/utils/__init__.py +29 -0
- digitalkin/utils/arg_parser.py +92 -0
- digitalkin/utils/development_mode_action.py +51 -0
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/llm_ready_schema.py +75 -0
- digitalkin/utils/package_discover.py +357 -0
- digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
- digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
- digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
- digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
- digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
- modules/__init__.py +0 -0
- modules/cpu_intensive_module.py +280 -0
- modules/dynamic_setup_module.py +338 -0
- modules/minimal_llm_module.py +347 -0
- modules/text_transform_module.py +203 -0
- services/filesystem_module.py +200 -0
- 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,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
|