fix(aux): stop probe stubs poisoning the client cache
`_store_cached_client()` refuses an `_AuxProbeClientStub`, but `_get_cached_client()` assigns to `_client_cache` directly and so never reaches that guard. check_fns resolve through this path inside `aux_probe_mode()` during tool-schema assembly, and the cache key carries no probe/runtime distinction — so the stored stub is returned to the next real caller sharing that key, which dies on attribute access with `_AuxProbeClientStub used as a real client (attribute 'chat')`. The `async_mode` field in the cache key is what kept this latent: the probe caches the sync variant, so async consumers (`analyze_image`) miss the entry and build a real client, while sync consumers (`browser_vision`) hit the poisoned one and fail on every call. Observed against a local OpenAI-compatible vision endpoint, where `browser_vision` failed every call with that RuntimeError while `analyze_image` against the same provider worked. Guard the inline store the same way `_store_cached_client()` does, and return the stub to the probe caller without caching it. The existing `test_probe_stub_never_cached` pins the invariant only on `_store_cached_client()`, which is why the unguarded door went unnoticed; the new test exercises `_get_cached_client()` and fails without this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5569,6 +5569,13 @@ def _get_cached_client(
|
||||
provider, model, async_mode, explicit_base_url=base_url, explicit_api_key=effective_api_key,
|
||||
api_mode=api_mode, main_runtime=runtime, is_vision=is_vision, task=task,
|
||||
)
|
||||
if client is not None and _aux_probe_active():
|
||||
# Availability probes answer "resolvable?" and must leave the cache untouched: the
|
||||
# probe stub (bare, or wrapped in a Codex/Anthropic adapter whose leaf is the stub)
|
||||
# shares the runtime key, and a cached one is served to every later caller — the
|
||||
# next probe dies in _compat_model() on stub attribute access, so check_fns flip to
|
||||
# False and vision tools vanish for the process lifetime (#87654).
|
||||
return client, model or default_model
|
||||
if client is not None:
|
||||
with _client_cache_lock:
|
||||
if cache_key not in _client_cache:
|
||||
|
||||
Reference in New Issue
Block a user