Skip to content

feat(plugin): add StageRouter support to the NeMo Relay plugin - #250

Draft
bbednarski9 wants to merge 53 commits into
NVIDIA-NeMo:mainfrom
bbednarski9:feat/nemo-relay-stage-router-plugin
Draft

feat(plugin): add StageRouter support to the NeMo Relay plugin#250
bbednarski9 wants to merge 53 commits into
NVIDIA-NeMo:mainfrom
bbednarski9:feat/nemo-relay-stage-router-plugin

Conversation

@bbednarski9

@bbednarski9 bbednarski9 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What

Adds StageRouter support to the external NeMo Relay dynamic plugin as an isolated draft follow-up to #220.

The two follow-up commits after #220 are:

  • c90d06ff: invalidate exact same-format request replay after libsy mutates a normalized request, then re-encode the current request while retaining captured top-level provider extensions; and
  • 616506d0: add typed StageRouter configuration, capable/efficient targets, picker policy, tier prompts, handoff notes, an optional per-turn classifier fallback, schema/docs, and E2E coverage.

The nested Stage classifier tracks libsy's current max_output_tokens setting. Relay still performs every provider call through the generic native API v2 LLM continuation from NVIDIA/NeMo-Relay#594; no Relay ABI or core-runtime change is required.

Why

StageRouter can add a tier-specific system prompt or handoff note before dispatch. Replaying the originally preserved provider body after that mutation silently discards the new instructions. This draft makes the preservation lifecycle explicit: exact replay remains available while the neutral request is unchanged, and mutation forces re-encoding of the current request.

Keeping this separate from #220 lets reviewers evaluate the request-mutation and fidelity contract without expanding the initial Random/classifier PR.

Relates to #220 and #192.

How tested

  • uv run ruff check clean for the modified E2E files
  • uv run mypy switchyard clean — N/A: no Python package changes
  • uv run pytest tests/ green — N/A: the changed contract is covered by Rust and process tests
  • Manual smoke: real Relay process plus the three-protocol fake provider

Exact validation at 616506d0c1a444c7c92bb235f42c8bf9e42df45f, two commits on #220 head cda935c5360f85e5b87bb825fd51137c27183e0c:

  • cargo test -p switchyard-nemo-relay-plugin: 22/22 passed;
  • focused and full workspace Clippy with warnings denied, formatting, and cdylib build passed;
  • Stage efficient and capable decisions, classifier fallback, tier-prompt/handoff mutation delivery, and retained top-level unknown fields passed;
  • inherited Random and capability-classifier E2E passed across all three protocols, including concurrency, retries/fallback, raw-event replay, credentials, and pass-through; and
  • buffered and streaming reliability, late-error, and exactly-once fallback assertions passed.

Checklist

  • One class per file; filename = snake_case of the primary class. (N/A: Rust implementation.)
  • New public symbols exported from switchyard/__init__.py.__all__ if intended for downstream use. (N/A: no Python symbols.)
  • Unit tests added for new components / bug fixes.
  • README / --help updated if customer-facing surface changed.
  • Commits signed off (Signed-off-by: Your Name <email>) per the DCO.

Notes for reviewers

This draft depends on #220. Until #220 merges, GitHub shows its cumulative base diff; the Stage-specific review range is cda935c5..616506d0. The branch does not include escalation.

It remains draft because mutation cannot reconstruct unknown provider fields nested inside messages or tool definitions and can canonicalize equivalent fields such as OpenAI max_tokens to max_completion_tokens. That does not yet satisfy the intended exact same-protocol preservation guarantee.

Other current limits:

  • Stage-specific process cases use OpenAI Chat; inherited generic routing covers all three protocols, but Stage mutation is not yet exercised for Responses or Anthropic;
  • the optional judge must use OpenAI Chat or Responses because its JSON-schema request is not losslessly representable by Anthropic;
  • generic HTTP 400 bodies are not parsed into ContextWindowExceeded; and
  • Relay marks do not expose libsy's internal Stage decision-source enum.

No Relay API/ABI expansion is required for the tested Stage flow.

@bbednarski9
bbednarski9 force-pushed the feat/nemo-relay-stage-router-plugin branch 7 times, most recently from dbbdadb to 283a2c9 Compare August 3, 2026 17:29
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
…replay

Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
…al event

Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Keep preservation on TranslationEngine, the API consumed by NVIDIA/NeMo-Relay#586, and remove the unused built-in convenience decoder.

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>
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>
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>
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>
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>
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>
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