From a60cc839f09a39ac1abb53ab7b8ed09ee88cbab9 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:31:31 -0400 Subject: [PATCH 1/7] docs: design spec for exposing DirectML via CLI (experimental tier) Contributor (Vageesha Gupta) reported DirectML support exists (PR #211) but is unreachable from the CLI. Design: expose --use_directml as explicit opt-in, add conditional discoverability hint, document as experimental with an honest per-architecture status, ship a patch release, and reply inviting validation of untested architectures. --- ...-07-08-directml-cli-experimental-design.md | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md diff --git a/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md b/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md new file mode 100644 index 0000000..ca1eb8a --- /dev/null +++ b/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md @@ -0,0 +1,148 @@ +# Expose DirectML via CLI (experimental tier) — Design + +**Date:** 2026-07-08 +**Status:** Approved (design) +**Author:** Andrew Beveridge (with Claude) +**Trigger:** Contributor email (Vageesha Gupta) reporting that DirectML support exists in the code but is unreachable from the CLI. + +## Background + +DirectML acceleration was contributed by an external contributor (zbear0808) in +PR #211 (May 2025). It is a real, working feature: + +- `Separator.__init__` accepts `use_directml` (default `False`), stored as + `self.use_directml` (`separator.py:123`, `:214`). +- `setup_torch_device()` branches into DirectML **only** when CUDA and MPS are + both unavailable, `use_directml=True`, and `torch_directml` is installed and + available (`separator.py:393-397`). +- `configure_dml()` sets the Torch device to DirectML and enables the ONNX + Runtime `DmlExecutionProvider` (`separator.py:431-444`). +- Packaging already supports it: `pyproject.toml` defines a `dml` extra + (`onnxruntime-directml` + `torch_directml`), and the README dev-setup mentions + `poetry install --extras "dml"`. + +**The gap:** `use_directml` is not exposed via the CLI. `cli.py` defines +`--use_soundfile` and `--use_autocast` and forwards them to `Separator(...)`, but +there is no `--use_directml` argument and it is not forwarded. A CLI user cannot +enable DirectML without editing source. This is an oversight in PR #211, not an +intentional gate. + +A contributor (Vageesha Gupta) independently diagnosed this exactly, patched the +CLI locally, and confirmed `UVR-MDX-NET-Inst_HQ_3.onnx` runs with DirectML +hardware acceleration on a Windows 11 / AMD integrated GPU setup. + +## Support posture (decision) + +**Experimental / best-effort.** Expose the feature and document it honestly as +experimental and community-supported. The maintainer has no Windows/DirectML +machine and cannot test or CI this path, so we set expectations rather than +promise a support tier. + +## Architecture compatibility (as understood today) + +| Architecture | Model types | DirectML status | +|---|---|---| +| MDX | `.onnx` | **Confirmed working** (ONNX `DmlExecutionProvider`; contributor-tested). | +| MDXC (incl. `bs_roformer`, the default model) | `.ckpt` / `.yaml` | Patched in PR #211 (load on CPU, move to device). Expected to work; community-untested. | +| VR | `.pth` | Patched in PR #211 (load on CPU, move to device). Expected to work; community-untested. | +| Demucs | — | **Not touched** by PR #211; loads straight to device. Unverified. | + +`use_autocast` self-disables on DirectML — `autocast_mode.is_autocast_available()` +returns `False` for the DML device type (`separator.py:1024`), so there is no +autocast/DML conflict to handle. + +## Design decisions + +**Fork 1 — Explicit opt-in (chosen) vs. auto-enable.** +Keep DirectML behind explicit `--use_directml` / `use_directml=True`. CUDA and MPS +auto-enable, but auto-enabling DirectML would silently route any user who merely +has `torch_directml` installed onto an untested acceleration path — an +unacceptable silent-regression risk for a feature we cannot test. Explicit opt-in +matches the experimental tier. + +**Fork 2 — Conditional discoverability hint (chosen) vs. static env_info line.** +Emit a targeted INFO hint only when DirectML packages are installed but DirectML +is not active. Fires exactly for the users who would benefit; invisible to +everyone else. + +## Scope + +### 1. CLI wiring — `audio_separator/utils/cli.py` + +- Add `--use_directml` to the "Common Separation Parameters" argument group, + `action="store_true"`, mirroring `--use_autocast`. + - Help text: *"Use DirectML for hardware-accelerated inference on Windows + AMD/Intel GPUs (experimental; requires the `dml` extra). Example: + --use_directml"* +- Forward `use_directml=args.use_directml` into the `Separator(...)` construction. + +### 2. Conditional discoverability hint — `audio_separator/separator/separator.py` + +- In `setup_torch_device()`, within the existing CPU-fallback block + (`if not hardware_acceleration_enabled:`), if `torch_directml` **or** + `onnxruntime-directml` is installed but `self.use_directml` is `False`, log one + INFO line: + *"DirectML packages detected but DirectML is not enabled. Pass + `use_directml=True` (or `--use_directml` on the CLI) to enable experimental + DirectML acceleration."* +- Reuses the existing `get_package_distribution(...)` and `has_torch_dml_installed` + checks — no new detection logic. + +### 3. Documentation — `README.md` + +- Add an install section **"🪟 Windows AMD/Intel GPU with DirectML + (experimental)"** mirroring the CUDA / Apple Silicon / CPU sections: + - `pip install "audio-separator[dml]"` + - The `--env_info` confirmation log line: + `ONNXruntime has DmlExecutionProvider available, enabling acceleration` + - The requirement to pass `--use_directml`. +- Include an honest status note reflecting the compatibility table above + (MDX confirmed; MDXC/roformer & VR expected but untested; Demucs unverified), + and ask users to open an issue with logs. +- Add `--use_directml` to the CLI usage/help reference block in the README if one + is maintained there. + +### 4. Tests — `tests/unit/test_cli.py`, `tests/unit/test_remote_cli.py` + +- Add `"use_directml": False` to the `common_expected_args` fixture + (`test_cli.py`). +- Add `test_cli_use_directml_argument` mirroring `test_cli_use_autocast_argument` + (asserts `--use_directml` results in `use_directml=True` in the `Separator` + call). +- Add `use_directml` to the forwarded-argument lists in `test_remote_cli.py` + (4 locations). +- No DirectML *execution* test — DML cannot run in CI. We test only the + CLI→constructor wiring, exactly as `use_autocast` is tested. + +### 5. Reply to contributor (draft; maintainer sends) + +- Confirm DirectML is a real, working feature (PR #211) that was only ever exposed + via the Python API — the CLI wiring was an oversight, not an intentional + disable. Her workaround was correct. +- State that `--use_directml` is landing in the next patch release. +- Thank her for the precise diagnosis. +- Light validation invite: ask if she would test MDXC/roformer, VR, and Demucs + models on her AMD GPU and report which work, noting MDX is the only architecture + confirmed so far. + +### 6. Release + +- Patch version bump in `pyproject.toml`. +- PR (with `@coderabbitai ignore` per workflow), merge, PyPI release via the + existing `publish-to-pypi` workflow. + +## Non-goals (YAGNI) + +- No auto-enable of DirectML — explicit opt-in only. +- No DirectML CI — infeasible (Windows + discrete non-NVIDIA GPU). +- No changes to Demucs or other architecture internals to "fix" DirectML — we + document status; we do not chase untested fixes. +- No formal support commitment or ongoing compatibility-matrix maintenance + burden. + +## Risk assessment + +Very low. The DirectML branch is unreachable unless CUDA and MPS are both absent, +`torch_directml` is installed, and the user explicitly opts in. The CLI flag and +the conditional hint cannot change behavior for any CUDA, MPS, or CPU user. Tests +cover the wiring; the runtime path is unchanged from the already-merged PR #211. From e48c1dc09d6273dd81afdf2428cd52de9fff425b Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:37:58 -0400 Subject: [PATCH 2/7] docs: implementation plan for DirectML CLI exposure + spec correction Correct spec to drop remote-CLI scope: DirectML is local-only and the remote separation servers run on CUDA/CPU, so plumbing use_directml through audio_separator/remote/* would be dead config. Add 5-task TDD plan (CLI flag, discoverability hint, README, version bump, reply draft). --- .../2026-07-08-directml-cli-experimental.md | 324 ++++++++++++++++++ ...-07-08-directml-cli-experimental-design.md | 18 +- 2 files changed, 338 insertions(+), 4 deletions(-) create mode 100644 docs/superpowers/plans/2026-07-08-directml-cli-experimental.md diff --git a/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md b/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md new file mode 100644 index 0000000..cdafd89 --- /dev/null +++ b/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md @@ -0,0 +1,324 @@ +# Expose DirectML via CLI (experimental tier) — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make the existing, working DirectML acceleration reachable from the CLI via an explicit `--use_directml` flag, add a targeted discoverability hint, document it honestly as experimental, and prepare a patch release. + +**Architecture:** DirectML support already exists end-to-end in `Separator` (PR #211) but is only reachable from the Python API. We wire it to the CLI (mirroring the existing `--use_autocast` flag), add one conditional INFO hint in the CPU-fallback path of `setup_torch_device()`, document a new experimental install section, and bump the patch version. No runtime/inference code changes. + +**Tech Stack:** Python 3.10+, argparse, pytest, Poetry. + +## Global Constraints + +- **Support tier:** experimental / best-effort. All user-facing copy must say "experimental". +- **Explicit opt-in only** — DirectML must NOT auto-enable; it stays behind `use_directml` / `--use_directml`. +- **No behavior change for CUDA / MPS / CPU users** — the DirectML branch is only reachable when CUDA and MPS are both unavailable AND `use_directml=True` AND `torch_directml` is installed & available. +- **No remote CLI / API / deploy-server changes** — DirectML is local-only; do not touch `audio_separator/remote/*` or `tests/unit/test_remote_cli.py`. +- **No DirectML execution tests** — DML cannot run in CI; test only wiring and the hint. +- **Confirmed-working architecture:** MDX (`.onnx`) only. MDXC/roformer & VR expected but community-untested; Demucs unverified. Docs must state this. +- Current version: `0.44.2` → bump to `0.44.3`. + +--- + +### Task 1: Expose `--use_directml` on the CLI and forward it to `Separator` + +**Files:** +- Modify: `audio_separator/utils/cli.py` (help text ~line 63; argument ~line 78; forwarding ~line 249) +- Test: `tests/unit/test_cli.py` (fixture line 43; new test after `test_cli_use_autocast_argument`, ~line 258) + +**Interfaces:** +- Consumes: existing `Separator(use_directml: bool = False, ...)` constructor param (`audio_separator/separator/separator.py:123`). +- Produces: CLI arg `args.use_directml` (bool, default `False`), forwarded as `use_directml=args.use_directml` in the `Separator(...)` call. + +**Coupling note:** As soon as `cli.py` forwards `use_directml`, every existing test that asserts `assert_called_once_with(**common_expected_args)` will fail unless the fixture gains the key. The fixture change and the `cli.py` forwarding MUST land in this same task/commit. + +- [ ] **Step 1: Write the failing test** + +Add to `tests/unit/test_cli.py`, immediately after `test_cli_use_autocast_argument` (after line 258): + +```python +# Test using use_directml argument +def test_cli_use_directml_argument(common_expected_args): + test_args = ["cli.py", "test_audio.mp3", "--use_directml"] + with patch("sys.argv", test_args): + with patch("audio_separator.separator.Separator") as mock_separator: + mock_separator_instance = mock_separator.return_value + mock_separator_instance.separate.return_value = ["output_file.mp3"] + main() + + # Update expected args for this specific test + expected_args = common_expected_args.copy() + expected_args["use_directml"] = True + + # Assertions + mock_separator.assert_called_once_with(**expected_args) +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `python -m pytest tests/unit/test_cli.py::test_cli_use_directml_argument -v` +Expected: FAIL — argparse rejects the unknown flag, raising `SystemExit: 2` ("unrecognized arguments: --use_directml"). + +- [ ] **Step 3: Add the CLI argument, help text, forwarding, and fixture key** + +In `audio_separator/utils/cli.py`, add the help variable after line 63 (`use_autocast_help = ...`): + +```python + use_directml_help = "Use DirectML for hardware-accelerated inference on Windows AMD/Intel GPUs (experimental; requires the 'dml' extra). Example: --use_directml" +``` + +Add the argument after line 78 (`common_params.add_argument("--use_autocast", ...)`): + +```python + common_params.add_argument("--use_directml", action="store_true", help=use_directml_help) +``` + +Forward it in the `Separator(...)` construction, immediately after line 249 (`use_autocast=args.use_autocast,`): + +```python + use_directml=args.use_directml, +``` + +In `tests/unit/test_cli.py`, add to the `common_expected_args` fixture (after line 43, `"use_autocast": False,`): + +```python + "use_directml": False, +``` + +- [ ] **Step 4: Run the CLI tests to verify all pass** + +Run: `python -m pytest tests/unit/test_cli.py -v` +Expected: PASS — the new `test_cli_use_directml_argument` passes, and all existing tests that use `common_expected_args` still pass (fixture now carries `use_directml=False`, matching the new forwarded arg). + +- [ ] **Step 5: Commit** + +```bash +git add audio_separator/utils/cli.py tests/unit/test_cli.py +git commit -m "feat: expose --use_directml CLI flag (experimental)" +``` + +--- + +### Task 2: Add conditional DirectML discoverability hint + +**Files:** +- Modify: `audio_separator/separator/separator.py` (CPU-fallback block in `setup_torch_device`, lines 399-402) +- Test: `tests/unit/test_directml.py` (new file) + +**Interfaces:** +- Consumes: `self.use_directml` (bool), `self.get_package_distribution(name)` → distribution or `None`, and the local `has_torch_dml_installed` var already computed at `separator.py:383`. +- Produces: one INFO log line when DirectML packages are installed but DirectML is not enabled. No return value, no state change. + +**Behavior note:** `audio-separator --env_info` constructs a full `Separator()` (not `info_only`), so this hint fires during `--env_info` on a DirectML-capable machine with no CUDA/MPS — the intended discovery path. + +- [ ] **Step 1: Write the failing tests** + +Create `tests/unit/test_directml.py`: + +```python +import logging +import platform +from unittest.mock import MagicMock, patch + +from audio_separator.separator import Separator + +HINT = "DirectML packages detected but DirectML is not enabled" + + +def _run_setup(use_directml, dml_installed): + """Construct a Separator without auto device-setup, then drive setup_torch_device + with CUDA and MPS forced unavailable so the CPU-fallback path always runs.""" + sep = Separator(info_only=True) + sep.use_directml = use_directml + + def fake_dist(name): + if name in ("torch_directml", "onnxruntime-directml") and dml_installed: + return MagicMock() + return None + + with patch.object(sep, "get_package_distribution", side_effect=fake_dist), \ + patch("torch.cuda.is_available", return_value=False), \ + patch("torch.backends.mps.is_available", return_value=False), \ + patch("audio_separator.separator.separator.ort.get_available_providers", return_value=["CPUExecutionProvider"]): + sep.setup_torch_device(platform.uname()) + return sep + + +def test_directml_hint_shown_when_packages_present_but_disabled(caplog): + with caplog.at_level(logging.INFO): + _run_setup(use_directml=False, dml_installed=True) + assert any(HINT in r.message for r in caplog.records) + + +def test_directml_hint_absent_when_no_packages(caplog): + with caplog.at_level(logging.INFO): + _run_setup(use_directml=False, dml_installed=False) + assert not any(HINT in r.message for r in caplog.records) +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `python -m pytest tests/unit/test_directml.py -v` +Expected: `test_directml_hint_shown_when_packages_present_but_disabled` FAILS (hint not emitted yet); `test_directml_hint_absent_when_no_packages` PASSES (no hint exists to leak). + +- [ ] **Step 3: Implement the hint** + +In `audio_separator/separator/separator.py`, replace the CPU-fallback block (currently lines 399-402): + +```python + if not hardware_acceleration_enabled: + self.logger.info("No hardware acceleration could be configured, running in CPU mode") + self.torch_device = self.torch_device_cpu + self.onnx_execution_provider = ["CPUExecutionProvider"] +``` + +with: + +```python + if not hardware_acceleration_enabled: + self.logger.info("No hardware acceleration could be configured, running in CPU mode") + self.torch_device = self.torch_device_cpu + self.onnx_execution_provider = ["CPUExecutionProvider"] + + # Discoverability hint: DirectML is an explicit opt-in (experimental). If the + # DirectML packages are installed but the feature wasn't enabled, tell the user how. + if not self.use_directml and (has_torch_dml_installed or self.get_package_distribution("onnxruntime-directml") is not None): + self.logger.info( + "DirectML packages detected but DirectML is not enabled. " + "Pass use_directml=True (or --use_directml on the CLI) to enable experimental DirectML acceleration." + ) +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `python -m pytest tests/unit/test_directml.py -v` +Expected: both tests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add audio_separator/separator/separator.py tests/unit/test_directml.py +git commit -m "feat: hint that --use_directml is available when DML packages are installed" +``` + +--- + +### Task 3: Document DirectML as an experimental install option + +**Files:** +- Modify: `README.md` (add a new install section after the CPU section; the CUDA section starts at line 93, Apple Silicon at line 115) + +**Interfaces:** none (documentation only). + +- [ ] **Step 1: Add the experimental DirectML install section** + +In `README.md`, add a new section alongside the other hardware install sections (place it after the CPU install section, before `## Usage`). Match the existing heading style: + +```markdown +### 🪟 Windows AMD / Intel GPU with DirectML (experimental) + +> **Experimental / community-supported.** DirectML acceleration was contributed by the community and is not tested in CI or by the maintainer (it requires Windows plus an AMD or Intel GPU). It is opt-in and will never affect CUDA, Apple Silicon, or CPU users. + +Install with the `dml` extra: + +```sh +pip install "audio-separator[dml]" +``` + +Then enable it explicitly with the `--use_directml` flag: + +```sh +audio-separator path/to/audio.wav --use_directml +``` + +💬 If DirectML is configured correctly you will see this log line when running `audio-separator --env_info`: +`ONNXruntime has DmlExecutionProvider available, enabling acceleration` + +**Model architecture status on DirectML:** + +| Architecture | Model types | Status | +|---|---|---| +| MDX | `.onnx` | ✅ Confirmed working | +| MDXC (incl. the default `bs_roformer` model) | `.ckpt` / `.yaml` | ⚠️ Expected to work, community-untested | +| VR | `.pth` | ⚠️ Expected to work, community-untested | +| Demucs | — | ❓ Unverified | + +If you test any of the untested architectures, please [open an issue](https://github.com/nomadkaraoke/python-audio-separator/issues) with your `--env_info` output and logs — reports are what move these from "untested" to "confirmed". +``` + +- [ ] **Step 2: Verify the README renders cleanly** + +Run: `python -c "import pathlib; t = pathlib.Path('README.md').read_text(); assert '--use_directml' in t and 'DirectML' in t and 'experimental' in t.lower(); print('README OK')"` +Expected: prints `README OK`. + +- [ ] **Step 3: Commit** + +```bash +git add README.md +git commit -m "docs: add experimental DirectML (Windows AMD/Intel GPU) install section" +``` + +--- + +### Task 4: Bump patch version + +**Files:** +- Modify: `pyproject.toml` (line 7) + +**Interfaces:** none. + +- [ ] **Step 1: Bump the version** + +In `pyproject.toml`, change line 7 from: + +```toml +version = "0.44.2" +``` + +to: + +```toml +version = "0.44.3" +``` + +- [ ] **Step 2: Verify** + +Run: `grep '^version' pyproject.toml` +Expected: `version = "0.44.3"` + +- [ ] **Step 3: Commit** + +```bash +git add pyproject.toml +git commit -m "chore: bump version to 0.44.3" +``` + +--- + +### Task 5: Draft the reply to Vageesha + +**Files:** +- Create: reply draft in the session scratchpad (NOT committed to the repo — it's an email draft, not project content). + +**Interfaces:** none. + +- [ ] **Step 1: Write the reply draft** + +Write to `/vageesha-directml-reply.md` and present it inline to the user for sending. Content must cover: + 1. Confirm DirectML is a real, working feature (PR #211) that was only ever exposed via the Python API — the missing CLI wiring was an oversight, not an intentional disable. Her diagnosis and workaround were exactly right. + 2. State that `--use_directml` is landing in v0.44.3 (installable via `pip install "audio-separator[dml]"`), plus the new `--env_info` discoverability hint. + 3. Thank her for the precise write-up. + 4. Light validation invite: since she offered logs, ask if she'd try MDXC/roformer, VR, and Demucs models on her AMD GPU and report which work — noting MDX is the only architecture confirmed so far. No pressure, no formal commitment. + +- [ ] **Step 2: Present the draft to the user** (no commit). + +--- + +## Final verification (after all tasks) + +- [ ] Run the full unit suite: `python -m pytest tests/unit/ -q` — expect all green. +- [ ] Confirm no `audio_separator/remote/*` files or `tests/unit/test_remote_cli.py` were modified: `git diff --name-only origin/main` should list only `cli.py`, `separator.py`, `test_cli.py`, `test_directml.py`, `README.md`, `pyproject.toml`, and the docs under `docs/superpowers/`. + +## Handoff to release + +After implementation, ship via the standard workflow: `/test-review` → `/docs-review` → `/coderabbit` → `/pr` (adds `@coderabbitai ignore`) → merge → PyPI release via the existing `publish-to-pypi` workflow. Then send the reply from Task 5. diff --git a/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md b/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md index ca1eb8a..87bd21e 100644 --- a/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md +++ b/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md @@ -102,17 +102,21 @@ everyone else. - Add `--use_directml` to the CLI usage/help reference block in the README if one is maintained there. -### 4. Tests — `tests/unit/test_cli.py`, `tests/unit/test_remote_cli.py` +### 4. Tests — `tests/unit/test_cli.py` - Add `"use_directml": False` to the `common_expected_args` fixture - (`test_cli.py`). + (`test_cli.py`). **Required:** once `cli.py` forwards `use_directml` into the + `Separator(...)` call, every test that asserts + `assert_called_once_with(**common_expected_args)` will fail unless the fixture + includes the key. The fixture update and the `cli.py` forwarding must land + together. - Add `test_cli_use_directml_argument` mirroring `test_cli_use_autocast_argument` (asserts `--use_directml` results in `use_directml=True` in the `Separator` call). -- Add `use_directml` to the forwarded-argument lists in `test_remote_cli.py` - (4 locations). - No DirectML *execution* test — DML cannot run in CI. We test only the CLI→constructor wiring, exactly as `use_autocast` is tested. +- **Remote CLI (`tests/unit/test_remote_cli.py`) is deliberately NOT touched** — + see Non-goals. ### 5. Reply to contributor (draft; maintainer sends) @@ -139,6 +143,12 @@ everyone else. document status; we do not chase untested fixes. - No formal support commitment or ongoing compatibility-matrix maintenance burden. +- **No remote CLI / API / deploy-server changes.** DirectML is local-only + (Windows + AMD/Intel GPU); the remote separation servers run on Cloud Run / + Modal with CUDA or CPU and can never present a DirectML device. Plumbing + `use_directml` through `audio_separator/remote/*` would be misleading dead + configuration. The local `cli.py` change does not affect the remote tests + (separate arg parser). ## Risk assessment From 5a0edbd9fb417de562b1372e41909f81482c01df Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:47:42 -0400 Subject: [PATCH 3/7] feat: expose --use_directml CLI flag (experimental) Wire the existing Separator(use_directml=...) support (PR #211) to the CLI, mirroring --use_autocast. Explicit opt-in only. Adds the fixture key + a wiring test in test_cli.py. --- audio_separator/utils/cli.py | 3 +++ tests/unit/test_cli.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/audio_separator/utils/cli.py b/audio_separator/utils/cli.py index c45fcaf..6c33fae 100755 --- a/audio_separator/utils/cli.py +++ b/audio_separator/utils/cli.py @@ -61,6 +61,7 @@ def main(): sample_rate_help = "Modify the sample rate of the output audio (default: %(default)s). Example: --sample_rate=44100" use_soundfile_help = "Use soundfile to write audio output (default: %(default)s). Example: --use_soundfile" use_autocast_help = "Use PyTorch autocast for faster inference (default: %(default)s). Do not use for CPU inference. Example: --use_autocast" + use_directml_help = "Use DirectML for hardware-accelerated inference on Windows AMD/Intel GPUs (experimental; requires the 'dml' extra). Example: --use_directml" chunk_duration_help = "Split audio into chunks of this duration in seconds (default: %(default)s = no chunking). Useful for processing very long audio files on systems with limited memory. Recommended: 600 (10 minutes) for files >1 hour. Chunks are concatenated without overlap/crossfade. Example: --chunk_duration=600" ensemble_algorithm_help = "Algorithm to use for ensembling multiple models (default: avg_wave). Choices: avg_wave, median_wave, min_wave, max_wave, avg_fft, median_fft, min_fft, max_fft, uvr_max_spec, uvr_min_spec, ensemble_wav. Example: --ensemble_algorithm=uvr_max_spec" ensemble_weights_help = "Weights for ensembling multiple models (default: equal). Number of weights must match number of models. Example: --ensemble_weights 1.0 0.5" @@ -76,6 +77,7 @@ def main(): common_params.add_argument("--sample_rate", type=int, default=44100, help=sample_rate_help) common_params.add_argument("--use_soundfile", action="store_true", help=use_soundfile_help) common_params.add_argument("--use_autocast", action="store_true", help=use_autocast_help) + common_params.add_argument("--use_directml", action="store_true", help=use_directml_help) common_params.add_argument("--chunk_duration", type=float, default=None, help=chunk_duration_help) common_params.add_argument( "--ensemble_algorithm", @@ -247,6 +249,7 @@ def main(): sample_rate=args.sample_rate, use_soundfile=args.use_soundfile, use_autocast=args.use_autocast, + use_directml=args.use_directml, chunk_duration=args.chunk_duration, ensemble_algorithm=args.ensemble_algorithm, ensemble_weights=args.ensemble_weights, diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index cae4518..ce49eef 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -41,6 +41,7 @@ def common_expected_args(): "sample_rate": 44100, "use_soundfile": False, "use_autocast": False, + "use_directml": False, "chunk_duration": None, "ensemble_algorithm": None, "ensemble_weights": None, @@ -257,6 +258,23 @@ def test_cli_use_autocast_argument(common_expected_args): mock_separator.assert_called_once_with(**expected_args) +# Test using use_directml argument +def test_cli_use_directml_argument(common_expected_args): + test_args = ["cli.py", "test_audio.mp3", "--use_directml"] + with patch("sys.argv", test_args): + with patch("audio_separator.separator.Separator") as mock_separator: + mock_separator_instance = mock_separator.return_value + mock_separator_instance.separate.return_value = ["output_file.mp3"] + main() + + # Update expected args for this specific test + expected_args = common_expected_args.copy() + expected_args["use_directml"] = True + + # Assertions + mock_separator.assert_called_once_with(**expected_args) + + # Test using custom_output_names arguments def test_cli_custom_output_names_argument(common_expected_args): custom_names = { From a7745b30dc2b118455acabfad57c2dbf089c52c0 Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:48:56 -0400 Subject: [PATCH 4/7] feat: hint that --use_directml is available when DML packages are installed In the CPU-fallback path of setup_torch_device, if torch_directml or onnxruntime-directml is installed but DirectML wasn't enabled, log a one-line INFO hint pointing to --use_directml. Fires during 'audio-separator --env_info' on DirectML-capable machines. --- audio_separator/separator/separator.py | 8 ++++++ tests/unit/test_directml.py | 38 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/unit/test_directml.py diff --git a/audio_separator/separator/separator.py b/audio_separator/separator/separator.py index 8302488..6b1259d 100644 --- a/audio_separator/separator/separator.py +++ b/audio_separator/separator/separator.py @@ -401,6 +401,14 @@ def setup_torch_device(self, system_info): self.torch_device = self.torch_device_cpu self.onnx_execution_provider = ["CPUExecutionProvider"] + # Discoverability hint: DirectML is an explicit opt-in (experimental). If the + # DirectML packages are installed but the feature wasn't enabled, tell the user how. + if not self.use_directml and (has_torch_dml_installed or self.get_package_distribution("onnxruntime-directml") is not None): + self.logger.info( + "DirectML packages detected but DirectML is not enabled. " + "Pass use_directml=True (or --use_directml on the CLI) to enable experimental DirectML acceleration." + ) + def configure_cuda(self, ort_providers): """ This method configures the CUDA device for PyTorch and ONNX Runtime, if available. diff --git a/tests/unit/test_directml.py b/tests/unit/test_directml.py new file mode 100644 index 0000000..2bbf862 --- /dev/null +++ b/tests/unit/test_directml.py @@ -0,0 +1,38 @@ +import logging +import platform +from unittest.mock import MagicMock, patch + +from audio_separator.separator import Separator + +HINT = "DirectML packages detected but DirectML is not enabled" + + +def _run_setup(use_directml, dml_installed): + """Construct a Separator without auto device-setup, then drive setup_torch_device + with CUDA and MPS forced unavailable so the CPU-fallback path always runs.""" + sep = Separator(info_only=True) + sep.use_directml = use_directml + + def fake_dist(name): + if name in ("torch_directml", "onnxruntime-directml") and dml_installed: + return MagicMock() + return None + + with patch.object(sep, "get_package_distribution", side_effect=fake_dist), \ + patch("torch.cuda.is_available", return_value=False), \ + patch("torch.backends.mps.is_available", return_value=False), \ + patch("audio_separator.separator.separator.ort.get_available_providers", return_value=["CPUExecutionProvider"]): + sep.setup_torch_device(platform.uname()) + return sep + + +def test_directml_hint_shown_when_packages_present_but_disabled(caplog): + with caplog.at_level(logging.INFO): + _run_setup(use_directml=False, dml_installed=True) + assert any(HINT in r.message for r in caplog.records) + + +def test_directml_hint_absent_when_no_packages(caplog): + with caplog.at_level(logging.INFO): + _run_setup(use_directml=False, dml_installed=False) + assert not any(HINT in r.message for r in caplog.records) From 508e9b217f2e8c6fdaa65e959f521d7847b3647b Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:50:59 -0400 Subject: [PATCH 5/7] docs: add experimental DirectML (Windows AMD/Intel GPU) install section New install section with the dml extra, --use_directml usage, env_info confirmation line, and an honest per-architecture status matrix. Also adds --use_directml to the ToC, the full CLI-options reference, and the Separator class parameters list. --- README.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e51e32d..1b90ba4 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The simplest (and probably most used) use case for this package is to separate a - [🎮 Nvidia GPU with CUDA or 🧪 Google Colab](#-nvidia-gpu-with-cuda-or--google-colab) - [ Apple Silicon, macOS Sonoma+ with M1 or newer CPU (CoreML acceleration)](#-apple-silicon-macos-sonoma-with-m1-or-newer-cpu-coreml-acceleration) - [🐢 No hardware acceleration, CPU only](#-no-hardware-acceleration-cpu-only) + - [🪟 Windows AMD / Intel GPU with DirectML (experimental)](#-windows-amd--intel-gpu-with-directml-experimental) - [🎥 FFmpeg dependency](#-ffmpeg-dependency) - [GPU / CUDA specific installation steps with Pip](#gpu--cuda-specific-installation-steps-with-pip) - [Multiple CUDA library versions may be needed](#multiple-cuda-library-versions-may-be-needed) @@ -139,6 +140,34 @@ Docker: beveradb/audio-separator ``` +### 🪟 Windows AMD / Intel GPU with DirectML (experimental) + +> **Experimental / community-supported.** DirectML acceleration was contributed by the community and is not tested in CI or by the maintainer (it requires Windows plus an AMD or Intel GPU). It is opt-in and will never affect CUDA, Apple Silicon, or CPU users. + +Install with the `dml` extra: +```sh +pip install "audio-separator[dml]" +``` + +Then enable it explicitly with the `--use_directml` flag: +```sh +audio-separator path/to/audio.wav --use_directml +``` + +💬 If DirectML is configured correctly you should see this log message when running `audio-separator --env_info`: + `ONNXruntime has DmlExecutionProvider available, enabling acceleration` + +**Model architecture status on DirectML:** + +| Architecture | Model types | Status | +|---|---|---| +| MDX | `.onnx` | ✅ Confirmed working | +| MDXC (incl. the default `bs_roformer` model) | `.ckpt` / `.yaml` | ⚠️ Expected to work, community-untested | +| VR | `.pth` | ⚠️ Expected to work, community-untested | +| Demucs | — | ❓ Unverified | + +If you test any of the untested architectures, please [open an issue](https://github.com/nomadkaraoke/python-audio-separator/issues) with your `--env_info` output and logs — reports are what move these from "untested" to "confirmed". + ### 🎥 FFmpeg dependency 💬 To test if `audio-separator` has been successfully configured to use FFmpeg, run `audio-separator --env_info`. The log will show `FFmpeg installed`. @@ -418,7 +447,7 @@ Presets are defined in `audio_separator/ensemble_presets.json` — contributions ```sh usage: audio-separator [-h] [-v] [-d] [-e] [-l] [--log_level LOG_LEVEL] [--list_filter LIST_FILTER] [--list_limit LIST_LIMIT] [--list_format {pretty,json}] [-m MODEL_FILENAME] [--output_format OUTPUT_FORMAT] [--output_bitrate OUTPUT_BITRATE] [--output_dir OUTPUT_DIR] [--model_file_dir MODEL_FILE_DIR] [--download_model_only] [--invert_spect] [--normalization NORMALIZATION] - [--amplification AMPLIFICATION] [--single_stem SINGLE_STEM] [--sample_rate SAMPLE_RATE] [--use_soundfile] [--use_autocast] [--custom_output_names CUSTOM_OUTPUT_NAMES] + [--amplification AMPLIFICATION] [--single_stem SINGLE_STEM] [--sample_rate SAMPLE_RATE] [--use_soundfile] [--use_autocast] [--use_directml] [--custom_output_names CUSTOM_OUTPUT_NAMES] [--mdx_segment_size MDX_SEGMENT_SIZE] [--mdx_overlap MDX_OVERLAP] [--mdx_batch_size MDX_BATCH_SIZE] [--mdx_hop_length MDX_HOP_LENGTH] [--mdx_enable_denoise] [--vr_batch_size VR_BATCH_SIZE] [--vr_window_size VR_WINDOW_SIZE] [--vr_aggression VR_AGGRESSION] [--vr_enable_tta] [--vr_high_end_process] [--vr_enable_post_process] [--vr_post_process_threshold VR_POST_PROCESS_THRESHOLD] [--demucs_segment_size DEMUCS_SEGMENT_SIZE] [--demucs_shifts DEMUCS_SHIFTS] [--demucs_overlap DEMUCS_OVERLAP] @@ -460,6 +489,7 @@ Common Separation Parameters: --sample_rate SAMPLE_RATE Modify the sample rate of the output audio (default: 44100). Example: --sample_rate=44100 --use_soundfile Use soundfile to write audio output (default: False). Example: --use_soundfile --use_autocast Use PyTorch autocast for faster inference (default: False). Do not use for CPU inference. Example: --use_autocast + --use_directml Use DirectML for hardware-accelerated inference on Windows AMD/Intel GPUs (experimental; requires the 'dml' extra). Example: --use_directml --custom_output_names CUSTOM_OUTPUT_NAMES Custom names for all output files in JSON format (default: None). Example: --custom_output_names='{"Vocals": "vocals_output", "Drums": "drums_output"}' MDX Architecture Parameters: @@ -616,6 +646,7 @@ You can also rename specific stems: - **`sample_rate`:** (Optional) Set the sample rate of the output audio. `Default: 44100` - **`use_soundfile`:** (Optional) Use soundfile for output writing, can solve OOM issues, especially on longer audio. - **`use_autocast`:** (Optional) Flag to use PyTorch autocast for faster inference. Do not use for CPU inference. `Default: False` +- **`use_directml`:** (Optional) Flag to use DirectML for hardware-accelerated inference on Windows AMD/Intel GPUs (experimental; requires the `dml` extra and only takes effect when CUDA and Apple Silicon MPS are unavailable). `Default: False` - **`mdx_params`:** (Optional) MDX Architecture Specific Attributes & Defaults. `Default: {"hop_length": 1024, "segment_size": 256, "overlap": 0.25, "batch_size": 1, "enable_denoise": False}` - **`vr_params`:** (Optional) VR Architecture Specific Attributes & Defaults. `Default: {"batch_size": 1, "window_size": 512, "aggression": 5, "enable_tta": False, "enable_post_process": False, "post_process_threshold": 0.2, "high_end_process": False}` - **`demucs_params`:** (Optional) Demucs Architecture Specific Attributes & Defaults. `Default: {"segment_size": "Default", "shifts": 2, "overlap": 0.25, "segments_enabled": True}` _(Note: `segment_size` "Default" uses the model's internal default, typically 40 for older Demucs models and 10 for Demucs v4/htdemucs)_ From b9c0c560853e73fbcfa5647b8592c482620ba0ad Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:51:15 -0400 Subject: [PATCH 6/7] chore: bump version to 0.44.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a9de5ca..cde5b4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "audio-separator" -version = "0.44.2" +version = "0.44.3" description = "Easy to use audio stem separation, using various models from UVR trained primarily by @Anjok07" authors = ["Andrew Beveridge "] license = "MIT" From 22567a87a09ddf8fddce27088b298db34c25586d Mon Sep 17 00:00:00 2001 From: Andrew Beveridge Date: Wed, 8 Jul 2026 14:56:34 -0400 Subject: [PATCH 7/7] chore: drop internal planning docs from PR --- .../2026-07-08-directml-cli-experimental.md | 324 ------------------ ...-07-08-directml-cli-experimental-design.md | 158 --------- 2 files changed, 482 deletions(-) delete mode 100644 docs/superpowers/plans/2026-07-08-directml-cli-experimental.md delete mode 100644 docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md diff --git a/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md b/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md deleted file mode 100644 index cdafd89..0000000 --- a/docs/superpowers/plans/2026-07-08-directml-cli-experimental.md +++ /dev/null @@ -1,324 +0,0 @@ -# Expose DirectML via CLI (experimental tier) — Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Make the existing, working DirectML acceleration reachable from the CLI via an explicit `--use_directml` flag, add a targeted discoverability hint, document it honestly as experimental, and prepare a patch release. - -**Architecture:** DirectML support already exists end-to-end in `Separator` (PR #211) but is only reachable from the Python API. We wire it to the CLI (mirroring the existing `--use_autocast` flag), add one conditional INFO hint in the CPU-fallback path of `setup_torch_device()`, document a new experimental install section, and bump the patch version. No runtime/inference code changes. - -**Tech Stack:** Python 3.10+, argparse, pytest, Poetry. - -## Global Constraints - -- **Support tier:** experimental / best-effort. All user-facing copy must say "experimental". -- **Explicit opt-in only** — DirectML must NOT auto-enable; it stays behind `use_directml` / `--use_directml`. -- **No behavior change for CUDA / MPS / CPU users** — the DirectML branch is only reachable when CUDA and MPS are both unavailable AND `use_directml=True` AND `torch_directml` is installed & available. -- **No remote CLI / API / deploy-server changes** — DirectML is local-only; do not touch `audio_separator/remote/*` or `tests/unit/test_remote_cli.py`. -- **No DirectML execution tests** — DML cannot run in CI; test only wiring and the hint. -- **Confirmed-working architecture:** MDX (`.onnx`) only. MDXC/roformer & VR expected but community-untested; Demucs unverified. Docs must state this. -- Current version: `0.44.2` → bump to `0.44.3`. - ---- - -### Task 1: Expose `--use_directml` on the CLI and forward it to `Separator` - -**Files:** -- Modify: `audio_separator/utils/cli.py` (help text ~line 63; argument ~line 78; forwarding ~line 249) -- Test: `tests/unit/test_cli.py` (fixture line 43; new test after `test_cli_use_autocast_argument`, ~line 258) - -**Interfaces:** -- Consumes: existing `Separator(use_directml: bool = False, ...)` constructor param (`audio_separator/separator/separator.py:123`). -- Produces: CLI arg `args.use_directml` (bool, default `False`), forwarded as `use_directml=args.use_directml` in the `Separator(...)` call. - -**Coupling note:** As soon as `cli.py` forwards `use_directml`, every existing test that asserts `assert_called_once_with(**common_expected_args)` will fail unless the fixture gains the key. The fixture change and the `cli.py` forwarding MUST land in this same task/commit. - -- [ ] **Step 1: Write the failing test** - -Add to `tests/unit/test_cli.py`, immediately after `test_cli_use_autocast_argument` (after line 258): - -```python -# Test using use_directml argument -def test_cli_use_directml_argument(common_expected_args): - test_args = ["cli.py", "test_audio.mp3", "--use_directml"] - with patch("sys.argv", test_args): - with patch("audio_separator.separator.Separator") as mock_separator: - mock_separator_instance = mock_separator.return_value - mock_separator_instance.separate.return_value = ["output_file.mp3"] - main() - - # Update expected args for this specific test - expected_args = common_expected_args.copy() - expected_args["use_directml"] = True - - # Assertions - mock_separator.assert_called_once_with(**expected_args) -``` - -- [ ] **Step 2: Run test to verify it fails** - -Run: `python -m pytest tests/unit/test_cli.py::test_cli_use_directml_argument -v` -Expected: FAIL — argparse rejects the unknown flag, raising `SystemExit: 2` ("unrecognized arguments: --use_directml"). - -- [ ] **Step 3: Add the CLI argument, help text, forwarding, and fixture key** - -In `audio_separator/utils/cli.py`, add the help variable after line 63 (`use_autocast_help = ...`): - -```python - use_directml_help = "Use DirectML for hardware-accelerated inference on Windows AMD/Intel GPUs (experimental; requires the 'dml' extra). Example: --use_directml" -``` - -Add the argument after line 78 (`common_params.add_argument("--use_autocast", ...)`): - -```python - common_params.add_argument("--use_directml", action="store_true", help=use_directml_help) -``` - -Forward it in the `Separator(...)` construction, immediately after line 249 (`use_autocast=args.use_autocast,`): - -```python - use_directml=args.use_directml, -``` - -In `tests/unit/test_cli.py`, add to the `common_expected_args` fixture (after line 43, `"use_autocast": False,`): - -```python - "use_directml": False, -``` - -- [ ] **Step 4: Run the CLI tests to verify all pass** - -Run: `python -m pytest tests/unit/test_cli.py -v` -Expected: PASS — the new `test_cli_use_directml_argument` passes, and all existing tests that use `common_expected_args` still pass (fixture now carries `use_directml=False`, matching the new forwarded arg). - -- [ ] **Step 5: Commit** - -```bash -git add audio_separator/utils/cli.py tests/unit/test_cli.py -git commit -m "feat: expose --use_directml CLI flag (experimental)" -``` - ---- - -### Task 2: Add conditional DirectML discoverability hint - -**Files:** -- Modify: `audio_separator/separator/separator.py` (CPU-fallback block in `setup_torch_device`, lines 399-402) -- Test: `tests/unit/test_directml.py` (new file) - -**Interfaces:** -- Consumes: `self.use_directml` (bool), `self.get_package_distribution(name)` → distribution or `None`, and the local `has_torch_dml_installed` var already computed at `separator.py:383`. -- Produces: one INFO log line when DirectML packages are installed but DirectML is not enabled. No return value, no state change. - -**Behavior note:** `audio-separator --env_info` constructs a full `Separator()` (not `info_only`), so this hint fires during `--env_info` on a DirectML-capable machine with no CUDA/MPS — the intended discovery path. - -- [ ] **Step 1: Write the failing tests** - -Create `tests/unit/test_directml.py`: - -```python -import logging -import platform -from unittest.mock import MagicMock, patch - -from audio_separator.separator import Separator - -HINT = "DirectML packages detected but DirectML is not enabled" - - -def _run_setup(use_directml, dml_installed): - """Construct a Separator without auto device-setup, then drive setup_torch_device - with CUDA and MPS forced unavailable so the CPU-fallback path always runs.""" - sep = Separator(info_only=True) - sep.use_directml = use_directml - - def fake_dist(name): - if name in ("torch_directml", "onnxruntime-directml") and dml_installed: - return MagicMock() - return None - - with patch.object(sep, "get_package_distribution", side_effect=fake_dist), \ - patch("torch.cuda.is_available", return_value=False), \ - patch("torch.backends.mps.is_available", return_value=False), \ - patch("audio_separator.separator.separator.ort.get_available_providers", return_value=["CPUExecutionProvider"]): - sep.setup_torch_device(platform.uname()) - return sep - - -def test_directml_hint_shown_when_packages_present_but_disabled(caplog): - with caplog.at_level(logging.INFO): - _run_setup(use_directml=False, dml_installed=True) - assert any(HINT in r.message for r in caplog.records) - - -def test_directml_hint_absent_when_no_packages(caplog): - with caplog.at_level(logging.INFO): - _run_setup(use_directml=False, dml_installed=False) - assert not any(HINT in r.message for r in caplog.records) -``` - -- [ ] **Step 2: Run tests to verify they fail** - -Run: `python -m pytest tests/unit/test_directml.py -v` -Expected: `test_directml_hint_shown_when_packages_present_but_disabled` FAILS (hint not emitted yet); `test_directml_hint_absent_when_no_packages` PASSES (no hint exists to leak). - -- [ ] **Step 3: Implement the hint** - -In `audio_separator/separator/separator.py`, replace the CPU-fallback block (currently lines 399-402): - -```python - if not hardware_acceleration_enabled: - self.logger.info("No hardware acceleration could be configured, running in CPU mode") - self.torch_device = self.torch_device_cpu - self.onnx_execution_provider = ["CPUExecutionProvider"] -``` - -with: - -```python - if not hardware_acceleration_enabled: - self.logger.info("No hardware acceleration could be configured, running in CPU mode") - self.torch_device = self.torch_device_cpu - self.onnx_execution_provider = ["CPUExecutionProvider"] - - # Discoverability hint: DirectML is an explicit opt-in (experimental). If the - # DirectML packages are installed but the feature wasn't enabled, tell the user how. - if not self.use_directml and (has_torch_dml_installed or self.get_package_distribution("onnxruntime-directml") is not None): - self.logger.info( - "DirectML packages detected but DirectML is not enabled. " - "Pass use_directml=True (or --use_directml on the CLI) to enable experimental DirectML acceleration." - ) -``` - -- [ ] **Step 4: Run tests to verify they pass** - -Run: `python -m pytest tests/unit/test_directml.py -v` -Expected: both tests PASS. - -- [ ] **Step 5: Commit** - -```bash -git add audio_separator/separator/separator.py tests/unit/test_directml.py -git commit -m "feat: hint that --use_directml is available when DML packages are installed" -``` - ---- - -### Task 3: Document DirectML as an experimental install option - -**Files:** -- Modify: `README.md` (add a new install section after the CPU section; the CUDA section starts at line 93, Apple Silicon at line 115) - -**Interfaces:** none (documentation only). - -- [ ] **Step 1: Add the experimental DirectML install section** - -In `README.md`, add a new section alongside the other hardware install sections (place it after the CPU install section, before `## Usage`). Match the existing heading style: - -```markdown -### 🪟 Windows AMD / Intel GPU with DirectML (experimental) - -> **Experimental / community-supported.** DirectML acceleration was contributed by the community and is not tested in CI or by the maintainer (it requires Windows plus an AMD or Intel GPU). It is opt-in and will never affect CUDA, Apple Silicon, or CPU users. - -Install with the `dml` extra: - -```sh -pip install "audio-separator[dml]" -``` - -Then enable it explicitly with the `--use_directml` flag: - -```sh -audio-separator path/to/audio.wav --use_directml -``` - -💬 If DirectML is configured correctly you will see this log line when running `audio-separator --env_info`: -`ONNXruntime has DmlExecutionProvider available, enabling acceleration` - -**Model architecture status on DirectML:** - -| Architecture | Model types | Status | -|---|---|---| -| MDX | `.onnx` | ✅ Confirmed working | -| MDXC (incl. the default `bs_roformer` model) | `.ckpt` / `.yaml` | ⚠️ Expected to work, community-untested | -| VR | `.pth` | ⚠️ Expected to work, community-untested | -| Demucs | — | ❓ Unverified | - -If you test any of the untested architectures, please [open an issue](https://github.com/nomadkaraoke/python-audio-separator/issues) with your `--env_info` output and logs — reports are what move these from "untested" to "confirmed". -``` - -- [ ] **Step 2: Verify the README renders cleanly** - -Run: `python -c "import pathlib; t = pathlib.Path('README.md').read_text(); assert '--use_directml' in t and 'DirectML' in t and 'experimental' in t.lower(); print('README OK')"` -Expected: prints `README OK`. - -- [ ] **Step 3: Commit** - -```bash -git add README.md -git commit -m "docs: add experimental DirectML (Windows AMD/Intel GPU) install section" -``` - ---- - -### Task 4: Bump patch version - -**Files:** -- Modify: `pyproject.toml` (line 7) - -**Interfaces:** none. - -- [ ] **Step 1: Bump the version** - -In `pyproject.toml`, change line 7 from: - -```toml -version = "0.44.2" -``` - -to: - -```toml -version = "0.44.3" -``` - -- [ ] **Step 2: Verify** - -Run: `grep '^version' pyproject.toml` -Expected: `version = "0.44.3"` - -- [ ] **Step 3: Commit** - -```bash -git add pyproject.toml -git commit -m "chore: bump version to 0.44.3" -``` - ---- - -### Task 5: Draft the reply to Vageesha - -**Files:** -- Create: reply draft in the session scratchpad (NOT committed to the repo — it's an email draft, not project content). - -**Interfaces:** none. - -- [ ] **Step 1: Write the reply draft** - -Write to `/vageesha-directml-reply.md` and present it inline to the user for sending. Content must cover: - 1. Confirm DirectML is a real, working feature (PR #211) that was only ever exposed via the Python API — the missing CLI wiring was an oversight, not an intentional disable. Her diagnosis and workaround were exactly right. - 2. State that `--use_directml` is landing in v0.44.3 (installable via `pip install "audio-separator[dml]"`), plus the new `--env_info` discoverability hint. - 3. Thank her for the precise write-up. - 4. Light validation invite: since she offered logs, ask if she'd try MDXC/roformer, VR, and Demucs models on her AMD GPU and report which work — noting MDX is the only architecture confirmed so far. No pressure, no formal commitment. - -- [ ] **Step 2: Present the draft to the user** (no commit). - ---- - -## Final verification (after all tasks) - -- [ ] Run the full unit suite: `python -m pytest tests/unit/ -q` — expect all green. -- [ ] Confirm no `audio_separator/remote/*` files or `tests/unit/test_remote_cli.py` were modified: `git diff --name-only origin/main` should list only `cli.py`, `separator.py`, `test_cli.py`, `test_directml.py`, `README.md`, `pyproject.toml`, and the docs under `docs/superpowers/`. - -## Handoff to release - -After implementation, ship via the standard workflow: `/test-review` → `/docs-review` → `/coderabbit` → `/pr` (adds `@coderabbitai ignore`) → merge → PyPI release via the existing `publish-to-pypi` workflow. Then send the reply from Task 5. diff --git a/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md b/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md deleted file mode 100644 index 87bd21e..0000000 --- a/docs/superpowers/specs/2026-07-08-directml-cli-experimental-design.md +++ /dev/null @@ -1,158 +0,0 @@ -# Expose DirectML via CLI (experimental tier) — Design - -**Date:** 2026-07-08 -**Status:** Approved (design) -**Author:** Andrew Beveridge (with Claude) -**Trigger:** Contributor email (Vageesha Gupta) reporting that DirectML support exists in the code but is unreachable from the CLI. - -## Background - -DirectML acceleration was contributed by an external contributor (zbear0808) in -PR #211 (May 2025). It is a real, working feature: - -- `Separator.__init__` accepts `use_directml` (default `False`), stored as - `self.use_directml` (`separator.py:123`, `:214`). -- `setup_torch_device()` branches into DirectML **only** when CUDA and MPS are - both unavailable, `use_directml=True`, and `torch_directml` is installed and - available (`separator.py:393-397`). -- `configure_dml()` sets the Torch device to DirectML and enables the ONNX - Runtime `DmlExecutionProvider` (`separator.py:431-444`). -- Packaging already supports it: `pyproject.toml` defines a `dml` extra - (`onnxruntime-directml` + `torch_directml`), and the README dev-setup mentions - `poetry install --extras "dml"`. - -**The gap:** `use_directml` is not exposed via the CLI. `cli.py` defines -`--use_soundfile` and `--use_autocast` and forwards them to `Separator(...)`, but -there is no `--use_directml` argument and it is not forwarded. A CLI user cannot -enable DirectML without editing source. This is an oversight in PR #211, not an -intentional gate. - -A contributor (Vageesha Gupta) independently diagnosed this exactly, patched the -CLI locally, and confirmed `UVR-MDX-NET-Inst_HQ_3.onnx` runs with DirectML -hardware acceleration on a Windows 11 / AMD integrated GPU setup. - -## Support posture (decision) - -**Experimental / best-effort.** Expose the feature and document it honestly as -experimental and community-supported. The maintainer has no Windows/DirectML -machine and cannot test or CI this path, so we set expectations rather than -promise a support tier. - -## Architecture compatibility (as understood today) - -| Architecture | Model types | DirectML status | -|---|---|---| -| MDX | `.onnx` | **Confirmed working** (ONNX `DmlExecutionProvider`; contributor-tested). | -| MDXC (incl. `bs_roformer`, the default model) | `.ckpt` / `.yaml` | Patched in PR #211 (load on CPU, move to device). Expected to work; community-untested. | -| VR | `.pth` | Patched in PR #211 (load on CPU, move to device). Expected to work; community-untested. | -| Demucs | — | **Not touched** by PR #211; loads straight to device. Unverified. | - -`use_autocast` self-disables on DirectML — `autocast_mode.is_autocast_available()` -returns `False` for the DML device type (`separator.py:1024`), so there is no -autocast/DML conflict to handle. - -## Design decisions - -**Fork 1 — Explicit opt-in (chosen) vs. auto-enable.** -Keep DirectML behind explicit `--use_directml` / `use_directml=True`. CUDA and MPS -auto-enable, but auto-enabling DirectML would silently route any user who merely -has `torch_directml` installed onto an untested acceleration path — an -unacceptable silent-regression risk for a feature we cannot test. Explicit opt-in -matches the experimental tier. - -**Fork 2 — Conditional discoverability hint (chosen) vs. static env_info line.** -Emit a targeted INFO hint only when DirectML packages are installed but DirectML -is not active. Fires exactly for the users who would benefit; invisible to -everyone else. - -## Scope - -### 1. CLI wiring — `audio_separator/utils/cli.py` - -- Add `--use_directml` to the "Common Separation Parameters" argument group, - `action="store_true"`, mirroring `--use_autocast`. - - Help text: *"Use DirectML for hardware-accelerated inference on Windows - AMD/Intel GPUs (experimental; requires the `dml` extra). Example: - --use_directml"* -- Forward `use_directml=args.use_directml` into the `Separator(...)` construction. - -### 2. Conditional discoverability hint — `audio_separator/separator/separator.py` - -- In `setup_torch_device()`, within the existing CPU-fallback block - (`if not hardware_acceleration_enabled:`), if `torch_directml` **or** - `onnxruntime-directml` is installed but `self.use_directml` is `False`, log one - INFO line: - *"DirectML packages detected but DirectML is not enabled. Pass - `use_directml=True` (or `--use_directml` on the CLI) to enable experimental - DirectML acceleration."* -- Reuses the existing `get_package_distribution(...)` and `has_torch_dml_installed` - checks — no new detection logic. - -### 3. Documentation — `README.md` - -- Add an install section **"🪟 Windows AMD/Intel GPU with DirectML - (experimental)"** mirroring the CUDA / Apple Silicon / CPU sections: - - `pip install "audio-separator[dml]"` - - The `--env_info` confirmation log line: - `ONNXruntime has DmlExecutionProvider available, enabling acceleration` - - The requirement to pass `--use_directml`. -- Include an honest status note reflecting the compatibility table above - (MDX confirmed; MDXC/roformer & VR expected but untested; Demucs unverified), - and ask users to open an issue with logs. -- Add `--use_directml` to the CLI usage/help reference block in the README if one - is maintained there. - -### 4. Tests — `tests/unit/test_cli.py` - -- Add `"use_directml": False` to the `common_expected_args` fixture - (`test_cli.py`). **Required:** once `cli.py` forwards `use_directml` into the - `Separator(...)` call, every test that asserts - `assert_called_once_with(**common_expected_args)` will fail unless the fixture - includes the key. The fixture update and the `cli.py` forwarding must land - together. -- Add `test_cli_use_directml_argument` mirroring `test_cli_use_autocast_argument` - (asserts `--use_directml` results in `use_directml=True` in the `Separator` - call). -- No DirectML *execution* test — DML cannot run in CI. We test only the - CLI→constructor wiring, exactly as `use_autocast` is tested. -- **Remote CLI (`tests/unit/test_remote_cli.py`) is deliberately NOT touched** — - see Non-goals. - -### 5. Reply to contributor (draft; maintainer sends) - -- Confirm DirectML is a real, working feature (PR #211) that was only ever exposed - via the Python API — the CLI wiring was an oversight, not an intentional - disable. Her workaround was correct. -- State that `--use_directml` is landing in the next patch release. -- Thank her for the precise diagnosis. -- Light validation invite: ask if she would test MDXC/roformer, VR, and Demucs - models on her AMD GPU and report which work, noting MDX is the only architecture - confirmed so far. - -### 6. Release - -- Patch version bump in `pyproject.toml`. -- PR (with `@coderabbitai ignore` per workflow), merge, PyPI release via the - existing `publish-to-pypi` workflow. - -## Non-goals (YAGNI) - -- No auto-enable of DirectML — explicit opt-in only. -- No DirectML CI — infeasible (Windows + discrete non-NVIDIA GPU). -- No changes to Demucs or other architecture internals to "fix" DirectML — we - document status; we do not chase untested fixes. -- No formal support commitment or ongoing compatibility-matrix maintenance - burden. -- **No remote CLI / API / deploy-server changes.** DirectML is local-only - (Windows + AMD/Intel GPU); the remote separation servers run on Cloud Run / - Modal with CUDA or CPU and can never present a DirectML device. Plumbing - `use_directml` through `audio_separator/remote/*` would be misleading dead - configuration. The local `cli.py` change does not affect the remote tests - (separate arg parser). - -## Risk assessment - -Very low. The DirectML branch is unreachable unless CUDA and MPS are both absent, -`torch_directml` is installed, and the user explicitly opts in. The CLI flag and -the conditional hint cannot change behavior for any CUDA, MPS, or CPU user. Tests -cover the wiring; the runtime path is unchanged from the already-merged PR #211.