From e7f555fff1e0dd507547fa4ec34af15ef5e9e1f1 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 7 Jul 2026 09:25:25 -0500 Subject: [PATCH] reactor.py: fix Ollama silent-empty-return bug (no error checking on API response), bump Ollama timeout 30s->90s (cold model loads took 15s+ alone), add exception type to fallback log lines --- deploy/reactor.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/deploy/reactor.py b/deploy/reactor.py index 47b3502..556b2ad 100644 --- a/deploy/reactor.py +++ b/deploy/reactor.py @@ -144,12 +144,18 @@ async def llm_call(messages: list, provider: str = "claude", system: str = "") - for p in order: try: if p == "claude" and CLAUDE_API_KEY: - return await _claude_call(messages, system) + result = await _claude_call(messages, system) elif p == "groq" and GROQ_API_KEY: - return await _groq_call(messages, system) + result = await _groq_call(messages, system) elif p == "ollama": - return await _ollama_call(messages, system) + result = await _ollama_call(messages, system) + else: + continue + if not result or not result.strip(): + raise RuntimeError(f"{p} returned empty content") + return result except Exception as e: + log.warning(f"[LLM] Provider {p} failed: {type(e).__name__}: {e}") last_err = e continue raise RuntimeError(f"All LLM providers failed (last error: {last_err})") @@ -183,8 +189,14 @@ async def _ollama_call(messages: list, system: str = "") -> str: prompt = (system + "\n\n" if system else "") + "\n".join(f"{m['role'].upper()}: {m['content']}" for m in messages) async with aiohttp.ClientSession() as session: async with session.post(f"{OLLAMA_HOST}/api/generate", json={"model": OLLAMA_MODEL, "prompt": prompt, "stream": False}, - timeout=aiohttp.ClientTimeout(total=30)) as resp: + timeout=aiohttp.ClientTimeout(total=90)) as resp: # bumped from 30s 2026-07-07: cold model loads on this CPU-only host alone took 15s+ in testing, leaving no room for actual generation data = await resp.json() + # Fixed 2026-07-07: this had no error checking at all — if the model wasn't + # pulled (or any other Ollama-side error), the API returns {"error": "..."} + # with no "response" key, and this silently returned "" instead of raising, + # which fooled llm_call()'s fallback into treating it as a real success. + if "error" in data: + raise RuntimeError(f"Ollama error: {data['error']}") return data.get("response", "")