fix(inference-gateway): backport EPP request-body forwarding (#11991) and prefill reservations (#13042) to v1.4.2 - #2
Merged
avinash-rafay merged 15 commits intoSep 1, 2026
Conversation
…orer (ai-dynamo#11991) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com> Co-authored-by: Avinash Varma <avinashvarmap@gmail.com> (cherry picked from commit ffae596) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
Signed-off-by: Thomas Montfort <tjmontfort12@gmail.com> (cherry picked from commit be9f00c) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
Signed-off-by: Thomas Montfort <tjmontfort12@gmail.com> (cherry picked from commit 92eadad) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
Signed-off-by: Thomas Montfort <tjmontfort12@gmail.com> (cherry picked from commit 583cdb2) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
Signed-off-by: Thomas Montfort <tjmontfort12@gmail.com> (cherry picked from commit 2ac4c67) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
… between integer types' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Thomas Montfort <61255722+tmonty12@users.noreply.github.com> (cherry picked from commit 858b76d) Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
The upstream change was developed against release/1.3.0, which predates the cache-namespace plumbing on this release line. Thread cache_namespace through the reservation path so it matches the v1.4.2 signatures: - reservations::reserve() and RouterHandles::reserve_prefill_worker() take cache_namespace and forward it to find_best_match_details(), which requires it on 1.4.2. - route_prefill_request_with_reservation() destructures the 5-tuple PreprocessedRequest and writes the namespace back via write_cache_namespace_to_result(), as the removed advisory route did. - CallRoutePrefillRequestWithReservation() surfaces CacheNamespace on RoutingResult, matching the decode path. - DynDecodeScorer keeps the 5-arg addRequest and passes state.CacheNamespace. Also drops an unused prefill_router binding in add_request_with_cache_namespace that is not present in the upstream PR head. Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
DynPrefillScorer.Score() called the cancelPrefill func field directly on the admission-timeout path, so any scorer built without that hook panics with a nil pointer dereference instead of reporting a configuration error. beginPrefill and releasePrefill are already accessed through nil-guarded wrappers; this adds the matching wrapper for cancelPrefill. Reproduced by TestPrefillScoreBoundsConcurrentReservations, which constructs a DynPrefillScorer without a cancelPrefill hook. The upstream PR head carries the same unguarded call and the same test, so it panics there too. Signed-off-by: Avinash Varma <avinashvarmap@gmail.com>
avinash-rafay
deployed
to
external_collaborator
September 1, 2026 08:51 — with
GitHub Actions
Active
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Backports two upstream
ai-dynamo/dynamochanges onto thev1.4.2release line for the inference-gateway EPP:fix(inference-gateway): forward full request body to EPP KV-router scorer. Not present inv1.4.2: the release tag still carries the pre-fix version ofdynamo_kv_scorer/plugin.goanddisagg/shared.go(verified by content —BuildOpenAIRequestJSONdoes not exist at the tag).fix(epp): reserve prefill load during routing, the prefill-reservation/booking lifecycle. Cherry-picked as 12 individual commits, preserving upstream authorship.Base branch
v1.4.2-fixeswas created from thev1.4.2tag exactly (2ecbdfdf19).Where the commits came from
PR ai-dynamo#13042 targets
main, whosePrefillRouterhas been refactored to a genericPrefillRouter<Sel>built onArcSwapOption<PrefillBinding>+drive_target.v1.4.2still uses theOnceLock<InnerPrefillRouter>design, so the Rust half of the PR does not apply to this line at all.The upstream author's own release backport branch —
tmonty12/dyn-12864-prefill-reservations-release-1.3.0— is already adapted toInnerPrefillRouter, and has the same history shape (the ai-dynamo#11991 pick, then the feature commits). These commits were taken from that branch, which reduced the conflict surface to a handful of hunks instead of a rewrite.Deliberate divergences from the 1.3.0 backport
cache_namespacethreadingv1.4.2added cache-namespace plumbing thatrelease/1.3.0predates.find_best_match_details()takescache_namespace(14 args, not 13) andPreprocessedRequestis a 5-tuple. Threaded throughreservations::reserve()→reserve_prefill_worker()→route_prefill_request_with_reservation(), and surfaced onRoutingResultin Go — mirroring what the (now removed) advisory prefill route did on this line.addRequestis 5-argv1.4.2'sCallAddRequesttakescacheNamespace. Note the 1.3.0 branch is internally inconsistent here (4-argCallAddRequestand 4-arg field, but a 5-arg stub inreservation_test.go, so its test package does not compile). This port is 5-arg throughout.extractCacheNamespace(Go) andwrite_cache_namespace_to_result(Rust) arev1.4.2-only helpers used outside the removed advisory route, so only the advisory functions were deleted, not the helpers.let prefill_router = handles.prefill_router.clone();inadd_request_with_cache_namespaceis unused (compiler warning). It is absent from the upstream PR head, so it was removed.These are collected in one clearly-labelled commit,
fix(inference-gateway): adapt prefill reservation port to 1.4.2 APIs, so the cherry-picks stay faithful to upstream.Upstream bug found and fixed here
DynPrefillScorer.Score()calls thecancelPrefillfunc field directly on the admission-timeout path (prefill_scorer.go:229), while the sibling hooksbeginPrefillandreleasePrefillare reached through nil-guarded wrappers. Any scorer built without acancelPrefillhook therefore panics with a nil pointer dereference.TestPrefillScoreBoundsConcurrentReservationsconstructs exactly such a scorer, so it segfaults the wholedisaggtest binary. This is not a backport artifact —prefill_scorer.goandreservation_test.gohere are byte-identical to the 1.3.0 branch, and the upstream PR head (858b76d5) carries the same unguarded call at the same line plus the same unstubbed test.fix(inference-gateway): guard nil prefill cancellation hookadds the matching wrapper. This is a divergence from upstream and is isolated in its own commit so it can be dropped if ai-dynamo#13042 fixes it differently.Commits intentionally not included
The three
fix(router):commits in ai-dynamo#13042 are empty on the upstream 1.3.0 backport. Two of them (preserve cleanup wake under saturation,remove stale actor queueing flag) touchlib/kv-router/src/scheduling/queue.rs. They were evaluated and deliberately excluded:On
main,new_request_lifecycle_lease()has noqueueing_enabledgate. Onv1.4.2it returnsNoneunless queueing is enabled, andRequestLifecycleLease::dropis the only producer intoAdmissionCleanup. So in default (non-queueing) mode no lease is ever created, cleanup is never populated, and removing thequeueing_enabled &&guard ondrain_cleanupis a no-op on this release line. The accompanying regression test cannot even be expressed here — it depends on anAdmissionCommand::SelectWithoutAdmissionvariant that does not exist inv1.4.2.fix(router): bound stale reservation reapingis not empty upstream and is included.queue.rsis therefore byte-identical tov1.4.2.Validation
Run on macOS/arm64. Go linking there additionally needs
CGO_LDFLAGS="-framework SystemConfiguration"(hyper_util's proxy matcher pulls in those symbols); this is a pre-existing local-dev gap, not introduced here, and does not affect the Linux CI/container build.cargo check -p libdynamo_llm -p dynamo-kv-router --tests— clean, no errors, no warnings.cargo fmt -p libdynamo_llm -p dynamo-llm -p dynamo-kv-router -- --check— clean.go vet ./...indeploy/inference-gateway/epp— clean (includes test files).make dynamo-lib+go build ./cmd/epp— EPP binary links against the freshly builtlibdynamo_llm_capi.a.go test ./pkg/plugins/...:dynamo_kv_scorer— okdisagg— 19 passed, 1 failed (TestPrefillScoreBoundsConcurrentReservations, see below)git diff v1.4.2 HEAD -- lib/kv-router/src/scheduling/queue.rs— empty, confirming no drift there.plugin_test.gofrom fix(inference-gateway): forward full request body to EPP KV-router scorer ai-dynamo/dynamo#11991; every line-count delta maps to a listed divergence.reservations.rs,booking_executor.go,booking_executor_test.go,reservation_test.go) diff against the 1.3.0 branch by exactly the twocache_namespacelines and the CodeQL fix; the two test files are byte-identical.Warning
TestPrefillScoreBoundsConcurrentReservationsfails (1 of 20). After the nil-guard above it no longer segfaults, but it still fails 5/5 runs, in two alternating modes:expected lifecycle for successful first reservationandfirst reservation cleanup did not finish.The test gives the scorer a 20 ms
reservationAdmissionTimeout, and that same context bounds the reservation wait — not just slot acquisition — so the firstScore()races its own deadline against the test releasingallowReserveReturn. The assertions expect the first reservation to succeed.Everything the test exercises (
prefill_scorer.go,reservation_test.go, and theregisterBookingLifecycle/findBookingLifecycle/cleanupComplete/armCancellationmachinery) is identical to the upstream 1.3.0 branch, so this is an upstream defect, not a backport artifact. It was not caught upstream because that branch'sdisaggtest package does not compile at all (4-argCallAddRequestvs 5-arg stub).Resolving it means changing upstream behaviour — most likely scoping the admission timeout to slot acquisition rather than the whole reservation — so it is deliberately left alone here pending a decision on ai-dynamo#13042.
Note
PR ai-dynamo#13042 is not yet merged upstream. This backport tracks its current head (
858b76d5, including the CodeQL integer-conversion fix). If the PR changes before it merges, this branch needs a refresh.