Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9295eb4
feat(translation): preserve raw stream events
bbednarski9 Jul 29, 2026
b4df242
docs(translation): describe stream preservation as parsed-JSON-value …
bbednarski9 Jul 30, 2026
c2f089d
fix(translation): suppress synthesized finish after a replayed termin…
bbednarski9 Jul 30, 2026
42e23ce
perf(translation): move preserved stream events
bbednarski9 Jul 30, 2026
1c160a2
refactor(translation): centralize preserved stream dispatch
bbednarski9 Jul 30, 2026
d53b4ff
refactor(translation): trim preserving stream API
bbednarski9 Jul 30, 2026
e3f9bc0
docs(protocol): define provider event boundary
bbednarski9 Jul 30, 2026
56012ed
refactor(protocol): compose preserved stream events
bbednarski9 Jul 30, 2026
3ccca40
fix(translation): retain served model on replay
bbednarski9 Jul 31, 2026
a485afc
docs(translation): clarify stream preservation contracts
bbednarski9 Aug 3, 2026
784837c
fix(translation): retain replay encoder state
bbednarski9 Aug 3, 2026
ae354a0
feat(plugin): add NeMo Relay dynamic integration
bbednarski9 Jul 30, 2026
62afc8c
test(plugin): add Relay process and bundle validation
bbednarski9 Jul 30, 2026
47b9d72
refactor(plugin): use targeted LLM continuation contract
bbednarski9 Jul 31, 2026
19b3bfd
chore(plugin): update Relay SDK pin
bbednarski9 Jul 31, 2026
9193405
chore(plugin): refresh Relay SDK pin
bbednarski9 Jul 31, 2026
6f9dbfc
refactor(plugin): use Relay safe native v2 SDK
bbednarski9 Aug 1, 2026
128bead
test(plugin): cover safe stream outcomes
bbednarski9 Aug 1, 2026
5ac0f3a
chore(plugin): update Relay safe v2 SDK pin
bbednarski9 Aug 1, 2026
325ed5f
chore(plugin): update Relay safe v2 SDK pin
bbednarski9 Aug 1, 2026
e6b6f49
refactor(plugin): own routing retry policy
bbednarski9 Aug 1, 2026
b56c907
chore(plugin): adopt cooperative Relay SDK
bbednarski9 Aug 2, 2026
cb2b785
refactor(plugin): rely on host cooperative runtime
bbednarski9 Aug 2, 2026
434e27f
fix(plugin): keep libsy futures executor neutral
bbednarski9 Aug 2, 2026
61a4c3f
docs(libsy): describe poll-driven run streams
bbednarski9 Aug 2, 2026
8edc9b5
refactor(plugin): simplify Relay run stream driver
bbednarski9 Aug 2, 2026
7226c03
refactor(plugin): simplify run stream routing
bbednarski9 Aug 2, 2026
8a15966
test(plugin): expand dynamic routing end-to-end coverage
bbednarski9 Aug 2, 2026
cd8b448
refactor(plugin): trim dynamic plugin surface
bbednarski9 Aug 2, 2026
642599f
refactor(plugin): tighten dynamic plugin contract
bbednarski9 Aug 2, 2026
55456f9
docs(plugin): clarify dynamic routing contract
bbednarski9 Aug 2, 2026
6d2a155
docs(plugin): define managed call boundary
bbednarski9 Aug 2, 2026
716cee4
chore(plugin): pin final Relay v2 revision
bbednarski9 Aug 2, 2026
6ae2ae1
chore(plugin): pin bounded Relay v2 transport
bbednarski9 Aug 2, 2026
3568275
refactor(plugin): reuse libsy classifier config
bbednarski9 Aug 2, 2026
a386765
refactor(plugin): remove draft retry leftovers
bbednarski9 Aug 2, 2026
90ce083
docs(plugin): describe shared bounded output
bbednarski9 Aug 3, 2026
10d204b
chore(plugin): pin reviewed Relay native API v2
bbednarski9 Aug 3, 2026
f92e6e2
fix(plugin): stop routing after terminal controls
bbednarski9 Aug 3, 2026
4e87155
fix(libsy): avoid unused classifier session state
bbednarski9 Aug 3, 2026
fc40dae
docs(plugin): define initial router scope
bbednarski9 Aug 3, 2026
f44666d
chore(plugin): pin rebased Relay native API v2
bbednarski9 Aug 3, 2026
77be543
chore(plugin): refresh Relay native API v2 pin
bbednarski9 Aug 3, 2026
34d48df
fix(plugin): address review safety findings
bbednarski9 Aug 3, 2026
63da586
fix(plugin): reject lossy Anthropic classifier targets
bbednarski9 Aug 3, 2026
c9d3808
chore(plugin): refresh Relay SDK pin
bbednarski9 Aug 3, 2026
dd1bd84
chore(plugin): refresh Relay SDK pin
bbednarski9 Aug 3, 2026
57254df
chore(plugin): refresh Relay SDK pin
bbednarski9 Aug 3, 2026
881e0d3
chore(plugin): refresh Relay SDK pin
bbednarski9 Aug 3, 2026
0b3b243
chore(plugin): refresh Relay SDK pin
bbednarski9 Aug 3, 2026
cda935c
chore(plugin): track classifier token limit
bbednarski9 Aug 3, 2026
c90d06f
fix(translation): invalidate stale request replay
bbednarski9 Aug 3, 2026
616506d
feat(plugin): add stage router support
bbednarski9 Aug 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 190 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"crates/switchyard-server",
"crates/switchyard-skill-distillation",
"crates/switchyard-translation",
"crates/switchyard-nemo-relay-plugin",
]

[workspace.package]
Expand All @@ -26,7 +27,9 @@ rust-version = "1.96.1"
async-stream = "0.3"
async-trait = "0.1"
futures = "0.3"
futures-timer = "3"
futures-util = "0.3"
http = "1"
httpdate = "1"
parking_lot = "0.12"
rand = "0.10"
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ Verify the proxy in another terminal:
curl http://localhost:4000/health
```

NeMo Relay can instead load libsy in process through the native API v2
`nvidia.switchyard` plugin. Relay performs provider I/O while the plugin drives
`run_stream` and uses `switchyard-translation` for every request and response.
See the
[`switchyard-nemo-relay-plugin` guide](crates/switchyard-nemo-relay-plugin/README.md).

For a complete configuration and a test request, follow
[Getting Started](docs/getting_started.md).

Expand Down Expand Up @@ -119,6 +125,7 @@ configured LLM client selects one upstream format.
- **[`switchyard-libsy`](crates/libsy/README.md)**: embed routing algorithms in a Rust application
- **[`switchyard-protocol`](crates/protocol/README.md)**: provider-neutral request, response, and streaming types
- **[`switchyard-translation`](crates/switchyard-translation/README.md)**: request, response, and stream translation
- **[`switchyard-nemo-relay-plugin`](crates/switchyard-nemo-relay-plugin/README.md)**: in-process routing for NeMo Relay

## Community

Expand Down
1 change: 1 addition & 0 deletions crates/libsy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ async-trait.workspace = true
serde.workspace = true
serde_json.workspace = true
futures.workspace = true
futures-timer.workspace = true
# Metrics-only OTel API: instruments record through the host-installed global
# meter provider. Pinned to the 0.32 line used across the workspace.
opentelemetry = { version = "0.32", default-features = false, features = ["metrics"] }
Expand Down
20 changes: 12 additions & 8 deletions crates/libsy/examples/streaming_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
//! offloaded as a [`Step::CallLlm`]. The agent serves it with a *streaming* response
//! ([`LlmResponse::Stream`]) rather than a buffered one. That stream rides untouched
//! through the algorithm and returns as [`Step::ReturnToAgent`], where the agent drives it
//! and prints each [`LlmResponseChunk`] as it arrives — true token streaming end to end.
//! and prints the normalized [`LlmResponseChunk`]s in each event as they arrive — true
//! token streaming end to end.
//! Contrast [`Algorithm::run`], which would aggregate the same stream into one buffered
//! answer. Run with:
//! cargo run -p libsy --example streaming_agent
Expand Down Expand Up @@ -40,7 +41,8 @@ fn streaming_response(model: &str, tokens: &[&str]) -> Response {
reason: Some("stop".to_string()),
});

let stream: LlmResponseStream = futures::stream::iter(chunks.into_iter().map(Ok)).boxed();
let stream: LlmResponseStream =
futures::stream::iter(chunks.into_iter().map(|chunk| Ok(chunk.into()))).boxed();
Response {
llm_response: LlmResponse::Stream(stream),
metadata: None,
Expand Down Expand Up @@ -81,15 +83,17 @@ async fn main() -> Result<()> {
}
Step::ReturnToAgent(response) => match response.llm_response {
// The stream reached the agent untouched: print each token as it arrives.
LlmResponse::Stream(mut chunks) => {
LlmResponse::Stream(mut events) => {
print!("agent sees: ");
while let Some(chunk) = chunks.next().await {
let chunk = chunk.map_err(|error| {
while let Some(event) = events.next().await {
let event = event.map_err(|error| {
LibsyError::external("reading response stream", error)
})?;
if let LlmResponseChunk::TextDelta { text, .. } = chunk {
print!("{text}");
std::io::stdout().flush().ok();
for chunk in event.normalized() {
if let LlmResponseChunk::TextDelta { text, .. } = chunk {
print!("{text}");
std::io::stdout().flush().ok();
}
}
}
println!();
Expand Down
Loading
Loading