You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(routing): session-routing plugin layer for per-session router affinity
One plugin category (session_routing) + one CLI flag (--session-routing
<mode>, with repeatable --session-routing-opt key=value) covers every
mechanism for telling an external router which session a request belongs
to, so all of a conversation's turns re-land on the replica holding its
KV prefix. "Session" (live instance keyed by x_correlation_id), not
"conversation" (dataset template), is the affinity identity. Exactly
one mode runs per invocation; the selected plugin is instantiated once
per worker by InferenceClient and invoked at the request-serialization
chokepoint.
Built-in modes:
- dynamo_headers (preset): X-Dynamo-Session-ID plus
X-Dynamo-Parent-Session-ID on subagent children, for a Dynamo frontend
running --router-session-affinity-ttl-secs.
- dynamo_nvext: nvext.session_control request-body metadata -- bind
(with timeout, option timeout_seconds, default 300) on every non-final
turn, close on the final turn -- for Dynamo builds that implement
session_control. Plugin values win over dataset-shipped
session_control keys; other nvext content is preserved.
- smg_routing_key (preset): X-SMG-Routing-Key for the SGLang Model
Gateway manual policy.
- session_id_header (preset): additive X-Session-ID, parameterless.
- identity_headers: fully generic tiered identity headers emitting
exactly what is configured, nothing by default. Options (each a
comma-separable name list): session (this session's correlation ID),
parent (immediate parent's ID, omitted for roots), root (session-tree
root's ID -- whole-tree affinity, e.g. pinning an entire agent tree to
one replica for prefix-cache locality). Requires at least one name
across tiers; names are validated as RFC 9110 tokens at config time
and must be case-insensitively unique across all tiers combined.
Each header preset is documented with its identity_headers equivalent.
Removed (superseded): --use-dynamo-conv-aware-routing /
--use-dynamo-session-control, --use-legacy-dynamo-session-control,
--dynamo-session-timeout-seconds, the dynamo_session_control module,
AIPERF_HTTP_X_SESSION_ID_FROM_CORRELATION_ID,
AIPERF_HTTP_X_SMG_ROUTING_KEY_FROM_CORRELATION_ID, and the
base_transports env-toggle header blocks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anthony Casagrande <acasagrande@nvidia.com>
Copy file name to clipboardExpand all lines: docs/cli-options.md
+7-13Lines changed: 7 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -266,21 +266,15 @@ Use the legacy 'max_tokens' field instead of 'max_completion_tokens' in request
266
266
Use server-reported token counts from API usage fields instead of client-side tokenization. When enabled, tokenizers are still loaded (needed for dataset generation) but tokenizer.encode() is not called for computing metrics. Token count fields will be None if the server does not provide usage information. For OpenAI-compatible streaming endpoints (chat/completions), stream_options.include_usage is automatically configured when this flag is enabled. Recommended whenever the AIPerf tokenizer can disagree with the server's tokenizer (e.g. unmatched tokenizer revision, vendor-specific BPE merges, or chat templates that differ from the server) — this most often shows up as an output sequence length (OSL) mismatch even when the server is honoring the request (e.g. with ignore_eos=true).
Emit Dynamo nvext.session_control in OpenAI-compatible request bodies so Dynamo can bind all turns from the same replayed conversation lineage to the same backend worker. This is only intended for Dynamo frontends that implement session_control.
272
-
<br/>_Flag (no value required)_
273
-
274
-
#### `--use-legacy-dynamo-session-control`
275
-
276
-
Emit the legacy Dynamo nvext.session_control lifecycle that released Dynamo (v1.2.x) understands: action 'open' on the first turn, session_id only on intermediate turns, and action 'close' on the final turn. Use this when the target Dynamo predates the 'bind' action (added in v1.3.0-dev); otherwise 'bind' is rejected with an HTTP 400. Requires --use-dynamo-conv-aware-routing, and the Dynamo deployment must expose a worker session_control endpoint for 'open' to take effect.
277
-
<br/>_Flag (no value required)_
271
+
Session-aware routing mode: stamps per-session identity on every request for router affinity. Presets: dynamo_headers (X-Dynamo-Session-ID + parent header), smg_routing_key (X-SMG-Routing-Key for the SGLang Model Gateway manual policy), session_id_header (additive X-Session-ID). Generic: identity_headers (any name(s) per identity tier -- session/parent/root -- via --session-routing-opt). Body-based: dynamo_nvext (nvext.session_control bind/close request-body metadata; --session-routing-opt timeout_seconds=N).
Dynamo nvext.session_control timeout in seconds when --use-dynamo-conv-aware-routing is enabled.
282
-
<br/>_Constraints: ≥ 1_
283
-
<br/>_Default: `300`_
276
+
Repeatable key=value option for the selected --session-routing mode (e.g. --session-routing-opt timeout_seconds=600), validated against the plugin's Options model. Commas inside the value are passed through to the plugin (repeat the flag for multiple opts).
|`AIPERF_HTTP_REQUEST_CANCELLATION_SEND_TIMEOUT`|`300.0`| ≥ 10.0, ≤ 3600.0 | Safety net timeout in seconds for waiting for HTTP request to be fully sent when request cancellation is enabled. Used as fallback when no explicit timeout is configured to prevent hanging indefinitely while waiting for the request to be written to the socket. |
136
136
|`AIPERF_HTTP_IP_VERSION`|`'4'`| — | IP version for HTTP socket connections. Options: '4' (AF_INET, default), '6' (AF_INET6), or 'auto' (AF_UNSPEC, system chooses). |
137
137
|`AIPERF_HTTP_TRUST_ENV`|`False`| — | Trust environment variables for HTTP client configuration. When enabled, aiohttp will read proxy settings from HTTP_PROXY, HTTPS_PROXY, and NO_PROXY environment variables. |
138
-
|`AIPERF_HTTP_X_SESSION_ID_FROM_CORRELATION_ID`|`False`| — | Also send X-Session-ID with the stable X-Correlation-ID value. Use this when an external router requires a session-affinity header. |
139
-
|`AIPERF_HTTP_X_SMG_ROUTING_KEY_FROM_CORRELATION_ID`|`False`| — | Also send X-SMG-Routing-Key with the stable X-Correlation-ID value. Use this with the SGLang Model Gateway manual routing policy. |
140
138
|`AIPERF_HTTP_VIDEO_POLL_INTERVAL`|`0.1`| ≥ 0.001, ≤ 10.0 | Interval in seconds between status polls for async video generation jobs. Lower values provide faster completion detection but increase server load. Applies to the aiohttp transport. |
|`endpoint`|`EndpointType`| API endpoint implementations (chat, completions, embeddings, etc.) |
129
129
|`transport`|`TransportType`| Network transport (HTTP via aiohttp) |
130
130
131
+
### Session Routing Category
132
+
133
+
| Category | Enum | Description |
134
+
|----------|------|-------------|
135
+
|`session_routing`|`SessionRoutingType`| Stamps per-session identity (headers or body metadata) onto outbound requests so an external router pins every turn of a session to one worker; selected via `--session-routing`|
136
+
137
+
**Purpose.** A session-routing plugin gives an external router (SGLang Model Gateway, Dynamo, a
138
+
generic session-affinity load balancer) the identity of the session a request belongs to, so all of
139
+
a conversation's turns re-land on the replica holding its KV prefix. Exactly one mode runs per
140
+
invocation; the selected plugin is instantiated once per worker by `InferenceClient` and invoked at
141
+
the request-serialization chokepoint. The base class is `SessionRoutingBase`
142
+
(`src/aiperf/workers/session_routing.py`); passthrough is the default (no headers, body unchanged).
143
+
144
+
**Built-ins:**
145
+
146
+
| Name | Class | Description |
147
+
|------|-------|-------------|
148
+
|`dynamo_headers`|`DynamoHeadersRouting`| Dynamo session affinity via `X-Dynamo-Session-ID`, plus `X-Dynamo-Parent-Session-ID` on subagent children. No options. |
149
+
|`dynamo_nvext`|`DynamoNvextRouting`| Dynamo session affinity via `nvext.session_control` request-body metadata (bind on non-final turns, close on the final turn). Only for Dynamo builds that implement `session_control`. Option: `timeout_seconds` (default 300). |
150
+
|`smg_routing_key`|`SmgRoutingKeyRouting`| SGLang Model Gateway `manual`-policy stickiness via `X-SMG-Routing-Key`. No options. |
151
+
|`session_id_header`|`SessionIdHeaderRouting`| Preset: additive `X-Session-ID` header carrying the session's correlation ID. No options. |
152
+
|`identity_headers`|`IdentityHeadersRouting`| Fully generic tiered identity headers: emits exactly what you configure, nothing by default. Options (each a comma-separable name list): `session` (this session's ID), `parent` (immediate parent's ID; omitted for roots), `root` (session-tree root's ID, for whole-tree affinity). Requires at least one name across tiers; names must be unique (case-insensitive) across all tiers combined, including within a single tier. |
153
+
154
+
The three header presets are expressible via `identity_headers`: `session_id_header` ≡
155
+
`session=X-Session-ID`, `smg_routing_key` ≡ `session=X-SMG-Routing-Key`, and `dynamo_headers` ≡
156
+
`session=X-Dynamo-Session-ID` + `parent=X-Dynamo-Parent-Session-ID`. Reach for `identity_headers`
157
+
when a router needs custom names, several headers stamped with the same value (layered routers),
158
+
or tree-scoped affinity; reach for a preset when it already says exactly what you mean.
159
+
160
+
**Options (`--session-routing-opt key=value`).** Each plugin exposes an `Options` Pydantic model
161
+
(`extra="forbid"`, so unknown keys are rejected at startup). Repeated `--session-routing-opt`
162
+
pairs populate it; values are coerced to the model's field types and canonicalized at config
163
+
resolution, so downstream code always sees typed values. Commas inside a value are passed through
164
+
to the plugin (repeat the flag for multiple opts). `--session-routing-opt` without
165
+
`--session-routing` is an error, and parameterless modes (`dynamo_headers`, `smg_routing_key`,
166
+
`session_id_header`) reject every opt key.
167
+
168
+
**`mutates_body` and the PAYLOAD_BYTES fast path.** A plugin sets the `mutates_body` class var to
169
+
`True` when `transform_body` changes the payload (only `dynamo_nvext` does). Body-mutating modes are
170
+
incompatible with the verbatim PAYLOAD_BYTES mmap fast path and are gated off it at three points:
171
+
dataset build, cache hit, and runtime. Header-only modes leave the body untouched and keep the fast
172
+
path. `transform_body` must never mutate its input — the structured path shares cached
173
+
`Turn.raw_payload` dicts with the dataset — so it returns a new dict.
174
+
175
+
**`on_session_end` contract.** Fires strictly after the session's last worker-side activity, on
176
+
every terminal path (final turn, cancellation, terminal context overflow, cancel-before-start). It
177
+
is post-session cleanup only and must be idempotent (default no-op).
178
+
179
+
**Stateful-plugin rule.** A stateful plugin must key its instance state on `ctx.x_correlation_id`
180
+
only. A session tree deliberately spans workers, so tree-keyed worker state fragments across
181
+
processes. For tree-scoped behavior, use the stateless per-request context facts
182
+
`root_correlation_id` and `is_tree_final` instead of accumulating state.
0 commit comments