Skip to content

fix(llm): set HTTP timeout on Gemini clients#903

Open
PraveenKumarSridhar wants to merge 3 commits into
plastic-labs:mainfrom
PraveenKumarSridhar:fix/785-gemini-http-timeout
Open

fix(llm): set HTTP timeout on Gemini clients#903
PraveenKumarSridhar wants to merge 3 commits into
plastic-labs:mainfrom
PraveenKumarSridhar:fix/785-gemini-http-timeout

Conversation

@PraveenKumarSridhar

@PraveenKumarSridhar PraveenKumarSridhar commented Jul 12, 2026

Copy link
Copy Markdown

Summary

  • Set a default 10-minute HTTP timeout on every Gemini client built in src/llm/registry.py (default factory, override factory, and module-level CLIENTS["gemini"]).
  • Add tests/llm/test_registry.py with regression coverage on all three Gemini construction sites plus Anthropic-baseline locks.

Closes #785.

Why

A stalled Gemini socket can hang the deriver worker indefinitely because the Gemini client is built without an HTTP timeout. All deriver workers share one uvloop event loop, so a single hung call wedges the entire process — representation, dreamer, and reconciler all stop. The Anthropic client already passes timeout=600.0; this aligns the Gemini client with that behavior.

google.genai.HttpOptions.timeout is an int in milliseconds, so the default is 600_000 ms (10 min) — same wall-clock budget as Anthropic.

Testing

  • .venv/bin/pytest tests/llm/ -q → 231 passed
  • .venv/bin/pytest tests/llm/test_registry.py -q → 7/7 passed (5 red→green for the new Gemini-timeout behavior, 2 baseline Anthropic locks)
  • .venv/bin/ruff check and .venv/bin/basedpyright on the changed files: no new errors

Broader Postgres-backed deriver queue tests were not run locally (env-level Postgres auth blocker; same gap as PR #901).

Developed with AI assistance; reproduced (unit test) and reviewed locally before submission.

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of Gemini and Anthropic provider connections by consistently applying configured request timeouts.
    • Preserved custom service endpoints while ensuring timeout settings remain active.
    • Gemini embedding requests now retain timeout protection even when no custom endpoint is configured.
  • Tests

    • Added coverage validating timeout behavior across standard and override provider configurations.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Gemini and Anthropic clients now use explicit, centralized HTTP timeout settings. Gemini embedding and registry construction preserve optional base URLs while always applying the timeout, with tests covering standard, override, and missing-URL cases.

Changes

Provider timeout configuration

Layer / File(s) Summary
Embedding client timeout
src/embedding_client.py, tests/llm/test_embedding_client.py
Gemini embedding clients apply a 600,000 ms timeout and retain configured or absent base URLs.
Registry timeout wiring
src/llm/registry.py
Gemini and Anthropic timeout constants are applied across standard, override, and module-level client construction.
Registry timeout validation
tests/llm/test_registry.py
Tests verify timeout propagation, base URL preservation, cache isolation, and helper behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit with a timeout tune,
Gemini waits no more till noon.
Anthropic’s clock is steady too,
Base URLs keep their proper hue.
Hops and tests now bound the queue!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The registry updates cover all three Gemini constructors in #785 and apply the required timeout.
Out of Scope Changes check ✅ Passed The changes stay focused on Gemini/Anthropic timeout wiring and related regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding HTTP timeouts to Gemini clients.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…labs#785)

Same wedge-class failure as the LLM client: a stalled Gemini embedding
socket hangs the in-process reconciler, which shares the deriver worker's
uvloop event loop. Apply the same 10-minute timeout here, in lockstep
with src/llm/registry.py's _build_gemini_http_options.
@PraveenKumarSridhar
PraveenKumarSridhar marked this pull request as ready for review July 12, 2026 23:52
@PraveenKumarSridhar

Copy link
Copy Markdown
Author

Expanded scope after independent code review: a Gemini embedding client in src/embedding_client.py had the same wedge-class bug (in-process reconciler shares the deriver's uvloop event loop). Now covered with the same 10-min timeout. src/ has no other genai.Client construction sites — verified. Local: 232/232 tests/llm/ pass, ruff + basedpyright clean.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/llm/test_registry.py (1)

25-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Initialize settings before importing the registry.

src.config and src.llm.registry are imported before this autouse fixture runs, so these environment variables cannot affect settings or the module-level CLIENTS initialization. Set them before collection (for example, in a pytest startup hook), or reload config and registry in an isolated test; then assert CLIENTS["gemini"] receives the timeout too.

Based on PR objectives: all three Gemini constructors require regression coverage.

🤖 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 `@tests/llm/test_registry.py` around lines 25 - 31, Move the environment setup
from the _patch_settings fixture to pytest startup or another pre-import
mechanism so src.config and src.llm.registry read the test values before
module-level CLIENTS initialization. Ensure registry tests cover all three
Gemini constructors and assert CLIENTS["gemini"] receives the configured
timeout.
🤖 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 `@tests/llm/test_registry.py`:
- Around line 25-31: Move the environment setup from the _patch_settings fixture
to pytest startup or another pre-import mechanism so src.config and
src.llm.registry read the test values before module-level CLIENTS
initialization. Ensure registry tests cover all three Gemini constructors and
assert CLIENTS["gemini"] receives the configured timeout.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2ca830af-c3ae-451e-96af-0f437671a65a

📥 Commits

Reviewing files that changed from the base of the PR and between de1b410 and 94b8aaa.

📒 Files selected for processing (4)
  • src/embedding_client.py
  • src/llm/registry.py
  • tests/llm/test_embedding_client.py
  • tests/llm/test_registry.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Deriver hangs indefinitely on a stalled Gemini call: Gemini client built without an HTTP timeout

1 participant