Skip to content

feat(plugin)!: Add Switchyard Decision API routing plugin#369

Draft
bbednarski9 wants to merge 7 commits into
NVIDIA:mainfrom
bbednarski9:bbednarski/switchyard-decision-api-plugin-mvp
Draft

feat(plugin)!: Add Switchyard Decision API routing plugin#369
bbednarski9 wants to merge 7 commits into
NVIDIA:mainfrom
bbednarski9:bbednarski/switchyard-decision-api-plugin-mvp

Conversation

@bbednarski9

@bbednarski9 bbednarski9 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Overview

This adds the Relay-side P0 integration between NeMo Relay and a Switchyard remote Decision API.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Summary

  • Add a standalone nemo-relay-switchyard first-party plugin with no Switchyard Rust dependency.
  • Normalize stable Relay identity and build the current switchyard.routing_request.v1 contract.
  • Validate switchyard.routing_decision.v1 responses against exact Relay-owned backend bindings.
  • Support enforce and observe-only routing, six request materialization modes, bounded retry-aware dispatch, per-protocol trusted fallback, and strict stream commit semantics.
  • Translate buffered and incremental SSE traffic across OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages while preserving same-protocol payloads.
  • Extend ATOF endpoint secrets, routing marks, structured upstream failures, reserved dispatch-header hardening, CLI validation, doctor checks, and the interactive plugin editor.
  • Add a CI-safe process-boundary E2E, cumulative-Switchyard compatibility E2E, and deterministic two-model Hermes/Ollama Cascade trajectory with ATOF, ATIF, and OTLP artifacts.

Details

The Decision API selects only a configured backend ID and exact model/protocol/endpoint tuple. Relay retains ownership of provider URLs, credentials, request/response translation, retries, and final trusted fallbacks. Decision failures, malformed responses, binding drift, unsupported provider extensions, and missing required identity all fail open to the same inbound protocol.

context_mode = "atof_required" requires Relay's existing ATOF HTTP exporter to target Switchyard's /v1/atof/events endpoint with preserved field names and environment-referenced authentication. The plugin emits best-effort canonical switchyard.routing.* marks but does not install a duplicate event subscriber or Relay-side feature accumulator.

The Hermes smoke runs a fixed three-query session through local Ollama and verifies llama3.2:latestqwen3.6:35bllama3.2:latest. Because the current Cascade Decision API classifies from accumulated Relay snapshots rather than current_request.body, the smoke inserts clearly labeled canonical tool-result ATOF fixtures between queries: a critical failure drives the strong override and a clean-test result drives the final weak classifier decision. Each fixture and its ingestion report is preserved with the trajectory.

The smoke writes:

  • one cumulative ATOF JSONL stream plus per-query ATOF segments and ingestion reports;
  • one ATIF trajectory per fixed query;
  • OTLP JSON through the OpenTelemetry Collector while forwarding the same spans to Phoenix;
  • a machine-readable trajectory summary containing the expected/actual route sequence and decision reasons;
  • a compressed shareable bundle with a generated README mapping every file to the behavior it verifies and explicitly labeling the intentional OOM fixture.

Where should the reviewer start?

  1. crates/switchyard/src/component.rs — lifecycle, validation, Decision API calls, retries, fallback, and marks.
  2. crates/switchyard/src/translation.rs and stream_translation.rs — the buffered and SSE 3×3 protocol matrices, including the standard OpenAI stream_options.include_usage transport hint.
  3. crates/cli/src/gateway.rs — reserved routed-dispatch headers and structured retry-aware provider failures.
  4. crates/cli/tests/switchyard_process_e2e.rs — CI-safe process-boundary coverage for routing, streaming, fail-open, and reserved-header stripping.
  5. examples/switchyard/run-real-e2e.sh and run-hermes-ollama-smoke.sh — cumulative Switchyard compatibility and shareable local trajectories.

The commits separate runtime/ATOF prerequisites, gateway retry infrastructure, plugin implementation, and E2E/trajectory work.

Review guidelines

  • Verify the plugin remains contract-only and does not introduce local Switchyard routers, accumulators, or Switchyard crate dependencies.
  • Check fail-open boundaries, especially unsupported extensions and the no-retry-after-first-stream-item rule.
  • Check that all target fields are validated exactly and internal dispatch headers cannot cross the gateway trust boundary.
  • Review environment-secret activation failures and the atof_required cross-component validation together.
  • Treat protocol translation and retry behavior as one atomic routing transaction: one Decision API call per provider attempt, followed by at most one trusted fallback.
  • For the trajectory smoke, distinguish user-query decisions from extra Hermes tool/summary calls using the recorded per-query ATOF line ranges.

Testing summary

  • cargo fmt --all -- --check
  • cargo clippy -p nemo-relay-switchyard -p nemo-relay-cli --all-targets -- -D warnings
  • cargo test --workspace (all workspace unit, integration, and doc tests passed)
  • cargo test -p nemo-relay-switchyard (22 passed)
  • cargo test -p nemo-relay-cli --test switchyard_process_e2e (buffered, SSE, Decision API fail-open, and reserved headers)
  • examples/switchyard/run-real-e2e.sh (weak → strong → strong, including SSE)
  • examples/switchyard/run-hermes-ollama-smoke.sh (weak → strong → weak across two Ollama models)
  • Generated trajectory: 4,994 ATOF events, 10 canonical routing marks, three ATIF files, and six OTLP export batches containing 18 spans; the run was also verified in Phoenix 13.22.2.

Previous feedback addressed

  • Split the previously combined implementation at the repository boundary and kept this as one atomic Relay-side PR.
  • Removed in-process Random, LLM, and Cascade routers and all Switchyard Rust dependencies.
  • Removed Relay-side Cascade accumulation and duplicate Switchyard event subscription.
  • Replaced the former single global fallback with exact trusted defaults for all three inbound protocols.
  • Replaced literal credentials with environment-referenced headers and activation-time validation.
  • Updated the integration to the current Decision API schemas and additive retry metadata rather than restoring legacy router fields.
  • Added focused mark-shape assertions and moved stable contract/translation behavior into unit tests.
  • Added deterministic fake-provider and real cumulative-Switchyard E2Es, plus a real Hermes/Ollama trajectory with shareable ATOF, ATIF, and OTLP output.

Related Issues

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 58b0641e-4b33-4b3d-9a5f-45dadcf22d28

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size:XXL PR is very large lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jul 6, 2026
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 bbednarski/switchyard-decision-api-plugin-mvp branch from 218718d to a376278 Compare July 6, 2026 22:37
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

@bbednarski9 bbednarski9 changed the title [codex] Add Switchyard Decision API routing plugin feat(plugin)! Add Switchyard Decision API routing plugin Jul 6, 2026
@bbednarski9 bbednarski9 added this to the 0.6 milestone Jul 7, 2026
@bbednarski9

bbednarski9 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

reference arch:

flowchart TD
    S["Relay startup"] --> V["Validate configuration, URLs, bindings, and secret environment variables"]
    V --> C["Build HTTP clients"]
    C --> R["Relay becomes ready"]
    R -. "No startup probe" .-> SY["Switchyard server"]

    Q["LLM request"] --> D["POST Decision API"]
    D -->|Valid decision| P["Dispatch selected provider"]
    D -->|Unavailable, timeout, invalid response, or binding drift| F["Dispatch trusted same-protocol fallback"]

    E["ATOF event"] --> A["Best-effort POST"]
    A -->|Switchyard unavailable| L["Log failure; routing continues"]
Loading

SPDX-License-Identifier: Apache-2.0
-->

# NeMo Relay Switchyard plugin

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure of this README should be in line with other READMEs.


## Configuration

Start from [`examples/switchyard/plugins.toml`](../../examples/switchyard/plugins.toml). Sensitive

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid relative links for READMEs which ultimately get published/viewed outside of GitHub

`/v1/atof/events` URL, `field_name_policy = "preserve"`, and at least one environment-referenced
authentication header.

## Verification

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should explicitly mention that all steps should be run from the repository root.

(Note: if this is still applicable/included)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example/switchyard needs to have a comprehensive README. The crate's README is not the correct place.

Comment on lines +8 to +10
`nemo-relay-switchyard` is Relay's first-party remote Decision API integration. It has no Rust
dependency on Switchyard: the current `switchyard.routing_request.v1` and
`switchyard.routing_decision.v1` JSON contracts are represented by local compatibility types.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit confusing to me. Is this something that we want to expose to users, just to change it later on to the proper switchyard crate?

Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@bbednarski9 bbednarski9 force-pushed the bbednarski/switchyard-decision-api-plugin-mvp branch from dec7a01 to 7cf4735 Compare July 7, 2026 16:23
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@willkill07 willkill07 changed the title feat(plugin)! Add Switchyard Decision API routing plugin feat(plugin)!: Add Switchyard Decision API routing plugin Jul 7, 2026
@github-actions github-actions Bot added Feature a new feature breaking PR introduces a breaking change labels Jul 7, 2026
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

breaking PR introduces a breaking change Feature a new feature lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XXL PR is very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants