Add optional client-side rate limiting (max_rpm) and preserve partial results on parallel failure - #520
Add optional client-side rate limiting (max_rpm) and preserve partial results on parallel failure#520ojassharma7 wants to merge 3 commits into
Conversation
|
Your branch is 1 commits behind git fetch origin main
git merge origin/main
git pushNote: Enable "Allow edits by maintainers" to allow automatic updates. |
4b318b0 to
7e535ee
Compare
|
Rebased onto Worth flagging what the conflict actually was, since it is not visible in the diff. Textually it was just two additions landing in the same place ( Failures are now collected uniformly and the pool is allowed to drain. What gets raised then depends on whether anything survived:
That keeps both behaviours: the no-text diagnostic from #534 propagates intact when it is the whole story, and completed work survives when there is some. Verification on the rebased tree: The full suite is differential-clean against |
|
Your branch is 3 commits behind git fetch origin main
git merge origin/main
git pushNote: Enable "Allow edits by maintainers" to allow automatic updates. |
…n failure Addresses the two remaining items of problem google#2 in google#358 (Gemini 429 handling); the exponential backoff it also asked for already landed in 3aab86c. max_rpm: a new optional GeminiLanguageModel parameter that spaces real-time requests client-side so at most N start per minute, shared across workers via a thread-safe leaky bucket. It prevents 429 RESOURCE_EXHAUSTED on quota-limited tiers (e.g. the free tier's 15 RPM) rather than only retrying after the fact. Defaults to 0 (disabled), so existing behavior is unchanged. Not applied to the Batch API. Partial results: the parallel infer() path raised on the first failed chunk, abandoning the still-running futures and discarding every chunk that had already succeeded. It now drains the pool, then raises once with the completed work attached to InferenceRuntimeError (partial_results, aligned to the input prompts, plus failed_indices), so a caller can recover instead of losing a whole batch. The all-or-nothing raise is preserved, so annotation's zip(batch, outputs) alignment is unaffected. Adds tests for the limiter's spacing, max_rpm wiring/validation, and partial preservation across the success, single-failure and multi-failure cases.
fake_time.sleep.side_effect = lambda s: sleeps.append(s) -> sleeps.append. No behavior change; CI's lint-tests job runs a stricter pylintrc than the root one I checked before the first push.
…ogle#534 Rebasing onto main surfaced a behavioural conflict that merged cleanly as text. google#534 added an `except InferenceRuntimeError: raise` pass-through to the parallel result loop, which returns the provider's own diagnostic unwrapped. That pass-through runs before the draining logic here, so once _process_single_prompt started wrapping failures as InferenceRuntimeError, every parallel failure short-circuited and the completed chunks were discarded again, which is the exact behaviour this branch set out to fix. Failures are now collected uniformly and the pool is allowed to drain. Which error is raised then depends on whether there is anything to preserve: - Nothing completed: re-raise the provider's own InferenceRuntimeError unchanged, so a blocked prompt or an exhausted token budget still reports its own reason rather than being buried a level down, with the drained bookkeeping attached to it. - Some chunks completed: raise the aggregate error carrying partial_results and failed_indices, with the first underlying error kept as `original` and quoted in the message. Both test suites pass together: the no-text tests added by google#534 and the partial-result tests on this branch.
7e535ee to
50c7e76
Compare
|
Thanks for putting this together. Gemini already has configurable retries with backoff for transient API errors, and custom providers can handle more specialized retry and rate-limiting policies. I’m closing this PR for now and holding off on adding rate-limiting and partial-result recovery APIs. If a specific use case shows where the existing options fall short, feel free to open a focused issue. Happy to revisit and reopen this if needed. |
Description
Addresses the two still-open items of problem #2 in #358 (Gemini
429 RESOURCE_EXHAUSTEDhandling). The exponential backoff that issue also asked for already landed in3aab86c, so this PR covers what remains:max_rpm— client-side throttle. A new optionalGeminiLanguageModelparameter. When> 0, real-time requests are spaced by a thread-safe leaky bucket so at mostmax_rpmstart per minute (shared acrossmax_workers). This prevents429 RESOURCE_EXHAUSTEDon quota-limited tiers (e.g. the free tier's 15 RPM) rather than only retrying after the fact. Defaults to0(disabled) — no behavior change for existing callers. Does not apply to the Batch API.infer()path raised on the first failed chunk, which abandoned the still-running futures and discarded every chunk that had already succeeded. It now lets the pool drain, then raises once with the completed work attached toInferenceRuntimeError(partial_results, aligned to the input prompts, plusfailed_indices). The all-or-nothing raise is preserved, soannotation'szip(batch, outputs)alignment is unaffected; callers who want to recover already-done work can now read it off the exception.This is a partial fix for #358 (chunking for very large documents is a separate design question, not touched here), so:
Related to #358
Type: Feature (the
max_rpmthrottle; the partial-result change also fixes a real loss-of-work bug).How Has This Been Tested?
Unit tests in
tests/gemini_ratelimit_partial_test.pycover the limiter's spacing (mocked clock),max_rpmwiring + validation, and partial-result preservation across the all-success, single-failure, and multi-failure cases.Differential-clean against the full suite: the only failures on my machine are pre-existing and environmental (the optional
openaiextra isn't installed, plus 3 plugin-packaging tests) — identical with and without this change — and the 10 new tests pass on top.pyink+isort+pylint --rcfile=.pylintrc/--rcfile=tests/.pylintrc(CI's exact configs) all clean.Checklist:
max_rpmconstructor docstring; the retry-family params aren't documented outside the docstring either).pylintover the affected code.