feat(audio-cpp): AUDIOCPP_DEFAULT_BACKEND fallback for models without a backend option (#12133)

Models whose options carry no explicit backend: open their session on the
CPU backend even in accelerator images. The gallery entries carry
backend:best since #11892; this covers hand-written model configurations
the same way, per deployment: the environment variable supplies the
fallback, an explicit backend: option always wins (merged beside the
existing threads and maingpu fallbacks), and validation reuses the
option parser.

Assisted-by: Claude:claude-fable-5

Signed-off-by: Plamen K. Kosseff <p.kosseff@gmail.com>
This commit is contained in:
Plamen K. Kosseff authored and GitHub committed 2026-09-23 12:16:31 +02:00
1 parent 2e279920b0
commit e7306a087a
6 files changed
+39 -1

No files matched your search

+19
View File
@@ -596,6 +596,25 @@ public:
if (!parsed.options.device_set && !request->maingpu().empty()) {
parsed.options.device = parse_device_index(request->maingpu());
}
// AUDIOCPP_DEFAULT_BACKEND is the deployment's backend fallback:
// an accelerator image sets it (typically to "best") so models
// without an explicit backend: option use the compiled
// accelerator instead of the CPU default — gallery entries carry
// backend:best, hand-written model configs get the same fix
// here. An explicit backend: option wins, matching threads and
// maingpu above. Validation reuses the option parser itself.
if (!parsed.options.backend_set) {
const char * env = std::getenv("AUDIOCPP_DEFAULT_BACKEND");
if (env != nullptr && *env != '\0') {
auto fallback = audiocpp_backend::parse_model_options(
{std::string("backend:") + env});
if (!fallback.error.empty()) {
throw audiocpp_backend::ConfigError(
"audio-cpp: AUDIOCPP_DEFAULT_BACKEND: " + fallback.error);
}
parsed.options.backend = fallback.options.backend;
}
}
const std::string path = audiocpp_backend::resolve_model_path(
request->modelpath(), request->modelfile(), request->model());
+1
View File
@@ -120,6 +120,7 @@ ParsedOptions parse_model_options(const std::vector<std::string> &entries) {
return parsed;
}
parsed.options.backend = value;
parsed.options.backend_set = true;
} else if (key == "model_spec_override") {
parsed.options.model_spec_override = value;
} else if (key == "device") {
+4
View File
@@ -19,6 +19,10 @@ struct ModelOptions {
std::string task;
// ggml backend: cpu, cuda, hip (or rocm), vulkan, metal, best.
std::string backend = "cpu";
// True once a `backend:` entry has been seen: "cpu" is both the default
// and a legitimate explicit choice, so the value alone cannot tell them
// apart, and a caller merging in its own fallback needs the difference.
bool backend_set = false;
int device = 0;
// True once a `device:` entry has been seen. 0 is both the default and a
// legitimate device index, so the value alone cannot tell an explicit
@@ -42,6 +42,10 @@ static void test_defaults() {
check(r.options.family.empty(), "family defaults to empty");
check(r.options.task.empty(), "task defaults to empty");
check(r.options.backend == "cpu", "backend defaults to cpu");
// backend_set separates the "cpu" default from an explicit backend:cpu —
// grpc-server merges the AUDIOCPP_DEFAULT_BACKEND fallback only when the
// model's options chose nothing.
check(!r.options.backend_set, "backend_set defaults to false");
check(r.options.device == 0, "device defaults to 0");
check(r.options.threads == 0, "threads defaults to 0");
check(r.options.busy_timeout_ms == 0, "busy_timeout_ms defaults to 0");
@@ -68,6 +72,7 @@ static void test_scalar_options() {
check(r.options.family == "qwen3_tts", "family parsed");
check(r.options.task == "tts", "task parsed");
check(r.options.backend == "cuda", "backend parsed");
check(r.options.backend_set, "backend_set records the explicit option");
check(r.options.device == 1, "device parsed");
check(r.options.threads == 8, "threads parsed");
check(r.options.busy_timeout_ms == 30000, "busy_timeout_ms parsed");
+5
View File
@@ -10,6 +10,11 @@ set -e
CURDIR=$(dirname "$(realpath "$0")")
# The image is built for one accelerator; models whose options carry no
# explicit backend: should use it rather than the wrapper's CPU default.
# An explicit backend: option and a caller's own environment both win.
export AUDIOCPP_DEFAULT_BACKEND="${AUDIOCPP_DEFAULT_BACKEND:-best}"
if [ "$(uname -s)" = "Darwin" ]; then
export DYLD_LIBRARY_PATH="$CURDIR/lib:$CURDIR:$DYLD_LIBRARY_PATH"
exec "$CURDIR/grpc-server" "$@"
+5 -1
View File
@@ -31,7 +31,11 @@ in the model YAML, or select it explicitly in the import form.
The bundled audio-cpp gallery entries set `backend:best` in `options` to select
an available compute backend, with CPU as the fallback. To force CPU execution,
replace that option with `backend:cpu`. Model configurations that omit this
option still default to CPU.
option still default to CPU — unless the `AUDIOCPP_DEFAULT_BACKEND`
environment variable is set on the backend process, which supplies the
fallback for exactly those models (an explicit `backend:` option always
wins). Set it to `best` in a deployment to give hand-written model
configurations the same accelerator selection the gallery entries get.
### Sortformer installation