From 1c48625c882f6a00b34800301510941a75482762 Mon Sep 17 00:00:00 2001 From: localai-org-maint-bot Date: Mon, 26 Oct 2026 21:09:49 +0100 Subject: [PATCH] chore: :arrow_up: Update NVIDIA/NeMo-Speech.cpp to `e680fc30ac7404ba3acd58532f713becf8625894` (#12417) * :arrow_up: Update NVIDIA/NeMo-Speech.cpp Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(nemo-speech-cpp): use upstream llama.cpp ggml patching Initialize ggml through the llama.cpp submodule and let upstream CMake apply the patch series with fail-closed error handling. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto * fix(nemo-speech-cpp): recognize upstream OpenFST ownership fix Accept both WrapUnique assignments unchanged while preserving the legacy header patch and rejecting unknown or incomplete forms. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto * fix(nemo-speech-cpp): bundle shared text normalization runtime Stage the normalization DSO when ITN is present and include it in the package alongside its dependency closure. Fail staging if the required DSO is missing. Assisted-by: nib:gpt-6.1-sol Signed-off-by: Ettore Di Giacinto --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Ettore Di Giacinto Co-authored-by: mudler <2420543+mudler@users.noreply.github.com> Co-authored-by: Ettore Di Giacinto --- backend/go/nemo-speech-cpp/Makefile | 75 ++++++++++++--------------- backend/go/nemo-speech-cpp/package.sh | 9 ++-- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/backend/go/nemo-speech-cpp/Makefile b/backend/go/nemo-speech-cpp/Makefile index 9d76b91f7..206a5813d 100644 --- a/backend/go/nemo-speech-cpp/Makefile +++ b/backend/go/nemo-speech-cpp/Makefile @@ -12,7 +12,7 @@ # runs 'make -C backend/go/$(BACKEND) build' and then copies package/), so it # has to produce the binary and the package, not just the shared libraries. -NEMO_SPEECH_VERSION?=4c101bc7113f49101a3e11d2c994c519f41939f6 +NEMO_SPEECH_VERSION?=e680fc30ac7404ba3acd58532f713becf8625894 NEMO_SPEECH_REPO?=https://github.com/NVIDIA/NeMo-Speech.cpp GOCMD?=go @@ -152,11 +152,8 @@ endif # behind NEMO_SPEECH_GGML_PATCHED itself, which guards a Q8_PLANAR flag write # that a non-CUDA buffer already throws before reaching. # -# So on macOS the series buys nothing, and it cannot be applied there anyway: -# upstream's scripts/apply-ggml-patches.sh uses mapfile, a bash 4 builtin, and -# macOS ships bash 3.2 as the only bash on the runner's PATH. Skip the patch -# step and tell cmake the linked ggml is stock, which is exactly upstream's own -# Metal configuration. Linux keeps applying the series unchanged. +# On macOS keep ggml stock, matching upstream's Metal configuration. Upstream +# CMake now manages the patch series; Linux keeps enabling it by default. ifeq ($(shell uname -s),Darwin) GGML_PATCHED?=OFF else @@ -175,28 +172,16 @@ sources/NeMo-Speech.cpp: # NMT links llama.cpp; ja needs open_jtalk; zh needs cppjieba. flashlight and # kenlm are deliberately not initialized, they are out of scope. cd sources/NeMo-Speech.cpp && git submodule update --init --recursive \ - ggml llama.cpp third_party/open_jtalk third_party/cppjieba third_party/cpp-httplib + llama.cpp third_party/open_jtalk third_party/cppjieba third_party/cpp-httplib -# NEMO_SPEECH_GGML_PATCHED defaults ON and silently assumes the ggml-patches -# series is applied. An unpatched checkout builds fine and produces wrong CUDA -# encoder output, so a failure here must stop the build rather than warn. +# ggml now comes from llama.cpp. Upstream's cmake/llama_cpp.cmake applies +# patches/series to a build-directory copy when NEMO_SPEECH_GGML_PATCHED=ON +# and fails configuration if a patch cannot apply. # -# Upstream's own script is the right tool: it applies the series in filename -# order, exits non-zero when a patch does not apply, and decides "already -# applied" by comparing the full-series tree hash rather than a timestamp. That -# makes it safe to run unconditionally, so there is no sentinel file to go stale -# or to wedge the build when deleted. -# -# Both branches keep the order-only clone prerequisite: it is the only thing -# that pulls sources/ in on a WITH_NORM=OFF tree, where the library rule has no -# other prerequisite left. -ifeq ($(GGML_PATCHED),ON) +# Keep the order-only clone prerequisite so patch-ggml still initializes sources +# before the runtime's CMake configuration. patch-ggml: | sources/NeMo-Speech.cpp - cd sources/NeMo-Speech.cpp && bash scripts/apply-ggml-patches.sh -else -patch-ggml: | sources/NeMo-Speech.cpp - @echo "[ggml-patch] skipped: NEMO_SPEECH_GGML_PATCHED=$(GGML_PATCHED), the series is CUDA-only" -endif + @echo "[ggml-patch] managed by upstream CMake (NEMO_SPEECH_GGML_PATCHED=$(GGML_PATCHED))" # The Sparrowhawk/OpenFST text-normalization stack, as a target in its own right # keyed on a file the build script actually produces. @@ -257,23 +242,29 @@ $(ITN_MARKER): | sources/NeMo-Speech.cpp # compiler could ever have emitted it. Only these two lines are affected: gcc 14 # reports exactly two errors over the whole OpenFST include closure, both here. # -# Guarded on both sides so a pinned-version bump cannot silently no-op it: the -# first check fails if neither the broken nor the fixed spelling is present, the -# last fails if the broken one survives. +# The newer OpenFST pin fixes both assignments with fst::WrapUnique. Accept +# that form unchanged; otherwise require both legacy assignments to be known +# before patching, and reject unknown or incomplete forms rather than no-op. patch-itn-headers: @test -f $(ITN_FST_HEADER) || { \ echo "ERROR: $(ITN_FST_HEADER) missing; the ITN prefix is not installed." >&2; \ exit 1; } - @grep -q 'isymbols_ = impl.isymbols_' $(ITN_FST_HEADER) || \ - grep -q 'isymbols_.reset(impl.isymbols_' $(ITN_FST_HEADER) || { \ - echo "ERROR: FstImpl::operator= in $(ITN_FST_HEADER) matches neither the" >&2; \ - echo " known-broken nor the patched form. OpenFST changed upstream;" >&2; \ - echo " re-check whether this patch is still needed before removing it." >&2; \ - exit 1; } - sed -i -E 's|^([[:space:]]*)([io]symbols_) = (impl\.[io]symbols_ \? impl\.[io]symbols_->Copy\(\) : nullptr);$$|\1\2.reset(\3);|' $(ITN_FST_HEADER) - @if grep -q 'symbols_ = impl.[io]symbols_' $(ITN_FST_HEADER); then \ - echo "ERROR: the FstImpl::operator= patch did not apply to $(ITN_FST_HEADER)." >&2; \ - exit 1; \ + @if grep -Fq 'isymbols_ = fst::WrapUnique(impl.isymbols_ ? impl.isymbols_->Copy() : nullptr);' $(ITN_FST_HEADER) && \ + grep -Fq 'osymbols_ = fst::WrapUnique(impl.osymbols_ ? impl.osymbols_->Copy() : nullptr);' $(ITN_FST_HEADER); then \ + echo "OpenFST copy assignment already fixed upstream."; \ + else \ + for symbol in isymbols_ osymbols_; do \ + grep -Fq "$$symbol = impl.$$symbol ? impl.$$symbol->Copy() : nullptr;" $(ITN_FST_HEADER) || \ + grep -Fq "$$symbol.reset(impl.$$symbol ? impl.$$symbol->Copy() : nullptr);" $(ITN_FST_HEADER) || { \ + echo "ERROR: FstImpl::operator= in $(ITN_FST_HEADER) has an unknown $$symbol assignment." >&2; \ + echo " OpenFST changed upstream; re-check whether this patch is still needed." >&2; \ + exit 1; }; \ + done; \ + sed -i -E 's|^([[:space:]]*)([io]symbols_) = (impl\.[io]symbols_ \? impl\.[io]symbols_->Copy\(\) : nullptr);$$|\1\2.reset(\3);|' $(ITN_FST_HEADER) || exit 1; \ + if grep -q 'symbols_ = impl.[io]symbols_' $(ITN_FST_HEADER); then \ + echo "ERROR: the FstImpl::operator= patch did not apply to $(ITN_FST_HEADER)." >&2; \ + exit 1; \ + fi; \ fi itn: $(ITN_MARKER) @@ -329,10 +320,9 @@ stage-libs: sources/NeMo-Speech.cpp/build/bin/libnemo_speech_asr_c.so # no counterpart, its implementation and ABI live in the same object. cp -af sources/NeMo-Speech.cpp/build/bin/libnemo_speech_asr.* . cp -af sources/NeMo-Speech.cpp/build/bin/libnemo_speech_nmt.* . - # nemo_speech_text_normalization is STATIC but links sparrowhawk, fstfar and - # fst PUBLIC, so those become DT_NEEDED on libnemo_speech_asr.so. They live in - # a project-local prefix that nothing else on the system provides, so without - # staging them here the packaged backend cannot dlopen at all. + # ASR and TTS now share nemo_speech_text_normalization as a separate DSO. + # Stage it alongside its Sparrowhawk/OpenFST dependencies from the local + # prefix so the packaged backend can resolve the whole normalization stack. # # Keyed on the prefix existing rather than on WITH_NORM, so this stages what # the tree actually built. A WITH_NORM=ON build cannot reach here without the @@ -340,6 +330,7 @@ stage-libs: sources/NeMo-Speech.cpp/build/bin/libnemo_speech_asr_c.so # library that needs Sparrowhawk somehow arrives unstaged, package.sh's # closure guard fails the build rather than shipping it. @if [ -d "$(ITN_LIB_DIR)" ]; then \ + cp -af sources/NeMo-Speech.cpp/build/bin/libnemo_speech_text_normalization.* . || exit 1; \ echo "cp -af $(ITN_LIB_DIR)/*.so* ."; \ cp -af $(ITN_LIB_DIR)/*.so* .; \ fi diff --git a/backend/go/nemo-speech-cpp/package.sh b/backend/go/nemo-speech-cpp/package.sh index c0cc3f965..7051e0948 100755 --- a/backend/go/nemo-speech-cpp/package.sh +++ b/backend/go/nemo-speech-cpp/package.sh @@ -40,14 +40,13 @@ for lib in libnemo_speech_asr_c libnemo_speech_asr libnemo_speech_tts libnemo_sp fi done -# Text normalization (WITH_NORM=ON, Linux only) links Sparrowhawk and OpenFST -# into libnemo_speech_asr.so. Those live in a project-local prefix that the -# Makefile stages here, so anything staged that is not a nemo_speech object is -# part of that stack. Absent on a WITH_NORM=OFF build, which is why this is a -# glob that tolerates no matches rather than a required list. +# Text normalization (WITH_NORM=ON, Linux only) is a shared NeMo library with +# Sparrowhawk/OpenFST dependencies. Bundle it and the prefix libraries staged +# by the Makefile. Absent on a WITH_NORM=OFF build, so tolerate no matches. shopt -s nullglob for so in "$CURDIR"/*.so "$CURDIR"/*.so.* "$CURDIR"/*.dylib; do case "$(basename "$so")" in + libnemo_speech_text_normalization.*) ;; libnemo_speech_*) continue ;; esac cp -avf "$so" "$CURDIR/package/lib/"