Repository navigation
onnx: Linux NPU route via Ryzen AI SW (RyzenAI EP), non-invasive - #386
Conversation
`1bit serve --device onnx` runs AMD's NPU on Linux through ryzenai-server built against a Ryzen AI Software 1.8.0 venv the user installed (nothing of AMD's is shipped). serve builds the child's environment from ONEBIT_RYZENAI_ROOT (and ONEBIT_XRT_ROOT) when the model's genai_config.json declares the RyzenAI provider: XILINX_XRT, LD_LIBRARY_PATH with XRT's lib first, RYZENAI_EP_PATH. CPU ONNX models and runs without the knob are unchanged. A failed non-streamed request to an NPU model is retried once (first dispatch after idle can hit a one-off firmware timeout). Child shutdown waits 30 s after SIGTERM before SIGKILL: an abnormal exit with a live NPU context panicked amdxdna 0.17.0. build-onnx.sh scans the libraries copied beside ryzenai-server for an executable-stack request and clears it with patchelf (copies only). docs/onnx.md gains a "Linux NPU" section: the non-invasive install (XRT debs extracted, not installed), the three traps, the knobs, the measured numbers. tests/onnx_npu_smoke.sh checks the route end to end and skips without it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Preparing review... |
PR Reviewer Guide 🔍(Review updated until commit 598afb7)Here are some key observations to aid the review process:
|
Serve-path numbers (Linux NPU, strixhalo)Build: this branch's working tree at Box: engine #315 GPU campaign running concurrently (HRX GLM servers, Qwen2.5-0.5B-Instruct_rai_1.8.0_npu_4K (n=3, median +- half-range; per-repeat decode in brackets)
The low end of each bracket is repeat 1 (campaign active). Server up (health 200) 3.0 s after exec; the child ryzenai-server (pid 227108) was the only holder of Same model, the The other AMD NPU-only models through the same path (decode tok/s at 128 / 512 / 2048, n=3, quiet box): Llama-3.2-1B 48.9 / 47.6 / 45.8; Llama-3.2-3B 21.1 / 20.4 / 18.8; Phi-3.5-mini 13.5 / 12.7 / 10.7. Stopping: what happened in the five lifetimes
Things the PR could take from this (owner's call): never escalate to SIGKILL for a backend that holds the NPU (report and leave it instead); make Raw logs: strixhalo |
…op policy
Child::alive() reaped an exited backend with waitpid(WNOHANG) and forgot it, so
Child::stop() still sent SIGTERM to the gone pid, waited the full grace and
printed "ignored SIGTERM for 30 s; killing it" (seen 2026-10-11 after a
ryzenai-server teardown abort). Every waitpid on the child now goes through
Child::reap(), which records the exit status and sets pid_ = -1; stop() is a
no-op for a reaped child and uses the same bookkeeping; the "a backend exited"
line says how it ended (exit N / signal N).
The grace between SIGTERM and SIGKILL is ONEBIT_BACKEND_STOP_GRACE_S (default
30); the warning before escalating names the amdxdna risk. SIGTERM goes to the
child itself (it shares serve's process group, so no group signal). The
escalation stays.
docs/onnx.md (Linux NPU): the ryzenai-server shutdown hang / double-free as a
known issue, the operating policy until the amdxdna fix is installed (one server
per session, no model cycling, leave a hung stop until reboot), and the 4096
prompt cap the _16K packages get through ryzenai-server as pinned.
tests/smoke_serve.sh: a fake backend that exits on its own ("fake_exit") must
end serve at once with "backend exited: exit 3" and no SIGTERM wait.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Persistent review updated to latest commit 948017b |
Follow-up: child reap + stop graceCommit 948017b, from the two findings in the serve-path numbers comment above. Reap bookkeeping ( Stop policy. SIGTERM goes to the child itself (it shares serve's process group, so there is no group signal: that would reach serve and every other backend). The grace before SIGKILL is Test. Run on strixhalo (CPU-only,
🤖 Generated with Claude Code |
Conflicts: serve.cpp Child takes main's on_line plus this branch's env_override; reap() bookkeeping now backs main's kill_now() too; the NPU retry passes the watchdog; docs/windows.md stays deleted (main dropped Windows packages). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…I Software - build-onnx.sh puts the ryzenai-server, ONNX Runtime GenAI and ONNX Runtime LICENSE / ThirdPartyNotices texts beside the binary; package-linux.sh packs them in onnx/. - weekly.sh unsets RYZENAI_INSTALL_PATH for the release build; package_checks fails when onnx/ holds anything beyond ryzenai-server, Microsoft's two libraries and their licence texts, or when onnx/ is missing. - NOTICE says what onnx/ ships and that Ryzen AI Software is the user's own. - serve: with a ryzenai-server that has no RyzenAI provider beside it (the release package's), the venv's deployment/lib goes before the server's directory so the venv's ONNX Runtime GenAI loads. - docs/onnx.md: what the package ships; round-2 serve-path numbers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Persistent review updated to latest commit 47e6a4d |
…s on the NPU - build-onnx.sh sets ryzenai-server's search path to $ORIGIN only with patchelf: DT_RUNPATH for the Microsoft build (LD_LIBRARY_PATH must win so serve can load a Ryzen AI venv's ONNX Runtime GenAI; the DT_RPATH it had kept the CPU-only copy, 'Unknown provider name RyzenAI'), DT_RPATH for a build against Ryzen AI Software, no build-tree path in either. - scripts/onnx/signal_shim.c, linked with -Wl,--wrap=signal: ryzenai-server's handler calls std::exit() from signal context, and exit() then destroys ONNX Runtime GenAI's, the provider's and XRT's globals before the model (g_server was created first). On the CPU that shows as 'Model was leaked'; on the NPU it hung after 'Shutting down...' until serve's SIGKILL. The shim takes SIGTERM/SIGINT on its own thread, registers an exit handler that deletes the server first, and calls stop(): main() returns and exits once. ryzenai-server's sources are unchanged. - weekly.sh package check: onnx/ryzenai-server has RUNPATH $ORIGIN, no RPATH, and the shim. - onnx_npu_smoke: fixed the stop-time label; fails when serve had to SIGKILL. - serve.cpp comment and docs/onnx.md: the RUNPATH reason and the root cause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Persistent review updated to latest commit d5d5736 |
|
Persistent review updated to latest commit 598afb7 |
…nside Lemonade) (#488) - README: lead with the census (43/43 on the GPU, #476/#480/#481) and Qwen3.8-27B MTP 36.0/26.8/29.9 tok/s (#487); one hop (#475), strict placement (#482), hw-gate kernels and placement (#478/#484), allocation safety (#486), Ubuntu 26.04 PPA lemond (#483), ONNX on the NPU via the user's Ryzen AI install (#386); Vulkan/ROCm/CUDA/ZINC/ComfyUI/MLX/Windows cut (#306); no release date; NPU in progress with no speed numbers. - npu.md: a status page with the public parts only (model format, serving, add-on hook, build); no runtime method, no NPU speeds. - releases.md: "Releases", first release when the checklist is done; neutral package checks. - lemonade.md, serve.md, hrx.md, laya.md, PORTING.md, registry.md: one hop, MTP on HRX (#487), census lead, add-on wording without private repository names or NPU speeds. Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The engine ships no ONNX Runtime of its own any more. Lemonade carries ONNX wherever it exists (its ryzenai-llm backend on Windows, its onnxruntime backend on the CPU), and the engine's NPU route is its own engine (--device npu). The Linux ONNX route (#386) had done its job: the OGA data it was built to collect is recorded, and the NPU-side work continues on the engine's own stack. Removed: --device onnx in serve (a removed-device message names Lemonade's backends), ONEBIT_ONNX and the onnx_npu_smoke test, scripts/build-onnx.sh and the SIGTERM shim, the third_party/ryzenai-server submodule, docs/onnx.md, the onnx/ directory of the Linux package and its weekly checks, the ryzenai-server pin in the weekly changes, and the ryzenai-server / ONNX Runtime entries in NOTICE. A model directory with genai_config.json is no longer recognised. Lemonade's own onnxruntime:cpu backend in the 1bit OS image is untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The engine ships no ONNX Runtime of its own any more. Lemonade carries ONNX wherever it exists (its ryzenai-llm backend on Windows, its onnxruntime backend on the CPU), and the engine's NPU route is its own engine (--device npu). The Linux ONNX route (#386) had done its job: the OGA data it was built to collect is recorded, and the NPU-side work continues on the engine's own stack. Removed: --device onnx in serve (a removed-device message names Lemonade's backends), ONEBIT_ONNX and the onnx_npu_smoke test, scripts/build-onnx.sh and the SIGTERM shim, the third_party/ryzenai-server submodule, docs/onnx.md, the onnx/ directory of the Linux package and its weekly checks, the ryzenai-server pin in the weekly changes, and the ryzenai-server / ONNX Runtime entries in NOTICE. A model directory with genai_config.json is no longer recognised. Lemonade's own onnxruntime:cpu backend in the 1bit OS image is untouched. Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Linux NPU route for
1bit serve --device onnxthrough AMD's Ryzen AI Software (RyzenAI execution provider), plus the ONNX pieces of the release package. AMD's Ryzen AI Software is not shipped and not copied into the repository: the engine builds against, and runs, an install the user made under AMD's own licence (RYZENAI_INSTALL_PATHat build time,ONEBIT_RYZENAI_ROOTat run time).Owner decision 2026-10-20: ONNX goes in the engine release.
What changes
app/serve.cpp(onnx device): reads the model'sgenai_config.json(RyzenAIprovider,hybrid_opt_token_backend: npu). With the provider declared andONEBIT_RYZENAI_ROOT=<venv>set, serve builds the child's environment:XILINX_XRT(ONEBIT_XRT_ROOT, else$XILINX_XRT, else/opt/xilinx/xrt),LD_LIBRARY_PATHwith XRT's lib first (the venv'svoewheel bundles a stalelibxrt_coreutil.so.2.19), andRYZENAI_EP_PATH. A server built against the venv (provider copy beside it) keeps its own directory before<venv>/deployment/lib; a server without one (the release package's, built against Microsoft's ONNX Runtime) gets<venv>/deployment/libfirst so the venv's ONNX Runtime GenAI loads. Unset knob or a CPU model: unchanged. An NPU model's failed (5xx) non-streamed request is retried once after 1 s (one-off firmware TDR on the first dispatch after idle).reap()bookkeeping shared byalive(),stop()and main's watchdogkill_now(); exit status on the "backend exited" line;ONEBIT_BACKEND_STOP_GRACE_S(default 30 s) before SIGKILL, with a warning line naming the amdxdna risk.scripts/build-onnx.shputs the ryzenai-server, ONNX Runtime GenAI and ONNX RuntimeLICENSE/ThirdPartyNotices.txttexts beside the binary;scripts/package-linux.shpacks them inonnx/.scripts/weekly.shunsetsRYZENAI_INSTALL_PATHfor the release build, andpackage_checksfails whenonnx/is missing or holds anything beyondryzenai-server,libonnxruntime-genai.so,libonnxruntime.so*and those texts (so no RyzenAI/VitisAI provider,AMD_LICENSEor AIE compiler library can ship).NOTICEsays whatonnx/carries.scripts/build-onnx.sh: withRYZENAI_INSTALL_PATH, clears executable-stack requests on the copied libraries (patchelf --clear-execstack, copies only). Sets ryzenai-server's search path to$ORIGINonly: DT_RUNPATH for the Microsoft (release) build soLD_LIBRARY_PATHcan put a venv's ONNX Runtime GenAI first (DT_RPATH kept the CPU-only copy: "Unknown provider name 'RyzenAI'", device check 2026-10-20), DT_RPATH for a build against the venv; no build-tree path.scripts/onnx/signal_shim.c, linked with-Wl,--wrap=signal; ryzenai-server's sources unchanged). Root cause of the NPU shutdown hang: ryzenai-server's SIGTERM handler callsstd::exit()from signal context;exit()destroys exit-time objects in reverse creation order, andg_server(holding the model) was created first, so ONNX Runtime GenAI's, the RyzenAI provider's and XRT's globals (created during the model load) are destroyed under a live session and hardware context. On the CPU this shows asOGA Error: 1 instances of N10Generators5ModelE were leakedon every exit; on the NPU it hung after[Server] Shutting down...(never reaching[InferenceEngine] Shutting down) until serve's SIGKILL. The shim blocks SIGTERM/SIGINT on every thread, takes them on its own thread, registers an exit handler that deletes the server (and model) first, and callsstop():main()returns andexit()runs once, model before runtime globals.docs/onnx.md"Linux NPU" (non-invasive install: XRT debs extracted withdpkg-deb -x, not installed; traps; knobs; shutdown policy; measured tables), what the release package ships;docs/serve.mdstop-grace knob;docs/npu.mdpointer.tests/onnx_npu_smoke.sh(ctestonnx_npu_smokeunder-DONEBIT_ONNX=ON, skip 77 without venv / NPU / model);smoke_servecase for a backend that exits on its own (fake_exitintests/fake_backend.py).Merge of main (47 commits)
Merged
origin/main(no rebase). Conflicts:app/serve.cpp:Childtakes main'son_line(stderr tail / watchdog) and this branch'senv_override(..., ready_path, on_line, env_override);alive()keepsreap(), main'skill_now()now goes throughreap()too;all_alivemoved before the request handlers (the NPU retry uses it); the retry passes main's&watch; the exit path keeps main'slifecycle.count_backend_exit()and this branch's exit text.docs/windows.md: deleted, as on main (no Windows package).docs/onnx.md: main's Windows wording, this branch's Linux NPU section; Windows items dropped from "Next".docs/serve.md,tests/fake_backend.py: both sides kept.Tests (ryzen, CPU only, no GPU, no NPU)
gcc-16/g++-16, Ninja, Release, every CI target): build clean,ctest34/34 passed.-DONEBIT_ONNX=ONbuild (Microsoft ONNX Runtime GenAI 0.11.2 / ORT 1.23.2):onnx_npu_smokeskipped (77, no NPU),smoke_serveandwatchdog_servepassed.amd/Qwen2.5-0.5B-Instruct-quantized_int4-float16-cpu-onnxthrough1bit serve(build tree and the unpacked package): "The capital of France is Paris.", 7 SSE chunks streamed, child gone after one SIGTERM.scripts/package-linux.shon that build:onnx/= ryzenai-server, the two ORT libraries, five licence texts;lddresolves insideonnx/. Theonnx/part ofpackage_checkspasses on it and fails whenAMD_LICENSEandlibonnxruntime_providers_ryzenai.soare dropped in.RUNPATH $ORIGIN, no RPATH; the CPU model through the packaged1bit serveanswers "Paris", streams 7 chunks, and one SIGTERM ends serve in 0.14 s with exit 0,[InferenceEngine] Shutting downprinted and no "leaked" lines (before: exit 15 with both leak lines). Theonnx/package check passes on the new package and fails on a DT_RPATH, shim-less server.xrt/lib:<server>:<venv>/deployment/lib; none ->xrt/lib:<venv>/deployment/lib:<server>.Device check (pending)
Not run: the NPU belongs to another agent right now. On strixhalo, on a quiet NPU, one job at a time under
~/npu.lock. Policy from 2026-10-11 stands: ryzenai-server does not always shut down cleanly; do notkill -9anything holding/dev/accel/accel0.Pass: both routes (route B is now expected to pass: the release-style server has RUNPATH, so the venv's ONNX Runtime GenAI loads) answer with "Paris" (known answer), the child holds
accel0(count >= 1), the log shows the NPU (VitisAI/RyzenAI) provider, and the kernel log shows no new amdxdna fault afterT0. SIGTERM criterion: one SIGTERM to serve ends ryzenai-server on its own: the log shows[Server] Shutting down...then[InferenceEngine] Shutting down, no "leaked" lines, noignored SIGTERM ... sending SIGKILLline, and serve's "backend exited" text (if any) isexit 0; route A's smoke test fails on the SIGKILL line. If ryzenai-server still hangs after[InferenceEngine] Shutting down, the hang is in the provider's own teardown of a released model (not the exit order) and the 30 s window plus the once-per-boot policy stay. A single TDR on the first dispatch is the known one-off (the retry covers it); any second fault stops the check.Measured earlier (Strix Halo, 2026-10-11, AMD NPU-only 1.8.0 models through
1bit serve --device onnx, n=3, 128 generated)Decode at 128 / 512 / 2048 prompt tokens: Qwen2.5-0.5B 4K 70.1 / 66.7 / 64.9 tok/s; Llama-3.2-1B 48.9 / 47.6 / 45.8; Llama-3.2-3B 21.1 / 20.4 / 18.8; Phi-3.5-mini 13.5 / 12.7 / 10.7. Table in
docs/onnx.md.Deferred
--device autosends everygenai_config.jsondirectory to onnx; the registry has no notion of an OGA directory's provider (NPU vs CPU).Licence
AMD's Ryzen AI Software (XRT, the RyzenAI execution provider, AMD's ONNX Runtime GenAI build) stays under AMD's EULA, installed by the user; the engine ships none of it and copies none of it into the repository, and the release package check enforces that. ryzenai-server (lemonade-sdk, MIT) is a pinned submodule; ryzenai-server, ONNX Runtime GenAI and ONNX Runtime (Microsoft, MIT) ship in
onnx/with their licence texts.tools/copyright.py --checkpasses.🤖 Generated with Claude Code