From 842443cd7565253c76438a502c7cdbadaa5e7cfe Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Mon, 27 Jul 2026 05:21:02 +0000 Subject: [PATCH] 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 --- backend/cpp/audio-cpp/loaded_model.cpp | 17 ++++++++++++++--- backend/cpp/audio-cpp/loaded_model.h | 9 ++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/backend/cpp/audio-cpp/loaded_model.cpp b/backend/cpp/audio-cpp/loaded_model.cpp index ba4d28ab8..779a19ab6 100644 --- a/backend/cpp/audio-cpp/loaded_model.cpp +++ b/backend/cpp/audio-cpp/loaded_model.cpp @@ -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); diff --git a/backend/cpp/audio-cpp/loaded_model.h b/backend/cpp/audio-cpp/loaded_model.h index 9714b70d6..c132fc72f 100644 --- a/backend/cpp/audio-cpp/loaded_model.h +++ b/backend/cpp/audio-cpp/loaded_model.h @@ -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:" resolves to -// /assets/, 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:", which resolves to /assets/, 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);