Skip to content

feat(planner): add the live retune configuration and deployment premises - #17734

Merged
tedzhouhk merged 10 commits into
hzhou/dgdr-trace-pvcfrom
hzhou/live-retune-2-config
Nov 2, 2026
Merged

tedzhouhk merged 10 commits into
hzhou/dgdr-trace-pvcfrom
hzhou/live-retune-2-config

Conversation

@tedzhouhk

@tedzhouhk tedzhouhk commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Note

Stacked PR, live retune (split from #17562). Merge order: #17483 → #17733 → #17734 (this PR) → #17735 → #17736 → #17562

This PR targets hzhou/live-retune-1-crn, the branch of #17733 (the PR below it), so its diff shows only this layer. #17562 was split into these layers for review. At the split they carried #17562's original diff exactly, plus the 40-line replay check that this PR adds; the review follow-up of 2026-11-02 has since changed several layers.

Overview

This is the second layer of live retune (#17562), which adds retune.executor: dgdr. That executor runs periodic retune on a live Planner on Kubernetes through DGDR v1beta2 Requests. This layer adds the configuration surface, the checks that decide whether a deployment can run live retune, and the shared types that the later layers build on.

Nothing runs a live retune in this layer:

Key Changes

About 2.9k lines of implementation (0.2k of them in the frontend and common configuration modules) and 2.6k lines of tests.

planner/config/planner_config.py:

  • RetuneConfig.executor (local by default, or dgdr) and RetuneConfig.dgdr, which is required if and only if the executor is dgdr:
    • DgdrExecutorConfig: window phase, the ReadWriteMany claim and its segment and window directories, an optional startup_router_config, the model, the SKU, the pinned AISimulate engine, the cut budget, Job start-up time, keep_requests, max_window_rows, promote_last_advisory, the Lease duration and the actuator interval.
    • DgdrRunConfig: parallelism, candidate_timeout_seconds and max_shards (at most 16).
  • For dgdr, the configuration premises P1–P3, P5 and P13 are validated:
    • P1: environment kubernetes; decision_latency_seconds 0, or measured with scale 1; an explicit timeout_seconds of at most interval_seconds − 600.
    • P2: retune-only mode (load and throughput scaling both off) and no retune.planner.
    • P3: only the three knobs a DGDR Request can search (overlap_score_credit, prefill_load_scale, temperature), with prefill_load_model none.
    • P5: an all-scalar engine that matches the planner mode and backend, with a single parallelism preset and no replicas.
    • P13: the grid splits into at most run.max_shards shards of at most 100 points, and the largest shard's need(n) ≤ timeout_seconds. P13 plans the shards with the same call the executor (feat(planner): run a live retune sweep through DGDR v1beta2 Requests #17736) makes for each window, so both see the same largest shard.
    • The claim paths must also be safe: relative directories inside the claim that do not contain one another, and an absolute mount path.
  • One DGDR shard planner that P13 and the executor share: balanced_shard_parts(sizes, max_shards, *, parallelism) minimizes the evaluation rounds of the largest shard first, then the shard count; largest_shard_points, dgdr_rounds and dgdr_need_seconds give that shard's points, rounds and wall time.

New package components/src/dynamo/planner/retune/live/:

  • contracts.py: the shared types, which import nothing else from the package. They include the eight /router_policy parameters, the DGDR knob name map, the Request labels, the DGDR group, version and plurals, NON_DECISION_FAILURES, the Kube* errors, and the protocols the later layers implement or consume (RetuneKube, RouterPolicyClient, SweepRegistry, WindowCutter, RetuneMetricsSink).
  • frontend_config.py: parses the DGD's frontend command, args and env into the startup KvRouterConfig. It reads the DGD as v1beta1 (spec.components), as the planner reads every DGD. It applies the operator's defaults, the graph and container env and $(VAR) expansion, and parses with the frontend's own parser from build_frontend_parser(env) (below), without reading or changing the Planner's own environment. evaluate_premises(dgd, *, planner_config) parses the frontend itself and evaluates the spec premises: P4 (every unsearched router field is at its default; this also fails when a worker advertises its own router configuration), P7–P10 (P9 reads the planner replicas from the DGD), and the P6 and P7 warnings. P6 treats only an ImportError as AISimulate being absent; any other failure is logged and reported as a P6 warning. envFrom produces only a warning. All functions are pure and make no Kubernetes calls. P14 belongs to the router actuator in feat(planner): enable live retune through DGDR v1beta2 #17562.
  • __init__.py: the package docstring.

Frontend and common configuration (frontend/frontend_args.py, common/configuration/utils.py, common/configuration/groups/router_args.py and kv_router_args.py):

  • ArgEnv carries another process's env. env_or_default, add_argument, add_negatable_bool_argument, RouterArgGroup, KvRouterArgGroup, FrontendArgGroup and add_worker_router_arguments take an optional env. With one, every env default comes from it, deprecation and removal notices are skipped, nothing reads the local filesystem (no --model-path check, no AISimulate perf-model arguments), and ArgEnv.env_dests records the env var → dest of every argument registered.
  • frontend_args.build_frontend_parser(env) returns the frontend's parser for that env and the env var → dest map.
  • Without env (every existing caller), parsing, notices and checks are unchanged.

planner/retune/sweep.py: SweepRequest gains the live fields router_domain, deadline_s, spec_replicas, replicas_verified and generation. SweepResult gains failure_reason and details. Replay leaves them at their defaults, and run_sweep ignores them. They are in this layer because #17736's executor uses them.

Tests:

  • unit/test_live_retune_config.py and unit/test_live_retune_frontend_config.py: every configuration and spec premise rule.
  • frontend/tests/test_frontend_parser_builder.py: build_frontend_parser reads only the given env, leaves os.environ unchanged, and maps env vars to dests. common/tests/configuration/test_utils.py: ArgEnv replaces os.environ, skips deprecation notices and records the dests.
  • The shared fakes (tests/live_retune_fakes.py) moved to feat(planner): run a live retune sweep through DGDR v1beta2 Requests #17736, whose executor tests use them.

Replay check (40 lines, new in the split; #17562 had no such check):

  • replay/api.py _retune_inputs raises planner.retune executor 'dgdr' is not supported in replay; replay retune runs executor 'local'. _retune_inputs is the only retune_inputs_factory, so the guard covers run_trace_replay, and aisimulate predict --stack dynamo through it. A caller that builds retune inputs itself and passes them straight to the replay planner adapter is not covered; no non-test code does that.
  • replay/tests/test_retune_replay.py::test_retune_rejects_the_dgdr_executor.

Validation

Ran on this layer (c99516cf5f, before the review follow-up) and compared with the same suites at the stack base 6e8087d359 (#17483's head):

Check Result
test_live_retune_config.py, test_live_retune_frontend_config.py and test_retune_rejects_the_dgdr_executor 300 passed
Planner and replay suites (components/src/dynamo/planner/tests, components/src/dynamo/replay/tests, no xdist) 59 failed, 2437 passed, 2 skipped. The failing tests are the same 59 as at the base (59 failed, 2131 passed).
pre-commit run --files on the 10 changed files All hooks pass, no files changed
CI mypy (the dynamo-pipeline.yml command, MYPYPATH=components/src:lib/bindings/python/src mypy --explicit-package-bases) on dynamo/planner and dynamo/replay The same 9 errors as at the base (missing yaml/pandas/nvtx stubs and existing arg-type errors); none is new
Behavior probe A dgdr PlannerConfig validates. Constructing a live Planner with it fails in NativePlannerBase.__init__. run_trace_replay with it fails at the guard.

The 59 failures come from this host. Its prebuilt bindings lack conversation_affinity (#16854) and the ais-forward-pass feature, and it has no etcd binary. CI builds the bindings from source.

After the review follow-up, the tests ran at the top of the stack (#17562's head 92cee14c88), where this layer's files differ only by #17736's SupportsIsSet in contracts.py and #17562's planner.retune description: the planner unit tests, the live retune e2e test, the frontend parser builder tests and the common configuration tests gave 2463 passed and 3 skipped (trtllm, sglang and vllm are not installed). pre-commit from 8d098bed48 to 92cee14c88 is clean. Per-layer runs, the replay suite and mypy were not part of that run.

This layer adds library code that nothing reachable calls until #17562. The retune.live modules are imported only by tests. core/, __main__.py, retune/controller.py and plugins/ are unchanged. The frontend and common configuration changes add an optional env parameter that only build_frontend_parser and frontend_config pass.

Issues and Ongoing Work

Review follow-up (2026-11-02)

Frontend parsing:

  • frontend_config no longer copies the frontend's argument functions with overridden globals or touches router_args' private action classes. The frontend and common configuration modules gained the public ArgEnv and build_frontend_parser(env), and the router, KV router and frontend argument groups take an optional env. This layer now changes those modules.

Configuration and premises:

  • P13 and the executor use one shard planner in planner_config (balanced_shard_parts with the Run parallelism, largest_shard_points, the now public dgdr_rounds, and dgdr_need_seconds).
  • evaluate_premises(dgd, *, planner_config) derives the frontend startup, the parse error and the planner replicas from the DGD itself.
  • Removed the unused trace_incomplete_threshold, the v1alpha1 DGD layout, the graph spec.envs fallback, and envFrom from the parse cache key.
  • P6 treats only ImportError as AISimulate being absent; other failures are logged and reported as a P6 warning.

Clean-up:

Second review follow-up (2026-11-02)

  • retune.controller.startup_router_domain holds the router checks that depend on the frontend startup (zero credit, grid, load model, policy config); the controller and P4 both call it, so P4 cannot pass a domain the controller then rejects.
  • A builder test sets every env var the frontend parser registers to a sentinel in os.environ and checks that build_frontend_parser({}) still returns the empty-env defaults.
  • Removed startup_router_config and its P4 check; balanced_shard_parts requires parallelism.
  • Tests build configs through validation; branches only impossible configs reached are gone; the P10 table keeps one row per branch.
  • contracts.py keeps only FrontendStartup and PremiseReport; the other types and the live SweepRequest/SweepResult fields moved to the layers that first use them.

Validation at df3a979458: planner unit 1733 passed; frontend and common configuration tests 373 passed, 4 skipped (engines not installed).

Pull Request Checklist
  • I used Dynamo's /pr-checklist skill.

🤖 Generated with Claude Code

tedzhouhk and others added 2 commits October 30, 2026 15:48
DynamoReplayRunner now replays every candidate of a trace sweep with
_deterministic=True: ordinal request ids and the canonical router seed.
Candidates of one trace then differ only by their configuration, so a
candidate's score does not depend on the other candidates, the Run or the
parallelism. Live planner retune compares candidates that different DGDR
Runs of one window scored on that basis. The synthetic-workload path is
unchanged.

First layer of the live retune stack split from #17562.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Add retune.executor ('local' | 'dgdr') and the retune.dgdr section
(DgdrExecutorConfig, DgdrRunConfig) to the planner config, with the
configuration premises of live retune: P1 (Kubernetes, decision latency,
explicit timeout), P2 (retune-only, router-only), P3 (DGDR router knobs),
P5 (a pinned, scalar AISimulate engine), safe claim paths and P13 (the
largest grid shard finishes within the sweep deadline).

Add the dependency-free live retune contracts (retune/live/contracts.py),
the frontend premise checks that parse the startup router config from the
DGD (retune/live/frontend_config.py), the live fields of SweepRequest and
SweepResult (router_domain, deadline_s, spec_replicas, replicas_verified,
generation; failure_reason, details), which replay leaves at their
defaults, and the shared test fakes.

Nothing runs a live retune yet: a live Planner still rejects any retune at
startup (NativePlannerBase), and the planner.retune description still says
replay-only; the last layer (#17562) wires the live Planner and updates that
description. Replay's _retune_inputs now rejects executor 'dgdr' (with a
test): replay runs local sweeps only, and before this check it accepted a
dgdr configuration and silently ran local sweeps instead.

Second layer of the live retune stack split from #17562.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
@tedzhouhk
tedzhouhk requested review from a team as code owners October 30, 2026 23:42
@github-actions github-actions Bot added the feat label Oct 30, 2026
tedzhouhk added a commit that referenced this pull request Oct 30, 2026
Stacks this PR on #17736. The library layers of live retune now come from
#17733 (CRN replay), #17734 (configuration and premises), #17735 (window
cut and reconciler) and #17736 (DGDR executor), so this PR's diff against
#17736 is the supervisor, state, actuator, metrics, the Planner wiring,
Helm RBAC, docs and the end-to-end tests. #17734 also adds a replay check
that rejects executor 'dgdr'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
@devin-ai-integration

devin-ai-integration Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

✅ Dynamo PR CI passed — run 41524033037 (attempt 2) on df3a979458

Gate checks: ✅ backend-status-check · ✅ deploy-status-check · ✅ dynamo-status-check

Posted automatically by Devin for run 41524033037. Updated on every full-CI run of this PR.

@jthomson04 jthomson04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 30dd626. Should-fix inline: an unused but documented knob, the _rebind parser approach, a dead v1alpha1 branch, and shard math duplicated with #17736. The rest are nits.

tedzhouhk and others added 5 commits November 2, 2026 08:26
Fold the trace replay determinism check into
test_trace_runner_preserves_current_replay_arguments with an agg and a
disagg row, and drop the separate test file. Cut the runner comment to
the invariant, and update replay/api.py: _deterministic now applies to
every trace replay through the Sweeper's Dynamo runner, not only to
retune sweeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
…mises

- Parse the frontend's and workers' router flags through a public parser
  builder: ArgEnv in dynamo.common.configuration.utils carries another
  process's env, the router, KV router and frontend argument groups take
  it, and frontend_args.build_frontend_parser(env) returns the parser and
  its env var -> dest map. Every env default comes from that env, notices
  are skipped and nothing reads the local filesystem, so frontend_config
  no longer copies functions with overridden globals or touches
  router_args' private action classes.
- Keep one DGDR shard planner: balanced_shard_parts takes the Run
  parallelism and minimizes evaluation rounds first, dgdr_rounds is
  public, and P13 plans with the same call the executor will use.
- evaluate_premises(dgd, planner_config=...) derives the startup and the
  parse error itself and reads the planner replicas from the DGD.
- Drop the unused trace_incomplete_threshold, the v1alpha1 DGD layout and
  the graph spec.envs fallback, and envFrom from the parse cache key.
- P6 treats only ImportError as "aisimulate absent"; any other failure is
  logged and reported as a P6 warning.
- Move the function-body imports to the module top, remove references to
  the design document, and move live_retune_fakes.py to the DGDR sweep
  layer, which uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
@tedzhouhk
tedzhouhk requested review from a team as code owners November 2, 2026 17:27
@github-actions github-actions Bot added the frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` label Nov 2, 2026

@jthomson04 jthomson04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at ba2a372. All nine comments are addressed: one shard-math implementation shared with #17736, a public build_frontend_parser, no v1alpha1 branch, no trace_incomplete_threshold. Two should-fix items inline (a second copy of the controller's domain checks, and nothing guarding the parser's env isolation); the rest are trims, about 200 lines.

Separately, this PR uses only FrontendStartup and PremiseReport from contracts.py. The kube, policy, actuation, reconciler, window and metrics types (most of its 471 lines) and the live SweepRequest/SweepResult fields could land with the PRs that use them.

tedzhouhk and others added 3 commits November 2, 2026 11:54
- P4 runs the controller constructor's own startup router checks:
  RetuneController._router_domain and live retune's _startup_errors both
  call the new module-level controller.startup_router_domain, replacing
  the copy in frontend_config.
- Guard build_frontend_parser's env isolation: a test sets every env var
  the parser registers to sentinels in os.environ and expects the
  empty-env defaults.
- Premise tests build validated PlannerConfigs; drop the tests and the
  branches only impossible configs reached (no router domain, a
  slash-wrapped segments_dir, an unexpandable or non-'none' load model).
- Drop retune.dgdr.startup_router_config and its P4 check.
- balanced_shard_parts requires parallelism; drop the points-only ranking
  and its tests.
- One P10 table row per branch.
- contracts.py keeps FrontendStartup and PremiseReport; the other types
  and the live SweepRequest/SweepResult fields move to the PRs that use
  them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: hongkuanz <hongkuanz@nvidia.com>
@tedzhouhk
tedzhouhk changed the base branch from hzhou/live-retune-1-crn to hzhou/dgdr-trace-pvc November 2, 2026 23:57
@tedzhouhk
tedzhouhk merged commit 2e6e39e into hzhou/dgdr-trace-pvc Nov 2, 2026
196 of 213 checks passed
@tedzhouhk
tedzhouhk deleted the hzhou/live-retune-2-config branch November 2, 2026 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` planner size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants