Feat/provider load balancing - #731
Draft
nerdalert wants to merge 11 commits into
Draft
Conversation
Add provider_route and credential_inject filters that accept only authenticated candidate metadata, enforce exact model and path mappings, and replace caller credentials at the final provider hop. Require peer trust before provider routing and keep private backends behind provider-local policy. Add a runnable mTLS example and integration coverage for authorized routing, credential replacement, and fail-closed rejection. The new coverage also fixes shared TLS test utilities by installing an explicit Rustls provider and separating HTTP-over-TLS requests from raw TCP/TLS helpers. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Update the provider-routing tests for the shared subrequest client now required by the registry builder on main. Use the same client for registry construction and pipeline resolution in the integration helper. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Cache the provider attribution header during filter construction and reject invalid values before serving traffic. Extend the provider example to prove clients without certificates fail the TLS boundary. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Honor struct-level serde rename rules when generating nested filter documentation, and regenerate credential paths with their accepted camelCase names. Keep both file-backed credential buffers zeroized after use. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Consolidate repeated filter registration assertions into a table-driven check so adding the provider security filters does not exceed the lint line limit. Preserve the explicit security-class assertions. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Keep routing test helpers in the repository-standard utilities section and verify that omitted demo attribution leaves response headers unchanged. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Treat Zeroizing as a transparent Rust implementation wrapper when generating YAML configuration references. This keeps the documented credential value type aligned with its string wire format. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
Add producer-defined selection groups and deterministic, random, and round-robin picker policies to intelligent_route. Build immutable group indexes when an overlay loads and keep request-time selection local to the gateway. Preserve legacy first-candidate behavior when picker metadata is absent. Resolve session affinity before new selection, enforce admission states, and fall through to the next viable group. Keep round-robin state scoped to the semantic snapshot so timestamp-only overlay rewrites do not reset distribution. Document the contract and cover concurrency, counter isolation, wraparound, malformed groups, and snapshot replacement. Signed-off-by: Brent Salisbury <bsalisbu@redhat.com>
nerdalert
marked this pull request as draft
August 13, 2026 05:32
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
This PR adds local provider selection policies to the AI gateway's intelligent_route filter.
It is rebased on #386, which provides the authenticated provider-routing boundary used by this implementation.
Grid remains the asynchronous control plane. It determines provider eligibility, admission state, ordering, and selection-group membership, then publishes that information in the routing overlay.
The AI gateway consumes the accepted overlay from memory. When the overlay is loaded, it validates the group metadata and builds an immutable index for request-time selection.
The supported picker policies are:
deterministic: select the first viable provider, preserving existing behavior.roundRobin: rotate evenly among viable providers in the active group.random: select uniformly among viable providers in the active group.Session affinity is evaluated before the picker. A valid existing binding continues to use its provider when admission permits it. New, unbound requests use the configured picker.
The request path does not call Grid, Kubernetes, the operator, overlay-sync, ConfigMaps, EPP, Prometheus, or a remote scoring service.
Responsibility Boundary
This change keeps control-plane and request-time responsibilities separate:
intelligent_route does not recompute scores, geography, health, or group membership.
Overlay Contract
The routing overlay may include an optional selection policy and producer-assigned groups:
Group 0 is preferred. If a group contains no candidate eligible for a new request, selection proceeds to the next group.
Provider-level retries after a forwarding failure are not introduced by this PR. Praxis core may retry endpoints within the selected cluster, but retrying a different Grid provider cluster remains follow-up work.
Compatibility
Existing overlays without
selection_policyorselection_groupretain the legacy deterministic, ordered-first behavior.An upgraded deployment therefore does not begin balancing traffic unless its overlay explicitly enables a picker.
Unknown picker values and malformed group metadata fail validation. Invalid reloads retain the last-known-good snapshot, and in-flight requests continue using the immutable snapshot with which they started.
The picker is not inferred from:
Scores and ranks do not implicitly become traffic weights. Weighted selection is a separate future extension.
Snapshot And Hot-Path Behavior
Selection-group indexes are constructed when a routing snapshot is loaded, not while processing requests.
Round-robin counters are:
The request path performs bounded in-memory lookups and one local picker operation. It does not parse configuration, rebuild groups, scan unrelated capabilities, scrape metrics, or make control-plane calls.
This provides balanced behavior per gateway. It does not attempt to coordinate one global round-robin counter across a gateway fleet.
Security And Tenancy
The picker only selects among candidates already present in the accepted routing overlay and eligible under their published admission state. It does not discover providers, expand authorization, resolve credentials, or override Grid's trust and policy decisions.
Selection groups must not cross security boundaries unintentionally. Grid remains responsible for ensuring that candidates grouped together are valid for the same capability and applicable policy scope. Praxis AI validates the group structure but does not reinterpret tenancy, locality, credential, or provider trust rules.
Provider credentials and authorization data are not used as picker inputs or exposed through selection metadata. The authenticated provider-routing boundary remains owned by #386 (#386).
Operational Observability
Successful route decisions expose bounded in-process metadata for:
This evidence supports logs and tracing without adding a request-time
control-plane dependency.
Provider IDs, session identifiers, request IDs, and other high-cardinality values should not become unbounded metric labels. Aggregate metrics should use controlled dimensions. Rich per-request routing details are better represented as bounded trace attributes when OpenTelemetry support is enabled separately.
Multiple Consumer Gateways
This design also works when several consumer or edge gateways accept traffic. Each gateway loads the same Grid overlay and independently applies the configured picker:
No consumer gateway coordinates with another gateway during request handling. Each process maintains its own round-robin counters and session-affinity state. This preserves the local, low-latency request path and avoids introducing a shared database or remote selection service.
With enough new requests, each gateway should independently distribute traffic across the active provider group. The combined distribution will generally approach the expected split, but a strict global sequence or exact fleet-wide ratio is not guaranteed.
Limitations include:
Functional Proof
The focused
provider-traffic demo (https://github.com/praxis-proxy/demos/tree/main/demos/grid-provider-traffic) exercises the intended topology:
The clean runtime validation demonstrated:
The demo proves equal provider selection from a stable snapshot. It does not claim to prove weighted routing, provider-level retry, lower-group runtime failover, or fleet-global round-robin coordination.
Other Grid demos cover separate concerns:
llm-d pool metrics (https://github.com/praxis-proxy/demos/tree/main/demos/grid-llmd-pool-metrics) demonstrates asynchronous EPP metric scoring and rank changes.
Grid GLB (https://github.com/praxis-proxy/demos/tree/main/demos/grid-glb-demo) demonstrates entry-point, edge, and provider routing across a larger topology.
OpenTelemetry instrumentation and the tracing UI are separate work and are not modified by this PR.
Dependency
This branch is based on the authenticated provider-routing work in #386 (#386).
The load-balancing implementation is isolated in one commit and should be rebased onto main after #386 merges.
Follow-Up Work
This PR establishes equal local selection within producer-defined groups.
Separate follow-up work includes:
Weighted routing must remain explicit. Scores, ranks, queue depth, and KV-cache pressure should not silently become traffic percentages.
Related Issue
Related to
praxis-proxy/grid#31.
The research spike includes the focused provider-traffic demo narrative, runtime evidence, and a video showing one consumer gateway distributing requests across three provider gateways.
This PR implements the AI request-time selection portion of the spike. It does not close the Grid-side overlay producer work or the remaining retry, fallback, and weighted-selection research.
Validation
Validated with:
cargo +nightly fmt --all -- --check
cargo test -p praxis-ai-filters
cargo test -p praxis-ai-filters --no-default-features
cargo test -p praxis-ai-filters --all-features
cargo clippy --workspace --all-targets -- -D warnings
make lint
make test
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
git diff --check
Focused coverage includes:
Checklist
The runtime demo validates functional distribution. It is not presented as a throughput or latency benchmark.
Breaking Changes
None intended.
Existing overlays retain deterministic behavior. New deployments can opt into roundRobin or random through an explicit selection policy. This PR does not change scoring semantics, add weighted routing, or modify OpenTelemetry instrumentation.
/cc @jordigilh