digitalkin 0.3.1.dev2__py3-none-any.whl → 0.3.2a3__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 +78 -36
- digitalkin/core/job_manager/taskiq_broker.py +7 -6
- digitalkin/core/job_manager/taskiq_job_manager.py +9 -5
- digitalkin/core/task_manager/base_task_manager.py +3 -1
- digitalkin/core/task_manager/surrealdb_repository.py +29 -7
- digitalkin/core/task_manager/task_executor.py +46 -12
- digitalkin/core/task_manager/task_session.py +132 -102
- digitalkin/grpc_servers/module_server.py +95 -171
- digitalkin/grpc_servers/module_servicer.py +121 -19
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +36 -10
- digitalkin/grpc_servers/utils/utility_schema_extender.py +106 -0
- digitalkin/models/__init__.py +1 -1
- digitalkin/models/core/job_manager_models.py +0 -8
- digitalkin/models/core/task_monitor.py +23 -1
- digitalkin/models/grpc_servers/models.py +95 -8
- digitalkin/models/module/__init__.py +26 -13
- digitalkin/models/module/base_types.py +61 -0
- digitalkin/models/module/module_context.py +279 -13
- digitalkin/models/module/module_types.py +28 -392
- digitalkin/models/module/setup_types.py +547 -0
- digitalkin/models/module/tool_cache.py +230 -0
- digitalkin/models/module/tool_reference.py +160 -0
- digitalkin/models/module/utility.py +167 -0
- digitalkin/models/services/cost.py +22 -1
- digitalkin/models/services/registry.py +77 -0
- digitalkin/modules/__init__.py +5 -1
- digitalkin/modules/_base_module.py +188 -63
- 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 +87 -0
- digitalkin/services/communication/default_communication.py +104 -0
- digitalkin/services/communication/grpc_communication.py +264 -0
- digitalkin/services/cost/cost_strategy.py +36 -14
- digitalkin/services/cost/default_cost.py +61 -1
- digitalkin/services/cost/grpc_cost.py +98 -2
- digitalkin/services/filesystem/grpc_filesystem.py +9 -2
- digitalkin/services/registry/__init__.py +22 -1
- digitalkin/services/registry/default_registry.py +156 -4
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +382 -0
- digitalkin/services/registry/registry_models.py +15 -0
- digitalkin/services/registry/registry_strategy.py +106 -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/__init__.py +15 -3
- digitalkin/utils/conditional_schema.py +260 -0
- digitalkin/utils/dynamic_schema.py +4 -0
- digitalkin/utils/schema_splitter.py +290 -0
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2a3.dist-info}/METADATA +12 -12
- digitalkin-0.3.2a3.dist-info/RECORD +144 -0
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2a3.dist-info}/WHEEL +1 -1
- {digitalkin-0.3.1.dev2.dist-info → digitalkin-0.3.2a3.dist-info}/top_level.txt +1 -0
- modules/archetype_with_tools_module.py +232 -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.2a3.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
|
|
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
|
|
50
|
+
msg = "setup_version_id is mandatory"
|
|
48
51
|
raise ValueError(msg)
|
|
49
52
|
if not mission_id:
|
|
50
|
-
msg = "mission_id is mandatory
|
|
53
|
+
msg = "mission_id is mandatory"
|
|
51
54
|
raise ValueError(msg)
|
|
52
55
|
if not job_id:
|
|
53
|
-
msg = "job_id is mandatory
|
|
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()
|