feat: musicgen + embeddings/rerank surfaces (ADR-0021, ADR-0022)

- NEW musicgen leaf package: blocking Generate over ACE-Step's async job
  queue (release_task -> poll query_result -> fetch file, all via
  /upstream); tolerant envelope parsing, double-encoded result handled
- NEW embeddings leaf package: EmbedModel + RerankModel as separate mints
  (two server instances on the host, llama.cpp #20085); InstructedQuery
  helper for Qwen3-style query/document asymmetry
- provider/llamaswap: /v1/embeddings + /v1/rerank clients with strict
  validation (index-ordered vectors, count mismatch and out-of-range
  index are hard errors; rerank sorted descending, minimal parser)

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-13 00:50:36 -04:00
co-authored by Claude Fable 5
parent 4a752ffa2a
commit cacce61ddc
7 changed files with 964 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# ADR-0021: musicgen interface (blocking Generate over an async job queue)
Status: Accepted (2026-07-12)
## Context
The llama-swap host gained ACE-Step 1.5 (full songs with vocals, seconds per
clip warm on the reference GPU). Its API is an async job queue —
`POST /release_task` → poll `POST /query_result``GET` the result file —
unlike every other majordomo media backend, which is one blocking call.
## Decision
- New `musicgen` leaf package, ADR-0016→0020 conventions: `Request{Prompt,
Lyrics, DurationSeconds, Format, Steps, Seed}`, `Result{Audio{Data, MIME},
Raw}`, functional options, zero value = backend default, bytes-only.
- **`Model.Generate` blocks, polling internally** (2s interval, ctx-bounded).
The one-call contract is the package's value: callers budget the whole job
with a context deadline exactly like imagegen/videogen, and the async
mechanics stay a provider detail. An async job surface (mirroring the
deliberately-deferred videogen one, ADR-0019) can come later if a caller
ever needs progress.
- provider/llamaswap reaches ACE-Step through the `/upstream/<model>/`
passthrough (ADR-0020). Envelope parsing is tolerant (`data` wrapper or
bare array; `result` arrives as a double-encoded JSON string) and the
result-file URL is routed back through the same upstream. `audio_duration`
is the v1 param name — an unknown field degrades to default clip length
upstream, never an error; verify at smoke.
## Consequences
- Music jobs occupy the exclusive GPU group like video; callers own the
timeout budget (mort keeps the tool timeout under its agent ceiling).
- The double-encoded `result` string and envelope shapes are pinned by the
netherstorm image build; host smoke tests are the drift defence.