From ecb7c81b31a66c2e17deeb5a2ab3bec4cd029102 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 3 Aug 2026 05:08:15 +0800 Subject: [PATCH] Fail fast on rejected keys and unknown models --- pageindex/tree_optimize.py | 6 ++++-- pageindex/utils.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pageindex/tree_optimize.py b/pageindex/tree_optimize.py index 9277135f3..04719ccb2 100644 --- a/pageindex/tree_optimize.py +++ b/pageindex/tree_optimize.py @@ -61,8 +61,8 @@ import sys from types import SimpleNamespace -from .utils import (ConfigLoader, _is_openai_model, llm_acompletion, - strip_internal_keys) +from .utils import (ConfigLoader, _is_openai_model, _is_unrecoverable, + llm_acompletion, strip_internal_keys) TRIGGER_PAGES = 5 # only look ahead on nodes larger than this ROUTING_COST = 1 # R(v), in pages @@ -679,6 +679,8 @@ async def expand(structure, pages, lines, args, log, frozen): try: proposed = await propose_children(node, pages, args) except Exception as exc: + if _is_unrecoverable(exc): + raise # every remaining node would fail identically log.append({"op": "expand", "node_id": node.get("node_id"), "decision": "error", "attempt": attempts, "detail": f"{type(exc).__name__}: {exc}"}) diff --git a/pageindex/utils.py b/pageindex/utils.py index b41cb919e..92fc46d85 100644 --- a/pageindex/utils.py +++ b/pageindex/utils.py @@ -43,6 +43,17 @@ def _is_openai_model(model): _openai_async_client = None +# Misconfiguration: no retry can fix a rejected key or a model that does not +# exist, and every later call fails the same way. Deliberately not 400, which +# also carries context_length_exceeded, a per-prompt failure the caller absorbs +# today. An unknown status is a transport failure and stays retryable. +_UNRECOVERABLE_STATUS = frozenset({401, 403, 404}) + + +def _is_unrecoverable(exc: Exception) -> bool: + return getattr(exc, "status_code", None) in _UNRECOVERABLE_STATUS + + def llm_completion(model, prompt, chat_history=None, return_finish_reason=False): use_openai_sdk = _is_openai_model(model) if model: @@ -76,6 +87,8 @@ def llm_completion(model, prompt, chat_history=None, return_finish_reason=False) return content, finish_reason return content except Exception as e: + if _is_unrecoverable(e): + raise print('************* Retrying *************') logging.error(f"Error: {e}") if i < max_retries - 1: @@ -116,6 +129,8 @@ async def llm_acompletion(model, prompt): ) return response.choices[0].message.content except Exception as e: + if _is_unrecoverable(e): + raise print('************* Retrying *************') logging.error(f"Error: {e}") if i < max_retries - 1: