Files
LocalAI/backend/go/acestep-cpp/goacestepcpp.go
LocalAI [bot] 0ec3ea4a46 fix(acestep-cpp): resolve relative model paths in options (#8993)
* fix(acestep-cpp): resolve relative model paths in options

The acestep-cpp backend was failing to load models because the model
paths in options (text_encoder_model, dit_model, vae_model) were being
passed to the C++ code without resolving their relative paths.

When a user configures acestep-cpp-turbo-4b, the model paths are specified
as relative paths like 'acestep-cpp/acestep-v15-turbo-Q8_0.gguf'. The
backend was passing these paths directly to the C++ code without joining
them with the model directory.

This fix:
1. Gets the base directory from the ModelFile path
2. Resolves all relative paths in options to be absolute paths
3. Adds debug logging to show resolved paths for troubleshooting

Fixes #8991

Signed-off-by: localai-bot <localai-bot@users.noreply.github.com>

* Apply suggestion from @mudler

Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>

* test: fix acestep tests to not join modeldir in options

According to code review feedback, the Options array in TestLoadModel
and TestSoundGeneration should contain just the model filenames without
filepath.Join with modelDir. The model paths are handled internally by
the backend.

* fix: change bpm parameter type to float32 to match C++ API signature

* test: fix TestLoadModel and TestSoundGeneration to use baseDir for model paths

- Modified TestLoadModel to compute baseDir from main model path and use it for relative model paths
- Modified TestSoundGeneration similarly to use baseDir for model paths
- Changed bpm parameter type from int32 to float32 to match C++ API

* Apply suggestions from code review

Co-authored-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>

---------

Signed-off-by: localai-bot <localai-bot@users.noreply.github.com>
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
Co-authored-by: localai-bot <localai-bot@users.noreply.github.com>
Co-authored-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
2026-03-14 01:16:13 +01:00

110 lines
3.0 KiB
Go

package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mudler/LocalAI/pkg/grpc/base"
pb "github.com/mudler/LocalAI/pkg/grpc/proto"
)
var (
CppLoadModel func(lmModelPath, textEncoderPath, ditModelPath, vaeModelPath string) int
CppGenerateMusic func(caption, lyrics string, bpm int, keyscale, timesignature string, duration, temperature float32, instrumental bool, seed int, dst string, threads int) int
)
type AceStepCpp struct {
base.SingleThread
}
func (a *AceStepCpp) Load(opts *pb.ModelOptions) error {
// ModelFile is the LM model path
lmModel := opts.ModelFile
// Get the base directory from ModelFile for resolving relative paths
baseDir := filepath.Dir(lmModel)
var textEncoderModel, ditModel, vaeModel string
for _, oo := range opts.Options {
parts := strings.SplitN(oo, ":", 2)
if len(parts) != 2 {
fmt.Fprintf(os.Stderr, "Unrecognized option: %v\n", oo)
continue
}
switch parts[0] {
case "text_encoder_model":
textEncoderModel = parts[1]
case "dit_model":
ditModel = parts[1]
case "vae_model":
vaeModel = parts[1]
default:
fmt.Fprintf(os.Stderr, "Unrecognized option: %v\n", oo)
}
}
if textEncoderModel == "" {
return fmt.Errorf("text_encoder_model option is required")
}
if ditModel == "" {
return fmt.Errorf("dit_model option is required")
}
if vaeModel == "" {
return fmt.Errorf("vae_model option is required")
}
// Resolve relative paths to the base directory
// If the path doesn't start with "/" it's relative
if !filepath.IsAbs(textEncoderModel) {
textEncoderModel = filepath.Join(baseDir, textEncoderModel)
}
if !filepath.IsAbs(ditModel) {
ditModel = filepath.Join(baseDir, ditModel)
}
if !filepath.IsAbs(vaeModel) {
vaeModel = filepath.Join(baseDir, vaeModel)
}
// Also resolve the lmModel if it's relative
if !filepath.IsAbs(lmModel) {
lmModel = filepath.Join(baseDir, lmModel)
}
fmt.Fprintf(os.Stderr, "[acestep-cpp] Resolved paths:\n")
fmt.Fprintf(os.Stderr, " LM Model: %s\n", lmModel)
fmt.Fprintf(os.Stderr, " Text Encoder: %s\n", textEncoderModel)
fmt.Fprintf(os.Stderr, " DiT Model: %s\n", ditModel)
fmt.Fprintf(os.Stderr, " VAE Model: %s\n", vaeModel)
if ret := CppLoadModel(lmModel, textEncoderModel, ditModel, vaeModel); ret != 0 {
return fmt.Errorf("failed to load acestep models (error code: %d)", ret)
}
return nil
}
func (a *AceStepCpp) SoundGeneration(req *pb.SoundGenerationRequest) error {
caption := req.GetCaption()
if caption == "" {
caption = req.GetText()
}
lyrics := req.GetLyrics()
bpm := int(req.GetBpm())
keyscale := req.GetKeyscale()
timesignature := req.GetTimesignature()
duration := req.GetDuration()
temperature := req.GetTemperature()
instrumental := req.GetInstrumental()
seed := 42
threads := 4
if ret := CppGenerateMusic(caption, lyrics, bpm, keyscale, timesignature, duration, temperature, instrumental, seed, req.GetDst(), threads); ret != 0 {
return fmt.Errorf("failed to generate music (error code: %d)", ret)
}
return nil
}