cacce61ddc
- 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 <noreply@anthropic.com>
121 lines
3.9 KiB
Go
121 lines
3.9 KiB
Go
// Package musicgen is majordomo's canonical music-generation surface (full
|
|
// songs from a text prompt, optionally with lyrics). Like imagegen/audio/
|
|
// videogen/meshgen, it is a deliberately separate leaf contract from the llm
|
|
// package (ADR-0021, following the ADR-0016→0020 lineage: functional
|
|
// options, zero values = backend default, bytes-only I/O, Raw escape hatch).
|
|
//
|
|
// The first implementation is provider/llamaswap, which targets an
|
|
// ACE-Step-1.5-style async job API reached through the /upstream
|
|
// passthrough; Generate blocks (polling internally) so callers get the
|
|
// imagegen-style one-call contract — bound it with a context deadline.
|
|
package musicgen
|
|
|
|
import "context"
|
|
|
|
// Audio is one generated piece: raw encoded bytes plus a MIME type.
|
|
type Audio struct {
|
|
// Data is the encoded audio container.
|
|
Data []byte
|
|
|
|
// MIME is the audio MIME type, e.g. "audio/mpeg".
|
|
MIME string
|
|
}
|
|
|
|
// Request is a music-generation request. Zero values mean "backend default".
|
|
type Request struct {
|
|
// Prompt describes the music (genre, mood, instrumentation, tempo...).
|
|
Prompt string
|
|
|
|
// Lyrics are optional song lyrics for models that sing; "" =
|
|
// instrumental or model-written lyrics, per backend behavior.
|
|
Lyrics string
|
|
|
|
// DurationSeconds is the requested clip length; 0 = backend default.
|
|
DurationSeconds int
|
|
|
|
// Format is the audio container ("mp3", "wav", "flac", "opus", "aac");
|
|
// "" = backend default (mp3 on ACE-Step).
|
|
Format string
|
|
|
|
// Steps is the number of inference steps; nil = backend default.
|
|
Steps *int
|
|
|
|
// Seed fixes the RNG seed for reproducible output; nil = backend
|
|
// default (random).
|
|
Seed *int64
|
|
}
|
|
|
|
// Result is the canonical music-generation result.
|
|
type Result struct {
|
|
// Audio is the generated piece.
|
|
Audio Audio
|
|
|
|
// Raw is the provider-native response object (e.g. the job result with
|
|
// bpm/keyscale metadata). May be nil.
|
|
Raw any
|
|
}
|
|
|
|
// Option mutates a Request before it is sent. Options passed to Generate are
|
|
// applied to a copy of the request, so a Request value can be reused.
|
|
type Option func(*Request)
|
|
|
|
// WithLyrics sets song lyrics.
|
|
func WithLyrics(l string) Option { return func(r *Request) { r.Lyrics = l } }
|
|
|
|
// WithDuration sets the requested clip length in seconds.
|
|
func WithDuration(seconds int) Option {
|
|
return func(r *Request) { r.DurationSeconds = seconds }
|
|
}
|
|
|
|
// WithFormat sets the audio container format.
|
|
func WithFormat(f string) Option { return func(r *Request) { r.Format = f } }
|
|
|
|
// WithSteps overrides the number of inference steps.
|
|
func WithSteps(n int) Option { return func(r *Request) { r.Steps = &n } }
|
|
|
|
// WithSeed fixes the RNG seed.
|
|
func WithSeed(seed int64) Option { return func(r *Request) { r.Seed = &seed } }
|
|
|
|
// Apply returns a copy of the request with all options applied. Providers
|
|
// call this once at the top of Generate.
|
|
func (r Request) Apply(opts ...Option) Request {
|
|
for _, opt := range opts {
|
|
opt(&r)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Model generates music from text.
|
|
type Model interface {
|
|
// Generate renders the request as one audio clip. It blocks until the
|
|
// backend finishes (or ctx expires) even when the backend is an async
|
|
// job queue — polling is the provider's business, not the caller's.
|
|
Generate(ctx context.Context, req Request, opts ...Option) (*Result, error)
|
|
}
|
|
|
|
// ModelOption configures a Model at construction time. Reserved for future
|
|
// per-model settings.
|
|
type ModelOption func(*ModelConfig)
|
|
|
|
// ModelConfig carries per-model construction settings.
|
|
type ModelConfig struct{}
|
|
|
|
// ApplyModelOptions folds options into a config.
|
|
func ApplyModelOptions(opts []ModelOption) ModelConfig {
|
|
var cfg ModelConfig
|
|
for _, opt := range opts {
|
|
opt(&cfg)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// Provider mints music models bound to one backend.
|
|
type Provider interface {
|
|
// Name is the registry identifier for the provider.
|
|
Name() string
|
|
|
|
// MusicModel returns a Model bound to the given id (passed through to
|
|
// the backend verbatim; no catalog validation).
|
|
MusicModel(id string, opts ...ModelOption) (Model, error)
|
|
}
|