Skip to content

feat: split stem separation into phantom-audio-separation plugin package#43

Merged
leesaenz merged 5 commits into
mainfrom
feat/separation-split
Jul 11, 2026
Merged

feat: split stem separation into phantom-audio-separation plugin package#43
leesaenz merged 5 commits into
mainfrom
feat/separation-split

Conversation

@leesaenz

Copy link
Copy Markdown
Collaborator

Summary

Splits the [separation] extra into a sibling distribution, per the plan in #7. The core phantom-audio package now carries zero PyTorch/Demucs dependency surface — its release cadence and platform matrix are decoupled from PyTorch's.

  • New workspace member packages/phantom-audio-separation/ (v1.4.0): the Demucs implementation moved verbatim (path validation, decode-bomb guard, silent-input guard, timeout, safe stem names) into phantom_separation.demucs_backend, registered under the phantom.separation entry-point group. Depends on phantom-audio>=1.3.1, demucs>=4.0, torch>=2.0, torchaudio>=2.0.
  • phantom/separation.py is now a thin shim: keeps SeparationResult and the separate_stems(input_path, output_dir) signature, discovers the installed backend via entry points, dispatches to it, and otherwise raises DependencyMissingError with the unchanged install hint (uv tool install "phantom-audio[separation]" --python 3.13). All consumers (phantom.__init__, MCP tool, phantom separate) work unchanged.
  • [separation] extra becomes a backward-compatible meta-installer pulling phantom-audio-separation; [all] stays correct transitively. The phantom-audio[separation]phantom-audio-separation workspace cycle resolves cleanly in uv lock.
  • Plugin detection replaces import probing (issue Split phantom-audio[separation] into a separate distribution #7's doctor/preflight requirement): new separation_plugin_status() in _diagnostics.py reports OK only when the entry point is present and loads. phantom doctor gains a phantom-audio-separation row, the MCP _startup_preflight reports separation=OK/missing from plugin detection, and phantom setup probes/installs the sibling package.
  • Tests: shim tests cover the missing-plugin hint, dispatch passthrough, entry-point discovery, and a core-metadata test (tomllib/tomli) asserting no torch/torchaudio/demucs anywhere in phantom-audio's dependencies or extras. The moved behavior tests (all demucs mocked via sys.modules — no torch execution in CI) live in packages/phantom-audio-separation/tests and run in the optional-deps CI job, where the workspace member installs via --extra separation.
  • CI/gate: ruff check/format now cover packages/ in CI and scripts/pre-push.

Release note

Not published to PyPI here and no phantom-audio version bump — both belong to the pending 1.4.0 release train. Until phantom-audio-separation is on PyPI, pip install "phantom-audio[separation]" from PyPI won't resolve the new extra; within this repo the uv workspace source covers it. RELEASING.md documents building/publishing the sibling package at release time.

Closes #7

Test plan

  • uv run pytest tests/ -x -q — 1041 passed, 47 skipped
  • PYTHONPATH=packages/phantom-audio-separation/src uv run pytest packages/phantom-audio-separation/tests -q — 13 passed, 1 skipped (entry-point registration test needs a real install; runs in the optional-deps CI job)
  • uv tool run ruff check src/ tests/ packages/ and ruff format --check — clean
  • Smoke-tested locally: phantom.separate_stems raises DependencyMissingError with the exact legacy hint when no plugin is installed; phantom doctor --json shows the phantom-audio-separation row; MCP preflight prints separation=missing
  • uv sync --locked --extra dev passes with the regenerated lockfile

🤖 Generated with Claude Code

leesaenz and others added 4 commits July 11, 2026 02:34
New sibling distribution carrying the Demucs/PyTorch dependency tree
(issue #7). The implementation moves verbatim from phantom/separation.py
into packages/phantom-audio-separation (phantom_separation.demucs_backend),
registered under the phantom.separation entry-point group. Behavior tests
move with it (all demucs mocked via sys.modules -- no torch execution).

Root package wiring:
- [tool.uv.workspace] members = packages/*, workspace source for the plugin
- [separation] extra becomes a backward-compatible meta-installer pulling
  phantom-audio-separation; core deps stay torch-free
- dev extra gains tomli for the py3.10 metadata test
- uv.lock regenerated (workspace cycle phantom-audio[separation] ->
  phantom-audio-separation -> phantom-audio resolves cleanly)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oint

phantom/separation.py becomes a thin shim: it keeps SeparationResult and
the separate_stems signature, discovers an installed backend plugin via
the phantom.separation entry-point group, and dispatches to it. With no
plugin installed it raises DependencyMissingError with the unchanged
install hint (uv tool install "phantom-audio[separation]").

Plugin detection replaces the direct demucs import probe everywhere:
- _diagnostics: separation_plugin_status() reports OK only when the entry
  point is present AND loads; demucs dropped from OPTIONAL_DEPS
- phantom doctor: phantom-audio-separation row via plugin detection
- MCP _startup_preflight: separation=OK/missing via plugin detection
- phantom setup: probes the plugin and installs phantom-audio-separation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tests/test_separation.py rewritten around the shim: missing-plugin
DependencyMissingError hint, backend dispatch passthrough, entry-point
discovery, and a metadata test asserting no torch/torchaudio/demucs in
phantom-audio's own dependencies or extras and that [separation] is
exactly the phantom-audio-separation meta-installer. Doctor tests gain
plugin-row coverage (OK only when loadable; missing plugin never fails
overall health).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ruff check/format now cover packages/ in CI and scripts/pre-push
- optional-deps job runs packages/phantom-audio-separation/tests (the
  workspace member installs there via --extra separation; tests stay
  mock-based, no torch execution)
- README notes the [separation] extra is a meta-installer for the sibling
  package; RELEASING.md documents building/publishing it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"""Importing the backend without demucs installed does not raise (SEP-02)."""
import builtins
import sys
import phantom_separation.demucs_backend
Comment thread tests/test_separation.py
PathSecurityError,
)

import phantom.separation as separation_mod
test_import_without_demucs reloads the backend module, replacing its
function objects; comparing ep.load() against the import captured at
collection time then fails. Compare against the module attribute at
assertion time instead (the same lookup ep.load() performs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"""The installed distribution exposes phantom.separation -> separate_stems."""
from importlib.metadata import entry_points

import phantom_separation.demucs_backend as backend_mod
@leesaenz leesaenz merged commit e76700d into main Jul 11, 2026
11 checks passed
@leesaenz leesaenz deleted the feat/separation-split branch July 11, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split phantom-audio[separation] into a separate distribution

1 participant