Skip to content

feat: stems option and CLI flag (0.15.0 / cli 0.14.0) - #31

Merged
spencer-zqian merged 1 commit into
mainfrom
feat/stems
Aug 17, 2026
Merged

feat: stems option and CLI flag (0.15.0 / cli 0.14.0)#31
spencer-zqian merged 1 commit into
mainfrom
feat/stems

Conversation

@spencer-zqian

Copy link
Copy Markdown
Contributor

Adds the API's new stems option to the core SDK and the CLI.

stems=True on text_to_music/video_to_music submit()/generate_async() returns the generated track split into four separated instrument tracks — drums, bass, vocals, other — as MusicResult.stems alongside the clean audio. Free of charge; async-only (bool(stems) joins the async guard — an explicit stems=False requests nothing finalize-time and is allowed on a stream, which the backend accepts). On video-to-music it splits the generated music, never the video's own audio.

Result contract: stems_for(stream_index) looks entries up by stream_index, never position (a stream whose separation failed is absent), and stems_error is independent — it can accompany a partial list. save_stem/asave_stem download one stem; a missing entry's error message surfaces stems_error. Separation adds 2-6 min typically with a 30-min ceiling, so the CLI's --stems runs use a 2400s wait timeout, following the dubbing precedent, and the README advises timeout=2400 for SDK polling.

CLI: --stems on both music commands (forces async), stems written as take.drums.m4a / per-variant take.1.drums.m4a named by each entry's own stream_index; partial failure warns on stderr without failing the run. Core pin widened to >=0.15.0,<0.16 as in #30.

Tests: core 289, cli 154, video-kit 53 — all green.

Propagates the API's new `stems` boolean to the SDK and the CLI: passed
with an async text-to-music or video-to-music task, it also splits the
GENERATED music — on video-to-music never the video's own audio — into
drums/bass/vocals/other. Free of charge.

The request side mirrors the existing async-only options: the field is
tri-state (omitted unless passed; an explicit false goes on the wire, same
as ducking) and requesting stems joins _resolve_music_mode's fail-fast via
bool(stems) — stems=False asks for nothing finalize-time, so unlike
ducking's `is not None` it must not force async. text-to-music's builder is
already async-only, so it needs no per-field guard.

The result side adds two INDEPENDENT fields to MusicResult, parsed
unconditionally because the contract lets them coexist:

- `stems`: one MusicStems entry per stream that separated successfully,
  looked up by stream_index and NEVER list position — the list can be
  shorter than `audio`, so stems[i] would silently pair the wrong stems
  with a track. stems_for() does the lookup; save_stem()/asave_stem()
  download one stem, and the missing-entry error names the API's own
  stems_error so it explains itself.
- `stems_error`: present when separation failed wholly or in part, or was
  skipped. It can accompany a PARTIAL stems list, so nothing treats it as
  "no stems".

Malformed stems entries are coerced-and-dropped like parse_dubbing_result's
outputs, for the same reason.

Separation runs after generation, typically adds 2-6 minutes and gives up
after 30 — long past DEFAULT_WAIT_TIMEOUT's 600 seconds. Following the
dubbing precedent, the SDK keeps the generic default and documents passing
timeout=2400, while the CLI's new --stems flag switches its wait to
STEMS_WAIT_TIMEOUT (2400 s) so it never abandons a task it is already
waiting on. The CLI saves stems next to the output (take.drums.m4a; per
variant take.1.drums.m4a, named by the entry's own stream_index) and
reports stems_error as a stderr warning — a partial separation of a free
add-on must not turn a succeeded generation into an error exit — while
still writing everything that did come back.

sonilo-cli's narrow pin on the core moves to >=0.15.0,<0.16 with the bump.
Core sonilo 0.14.0 -> 0.15.0, sonilo-cli 0.13.0 -> 0.14.0.

pytest: 289 passed (core), 154 passed (sonilo-cli), 53 passed
(sonilo-video-kit, unaffected).
@sapient-app

sapient-app Bot commented Aug 17, 2026

Copy link
Copy Markdown

Lightsage docs evals

Waiting for the staging docs URL before running evals.

Lightsage will start the selected PR evals automatically when GitHub reports a successful docs deployment for this PR. This usually happens within 15 minutes.

Commit: 45291e8
Status: waiting for staging docs URL

@spencer-zqian
spencer-zqian merged commit 00e6016 into main Aug 17, 2026
2 checks passed
@spencer-zqian
spencer-zqian deleted the feat/stems branch August 17, 2026 21:12
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.

1 participant