Streamed ExtProc and immediate responses
This guide explains how to run vLLM Semantic Router behind an Envoy-compatible gateway when request bodies are delivered to ExtProc in streamed mode, and how streamed clients receive Semantic Router immediate responses such as looper, semantic-cache, and fast_response results.
Use this guide when you need one of the following:
- large OpenAI-compatible request bodies that should not be fully buffered by the gateway before ExtProc sees them;
- agentgateway
FullDuplexStreamedExtProc processing; - Envoy AI Gateway or raw Envoy
STREAMEDrequest body processing; - streamed Chat Completions clients (
"stream": true) that may be short-circuited by Semantic Router before the upstream backend responds.
How it works
Semantic Router is an Envoy External Processor. In buffered mode the gateway sends the full request body in one ExtProc message. In streamed mode the gateway sends multiple body chunks. Semantic Router's streamed body handler accumulates the chunks, applies the same routing and mutation pipeline at end-of-stream, and then emits one complete mutated request body or an immediate response.
For streamed Chat Completions responses, immediate responses keep OpenAI-compatible behavior:
- looper algorithms return
Content-Type: text/event-streamwhen the original request has"stream": true; - looper responses include
x-vsr-looper-*headers such asx-vsr-looper-model,x-vsr-looper-models-used,x-vsr-looper-iterations, andx-vsr-looper-algorithm; - non-streaming immediate responses, including many
fast_responseblocks, return a complete JSON response immediately; - Response API requests are translated back through the Response API layer, so looper execution is forced to non-streaming internally for those requests.
"Streamed request body" and "streamed model response" are separate knobs. request_body_mode: STREAMED or requestBodyMode: FullDuplexStreamed controls how the gateway sends the request body to Semantic Router. The OpenAI request field "stream": true controls whether the client expects Server-Sent Events from the final model or immediate response.
Semantic Router configuration
Enable streamed request body handling in the Semantic Router runtime config. The setting lives under global.router.streamed_body in the canonical config.
global:
router:
streamed_body:
enabled: true
max_bytes: 10485760 # reject larger accumulated bodies with 413
timeout_sec: 30 # reject slow body accumulation with 408
Keep max_bytes high enough for your largest prompt or multimodal payload. Keep timeout_sec greater than the expected upload time between the first body chunk and end-of-stream.
The 10 MiB and 30-second values above are example guardrails matching the
streaming e2e profile in e2e/profiles/streaming/values.yaml; they are not
runtime defaults or experimentally calibrated limits. Omitting either value or
setting it to zero disables that guard. The reference config/config.yaml
demonstrates a smaller 1 MiB and 15-second policy.
Envoy AI Gateway / Envoy Gateway
For Envoy AI Gateway examples that use EnvoyPatchPolicy, change the Semantic Router ExtProc filter from buffered request bodies to streamed request bodies.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: ai-gateway-prepost-extproc-patch-policy
namespace: default
spec:
jsonPatches:
- name: default/semantic-router/http
operation:
op: add
path: /default_filter_chain/filters/0/typed_config/http_filters/0
value:
name: semantic-router-extproc
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
allowModeOverride: true
grpcService:
envoyGrpc:
authority: semantic-router.vllm-semantic-router-system:50051
clusterName: semantic-router
timeout: 60s
messageTimeout: 60s
processingMode:
requestHeaderMode: SEND
requestBodyMode: STREAMED
requestTrailerMode: SKIP
responseHeaderMode: SEND
responseBodyMode: BUFFERED
responseTrailerMode: SKIP
The important fields are:
requestBodyMode: STREAMEDso request chunks are sent to ExtProc;allowModeOverride: trueso Semantic Router can request per-route response-body processing changes when needed;messageTimeoutandgrpcService.timeoutlarge enough for classification and body accumulation.
A complete Kubernetes example is available in deploy/kubernetes/streaming/aigw-resources/gwapi-resources.yaml.
agentgateway
agentgateway uses the Gateway API AgentgatewayPolicy abstraction rather than raw Envoy processing_mode names. For streamed bodies use FullDuplexStreamed.
Buffered request bodies remain common in proxy defaults and other deployment
examples. The bundled agentgateway example opts into streaming explicitly in
deploy/kubernetes/agentgateway/extproc-policy.yaml; the Helm command in the
agentgateway installation guide explicitly enables
global.router.streamed_body. Use both settings together when adopting that
example.
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
name: semantic-router-extproc
namespace: agentgateway-system
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: agentgateway-proxy
traffic:
extProc:
backendRef:
name: semantic-router
namespace: agentgateway-system
port: 50051
processingOptions:
requestHeaderMode: Send
requestBodyMode: FullDuplexStreamed
responseHeaderMode: Send
responseBodyMode: Buffered
requestTrailerMode: Send
responseTrailerMode: Send
allowModeOverride: true
agentgateway does not support a separate Streamed request-body mode. Use FullDuplexStreamed for streamed request bodies and enable global.router.streamed_body in Semantic Router.
Semantic Router detects the negotiated ExtProc body mode. With
FullDuplexStreamed, it buffers intermediate request chunks without emitting
body replacements, then sends the complete processed request as one
end-of-stream StreamedBodyResponse. With Envoy STREAMED, it retains the
one-response-per-chunk behavior required by that mode.