Skip to content

[NV] [PR1] feat(synthetic-al): generic synthetic-acceptance injection scaffold#2088

Open
qiching wants to merge 2 commits into
mainfrom
albecheng/synthetic-al-generic
Open

[NV] [PR1] feat(synthetic-al): generic synthetic-acceptance injection scaffold#2088
qiching wants to merge 2 commits into
mainfrom
albecheng/synthetic-al-generic

Conversation

@qiching

@qiching qiching commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

root added 2 commits July 5, 2026 18:36
Two changes needed to run dsv4 fp4 multinode sweeps on the watchtower
(Oracle) gb200 cluster:

- Point the dynamo-vllm dsv4 MODEL_PATH at the base DeepSeek-V4-Pro
  checkpoint instead of the -NVFP4 re-quant. The pinned v0.20.1
  container's deepseek_v4 loader doesn't define the NVFP4 export's extra
  quant params (e.g. ffn.experts.w13_input_scale) and KeyErrors at load;
  the base checkpoint loads and matches the recipe's served identity.
- Add dsv4/dynamo-vllm to uses_watchtower_shared_fs() so the srt-slurm
  workspace/outputs land on a compute-visible shared FS (the runner home
  is not cross-mounted to compute nodes, otherwise the batch step dies).
Framework-agnostic plumbing for optionally overriding a recipe's
speculative-decoding acceptance length with a synthetic value, gated on
SYNTHETIC_ACCEPTANCE=true (no-op otherwise, so this commit changes no
existing behavior).

- runners/synthetic_injectors/: backend registry (register/get_injector).
  No backends registered yet -> the generic layer alone injects nothing.
- runners/inject_synthetic_acceptance.py: generic driver. Handles flag
  gating, acceptance-length resolution (explicit env or reference-yaml
  lookup) and file I/O, then delegates the recipe rewrite to the
  framework backend resolved from FRAMEWORK. Errors clearly if the flag
  is on but no backend is registered.
- runners/launch_gb200-nv.sh: invoke the driver after the name override
  and before srtctl apply. With no backend registered this is a no-op.
@qiching qiching marked this pull request as ready for review July 8, 2026 17:05
@qiching qiching requested a review from a team July 8, 2026 17:05
@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Claude finished @qiching's task in 4m 6s —— View job


Review of PR #2088 — synthetic-acceptance injection scaffold

  • Gather context (PR diff vs origin/main, read all 3 changed files)
  • Review runners/inject_synthetic_acceptance.py (driver logic, env handling, YAML lookup)
  • Review runners/synthetic_injectors/__init__.py (registry scaffold)
  • Review runners/launch_gb200-nv.sh changes (injection hook, dsv4 fp4 path, shared-FS routing)
  • Post findings

Summary

Found 1 issue (posted inline): the injection hook at runners/launch_gb200-nv.sh:476 doesn't propagate failures — the script has no set -e, and every error path in the injector is sys.exit(1), so with SYNTHETIC_ACCEPTANCE=true a failed injection (which is every opted-in run in this PR, since the registry is empty) silently degrades to a vanilla run whose results are then mislabeled as synthetic-acceptance. Suggested fix is appending || exit 1, matching the idiom already used elsewhere in this script.

Everything else checks out: the no-op default path is genuinely behavior-preserving, the synthetic_injectors import resolves correctly (script dir is on sys.path), the missing benchmarks/speedbench-reference-al.yaml is handled with an explicit error and is unreachable until the follow-up backend PR, and the dsv4 fp4 MODEL_PATH / uses_watchtower_shared_fs changes are deliberate and well-documented.

# Optionally inject synthetic acceptance into the recipe's speculative-config
# when SYNTHETIC_ACCEPTANCE=true (no-op otherwise). Must run after the name
# override and before srtctl apply so the rendered job picks it up.
python3 "$GITHUB_WORKSPACE/runners/inject_synthetic_acceptance.py" "$CONFIG_PATH" "$FRAMEWORK"

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.

🟡 WARNING: Injector failures are silently swallowed — the run proceeds without synthetic acceptance.

Why it matters: This script has no set -e (only set -x). Every failure path in inject_synthetic_acceptance.py is a sys.exit(1) — no registered backend, missing benchmarks/speedbench-reference-al.yaml, unknown model key, unmatched num_spec_tokens, or an ImportError if PyYAML is absent from the venv. With SYNTHETIC_ACCEPTANCE=true, any of those prints an error into the set -x log noise and the launch continues, submitting the benchmark without injection — producing results labeled as synthetic-acceptance that actually ran vanilla MTP. In this PR the registry is empty, so every opted-in run would hit exactly this path.

Fix: fail the launch on injector error, matching the || exit 1 idiom already used elsewhere in this script:

Suggested change
python3 "$GITHUB_WORKSPACE/runners/inject_synthetic_acceptance.py" "$CONFIG_PATH" "$FRAMEWORK"
python3 "$GITHUB_WORKSPACE/runners/inject_synthetic_acceptance.py" "$CONFIG_PATH" "$FRAMEWORK" || exit 1

Comment on lines +473 to +476
# Optionally inject synthetic acceptance into the recipe's speculative-config
# when SYNTHETIC_ACCEPTANCE=true (no-op otherwise). Must run after the name
# override and before srtctl apply so the rendered job picks it up.
python3 "$GITHUB_WORKSPACE/runners/inject_synthetic_acceptance.py" "$CONFIG_PATH" "$FRAMEWORK"

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.

🟡 The python3 inject_synthetic_acceptance.py invocation at line 476 lacks a || exit 1 guard, and the launcher only sets set -x (no set -e), so any sys.exit("ERROR: …") from the injector prints to stderr but bash proceeds to srtctl apply against the unmodified recipe — the sweep succeeds and produces well-formatted results that a user believing SYNTHETIC_ACCEPTANCE=true took effect would misread as synthetic-acceptance data. Fix is a one-token || exit 1 matching the file's own established pattern (lines 448 mkdir, 455 rsync, and every git-clone block).

Extended reasoning...

What the bug is. The launcher at runners/launch_gb200-nv.sh:476 invokes:

python3 "$GITHUB_WORKSPACE/runners/inject_synthetic_acceptance.py" "$CONFIG_PATH" "$FRAMEWORK"

with no exit-code guard. The file's shebang line runs only set -x (line 5) — grep -n '^set ' confirms no set -e anywhere — so a non-zero exit from the python3 call does not abort the script. Bash continues past it into unset VIRTUAL_ENV and then srtctl apply, submitting the unmodified recipe.

Why the failure paths are reachable on this PR as merged. inject_synthetic_acceptance.py has multiple sys.exit("ERROR: …") paths (missing backend at lines 127–131; missing reference YAML at 89–93; missing MODEL_PREFIX/THINKING_MODE/num_spec_tokens keys at 73/103/113; unhandled ValueError from float() on a non-numeric SYNTHETIC_ACCEPTANCE_LENGTH). On this scaffold PR two of them are guaranteed to fire on any opt-in: runners/synthetic_injectors/__init__.py registers no backends (_INJECTORS = {}, intentional per its docstring), and benchmarks/speedbench-reference-al.yaml does not exist in the tree.

Concrete step-by-step proof.

  1. A user reads the new module's docstring and exports SYNTHETIC_ACCEPTANCE=true before launching a dsv4 dynamo-vllm sweep.
  2. The launcher reaches line 476 and runs python3 …/inject_synthetic_acceptance.py $CONFIG_PATH dynamo-vllm.
  3. Inside inject(), get_injector('dynamo-vllm') returns None (empty registry).
  4. The script calls sys.exit("ERROR: SYNTHETIC_ACCEPTANCE=true but no synthetic-acceptance injector is registered for FRAMEWORK='dynamo-vllm'") → process exits with code 1.
  5. Bash does not stop (no set -e, no || exit 1). The ERROR line prints to stderr, then execution continues at line 478: unset VIRTUAL_ENV.
  6. srtctl apply -f "$CONFIG_PATH" … submits the recipe as it was after the sed name override — i.e. the plain speculative-decoding recipe, no synthetic-acceptance parameters injected.
  7. The sweep completes and writes results_concurrency_*.json files with the standard schema. Downstream tooling and dashboards treat these numbers as synthetic-acceptance measurements.

Why existing safeguards don't catch it. No set -e; no explicit rc=$? check after the python3 call; no downstream assertion that the recipe was modified when SYNTHETIC_ACCEPTANCE=true. The ERROR text is present in stderr but interleaved with set -x trace output; an attentive operator watching a live run would notice, but sweep artifacts (JSON results, pass/fail status) look normal.

Impact. On this PR alone the impact is bounded: the docstring explicitly notes 'merging it changes no behavior' with the flag off, and no existing sweep sets SYNTHETIC_ACCEPTANCE=true. The bug becomes concrete data-integrity risk once the follow-up framework-support PR registers backends and the auto-lookup YAML lands — any remaining failure path (missing MODEL_PREFIX key, non-numeric LENGTH, backend registered for one framework but not another) would still silently mislabel results as synthetic-acceptance data.

How to fix. Add || exit 1 (or explicit rc handling) to line 476 — a one-token change that matches the file's own established pattern for critical operations: line 448 mkdir -p … || exit 1, line 455 rsync … || exit 1, and every git clone/git checkout/cp -rT block in the srt-slurm-setup section. The design contract of an opt-in flag is 'either apply the change or fail loudly'; silently ignoring failure is worse than never having the seam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant