QVAC-23201 feat[api]: integrate @qvac/audiogen-ggml 0.2.1 into the SDK - #3899
Conversation
Bump the AudioGen addon to 0.2.1 and expose its new generation surface through audioGen(): - referenceAudio (timbre conditioning) and sourceAudio with taskType "cover-nofsq", audioCoverStrength and coverNoiseStrength. Both accept a file path (decoded server-side via the FFmpeg decoder to 48 kHz float PCM, or read as raw stereo f32le for other extensions) or an in-memory Buffer/Uint8Array of raw interleaved stereo 48 kHz Float32 PCM. - ACE-Step LM sampling controls (lmTemperature, lmTopP, lmTopK, lmCfgScale, lmPhase1) and Haar DCW parameters (dcwEnabled, dcwScaler, dcwHighScaler). - Cover tasks require sourceAudio, validated on the client before RPC and again on the wire schema. Malformed PCM fails with the new INVALID_AUDIO_INPUT server error before the addon is invoked. - AUDIOGEN_TASK_TYPES / AUDIOGEN_INPUT_SAMPLE_RATE / AUDIOGEN_INPUT_CHANNELS root exports; AudioGenTaskType registered for cross-language codegen. - decodeAudioToStream() takes an optional output sample rate. - Contract + generated Python client regenerated; docs and a cover example added; desktop e2e gains reference-audio and cover-nofsq runs plus the cover-missing-source client validation case.
License compliance — findings detected (warn-only)Critical: 0 · High: 1 · Medium: 0
How to resolve a blocking finding:
Warn-only (shadow) mode — this check does not block merges yet. Updated automatically by the canonical license compliance workflow. NOTICE presence (advisory)Missing NOTICE (advisory, does not block):
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Review StatusCurrent Status: ❌ PENDING Pending reviews: Needs 1 Management or Team Lead, and 1 more from Management, Team Lead, or Member. |
Address review findings on the reference/source audio path: - Re-check the abort signal after reference/source audio decoding and skip model.run() when the request was cancelled mid-decode; previously the abort bridge fired before a native job existed (no-op cancel) and the run still started, leaving a ghost generation on the per-model slot. - Read raw PCM files asynchronously (stat + createReadStream) instead of readFileSync, and bound every input by AUDIOGEN_INPUT_MAX_SECONDS (600 s): raw bytes, raw files, and the FFmpeg-decoded stream are all capped before they are materialized. - Reject non-finite samples in the SDK so every malformed-PCM case surfaces as INVALID_AUDIO_INPUT rather than an addon error. - View aligned buffers as Float32Array without copying; only misaligned slices are copied. - Decode both inputs with Promise.allSettled so a second failure is logged instead of masked. - Document the cap/finiteness rules, note the deliberate e2e executor option-injection choice, and add bare tests for cancel-during-decode, the size limit (sparse file), and NaN rejection.
QVAC E2E —
|
QVAC E2E —
|
QVAC E2E —
|
QVAC E2E —
|
GustavoA1604
left a comment
There was a problem hiding this comment.
[medium] packages/sdk/server/bare/plugins/audiogen-ggml/ops/audio-gen-input.ts — line 92 (const stream = await decodeAudioToStream(filePath, 'f32le', AUDIOGEN_INPUT_SAMPLE_RATE))
decodeAudioToStream() has a fixed 10-second total timeout that is not reset when output arrives. Consequently, valid reference or source files can fail long before the advertised 600-second limit. Make the timeout configurable or inactivity-based and add a long-running decode regression test.
[medium] packages/sdk/schemas/audio-gen.ts — line 134 (audioCoverStrength: unitIntervalSchema)
This accepts values below 1 for cover-nofsq, although the engine currently rejects them. Add conditional validation requiring audioCoverStrength === 1 for that task and test the unsupported-value branch.
…rength Address review feedback on the AudioGen input path: - decodeAudioToStream() previously armed a single 10 s timer for the whole decode, so long reference/source files could fail well before the 600 s input limit. The timer is now re-armed on every decoded chunk (inactivity bound), configurable through a DecodeAudioOptions object, and the decoder is unloaded on the timeout path too. The response→stream bridge is extracted as decoderResponseToStream() so the semantics are unit-testable; new bare tests cover a decode that outlives the window, a hung decoder, and a 5-minute WAV decoded end to end. - audioCoverStrength is now validated per task: cover-nofsq rejects any explicit value other than 1 (the engine has no partial source context yet) on both the client and wire schemas; text2music is unaffected. Tests cover the rejected branch and the docs describe the rule.
|
@GustavoA1604 thanks — both addressed in 202f9e0:
Also rebased on the |
There was a problem hiding this comment.
exposes audiogen-ggml 0.2.1 through audioGen() (reference/source, cover-nofsq, LM/DCW). abort-during-decode recheck is the right shape; gustavo's timeout + cover-strength notes look addressed. CI green.
two leftover decode-path issues: destroy doesn't cancel ffmpeg, and the 600s cap doesn't bound decoder-audio's full-file slurp. new e2e defs aren't in smoke (needs a full desktop e2e label to actually run).
…file size - decoderResponseToStream() settles on the output stream close event, so a consumer that destroys the stream early cancels the FFmpeg decode instead of leaving it running. - Decodable reference/source files are stat()-ed before decoding and rejected when larger than the raw stereo Float32 size of a AUDIOGEN_INPUT_MAX_SECONDS clip, bounding the decoder full-file buffer that the PCM output cap could not reach.
…-audiogen-021 # Conflicts: # packages/sdk/schemas/constants-registry.ts
🎯 What problem does this PR solve?
@qvac/audiogen-ggml0.2.1 (QVAC-22886 feat[audiogen-ggml]: add reference and cover audio inputs #3782, QVAC-23306 feat[audiogen-ggml]: align generation with official ACE-Step #3743, QVAC-23558: fix[audiogen] harden package lifecycle and distribution #3822, Bump tts-ggml to 0.7.0 and audiogen-ggml to 0.2.1 #3877) added reference/source audio inputs, thecover-nofsqtask, LM sampling + Haar DCW controls, and official mobile prebuilds (Android Vulkan/CPU, iOS CPU), but the SDK still pinned^0.1.0and only forwarded the original caption/lyrics/musical controls.audioGen().📝 How does it solve it?
@qvac/audiogen-ggmlto^0.2.1.audioGen()gainsreferenceAudio/sourceAudio,taskType(text2music|cover-nofsq),audioCoverStrength,coverNoiseStrength, LM sampling (lmTemperature,lmTopP,lmTopK,lmCfgScale,lmPhase1) and DCW (dcwEnabled,dcwScaler,dcwHighScaler) controls; the plugin forwards them 1:1 toAudioGen.run().transcribe'saudioChunk): paths with a decodable extension are decoded server-side through the existing FFmpeg decoder (now with a 48 kHz output option) and duplicated onto both channels; other paths (streamed asynchronously) and buffers are read as raw interleaved stereo 48 kHz Float32 LE PCM. Every input is bounded byAUDIOGEN_INPUT_MAX_SECONDS(600 s, enforced before/while materializing) and checked for finite samples; malformed or oversized PCM fails with the newINVALID_AUDIO_INPUT(52422) before the addon is invoked; missing files surfaceAUDIO_FILE_NOT_FOUND. Aligned buffers are viewed as Float32Array without copying.model.run()never starts for a cancelled request.sourceAudio, andcover-nofsqrejects any explicitaudioCoverStrengthother than1(the engine has no partial source context yet) — validated client-side (before RPC) and on the wire schema.decodeAudioToStream()now takes an options object; its timeout is inactivity-based (re-armed per decoded chunk, default 10 s, decoder unloaded on timeout) instead of a fixed 10 s total, so long inputs decode up to the 600 s limit. The response→stream bridge is exported asdecoderResponseToStream()for tests.AUDIOGEN_TASK_TYPES,AUDIOGEN_INPUT_SAMPLE_RATE,AUDIOGEN_INPUT_CHANNELS,AUDIOGEN_INPUT_MAX_SECONDS(+AudioGenTaskType/AudioGenAudioInputtypes);AudioGenTaskTyperegistered in the constants registry for cross-language codegen. Contract + generated Python client regenerated.examples/audiogen/generate-cover.ts.audioCodes(frozen semantic codes; no SDK path produces them) and the reserved, unimplemented FSQcovertask.🧪 How was it tested?
bun run build(lint + typecheck),bun run test:unit(109/109 files),bun run contract:check,sdk-python scripts/generate.py --check, e2enpm run typecheck.test/bare/audio-gen-input.test.ts, run underbrittle-barewith the 0.2.1 prebuild): raw base64/raw file inputs, real WAV → FFmpeg → 48 kHz stereo float decode (24000 frames for 0.5 s @ 44.1 kHz mono), option forwarding toAudioGen.run(), fail-fast on malformed PCM / NaN samples / over-limit clips (sparse file) without invoking the addon, and cancel-during-decode never starting the native run;test/bare/audio-decoder-timeout.test.tsproves the inactivity semantics (decode outliving the window, hung decoder, 5-minute WAV end to end); existingaudio-gen-plugincancellation tests still pass on 0.2.1.audio-gen-reference-audio(bundled WAV via file path),audio-gen-cover-nofsq(synthesized 1 s stereo tone as raw PCM buffer),audio-gen-cover-missing-source-error(client validation). Not run locally (needs the ~3.3 GB model set + GPU); to be exercised via thetest-e2e-*label.🔌 API Changes