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.
@@ -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
- sub_agents.append(
41
- SubAgentTool(
42
- configuration=sub_agent_tool_config,
43
- event=event,
44
- tool_progress_reporter=self._tool_progress_reporter,
45
- name=tool_config.name,
46
- display_name=tool_config.display_name,
47
- response_watcher=self._response_watcher,
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
- """Check the given values for."""
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
- is_mcp_tool = value.get("mcp_source_id", "") != ""
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
- if is_sub_agent_tool:
106
- from unique_toolkit.agentic.tools.a2a import ExtendedSubAgentToolConfig
107
-
108
- config = ExtendedSubAgentToolConfig.model_validate(configuration)
109
- elif isinstance(configuration, dict):
110
- # Local import to avoid circular import at module import time
111
- from unique_toolkit.agentic.tools.factory import ToolFactory
112
-
113
- config = ToolFactory.build_tool_config(
114
- value["name"],
115
- **configuration,
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
- else:
118
- # Check that the type of config matches the tool name
119
- from unique_toolkit.agentic.tools.factory import ToolFactory
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 for tool in self._config.tools if tool.is_exclusive
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 tool_init_event is not None:
210
- built_tool = ToolFactory.build_tool_with_settings(
211
- t.name,
212
- t,
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
- self._internal_tools.append(built_tool)
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 = (
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.28.0.dev9
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=pk06UUXKQdIUY-PyykYiItubBjmIydOaqWvBBDwhMN4,1939
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=LUKSENFvBuOPyllCGEmoO8Q_1HpAw4hZuTxfNmyFHWA,3905
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=FNf_Yfi-Nt8YfGA3A_ZHUStzSuIJiY_rSdRJLVGu6z8,27719
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.dev9.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
410
- unique_toolkit-2026.28.0.dev9.dist-info/METADATA,sha256=qXosj8SbRg2-BNbpviYvDe9xj98kVKyr0_KLcJFvYvs,7427
411
- unique_toolkit-2026.28.0.dev9.dist-info/RECORD,,
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,,