fix(agent): keep MoA verdicts fallback-free; nest the gated fallback under one if
Two CI regressions from the previous commit, both mine: - `_moa_special_cases` was switched to `_ABORT_FALLBACK`, which flips `should_fallback` to True for the MoA adapter-shape and missing-preset verdicts. #55933 made those deliberately NOT fall back (a fallback would silently replace the MoA route with a single model); restore `retryable=False` only. The gate in `settle_unrecovered_error` now honours that for real: on main these verdicts never reached the fallback branch because the flag was never consulted. - `tests/agent/test_prompt_cache_ttl_propagation.py` pins every `_try_activate_fallback` reference to a direct `if agent._try_activate_fallback():` site (#84733 restart discipline); `fallback_allowed and agent._try_activate_fallback()` broke that shape. Nest the two calls under one `if should_fallback or local_validation:` block instead.
This commit is contained in:
@@ -609,10 +609,10 @@ def _moa_special_cases(c: _Ctx) -> Optional[Verdict]:
|
||||
# Local MoA streaming adapter-shape bugs are not a provider outage; falling
|
||||
# back would silently replace the MoA route with a single model (#55933).
|
||||
if c.provider_slug == "moa" and any(s in str(c.error) for s in _MOA_ADAPTER_SHAPE_BUGS):
|
||||
return _v(_R.format_error, **_ABORT_FALLBACK)
|
||||
return _v(_R.format_error, retryable=False)
|
||||
# Persisted MoA preset name that was renamed/deleted — deterministic config error.
|
||||
from agent.errors import MoAPresetNotFoundError
|
||||
return _v(_R.model_not_found, **_ABORT_FALLBACK) if isinstance(c.error, MoAPresetNotFoundError) else None
|
||||
return _v(_R.model_not_found, retryable=False) if isinstance(c.error, MoAPresetNotFoundError) else None
|
||||
|
||||
|
||||
def _by_error_code(c: _Ctx) -> Optional[Verdict]:
|
||||
|
||||
@@ -308,18 +308,18 @@ def settle_unrecovered_error(
|
||||
# ``should_fallback=False`` marks a deterministic failure no other provider can fix (the
|
||||
# model's own malformed tool-call JSON, #12770): skip the cascade. Local validation errors
|
||||
# carry no classifier verdict and keep their historical fallback.
|
||||
fallback_allowed = classified.should_fallback or is_local_validation_error
|
||||
# Announce the fallback only when a chain exists, else "trying fallback..." lies
|
||||
# before a silent abort.
|
||||
if fallback_allowed and agent._has_pending_fallback():
|
||||
_label = _NONRETRYABLE_LABELS.get(classified.reason, f"Non-retryable error (HTTP {status_code})")
|
||||
agent._buffer_status(f"⚠️ {_label} — trying fallback...")
|
||||
if fallback_allowed and agent._try_activate_fallback():
|
||||
# Direct ``return _verdict("break")`` is load-bearing: the restart handler
|
||||
# re-runs the pre-API preflight against the fallback's context window.
|
||||
active_system_prompt = _arm_fallback_restart(agent, api_messages, active_system_prompt, _retry)
|
||||
retry_count = compression_attempts = 0
|
||||
return _verdict("break")
|
||||
if classified.should_fallback or is_local_validation_error:
|
||||
# Announce the fallback only when a chain exists, else "trying fallback..." lies
|
||||
# before a silent abort.
|
||||
if agent._has_pending_fallback():
|
||||
_label = _NONRETRYABLE_LABELS.get(classified.reason, f"Non-retryable error (HTTP {status_code})")
|
||||
agent._buffer_status(f"⚠️ {_label} — trying fallback...")
|
||||
if agent._try_activate_fallback():
|
||||
# Direct ``return _verdict("break")`` is load-bearing: the restart handler
|
||||
# re-runs the pre-API preflight against the fallback's context window.
|
||||
active_system_prompt = _arm_fallback_restart(agent, api_messages, active_system_prompt, _retry)
|
||||
retry_count = compression_attempts = 0
|
||||
return _verdict("break")
|
||||
return _verdict("return", nonretryable_client_error_result(
|
||||
agent, api_error, classified, status_code=status_code, api_kwargs=api_kwargs,
|
||||
api_messages=api_messages, messages=messages, conversation_history=conversation_history,
|
||||
|
||||
Reference in New Issue
Block a user