|
29 | 29 |
|
30 | 30 | from agent.codex_responses_adapter import _summarize_user_message_for_log |
31 | 31 | from agent.display import KawaiiSpinner |
32 | | -from agent.error_classifier import FailoverReason, classify_api_error |
| 32 | +from agent.error_classifier import ( |
| 33 | + FailoverReason, |
| 34 | + classify_api_error, |
| 35 | + next_untried_model_variant, |
| 36 | +) |
33 | 37 | from agent.iteration_budget import IterationBudget |
34 | 38 | from agent.turn_context import build_turn_context |
35 | 39 | from agent.turn_retry_state import TurnRetryState |
@@ -3253,6 +3257,41 @@ def _perform_api_call(next_api_kwargs): |
3253 | 3257 | ) and not is_context_length_error |
3254 | 3258 |
|
3255 | 3259 | if is_client_error: |
| 3260 | + # ── Model 404 self-correct (#348) — try suffix variants of |
| 3261 | + # the SAME model before switching providers. A bare model |
| 3262 | + # id (no ``:suffix``) that 404s often resolves once |
| 3263 | + # suffixed (``glm-4`` → ``glm-4:cloud``). Try each untried |
| 3264 | + # variant once; only on exhaustion do we fall through to |
| 3265 | + # the provider fallback / abort below. Variants are derived |
| 3266 | + # from the ORIGINAL id so a mutated ``agent.model`` from a |
| 3267 | + # prior attempt doesn't shrink the candidate set. |
| 3268 | + if classified.reason == FailoverReason.model_not_found: |
| 3269 | + _mnf_base = getattr(agent, "_model_404_base", None) |
| 3270 | + _mnf_tried = getattr(agent, "_model_404_tried", None) |
| 3271 | + # Fresh model (first 404, or the user switched models |
| 3272 | + # mid-session) → restart the variant search from it so a |
| 3273 | + # stale base never narrows the candidates. |
| 3274 | + if _mnf_tried is None or (agent.model or "") not in _mnf_tried: |
| 3275 | + _mnf_base = agent.model or "" |
| 3276 | + _mnf_tried = {_mnf_base} |
| 3277 | + _variant = next_untried_model_variant(_mnf_base, _mnf_tried) |
| 3278 | + if _variant is not None: |
| 3279 | + _mnf_tried.add(_variant) |
| 3280 | + agent._model_404_base = _mnf_base |
| 3281 | + agent._model_404_tried = _mnf_tried |
| 3282 | + agent._buffer_status( |
| 3283 | + f"⚠️ Model '{agent.model}' not found — trying '{_variant}'..." |
| 3284 | + ) |
| 3285 | + agent._vprint( |
| 3286 | + f"{agent.log_prefix} 💡 Model not found; self-correcting " |
| 3287 | + f"to suffixed variant '{_variant}'.", |
| 3288 | + force=True, |
| 3289 | + ) |
| 3290 | + agent.model = _variant |
| 3291 | + retry_count = 0 |
| 3292 | + compression_attempts = 0 |
| 3293 | + _retry.primary_recovery_attempted = False |
| 3294 | + continue |
3256 | 3295 | # Try fallback before aborting — a different provider may |
3257 | 3296 | # not have the same issue (rate limit, auth, etc.). Only |
3258 | 3297 | # announce the attempt when a fallback chain actually |
|
0 commit comments