Skip to content

feat(llm): add opt-in provider-wide in-flight limit for review runs - #1102

Open
nitishagar wants to merge 1 commit into
alibaba:mainfrom
nitishagar:feat/787-provider-in-flight-gate
Open

feat(llm): add opt-in provider-wide in-flight limit for review runs#1102
nitishagar wants to merge 1 commit into
alibaba:mainfrom
nitishagar:feat/787-provider-in-flight-gate

Conversation

@nitishagar

Copy link
Copy Markdown
Contributor

Implements #787. The issue's Status section asks for the configuration surface to be aligned before code — this PR is that proposal made concrete: field max_in_flight on provider entries (custom and preset), ocr config set custom_providers.<name>.max_in_flight N, default unset/0 = disabled, provider-only (no llm.* mirror — it describes endpoint capacity, not model config). Every naming/placement decision below is one comment away from adjustment if you'd like it shaped differently.

What

One provider-scoped admission gate per review run, shared by every ocr review LLM request path — plan, main task, grace round, memory compression, comment re-location, review filter:

  • Admitted per real SDK attempt, at the shared middleware boundary, inside the SDK retry loop. The SDK stays the sole retry owner: no retry policy, count, backoff, or server-hint behavior changes.
  • Permit lifecycle: acquired before transport; released immediately on transport error / nil response / body-less response; otherwise ownership transfers to an idempotent body wrapper released at EOF, any read error, or Close (double-close safe; a panic between acquisition and handoff releases via a deferred path). The SDK closes retryable bodies before backoff, so 429/529 retries free their slot during the wait; streaming responses hold theirs until the stream ends.
  • Scope via context, not RequestMeta: the review run's context is marked once (review_cmd.go); context.WithoutCancel children (async compression, pooled re-location) inherit it. ocr scan and ocr llm test never carry the scope — ungated by construction, verified by tests.
  • No deadlock at limit=1: a request holds at most one permit and never acquires another while holding, so foreground/background paths can queue on each other but never cycle; everything drains through the existing WaitBackground boundary.
  • Retry-report compatible: ocr.llm-retry-report/v1 schema untouched. The gate mounts outside the retry observer, so duration_to_headers_ms still measures transport time only (test-asserted); observed_backoff_ms may include admission waiting — documented at its definition per the issue's wording.
  • Disabled by default: unset/0 mounts no middleware at all — the request path is byte-identical to today.

Config example

ocr config set custom_providers.my-gateway.max_in_flight 4

Tests

  • Middleware unit tests: passthrough (no gate / no scope / disabled), cancel-while-queued (no transport call, no waiter residue), release matrix, EOF/Close/read-error/panic exactly-once, limit enforcement, leak-canary drain in every case.
  • Real-client integration (httptest): server-observed concurrency never exceeds the limit; 429/529 release-before-backoff proven by arrival order; streaming holds until stream end; deadline-while-queued documented; unscoped requests ungated; duration_to_headers_ms excludes gate wait; limit=1 foreground+background both complete.
  • Scope wiring: loop-level tests prove main/grace/compression/re-location carry the scope and scan does not; a full-command e2e runs the real ocr review against a config-file provider with max_in_flight: 1 and asserts the same review that overlapped without the limit serializes to exactly one in-flight attempt.
  • Config: set round trip, negative/non-int rejection, TUI clone preservation, resolver mapping + negative rejection. Coverage 90.9% (make coverage threshold 90%), full suite green under -race.

Part of #368 (the fixed provider-wide admission limit it requires; repository workflows may display but must not implement provider admission).

Closes #787

Review runs overlap LLM requests from several paths: file-foreground
work (plan, main loop, grace round), one async memory-compression job
per file conversation, the comment worker pool's re-location calls,
and the review filter. --concurrency bounds file workers only, so a
loaded provider sees unbounded overlap.

This adds a provider-scoped admission gate at the shared SDK
middleware boundary, acquired once per real SDK attempt (inside the
SDK retry loop) and released through an idempotent response-body
wrapper: transport errors and body-less responses release
immediately, retryable responses free their slot before the SDK's
backoff sleep, and streaming bodies hold theirs until the stream
ends. A panic anywhere between acquisition and the body handoff
still releases via a deferred path, and a failed read frees the
slot just like EOF.

Admission applies only to ocr review: the run context is marked
with an admission scope that survives context.WithoutCancel, so
compression and pooled re-location children are gated, while
ocr scan and ocr llm test never carry the scope and are untouched.
The gate mounts outside the retry observer, keeping gate queue time
out of the retry report's duration_to_headers_ms; observed_backoff_ms
may include it, documented at its definition.

Configuration is a per-provider max_in_flight field (custom and
preset provider entries), settable via ocr config set, defaulting to
unset/0 which mounts no middleware at all. The SDK remains the sole
retry owner: no retry policy, count, or backoff behavior changes.

Part of alibaba#368
@github-actions

Copy link
Copy Markdown
Contributor

OpenCodeReview: Review complete: 0 finding(s) across 8 selected item(s).

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.

Add a provider-wide per-attempt concurrency gate

1 participant