Retry Ollama 500 errors in optimistic stream with backoff

With optimistic streaming, intent (qwen2.5:1.5b) and the main stream
(qwen3:latest) start concurrently. When both models are cold-loading,
Ollama returns 500 for both simultaneously. The intent 500 was already
handled silently in classify_intent; the stream 500 now retries up to
2 times (3s then 6s delay) before propagating as an error. 500s only
occur on the first cold-load pair — subsequent requests hit warm models.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 23:15:05 -05:00
parent 24d9a01554
commit fc7b2e7305
@@ -11,6 +11,8 @@ import re
import time import time
from collections.abc import AsyncGenerator from collections.abc import AsyncGenerator
import httpx
from sqlalchemy import update from sqlalchemy import update
from fabledassistant.config import Config from fabledassistant.config import Config
@@ -291,13 +293,30 @@ async def run_generation(
stream_queue: asyncio.Queue[ChatChunk | BaseException | None] = asyncio.Queue(maxsize=256) stream_queue: asyncio.Queue[ChatChunk | BaseException | None] = asyncio.Queue(maxsize=256)
async def _fill_queue() -> None: async def _fill_queue() -> None:
try: # Retry on Ollama 500 (model cold-loading race) up to 2 times.
async for c in stream_chat_with_tools(messages, model, tools=tools, think=think): last_exc: BaseException | None = None
await stream_queue.put(c) for attempt in range(3):
except BaseException as exc: if attempt > 0:
await stream_queue.put(exc) wait = 3.0 * attempt
finally: logger.warning(
await stream_queue.put(None) "Ollama stream 500 (attempt %d/3), retrying in %.0fs", attempt, wait
)
await asyncio.sleep(wait)
try:
async for c in stream_chat_with_tools(messages, model, tools=tools, think=think):
await stream_queue.put(c)
last_exc = None
break
except httpx.HTTPStatusError as exc:
last_exc = exc
if exc.response.status_code != 500:
break # non-500 errors are not retryable
except BaseException as exc:
last_exc = exc
break
if last_exc is not None:
await stream_queue.put(last_exc)
await stream_queue.put(None)
stream_fill_task = asyncio.create_task(_fill_queue()) stream_fill_task = asyncio.create_task(_fill_queue())
buf.append_event("status", {"status": "Generating response..."}) buf.append_event("status", {"status": "Generating response..."})