Skip to content

feat: add Relay-aware Cascade routing#11

Open
bbednarski9 wants to merge 12 commits into
NVIDIA-NeMo:topic/nemo-relay-integrationfrom
bbednarski9:feature/relay-cascade-integration
Open

feat: add Relay-aware Cascade routing#11
bbednarski9 wants to merge 12 commits into
NVIDIA-NeMo:topic/nemo-relay-integrationfrom
bbednarski9:feature/relay-cascade-integration

Conversation

@bbednarski9

@bbednarski9 bbednarski9 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

What

Summary

Adds the third NeMo Relay integration layer on top of #8 and #9:

  • an owned DecisionContext that carries the typed request and one immutable, exact Relay snapshot through the existing profile registry;
  • Relay-aware Cascade decisions with typed FeatureFreshness, explicit cold-default behavior, and no selected-target dispatch;
  • exact, canonicalized (session_id, owner_id) lookup without parent, root, or session-only fallback;
  • unified typed session metadata for Decision, normal Rust endpoints, and Python profile calls;
  • strict reconciliation of proxy_x_session_id and x-nemo-relay-session-id, including repeated-value, conflict, empty-value, invalid-UTF-8, and body/header mismatch handling.

This is the Cascade and request-metadata slice of a previous end-to-end implementation, split into a focused public-repository change.

Why

The Decision API and router-neutral ATOF snapshots become end-to-end useful when an existing router can consume them safely. This PR lets Cascade route from Relay history while preserving its normal inference path, the single configured profile runtime, strict tenant/session isolation, and Relay's current body-only Decision identity behavior.

How tested

Testing summary

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo check --workspace --all-targets
  • cargo test -p switchyard-components-v2 (70 unit tests plus 51 profile integration tests)
  • cargo test -p switchyard-server (1 CLI, 10 migration, and 32 server tests)
  • uv run ruff check .
  • uv run mypy switchyard (160 source files)
  • uv run pytest tests/ -q -o addopts= (1,880 passed, 50 skipped)
  • One-record Relay http_post ATOF ingestion followed by a body-only Decision request warms Cascade and returns a strong route while both selected target URLs are unreachable.
  • Cold state, turn-only state, exact owner isolation, padded/blank owner canonicalization, no owner/session fallback, header alias reconciliation, invalid UTF-8, turn-depth saturation, and unchanged normal Cascade dispatch are covered.

cargo test -p switchyard-py reaches the known macOS PyO3 cdylib link limitation (undefined _Py* symbols); the crate passes workspace check/clippy, and the binding behavior passes in the full 1,880-test Python suite.

Checklist

  • One class per file; filename = snake_case of the primary class. (N/A: Rust/Python functional API change.)
  • New public symbols exported from switchyard/__init__.py.__all__ if intended for downstream use. (N/A: Rust symbols are exported from the crate root; the existing Python module exposes the extended metadata type.)
  • Unit tests added for new components / bug fixes.
  • README / --help updated if customer-facing surface changed. (DESIGN.md documents decision-scoped resources and session metadata.)
  • Commits signed off (Signed-off-by: Your Name <email>) per the DCO.

Notes for reviewers

Dependency and delta

This PR depends on #8 and #9. Because the author has read-only access to the upstream repository, this draft must target upstream main and temporarily contains both prerequisite commits.

Review the #11 delta commits 2bb2abc0 (Cascade integration), 8425e3ab (Decision bearer auth and E2E coverage), c5701405 (envelope-validation coverage), and ae5a982a (shared Relay auth documentation), plus the bounded-memory/reporting fixes inherited from #9. Use the exact fork delta. After #9 merges, this branch will be rebased onto updated main so the PR contains only the Cascade/auth delta.

Review guidelines

Suggested review order:

  1. decision.rs, profile.rs, and registry.rs for the owned DecisionContext, typed freshness/session metadata, and same-runtime dispatch.
  2. profiles/cascade.rs for snapshot projection, cold/fresh behavior, source accounting, and the decision-only no-dispatch boundary.
  3. switchyard-server for exact canonical identity lookup and session-header/body reconciliation.
  4. The PyO3 binding and stub for the append-only session_id argument and matching alias semantics.
  5. Component and server tests for one-record ATOF-to-Decision behavior, isolation, and unchanged normal Cascade inference.

Fresh snapshots use Cascade's existing override/scorer/classifier policy. Missing or turn-only snapshots select the configured picker default immediately, report cascade_feature_cold_default, retain the existing fall_open source, omit confidence, and do not call the classifier or selected target.

Previous feedback addressed

  • Completes the requested three-PR split: Decision API contracts/profiles, ATOF subscriptions/accumulation, then Cascade and request-metadata integration.
  • Keeps one server-owned router-neutral accumulator and the same configured Arc<dyn Profile> for inference and decisions; no parallel registry, duplicate runtime, or Cascade-owned event state is introduced.
  • Replaces string-based freshness control flow with Option<FeatureFreshness> and an exhaustive Fresh match.
  • Uses exact canonical (session_id, owner_id) lookup only. Parent/root scopes and session-only state cannot satisfy an owner-specific miss; padded owners normalize and blank owners match ownerless ATOF state.
  • Preserves the previous implementation's full ATOF identity compatibility order implemented in feat: add bounded Relay ATOF snapshots #9 while making request-side identity canonical.
  • Accepts Decision requests without a session header because Relay currently sends body identity; when either supported header is present, every value must agree with the body and with the other alias.
  • Keeps legacy Relay's additive decision_profile.router field compatible while profile_id remains authoritative.
  • Makes cold Cascade behavior explicit and deterministic, records its routing source, and preserves the existing normal run() signal extraction and selected-target dispatch path.
  • Adds typed Rust/Python session metadata with an append-only Python constructor argument and matching conflict/empty-value validation.
  • Avoids new fallible .expect(), .unwrap(), or panic paths in the integration code.

Follow-up fixes

  • Applies the configured Relay bearer token consistently to both ATOF ingestion and the Decision endpoint.
  • Adds an end-to-end server test covering missing, invalid, and accepted Decision authorization.

Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@bbednarski9 bbednarski9 marked this pull request as ready for review July 1, 2026 04:04
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@bbednarski9 bbednarski9 force-pushed the feature/relay-cascade-integration branch 3 times, most recently from df3a7e8 to fd105d2 Compare July 6, 2026 17:20
@bbednarski9 bbednarski9 changed the base branch from main to topic/nemo-relay-integration July 8, 2026 00:59
@bbednarski9 bbednarski9 requested a review from a team as a code owner July 8, 2026 00:59
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.

1 participant