unique_toolkit 2026.30.0.dev3__py3-none-any.whl → 2026.30.0.dev4__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.
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections.abc import Sequence
2
3
  from enum import StrEnum
3
4
  from typing import Annotated, Any, Generic
4
5
 
@@ -98,6 +99,16 @@ class ToolBuildConfig(BaseModel, Generic[T]):
98
99
 
99
100
  is_enabled: bool = Field(default=True)
100
101
 
102
+ config_error: str | None = Field(
103
+ default=None,
104
+ exclude=True,
105
+ description=(
106
+ "Set when this tool's stored configuration failed to resolve and was "
107
+ "demoted to a disabled BaseToolConfig fallback. None for tools that are "
108
+ "valid, including tools that are deliberately disabled."
109
+ ),
110
+ )
111
+
101
112
  @model_validator(mode="before")
102
113
  def initialize_config_based_on_tool_name(
103
114
  cls,
@@ -115,6 +126,16 @@ class ToolBuildConfig(BaseModel, Generic[T]):
115
126
  if not isinstance(value, dict):
116
127
  return value
117
128
 
129
+ # config_error must only ever be set by this validator's own demotion
130
+ # path below. Since this is a plain field on a model with
131
+ # populate_by_name + a camelCase alias generator, an externally
132
+ # supplied config_error/configError (e.g. a stray key on a stored
133
+ # payload) would otherwise pass straight through untouched on every
134
+ # non-demotion return path and make a perfectly healthy tool look
135
+ # misconfigured to collect_tool_configuration_errors.
136
+ value.pop("config_error", None)
137
+ value.pop("configError", None)
138
+
118
139
  is_mcp_tool = _is_mcp_tool_payload(value)
119
140
  mcp_configuration = value.get("configuration", {})
120
141
 
@@ -168,8 +189,23 @@ class ToolBuildConfig(BaseModel, Generic[T]):
168
189
  )
169
190
  value["is_enabled"] = False
170
191
  value["isEnabled"] = False
192
+ value["config_error"] = f"Tool '{tool_name}' has invalid configuration."
171
193
  _ensure_base_tool_config(value)
172
194
  return value
173
195
 
174
196
  value["configuration"] = config
175
197
  return value
198
+
199
+
200
+ def collect_tool_configuration_errors(
201
+ tools: Sequence["ToolBuildConfig[Any]"],
202
+ ) -> list[str]:
203
+ """Return the config_error message for every tool demoted due to an invalid config.
204
+
205
+ `initialize_config_based_on_tool_name` never raises: a tool whose stored
206
+ configuration is invalid is silently disabled with a BaseToolConfig fallback so
207
+ that one broken tool cannot take down the whole space. Callers that need to
208
+ surface these failures instead of ignoring them (e.g. to fail a chat request the
209
+ way an invalid configuration used to) can use this helper to recover the list.
210
+ """
211
+ return [tool.config_error for tool in tools if tool.config_error]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.30.0.dev3
3
+ Version: 2026.30.0.dev4
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>
@@ -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=5jOtXSU9oggG1dVUd7PAb3hd_-MLyGnSR5_lUQvYPfA,22160
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=g_NKV94pq6cZ8nH-C69_BNScyQ-tjRjzYE8IKKqqDag,5726
179
+ unique_toolkit/agentic/tools/config.py,sha256=AOtBn2tL1rIoPPzl1csAHLD5aetYqPL-jAqkIoSBxyE,7468
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
@@ -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.30.0.dev3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
410
- unique_toolkit-2026.30.0.dev3.dist-info/METADATA,sha256=Um_hksszph6NyBJB57I6C7BbcJ60MK4xteal7vJNNm0,7426
411
- unique_toolkit-2026.30.0.dev3.dist-info/RECORD,,
409
+ unique_toolkit-2026.30.0.dev4.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
410
+ unique_toolkit-2026.30.0.dev4.dist-info/METADATA,sha256=4iN85BIBq_wHZenyR9KfKIvdQ9JF699LsQ_PVG3HMFY,7426
411
+ unique_toolkit-2026.30.0.dev4.dist-info/RECORD,,