Repository navigation
feat(setup): install ATOM on bare metal and fold it into the 12h demos; fix the failures of its 12h release run - #1694
Conversation
c8d1f7c to
8d29267
Compare
Bare-metal setup could install SGLang and vLLM but not ATOM, so an ATOM workload needed an environment that already shipped it (in practice the rocm/atom-dev image). install_baremetal.sh now accepts --install-framework atom. Like SGLang it installs into the shared interpreter against the existing ROCm torch: AITER first (newest tag compatible with torch/triton, or AITER_REF), then ATOM from source as an editable checkout under torch/triton constraints, then verifies that `atom` and `atom.entrypoints.openai.api_server` import. The server module is imported instead of running `--help`, because ATOM's argparse help text carries unescaped "%" and rendering it raises even though the server starts and serves. ATOM_REF defaults to the ATOM_COMMIT rocm/atom-dev:v0.1.7-rc0 was built from. An isolated framework env is rejected, as for SGLang, and the installed framework is recorded as FRAMEWORK=atom. Preflight lists atom as setup-installable and prints the install command when ATOM is missing; the setup skill, ATOM example and docs describe the new option.
8d29267 to
1873052
Compare
lishuoshuo-amd
left a comment
There was a problem hiding this comment.
PR #1694 -- feat(setup): install ATOM on bare metal with --install-framework atom
What it does: bare-metal setup could install SGLang and vLLM but not ATOM, so an ATOM workload needed a host that already shipped it (in practice the rocm/atom-dev image) and the preflight gate could only tell the operator that setup does not install ATOM. The PR adds the missing arm at the layer that already owns framework installation: install_baremetal.sh accepts --install-framework atom, Phase 2 dispatches to install_atom_framework, which installs AITER through the existing ensure_aiter_for_python and then ATOM as an editable checkout of ATOM_REPO@ATOM_REF under the write_rocm_torch_constraints torch/triton constraint file, verifies that atom and atom.entrypoints.openai.api_server import, records FRAMEWORK=atom, and rejects --framework-env isolated exactly as it does for SGLang; preflight.py adds atom to _SETUP_INSTALLABLE_FRAMEWORKS, so the missing-ATOM error now prints an install command that works, while the importable-but-CUDA-torch case still prints none.
Blocking issues: none
Checked: install_baremetal.sh (the --install-framework case arm, resolve_installed_framework, base_preflight's framework loop and its atom parser probe, install_atom_framework/install_atom_from_source against install_sglang_framework/install_sglang_from_source, ensure_aiter_for_python, write_rocm_torch_constraints, install_requested_framework, write_runtime_dotenv, restore_persisted_framework_env, main), cli/preflight.py _check_serving_framework + _SETUP_INSTALLABLE_FRAMEWORKS + _setup_install_command and all their readers, framework_paths.py discovery of an editable checkout, bypass_engine.py:112 and reference_script.py:515 for the launched module, skills/hyperloom-setup/SKILL.md, both ATOM examples, .env.template. Upstream at the pin: atom/entrypoints/openai_server.py is a shim importing atom.entrypoints.openai.api_server (USE_ATOMESH_ENTRYPOINTS defaults to 0), ATOM_MESH_BUILD defaults to 0, ATOM declares no ext_modules and no submodules, aiter is a hard runtime import and is absent from ATOM's pyproject, atom/model_engine/arg_utils.py:441 carries the unescaped % and rendering that help raises TypeError (reproduced), and fe1099b15ddc52e6873e1932935f722864cdeee0 matches ATOM_COMMIT=fe1099b15 in the build history of rocm/atom-dev:v0.1.7-rc0 read from its registry config blob. | Ran: pytest test_setup_cli.py -k atom, test_preflight_serving_framework.py, test_dotenv_precedence_recipes.py, test_install_baremetal_vllm_target.py at head (all pass); the same new tests against the merge base with only the test files copied in: 13 failed, 63 passed, so every added test falsifies. | Base: ec95c36 | Head: 1873052
The 12h demo already selects SGLang or vLLM through FRAMEWORK, and the 12h forge demo is the same run with the KernelForge backend. ATOM had a separate pair (hyperloom-qwen3-14b-fp8-12h-atom and -atom-forge) that repeated both, so the framework and backend axes were mixed into four skills. ATOM is now a framework option of the 12h demo: its "## ATOM" section carries the ATOM-specific run mode, execution shell, Docker/baremetal entry, Python selection and setup (including --install-framework atom), cleanup, launch, resume and status checks. The 12h forge demo points at that section and adds only the KERNEL_OPT_BACKEND_ORDER=forge export. The two ATOM example directories, their packaged skill entries and the setup skill's ATOM routing are removed; the recipe tests now execute the 12h demo's ATOM section.
ATOM registers vllm.platform_plugins, vllm.general_plugins and sglang.srt.plugins entry points. vLLM and SGLang load every registered plugin by default, so after --install-framework atom a later vLLM run on the same Python selects ATOMPlatform and serves Qwen3ForCausalLM through ATOM's wrapper, and SGLang runs with ATOM's loader and CUDA-graph patches. The installer now refuses to put ATOM and vLLM/SGLang into one interpreter: --install-framework atom dies when SGLang or vLLM imports from the selected Python or from a system-site-packages vLLM overlay over it, and --install-framework sglang, shared vLLM and ROCm 10 source vLLM die when ATOM imports from that Python. The self-contained isolated vLLM wheel venv is unaffected.
…raceLens patch A source-built vLLM in the isolated venv is installed editable, so site-packages holds only vllm.egg-link and the package lives in the checkout. _probe_isolated_vllm only globbed site-packages/vllm, returned None, and the TraceLens runtime patch was skipped, degrading every profile of a baremetal vLLM source build. Resolve the install root from the venv interpreter's vllm.__file__ when site-packages has no package.
…ules a kernel non-patchable The active finder's symbol-detected non-patchable verdict is stamped with source_resolution_method=active_finder but names no source file. The artifact projection echoed that stamp, validate_document rejected the entry (a location method with no source_file), and the whole kernel_source_resolution.json was dropped -- the file forge_handoff hands to KernelForge. A tier that rules without locating a file now projects as unresolved; its verdict stays in reason and reason_class.
…harvest A specialist that wrote its heartbeat.json at the worktree top had it harvested as a creation patch. On a docker SGLang leg the worktree was based on the InferenceX checkout, so integrate_patch stashed that tree, the benchmark's InferenceX runtime patcher dirtied it again, git stash pop failed, and the session stopped with patch_recovery_incomplete right after measuring a +21.8% keep. heartbeat.json is a task-owned liveness file like specialist_done.json, so list it with the other work artifacts that the harvest pathspec and vet_patches exclude.
… run The TraceLens orchestrator fans out async sub-agents and waits for them. The Agent tool call that launches one returns at once, so the single tool_in_flight flag was cleared while the sub-agent kept running. With the main agent idle and waiting, no SDK message arrived for 300s, the stream-idle watchdog killed the run, analysis.md was never written and the roofline degraded (seen twice in one release run: "10 of 11 subagents complete. Only the gemm analyzer is still running"). Track background task ids from TaskStartedMessage until a TaskNotificationMessage or a terminal TaskUpdatedMessage, as the SDK documents, and apply the in-flight bound while any are still running.
…framework package The session names the framework by its package dir, e.g. <checkout>/python/sglang. _pick_worktree_base only accepted a preferred path that itself held .git, so it fell through to the first git root in discovery order -- the InferenceX benchmark checkout. Specialists then edited InferenceX, integrate_patch stashed it, the benchmark's InferenceX runtime patcher dirtied the same files, git stash pop failed, and two SGLang legs of one release run stopped with patch_recovery_incomplete right after measuring +21.8% and +27.8% keeps. Walk up from the preferred path to the checkout that contains it, and have the worktree read the preferred tree the same way the patch preflight does (session_framework_tree, then the framework's tree).
An ATOM variant with max_num_batched_tokens=65536 hit a GPU memory access fault; its ModelRunner died (exitcode -6) while the HTTP front end stayed up and kept answering the monitor's GET /metrics polls. Every poll grew server.log, so the silence gate never fired and lm_eval waited on 64 requests nothing would serve until the 7800s hard cap. The liveness report already refuses health-probe lines as evidence of progress; apply the same rule to the silence gate: an increment made only of GET /metrics|/health|/healthz|/ping access lines is not activity.
…heckout An integrate that carries only an artifact (an AITER tuned GEMM CSV, no patch) has nothing to derive a root from. With no <FRAMEWORK>_REPO_PATH or <FRAMEWORK>_DIR set, _resolve_framework_root fell through to the first git root in discovery order -- the InferenceX benchmark checkout. integrate_patch stashed it, the benchmark's InferenceX runtime patcher dirtied the same files, git stash pop failed, and the session stopped with patch_recovery_incomplete. Before that fallback, take the checkout that holds the framework's own tree (resolve_framework_tree). The walk-up from a package dir to its checkout is shared with the specialist worktree base as enclosing_checkout.
…ct repr
A proposal whose extra_args arrives as a flag -> value mapping, e.g.
{"--kv-cache-dtype": "fp8_e4m3"}, went through str() in coerce_args and
became "{'--kv-cache-dtype': 'fp8_e4m3'}". The grid argv gate then
rejected every such variant with "extra_server_args must be argv-like
flags, not bare positional arguments" before it was ever measured.
Render a mapping as "--flag value" pairs (True = bare flag, False =
dropped), the same way the list form is already space-joined.
…ht bound
The TraceLens runner already keeps its own idle bound at the in-flight
value while async sub-agents run, but the Claude CLI has a second bound of
its own: in print mode it terminates background tasks still running 600s
after the main turn goes quiet. In a release run the rmsnorm analyzer, the
last of 11 sub-agents, was killed that way ("Background tasks still running
after 600s; terminating"), analysis.md was never written, and the session
lost its roofline.
Pass CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS to the CLI at the runner's
in-flight bound (0 when that bound is disabled), so the runner's watchdog
is the one that decides. An explicit CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS
in the environment still wins.
…mework root The fallback for an integrate with no patch now takes the checkout holding the framework's own tree, but a pip-installed framework has none: the rocm/vllm image serves vLLM from site-packages, and its /app/vllm source checkout is not where the import resolves. _resolve_framework_root then fell through to the first git root in discovery order, still the InferenceX benchmark checkout. In a release run a docker vLLM leg stashed it for a flags-only integrate, the benchmark's InferenceX runtime patcher rewrote benchmarks/benchmark_lib.sh and lm_eval_sitecustomize.py, git stash pop refused to overwrite them, and the session stopped with patch_recovery_incomplete. Drop the checkout named by $INFERENCEX_PATH from that fallback: it is the benchmark harness, never the tree under optimisation. A non-git root is already treated as clean by the stash step, so falling through to one is safe.
…erenceX checkout A pip-installed framework has no checkout of its own, so _pick_worktree_base falls back to the first git root among the source roots, which is the InferenceX benchmark checkout. The specialist's scratch files in that worktree were then harvested as _worktree_diff.patch with InferenceX recorded as its apply root. integrate_patch trusted the recorded root, stashed the InferenceX checkout, the InferenceX runtime patcher rewrote benchmarks/benchmark_lib.sh during the benchmark, stash pop failed, and a docker vLLM release leg stopped with patch_recovery_incomplete right after a +10% artifact-only integrate. Skip the harvest when the worktree base is the checkout named by $INFERENCEX_PATH: it is the benchmark harness, never the tree under optimisation. Patch files the specialist wrote explicitly are still collected and resolved from their contents.
…he first git root Two resolvers picked "the framework" by position in the search-root list, which records only how roots were discovered: _pick_worktree_base, for the specialist worktree, and _resolve_framework_root, for an integrate with no patch to place. Both fell back to the first git root when the framework had no checkout of its own. Since the InferenceX benchmark checkout joined the search roots, that root is InferenceX, and on the rocm/vllm image, where vLLM is pip-installed in site-packages, every docker vLLM session hit it: specialists were cut worktrees off InferenceX, their scratch was harvested as a patch recorded against InferenceX, integrate stashed InferenceX, the benchmark's runtime patcher rewrote benchmarks/benchmark_lib.sh, stash pop failed and the session stopped with patch_recovery_incomplete. The specialist prompt meanwhile listed the served site-packages tree as read-only and InferenceX and /app/vllm as git checkouts. framework_apply_tree now classifies the tree the session names (resolve_framework_tree): a checkout that tracks it, or the tree itself, the form the search roots already give a pip-installed package in. Both resolvers use it and no longer fall back by position. A tree with no checkout gets a session-owned bare git snapshot (tree_snapshot), refreshed at each dispatch unless an integrate is still deciding on a candidate, and the specialist's worktree is branched off it, so it edits the code the server imports and its harvest applies to that tree through the existing non-git apply and backup path. Nothing is written into the tree itself. resolve_framework_tree maps xdit to its xfuser package, so xDiT keeps a named tree without the fallback. The InferenceX special cases added for the same failures are removed.
…aremetal-install # Conflicts: # src/hyperloom/agents/kernel/tests/test_tracelens_csv.py # src/hyperloom/agents/kernel/tools/tracelens_skill_runner.py
xiaofei-zheng
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM.
Reviewed at c9590afc against merge base 245247f3.
What I checked:
- ATOM bare-metal arm:
install_baremetal.sh(--install-framework atom,ATOM_REPO/ATOM_REF/ATOM_ROOT,refuse_atom_beside_vllm_or_sglang, theisolatedrejection) againstpreflight._SETUP_INSTALLABLE_FRAMEWORKS; the removal of the two12h-atomexample skills and their pyproject entries leaves no tracked reference behind, and the docs/examples move with it. - Framework-tree resolution:
FrameworkTree/framework_apply_tree/enclosing_checkout/_tracksreach both consumers (specialists/runner.py,executors/integrate_patch.py);_pick_worktree_baseis deleted with its last reader and no reference remains;specialist_patch_preflight_error's narrowed signature matches both production callers. tree_snapshot.py: session-owned bare repo outside the tree, per-repo lock, finite timeouts, every git failure surfaced as an error rather than swallowed; reached throughasyncio.to_thread. Pinned end-to-end through_maybe_setup_worktreewith real git._candidate_resolution_method's pathless set matches the validator incommon/kernel_source_contract.py;coerce_args' mapping branch output passes thegrid_server_argsargv gate;kwargs["env"]is an SDK overlay over the inherited environment, not a replacement;_IncrementScan.activedefaults true so non-server log scanners keep their prior meaning.- Title and the 14-item description match the diff at the current head.
Earlier blockers, both now resolved: the branch was CONFLICTING, which had suppressed the pull_request workflow set for 12 commits; and ruff format --check failed on test_proposal_identity_unit.py:137. After the merge of origin/main the full matrix ran green at 7e308fce (py3.10/py3.11, 6 shards each, plus coverage, CodeQL, pylint, mypy, bandit, reuse, docs, pyproject declarations), and c9590afc is a pure reformat of that one parametrize entry with ruff passing. Test shards for c9590afc were still running when I approved.
Problem
install_baremetal.sh --install-framework) can install SGLang or vLLM, but not ATOM. On an environment without ATOM (for example the plainrocm/pytorch:rocm10.0_ubuntu24.04_py3.12_pytorch_release_2.11.0image), an ATOM workload has no install path and fails preflight; ATOM had to come from a prebuilt image such asrocm/atom-dev.vllm.platform_plugins,vllm.general_pluginsandsglang.srt.pluginsentry points, which both engines load by default, so a vLLM or SGLang run on the same Python as ATOM would serve through ATOM's platform, model classes and loader patches.FRAMEWORK, and the 12h forge demo is the same run with the KernelForge backend. ATOM had its own pair of demo skills (hyperloom-qwen3-14b-fp8-12h-atomandhyperloom-qwen3-14b-fp8-12h-atom-forge) that duplicated both, mixing the framework axis into the backend axis and giving four skills for a 2-option backend choice.The 12h release run of this branch (8 legs on one MI355X node, SGLang / vLLM / ATOM, docker and bare metal, with and without forge) then hit the following:
setup.py develop(Bring up vLLM on ROCm 10 and fix scheduler stalls found in the 6-leg run #1605), so site-packages holds onlyvllm.egg-linkand the package lives in the checkout._probe_isolated_vllmonly globbedsite-packages/vllm, returnedNone, and the patch was skipped.kernel_source_resolution.json, the fileforge_handoffhands to KernelForge, was dropped. The active finder stamps its symbol-detected non-patchable verdict withsource_resolution_method=active_finderbut names no source file.validate_documentrejects a location method without asource_file(fix(kernel): stop sentinel strings from short-circuiting source resolution #1107), and one invalid entry drops the whole document.heartbeat.jsonat the worktree top had it harvested as a creation patch, because it is missing from the task-owned files (specialist_done.jsonand friends) that the harvest pathspec andvet_patchesexclude.analysis.mdwas never written and the roofline degraded (twice in one run: "10 of 11 subagents complete. Only the gemm analyzer is still running"). The Agent tool call that launches an async sub-agent returns at once, clearing the singletool_in_flightflag; with the main agent waiting and no SDK message for 300s, the stream-idle watchdog killed the run.<checkout>/python/sglang), and_pick_worktree_baseonly accepted a preferred path that itself held.git, so it fell through to the first git root in discovery order, which is InferenceX since Make the ENABLEMENT self-repair loop deliver, and fail loudly when it cannot #1604. Specialists edited InferenceX,integrate_patchstashed it, the benchmark's InferenceX runtime patcher dirtied the same files,git stash popfailed, and two SGLang legs stopped withpatch_recovery_incompleteright after measuring +21.8% and +27.8% keeps.max_num_batched_tokens=65536hit a GPU memory access fault; its ModelRunner died while the HTTP front end stayed up and kept answering the KV-metricsGET /metricspolls (feat(kv): collect KV cache occupancy and pressure from the engine #1446). Every poll grewserver.log, which the silence gate counts as activity (refactor: retire robustness supervision and simplify benchmark watchdogs #1547), so lm_eval waited on 64 requests nothing would serve until the 7800s hard cap.<FRAMEWORK>_REPO_PATHor<FRAMEWORK>_DIRset,_resolve_framework_rootfell through to the first git root in discovery order, and the session stopped withpatch_recovery_incompleteexactly as in 8 (the docker SGLang forge leg and the bare-metal SGLang leg, after the fix for 8 was deployed).extra_argsarrived as a flag -> value mapping, e.g.{"--kv-cache-dtype": "fp8_e4m3"}, went throughstr()incoerce_argsand became"{'--kv-cache-dtype': 'fp8_e4m3'}". The grid argv gate then rejected every such variant (extra_server_args must be argv-like flags) before it was measured; one docker SGLang forge round lost all 3 of its variants this way.Background tasks still running after 600s; terminating). In the rerun, one leg's rmsnorm analyzer, the last of 11 sub-agents, was killed this way,analysis.mdwas never written, and the session lost its roofline./app/vllmcheckout is not where the import resolves), so there is no framework checkout to take and_resolve_framework_rootfell through to the first git root, InferenceX. In the rerun a docker vLLM leg stashed it for a flags-only integrate, the runtime patcher rewrotebenchmarks/benchmark_lib.shandlm_eval_sitecustomize.py,git stash poprefused to overwrite them, and the session stopped withpatch_recovery_incomplete._pick_worktree_basefell through to the first git root, InferenceX. A specialist's tuning scratch (scratch/custom_tune.pyand its log) was harvested as_worktree_diff.patchwith InferenceX recorded as its apply root,integrate_patchtrusted that root and stashed InferenceX, and a docker vLLM leg stopped withpatch_recovery_incompleteright after a +10.35% AITER tuned GEMM CSV integrate, as in 8 and 13.Fix
--install-framework atom. Like SGLang, it installs into the shared interpreter against the existing ROCm torch: AITER first (newest tag compatible with the installed torch/triton, orAITER_REF), then ATOM from source as an editable checkout under torch/triton constraints. It recordsFRAMEWORK=atomand rejects--framework-env isolated, as for SGLang.ATOM_REFdefaults tofe1099b15ddc52e6873e1932935f722864cdeee0, theATOM_COMMITthatrocm/atom-dev:v0.1.7-rc0was built from (ATOM publishes no release tags);ATOM_REPO,ATOM_REFandATOM_ROOTare overridable. The post-install check importsatomandatom.entrypoints.openai.api_serverinstead of runningopenai_server --help, whose help rendering raises on ATOM's unescaped%. Preflight listsatomas setup-installable and prints the install command when ATOM is missing. (1873052, f791125)--install-framework atomstops when SGLang or vLLM imports from the selected Python or from a ROCm 10 vLLM venv built over it with system site-packages.--install-framework sglang, shared vLLM and ROCm 10 source vLLM stop when ATOM imports there. The self-contained isolated vLLM wheel venv is unaffected.docs/compatibility.rst, the setup skill and the 12h demo's ATOM section say to run ATOM in a separate container instead. (4aa156a)hyperloom-qwen3-14b-fp8-12hgains an## ATOMsection with the ATOM-specific run mode, execution shell, Docker/baremetal entry, Python selection and setup (including--install-framework atom), cleanup, launch, resume and status checks;hyperloom-qwen3-14b-fp8-12h-forgepoints at that section and adds onlyKERNEL_OPT_BACKEND_ORDER=forge. The two ATOM example directories, their packaged skill entries inpyproject.tomland the setup skill's ATOM routing are removed, and the recipe tests execute the 12h demo's ATOM section. (c1c8ae6)vllm/package, resolve the install root from the venv interpreter'svllm.__file__. (e99dac9)unresolved; its verdict stays inreasonandreason_class. The validator is unchanged: the entry it rejected was self-contradictory, and the producer now writes a valid one, so the document is kept. (7aacb66)heartbeat.jsonwith the other task-owned work artifacts excluded from the harvest. (4b347d6)TaskStartedMessageuntil aTaskNotificationMessageor a terminalTaskUpdatedMessage, as the SDK documents, and apply the in-flight idle bound while any are still running. (0b07ec6)session_framework_tree, then the framework's tree). (29e9d06)GET /metrics|/health|/healthz|/pingaccess lines is not activity for the silence gate, the same rule the liveness report already applies. (3feb51f)resolve_framework_tree). The walk-up from a package dir to its checkout is shared with the worktree base of 8 asenclosing_checkout. (d83516d)--flag valuepairs (True= bare flag,False= dropped), the same way the list form is already space-joined. (b4096c7)CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MSto the CLI at the runner's in-flight idle bound (0when that bound is disabled), so the runner's watchdog from 7 decides; an explicit value in the environment still wins. (be837fc)framework_apply_treeclassifies the treeresolve_framework_treenames: the checkout that tracks it, or the tree itself for a pip-installed package, the form the search roots already give such a package in. The specialist worktree and_resolve_framework_rootwithout patches both use it, and neither falls back to the first git root any more, which also retires the fallback 8 and 10 still left behind. A tree with no checkout gets a session-owned bare git snapshot, refreshed at each dispatch unless an integrate is still deciding on a candidate; the worktree is branched off it, so the specialist edits the code the server imports and its harvest applies to that tree through the existing non-git apply and backup path. Nothing is written into the tree itself. The specialist prompt describes that worktree instead of calling the served tree read-only.resolve_framework_treemapsxditto itsxfuserpackage so xDiT keeps a named tree without the fallback. The InferenceX special cases of 6686aed and 18a68d9 are removed. (7f442da)