feat(dynamo): opt-in lineage-scoped session affinity#14
Conversation
Add --dynamo-session-affinity-scope {conversation,lineage} (default
conversation, no behavior change). With lineage, fork/spawn child
conversations bind Dynamo session affinity with the agent-tree root
correlation id so the whole lineage co-locates with the shared parent
prefix; children never close the shared key (only the root conversation's
final turn closes, TTL covers leftovers).
Try out this PRQuick install: pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@ea59b0137aba0a8e894d3bb268954bf236914930Recommended with virtual environment (using uv): uv venv --python 3.12 && source .venv/bin/activate
uv pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@ea59b0137aba0a8e894d3bb268954bf236914930Last updated for commit: |
|
Positioning note: this flag is intended as an interim, client-side measure. The longer-term home for subagent co-location is a router-side placement policy consuming the dedicated parent/agentic identity hints — filed as ai-dynamo/dynamo#11343. Once such a policy exists, this flag simply stays off (default behavior is unchanged either way). |
…finity 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. Option scope=lineage binds every session in an agent tree with the tree ROOT's correlation ID so the whole lineage co-locates on the worker holding the shared parent prefix (for deployments without KV-event prefix indexing); the shared key must never be torn down while sibling sessions may still run, so close fires only on a request the issuer stamped is_tree_final (conservative-exact under agentic replay) with the session_control TTL reclaiming the key under indeterminate modes. Lineage-scope design and motivation from SemiAnalysisAI#14 (YAMY1234). - 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: YAMY1234 <yamy1234@users.noreply.github.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Anthony Casagrande <acasagrande@nvidia.com>
|
Heads-up: #15 (session-routing plugin layer, targeting 🤖 Generated with Claude Code |
Adds scope={conversation,lineage} to the dynamo_nvext mode
(--session-routing-opt scope=lineage; default conversation, unchanged
behavior). Under lineage, every session in an agent tree binds
nvext.session_control with the tree ROOT's correlation ID instead of its
own, so fork/spawn children co-locate on the worker (and data-parallel
rank) already holding the shared parent prefix -- an explicit client-side
affinity for Dynamo deployments without KV-event prefix indexing, where
an unbound child request is otherwise placed arbitrarily and re-prefills
the shared parent prefix once per child.
Close discipline: a shared affinity key must never be torn down while
sibling sessions may still run (a straggler's next bind would re-place it
arbitrarily and lose co-location), so under lineage the close fires only
on a request the credit issuer stamped is_tree_final -- provably the last
request of the whole tree, available under agentic replay, where the
has_branches gate guarantees a branch-declaring final turn never reads as
tree-final while children are pending. Under indeterminate modes
(reactive dag_jsonl) nothing closes and the session_control TTL reclaims
the key.
Lineage-scope design and motivation from SemiAnalysisAI#14
(YAMY1234), rebased onto the session-routing plugin layer: the flag
surface there (--dynamo-session-affinity-scope + EndpointDefaults +
EndpointInfo plumbing) collapses into this one Options field, and the
root-final-turn close is replaced by the is_tree_final discipline.
Verified with back-to-back --export-level raw mock-server runs of
--scenario inferencex-agentx-mvp against the PR ai-dynamo#14 implementation:
child binds are wire-identical (keyed on the tree-root ID in both), and
the lineage close fired exactly once -- on the provably-last wire request
of a fully-drained tree.
Co-Authored-By: YAMY1234 <yamy1234@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Anthony Casagrande <acasagrande@nvidia.com>
|
Update: your lineage-scoped affinity now lives as a dedicated, co-authored commit on the #15 branch — What carried over vs. what changed, relative to this PR:
Verified with back-to-back Thanks for the design — the motivation writeup (KV-event-index-less placement, the sibling-teardown hazard) transferred directly. This PR still applies as-is to its 🤖 Generated with Claude Code |
Summary
Adds an opt-in
--dynamo-session-affinity-scope {conversation, lineage}endpoint option (defaultconversation— no behavior change).With
lineage, fork/spawn child conversations bind Dynamo session affinity (nvext.session_control) with the agent-tree root correlation id instead of their own conversation id, so the whole lineage co-locates on the worker (and data-parallel rank) that already holds the shared parent prefix.Motivation
With
--use-dynamo-conv-aware-routing, each child conversation currently binds its ownsession_id. As the note indynamo_session_control.pyexplains, cross-branch KV reuse is expected to survive because a child's first (still-unbound) request is placed by Dynamo's prefix-overlap router onto the worker already holding the shared prefix.That expectation holds only when the deployment has KV-event prefix indexing configured. On Dynamo deployments without a KV-event index, the router has no prefix knowledge for unbound requests: each child conversation's first turn is placed arbitrarily and re-prefills the shared parent prefix — once per spawned child. Since agentic traces are dominated by subagent requests sharing large parent prefixes, this loses a significant amount of prefix-cache reuse.
lineagescope gives such deployments an explicit, client-side way to keep an agent tree together, without requiring any server-side configuration.Close semantics
A shared affinity key must not be torn down while sibling conversations may still be running — exactly the hazard the existing docstring warns about. Under
lineage:bind(neverclose); every bind refreshes the TTL;close;Notes
conversationkeeps today's behavior unchanged.