Spaces:
Sleeping
Sleeping
| import time | |
| class UpstreamUnavailableError(Exception): | |
| """Raised when the LLM provider fails even after retries (e.g. persistent 503s).""" | |
| pass | |
| def invoke_with_retry(chain, payload: dict, max_retries: int = 3, base_delay: float = 1.5): | |
| """Invokes a langchain chain with exponential backoff on transient failures. | |
| The Hugging Face router + featherless-ai provider can return a 503 "Service | |
| Unavailable" HTML page instead of JSON during cold starts or overload. The | |
| OpenAI-compatible client then throws while trying to parse that as a chat | |
| completion. Retrying with backoff handles the common transient case instead | |
| of failing the whole turn on the first hiccup. | |
| """ | |
| last_error = None | |
| for attempt in range(1, max_retries + 1): | |
| try: | |
| return chain.invoke(payload) | |
| except Exception as e: | |
| last_error = e | |
| is_last = attempt == max_retries | |
| print(f"[llm_utils] attempt {attempt}/{max_retries} failed: {e}") | |
| if not is_last: | |
| time.sleep(base_delay * (2 ** (attempt - 1))) # 1.5s, 3s, 6s... | |
| raise UpstreamUnavailableError(str(last_error)) from last_error |