unique_toolkit 2026.28.0.dev9__py3-none-any.whl → 2026.28.0.dev10__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.
- unique_toolkit/agentic/tools/a2a/manager.py +23 -9
- unique_toolkit/agentic/tools/config.py +77 -21
- unique_toolkit/agentic/tools/tool_manager.py +25 -12
- {unique_toolkit-2026.28.0.dev9.dist-info → unique_toolkit-2026.28.0.dev10.dist-info}/METADATA +1 -1
- {unique_toolkit-2026.28.0.dev9.dist-info → unique_toolkit-2026.28.0.dev10.dist-info}/RECORD +6 -6
- {unique_toolkit-2026.28.0.dev9.dist-info → unique_toolkit-2026.28.0.dev10.dist-info}/WHEEL +0 -0
|
@@ -29,6 +29,13 @@ class A2AManager:
|
|
|
29
29
|
if not tool_config.is_sub_agent:
|
|
30
30
|
continue
|
|
31
31
|
|
|
32
|
+
if not tool_config.is_enabled:
|
|
33
|
+
self._logger.info(
|
|
34
|
+
"Skipping disabled sub-agent '%s'",
|
|
35
|
+
tool_config.name,
|
|
36
|
+
)
|
|
37
|
+
continue
|
|
38
|
+
|
|
32
39
|
if not isinstance(tool_config.configuration, SubAgentToolConfig):
|
|
33
40
|
self._logger.error(
|
|
34
41
|
"tool_config.configuration must be of type SubAgentToolConfig"
|
|
@@ -37,16 +44,23 @@ class A2AManager:
|
|
|
37
44
|
|
|
38
45
|
sub_agent_tool_config = tool_config.configuration
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
try:
|
|
48
|
+
sub_agents.append(
|
|
49
|
+
SubAgentTool(
|
|
50
|
+
configuration=sub_agent_tool_config,
|
|
51
|
+
event=event,
|
|
52
|
+
tool_progress_reporter=self._tool_progress_reporter,
|
|
53
|
+
name=tool_config.name,
|
|
54
|
+
display_name=tool_config.display_name,
|
|
55
|
+
response_watcher=self._response_watcher,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
except Exception:
|
|
59
|
+
self._logger.warning(
|
|
60
|
+
"Skipping sub-agent '%s' due to initialization failure.",
|
|
61
|
+
tool_config.name,
|
|
62
|
+
exc_info=True,
|
|
48
63
|
)
|
|
49
|
-
)
|
|
50
64
|
|
|
51
65
|
filtered_tool_config = [
|
|
52
66
|
tool_config for tool_config in tool_configs if not tool_config.is_sub_agent
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from enum import StrEnum
|
|
2
3
|
from typing import Annotated, Any, Generic
|
|
3
4
|
|
|
@@ -14,9 +15,36 @@ from typing_extensions import TypeVar
|
|
|
14
15
|
from unique_toolkit._common.pydantic_helpers import get_configuration_dict
|
|
15
16
|
from unique_toolkit.agentic.tools.schemas import BaseToolConfig
|
|
16
17
|
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
17
20
|
T = TypeVar("T", bound=BaseToolConfig, default=BaseToolConfig)
|
|
18
21
|
|
|
19
22
|
|
|
23
|
+
def _ensure_base_tool_config(value: dict[str, Any]) -> None:
|
|
24
|
+
"""Make configuration parseable; disabled/demoted tools are never built at runtime."""
|
|
25
|
+
configuration = value.get("configuration")
|
|
26
|
+
if not isinstance(configuration, BaseToolConfig):
|
|
27
|
+
value["configuration"] = BaseToolConfig()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _non_empty_str(value: dict[str, Any], snake_key: str, camel_key: str) -> str:
|
|
31
|
+
raw = value.get(snake_key, value.get(camel_key, ""))
|
|
32
|
+
if raw is None:
|
|
33
|
+
return ""
|
|
34
|
+
return str(raw)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _is_mcp_tool_payload(value: dict[str, Any]) -> bool:
|
|
38
|
+
if _non_empty_str(value, "mcp_source_id", "mcpSourceId"):
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
configuration = value.get("configuration", {})
|
|
42
|
+
if isinstance(configuration, dict):
|
|
43
|
+
if _non_empty_str(configuration, "mcp_source_id", "mcpSourceId"):
|
|
44
|
+
return True
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
20
48
|
class ToolIcon(StrEnum):
|
|
21
49
|
ANALYTICS = "IconAnalytics"
|
|
22
50
|
BOOK = "IconBook"
|
|
@@ -76,11 +104,26 @@ class ToolBuildConfig(BaseModel, Generic[T]):
|
|
|
76
104
|
value: Any,
|
|
77
105
|
info: ValidationInfo,
|
|
78
106
|
) -> Any:
|
|
79
|
-
"""
|
|
107
|
+
"""Validate and resolve tool configuration based on tool type and name.
|
|
108
|
+
|
|
109
|
+
Disabled tools skip validation entirely.
|
|
110
|
+
Enabled tools that fail validation are demoted to disabled.
|
|
111
|
+
"""
|
|
80
112
|
if not isinstance(value, dict):
|
|
81
113
|
return value
|
|
82
114
|
|
|
83
|
-
|
|
115
|
+
if "isEnabled" in value:
|
|
116
|
+
is_enabled = bool(value["isEnabled"])
|
|
117
|
+
elif "is_enabled" in value:
|
|
118
|
+
is_enabled = bool(value["is_enabled"])
|
|
119
|
+
else:
|
|
120
|
+
is_enabled = True
|
|
121
|
+
|
|
122
|
+
if not is_enabled:
|
|
123
|
+
_ensure_base_tool_config(value)
|
|
124
|
+
return value
|
|
125
|
+
|
|
126
|
+
is_mcp_tool = _is_mcp_tool_payload(value)
|
|
84
127
|
mcp_configuration = value.get("configuration", {})
|
|
85
128
|
|
|
86
129
|
# Import at runtime to avoid circular imports
|
|
@@ -102,26 +145,39 @@ class ToolBuildConfig(BaseModel, Generic[T]):
|
|
|
102
145
|
|
|
103
146
|
configuration = value.get("configuration", {})
|
|
104
147
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
148
|
+
try:
|
|
149
|
+
if is_sub_agent_tool:
|
|
150
|
+
from unique_toolkit.agentic.tools.a2a import ExtendedSubAgentToolConfig
|
|
151
|
+
|
|
152
|
+
config = ExtendedSubAgentToolConfig.model_validate(configuration)
|
|
153
|
+
elif isinstance(configuration, dict):
|
|
154
|
+
# Local import to avoid circular import at module import time
|
|
155
|
+
from unique_toolkit.agentic.tools.factory import ToolFactory
|
|
156
|
+
|
|
157
|
+
config = ToolFactory.build_tool_config(
|
|
158
|
+
value["name"],
|
|
159
|
+
**configuration,
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
# Check that the type of config matches the tool name
|
|
163
|
+
from unique_toolkit.agentic.tools.factory import ToolFactory
|
|
164
|
+
|
|
165
|
+
assert isinstance(
|
|
166
|
+
configuration,
|
|
167
|
+
ToolFactory.tool_config_map[value["name"]], # pyright: ignore[reportArgumentType]
|
|
168
|
+
)
|
|
169
|
+
config = configuration
|
|
170
|
+
except Exception:
|
|
171
|
+
tool_name = value.get("name", "<unknown>")
|
|
172
|
+
logger.warning(
|
|
173
|
+
"Tool '%s' has invalid configuration and will be disabled.",
|
|
174
|
+
tool_name,
|
|
175
|
+
exc_info=True,
|
|
116
176
|
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
177
|
+
value["is_enabled"] = False
|
|
178
|
+
value["isEnabled"] = False
|
|
179
|
+
_ensure_base_tool_config(value)
|
|
180
|
+
return value
|
|
120
181
|
|
|
121
|
-
assert isinstance(
|
|
122
|
-
configuration,
|
|
123
|
-
ToolFactory.tool_config_map[value["name"]], # pyright: ignore[reportArgumentType]
|
|
124
|
-
)
|
|
125
|
-
config = configuration
|
|
126
182
|
value["configuration"] = config
|
|
127
183
|
return value
|
|
@@ -147,7 +147,9 @@ class _ToolManager(Generic[_ApiMode]):
|
|
|
147
147
|
self._tool_choices = run_context.tool_choices
|
|
148
148
|
self._disabled_tools = run_context.disabled_tools
|
|
149
149
|
self._exclusive_tools = [
|
|
150
|
-
tool.name
|
|
150
|
+
tool.name
|
|
151
|
+
for tool in self._config.tools
|
|
152
|
+
if tool.is_exclusive and tool.is_enabled
|
|
151
153
|
]
|
|
152
154
|
# this needs to be a set of strings to avoid duplicates
|
|
153
155
|
self._tool_evaluation_check_list: set[EvaluationMetricName] = set()
|
|
@@ -199,28 +201,39 @@ class _ToolManager(Generic[_ApiMode]):
|
|
|
199
201
|
|
|
200
202
|
registered_tool_names.update(t.name for t in self._mcp_tools)
|
|
201
203
|
|
|
202
|
-
# Build internal tools from configurations
|
|
204
|
+
# Build internal tools from configurations, skipping disabled and failing tools
|
|
203
205
|
self._internal_tools.clear()
|
|
206
|
+
safe_executor = SafeTaskExecutor(logger=self._logger, log_exceptions=False)
|
|
204
207
|
for t in tool_configs:
|
|
205
208
|
if t.name in registered_tool_names:
|
|
206
209
|
continue
|
|
207
210
|
if t.name in OpenAIBuiltInToolName:
|
|
208
211
|
continue
|
|
209
|
-
if
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
t.configuration,
|
|
214
|
-
tool_init_event,
|
|
215
|
-
tool_progress_reporter=self._tool_progress_reporter,
|
|
216
|
-
)
|
|
217
|
-
else:
|
|
212
|
+
if not t.is_enabled:
|
|
213
|
+
self._logger.info("Skipping disabled tool '%s'", t.name)
|
|
214
|
+
continue
|
|
215
|
+
if tool_init_event is None:
|
|
218
216
|
self._logger.info(
|
|
219
217
|
"Skipping internal tool '%s' (requires chat event for initialization)",
|
|
220
218
|
t.name,
|
|
221
219
|
)
|
|
222
220
|
continue
|
|
223
|
-
|
|
221
|
+
result = safe_executor.execute(
|
|
222
|
+
ToolFactory.build_tool_with_settings,
|
|
223
|
+
t.name,
|
|
224
|
+
t,
|
|
225
|
+
t.configuration,
|
|
226
|
+
tool_init_event,
|
|
227
|
+
tool_progress_reporter=self._tool_progress_reporter,
|
|
228
|
+
)
|
|
229
|
+
if result.success:
|
|
230
|
+
self._internal_tools.append(result.unpack())
|
|
231
|
+
else:
|
|
232
|
+
self._logger.warning(
|
|
233
|
+
"Skipping tool '%s' due to initialization failure.",
|
|
234
|
+
t.name,
|
|
235
|
+
exc_info=result.exception,
|
|
236
|
+
)
|
|
224
237
|
|
|
225
238
|
# Combine all types of tools
|
|
226
239
|
self.available_tools = (
|
{unique_toolkit-2026.28.0.dev9.dist-info → unique_toolkit-2026.28.0.dev10.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.28.0.
|
|
3
|
+
Version: 2026.28.0.dev10
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
|
|
6
6
|
Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
|
|
@@ -155,7 +155,7 @@ unique_toolkit/agentic/tools/a2a/evaluation/_utils.py,sha256=-fvd14FdEnQETGq-GRk
|
|
|
155
155
|
unique_toolkit/agentic/tools/a2a/evaluation/config.py,sha256=DLdDXWiXqkRgl-lmoSJxeMiXMScA4PFu2xVuMAqTHyA,2358
|
|
156
156
|
unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py,sha256=dMlBrQBkytJnBXXLl7GZOtFPN49-Cnd5ySdzsJC1QAU,9735
|
|
157
157
|
unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2,sha256=acP1YqD_sCy6DT0V2EIfhQTmaUKeqpeWNJ7RGgceo8I,271
|
|
158
|
-
unique_toolkit/agentic/tools/a2a/manager.py,sha256=
|
|
158
|
+
unique_toolkit/agentic/tools/a2a/manager.py,sha256=D_QOK0hE1aTnCMPwfHmcit1ChJVBBQdQvR2_dp7Xul8,2449
|
|
159
159
|
unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py,sha256=aVtUBPN7kDrqA6Bze34AbqQpcBBqpvfyJG-xF65w7R0,659
|
|
160
160
|
unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py,sha256=QydJqT7jfbspnYdouTdvCxLsSGOGikgBXaVDeI_zKsE,7107
|
|
161
161
|
unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py,sha256=tfH3Bhx67uQtH3fYoUr2Zp45HVw7hV_N-5F_qRwiOas,2727
|
|
@@ -176,7 +176,7 @@ unique_toolkit/agentic/tools/a2a/tool/config.py,sha256=v4rZQU42RjUTC5Md-Cye1rUIW
|
|
|
176
176
|
unique_toolkit/agentic/tools/a2a/tool/service.py,sha256=mRrDYBEMYgk36xqLwtolRh9ravIiXJDTgadietHXb7g,21920
|
|
177
177
|
unique_toolkit/agentic/tools/a2a/tool/test/test_service_utils.py,sha256=x4leRxnGgqD-XyXPkW2z1ekv4Ja7EStXd-24LGadPZA,24858
|
|
178
178
|
unique_toolkit/agentic/tools/agent_chunks_hanlder.py,sha256=eO0g46wZOQlmWqYYVGiszRVe8K2LX-9qW30ZCdGg5eE,1943
|
|
179
|
-
unique_toolkit/agentic/tools/config.py,sha256=
|
|
179
|
+
unique_toolkit/agentic/tools/config.py,sha256=1Vksow2fiHftOIv6d0j7YQIXRIDn4YfM-zpb4LLSd9o,5773
|
|
180
180
|
unique_toolkit/agentic/tools/experimental/__init__.py,sha256=ArEcXHGPtMRNsDnsyo9v1fev0EZWdJ6D7NWdPEFLQKM,956
|
|
181
181
|
unique_toolkit/agentic/tools/experimental/ask_user_tool/__init__.py,sha256=B8srRVw4vMLa4dZE5yoF1Bqpjlv8moh_m-dP2BVRjDc,375
|
|
182
182
|
unique_toolkit/agentic/tools/experimental/ask_user_tool/config.py,sha256=z95ZaxkMvsmjVaUYWWZrvpD21-Us1KNUrMgXzpF4uo4,5923
|
|
@@ -219,7 +219,7 @@ unique_toolkit/agentic/tools/schemas.py,sha256=4BTJ0q7fiAVQ2Uy9kVJnS0rcKeKKaYTkO
|
|
|
219
219
|
unique_toolkit/agentic/tools/test/test_mcp_manager.py,sha256=uWrTmnenQ70s5hrTfMYVah2vDT-jZq4nQmvhutypEK8,19195
|
|
220
220
|
unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py,sha256=XHNezB8itj9KzpQgD0cwRtp2AgRUfUkQsHc3bTyPj6c,15801
|
|
221
221
|
unique_toolkit/agentic/tools/tool.py,sha256=jDGXRD9H0aagibLARWjJaX8DGMFjICeCojgONs40ghY,10721
|
|
222
|
-
unique_toolkit/agentic/tools/tool_manager.py,sha256=
|
|
222
|
+
unique_toolkit/agentic/tools/tool_manager.py,sha256=OMKaEWBrnUST2KD1QtiborRd_CsPQxYVLJlMGyLMuY4,28262
|
|
223
223
|
unique_toolkit/agentic/tools/tool_progress_reporter.py,sha256=GaR0oqDUJZvBB9WCUVYYh0Zvs6U-LMygJCCrqPlitgA,10296
|
|
224
224
|
unique_toolkit/agentic/tools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
225
225
|
unique_toolkit/agentic/tools/utils/source_handling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -406,6 +406,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
|
|
|
406
406
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
407
407
|
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
408
408
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
409
|
-
unique_toolkit-2026.28.0.
|
|
410
|
-
unique_toolkit-2026.28.0.
|
|
411
|
-
unique_toolkit-2026.28.0.
|
|
409
|
+
unique_toolkit-2026.28.0.dev10.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
410
|
+
unique_toolkit-2026.28.0.dev10.dist-info/METADATA,sha256=rCrdzf8NROcJCblXJ5ZuDFASi4vd29nwuhQivxvQ7p4,7428
|
|
411
|
+
unique_toolkit-2026.28.0.dev10.dist-info/RECORD,,
|
|
File without changes
|