Skip to content

media-use audio engine ignores HYPERFRAMES_PYTHON, so local MusicGen BGM is skipped even though doctor reports it installed #4614

Description

@cybertron-007

Describe the bug

The CLI and the media-use audio engine resolve Python differently:

  • packages/cli/src/tts/python.ts → findPython() checks HYPERFRAMES_PYTHON first. doctor uses it, and the CLI's install hints say to "point HYPERFRAMES_PYTHON at a venv python that has them".
  • skills/media-use/audio/scripts/lib/python.mjs → resolvePythonCommand() only probes python3 / python on PATH.

On macOS with Homebrew Python (PEP 668, externally managed), the documented setup is a venv plus HYPERFRAMES_PYTHON. With that setup doctor reports MusicGen as installed, but the audio engine probes the bare Homebrew python3 and disables BGM. So every workflow renders without music on a machine configured the way the docs describe. Its fallback python3 -m pip install … into the system interpreter also fails, as PEP 668 intends. Kokoro TTS is unaffected because it goes through the CLI, which does honour the variable.

Minimal reproduction

Self-contained. No project or assets needed:

uv venv ~/.venvs/hf --python 3.12
uv pip install --python ~/.venvs/hf/bin/python transformers torch soundfile numpy
export HYPERFRAMES_PYTHON=~/.venvs/hf/bin/python
mkdir repro && cd repro
echo '{"lines":[],"bgm":{"mode":"generate","prompt":"calm ambient pad"}}' > audio_request.json
node <skills>/media-use/audio/scripts/audio.mjs --request ./audio_request.json --hyperframes . --out ./audio_meta.json --only bgm

Steps to reproduce

  1. Set up the venv and export HYPERFRAMES_PYTHON as above. python3 on PATH is Homebrew's, with no torch.
  2. npx hyperframes doctor → ✓ BGM (MusicGen) MusicGen deps installed
  3. Run the audio engine command above.

Expected behavior

The engine uses HYPERFRAMES_PYTHON, as the CLI does, launches MusicGen, and writes assets/bgm/track.wav.

Actual behavior

anomalies (non-fatal):
  - bgm: no Lyria key/recipe and local MusicGen deps unavailable (pip install transformers torch soundfile numpy)

No music track is produced. Workflows continue silently without BGM.

Environment

macOS 26.6 (Apple Silicon M5), Node 25.9.0, hyperframes 0.8.81, skills at main @ ff6e210, Python 3.12 venv (uv), Homebrew python3 3.14.

Proposed fix

resolvePythonCommand() checks HYPERFRAMES_PYTHON first, probing that it runs. It falls through to the existing PATH probe if it doesn't, the same way findPython() does. There are 3 new unit tests: the override wins, a non-running override falls through, and an empty override is never probed. python.test.mjs passes 10/10, and the bgm / tts suites pass 14/14. End to end, the repro above launches MusicGen and writes the track with no PATH changes.

diff --git a/skills/media-use/audio/scripts/lib/python.mjs b/skills/media-use/audio/scripts/lib/python.mjs
--- a/skills/media-use/audio/scripts/lib/python.mjs
+++ b/skills/media-use/audio/scripts/lib/python.mjs
@@ -26,12 +26,27 @@ function defaultProbe(cmd, args) {
  * Pick the argv prefix that launches Python 3 on this platform.
  * Returns e.g. `["python3"]`, `["python"]`, or `["py", "-3"]`.
  *
- * Pure except for `probe` (which runs `<cmd> … --version`); both `platform`
- * and `probe` are injectable so every branch is unit-testable without spawning.
- * If nothing probes OK, falls back to the canonical name for the platform so
- * the eventual spawn fails loudly exactly as it did before — never worse.
+ * `HYPERFRAMES_PYTHON` wins when it runs, matching the CLI's `findPython()`
+ * (packages/cli/src/tts/python.ts). The CLI's install hints and `doctor` both
+ * tell users to point it at a venv holding the MusicGen/ElevenLabs deps, and
+ * `doctor` then reports them as installed — so ignoring it here left BGM
+ * generation probing a bare `python3` without those deps and silently skipping
+ * the track. An override that does not run falls through to the PATH probe,
+ * as the CLI does.
+ *
+ * Pure except for `probe` (which runs `<cmd> … --version`); `platform`,
+ * `probe` and `env` are injectable so every branch is unit-testable without
+ * spawning. If nothing probes OK, falls back to the canonical name for the
+ * platform so the eventual spawn fails loudly exactly as it did before — never
+ * worse.
  */
-export function resolvePythonCommand(platform = process.platform, probe = defaultProbe) {
+export function resolvePythonCommand(
+  platform = process.platform,
+  probe = defaultProbe,
+  env = process.env,
+) {
+  const override = env.HYPERFRAMES_PYTHON;
+  if (override && probe(override, ["--version"])) return [override];
   const candidates =
     platform === "win32" ? [["python3"], ["python"], ["py", "-3"]] : [["python3"], ["python"]];
   for (const prefix of candidates) {
diff --git a/skills/media-use/audio/scripts/lib/python.test.mjs b/skills/media-use/audio/scripts/lib/python.test.mjs
--- a/skills/media-use/audio/scripts/lib/python.test.mjs
+++ b/skills/media-use/audio/scripts/lib/python.test.mjs
@@ -55,6 +55,29 @@ test("falls back to the canonical name (loud failure, unchanged) when nothing ru
   );
 });
 
+test("HYPERFRAMES_PYTHON wins over PATH when it runs (matches the CLI's findPython)", () => {
+  const venv = "/home/u/.venvs/hf/bin/python";
+  const env = { HYPERFRAMES_PYTHON: venv };
+  assert.deepEqual(resolvePythonCommand("darwin", probeFor(venv, "python3"), env), [venv]);
+  assert.deepEqual(resolvePythonCommand("win32", probeFor(venv, "python3", "py"), env), [venv]);
+});
+
+test("a HYPERFRAMES_PYTHON that does not run falls through to the PATH probe", () => {
+  const env = { HYPERFRAMES_PYTHON: "/nonexistent/python" };
+  assert.deepEqual(resolvePythonCommand("linux", probeFor("python3"), env), ["python3"]);
+  assert.deepEqual(resolvePythonCommand("win32", probeFor("py"), env), ["py", "-3"]);
+});
+
+test("an empty HYPERFRAMES_PYTHON is ignored and never probed", () => {
+  const seen = [];
+  const probe = (cmd) => {
+    seen.push(cmd);
+    return cmd === "python3";
+  };
+  assert.deepEqual(resolvePythonCommand("linux", probe, { HYPERFRAMES_PYTHON: "" }), ["python3"]);
+  assert.deepEqual(seen, ["python3"]);
+});
+
 test("pythonInvocation prepends the resolved prefix ahead of caller args", () => {
   assert.deepEqual(pythonInvocation(["-c", "import x"], ["python"]), {
     cmd: "python",

Happy to open this as a PR if that's easier to review.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions