fix(llm): stop paging on exhausted-retry LLM failures; retry hard timeouts; per-model timeout floors - #142
Conversation
…eouts; per-model timeout floors - run_tool_loop now catches LiteLLMClientError ahead of the generic handler and logs at WARNING: the client already exhausted its retries and fallbacks, so this is a known failure mode, not a code bug. Same finished_reason='error' result, so downstream behavior is unchanged. - A hard timeout kills the litellm subprocess before num_retries can fire, so the client now owes one explicit retry at that level. - Add per-model timeout floors (minimax/MiniMax-M3 -> 240s): reasoning models routinely exceed the default 120s on large extraction contexts. Floors never lower a configured timeout, and explicit per-call timeouts bypass them.
📝 WalkthroughWalkthroughThis PR introduces timeout resilience improvements to LLM request handling and error recovery. It adds per-model timeout floors to prevent providers that routinely exceed defaults from blocking, implements client-level retry on hard timeouts, adds graceful error handling in the tool loop, and updates configuration default expectations. ChangesLLM Timeout Resilience & Error Handling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
reflexio/server/llm/tools.py (1)
581-582: 💤 Low valueComment is slightly misleading about "type-only dependency".
The comment states the local import keeps
litellm_clienta "type-only dependency," but this is a runtime import needed to catch the exception. The intent appears to be minimizing runtime coupling by importing only in the native tool-calling path, but "type-only" specifically refers to imports underif TYPE_CHECKING:. Consider rephrasing to something like "Local import defers the runtime dependency to the native tool-calling path only."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@reflexio/server/llm/tools.py` around lines 581 - 582, The comment near the local import of LiteLLMClientError is misleading—it's a runtime import, not a type-only import—so update the comment to reflect that this local import defers the runtime dependency to the native tool-calling path instead of claiming it is "type-only"; locate the import of LiteLLMClientError in reflexio.server.llm.tools and replace the comment text to something like "Local import defers the runtime dependency to the native tool-calling path only" (or equivalent wording) so it accurately describes intent without implying TYPE_CHECKING semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@reflexio/server/llm/tools.py`:
- Around line 581-582: The comment near the local import of LiteLLMClientError
is misleading—it's a runtime import, not a type-only import—so update the
comment to reflect that this local import defers the runtime dependency to the
native tool-calling path instead of claiming it is "type-only"; locate the
import of LiteLLMClientError in reflexio.server.llm.tools and replace the
comment text to something like "Local import defers the runtime dependency to
the native tool-calling path only" (or equivalent wording) so it accurately
describes intent without implying TYPE_CHECKING semantics.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: b05c8763-52df-48fa-87ab-db470b279500
📒 Files selected for processing (12)
reflexio/cli/commands/setup_cmd.pyreflexio/server/llm/litellm_client.pyreflexio/server/llm/llm_utils.pyreflexio/server/llm/tools.pyreflexio/server/services/extractor_interaction_utils.pytests/models/test_retrieval_floor_config.pytests/server/llm/test_litellm_client_unit.pytests/server/llm/test_model_defaults.pytests/server/llm/test_tools.pytests/server/services/playbook/test_playbook_aggregator.pytests/server/services/test_base_generation_service.pytests/server/services/test_extractor_interaction_utils.py
Summary
Three LLM-reliability fixes that stop expected failure modes from surfacing as Sentry errors and reduce spurious hard-timeout failures:
run_tool_loopnow catchesLiteLLMClientErrorahead of the genericexcept Exceptionhandler and logs it at WARNING (event=tool_loop_llm_error, no traceback). By the time this exception reaches the loop, the client has already exhausted its retries and fallbacks — it is a known failure mode (provider errors, timeouts), not a code bug. The result is unchanged:finished_reason="error"with the accumulated messages, so downstream behavior is identical. The generic handler keepslogger.exceptionfor truly unexpected errors.num_retriesnever gets a chance to fire. The client now performs one explicit retry at its own level afterLLMHardTimeoutError, covering transient provider hangs._MODEL_TIMEOUT_FLOOR_SECONDSraises the effective timeout tomax(configured, floor)(currentlyminimax/MiniMax-M3→ 240s). Floors never lower a configured timeout, and an explicit per-calltimeoutkwarg bypasses them entirely.Also includes minor cleanups: ruff formatting in
setup_cmd.py/llm_utils.pyand a docstring correction inextractor_interaction_utils.py(defaults come fromconfig_schemaconstants, not hardcoded values).Changes
reflexio/server/llm/tools.py— catchLiteLLMClientErrorin the native tool loop, log at WARNING, return gracefulToolLoopResultreflexio/server/llm/litellm_client.py— one explicit retry afterLLMHardTimeoutError; per-model timeout floor via_effective_timeout_for_modelreflexio/cli/commands/setup_cmd.py,reflexio/server/llm/llm_utils.py— formatting onlyreflexio/server/services/extractor_interaction_utils.py— docstring fixLiteLLMClientErrorproduces no ERROR-level records, hard-timeout retry coverage, timeout-floor coverage, and test updates for constants-based defaultsTest Plan
ruff check/ruff formatclean on all changed filespyrightintroduces no new errors vs base (identical error distribution before/after)Summary by CodeRabbit
New Features
Bug Fixes
Configuration Changes