digitalkin 0.3.1.dev2__py3-none-any.whl → 0.3.2.dev14__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/server_async_insecure.py +6 -5
- base_server/server_async_secure.py +6 -5
- base_server/server_sync_insecure.py +5 -4
- base_server/server_sync_secure.py +5 -4
- digitalkin/__version__.py +1 -1
- digitalkin/core/job_manager/base_job_manager.py +1 -1
- digitalkin/core/job_manager/single_job_manager.py +28 -9
- digitalkin/core/job_manager/taskiq_broker.py +7 -6
- digitalkin/core/job_manager/taskiq_job_manager.py +1 -1
- digitalkin/core/task_manager/surrealdb_repository.py +7 -7
- digitalkin/core/task_manager/task_session.py +60 -98
- digitalkin/grpc_servers/module_server.py +109 -168
- digitalkin/grpc_servers/module_servicer.py +38 -16
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +24 -8
- digitalkin/grpc_servers/utils/utility_schema_extender.py +100 -0
- digitalkin/models/__init__.py +1 -1
- digitalkin/models/core/job_manager_models.py +0 -8
- digitalkin/models/core/task_monitor.py +4 -0
- digitalkin/models/grpc_servers/models.py +91 -6
- digitalkin/models/module/__init__.py +18 -13
- digitalkin/models/module/base_types.py +61 -0
- digitalkin/models/module/module_context.py +173 -13
- digitalkin/models/module/module_types.py +28 -392
- digitalkin/models/module/setup_types.py +490 -0
- digitalkin/models/module/tool_cache.py +68 -0
- digitalkin/models/module/tool_reference.py +117 -0
- digitalkin/models/module/utility.py +167 -0
- digitalkin/models/services/registry.py +35 -0
- digitalkin/modules/__init__.py +5 -1
- digitalkin/modules/_base_module.py +154 -61
- digitalkin/modules/archetype_module.py +6 -1
- digitalkin/modules/tool_module.py +6 -1
- digitalkin/modules/triggers/__init__.py +8 -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/services/__init__.py +4 -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 +234 -0
- digitalkin/services/cost/grpc_cost.py +1 -1
- digitalkin/services/filesystem/grpc_filesystem.py +1 -1
- digitalkin/services/registry/__init__.py +22 -1
- digitalkin/services/registry/default_registry.py +135 -4
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +15 -0
- digitalkin/services/registry/registry_strategy.py +88 -4
- digitalkin/services/services_config.py +25 -3
- digitalkin/services/services_models.py +5 -1
- digitalkin/services/setup/default_setup.py +1 -1
- digitalkin/services/setup/grpc_setup.py +1 -1
- digitalkin/services/storage/grpc_storage.py +1 -1
- digitalkin/services/user_profile/__init__.py +11 -0
- digitalkin/services/user_profile/grpc_user_profile.py +2 -2
- digitalkin/services/user_profile/user_profile_strategy.py +0 -15
- digitalkin/utils/schema_splitter.py +207 -0
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2.dev14.dist-info}/METADATA +5 -5
- digitalkin-0.3.2.dev14.dist-info/RECORD +143 -0
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2.dev14.dist-info}/top_level.txt +1 -0
- modules/archetype_with_tools_module.py +244 -0
- modules/cpu_intensive_module.py +1 -1
- modules/dynamic_setup_module.py +5 -29
- modules/minimal_llm_module.py +1 -1
- modules/text_transform_module.py +1 -1
- monitoring/digitalkin_observability/__init__.py +46 -0
- monitoring/digitalkin_observability/http_server.py +150 -0
- monitoring/digitalkin_observability/interceptors.py +176 -0
- monitoring/digitalkin_observability/metrics.py +201 -0
- monitoring/digitalkin_observability/prometheus.py +137 -0
- monitoring/tests/test_metrics.py +172 -0
- services/filesystem_module.py +7 -5
- services/storage_module.py +4 -2
- digitalkin/grpc_servers/registry_server.py +0 -65
- digitalkin/grpc_servers/registry_servicer.py +0 -456
- digitalkin-0.3.1.dev2.dist-info/RECORD +0 -119
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2.dev14.dist-info}/WHEEL +0 -0
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2.dev14.dist-info}/licenses/LICENSE +0 -0
|
@@ -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/services/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""This package contains the abstract base class for all services."""
|
|
2
2
|
|
|
3
3
|
from digitalkin.services.agent import AgentStrategy, DefaultAgent
|
|
4
|
+
from digitalkin.services.communication import CommunicationStrategy, DefaultCommunication, GrpcCommunication
|
|
4
5
|
from digitalkin.services.cost import CostStrategy, DefaultCost
|
|
5
6
|
from digitalkin.services.filesystem import DefaultFilesystem, FilesystemStrategy
|
|
6
7
|
from digitalkin.services.identity import DefaultIdentity, IdentityStrategy
|
|
@@ -10,8 +11,10 @@ from digitalkin.services.storage import DefaultStorage, StorageStrategy
|
|
|
10
11
|
|
|
11
12
|
__all__ = [
|
|
12
13
|
"AgentStrategy",
|
|
14
|
+
"CommunicationStrategy",
|
|
13
15
|
"CostStrategy",
|
|
14
16
|
"DefaultAgent",
|
|
17
|
+
"DefaultCommunication",
|
|
15
18
|
"DefaultCost",
|
|
16
19
|
"DefaultFilesystem",
|
|
17
20
|
"DefaultIdentity",
|
|
@@ -19,6 +22,7 @@ __all__ = [
|
|
|
19
22
|
"DefaultSnapshot",
|
|
20
23
|
"DefaultStorage",
|
|
21
24
|
"FilesystemStrategy",
|
|
25
|
+
"GrpcCommunication",
|
|
22
26
|
"IdentityStrategy",
|
|
23
27
|
"RegistryStrategy",
|
|
24
28
|
"SnapshotStrategy",
|
|
@@ -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
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""gRPC client implementation for Communication service."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from collections.abc import AsyncGenerator, Awaitable, Callable
|
|
5
|
+
|
|
6
|
+
from agentic_mesh_protocol.module.v1 import (
|
|
7
|
+
information_pb2,
|
|
8
|
+
lifecycle_pb2,
|
|
9
|
+
module_service_pb2_grpc,
|
|
10
|
+
)
|
|
11
|
+
from google.protobuf import json_format, struct_pb2
|
|
12
|
+
|
|
13
|
+
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
14
|
+
from digitalkin.logger import logger
|
|
15
|
+
from digitalkin.models.grpc_servers.models import ClientConfig
|
|
16
|
+
from digitalkin.services.base_strategy import BaseStrategy
|
|
17
|
+
from digitalkin.services.communication.communication_strategy import CommunicationStrategy
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GrpcCommunication(CommunicationStrategy, GrpcClientWrapper):
|
|
21
|
+
"""gRPC client for module-to-module communication.
|
|
22
|
+
|
|
23
|
+
This class provides methods to communicate with remote modules
|
|
24
|
+
using the Module Service gRPC protocol.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
mission_id: str,
|
|
30
|
+
setup_id: str,
|
|
31
|
+
setup_version_id: str,
|
|
32
|
+
client_config: ClientConfig,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Initialize the gRPC communication client.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
mission_id: Mission identifier
|
|
38
|
+
setup_id: Setup identifier
|
|
39
|
+
setup_version_id: Setup version identifier
|
|
40
|
+
client_config: Client configuration for gRPC connection
|
|
41
|
+
"""
|
|
42
|
+
BaseStrategy.__init__(self, mission_id, setup_id, setup_version_id)
|
|
43
|
+
self.client_config = client_config
|
|
44
|
+
|
|
45
|
+
logger.debug(
|
|
46
|
+
"Initialized GrpcCommunication",
|
|
47
|
+
extra={"security": client_config.security},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _create_stub(self, module_address: str, module_port: int) -> module_service_pb2_grpc.ModuleServiceStub:
|
|
51
|
+
"""Create a new stub for the target module.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
module_address: Module host address
|
|
55
|
+
module_port: Module port
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
ModuleServiceStub for the target module
|
|
59
|
+
"""
|
|
60
|
+
logger.debug(
|
|
61
|
+
"Creating connection",
|
|
62
|
+
extra={"address": module_address, "port": module_port},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
config = ClientConfig(
|
|
66
|
+
host=module_address,
|
|
67
|
+
port=module_port,
|
|
68
|
+
mode=self.client_config.mode,
|
|
69
|
+
security=self.client_config.security,
|
|
70
|
+
credentials=self.client_config.credentials,
|
|
71
|
+
channel_options=self.client_config.channel_options,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
channel = self._init_channel(config)
|
|
75
|
+
return module_service_pb2_grpc.ModuleServiceStub(channel)
|
|
76
|
+
|
|
77
|
+
async def get_module_schemas(
|
|
78
|
+
self,
|
|
79
|
+
module_address: str,
|
|
80
|
+
module_port: int,
|
|
81
|
+
*,
|
|
82
|
+
llm_format: bool = False,
|
|
83
|
+
) -> dict[str, dict]:
|
|
84
|
+
"""Get module schemas via gRPC.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
module_address: Target module address
|
|
88
|
+
module_port: Target module port
|
|
89
|
+
llm_format: Return LLM-friendly format
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Dictionary containing schemas
|
|
93
|
+
"""
|
|
94
|
+
stub = self._create_stub(module_address, module_port)
|
|
95
|
+
|
|
96
|
+
# Create requests
|
|
97
|
+
input_request = information_pb2.GetModuleInputRequest(llm_format=llm_format)
|
|
98
|
+
output_request = information_pb2.GetModuleOutputRequest(llm_format=llm_format)
|
|
99
|
+
setup_request = information_pb2.GetModuleSetupRequest(llm_format=llm_format)
|
|
100
|
+
secret_request = information_pb2.GetModuleSecretRequest(llm_format=llm_format)
|
|
101
|
+
|
|
102
|
+
# Get all schemas in parallel
|
|
103
|
+
try:
|
|
104
|
+
input_response, output_response, setup_response, secret_response = await asyncio.gather(
|
|
105
|
+
asyncio.to_thread(stub.GetModuleInput, input_request),
|
|
106
|
+
asyncio.to_thread(stub.GetModuleOutput, output_request),
|
|
107
|
+
asyncio.to_thread(stub.GetModuleSetup, setup_request),
|
|
108
|
+
asyncio.to_thread(stub.GetModuleSecret, secret_request),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
logger.debug(
|
|
112
|
+
"Retrieved module schemas",
|
|
113
|
+
extra={
|
|
114
|
+
"module_address": module_address,
|
|
115
|
+
"module_port": module_port,
|
|
116
|
+
"llm_format": llm_format,
|
|
117
|
+
},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
"input": json_format.MessageToDict(input_response.input_schema),
|
|
122
|
+
"output": json_format.MessageToDict(output_response.output_schema),
|
|
123
|
+
"setup": json_format.MessageToDict(setup_response.setup_schema),
|
|
124
|
+
"secret": json_format.MessageToDict(secret_response.secret_schema),
|
|
125
|
+
}
|
|
126
|
+
except Exception:
|
|
127
|
+
logger.exception(
|
|
128
|
+
"Failed to get module schemas",
|
|
129
|
+
extra={
|
|
130
|
+
"module_address": module_address,
|
|
131
|
+
"module_port": module_port,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
raise
|
|
135
|
+
|
|
136
|
+
async def call_module(
|
|
137
|
+
self,
|
|
138
|
+
module_address: str,
|
|
139
|
+
module_port: int,
|
|
140
|
+
input_data: dict,
|
|
141
|
+
setup_id: str,
|
|
142
|
+
mission_id: str,
|
|
143
|
+
callback: Callable[[dict], Awaitable[None]] | None = None,
|
|
144
|
+
) -> AsyncGenerator[dict, None]:
|
|
145
|
+
"""Call a module and stream responses via gRPC.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
module_address: Target module address
|
|
149
|
+
module_port: Target module port
|
|
150
|
+
input_data: Input data as dictionary
|
|
151
|
+
setup_id: Setup configuration ID
|
|
152
|
+
mission_id: Mission context ID
|
|
153
|
+
callback: Optional callback for each response
|
|
154
|
+
|
|
155
|
+
Yields:
|
|
156
|
+
Streaming responses from module as dictionaries
|
|
157
|
+
"""
|
|
158
|
+
stub = self._create_stub(module_address, module_port)
|
|
159
|
+
|
|
160
|
+
# Convert input data to protobuf Struct
|
|
161
|
+
input_struct = struct_pb2.Struct()
|
|
162
|
+
input_struct.update(input_data)
|
|
163
|
+
|
|
164
|
+
# Create request
|
|
165
|
+
request = lifecycle_pb2.StartModuleRequest(
|
|
166
|
+
input=input_struct,
|
|
167
|
+
setup_id=setup_id,
|
|
168
|
+
mission_id=mission_id,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
logger.debug(
|
|
172
|
+
"Calling module",
|
|
173
|
+
extra={
|
|
174
|
+
"module_address": module_address,
|
|
175
|
+
"module_port": module_port,
|
|
176
|
+
"setup_id": setup_id,
|
|
177
|
+
"mission_id": mission_id,
|
|
178
|
+
},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
# Call StartModule with streaming response
|
|
183
|
+
response_stream = stub.StartModule(request)
|
|
184
|
+
|
|
185
|
+
# Stream responses
|
|
186
|
+
for response in response_stream:
|
|
187
|
+
# Convert protobuf Struct to dict
|
|
188
|
+
output_dict = json_format.MessageToDict(response.output)
|
|
189
|
+
|
|
190
|
+
# Check for end_of_stream signal
|
|
191
|
+
if output_dict.get("root", {}).get("protocol") == "end_of_stream":
|
|
192
|
+
logger.debug(
|
|
193
|
+
"End of stream received",
|
|
194
|
+
extra={
|
|
195
|
+
"module_address": module_address,
|
|
196
|
+
"module_port": module_port,
|
|
197
|
+
},
|
|
198
|
+
)
|
|
199
|
+
break
|
|
200
|
+
|
|
201
|
+
# Add job_id and success flag
|
|
202
|
+
response_dict = {
|
|
203
|
+
"success": response.success,
|
|
204
|
+
"job_id": response.job_id,
|
|
205
|
+
"output": output_dict,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
logger.debug(
|
|
209
|
+
"Received module response",
|
|
210
|
+
extra={
|
|
211
|
+
"module_address": module_address,
|
|
212
|
+
"module_port": module_port,
|
|
213
|
+
"success": response.success,
|
|
214
|
+
"job_id": response.job_id,
|
|
215
|
+
},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Call callback if provided
|
|
219
|
+
if callback:
|
|
220
|
+
await callback(response_dict)
|
|
221
|
+
|
|
222
|
+
yield response_dict
|
|
223
|
+
|
|
224
|
+
except Exception:
|
|
225
|
+
logger.exception(
|
|
226
|
+
"Failed to call module",
|
|
227
|
+
extra={
|
|
228
|
+
"module_address": module_address,
|
|
229
|
+
"module_port": module_port,
|
|
230
|
+
"setup_id": setup_id,
|
|
231
|
+
"mission_id": mission_id,
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
raise
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from agentic_mesh_protocol.cost.v1 import cost_pb2, cost_service_pb2_grpc
|
|
6
6
|
from google.protobuf import json_format
|
|
7
7
|
|
|
8
8
|
from digitalkin.grpc_servers.utils.grpc_client_wrapper import GrpcClientWrapper
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Literal
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from agentic_mesh_protocol.filesystem.v1 import filesystem_pb2, filesystem_service_pb2_grpc
|
|
6
6
|
from google.protobuf import struct_pb2
|
|
7
7
|
from google.protobuf.json_format import MessageToDict
|
|
8
8
|
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
"""This module is responsible for handling the registry service."""
|
|
2
2
|
|
|
3
|
+
from digitalkin.models.services.registry import (
|
|
4
|
+
ModuleInfo,
|
|
5
|
+
RegistryModuleStatus,
|
|
6
|
+
RegistryModuleType,
|
|
7
|
+
)
|
|
3
8
|
from digitalkin.services.registry.default_registry import DefaultRegistry
|
|
9
|
+
from digitalkin.services.registry.exceptions import (
|
|
10
|
+
RegistryModuleNotFoundError,
|
|
11
|
+
RegistryServiceError,
|
|
12
|
+
)
|
|
13
|
+
from digitalkin.services.registry.grpc_registry import GrpcRegistry
|
|
14
|
+
from digitalkin.services.registry.registry_models import ModuleStatusInfo
|
|
4
15
|
from digitalkin.services.registry.registry_strategy import RegistryStrategy
|
|
5
16
|
|
|
6
|
-
__all__ = [
|
|
17
|
+
__all__ = [
|
|
18
|
+
"DefaultRegistry",
|
|
19
|
+
"GrpcRegistry",
|
|
20
|
+
"ModuleInfo",
|
|
21
|
+
"ModuleStatusInfo",
|
|
22
|
+
"RegistryModuleNotFoundError",
|
|
23
|
+
"RegistryModuleStatus",
|
|
24
|
+
"RegistryModuleType",
|
|
25
|
+
"RegistryServiceError",
|
|
26
|
+
"RegistryStrategy",
|
|
27
|
+
]
|