@@ -8,8 +8,8 @@ SPDX-License-Identifier: Apache-2.0
88This crate builds the external ` nvidia.switchyard ` native plugin. It embeds
99` switchyard-libsy ` , drives ` Algorithm::run_stream ` , and uses
1010` switchyard-llm-client ` for provider HTTP calls. Managed calls use Relay's
11- public Rust LLM execution-intercept SDK and do not require a targeted provider
12- continuation from Relay.
11+ completion-based asynchronous middleware hooks and do not require a targeted
12+ provider continuation from Relay.
1313
1414The plugin uses NeMo Relay native API v1. It depends on the small
1515` nemo-relay-plugin ` authoring SDK, not the Relay runtime, and does not start
@@ -59,85 +59,39 @@ This boundary has two important consequences:
5959 transport activity is therefore not represented as nested Relay LLM
6060 lifecycle events. Relay records the outer managed call and the plugin emits
6161 Switchyard routing marks; bridging Switchyard transport spans into Relay is
62- future work. Routing marks are delivered through the public SDK while Relay
63- is polling the active execution callback or stream, so they remain attached
64- to the current Relay scope stack.
62+ future work. The adapter captures the active Relay scope before returning
63+ ` Pending ` , so asynchronous routing marks retain their event parent.
6564- Switchyard owns provider URLs, credentials, HTTP retry behavior, and
6665 translation for managed calls. Relay neither validates nor transports those
6766 target details.
6867
69- ## Native API v1 and the public Rust SDK
70-
71- The manifest uses ` compat.native_api = "1" ` . The implementation registers with
72- ` PluginContext::register_llm_execution_intercept ` and
73- ` PluginContext::register_llm_stream_execution_intercept ` , receives typed
74- ` LlmRequest ` , ` LlmNext ` , and ` LlmStreamNext ` values, and returns the SDK's JSON
75- result or ` LlmJsonStream ` . The ` nemo-relay-plugin ` SDK owns the C callback
76- trampolines, host strings, continuation handles, panic containment, and native
77- stream lifecycle. Switchyard contains no raw C callback or host-table adapter.
78-
79- Switchyard performs libsy and provider I/O on a plugin-owned Tokio runtime. A
80- buffered SDK callback waits for that executor to finish. A managed streaming
81- callback returns a pull-based Rust iterator backed by a bounded 32-message
82- channel; the async producer waits when the consumer is slow. Dropping that
83- iterator closes the channel and aborts its in-flight routing task.
84-
85- The public native API v1 callback shapes do not provide full in-flight caller
86- cancellation. A buffered callback has no cancellation token, so a provider
87- request already in progress continues until it responds or reaches the client
88- timeout after the caller disconnects. Relay can cancel a streaming iterator
89- between pulls, but its synchronous ` Iterator::next ` call cannot be interrupted
90- while it is waiting for the next provider event. Supporting prompt disconnect
91- propagation would require an asynchronous public SDK callback or stream-polling
92- contract; the plugin does not bypass the SDK to recover that behavior.
93-
94- ### Relay runtime capacity
95-
96- In Relay 0.7, the safe native LLM callbacks run directly in Relay's asynchronous
97- middleware future; Relay does not move them to Tokio's separate blocking pool.
98- Consequently, each active buffered Switchyard call occupies a normal Relay
99- Tokio worker while it waits for the plugin executor, and a streaming call
100- occupies a worker while its synchronous ` Iterator::next ` waits for the next
101- provider event. Exhausting those workers can delay unrelated Relay work.
102-
103- The Relay CLI constructs a Tokio multi-thread runtime without setting an
104- explicit worker count. Tokio therefore defaults to the number of CPU cores
105- available to the process and honors its ` TOKIO_WORKER_THREADS ` environment
106- variable. Operators can provide additional capacity while using this native API
107- v1 integration, for example:
108-
109- ``` bash
110- TOKIO_WORKER_THREADS=32 nemo-relay ...
111- ```
112-
113- This adjusts the normal asynchronous worker pool, not Tokio's blocking pool;
114- increasing ` max_blocking_threads ` does not address this integration. There is no
115- universal recommended value. Size the pool with headroom above the expected
116- number of concurrent managed calls and Relay's other work, then validate it
117- under representative provider latency and streaming concurrency. Increasing
118- the worker count mitigates starvation but does not restore cancellation or make
119- the synchronous boundary non-blocking.
120-
121- Embedded Relay hosts own their Tokio runtime and should configure the same
122- capacity explicitly:
123-
124- ``` rust
125- let runtime = tokio :: runtime :: Builder :: new_multi_thread ()
126- . worker_threads (32 )
127- . enable_all ()
128- . build ()? ;
129- ```
130-
131- The durable resolution is a public Relay SDK callback and stream-polling
132- contract that can yield while provider I/O is pending and receive caller
133- cancellation. Until that exists, provision worker capacity and load-test the
134- intended concurrency rather than relying on thread-pool growth alone.
135-
136- Unmanaged profiles call the typed ` LlmNext ` or ` LlmStreamNext ` continuation and
137- return its result directly. Managed streams use the bounded Switchyard channel;
138- unmanaged streams use Relay's SDK-owned pull iterator without an additional
139- bridge. None of the managed HTTP or routing operations requires a targeted LLM
140- continuation or direct access to Relay's C ABI.
68+ ## Native API v1 and asynchronous execution
69+
70+ The manifest remains ` compat.native_api = "1" ` , but the plugin requires the
71+ generic host-table v3 extension shipped by Relay 0.7. It registers through
72+ v3's completion-based buffered and incremental streaming hooks, returns
73+ ` Pending ` immediately, and performs libsy and provider HTTP work on a
74+ plugin-owned Tokio runtime. Relay workers therefore do not wait synchronously
75+ for provider I/O.
76+
77+ The stream adapter forwards the plugin's bounded 32-message channel into
78+ Relay's bounded output queue. It retries a logical event when the host queue is
79+ full and checks cancellation between attempts. Managed HTTP work is selected
80+ against Relay caller cancellation, so cancelling a buffered or streaming call
81+ drops its in-flight provider future.
82+
83+ Unmanaged profiles use the same v3 continuation hooks for pass-through. V3's
84+ downstream stream callback has continue/cancel control but no asynchronous
85+ acknowledgement, so the adapter uses a nonblocking bridge capped at 8 MiB of
86+ queued encoded payloads and 256 events. A pass-through stream that outruns
87+ either bound is rejected rather than consuming unbounded memory.
88+
89+ This is a raw C boundary: Switchyard contains a small ownership adapter for
90+ host strings, completion and stream handles, continuation handles, and captured
91+ scope handles because Relay 0.7 does not expose a safe Rust facade for its
92+ generic asynchronous surface. The HTTP, routing, and translation behavior
93+ remains in Switchyard. The adapter can be replaced with the safe typed surface
94+ when Relay exposes equivalent asynchronous callbacks and cancellation.
14195
14296## Supported routers
14397
@@ -183,10 +137,10 @@ per-event preservation envelope.
183137
184138The manifest declares ` compat.native_api = "1" ` and Relay ` >=0.7.0,<0.8 ` , and
185139the Rust SDK uses the exact published ` 0.7.0 ` crate. The manifest API value
186- selects Relay's released native plugin contract; plugin authors use its safe
187- Rust SDK rather than the underlying C table directly. Rebuild the bundle when
188- changing SDK versions rather than assuming Rust dynamic-library compatibility
189- from the manifest value alone.
140+ selects Relay's released native plugin contract; the binary also requires the
141+ v3 C host table shipped on the Relay 0.7 line. Rebuild the bundle when changing
142+ SDK versions rather than assuming Rust dynamic-library compatibility from the
143+ manifest value alone.
190144
191145A Relay project can configure a seeded weighted-random router as follows:
192146
0 commit comments