backend(audio-cpp): make bundled: reachable from a model YAML

resolve_model_path() tested the bundled: prefix on `candidate`, which prefers
ModelFile and falls back to Model. LocalAI fills ModelFile by joining ModelPath
onto the configured model string (pkg/model/loader.go, LoadModelWithFile), and
only sets it from a managed artifact otherwise, so a model YAML saying
`model: bundled:silero_vad` arrives as ModelFile "/models/bundled:silero_vad"
and Model "bundled:silero_vad". The prefix therefore never matched through the
normal load path: it matched only for a hand-written LoadModel call that left
ModelFile empty, which is exactly how task 15 verified it, and every model YAML
using the form failed with "model path does not exist:
/models/bundled:silero_vad".

Both fields are now checked, Model first, so the zero-download VAD path the
package ships assets for is reachable the way it is documented. A caller that
puts the form in ModelFile still works, so task 15's verification stands.

Compiled clean; the runtime check could not run on this host, whose system
libprotobuf/libre2 have gone missing (the pre-existing grpc-server binary no
longer resolves its libraries either), so it wants a container run.

Assisted-by: Claude:claude-opus-5 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-07-27 05:21:02 +00:00
parent e5236ed498
commit 842443cd75
2 changed files with 20 additions and 6 deletions

View File

@@ -322,10 +322,21 @@ std::string resolve_model_path(const std::string &model_path_dir,
const std::string &model_name) {
const std::string candidate = !model_file.empty() ? model_file : model_name;
// The bundled: form is looked for in BOTH fields, and in ModelOptions.Model
// FIRST, because that is the only field it survives in. LocalAI fills
// ModelFile by joining ModelPath onto the configured model string
// (pkg/model/loader.go, LoadModelWithFile), so a model YAML saying
// `model: bundled:silero_vad` arrives here as ModelFile
// "/models/bundled:silero_vad" and Model "bundled:silero_vad". Testing
// `candidate` alone therefore made the zero-download VAD path reachable only
// from a hand-written LoadModel call that left ModelFile empty, and every
// model YAML using it failed with "model path does not exist".
const std::string bundled_prefix = "bundled:";
if (candidate.rfind(bundled_prefix, 0) == 0) {
const std::string name = candidate.substr(bundled_prefix.size());
return (executable_directory() / "assets" / name).string();
for (const std::string *field : {&model_name, &model_file}) {
if (field->rfind(bundled_prefix, 0) == 0) {
const std::string name = field->substr(bundled_prefix.size());
return (executable_directory() / "assets" / name).string();
}
}
std::filesystem::path path(candidate);

View File

@@ -48,9 +48,12 @@ Capabilities to_capabilities(const std::string &family,
std::string read_gguf_family(const std::string &path);
// Builds the absolute model path from LocalAI's (ModelPath, ModelFile, Model)
// triple. A model file of the form "bundled:<name>" resolves to
// <executable dir>/assets/<name>, which is where package.sh puts upstream's
// bundled silero_vad and marblenet_vad assets.
// triple. Either the Model or the ModelFile field may carry the form
// "bundled:<name>", which resolves to <executable dir>/assets/<name>, where
// package.sh puts upstream's bundled silero_vad and marblenet_vad assets. BOTH
// are checked because LocalAI fills ModelFile by joining ModelPath onto the
// configured model string, so a model YAML using the form has it intact only in
// Model.
std::string resolve_model_path(const std::string &model_path_dir,
const std::string &model_file,
const std::string &model_name);