fix(tools): stop check_fns swallowing resolver crashes into "returned False"
check_vision_requirements (and five siblings: browser_vision, image/video generation, x_search, browser_vault) wrapped their whole probe in `except Exception: return False`. The registry then logged "returned False", indistinguishable from an unconfigured backend, and the only diagnostic for a crashed resolver was gone (#87950: named custom provider lookup failing in a long-lived multi-profile process, reported as vision tools silently vanishing). The registry owns the verdict: _run_check_fn_uncached and _check_fn_cached both catch, log with traceback, and return False. Let the exception reach them. Behaviour for the model is unchanged (tool hidden either way); agent.log now says why.
This commit is contained in:
@@ -540,6 +540,18 @@ class TestVisionRequirements:
|
||||
|
||||
assert check_vision_requirements() is True
|
||||
|
||||
def test_resolver_crash_propagates_instead_of_reading_as_unconfigured(self, monkeypatch):
|
||||
"""The gate must not swallow resolver exceptions into False: the registry owns the
|
||||
verdict and logs the traceback, so users can tell "misconfigured" from "crashed" (#87950)."""
|
||||
import agent.auxiliary_client as aux
|
||||
|
||||
def _boom(**kw):
|
||||
raise RuntimeError("named custom provider lookup failed")
|
||||
|
||||
monkeypatch.setattr(aux, "resolve_vision_provider_client", _boom)
|
||||
with pytest.raises(RuntimeError, match="lookup failed"):
|
||||
check_vision_requirements()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Local path forms: tilde expansion and file:// URIs
|
||||
|
||||
@@ -335,8 +335,5 @@ def check_browser_vision_requirements() -> bool:
|
||||
"""
|
||||
if not check_browser_requirements():
|
||||
return False
|
||||
try:
|
||||
from tools.vision_tools import check_vision_requirements
|
||||
except ImportError:
|
||||
return False
|
||||
from tools.vision_tools import check_vision_requirements
|
||||
return check_vision_requirements()
|
||||
|
||||
@@ -42,14 +42,11 @@ def _check_vault_available() -> bool:
|
||||
"""Schema-gate: the vault tools ride with the browser. An empty vault still needs
|
||||
browser_vault_save_login so the agent can offer to remember a login the first time it meets a
|
||||
form; hiding the tools until an item exists meant nobody ever discovered the feature."""
|
||||
try:
|
||||
from tools.browser_tool_install import check_browser_requirements
|
||||
from tools.browser_use_cli import is_browser_use_cli_mode
|
||||
# check_browser_requirements() is False by design in Browser Use mode (browser_exec replaces the
|
||||
# built-in surface); the vault serves both stacks.
|
||||
return bool(is_browser_use_cli_mode() or check_browser_requirements())
|
||||
except Exception:
|
||||
return False
|
||||
from tools.browser_tool_install import check_browser_requirements
|
||||
from tools.browser_use_cli import is_browser_use_cli_mode
|
||||
# check_browser_requirements() is False by design in Browser Use mode (browser_exec replaces the
|
||||
# built-in surface); the vault serves both stacks.
|
||||
return bool(is_browser_use_cli_mode() or check_browser_requirements())
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -540,11 +540,8 @@ def check_image_generation_requirements() -> bool:
|
||||
if configured is None:
|
||||
return False
|
||||
# Probe only the selected plugin: a cloud key alone must not opt a user into a paid backend.
|
||||
try:
|
||||
provider = _get_plugin_provider(configured)
|
||||
return bool(provider and provider.is_available())
|
||||
except Exception:
|
||||
return False
|
||||
provider = _get_plugin_provider(configured)
|
||||
return bool(provider and provider.is_available())
|
||||
|
||||
|
||||
# --- Registry ---
|
||||
|
||||
@@ -104,11 +104,8 @@ def _discovered_registry():
|
||||
|
||||
def check_video_generation_requirements() -> bool:
|
||||
"""True when at least one registered provider reports available."""
|
||||
try:
|
||||
registry_mod, _ = _discovered_registry()
|
||||
return any(_provider_call(p, "is_available", False) for p in registry_mod.list_providers())
|
||||
except Exception:
|
||||
return False
|
||||
registry_mod, _ = _discovered_registry()
|
||||
return any(_provider_call(p, "is_available", False) for p in registry_mod.list_providers())
|
||||
|
||||
|
||||
def _resolve_active_provider():
|
||||
|
||||
@@ -806,14 +806,13 @@ def check_vision_requirements() -> bool:
|
||||
|
||||
See #31179.
|
||||
"""
|
||||
try:
|
||||
from agent.auxiliary_client import aux_probe_mode, resolve_vision_provider_client
|
||||
with aux_probe_mode():
|
||||
return any(
|
||||
resolve_vision_provider_client(**kw)[1] is not None for kw in ({}, {"provider": "auto"})
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
from agent.auxiliary_client import aux_probe_mode, resolve_vision_provider_client
|
||||
# No blanket except: a resolver crash must reach the registry, which logs it with a
|
||||
# traceback; a swallowed exception reads as "no vision backend configured" (#87950).
|
||||
with aux_probe_mode():
|
||||
return any(
|
||||
resolve_vision_provider_client(**kw)[1] is not None for kw in ({}, {"provider": "auto"})
|
||||
)
|
||||
|
||||
|
||||
from tools.registry import registry, tool_error
|
||||
|
||||
@@ -78,10 +78,7 @@ def _resolve_xai_bearer() -> Tuple[str, str, str]:
|
||||
|
||||
def check_x_search_requirements() -> bool:
|
||||
"""True when xAI credentials resolve to a non-empty bearer (OAuth auto-refreshed)."""
|
||||
try:
|
||||
return bool(str(resolve_xai_http_credentials().get("api_key") or "").strip())
|
||||
except Exception:
|
||||
return False
|
||||
return bool(str(resolve_xai_http_credentials().get("api_key") or "").strip())
|
||||
|
||||
|
||||
def _normalize_handles(handles: Optional[List[str]], field_name: str) -> List[str]:
|
||||
|
||||
Reference in New Issue
Block a user