Middleware and Continuations
Python and Rust workers expose the same registration model even though their closure syntax differs. The checked examples share configuration and observable behavior so a reader can compare runtime mechanics instead of reverse-engineering two unrelated demonstrations.
Callback Families
The Event metadata injector, subscriber, three event sanitizers, five tool registrations,
and six LLM registrations make 16 surfaces. Registration names are component-local;
priority and break_chain are sent in
registration metadata and enforced by the host after it merges visible
middleware.
Inject Event Metadata
Choose a worker language to register a callback that proposes metadata additions:
Python
Rust
Relay sends the immutable Event snapshot through the existing unary Invoke RPC. It
validates and merges accepted additions before Event sanitizers run. A callback error
omits that callback’s additions without dropping the Event. Stopping the worker removes
the registration.
Register Synchronous and Asynchronous Callbacks
Python callbacks can be ordinary functions or coroutines on surfaces that the SDK normalizes. This excerpt uses a synchronous event sanitizer and an asynchronous LLM sanitizer because codec operations call back to the host. All three event sanitizers return the same complete field object.
The equivalent Rust closure always returns a future. The codec proxy is asynchronous in a worker because decode and encode are authenticated host-runtime RPCs.
The codec object is scoped to this invocation. Its opaque capability expires when the callback finishes, even if the worker retained a language-level object that previously referenced it.
Return Complete Request Outcomes
Request intercepts affect real execution. The LLM form returns more than a provider request because Relay must carry its normalized annotation and accounting separately.
Python
Rust
Returning only rewritten would be the wrong callback result for this surface. The tool
request intercept does return JSON directly because it has no annotated request or LLM
optimization accounting.
Continuation Behavior
When the example enables repeat_downstream, it starts two concurrent downstream calls
and returns the first response after both calls settle. The first call alone determines
whether the wrapper succeeds. The second call is intentional demonstration code: it can
still consume provider capacity, incur cost, and cause provider-side effects even though
Relay does not expose its result to the application. Its failure is deliberately ignored.
ToolNext, LlmNext, and LlmStreamNext are host proxies identified by an opaque
continuation ID. Calling one issues a host-runtime RPC under the scope snapshot captured
for that worker invocation. A callback can call a unary proxy zero, one, or multiple
times, including concurrently when the SDK type permits cloning. Each call can repeat
side effects, provider charges, events, and downstream middleware.
ToolNext returns ToolExecutionResult, not a raw JSON value. Forwarding middleware
must preserve both downstream.result and downstream.annotation in its outcome. The
Python example keeps the downstream annotation under upstream while adding its own
worker metadata; the Rust example forwards it unchanged. A repeated tool continuation
returns another independent structured result; it still does not expose downstream
pending marks.
The example uses one ordinary wrapper and one explicitly requested concurrent path. It does not retry implicitly. Tool pending marks remain in the tool execution outcome; LLM annotations, pending marks, and optimization contributions remain in the request intercept outcome. The unary execution callback returns only provider-response JSON.
LlmStreamNext returns a remote stream. The worker transforms each chunk as it arrives
and yields immediately. If the host cancels the invocation or the consumer abandons the
stream, the Python task receives asyncio.CancelledError and the Rust callback future is
aborted. Cleanup belongs in finally or a drop-safe guard. Acknowledged cancellation
does not prove that external blocking work has stopped.
Python
Rust
The second unary result is awaited even though the first response is selected. That prevents an unobserved continuation from outliving the worker callback. In the stream case, each error remains an error item and no chunks are requested before the consumer polls the mapped stream.
Verify the Shared Contract
Use the following procedure to verify equivalent behavior across the two worker SDKs:
- Assert that registration returns exactly 15 unique surface and local-name pairs.
- Exercise all three event sanitizers and both tool and LLM sanitizer directions; prove that only observability values change.
- Block configured tool and model names, rewrite allowed requests, and preserve an annotated LLM request through later request intercepts and the managed start event.
- Call each unary continuation once, then use the explicit repeated path to call it twice concurrently and account for both downstream invocations.
- Consume a transformed stream incrementally, then cancel a second stream and confirm worker cleanup.
- Inspect emitted outcomes to ensure pending marks and optimization contributions are not present in application results.
Success means Python and Rust exhibit the same Relay semantics despite their different callback syntax. The code examples show each callback contract, while the atomic Python example tests and worker SDK integration suites verify callback behavior, authenticated transport, and host invocation.