digitalkin 0.3.2.dev21__py3-none-any.whl → 0.3.2.dev22__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.
- digitalkin/__version__.py +1 -1
- digitalkin/grpc_servers/module_servicer.py +2 -2
- digitalkin/models/module/__init__.py +2 -4
- digitalkin/models/module/module_context.py +9 -11
- digitalkin/models/module/setup_types.py +30 -29
- digitalkin/models/module/tool_cache.py +34 -36
- digitalkin/models/module/tool_reference.py +77 -110
- digitalkin/models/services/registry.py +6 -6
- digitalkin/services/registry/grpc_registry.py +1 -1
- {digitalkin-0.3.2.dev21.dist-info → digitalkin-0.3.2.dev22.dist-info}/METADATA +1 -1
- {digitalkin-0.3.2.dev21.dist-info → digitalkin-0.3.2.dev22.dist-info}/RECORD +15 -15
- modules/archetype_with_tools_module.py +4 -16
- {digitalkin-0.3.2.dev21.dist-info → digitalkin-0.3.2.dev22.dist-info}/WHEEL +0 -0
- {digitalkin-0.3.2.dev21.dist-info → digitalkin-0.3.2.dev22.dist-info}/licenses/LICENSE +0 -0
- {digitalkin-0.3.2.dev21.dist-info → digitalkin-0.3.2.dev22.dist-info}/top_level.txt +0 -0
digitalkin/__version__.py
CHANGED
|
@@ -165,7 +165,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
165
165
|
if isinstance(updated_setup_data, ModuleCodeModel):
|
|
166
166
|
logger.error(
|
|
167
167
|
"Config setup failed",
|
|
168
|
-
extra={"job_id": job_id, "code": updated_setup_data.code, "
|
|
168
|
+
extra={"job_id": job_id, "code": updated_setup_data.code, "error_message": updated_setup_data.message},
|
|
169
169
|
)
|
|
170
170
|
context.set_code(grpc.StatusCode.INTERNAL)
|
|
171
171
|
context.set_details(updated_setup_data.message or "Config setup failed")
|
|
@@ -178,7 +178,7 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
|
|
|
178
178
|
extra={
|
|
179
179
|
"job_id": job_id,
|
|
180
180
|
"code": updated_setup_data["code"],
|
|
181
|
-
"
|
|
181
|
+
"error_message": updated_setup_data.get("message"),
|
|
182
182
|
},
|
|
183
183
|
)
|
|
184
184
|
context.set_code(grpc.StatusCode.INTERNAL)
|
|
@@ -14,8 +14,7 @@ from digitalkin.models.module.tool_cache import (
|
|
|
14
14
|
)
|
|
15
15
|
from digitalkin.models.module.tool_reference import (
|
|
16
16
|
ToolReference,
|
|
17
|
-
|
|
18
|
-
ToolSelectionMode,
|
|
17
|
+
ToolReferenceInput,
|
|
19
18
|
)
|
|
20
19
|
from digitalkin.models.module.utility import (
|
|
21
20
|
EndOfStreamOutput,
|
|
@@ -36,8 +35,7 @@ __all__ = [
|
|
|
36
35
|
"ToolModuleInfo",
|
|
37
36
|
"ToolParameter",
|
|
38
37
|
"ToolReference",
|
|
39
|
-
"
|
|
40
|
-
"ToolSelectionMode",
|
|
38
|
+
"ToolReferenceInput",
|
|
41
39
|
"UtilityProtocol",
|
|
42
40
|
"UtilityRegistry",
|
|
43
41
|
]
|
|
@@ -227,21 +227,19 @@ class ModuleContext:
|
|
|
227
227
|
llm_format=llm_format,
|
|
228
228
|
)
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
def create_openai_style_tools(self, slug: str) -> list[dict[str, Any]]:
|
|
231
231
|
"""Create OpenAI-style function calling schemas for a tool module.
|
|
232
232
|
|
|
233
233
|
Uses tool cache (fast path) with registry fallback. Returns one schema
|
|
234
234
|
per ToolDefinition (protocol) in the module.
|
|
235
235
|
|
|
236
236
|
Args:
|
|
237
|
-
|
|
237
|
+
slug: Module ID to look up (checks cache first, then registry).
|
|
238
238
|
|
|
239
239
|
Returns:
|
|
240
240
|
List of OpenAI-style tool schemas, one per protocol. Empty if not found.
|
|
241
241
|
"""
|
|
242
|
-
tool_module_info =
|
|
243
|
-
module_id, registry=self.registry, communication=self.communication
|
|
244
|
-
)
|
|
242
|
+
tool_module_info = self.tool_cache.get(slug)
|
|
245
243
|
if not tool_module_info:
|
|
246
244
|
return []
|
|
247
245
|
|
|
@@ -250,8 +248,8 @@ class ModuleContext:
|
|
|
250
248
|
"type": "function",
|
|
251
249
|
"function": {
|
|
252
250
|
"module_id": tool_module_info.module_id,
|
|
253
|
-
"toolkit_name": tool_module_info.
|
|
254
|
-
"name": tool_def.name,
|
|
251
|
+
"toolkit_name": tool_module_info.tool_name or "undefined",
|
|
252
|
+
"name": tool_module_info.slug + "_" + tool_def.name,
|
|
255
253
|
"description": tool_def.description,
|
|
256
254
|
"parameters": ModuleContext._build_parameters_schema(tool_def.parameters),
|
|
257
255
|
},
|
|
@@ -277,9 +275,9 @@ class ModuleContext:
|
|
|
277
275
|
|
|
278
276
|
def create_tool_functions(
|
|
279
277
|
self,
|
|
280
|
-
|
|
278
|
+
slug: str,
|
|
281
279
|
) -> list[tuple[ToolDefinition, Callable[..., AsyncGenerator[dict, None]]]]:
|
|
282
|
-
"""Create tool functions for all protocols in a tool
|
|
280
|
+
"""Create tool functions for all protocols in a tool setup.
|
|
283
281
|
|
|
284
282
|
Returns an async generator per ToolDefinition that calls the remote tool
|
|
285
283
|
module via gRPC with the protocol auto-injected.
|
|
@@ -288,12 +286,12 @@ class ModuleContext:
|
|
|
288
286
|
in sync contexts like __init__ methods.
|
|
289
287
|
|
|
290
288
|
Args:
|
|
291
|
-
|
|
289
|
+
slug: Setup ID to look up in cache.
|
|
292
290
|
|
|
293
291
|
Returns:
|
|
294
292
|
List of (ToolDefinition, async_generator_function) tuples. Empty if not found.
|
|
295
293
|
"""
|
|
296
|
-
tool_module_info = self.tool_cache.entries.get(
|
|
294
|
+
tool_module_info = self.tool_cache.entries.get(slug)
|
|
297
295
|
if not tool_module_info:
|
|
298
296
|
return []
|
|
299
297
|
|
|
@@ -29,7 +29,6 @@ SetupModelT = TypeVar("SetupModelT", bound="SetupModel")
|
|
|
29
29
|
class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
30
30
|
"""Base setup model with dynamic schema and tool cache support."""
|
|
31
31
|
|
|
32
|
-
model_config = ConfigDict(extra="allow")
|
|
33
32
|
_clean_model_cache: ClassVar[dict[tuple[type, bool, bool], type]] = {}
|
|
34
33
|
resolved_tools: dict[str, ToolModuleInfo] = Field(
|
|
35
34
|
default_factory=dict,
|
|
@@ -347,7 +346,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
347
346
|
resolved_tools: Cache of already resolved tools.
|
|
348
347
|
"""
|
|
349
348
|
if isinstance(field_value, ToolReference):
|
|
350
|
-
await cls.
|
|
349
|
+
await cls._resolve_tool_reference(
|
|
351
350
|
field_name,
|
|
352
351
|
field_value,
|
|
353
352
|
registry,
|
|
@@ -377,7 +376,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
377
376
|
)
|
|
378
377
|
|
|
379
378
|
@classmethod
|
|
380
|
-
async def
|
|
379
|
+
async def _resolve_tool_reference(
|
|
381
380
|
cls,
|
|
382
381
|
field_name: str,
|
|
383
382
|
tool_ref: ToolReference,
|
|
@@ -385,7 +384,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
385
384
|
communication: "CommunicationStrategy",
|
|
386
385
|
resolved_tools: dict[str, ToolModuleInfo],
|
|
387
386
|
) -> None:
|
|
388
|
-
"""Resolve a
|
|
387
|
+
"""Resolve a ToolReference (may contain multiple selected tools).
|
|
389
388
|
|
|
390
389
|
Args:
|
|
391
390
|
field_name: Name of the field for logging.
|
|
@@ -394,21 +393,23 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
394
393
|
communication: Communication service for module schemas.
|
|
395
394
|
resolved_tools: Cache of already resolved tools.
|
|
396
395
|
"""
|
|
397
|
-
logger.info("Resolving ToolReference '%s' with
|
|
396
|
+
logger.info("Resolving ToolReference '%s' with %d selected tools", field_name, len(tool_ref.selected_tools))
|
|
398
397
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
398
|
+
if not tool_ref.selected_tools:
|
|
399
|
+
logger.info("ToolReference '%s' has no selected tools, skipping", field_name)
|
|
400
|
+
return
|
|
401
|
+
|
|
402
|
+
tools_to_resolve = [tool for tool in tool_ref.selected_tools if tool.slug and tool.slug not in resolved_tools]
|
|
403
|
+
|
|
404
|
+
if not tools_to_resolve:
|
|
405
|
+
logger.info("All tools for '%s' already cached", field_name)
|
|
406
|
+
return
|
|
406
407
|
|
|
407
408
|
try:
|
|
408
|
-
|
|
409
|
-
|
|
409
|
+
infos = await tool_ref.resolve(registry, communication)
|
|
410
|
+
for info in infos:
|
|
410
411
|
resolved_tools[info.setup_id] = info
|
|
411
|
-
|
|
412
|
+
logger.info("Resolved tool '%s' -> module_id=%s", info.setup_id, info.module_id)
|
|
412
413
|
except Exception:
|
|
413
414
|
logger.exception("Failed to resolve ToolReference '%s'", field_name)
|
|
414
415
|
|
|
@@ -430,7 +431,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
430
431
|
"""
|
|
431
432
|
for item in items:
|
|
432
433
|
if isinstance(item, ToolReference):
|
|
433
|
-
await cls.
|
|
434
|
+
await cls._resolve_tool_reference(
|
|
434
435
|
"list_item",
|
|
435
436
|
item,
|
|
436
437
|
registry,
|
|
@@ -463,7 +464,7 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
463
464
|
"""
|
|
464
465
|
for item in mapping.values():
|
|
465
466
|
if isinstance(item, ToolReference):
|
|
466
|
-
await cls.
|
|
467
|
+
await cls._resolve_tool_reference(
|
|
467
468
|
"dict_value",
|
|
468
469
|
item,
|
|
469
470
|
registry,
|
|
@@ -500,10 +501,10 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
500
501
|
if field_value is None:
|
|
501
502
|
continue
|
|
502
503
|
if isinstance(field_value, ToolReference):
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
504
|
+
for tool in field_value.selected_tools:
|
|
505
|
+
tool_module_info = self.resolved_tools.get(tool.slug or "")
|
|
506
|
+
if tool_module_info:
|
|
507
|
+
cache.add(tool_module_info)
|
|
507
508
|
elif isinstance(field_value, BaseModel):
|
|
508
509
|
self._build_tool_cache_recursive(field_value, cache)
|
|
509
510
|
elif isinstance(field_value, list):
|
|
@@ -520,10 +521,10 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
520
521
|
"""
|
|
521
522
|
for item in items:
|
|
522
523
|
if isinstance(item, ToolReference):
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
524
|
+
for tool in item.selected_tools:
|
|
525
|
+
tool_module_info = self.resolved_tools.get(tool.slug or "")
|
|
526
|
+
if tool_module_info:
|
|
527
|
+
cache.add(tool_module_info)
|
|
527
528
|
elif isinstance(item, BaseModel):
|
|
528
529
|
self._build_tool_cache_recursive(item, cache)
|
|
529
530
|
|
|
@@ -536,9 +537,9 @@ class SetupModel(BaseModel, Generic[SetupModelT]):
|
|
|
536
537
|
"""
|
|
537
538
|
for item in mapping.values():
|
|
538
539
|
if isinstance(item, ToolReference):
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
540
|
+
for tool in item.selected_tools:
|
|
541
|
+
tool_module_info = self.resolved_tools.get(tool.slug or "")
|
|
542
|
+
if tool_module_info:
|
|
543
|
+
cache.add(tool_module_info)
|
|
543
544
|
elif isinstance(item, BaseModel):
|
|
544
545
|
self._build_tool_cache_recursive(item, cache)
|
|
@@ -6,7 +6,16 @@ from pydantic import BaseModel, Field
|
|
|
6
6
|
|
|
7
7
|
from digitalkin.logger import logger
|
|
8
8
|
from digitalkin.models.services.registry import ModuleInfo
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SelectedTool(BaseModel):
|
|
12
|
+
"""Selected tool information."""
|
|
13
|
+
|
|
14
|
+
setup_id: str = ""
|
|
15
|
+
module_id: str = ""
|
|
16
|
+
slug: str = ""
|
|
17
|
+
name: str = ""
|
|
18
|
+
|
|
10
19
|
|
|
11
20
|
if TYPE_CHECKING:
|
|
12
21
|
from digitalkin.services.communication import CommunicationStrategy
|
|
@@ -51,8 +60,14 @@ class ToolDefinition(BaseModel):
|
|
|
51
60
|
class ToolModuleInfo(ModuleInfo):
|
|
52
61
|
"""Module info for tool modules."""
|
|
53
62
|
|
|
54
|
-
tools: list[ToolDefinition]
|
|
55
|
-
|
|
63
|
+
tools: list[ToolDefinition] = Field(default_factory=list)
|
|
64
|
+
tool_name: str = ""
|
|
65
|
+
setup_id: str = ""
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def slug(self) -> str:
|
|
69
|
+
"""Module ID."""
|
|
70
|
+
return self.setup_id + "_" + self.tool_name
|
|
56
71
|
|
|
57
72
|
|
|
58
73
|
class ToolCache(BaseModel):
|
|
@@ -60,53 +75,35 @@ class ToolCache(BaseModel):
|
|
|
60
75
|
|
|
61
76
|
entries: dict[str, ToolModuleInfo] = Field(default_factory=dict)
|
|
62
77
|
|
|
63
|
-
def add(self,
|
|
78
|
+
def add(self, tool_module_info: ToolModuleInfo) -> None:
|
|
64
79
|
"""Add a tool to the cache.
|
|
65
80
|
|
|
66
81
|
Args:
|
|
67
|
-
setup_id: Field name from SetupModel used as cache key.
|
|
68
82
|
tool_module_info: Resolved tool module information.
|
|
69
83
|
"""
|
|
70
|
-
self.entries[
|
|
84
|
+
self.entries[tool_module_info.slug] = tool_module_info
|
|
71
85
|
logger.debug(
|
|
72
86
|
"Tool cached",
|
|
73
|
-
extra={
|
|
87
|
+
extra={
|
|
88
|
+
"slug": tool_module_info.slug,
|
|
89
|
+
"module_id": tool_module_info.module_id,
|
|
90
|
+
"setup_id": tool_module_info.setup_id,
|
|
91
|
+
},
|
|
74
92
|
)
|
|
75
93
|
|
|
76
|
-
|
|
94
|
+
def get(
|
|
77
95
|
self,
|
|
78
|
-
|
|
79
|
-
*,
|
|
80
|
-
registry: RegistryStrategy | None = None,
|
|
81
|
-
communication: "CommunicationStrategy | None" = None,
|
|
96
|
+
slug: str,
|
|
82
97
|
) -> ToolModuleInfo | None:
|
|
83
98
|
"""Get a tool from cache, optionally querying registry on miss.
|
|
84
99
|
|
|
85
100
|
Args:
|
|
86
|
-
|
|
87
|
-
registry: Optional registry to query on cache miss.
|
|
88
|
-
communication: Optional communication strategy for schema fetching.
|
|
101
|
+
slug: Field name to look up.
|
|
89
102
|
|
|
90
103
|
Returns:
|
|
91
104
|
ToolModuleInfo if found, None otherwise.
|
|
92
105
|
"""
|
|
93
|
-
|
|
94
|
-
if cached:
|
|
95
|
-
return cached
|
|
96
|
-
|
|
97
|
-
if registry and communication:
|
|
98
|
-
try:
|
|
99
|
-
setup_info = registry.get_setup(setup_id)
|
|
100
|
-
if setup_info and setup_info.module_id:
|
|
101
|
-
info = registry.discover_by_id(setup_info.module_id)
|
|
102
|
-
if info:
|
|
103
|
-
tool_info = await module_info_to_tool_module_info(info, setup_id, communication)
|
|
104
|
-
self.add(setup_id, tool_info)
|
|
105
|
-
return tool_info
|
|
106
|
-
except Exception:
|
|
107
|
-
logger.exception("Registry lookup failed", extra={"setup_id": setup_id})
|
|
108
|
-
|
|
109
|
-
return None
|
|
106
|
+
return self.entries.get(slug)
|
|
110
107
|
|
|
111
108
|
def clear(self) -> None:
|
|
112
109
|
"""Clear all cache entries."""
|
|
@@ -123,7 +120,7 @@ class ToolCache(BaseModel):
|
|
|
123
120
|
|
|
124
121
|
async def module_info_to_tool_module_info(
|
|
125
122
|
module_info: ModuleInfo,
|
|
126
|
-
|
|
123
|
+
tool: SelectedTool,
|
|
127
124
|
communication: "CommunicationStrategy",
|
|
128
125
|
*,
|
|
129
126
|
llm_format: bool = True,
|
|
@@ -135,7 +132,7 @@ async def module_info_to_tool_module_info(
|
|
|
135
132
|
|
|
136
133
|
Args:
|
|
137
134
|
module_info: Module info from registry.
|
|
138
|
-
|
|
135
|
+
tool: Selected tool information.
|
|
139
136
|
communication: Communication strategy for gRPC calls.
|
|
140
137
|
llm_format: Use LLM-friendly schema format.
|
|
141
138
|
|
|
@@ -160,11 +157,12 @@ async def module_info_to_tool_module_info(
|
|
|
160
157
|
address=module_info.address,
|
|
161
158
|
port=module_info.port,
|
|
162
159
|
version=module_info.version,
|
|
163
|
-
|
|
160
|
+
module_name=module_info.module_name,
|
|
164
161
|
documentation=module_info.documentation,
|
|
165
162
|
status=module_info.status,
|
|
166
163
|
tools=tools,
|
|
167
|
-
setup_id=setup_id,
|
|
164
|
+
setup_id=tool.setup_id,
|
|
165
|
+
tool_name=tool.name,
|
|
168
166
|
)
|
|
169
167
|
|
|
170
168
|
|
|
@@ -1,136 +1,103 @@
|
|
|
1
1
|
"""Tool reference types for module configuration."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import Annotated
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel,
|
|
5
|
+
from pydantic import BaseModel, BeforeValidator, Field
|
|
6
|
+
from pydantic.json_schema import GetJsonSchemaHandler, JsonSchemaValue
|
|
7
|
+
from pydantic_core import CoreSchema
|
|
6
8
|
|
|
7
|
-
from digitalkin.models.module.tool_cache import
|
|
9
|
+
from digitalkin.models.module.tool_cache import (
|
|
10
|
+
SelectedTool,
|
|
11
|
+
ToolModuleInfo,
|
|
12
|
+
module_info_to_tool_module_info,
|
|
13
|
+
)
|
|
8
14
|
from digitalkin.services.communication.communication_strategy import CommunicationStrategy
|
|
9
15
|
from digitalkin.services.registry import RegistryStrategy
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
class ToolSelectionMode(str, Enum):
|
|
13
|
-
"""Tool selection mode."""
|
|
14
|
-
|
|
15
|
-
TAG = "tag"
|
|
16
|
-
FIXED = "fixed"
|
|
17
|
-
DISCOVERABLE = "discoverable"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ToolReferenceConfig(BaseModel):
|
|
21
|
-
"""Tool selection configuration. The module_id serves as both identifier and cache key."""
|
|
22
|
-
|
|
23
|
-
mode: ToolSelectionMode = Field(default=ToolSelectionMode.FIXED)
|
|
24
|
-
setup_id: str = Field(default="")
|
|
25
|
-
module_id: str = Field(default="")
|
|
26
|
-
tag: str = Field(default="")
|
|
27
|
-
organization_id: str = Field(default="")
|
|
28
|
-
|
|
29
|
-
@model_validator(mode="after")
|
|
30
|
-
def validate_config(self) -> "ToolReferenceConfig":
|
|
31
|
-
"""Validate required fields based on mode.
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
Self if validation passes.
|
|
35
|
-
|
|
36
|
-
Raises:
|
|
37
|
-
ValueError: If required field is missing for the mode.
|
|
38
|
-
"""
|
|
39
|
-
if self.mode == ToolSelectionMode.FIXED and not self.setup_id:
|
|
40
|
-
msg = "setup_id required when mode is FIXED"
|
|
41
|
-
raise ValueError(msg)
|
|
42
|
-
if self.mode == ToolSelectionMode.TAG and not self.tag:
|
|
43
|
-
msg = "tag required when mode is TAG"
|
|
44
|
-
raise ValueError(msg)
|
|
45
|
-
return self
|
|
46
|
-
|
|
47
|
-
|
|
48
18
|
class ToolReference(BaseModel):
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
19
|
+
"""Tool selection configuration and reference.
|
|
20
|
+
|
|
21
|
+
The mode determines validation requirements and resolution behavior:
|
|
22
|
+
- FIXED: Requires setup_id, resolves to exact tool
|
|
23
|
+
- MODULE: Requires module_id, returns constraint for frontend selection
|
|
24
|
+
- TAG: Requires tag, returns constraint for frontend selection
|
|
25
|
+
- DISCOVERABLE: Optional module_id/tag constraints, returns constraint info
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
selected_tools: list[SelectedTool] = Field(default=[], description="Tools selected by the user.")
|
|
29
|
+
setup_ids: list[str] = Field(default=[], description="Setup IDs for the user to choose from.")
|
|
30
|
+
module_ids: list[str] = Field(default=[], description="Module IDs for the user to choose from.")
|
|
31
|
+
tags: list[str] = Field(default=[], description="Tags for the user to choose from.")
|
|
32
|
+
max_tools: int = Field(default=0, description="Maximum tools to select. 0 for unlimited.")
|
|
33
|
+
|
|
34
|
+
async def resolve(self, registry: RegistryStrategy, communication: CommunicationStrategy) -> list[ToolModuleInfo]:
|
|
35
|
+
"""Resolve this reference using the registry.
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
Args:
|
|
38
|
+
registry: Registry service for module discovery.
|
|
39
|
+
communication: Communication service for module schemas.
|
|
57
40
|
|
|
58
41
|
Returns:
|
|
59
|
-
|
|
42
|
+
List of ToolModuleInfo if resolved.
|
|
60
43
|
"""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
44
|
+
resolved: list[ToolModuleInfo] = []
|
|
45
|
+
for tool in self.selected_tools:
|
|
46
|
+
setup = registry.get_setup(tool.setup_id)
|
|
47
|
+
if setup and setup.module_id:
|
|
48
|
+
info = registry.discover_by_id(setup.module_id)
|
|
49
|
+
tool.slug = tool.setup_id
|
|
50
|
+
tool.module_id = setup.module_id
|
|
51
|
+
tool.name = setup.name
|
|
52
|
+
if info:
|
|
53
|
+
resolved.append(await module_info_to_tool_module_info(info, tool, communication))
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
Module ID or empty string if not set.
|
|
69
|
-
"""
|
|
70
|
-
return self.config.module_id
|
|
55
|
+
return resolved
|
|
71
56
|
|
|
72
|
-
@property
|
|
73
|
-
def setup_id(self) -> str:
|
|
74
|
-
"""Setup identifier.
|
|
75
57
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"""
|
|
79
|
-
return self.config.setup_id
|
|
58
|
+
def _convert_to_tool_reference(v: object) -> "ToolReference | object":
|
|
59
|
+
"""Convert list of setup IDs to ToolReference.
|
|
80
60
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"""Resolved module information.
|
|
61
|
+
Args:
|
|
62
|
+
v: Input value, either a list of setup IDs or passthrough.
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
64
|
+
Returns:
|
|
65
|
+
ToolReference if input is list, otherwise original value.
|
|
66
|
+
"""
|
|
67
|
+
if isinstance(v, list):
|
|
68
|
+
return ToolReference(selected_tools=[SelectedTool(setup_id=sid, slug=sid) for sid in v])
|
|
69
|
+
return v
|
|
89
70
|
|
|
90
|
-
@property
|
|
91
|
-
def is_resolved(self) -> bool:
|
|
92
|
-
"""Whether this reference has been resolved.
|
|
93
71
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"""
|
|
97
|
-
return self._cached_info is not None
|
|
72
|
+
class _ToolReferenceInputSchema:
|
|
73
|
+
"""Custom JSON schema generator that wraps ToolReference in anyOf with array option."""
|
|
98
74
|
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
@staticmethod
|
|
76
|
+
def __get_pydantic_json_schema__( # noqa: PLW3201
|
|
77
|
+
schema: CoreSchema,
|
|
78
|
+
handler: GetJsonSchemaHandler,
|
|
79
|
+
) -> JsonSchemaValue:
|
|
80
|
+
"""Generate JSON schema accepting both list[str] and ToolReference.
|
|
101
81
|
|
|
102
82
|
Args:
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
schema: The core schema from Pydantic.
|
|
84
|
+
handler: Handler to generate JSON schema from core schema.
|
|
105
85
|
|
|
106
86
|
Returns:
|
|
107
|
-
|
|
87
|
+
JSON schema with anyOf accepting array or ToolReference.
|
|
108
88
|
"""
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
name=self.config.tag,
|
|
125
|
-
module_type="tool",
|
|
126
|
-
organization_id=self.config.organization_id,
|
|
127
|
-
)
|
|
128
|
-
if results:
|
|
129
|
-
tool_module_info = await module_info_to_tool_module_info(
|
|
130
|
-
results[0], self.config.setup_id, communication
|
|
131
|
-
)
|
|
132
|
-
self._cached_info = tool_module_info
|
|
133
|
-
self.config.module_id = tool_module_info.module_id
|
|
134
|
-
return tool_module_info
|
|
135
|
-
|
|
136
|
-
return None
|
|
89
|
+
json_schema = handler(schema)
|
|
90
|
+
return {
|
|
91
|
+
"anyOf": [
|
|
92
|
+
{"type": "array", "items": {"type": "string"}},
|
|
93
|
+
json_schema,
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
ToolReferenceInput = Annotated[
|
|
99
|
+
ToolReference,
|
|
100
|
+
BeforeValidator(_convert_to_tool_reference),
|
|
101
|
+
_ToolReferenceInputSchema,
|
|
102
|
+
]
|
|
103
|
+
"""Type alias for ToolReference fields that accept list[str] input from frontend."""
|
|
@@ -26,12 +26,12 @@ class RegistryModuleType(str, Enum):
|
|
|
26
26
|
class ModuleInfo(BaseModel):
|
|
27
27
|
"""Module information from registry."""
|
|
28
28
|
|
|
29
|
-
module_id: str
|
|
30
|
-
module_type: RegistryModuleType
|
|
31
|
-
address: str
|
|
32
|
-
port: int
|
|
33
|
-
version: str
|
|
34
|
-
|
|
29
|
+
module_id: str = ""
|
|
30
|
+
module_type: RegistryModuleType = RegistryModuleType.UNSPECIFIED
|
|
31
|
+
address: str = ""
|
|
32
|
+
port: int = 0
|
|
33
|
+
version: str = ""
|
|
34
|
+
module_name: str = ""
|
|
35
35
|
documentation: str | None = None
|
|
36
36
|
status: RegistryModuleStatus | None = None
|
|
37
37
|
|
|
@@ -74,7 +74,7 @@ class GrpcRegistry(RegistryStrategy, GrpcClientWrapper, GrpcErrorHandlerMixin):
|
|
|
74
74
|
address=descriptor.address,
|
|
75
75
|
port=descriptor.port,
|
|
76
76
|
version=descriptor.version,
|
|
77
|
-
|
|
77
|
+
module_name=descriptor.name,
|
|
78
78
|
documentation=descriptor.documentation or None,
|
|
79
79
|
)
|
|
80
80
|
|
|
@@ -7,7 +7,7 @@ base_server/mock/__init__.py,sha256=YZFT-F1l_TpvJYuIPX-7kTeE1CfOjhx9YmNRXVoi-jQ,
|
|
|
7
7
|
base_server/mock/mock_pb2.py,sha256=sETakcS3PAAm4E-hTCV1jIVaQTPEAIoVVHupB8Z_k7Y,1843
|
|
8
8
|
base_server/mock/mock_pb2_grpc.py,sha256=BbOT70H6q3laKgkHfOx1QdfmCS_HxCY4wCOX84YAdG4,3180
|
|
9
9
|
digitalkin/__init__.py,sha256=7LLBAba0th-3SGqcpqFO-lopWdUkVLKzLZiMtB-mW3M,162
|
|
10
|
-
digitalkin/__version__.py,sha256=
|
|
10
|
+
digitalkin/__version__.py,sha256=dZgyJRHQFh_03su8ZElZY21agbb3n1gwA2QNWlIpunk,196
|
|
11
11
|
digitalkin/logger.py,sha256=8ze_tjt2G6mDTuQcsf7-UTXWP3UHZ7LZVSs_iqF4rX4,4685
|
|
12
12
|
digitalkin/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
digitalkin/core/__init__.py,sha256=FJRcJ-B1Viyn-38L8XpOpZ8KOnf1I7PCDOAmKXLQhqc,71
|
|
@@ -28,7 +28,7 @@ digitalkin/core/task_manager/task_session.py,sha256=5jw21bT_SPXUzWE7tk6YG62EXqlR
|
|
|
28
28
|
digitalkin/grpc_servers/__init__.py,sha256=ZIRMJ1Lcas8yQ106GCup6hn2UBOsx1sNk8ap0lpEDnY,72
|
|
29
29
|
digitalkin/grpc_servers/_base_server.py,sha256=ZVeCDwI7w7fFbPTXPkeJb_SOuLfd2T7za3T4oCu2UWY,18680
|
|
30
30
|
digitalkin/grpc_servers/module_server.py,sha256=Ec3izzV2YpdN8rGs_cX-iVulQ00FkLR5dBflHlQ8a6Y,7849
|
|
31
|
-
digitalkin/grpc_servers/module_servicer.py,sha256=
|
|
31
|
+
digitalkin/grpc_servers/module_servicer.py,sha256=yhS5zOxxOTh3aXNVZBbgomRtFa5k6Q7jm-PZh54Gus8,21717
|
|
32
32
|
digitalkin/grpc_servers/utils/__init__.py,sha256=ZnAIb_F8z4NhtPypqkdmzgRSzolKnJTk3oZx5GfWH5Y,38
|
|
33
33
|
digitalkin/grpc_servers/utils/exceptions.py,sha256=LtaDtlqXCeT6iqApogs4pbtezotOVeg4fhnFzGBvFsY,692
|
|
34
34
|
digitalkin/grpc_servers/utils/grpc_client_wrapper.py,sha256=nGG8QdKnBH0UG9qbKrlPwIvcvPgW3osw7O3cImxisPE,3279
|
|
@@ -50,18 +50,18 @@ digitalkin/models/core/task_monitor.py,sha256=CW-jydSgXMV464W0pqfar0HpgqlSxqdujm
|
|
|
50
50
|
digitalkin/models/grpc_servers/__init__.py,sha256=0tA71nPSXgRrh9DoLvx-TSwZXdYIRUEItoadpTL1cTo,42
|
|
51
51
|
digitalkin/models/grpc_servers/models.py,sha256=gRX94eL71a5mLIie-lCOwE7a0As_AuGduxPPzTHbAe4,13797
|
|
52
52
|
digitalkin/models/grpc_servers/types.py,sha256=rQ78s4nAet2jy-NIDj_PUWriT0kuGHr_w6ELjmjgBao,539
|
|
53
|
-
digitalkin/models/module/__init__.py,sha256=
|
|
53
|
+
digitalkin/models/module/__init__.py,sha256=XlFlLGmLUYPV4ZOgBxfGclq9wupGDXNUyWB4EB6iTXA,909
|
|
54
54
|
digitalkin/models/module/base_types.py,sha256=oIylVNqo0idTFj4dRgCt7P19daNZ-AlvgCPpL9TJvto,1850
|
|
55
55
|
digitalkin/models/module/module.py,sha256=k0W8vfJJFth8XdDzkHm32SyTuSf3h2qF0hSrxAfGF1s,956
|
|
56
|
-
digitalkin/models/module/module_context.py,sha256=
|
|
56
|
+
digitalkin/models/module/module_context.py,sha256=kwaAobsA82Du6L0XH9Is9u6Qj1rEjhYqVk00FonMaw4,12087
|
|
57
57
|
digitalkin/models/module/module_types.py,sha256=C9azCNBk76xMa-Mww8_6AiwQR8MLAsEyUOvBYxytovI,739
|
|
58
|
-
digitalkin/models/module/setup_types.py,sha256=
|
|
59
|
-
digitalkin/models/module/tool_cache.py,sha256=
|
|
60
|
-
digitalkin/models/module/tool_reference.py,sha256=
|
|
58
|
+
digitalkin/models/module/setup_types.py,sha256=8xEI6DvCYBHSOsuoeuZjtBL5rPsR_7DwWFxQnn2bo3c,19249
|
|
59
|
+
digitalkin/models/module/tool_cache.py,sha256=xAMSyY73aduYGPz6C54g0YMAa2OnXPN6QRS-W4TK_js,6568
|
|
60
|
+
digitalkin/models/module/tool_reference.py,sha256=rdkke5ydAgXcFfu34yxiHyaiRGcclnNGwPJK-01B2t8,3829
|
|
61
61
|
digitalkin/models/module/utility.py,sha256=gnbYfWpXGbomUI0fWf7T-Qm_VvT-LXDv1OuA9zObwVg,5589
|
|
62
62
|
digitalkin/models/services/__init__.py,sha256=jhfVw6egq0OcHmos_fypH9XFehbHTBw09wluVFVFEyw,226
|
|
63
63
|
digitalkin/models/services/cost.py,sha256=9PXvd5RrIk9vCrRjcUGQ9ZyAokEbwLg4s0RfnE-aLP4,1616
|
|
64
|
-
digitalkin/models/services/registry.py,sha256=
|
|
64
|
+
digitalkin/models/services/registry.py,sha256=bCdVpsiu8bqKL-7P5r3OPmllZj4MAtpsfsdlsg5TAww,1887
|
|
65
65
|
digitalkin/models/services/storage.py,sha256=wp7F-AvTsU46ujGPcguqM5kUKRZx4399D4EGAAJt2zs,1143
|
|
66
66
|
digitalkin/modules/__init__.py,sha256=vTQk8DWopxQSJ17BjE5dNhq247Rou55iQLJdBxoPUmo,296
|
|
67
67
|
digitalkin/modules/_base_module.py,sha256=0XC0aQAxlNfvz0KK9ut7K0JbZql3cZMU4aeg7ISEsD0,21971
|
|
@@ -97,7 +97,7 @@ digitalkin/services/identity/identity_strategy.py,sha256=skappBbds1_qa0Gr24FGrNX
|
|
|
97
97
|
digitalkin/services/registry/__init__.py,sha256=WPGQM3U-QvMXhsaOy9BN0kVMU3QkPFwAMT3lGmTR-Ko,835
|
|
98
98
|
digitalkin/services/registry/default_registry.py,sha256=tOqw9Ve9w_BzhqrZmHuUl5Ps-J_KTEwYg3tu1gNIHmw,4258
|
|
99
99
|
digitalkin/services/registry/exceptions.py,sha256=tAcVXioCzDqfBvxB_P0uQpaK_LDLrFb0KpymROuqs-8,1371
|
|
100
|
-
digitalkin/services/registry/grpc_registry.py,sha256=
|
|
100
|
+
digitalkin/services/registry/grpc_registry.py,sha256=Myrt2WqmRQG1yLu2voUOLH4vp3Kwf_WitOBG3YwhtB4,13086
|
|
101
101
|
digitalkin/services/registry/registry_models.py,sha256=DJEwMJg5_BewpgHDtY8xIGWj9jA9H07iYgHLCv81giY,331
|
|
102
102
|
digitalkin/services/registry/registry_strategy.py,sha256=oxCm5mQcO1PSwMwOtEv8dwzQx_F0uGKgiWeVMeiV51s,2905
|
|
103
103
|
digitalkin/services/setup/__init__.py,sha256=t6xcvEWqTbcRZstBFK9cESEqaZKvpW14VtYygxIqfYQ,65
|
|
@@ -123,9 +123,9 @@ digitalkin/utils/dynamic_schema.py,sha256=y5csxjuqVHjWDpnTUzxbcUuI_wou9-ibRVHQlB
|
|
|
123
123
|
digitalkin/utils/llm_ready_schema.py,sha256=JjMug_lrQllqFoanaC091VgOqwAd-_YzcpqFlS7p778,2375
|
|
124
124
|
digitalkin/utils/package_discover.py,sha256=sa6Zp5Kape1Zr4iYiNrnZxiHDnqM06ODk6yfWHom53w,13465
|
|
125
125
|
digitalkin/utils/schema_splitter.py,sha256=6PeDrEBGEFyVEPgzGnwcCZuwxLobThmuPwJhGlRpU_0,11137
|
|
126
|
-
digitalkin-0.3.2.
|
|
126
|
+
digitalkin-0.3.2.dev22.dist-info/licenses/LICENSE,sha256=Ies4HFv2r2hzDRakJYxk3Y60uDFLiG-orIgeTpstnIo,20327
|
|
127
127
|
modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
|
-
modules/archetype_with_tools_module.py,sha256=
|
|
128
|
+
modules/archetype_with_tools_module.py,sha256=kJkVhAFWG0aDDqzupOXOnV3l8j3z5bEdWos_6Z9rUP8,7303
|
|
129
129
|
modules/cpu_intensive_module.py,sha256=GZlirQDZdYuXrI46sv1q4RNAHZjL4EptHVQTvgK9zz8,8363
|
|
130
130
|
modules/dynamic_setup_module.py,sha256=tKvUWZdlYZkfAgKR0mLuFcLiFGKpVgpsz10LeJ6B2QI,11410
|
|
131
131
|
modules/minimal_llm_module.py,sha256=N9aIzZQI-miyH4AB4xTmGHpMvdSLnYyXNOD4Z3YFzis,11216
|
|
@@ -138,7 +138,7 @@ monitoring/digitalkin_observability/prometheus.py,sha256=gDmM9ySaVwPAe7Yg84pLxmE
|
|
|
138
138
|
monitoring/tests/test_metrics.py,sha256=ugnYfAwqBPO6zA8z4afKTlyBWECTivacYSN-URQCn2E,5856
|
|
139
139
|
services/filesystem_module.py,sha256=U4dgqtuDadaXz8PJ1d_uQ_1EPncBqudAQCLUICF9yL4,7421
|
|
140
140
|
services/storage_module.py,sha256=Wz2MzLvqs2D_bnBBgtnujYcAKK2V2KFMk8K21RoepSE,6972
|
|
141
|
-
digitalkin-0.3.2.
|
|
142
|
-
digitalkin-0.3.2.
|
|
143
|
-
digitalkin-0.3.2.
|
|
144
|
-
digitalkin-0.3.2.
|
|
141
|
+
digitalkin-0.3.2.dev22.dist-info/METADATA,sha256=6Zn0jPPta8ZRH7ohcui_18g4PsaoCBO6bSi59U4kg9o,29725
|
|
142
|
+
digitalkin-0.3.2.dev22.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
143
|
+
digitalkin-0.3.2.dev22.dist-info/top_level.txt,sha256=AYVIesKrO0jnedQ-Muog9JBehG81WeTCNeOFoJgwsgE,51
|
|
144
|
+
digitalkin-0.3.2.dev22.dist-info/RECORD,,
|
|
@@ -8,11 +8,7 @@ from pydantic import BaseModel, Field
|
|
|
8
8
|
from digitalkin.models.grpc_servers.models import ClientConfig, SecurityMode, ServerMode
|
|
9
9
|
from digitalkin.models.module.module_context import ModuleContext
|
|
10
10
|
from digitalkin.models.module.setup_types import SetupModel
|
|
11
|
-
from digitalkin.models.module.tool_reference import
|
|
12
|
-
ToolReference,
|
|
13
|
-
ToolReferenceConfig,
|
|
14
|
-
ToolSelectionMode,
|
|
15
|
-
)
|
|
11
|
+
from digitalkin.models.module.tool_reference import ToolReference
|
|
16
12
|
from digitalkin.modules._base_module import BaseModule # noqa: PLC2701
|
|
17
13
|
from digitalkin.services.services_models import ServicesStrategy
|
|
18
14
|
|
|
@@ -64,29 +60,21 @@ class ArchetypeSetup(SetupModel):
|
|
|
64
60
|
|
|
65
61
|
search_tool: ToolReference = Field(
|
|
66
62
|
default_factory=lambda: ToolReference(
|
|
67
|
-
|
|
68
|
-
mode=ToolSelectionMode.FIXED,
|
|
69
|
-
module_id="search-tool-v1",
|
|
70
|
-
)
|
|
63
|
+
module_ids=["search-tool-v1"],
|
|
71
64
|
),
|
|
72
65
|
json_schema_extra={"config": True},
|
|
73
66
|
)
|
|
74
67
|
|
|
75
68
|
calculator_tool: ToolReference = Field(
|
|
76
69
|
default_factory=lambda: ToolReference(
|
|
77
|
-
|
|
78
|
-
mode=ToolSelectionMode.TAG,
|
|
79
|
-
tag="math-calculator",
|
|
80
|
-
)
|
|
70
|
+
tags=["math-calculator"],
|
|
81
71
|
),
|
|
82
72
|
json_schema_extra={"config": True},
|
|
83
73
|
)
|
|
84
74
|
|
|
85
75
|
dynamic_tool: ToolReference = Field(
|
|
86
76
|
default_factory=lambda: ToolReference(
|
|
87
|
-
|
|
88
|
-
mode=ToolSelectionMode.DISCOVERABLE,
|
|
89
|
-
)
|
|
77
|
+
tags=["discoverable"],
|
|
90
78
|
),
|
|
91
79
|
json_schema_extra={"config": True},
|
|
92
80
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|