diff --git a/.github/backend-matrix.yml b/.github/backend-matrix.yml index 59826c9cb..fbc830750 100644 --- a/.github/backend-matrix.yml +++ b/.github/backend-matrix.yml @@ -5126,6 +5126,12 @@ includeDarwin: tag-suffix: "-metal-darwin-arm64-sam3-cpp" build-type: "metal" lang: "go" + # privacy-filter (PII/NER) is a C++/ggml backend built by a bespoke darwin + # script (make backends/privacy-filter-darwin); ggml defaults Metal ON on Apple + # so the build is Metal-enabled. lang=go drives runner/toolchain selection only. + - backend: "privacy-filter" + tag-suffix: "-metal-darwin-arm64-privacy-filter" + lang: "go" # LocalVQE has no Metal path; on Apple Silicon it builds CPU-only (GGML_METAL # OFF) but is still a native arm64 image. Uses the darwin/metal build profile. - backend: "localvqe" diff --git a/.github/workflows/backend_build_darwin.yml b/.github/workflows/backend_build_darwin.yml index 749ffd4de..c0ded5b85 100644 --- a/.github/workflows/backend_build_darwin.yml +++ b/.github/workflows/backend_build_darwin.yml @@ -99,6 +99,7 @@ jobs: /opt/homebrew/Cellar/xxhash /opt/homebrew/Cellar/zstd /opt/homebrew/Cellar/nlohmann-json + /opt/homebrew/Cellar/opus key: brew-${{ runner.os }}-${{ runner.arch }}-v1-${{ hashFiles('.github/workflows/backend_build_darwin.yml') }} - name: Dependencies @@ -113,7 +114,12 @@ jobs: # nlohmann-json is header-only and required by the ds4 backend # (dsml_renderer.cpp includes ); on Linux it comes # from the apt-installed nlohmann-json3-dev in the build image. - brew install protobuf grpc make protoc-gen-go protoc-gen-go-grpc libomp llvm ccache blake3 fmt hiredis xxhash zstd nlohmann-json + # opus + pkg-config are required by the opus go backend: its + # Makefile/package.sh call `pkg-config --cflags/--libs opus` to build + # libopusshim.dylib and to locate libopus.dylib for bundling. brew's + # pkg-config defaults its search path to the Homebrew prefix so the + # opus.pc is found. + brew install protobuf grpc make protoc-gen-go protoc-gen-go-grpc libomp llvm ccache blake3 fmt hiredis xxhash zstd nlohmann-json opus pkg-config # Force-reinstall ccache so brew re-validates its full runtime-dep # closure on every run. This is the durable fix: when the upstream # ccache formula gains a new transitive dep (as it has multiple times @@ -132,7 +138,7 @@ jobs: # and decides "already installed" without re-linking, so on a cache- # hit run the formulas aren't on PATH. Force-link them; --overwrite # tolerates pre-existing symlinks from earlier installs. - brew link --overwrite protobuf grpc make protoc-gen-go protoc-gen-go-grpc libomp llvm ccache blake3 fmt hiredis xxhash zstd nlohmann-json 2>/dev/null || true + brew link --overwrite protobuf grpc make protoc-gen-go protoc-gen-go-grpc libomp llvm ccache blake3 fmt hiredis xxhash zstd nlohmann-json opus pkg-config 2>/dev/null || true - name: Save Homebrew cache if: github.event_name != 'pull_request' && steps.brew-cache.outputs.cache-hit != 'true' @@ -153,6 +159,7 @@ jobs: /opt/homebrew/Cellar/xxhash /opt/homebrew/Cellar/zstd /opt/homebrew/Cellar/nlohmann-json + /opt/homebrew/Cellar/opus key: brew-${{ runner.os }}-${{ runner.arch }}-v1-${{ hashFiles('.github/workflows/backend_build_darwin.yml') }} # ---- ccache for llama.cpp CMake builds ---- @@ -228,8 +235,17 @@ jobs: run: | make backends/ds4-darwin + # privacy-filter is a C++/ggml backend like ds4 - a single grpc-server with + # otool dylib bundling - so it gets its own bespoke darwin script rather than + # the generic build-darwin-go-backend path. + - name: Build privacy-filter backend (Darwin Metal) + if: inputs.backend == 'privacy-filter' + run: | + make protogen-go + make backends/privacy-filter-darwin + - name: Build ${{ inputs.backend }}-darwin - if: inputs.backend != 'llama-cpp' && inputs.backend != 'ds4' + if: inputs.backend != 'llama-cpp' && inputs.backend != 'ds4' && inputs.backend != 'privacy-filter' run: | make protogen-go BACKEND=${{ inputs.backend }} BUILD_TYPE=${{ inputs.build-type }} USE_PIP=${{ inputs.use-pip }} make build-darwin-${{ inputs.lang }}-backend diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 614c1de3e..b1b585c4b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -24,6 +24,11 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MACOS_SIGN_P12: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} launcher-build-darwin: runs-on: macos-latest steps: @@ -35,9 +40,19 @@ jobs: uses: actions/setup-go@v5 with: go-version: 1.23 - - name: Build launcher for macOS ARM64 - run: | - make build-launcher-darwin + - name: Import signing certificate + env: + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.MACOS_CI_KEYCHAIN_PWD }} + run: bash contrib/macos/sign-and-notarize.sh import-cert + - name: Build, sign and notarize the DMG + env: + MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }} + MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }} + MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }} + MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} + run: make release-launcher-darwin - name: Upload DMG to Release uses: softprops/action-gh-release@v3 with: diff --git a/.gitignore b/.gitignore index bb5d7ef66..567843acb 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,6 @@ core/http/react-ui/test-results/ # SDD / brainstorm scratch (agent-driven development) .superpowers/ + +# Local Apple signing material (never commit) +.certs/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 71c9c96e4..88d6e9ecd 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -9,7 +9,8 @@ source: enabled: true name_template: '{{ .ProjectName }}-{{ .Tag }}-source' builds: - - main: ./cmd/local-ai + - id: local-ai + main: ./cmd/local-ai env: - CGO_ENABLED=0 ldflags: @@ -35,3 +36,19 @@ snapshot: version_template: "{{ .Tag }}-next" changelog: use: github-native +# Sign + notarize the macOS server binary via the quill backend (runs on Linux, +# no macOS runner needed). Disabled automatically when MACOS_SIGN_P12 is unset +# (forks / PRs), so those builds stay unsigned and green. +notarize: + macos: + - enabled: '{{ isEnvSet "MACOS_SIGN_P12" }}' + ids: + - local-ai + sign: + certificate: "{{.Env.MACOS_SIGN_P12}}" + password: "{{.Env.MACOS_SIGN_PASSWORD}}" + notarize: + issuer_id: "{{.Env.MACOS_NOTARY_ISSUER_ID}}" + key_id: "{{.Env.MACOS_NOTARY_KEY_ID}}" + key: "{{.Env.MACOS_NOTARY_KEY}}" + wait: true diff --git a/Makefile b/Makefile index 7b97be127..81571bf0d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Disable parallel execution for backend builds -.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/crispasr backends/parakeet-cpp backends/faster-whisper backends/silero-vad backends/local-store backends/huggingface backends/rfdetr backends/rfdetr-cpp backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/omnivoice-cpp backends/vibevoice-cpp backends/localvqe backends/tinygrad backends/sherpa-onnx backends/ds4 backends/ds4-darwin backends/liquid-audio backends/supertonic backends/depth-anything-cpp backends/privacy-filter backends/llama-cpp-localai-paged +.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/crispasr backends/parakeet-cpp backends/faster-whisper backends/silero-vad backends/local-store backends/huggingface backends/rfdetr backends/rfdetr-cpp backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/omnivoice-cpp backends/vibevoice-cpp backends/localvqe backends/tinygrad backends/sherpa-onnx backends/ds4 backends/ds4-darwin backends/liquid-audio backends/supertonic backends/depth-anything-cpp backends/privacy-filter backends/privacy-filter-darwin backends/llama-cpp-localai-paged GOCMD=go GOTEST=$(GOCMD) test @@ -1138,6 +1138,10 @@ backends/ds4-darwin: build bash ./scripts/build/ds4-darwin.sh ./local-ai backends install "ocifile://$(abspath ./backend-images/ds4.tar)" +backends/privacy-filter-darwin: build + bash ./scripts/build/privacy-filter-darwin.sh + ./local-ai backends install "ocifile://$(abspath ./backend-images/privacy-filter.tar)" + build-darwin-python-backend: build bash ./scripts/build/python-darwin.sh @@ -1463,13 +1467,32 @@ docs: docs/static/gallery.html ######################################################## ## fyne cross-platform build -build-launcher-darwin: build-launcher - go run github.com/tiagomelo/macos-dmg-creator/cmd/createdmg@latest \ - --appName "LocalAI" \ - --appBinaryPath "$(LAUNCHER_BINARY_NAME)" \ - --bundleIdentifier "com.localai.launcher" \ - --iconPath "core/http/static/logo.png" \ - --outputDir "dist/" +# Build LocalAI.app from the launcher via fyne (metadata read from cmd/launcher/FyneApp.toml). +# Signing happens via contrib/macos/sign-and-notarize.sh, which is a no-op when the signing +# secrets are unset, so unsigned local/fork builds keep working. +build-launcher-darwin: + rm -rf dist/LocalAI.app cmd/launcher/LocalAI.app + mkdir -p dist + cd cmd/launcher && go run fyne.io/tools/cmd/fyne@latest package -os darwin -icon ../../core/http/static/logo.png --executable $(LAUNCHER_BINARY_NAME) + mv cmd/launcher/LocalAI.app dist/LocalAI.app + bash contrib/macos/sign-and-notarize.sh sign dist/LocalAI.app + +# Wrap the (signed) app into a drag-to-Applications DMG via hdiutil, then sign the DMG. +dmg-launcher-darwin: build-launcher-darwin + rm -rf dist/dmg dist/LocalAI.dmg + mkdir -p dist/dmg + cp -R dist/LocalAI.app dist/dmg/LocalAI.app + ln -s /Applications dist/dmg/Applications + hdiutil create -volname "LocalAI" -srcfolder dist/dmg -ov -format UDZO dist/LocalAI.dmg + bash contrib/macos/sign-and-notarize.sh sign dist/LocalAI.dmg + +# Submit the DMG to Apple notarization and staple the ticket (no-op without notary secrets). +notarize-launcher-darwin: dmg-launcher-darwin + bash contrib/macos/sign-and-notarize.sh notarize dist/LocalAI.dmg + +# Single entrypoint for CI: build -> sign app -> dmg -> sign dmg -> notarize -> staple. +release-launcher-darwin: notarize-launcher-darwin + @echo "dist/LocalAI.dmg is ready" build-launcher-linux: - cd cmd/launcher && go run fyne.io/tools/cmd/fyne@latest package -os linux -icon ../../core/http/static/logo.png --executable $(LAUNCHER_BINARY_NAME)-linux && mv launcher.tar.xz ../../$(LAUNCHER_BINARY_NAME)-linux.tar.xz + cd cmd/launcher && go run fyne.io/tools/cmd/fyne@latest package -os linux -icon ../../core/http/static/logo.png --executable $(LAUNCHER_BINARY_NAME)-linux && mv LocalAI.tar.xz ../../$(LAUNCHER_BINARY_NAME)-linux.tar.xz diff --git a/backend/cpp/ik-llama-cpp/Makefile b/backend/cpp/ik-llama-cpp/Makefile index 860606253..d76a07854 100644 --- a/backend/cpp/ik-llama-cpp/Makefile +++ b/backend/cpp/ik-llama-cpp/Makefile @@ -1,5 +1,5 @@ -IK_LLAMA_VERSION?=d5507e33ae7ee2b7b41475f08044d3bde3b839ee +IK_LLAMA_VERSION?=b84902d2ad27c34f989f23947200c4b91b1568fd LLAMA_REPO?=https://github.com/ikawrakow/ik_llama.cpp CMAKE_ARGS?= diff --git a/backend/cpp/ik-llama-cpp/run.sh b/backend/cpp/ik-llama-cpp/run.sh index 1c4ee2a69..d10d19367 100644 --- a/backend/cpp/ik-llama-cpp/run.sh +++ b/backend/cpp/ik-llama-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -13,28 +13,28 @@ grep -e "flags" /proc/cpuinfo | head -1 # ik_llama.cpp requires AVX2 — default to avx2 binary BINARY=ik-llama-cpp-avx2 -if [ -e $CURDIR/ik-llama-cpp-fallback ] && ! grep -q -e "\savx2\s" /proc/cpuinfo ; then +if [ -e "$CURDIR"/ik-llama-cpp-fallback ] && ! grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 NOT found, using fallback" BINARY=ik-llama-cpp-fallback fi # Extend ld library path with the dir where this script is located/lib if [ "$(uname)" == "Darwin" ]; then - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH - #export DYLD_FALLBACK_LIBRARY_PATH=$CURDIR/lib:$DYLD_FALLBACK_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH + #export DYLD_FALLBACK_LIBRARY_PATH="$CURDIR"/lib:$DYLD_FALLBACK_LIBRARY_PATH else - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using binary: $BINARY" - exec $CURDIR/lib/ld.so $CURDIR/$BINARY "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/$BINARY "$@" fi echo "Using binary: $BINARY" -exec $CURDIR/$BINARY "$@" +exec "$CURDIR"/$BINARY "$@" # We should never reach this point, however just in case we do, run fallback -exec $CURDIR/ik-llama-cpp-fallback "$@" +exec "$CURDIR"/ik-llama-cpp-fallback "$@" diff --git a/backend/cpp/llama-cpp/Makefile b/backend/cpp/llama-cpp/Makefile index 25b1ed24d..f4d3f3765 100644 --- a/backend/cpp/llama-cpp/Makefile +++ b/backend/cpp/llama-cpp/Makefile @@ -1,5 +1,5 @@ -LLAMA_VERSION?=8be759e6f70d629638a7eb70db3824cbdcea370b +LLAMA_VERSION?=9d5d882d8cd0f0a9283d87ed5e6fe3ee0d925fb1 LLAMA_REPO?=https://github.com/ggerganov/llama.cpp # LLAMA_PAGED controls whether the vendored paged-attention patch series # (patches/paged/) is applied on top of the pinned llama.cpp. Default on; set diff --git a/backend/cpp/llama-cpp/run.sh b/backend/cpp/llama-cpp/run.sh index db8498f4b..09a13b0fe 100755 --- a/backend/cpp/llama-cpp/run.sh +++ b/backend/cpp/llama-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -16,37 +16,37 @@ BINARY=llama-cpp-fallback # CPU_ALL_VARIANTS: ggml's backend registry dlopens the best libggml-cpu-*.so for this # host, so no shell-side AVX probing. GPU images (cublas/sycl/vulkan/hipblas) ship only # llama-cpp-fallback (the accelerator does the compute), so fall back to it when absent. -if [ -e $CURDIR/llama-cpp-cpu-all ]; then +if [ -e "$CURDIR"/llama-cpp-cpu-all ]; then BINARY=llama-cpp-cpu-all fi if [ -n "$LLAMACPP_GRPC_SERVERS" ]; then - if [ -e $CURDIR/llama-cpp-grpc ]; then + if [ -e "$CURDIR"/llama-cpp-grpc ]; then BINARY=llama-cpp-grpc fi fi # Extend ld library path with the dir where this script is located/lib if [ "$(uname)" == "Darwin" ]; then - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH - #export DYLD_FALLBACK_LIBRARY_PATH=$CURDIR/lib:$DYLD_FALLBACK_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH + #export DYLD_FALLBACK_LIBRARY_PATH="$CURDIR"/lib:$DYLD_FALLBACK_LIBRARY_PATH else - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH # Tell rocBLAS where to find TensileLibrary data (GPU kernel tuning files) if [ -d "$CURDIR/lib/rocblas/library" ]; then - export ROCBLAS_TENSILE_LIBPATH=$CURDIR/lib/rocblas/library + export ROCBLAS_TENSILE_LIBPATH="$CURDIR"/lib/rocblas/library fi fi # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using binary: $BINARY" - exec $CURDIR/lib/ld.so $CURDIR/$BINARY "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/$BINARY "$@" fi echo "Using binary: $BINARY" -exec $CURDIR/$BINARY "$@" +exec "$CURDIR"/$BINARY "$@" # We should never reach this point, however just in case we do, run fallback -exec $CURDIR/llama-cpp-fallback "$@" \ No newline at end of file +exec "$CURDIR"/llama-cpp-fallback "$@" \ No newline at end of file diff --git a/backend/cpp/privacy-filter/CMakeLists.txt b/backend/cpp/privacy-filter/CMakeLists.txt index b8580d1d4..f9ee78328 100644 --- a/backend/cpp/privacy-filter/CMakeLists.txt +++ b/backend/cpp/privacy-filter/CMakeLists.txt @@ -51,6 +51,14 @@ add_library(hw_grpc_proto STATIC ${HW_GRPC_SRCS} ${HW_GRPC_HDRS} ${HW_PROTO_SRCS} ${HW_PROTO_HDRS}) target_include_directories(hw_grpc_proto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +# The generated proto/grpc sources include protobuf and grpc++ headers, so this +# library must see their include dirs. Linking the imported targets propagates +# them. On Linux the apt headers live in /usr/include (default search path) so +# this was a no-op; on macOS the Homebrew headers are under /opt/homebrew and +# would otherwise be missed (runtime_version.h not found). +target_link_libraries(hw_grpc_proto PUBLIC + protobuf::libprotobuf + gRPC::grpc++) # Build only the pf static lib (+ ggml) from the engine tree — no CLI/bench/tests. # PF_VULKAN is honored when passed on the cmake command line (it lands in the diff --git a/backend/cpp/privacy-filter/run.sh b/backend/cpp/privacy-filter/run.sh index 489b64580..0f4f22270 100755 --- a/backend/cpp/privacy-filter/run.sh +++ b/backend/cpp/privacy-filter/run.sh @@ -2,7 +2,13 @@ # Entry point for the privacy-filter backend image / BACKEND_BINARY mode. set -e CURDIR=$(dirname "$(realpath "$0")") -export LD_LIBRARY_PATH="$CURDIR/lib:$LD_LIBRARY_PATH" +# macOS has no bundled ld.so; the darwin package ships only dylibs under lib/, +# resolved via DYLD_LIBRARY_PATH (the ld.so branch below is skipped there). +if [ "$(uname)" = "Darwin" ]; then + export DYLD_LIBRARY_PATH="$CURDIR/lib:$DYLD_LIBRARY_PATH" +else + export LD_LIBRARY_PATH="$CURDIR/lib:$LD_LIBRARY_PATH" +fi if [ -f "$CURDIR/lib/ld.so" ]; then exec "$CURDIR/lib/ld.so" "$CURDIR/grpc-server" "$@" fi diff --git a/backend/cpp/turboquant/run.sh b/backend/cpp/turboquant/run.sh index cd41a0f7f..33864385d 100755 --- a/backend/cpp/turboquant/run.sh +++ b/backend/cpp/turboquant/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,36 +15,36 @@ BINARY=turboquant-fallback # x86/arm64 ship a single turboquant-cpu-all built with ggml CPU_ALL_VARIANTS: ggml's # backend registry dlopens the best libggml-cpu-*.so for this host, so no shell-side # probing. ROCm ships only turboquant-fallback, so fall back to it when cpu-all is absent. -if [ -e $CURDIR/turboquant-cpu-all ]; then +if [ -e "$CURDIR"/turboquant-cpu-all ]; then BINARY=turboquant-cpu-all fi if [ -n "$LLAMACPP_GRPC_SERVERS" ]; then - if [ -e $CURDIR/turboquant-grpc ]; then + if [ -e "$CURDIR"/turboquant-grpc ]; then BINARY=turboquant-grpc fi fi # Extend ld library path with the dir where this script is located/lib if [ "$(uname)" == "Darwin" ]; then - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH # Tell rocBLAS where to find TensileLibrary data (GPU kernel tuning files) if [ -d "$CURDIR/lib/rocblas/library" ]; then - export ROCBLAS_TENSILE_LIBPATH=$CURDIR/lib/rocblas/library + export ROCBLAS_TENSILE_LIBPATH="$CURDIR"/lib/rocblas/library fi fi # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using binary: $BINARY" - exec $CURDIR/lib/ld.so $CURDIR/$BINARY "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/$BINARY "$@" fi echo "Using binary: $BINARY" -exec $CURDIR/$BINARY "$@" +exec "$CURDIR"/$BINARY "$@" # We should never reach this point, however just in case we do, run fallback -exec $CURDIR/turboquant-fallback "$@" +exec "$CURDIR"/turboquant-fallback "$@" diff --git a/backend/go/acestep-cpp/run.sh b/backend/go/acestep-cpp/run.sh index bcdfbc09e..c026e2051 100755 --- a/backend/go/acestep-cpp/run.sh +++ b/backend/go/acestep-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -21,20 +21,20 @@ if [ "$(uname)" = "Darwin" ]; then if [ ! -e "$LIBRARY" ]; then LIBRARY="$CURDIR/libgoacestepcpp-fallback.so" fi - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgoacestepcpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgoacestepcpp-avx.so ]; then + if [ -e "$CURDIR"/libgoacestepcpp-avx.so ]; then LIBRARY="$CURDIR/libgoacestepcpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgoacestepcpp-avx2.so ]; then + if [ -e "$CURDIR"/libgoacestepcpp-avx2.so ]; then LIBRARY="$CURDIR/libgoacestepcpp-avx2.so" fi fi @@ -42,22 +42,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgoacestepcpp-avx512.so ]; then + if [ -e "$CURDIR"/libgoacestepcpp-avx512.so ]; then LIBRARY="$CURDIR/libgoacestepcpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export ACESTEP_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/acestep-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/acestep-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/acestep-cpp "$@" +exec "$CURDIR"/acestep-cpp "$@" diff --git a/backend/go/ced/run.sh b/backend/go/ced/run.sh index 1f95f748f..5375af4ef 100755 --- a/backend/go/ced/run.sh +++ b/backend/go/ced/run.sh @@ -4,10 +4,10 @@ set -e CURDIR=$(dirname "$(realpath "$0")") if [ "$(uname)" = "Darwin" ]; then - export DYLD_LIBRARY_PATH="$CURDIR/lib:$CURDIR:${DYLD_LIBRARY_PATH:-}" + export DYLD_LIBRARY_PATH="$CURDIR/lib:"$CURDIR":${DYLD_LIBRARY_PATH:-}" export CED_LIBRARY="$CURDIR/lib/libced.dylib" else - export LD_LIBRARY_PATH="$CURDIR/lib:$CURDIR:${LD_LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="$CURDIR/lib:"$CURDIR":${LD_LIBRARY_PATH:-}" fi # If a self-contained ld.so was packaged, route through it so the packaged diff --git a/backend/go/cloud-proxy/run.sh b/backend/go/cloud-proxy/run.sh index c533c093a..efef811de 100755 --- a/backend/go/cloud-proxy/run.sh +++ b/backend/go/cloud-proxy/run.sh @@ -1,6 +1,6 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -exec $CURDIR/cloud-proxy "$@" +exec "$CURDIR"/cloud-proxy "$@" diff --git a/backend/go/crispasr/Makefile b/backend/go/crispasr/Makefile index 1b32240e3..d6921b15a 100644 --- a/backend/go/crispasr/Makefile +++ b/backend/go/crispasr/Makefile @@ -8,7 +8,7 @@ JOBS?=$(shell nproc --ignore=1) # CrispASR version (release tag) CRISPASR_REPO?=https://github.com/CrispStrobe/CrispASR -CRISPASR_VERSION?=96b2a6ee31d30389fed8a7ef1a54239b75231ddc +CRISPASR_VERSION?=8f1218141b792b8868861c1af17ba1e361b05dc0 SO_TARGET?=libgocrispasr.so CMAKE_ARGS+=-DBUILD_SHARED_LIBS=OFF diff --git a/backend/go/crispasr/run.sh b/backend/go/crispasr/run.sh index 6d3c4b216..078e0659f 100755 --- a/backend/go/crispasr/run.sh +++ b/backend/go/crispasr/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgocrispasr-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgocrispasr-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgocrispasr-avx.so ]; then + if [ -e "$CURDIR"/libgocrispasr-avx.so ]; then LIBRARY="$CURDIR/libgocrispasr-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgocrispasr-avx2.so ]; then + if [ -e "$CURDIR"/libgocrispasr-avx2.so ]; then LIBRARY="$CURDIR/libgocrispasr-avx2.so" fi fi @@ -36,12 +36,12 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgocrispasr-avx512.so ]; then + if [ -e "$CURDIR"/libgocrispasr-avx512.so ]; then LIBRARY="$CURDIR/libgocrispasr-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export CRISPASR_LIBRARY=$LIBRARY @@ -49,14 +49,14 @@ export CRISPASR_LIBRARY=$LIBRARY # Point piper's espeak-ng phonemizer at the bundled voice data. The variable # names the directory CONTAINING espeak-ng-data (package.sh drops it next to # this script). Harmless when espeak-ng wasn't bundled. -export CRISPASR_ESPEAK_DATA_PATH=$CURDIR +export CRISPASR_ESPEAK_DATA_PATH="$CURDIR" # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/crispasr "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/crispasr "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/crispasr "$@" +exec "$CURDIR"/crispasr "$@" diff --git a/backend/go/depth-anything-cpp/run.sh b/backend/go/depth-anything-cpp/run.sh index cbff6b0b5..8729389b4 100755 --- a/backend/go/depth-anything-cpp/run.sh +++ b/backend/go/depth-anything-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libdepthanythingcpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libdepthanythingcpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libdepthanythingcpp-avx.so ]; then + if [ -e "$CURDIR"/libdepthanythingcpp-avx.so ]; then LIBRARY="$CURDIR/libdepthanythingcpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libdepthanythingcpp-avx2.so ]; then + if [ -e "$CURDIR"/libdepthanythingcpp-avx2.so ]; then LIBRARY="$CURDIR/libdepthanythingcpp-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libdepthanythingcpp-avx512.so ]; then + if [ -e "$CURDIR"/libdepthanythingcpp-avx512.so ]; then LIBRARY="$CURDIR/libdepthanythingcpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export DEPTHANYTHING_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/depth-anything-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/depth-anything-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/depth-anything-cpp "$@" +exec "$CURDIR"/depth-anything-cpp "$@" diff --git a/backend/go/local-store/run.sh b/backend/go/local-store/run.sh index 479f3b486..ba0173401 100755 --- a/backend/go/local-store/run.sh +++ b/backend/go/local-store/run.sh @@ -1,6 +1,6 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -exec $CURDIR/local-store "$@" \ No newline at end of file +exec "$CURDIR"/local-store "$@" \ No newline at end of file diff --git a/backend/go/localvqe/run.sh b/backend/go/localvqe/run.sh index d14d427c4..542885572 100755 --- a/backend/go/localvqe/run.sh +++ b/backend/go/localvqe/run.sh @@ -1,34 +1,34 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") # LocalVQE's runtime CPU-variant loader (ggml_backend_load_all) searches # get_executable_path() and current_path() — the second one is what saves us # when /proc/self/exe resolves to lib/ld.so under the bundled-loader path. -# So we cd into $CURDIR (where all the libggml-cpu-*.so files live) before +# So we cd into "$CURDIR" (where all the libggml-cpu-*.so files live) before # exec'ing the binary. cd "$CURDIR" if [ "$(uname)" = "Darwin" ]; then # macOS: LocalVQE is built as a SHARED library, so dyld needs the .dylib + # DYLD_LIBRARY_PATH. Prefer .dylib and fall back to .so just in case. - export DYLD_LIBRARY_PATH=$CURDIR:$CURDIR/lib:$DYLD_LIBRARY_PATH - LOCALVQE_LIBRARY=$CURDIR/liblocalvqe.dylib + export DYLD_LIBRARY_PATH="$CURDIR":"$CURDIR"/lib:$DYLD_LIBRARY_PATH + LOCALVQE_LIBRARY="$CURDIR"/liblocalvqe.dylib if [ ! -e "$LOCALVQE_LIBRARY" ]; then - LOCALVQE_LIBRARY=$CURDIR/liblocalvqe.so + LOCALVQE_LIBRARY="$CURDIR"/liblocalvqe.so fi export LOCALVQE_LIBRARY else - export LD_LIBRARY_PATH=$CURDIR:$CURDIR/lib:$LD_LIBRARY_PATH - export LOCALVQE_LIBRARY=$CURDIR/liblocalvqe.so + export LD_LIBRARY_PATH="$CURDIR":"$CURDIR"/lib:$LD_LIBRARY_PATH + export LOCALVQE_LIBRARY="$CURDIR"/liblocalvqe.so fi -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LOCALVQE_LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/localvqe "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/localvqe "$@" fi echo "Using library: $LOCALVQE_LIBRARY" -exec $CURDIR/localvqe "$@" +exec "$CURDIR"/localvqe "$@" diff --git a/backend/go/locate-anything-cpp/run.sh b/backend/go/locate-anything-cpp/run.sh index 4eebb3c63..3a6ccf259 100755 --- a/backend/go/locate-anything-cpp/run.sh +++ b/backend/go/locate-anything-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/liblocateanythingcpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/liblocateanythingcpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/liblocateanythingcpp-avx.so ]; then + if [ -e "$CURDIR"/liblocateanythingcpp-avx.so ]; then LIBRARY="$CURDIR/liblocateanythingcpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/liblocateanythingcpp-avx2.so ]; then + if [ -e "$CURDIR"/liblocateanythingcpp-avx2.so ]; then LIBRARY="$CURDIR/liblocateanythingcpp-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/liblocateanythingcpp-avx512.so ]; then + if [ -e "$CURDIR"/liblocateanythingcpp-avx512.so ]; then LIBRARY="$CURDIR/liblocateanythingcpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export LOCATEANYTHING_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/locate-anything-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/locate-anything-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/locate-anything-cpp "$@" +exec "$CURDIR"/locate-anything-cpp "$@" diff --git a/backend/go/omnivoice-cpp/run.sh b/backend/go/omnivoice-cpp/run.sh index 81ea2b719..706f80dd6 100755 --- a/backend/go/omnivoice-cpp/run.sh +++ b/backend/go/omnivoice-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgomnivoicecpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgomnivoicecpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgomnivoicecpp-avx.so ]; then + if [ -e "$CURDIR"/libgomnivoicecpp-avx.so ]; then LIBRARY="$CURDIR/libgomnivoicecpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgomnivoicecpp-avx2.so ]; then + if [ -e "$CURDIR"/libgomnivoicecpp-avx2.so ]; then LIBRARY="$CURDIR/libgomnivoicecpp-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgomnivoicecpp-avx512.so ]; then + if [ -e "$CURDIR"/libgomnivoicecpp-avx512.so ]; then LIBRARY="$CURDIR/libgomnivoicecpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export OMNIVOICE_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/omnivoice-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/omnivoice-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/omnivoice-cpp "$@" +exec "$CURDIR"/omnivoice-cpp "$@" diff --git a/backend/go/opus/Makefile b/backend/go/opus/Makefile index 028b16bc5..49083b3dc 100644 --- a/backend/go/opus/Makefile +++ b/backend/go/opus/Makefile @@ -1,13 +1,30 @@ GOCMD?=go GO_TAGS?= +# The opus shim is a small C wrapper around libopus' variadic +# opus_encoder_ctl (see csrc/opus_shim.c). It is built as a shared library +# and dlopen'd at runtime by the Go backend (codec.go). The extension is +# OS-specific: Linux uses .so, macOS uses .dylib. OS is exported by the root +# Makefile (`export OS := $(shell uname -s)`). +SHIM_EXT=so + OPUS_CFLAGS := $(shell pkg-config --cflags opus) OPUS_LIBS := $(shell pkg-config --libs opus) +SHIM_LDFLAGS := $(OPUS_LIBS) -libopusshim.so: csrc/opus_shim.c - $(CC) -shared -fPIC -o $@ $< $(OPUS_CFLAGS) $(OPUS_LIBS) +ifeq ($(OS),Darwin) + SHIM_EXT=dylib + # Resolve libopus symbols lazily from the already globally-loaded + # libopus (codec.go dlopens it RTLD_GLOBAL before the shim) rather than + # recording an absolute Homebrew path in the dylib. This keeps the + # packaged shim relocatable on machines that have no Homebrew. + SHIM_LDFLAGS := -undefined dynamic_lookup +endif -opus: libopusshim.so +libopusshim.$(SHIM_EXT): csrc/opus_shim.c + $(CC) -shared -fPIC -o $@ $< $(OPUS_CFLAGS) $(SHIM_LDFLAGS) + +opus: libopusshim.$(SHIM_EXT) $(GOCMD) build -tags "$(GO_TAGS)" -o opus ./ package: opus @@ -16,4 +33,7 @@ package: opus build: package clean: - rm -f opus libopusshim.so + rm -f opus libopusshim.$(SHIM_EXT) + rm -rf package + +.PHONY: build package clean diff --git a/backend/go/opus/package.sh b/backend/go/opus/package.sh index a55834f3e..1e1aaeabf 100644 --- a/backend/go/opus/package.sh +++ b/backend/go/opus/package.sh @@ -8,13 +8,23 @@ mkdir -p $CURDIR/package/lib cp -avf $CURDIR/opus $CURDIR/package/ cp -avf $CURDIR/run.sh $CURDIR/package/ -# Copy the opus shim library -cp -avf $CURDIR/libopusshim.so $CURDIR/package/lib/ +# The shim extension is OS-specific (.so on Linux, .dylib on macOS). +SHIM_EXT=so +if [ "$(uname)" = "Darwin" ]; then + SHIM_EXT=dylib +fi -# Copy system libopus +# Copy the opus shim library +cp -avf $CURDIR/libopusshim.$SHIM_EXT $CURDIR/package/lib/ + +# Copy system libopus so the backend is self-contained: the runtime base +# image has neither libopus-dev (Linux) nor Homebrew (macOS), so codec.go's +# dlopen would otherwise fail. Both name patterns are attempted; only the +# host's matching one exists. if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists opus; then LIBOPUS_DIR=$(pkg-config --variable=libdir opus) - cp -avfL $LIBOPUS_DIR/libopus.so* $CURDIR/package/lib/ 2>/dev/null || true + cp -avf $LIBOPUS_DIR/libopus.so* $CURDIR/package/lib/ 2>/dev/null || true + cp -avf $LIBOPUS_DIR/libopus*.dylib $CURDIR/package/lib/ 2>/dev/null || true fi # Detect architecture and copy appropriate libraries @@ -38,6 +48,8 @@ elif [ -f "/lib/ld-linux-aarch64.so.1" ]; then cp -arfLv /lib/aarch64-linux-gnu/libdl.so.2 $CURDIR/package/lib/libdl.so.2 cp -arfLv /lib/aarch64-linux-gnu/librt.so.1 $CURDIR/package/lib/librt.so.1 cp -arfLv /lib/aarch64-linux-gnu/libpthread.so.0 $CURDIR/package/lib/libpthread.so.0 +elif [ "$(uname -s)" = "Darwin" ]; then + echo "Detected Darwin — system libraries linked dynamically, no bundled loader needed" else echo "Warning: Could not detect architecture for system library bundling" fi diff --git a/backend/go/opus/run.sh b/backend/go/opus/run.sh index d926c57d0..d2773fd28 100644 --- a/backend/go/opus/run.sh +++ b/backend/go/opus/run.sh @@ -1,15 +1,20 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH -export OPUS_SHIM_LIBRARY=$CURDIR/lib/libopusshim.so - -# If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then - echo "Using lib/ld.so" - exec $CURDIR/lib/ld.so $CURDIR/opus "$@" +if [ "$(uname)" = "Darwin" ]; then + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH + export OPUS_SHIM_LIBRARY="$CURDIR"/lib/libopusshim.dylib +else + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH + export OPUS_SHIM_LIBRARY="$CURDIR"/lib/libopusshim.so fi -exec $CURDIR/opus "$@" +# If there is a lib/ld.so, use it +if [ -f "$CURDIR"/lib/ld.so ]; then + echo "Using lib/ld.so" + exec "$CURDIR"/lib/ld.so "$CURDIR"/opus "$@" +fi + +exec "$CURDIR"/opus "$@" diff --git a/backend/go/parakeet-cpp/run.sh b/backend/go/parakeet-cpp/run.sh index be859f381..5f99c5d24 100755 --- a/backend/go/parakeet-cpp/run.sh +++ b/backend/go/parakeet-cpp/run.sh @@ -4,10 +4,10 @@ set -e CURDIR=$(dirname "$(realpath "$0")") if [ "$(uname)" = "Darwin" ]; then - export DYLD_LIBRARY_PATH="$CURDIR/lib:$CURDIR:${DYLD_LIBRARY_PATH:-}" + export DYLD_LIBRARY_PATH="$CURDIR/lib:"$CURDIR":${DYLD_LIBRARY_PATH:-}" export PARAKEET_LIBRARY="$CURDIR/lib/libparakeet.dylib" else - export LD_LIBRARY_PATH="$CURDIR/lib:$CURDIR:${LD_LIBRARY_PATH:-}" + export LD_LIBRARY_PATH="$CURDIR/lib:"$CURDIR":${LD_LIBRARY_PATH:-}" export PARAKEET_LIBRARY="$CURDIR/lib/libparakeet.so" fi diff --git a/backend/go/piper/run.sh b/backend/go/piper/run.sh index fe120ea88..6e04021e3 100755 --- a/backend/go/piper/run.sh +++ b/backend/go/piper/run.sh @@ -1,15 +1,15 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -export ESPEAK_NG_DATA=$CURDIR/espeak-ng-data -export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH +export ESPEAK_NG_DATA="$CURDIR"/espeak-ng-data +export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" - exec $CURDIR/lib/ld.so $CURDIR/piper "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/piper "$@" fi -exec $CURDIR/piper "$@" \ No newline at end of file +exec "$CURDIR"/piper "$@" \ No newline at end of file diff --git a/backend/go/qwen3-tts-cpp/run.sh b/backend/go/qwen3-tts-cpp/run.sh index 638cf9661..2b8934d1b 100755 --- a/backend/go/qwen3-tts-cpp/run.sh +++ b/backend/go/qwen3-tts-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgoqwen3ttscpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgoqwen3ttscpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgoqwen3ttscpp-avx.so ]; then + if [ -e "$CURDIR"/libgoqwen3ttscpp-avx.so ]; then LIBRARY="$CURDIR/libgoqwen3ttscpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgoqwen3ttscpp-avx2.so ]; then + if [ -e "$CURDIR"/libgoqwen3ttscpp-avx2.so ]; then LIBRARY="$CURDIR/libgoqwen3ttscpp-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgoqwen3ttscpp-avx512.so ]; then + if [ -e "$CURDIR"/libgoqwen3ttscpp-avx512.so ]; then LIBRARY="$CURDIR/libgoqwen3ttscpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export QWEN3TTS_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/qwen3-tts-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/qwen3-tts-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/qwen3-tts-cpp "$@" +exec "$CURDIR"/qwen3-tts-cpp "$@" diff --git a/backend/go/rfdetr-cpp/run.sh b/backend/go/rfdetr-cpp/run.sh index ffbd604dd..0e7775cb7 100755 --- a/backend/go/rfdetr-cpp/run.sh +++ b/backend/go/rfdetr-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/librfdetrcpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/librfdetrcpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/librfdetrcpp-avx.so ]; then + if [ -e "$CURDIR"/librfdetrcpp-avx.so ]; then LIBRARY="$CURDIR/librfdetrcpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/librfdetrcpp-avx2.so ]; then + if [ -e "$CURDIR"/librfdetrcpp-avx2.so ]; then LIBRARY="$CURDIR/librfdetrcpp-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/librfdetrcpp-avx512.so ]; then + if [ -e "$CURDIR"/librfdetrcpp-avx512.so ]; then LIBRARY="$CURDIR/librfdetrcpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export RFDETR_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/rfdetr-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/rfdetr-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/rfdetr-cpp "$@" +exec "$CURDIR"/rfdetr-cpp "$@" diff --git a/backend/go/sam3-cpp/run.sh b/backend/go/sam3-cpp/run.sh index 7bff52df6..7794facde 100755 --- a/backend/go/sam3-cpp/run.sh +++ b/backend/go/sam3-cpp/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgosam3-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgosam3-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgosam3-avx.so ]; then + if [ -e "$CURDIR"/libgosam3-avx.so ]; then LIBRARY="$CURDIR/libgosam3-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgosam3-avx2.so ]; then + if [ -e "$CURDIR"/libgosam3-avx2.so ]; then LIBRARY="$CURDIR/libgosam3-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgosam3-avx512.so ]; then + if [ -e "$CURDIR"/libgosam3-avx512.so ]; then LIBRARY="$CURDIR/libgosam3-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export SAM3_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/sam3-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/sam3-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/sam3-cpp "$@" +exec "$CURDIR"/sam3-cpp "$@" diff --git a/backend/go/sherpa-onnx/run.sh b/backend/go/sherpa-onnx/run.sh index 771324326..e5e4a7c62 100755 --- a/backend/go/sherpa-onnx/run.sh +++ b/backend/go/sherpa-onnx/run.sh @@ -1,19 +1,19 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") if [ "$(uname)" = "Darwin" ]; then - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH - export SHERPA_SHIM_LIBRARY=$CURDIR/lib/libsherpa-shim.dylib - export SHERPA_ONNX_LIBRARY=$CURDIR/lib/libsherpa-onnx-c-api.dylib + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH + export SHERPA_SHIM_LIBRARY="$CURDIR"/lib/libsherpa-shim.dylib + export SHERPA_ONNX_LIBRARY="$CURDIR"/lib/libsherpa-onnx-c-api.dylib else - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" - exec $CURDIR/lib/ld.so $CURDIR/sherpa-onnx "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/sherpa-onnx "$@" fi -exec $CURDIR/sherpa-onnx "$@" +exec "$CURDIR"/sherpa-onnx "$@" diff --git a/backend/go/silero-vad/run.sh b/backend/go/silero-vad/run.sh index 72658908a..d8c343223 100755 --- a/backend/go/silero-vad/run.sh +++ b/backend/go/silero-vad/run.sh @@ -1,14 +1,14 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" - exec $CURDIR/lib/ld.so $CURDIR/silero-vad "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/silero-vad "$@" fi -exec $CURDIR/silero-vad "$@" \ No newline at end of file +exec "$CURDIR"/silero-vad "$@" \ No newline at end of file diff --git a/backend/go/stablediffusion-ggml/run.sh b/backend/go/stablediffusion-ggml/run.sh index e026b4b28..94d920490 100755 --- a/backend/go/stablediffusion-ggml/run.sh +++ b/backend/go/stablediffusion-ggml/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -20,20 +20,20 @@ if [ "$(uname)" = "Darwin" ]; then if [ ! -e "$LIBRARY" ]; then LIBRARY="$CURDIR/libgosd-fallback.so" fi - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgosd-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgosd-avx.so ]; then + if [ -e "$CURDIR"/libgosd-avx.so ]; then LIBRARY="$CURDIR/libgosd-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgosd-avx2.so ]; then + if [ -e "$CURDIR"/libgosd-avx2.so ]; then LIBRARY="$CURDIR/libgosd-avx2.so" fi fi @@ -41,22 +41,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgosd-avx512.so ]; then + if [ -e "$CURDIR"/libgosd-avx512.so ]; then LIBRARY="$CURDIR/libgosd-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export SD_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/stablediffusion-ggml "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/stablediffusion-ggml "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/stablediffusion-ggml "$@" +exec "$CURDIR"/stablediffusion-ggml "$@" diff --git a/backend/go/supertonic/run.sh b/backend/go/supertonic/run.sh index 683c52ab2..5d116e834 100755 --- a/backend/go/supertonic/run.sh +++ b/backend/go/supertonic/run.sh @@ -1,21 +1,21 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") if [ "$(uname)" = "Darwin" ]; then # macOS uses dyld: there is no ld.so loader, and the search path env # var is DYLD_LIBRARY_PATH. ONNX Runtime ships as a .dylib here. - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH - export ONNXRUNTIME_LIB_PATH=$CURDIR/lib/libonnxruntime.dylib + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH + export ONNXRUNTIME_LIB_PATH="$CURDIR"/lib/libonnxruntime.dylib else - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH - export ONNXRUNTIME_LIB_PATH=$CURDIR/lib/libonnxruntime.so + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH + export ONNXRUNTIME_LIB_PATH="$CURDIR"/lib/libonnxruntime.so - if [ -f $CURDIR/lib/ld.so ]; then + if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" - exec $CURDIR/lib/ld.so $CURDIR/supertonic "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/supertonic "$@" fi fi -exec $CURDIR/supertonic "$@" +exec "$CURDIR"/supertonic "$@" diff --git a/backend/go/vibevoice-cpp/run.sh b/backend/go/vibevoice-cpp/run.sh index ec5a39c14..6ce29cb44 100755 --- a/backend/go/vibevoice-cpp/run.sh +++ b/backend/go/vibevoice-cpp/run.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -14,41 +14,41 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgovibevoicecpp-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgovibevoicecpp-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgovibevoicecpp-avx.so ]; then + if [ -e "$CURDIR"/libgovibevoicecpp-avx.so ]; then LIBRARY="$CURDIR/libgovibevoicecpp-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgovibevoicecpp-avx2.so ]; then + if [ -e "$CURDIR"/libgovibevoicecpp-avx2.so ]; then LIBRARY="$CURDIR/libgovibevoicecpp-avx2.so" fi fi if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgovibevoicecpp-avx512.so ]; then + if [ -e "$CURDIR"/libgovibevoicecpp-avx512.so ]; then LIBRARY="$CURDIR/libgovibevoicecpp-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export VIBEVOICECPP_LIBRARY=$LIBRARY -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/vibevoice-cpp "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/vibevoice-cpp "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/vibevoice-cpp "$@" +exec "$CURDIR"/vibevoice-cpp "$@" diff --git a/backend/go/voxtral/run.sh b/backend/go/voxtral/run.sh index 748c30341..b8be0580d 100644 --- a/backend/go/voxtral/run.sh +++ b/backend/go/voxtral/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,35 +15,35 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgovoxtral-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgovoxtral-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgovoxtral-avx.so ]; then + if [ -e "$CURDIR"/libgovoxtral-avx.so ]; then LIBRARY="$CURDIR/libgovoxtral-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgovoxtral-avx2.so ]; then + if [ -e "$CURDIR"/libgovoxtral-avx2.so ]; then LIBRARY="$CURDIR/libgovoxtral-avx2.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export VOXTRAL_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it (Linux only) -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/voxtral "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/voxtral "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/voxtral "$@" +exec "$CURDIR"/voxtral "$@" diff --git a/backend/go/whisper/run.sh b/backend/go/whisper/run.sh index 0e2bd7eb0..444a247c7 100755 --- a/backend/go/whisper/run.sh +++ b/backend/go/whisper/run.sh @@ -2,7 +2,7 @@ set -ex # Get the absolute current dir where the script is located -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") cd / @@ -15,20 +15,20 @@ fi if [ "$(uname)" = "Darwin" ]; then # macOS: single dylib variant (Metal or Accelerate) LIBRARY="$CURDIR/libgowhisper-fallback.dylib" - export DYLD_LIBRARY_PATH=$CURDIR/lib:$DYLD_LIBRARY_PATH + export DYLD_LIBRARY_PATH="$CURDIR"/lib:$DYLD_LIBRARY_PATH else LIBRARY="$CURDIR/libgowhisper-fallback.so" if grep -q -e "\savx\s" /proc/cpuinfo ; then echo "CPU: AVX found OK" - if [ -e $CURDIR/libgowhisper-avx.so ]; then + if [ -e "$CURDIR"/libgowhisper-avx.so ]; then LIBRARY="$CURDIR/libgowhisper-avx.so" fi fi if grep -q -e "\savx2\s" /proc/cpuinfo ; then echo "CPU: AVX2 found OK" - if [ -e $CURDIR/libgowhisper-avx2.so ]; then + if [ -e "$CURDIR"/libgowhisper-avx2.so ]; then LIBRARY="$CURDIR/libgowhisper-avx2.so" fi fi @@ -36,22 +36,22 @@ else # Check avx 512 if grep -q -e "\savx512f\s" /proc/cpuinfo ; then echo "CPU: AVX512F found OK" - if [ -e $CURDIR/libgowhisper-avx512.so ]; then + if [ -e "$CURDIR"/libgowhisper-avx512.so ]; then LIBRARY="$CURDIR/libgowhisper-avx512.so" fi fi - export LD_LIBRARY_PATH=$CURDIR/lib:$LD_LIBRARY_PATH + export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH fi export WHISPER_LIBRARY=$LIBRARY # If there is a lib/ld.so, use it -if [ -f $CURDIR/lib/ld.so ]; then +if [ -f "$CURDIR"/lib/ld.so ]; then echo "Using lib/ld.so" echo "Using library: $LIBRARY" - exec $CURDIR/lib/ld.so $CURDIR/whisper "$@" + exec "$CURDIR"/lib/ld.so "$CURDIR"/whisper "$@" fi echo "Using library: $LIBRARY" -exec $CURDIR/whisper "$@" \ No newline at end of file +exec "$CURDIR"/whisper "$@" \ No newline at end of file diff --git a/backend/index.yaml b/backend/index.yaml index 36bfff6db..09eb32116 100644 --- a/backend/index.yaml +++ b/backend/index.yaml @@ -1129,6 +1129,7 @@ amd: "vulkan-privacy-filter" intel: "vulkan-privacy-filter" vulkan: "vulkan-privacy-filter" + metal: "metal-privacy-filter" - &faster-whisper icon: https://avatars.githubusercontent.com/u/1520500?s=200&v=4 description: | @@ -3075,6 +3076,16 @@ uri: "quay.io/go-skynet/local-ai-backends:master-gpu-vulkan-privacy-filter" mirrors: - localai/localai-backends:master-gpu-vulkan-privacy-filter +- !!merge <<: *privacyfilter + name: "metal-privacy-filter" + uri: "quay.io/go-skynet/local-ai-backends:latest-metal-darwin-arm64-privacy-filter" + mirrors: + - localai/localai-backends:latest-metal-darwin-arm64-privacy-filter +- !!merge <<: *privacyfilter + name: "metal-privacy-filter-development" + uri: "quay.io/go-skynet/local-ai-backends:master-metal-darwin-arm64-privacy-filter" + mirrors: + - localai/localai-backends:master-metal-darwin-arm64-privacy-filter - !!merge <<: *privacyfilter name: "cuda13-privacy-filter" uri: "quay.io/go-skynet/local-ai-backends:latest-gpu-nvidia-cuda-13-privacy-filter" diff --git a/backend/rust/kokoros/run.sh b/backend/rust/kokoros/run.sh index bdea9f77a..b2acfe184 100755 --- a/backend/rust/kokoros/run.sh +++ b/backend/rust/kokoros/run.sh @@ -1,23 +1,23 @@ #!/bin/bash set -ex -CURDIR=$(dirname "$(realpath $0)") +CURDIR=$(dirname "$(realpath "$0")") -export LD_LIBRARY_PATH=$CURDIR/lib:${LD_LIBRARY_PATH:-} +export LD_LIBRARY_PATH="$CURDIR"/lib:${LD_LIBRARY_PATH:-} # SSL certificates for model auto-download if [ -d "$CURDIR/etc/ssl/certs" ]; then - export SSL_CERT_DIR=$CURDIR/etc/ssl/certs + export SSL_CERT_DIR="$CURDIR"/etc/ssl/certs fi # espeak-ng data directory if [ -d "$CURDIR/espeak-ng-data" ]; then - export ESPEAK_NG_DATA=$CURDIR/espeak-ng-data + export ESPEAK_NG_DATA="$CURDIR"/espeak-ng-data fi # Use bundled ld.so if present (portability) -if [ -f $CURDIR/lib/ld.so ]; then - exec $CURDIR/lib/ld.so $CURDIR/kokoros-grpc "$@" +if [ -f "$CURDIR"/lib/ld.so ]; then + exec "$CURDIR"/lib/ld.so "$CURDIR"/kokoros-grpc "$@" fi -exec $CURDIR/kokoros-grpc "$@" +exec "$CURDIR"/kokoros-grpc "$@" diff --git a/cmd/launcher/FyneApp.toml b/cmd/launcher/FyneApp.toml new file mode 100644 index 000000000..cb0fc38b9 --- /dev/null +++ b/cmd/launcher/FyneApp.toml @@ -0,0 +1,8 @@ +Website = "https://localai.io" + +[Details] +Icon = "../../core/http/static/logo.png" +Name = "LocalAI" +ID = "com.localai.launcher" +Version = "0.0.0" +Build = 1 diff --git a/contrib/macos/Launcher.entitlements b/contrib/macos/Launcher.entitlements new file mode 100644 index 000000000..a46f95113 --- /dev/null +++ b/contrib/macos/Launcher.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/contrib/macos/sign-and-notarize.sh b/contrib/macos/sign-and-notarize.sh new file mode 100755 index 000000000..73497c769 --- /dev/null +++ b/contrib/macos/sign-and-notarize.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# Code-sign and notarize macOS artifacts for LocalAI. +# Every sub-command is a no-op (exit 0) when its required secret is unset, +# so unsigned builds (forks, local dev, PRs) keep working. +set -euo pipefail + +ENTITLEMENTS="contrib/macos/Launcher.entitlements" +KEYCHAIN="localai-ci.keychain-db" + +cmd_import_cert() { + if [ -z "${MACOS_CERTIFICATE:-}" ]; then + echo "[sign] MACOS_CERTIFICATE unset: skipping cert import (unsigned build)" + return 0 + fi + local certfile keychain_pwd default_keychain + certfile="$(mktemp).p12" + keychain_pwd="${MACOS_CI_KEYCHAIN_PWD:?MACOS_CI_KEYCHAIN_PWD required when signing}" + echo "$MACOS_CERTIFICATE" | base64 --decode > "$certfile" + security create-keychain -p "$keychain_pwd" "$KEYCHAIN" + security set-keychain-settings -lut 21600 "$KEYCHAIN" + security unlock-keychain -p "$keychain_pwd" "$KEYCHAIN" + security import "$certfile" -k "$KEYCHAIN" -P "${MACOS_CERTIFICATE_PWD:?}" \ + -T /usr/bin/codesign -T /usr/bin/security + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$keychain_pwd" "$KEYCHAIN" >/dev/null + default_keychain="$(security default-keychain | tr -d ' "')" + security list-keychains -d user -s "$KEYCHAIN" "$default_keychain" + rm -f "$certfile" + echo "[sign] certificate imported into $KEYCHAIN" +} + +cmd_sign() { + local target="$1" + if [ -z "${MACOS_SIGN_IDENTITY:-}" ]; then + echo "[sign] MACOS_SIGN_IDENTITY unset: skipping codesign of $target" + return 0 + fi + case "$target" in + *.app) + # Hardened runtime + entitlements are required for notarizing the app bundle. + codesign --deep --force --options runtime --timestamp \ + --entitlements "$ENTITLEMENTS" \ + --sign "$MACOS_SIGN_IDENTITY" "$target" + ;; + *) + # A disk image carries no entitlements/runtime; just sign the container. + codesign --force --timestamp --sign "$MACOS_SIGN_IDENTITY" "$target" + ;; + esac + codesign --verify --strict --verbose=2 "$target" + echo "[sign] signed $target" +} + +cmd_notarize() { + local dmg="$1" + if [ -z "${MACOS_NOTARY_KEY:-}" ]; then + echo "[notarize] MACOS_NOTARY_KEY unset: skipping notarization of $dmg" + return 0 + fi + local keyfile + keyfile="$(mktemp).p8" + echo "$MACOS_NOTARY_KEY" | base64 --decode > "$keyfile" + xcrun notarytool submit "$dmg" \ + --key "$keyfile" \ + --key-id "${MACOS_NOTARY_KEY_ID:?}" \ + --issuer "${MACOS_NOTARY_ISSUER_ID:?}" \ + --wait + rm -f "$keyfile" + xcrun stapler staple "$dmg" + xcrun stapler validate "$dmg" + echo "[notarize] notarized and stapled $dmg" +} + +main() { + local sub="${1:-}"; shift || true + case "$sub" in + import-cert) cmd_import_cert ;; + sign) cmd_sign "$@" ;; + notarize) cmd_notarize "$@" ;; + *) echo "usage: $0 {import-cert|sign |notarize }" >&2; exit 2 ;; + esac +} + +main "$@" diff --git a/core/cli/run.go b/core/cli/run.go index fd7ba8cd9..0302b5706 100644 --- a/core/cli/run.go +++ b/core/cli/run.go @@ -203,6 +203,7 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error { system.WithBackendImagesReleaseTag(r.BackendImagesReleaseTag), system.WithBackendImagesBranchTag(r.BackendImagesBranchTag), system.WithBackendDevSuffix(r.BackendDevSuffix), + system.WithPreferDevelopmentBackends(r.PreferDevelopmentBackends), ) if err != nil { return err diff --git a/core/gallery/backends.go b/core/gallery/backends.go index 98b1254ef..ab324addf 100644 --- a/core/gallery/backends.go +++ b/core/gallery/backends.go @@ -59,6 +59,22 @@ func getFallbackTagValues(systemState *system.SystemState) (latestTag, masterTag return latestTag, masterTag, devSuffix } +// developmentURI returns the development image URI for a released backend URI by +// swapping the released tag for the branch tag (e.g. +// latest-metal-darwin-arm64-llama-cpp -> master-metal-darwin-arm64-llama-cpp). +// The branch image tracks development. ok is false when uri has no released tag +// to swap or already uses the branch tag. +func developmentURI(uri, latestTag, masterTag string) (string, bool) { + if strings.Contains(uri, masterTag+"-") { + return "", false + } + branchURI := strings.Replace(uri, latestTag+"-", masterTag+"-", 1) + if branchURI == uri { + return "", false + } + return branchURI, true +} + // backendCandidate represents an installed concrete backend option for a given alias type backendCandidate struct { name string @@ -295,15 +311,28 @@ func InstallBackend(ctx context.Context, systemState *system.SystemState, modelL return fmt.Errorf("backend %q: %w", config.Name, optsErr) } - uri := downloader.URI(config.URI) + // PreferDevelopmentBackends installs the development image as the primary URI, + // keeping the released image reachable as the first fallback — instead of only + // reaching development when the released image is missing. + primaryURI := string(config.URI) + mirrors := config.Mirrors + if systemState.PreferDevelopmentBackends { + if devURI, ok := developmentURI(string(config.URI), latestTag, masterTag); ok { + xlog.Info("PreferDevelopmentBackends: installing development image first", "development", devURI, "released", config.URI) + primaryURI = devURI + mirrors = append([]string{string(config.URI)}, config.Mirrors...) + } + } + + uri := downloader.URI(primaryURI) // Check if it is a directory if uri.LooksLikeDir() { // It is a directory, we just copy it over in the backend folder - if err := cp.Copy(config.URI, backendPath); err != nil { + if err := cp.Copy(string(uri), backendPath); err != nil { return fmt.Errorf("failed copying: %w", err) } } else { - xlog.Debug("Downloading backend", "uri", config.URI, "backendPath", backendPath) + xlog.Debug("Downloading backend", "uri", primaryURI, "backendPath", backendPath) if err := uri.DownloadFileWithContext(ctx, backendPath, config.SHA256, 1, 1, downloadStatus, downloadOpts...); err != nil { xlog.Debug("Backend download failed, trying fallback", "backendPath", backendPath, "error", err) @@ -316,8 +345,9 @@ func InstallBackend(ctx context.Context, systemState *system.SystemState, modelL } success := false - // Try to download from mirrors - for _, mirror := range config.Mirrors { + // Try to download from mirrors (when development is preferred, the + // released image is prepended here as the first fallback). + for _, mirror := range mirrors { // Check for cancellation before trying next mirror select { case <-ctx.Done(): diff --git a/core/gallery/backends_devuri_test.go b/core/gallery/backends_devuri_test.go new file mode 100644 index 000000000..f82b318eb --- /dev/null +++ b/core/gallery/backends_devuri_test.go @@ -0,0 +1,26 @@ +package gallery + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("developmentURI", func() { + const latest, master = "latest", "master" + + It("rewrites a released image to its branch (development) image", func() { + got, ok := developmentURI("quay.io/go-skynet/local-ai-backends:latest-metal-darwin-arm64-llama-cpp", latest, master) + Expect(ok).To(BeTrue()) + Expect(got).To(Equal("quay.io/go-skynet/local-ai-backends:master-metal-darwin-arm64-llama-cpp")) + }) + + It("leaves an image already on the branch tag untouched", func() { + _, ok := developmentURI("quay.io/go-skynet/local-ai-backends:master-metal-darwin-arm64-llama-cpp", latest, master) + Expect(ok).To(BeFalse()) + }) + + It("returns ok=false when there is no released tag to swap", func() { + _, ok := developmentURI("oci://localhost/custom-backend:edge", latest, master) + Expect(ok).To(BeFalse()) + }) +}) diff --git a/docs/content/features/agents.md b/docs/content/features/agents.md index e6fd1d0e9..ebdb91c26 100644 --- a/docs/content/features/agents.md +++ b/docs/content/features/agents.md @@ -86,6 +86,18 @@ LOCALAI_AGENT_POOL_DATABASE_URL=postgresql://localrecall:localrecall@postgres:54 The PostgreSQL image `quay.io/mudler/localrecall:v0.5.2-postgresql` is pre-configured with pgvector and ready to use. +#### Connection safety timeouts (PostgreSQL only) + +The embedded vector store sets per-connection timeouts so a single stuck or corrupt index can never hold a lock indefinitely and stall every other collection operation. Safe defaults are applied automatically — you only need to set these to override them: + +| Variable | Default | Description | +|----------|---------|-------------| +| `POSTGRES_LOCK_TIMEOUT` | `30s` | Bounds how long a statement waits to acquire a lock, so queued statements fail fast instead of piling up. Set `0`/`off` to disable. | +| `POSTGRES_IDLE_IN_TRANSACTION_TIMEOUT` | `300s` | Reaps abandoned transactions that would otherwise pin locks. Set `0`/`off` to disable. | +| `POSTGRES_STATEMENT_TIMEOUT` | _(unset)_ | Bounds total statement runtime, auto-aborting a wedged query. Off by default since a large vector index build can exceed any fixed limit; index builds are exempted, so it is safe to enable. | + +These are read directly from the LocalAI process environment by the embedded store (the same as `DATABASE_URL` and `HYBRID_SEARCH_*`). + ### Docker Compose Example Basic setup with in-memory vector store: diff --git a/docs/content/installation/macos.md b/docs/content/installation/macos.md index fc254cedb..dfda42df2 100644 --- a/docs/content/installation/macos.md +++ b/docs/content/installation/macos.md @@ -22,13 +22,16 @@ Download the latest DMG from GitHub releases: 3. Drag the LocalAI application to your Applications folder 4. Launch LocalAI from your Applications folder -## Known Issues +## Verification -> **Note**: The DMGs are not signed by Apple and may show as quarantined. -> -> **Workaround**: See [this issue](https://github.com/mudler/LocalAI/issues/6268) for details on how to bypass the quarantine. -> -> **Fix tracking**: The signing issue is being tracked in [this issue](https://github.com/mudler/LocalAI/issues/6244). +The `LocalAI.dmg` (and the app inside it) and the `local-ai` server binary are +signed with an Apple Developer ID and notarized by Apple, so they launch with no +quarantine prompt or workaround. To inspect the signature yourself: + +```bash +spctl --assess --type open --context context:primary-signature -v /Applications/LocalAI.app +codesign --verify --deep --strict --verbose=2 /Applications/LocalAI.app +``` ## Next Steps diff --git a/gallery/index.yaml b/gallery/index.yaml index d35ff8091..d85f2ba31 100644 --- a/gallery/index.yaml +++ b/gallery/index.yaml @@ -98,6 +98,60 @@ - filename: llama-cpp/models/Qwen3.6-35B-A3B-NVFP4-GGUF/q36-35b-a3b-nvfp4.gguf # TODO(GGUF publish): fill sha256 after uploading the GGUF (sha256sum). uri: https://huggingface.co/mudler/Qwen3.6-35B-A3B-NVFP4-GGUF/resolve/main/q36-35b-a3b-nvfp4.gguf +- name: "ornith-1.0-35b" + url: "github:mudler/LocalAI/gallery/virtual.yaml@master" + urls: + - https://huggingface.co/deepreinforce-ai/Ornith-1.0-35B-GGUF + description: | + [](https://deep-reinforce.com/ornith.html) + + # Ornith-1.0-35B-GGUF + + Aloha! 🌺 Today, we are releasing Ornith-1.0, a self-improving family of open-source models for agentic coding. + + Highlights: + + - **State-of-the-Art Coding Agents**: Available in 9B-Dense, 31B-Dense, 35B-MoE, and 397B-MoE (post-trained on top of Gemma 4 and Qwen 3.5), achieving state-of-the-art performance among open-source models of comparable size on coding benchmarks such as Terminal-Bench 2.1, SWE-Bench, NL2Repo and OpenClaw. + - **Self-Improving Training Framework**: Ornith-1.0 employs RL to learn to generate not only solution rollouts, but also the scallfold that drive those rollouts. By jointly optimizing the scaffold and the resulting solution, the model discovers better search trajectories and generates higher-quality solutions. + - **Licence**: MIT licensed, globally accessible, and free from regional limitations. + + ## Ornith 1.0 35B + + This model card documents **Ornith-1.0-35B**, the lightweight member of the Ornith family, designed for efficient single-GPU deployment. + + ### Benchmarks + + Ornith-1.0-35B + Qwen3.5-35B + Qwen3.6-35B + Gemma4-31B + Qwen3.5-397B + + Agentic Coding + + ... + license: "mit" + tags: + - llm + - gguf + overrides: + backend: llama-cpp + function: + automatic_tool_parsing_fallback: true + grammar: + disable: true + known_usecases: + - chat + options: + - use_jinja:true + parameters: + model: llama-cpp/models/Ornith-1.0-35B-GGUF/ornith-1.0-35b-Q4_K_M.gguf + template: + use_tokenizer_template: true + files: + - filename: llama-cpp/models/Ornith-1.0-35B-GGUF/ornith-1.0-35b-Q4_K_M.gguf + sha256: ff25291b2599fb927a835e624d2b3540106af61761c3fa57ac4264046dbec002 + uri: https://huggingface.co/deepreinforce-ai/Ornith-1.0-35B-GGUF/resolve/main/ornith-1.0-35b-Q4_K_M.gguf - name: "gemmable-4-12b-mtp" url: "github:mudler/LocalAI/gallery/virtual.yaml@master" urls: diff --git a/go.mod b/go.mod index 185407279..fa3608c62 100644 --- a/go.mod +++ b/go.mod @@ -221,7 +221,7 @@ require ( github.com/labstack/gommon v0.4.2 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/mudler/LocalAGI v0.0.0-20260606071251-14aed1ae4336 - github.com/mudler/localrecall v0.6.3-0.20260618142827-d0073dd5dc32 // indirect + github.com/mudler/localrecall v0.6.3 // indirect github.com/mudler/skillserver v0.0.7-0.20260520220837-a7317cbf9145 github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/oxffaa/gopher-parse-sitemap v0.0.0-20191021113419-005d2eb1def4 // indirect diff --git a/go.sum b/go.sum index ea32405fe..129976bbc 100644 --- a/go.sum +++ b/go.sum @@ -976,10 +976,8 @@ github.com/mudler/go-piper v0.0.0-20241023091659-2494246fd9fc h1:RxwneJl1VgvikiX github.com/mudler/go-piper v0.0.0-20241023091659-2494246fd9fc/go.mod h1:O7SwdSWMilAWhBZMK9N9Y/oBDyMMzshE3ju8Xkexwig= github.com/mudler/go-processmanager v0.1.1 h1:c/1NRZOZpW8HuFv9RhBG57nQu1oDMRomEHedwBFMlrw= github.com/mudler/go-processmanager v0.1.1/go.mod h1:h6kmHUZeafr+k5hRYpGLMzJFH4hItHffgpRo2QIkP+o= -github.com/mudler/localrecall v0.6.3-0.20260606070048-9a3b3321a9cd h1:trn9D5UHAE6zdRyD2uX04W1tLSslAwozVwcyNTd72Ak= -github.com/mudler/localrecall v0.6.3-0.20260606070048-9a3b3321a9cd/go.mod h1:28k5n19raUrkuwXkacdNsBlj8yuSnGhpT16tu+2+4dU= -github.com/mudler/localrecall v0.6.3-0.20260618142827-d0073dd5dc32 h1:RP4BVGTHHpJIrGAwqRD3Wq1wmURmc1SxhwacnIWgI+g= -github.com/mudler/localrecall v0.6.3-0.20260618142827-d0073dd5dc32/go.mod h1:28k5n19raUrkuwXkacdNsBlj8yuSnGhpT16tu+2+4dU= +github.com/mudler/localrecall v0.6.3 h1:uXOrP9JmetzxgVKzSrawviyBHZfAcvPBBIrvVUdZjDA= +github.com/mudler/localrecall v0.6.3/go.mod h1:28k5n19raUrkuwXkacdNsBlj8yuSnGhpT16tu+2+4dU= github.com/mudler/memory v0.0.0-20260406210934-424c1ecf2cf8 h1:Ry8RiWy8fZ6Ff4E7dPmjRsBrnHOnPeOOj2LhCgyjQu0= github.com/mudler/memory v0.0.0-20260406210934-424c1ecf2cf8/go.mod h1:EA8Ashhd56o32qN7ouPKFSRUs/Z+LrRCF4v6R2Oarm8= github.com/mudler/skillserver v0.0.7-0.20260520220837-a7317cbf9145 h1:z59tA3IDYPt71nzH1jpxeaA1LuDw8aZfpTQFNU43Zb8= diff --git a/pkg/system/state.go b/pkg/system/state.go index 2d9afcf04..2ad52b7d3 100644 --- a/pkg/system/state.go +++ b/pkg/system/state.go @@ -26,6 +26,10 @@ type SystemState struct { BackendImagesReleaseTag string BackendImagesBranchTag string BackendDevSuffix string + // PreferDevelopmentBackends installs the development image as the primary + // backend URI (the released image becomes a fallback) rather than only using + // development as a download fallback when the released image is missing. + PreferDevelopmentBackends bool } type SystemStateOptions func(*SystemState) @@ -66,6 +70,12 @@ func WithBackendDevSuffix(suffix string) SystemStateOptions { } } +func WithPreferDevelopmentBackends(prefer bool) SystemStateOptions { + return func(s *SystemState) { + s.PreferDevelopmentBackends = prefer + } +} + func GetSystemState(opts ...SystemStateOptions) (*SystemState, error) { state := &SystemState{} for _, opt := range opts { diff --git a/scripts/build/golang-darwin.sh b/scripts/build/golang-darwin.sh index 02e75f736..fddd4bc4f 100644 --- a/scripts/build/golang-darwin.sh +++ b/scripts/build/golang-darwin.sh @@ -22,8 +22,20 @@ if [ -f "${BACKEND_DIR}/run.sh" ]; then RUN_BINARY=$(grep -oE '\$CURDIR/[A-Za-z0-9._-]+' "${BACKEND_DIR}/run.sh" | grep -v 'ld\.so' | tail -1 | sed 's|\$CURDIR/||') fi RUN_BINARY="${RUN_BINARY:-${BACKEND}}" -if [ ! -x "${BACKEND_DIR}/${RUN_BINARY}" ]; then - echo "ERROR: ${BACKEND_DIR}/${RUN_BINARY} not found after build; refusing to package a broken backend image (see issue #10267)." >&2 + +# Ship the self-contained package/ dir (run.sh + binary + lib/), matching the +# Linux Dockerfile.golang (`COPY .../package/. ./`). Packaging the whole backend +# dir instead left the runtime libraries under package/lib while run.sh looks in +# $CURDIR/lib, so backends such as sherpa-onnx could not dlopen their libs at +# runtime (they started fine only when run from inside package/). Backends that +# don't assemble a package/ fall back to the backend dir. +OCI_ROOT="${BACKEND_DIR}" +if [ -d "${BACKEND_DIR}/package" ]; then + OCI_ROOT="${BACKEND_DIR}/package" +fi + +if [ ! -x "${OCI_ROOT}/${RUN_BINARY}" ]; then + echo "ERROR: ${OCI_ROOT}/${RUN_BINARY} not found after build; refusing to package a broken backend image (see issue #10267)." >&2 exit 1 fi @@ -31,7 +43,7 @@ PLATFORMARCH="${PLATFORMARCH:-darwin/arm64}" IMAGE_NAME="${IMAGE_NAME:-localai/${BACKEND}-darwin}" ./local-ai util create-oci-image \ - backend/go/${BACKEND}/. \ + "${OCI_ROOT}/." \ --output ./backend-images/${BACKEND}.tar \ --image-name $IMAGE_NAME \ --platform $PLATFORMARCH diff --git a/scripts/build/privacy-filter-darwin.sh b/scripts/build/privacy-filter-darwin.sh new file mode 100644 index 000000000..aca901822 --- /dev/null +++ b/scripts/build/privacy-filter-darwin.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Darwin/Metal build for the privacy-filter backend. Mirrors ds4-darwin.sh: +# native make of the single grpc-server, otool -L dylib bundling, then assemble +# an OCI tar that `local-ai backends install` can consume. +# +# privacy-filter.cpp pulls ggml, which defaults GGML_METAL=ON on Apple - the +# engine's CMake never forces it off, so a plain Darwin build is Metal-enabled. +# grpc++/protobuf are resolved from Homebrew via find_package(... CONFIG). +set -ex + +IMAGE_NAME="${IMAGE_NAME:-localai/privacy-filter-darwin}" + +pushd backend/cpp/privacy-filter +make grpc-server +popd + +mkdir -p build/darwin +mkdir -p build/darwin/lib +mkdir -p backend-images + +cp -rf backend/cpp/privacy-filter/grpc-server build/darwin/ +cp -rf backend/cpp/privacy-filter/run.sh build/darwin/ + +# Apple Silicon: pick up Homebrew-installed protobuf utf8_validity if present +# (same as ds4-darwin.sh - it is a transitive dep otool may not surface). +if [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]; then + ADDITIONAL_LIBS=${ADDITIONAL_LIBS:-$(ls /opt/homebrew/Cellar/protobuf/**/lib/libutf8_validity*.dylib 2>/dev/null)} +else + ADDITIONAL_LIBS=${ADDITIONAL_LIBS:-""} +fi +for file in $ADDITIONAL_LIBS; do + cp -rfv "$file" build/darwin/lib +done + +# Bundle the ggml shared libs the binary @rpath-links (libggml, -cpu, -blas, +# -metal). The engine builds ggml shared, scattered under the build tree; flatten +# them (with their version symlinks) into lib/, resolved at runtime by leaf name +# via run.sh's DYLD_LIBRARY_PATH=lib. Without this the packaged binary can't find +# libggml*.dylib once the build dir is gone. +GGML_SRC="backend/cpp/privacy-filter/build/privacy-filter.cpp/ggml/src" +find "$GGML_SRC" -name 'libggml*.dylib' -exec cp -a {} build/darwin/lib/ \; + +# Walk dylibs via otool -L and bundle anything that isn't a system framework. +for file in build/darwin/grpc-server; do + LIBS="$(otool -L "$file" | awk 'NR > 1 { system("echo " $1) } ' | xargs echo)" + for lib in $LIBS; do + if [[ "$lib" == *.dylib ]] && [[ -e "$lib" ]]; then + cp -rvf "$lib" build/darwin/lib + fi + done +done + +echo "Bundled libraries:" +ls -la build/darwin/lib + +# Build an OCI image tar (with manifest.json) that `local-ai backends install` +# can consume - mirrors ds4-darwin.sh. +PLATFORMARCH="${PLATFORMARCH:-darwin/arm64}" + +./local-ai util create-oci-image \ + build/darwin/. \ + --output ./backend-images/privacy-filter.tar \ + --image-name "$IMAGE_NAME" \ + --platform "$PLATFORMARCH" + +rm -rf build/darwin diff --git a/scripts/changed-backends.js b/scripts/changed-backends.js index ec6f4f742..b908df8b9 100644 --- a/scripts/changed-backends.js +++ b/scripts/changed-backends.js @@ -85,6 +85,11 @@ function inferBackendPathDarwin(item) { if (item.backend === "ds4") { return `backend/cpp/ds4/`; } + // privacy-filter is C++ too (built via `make backends/privacy-filter-darwin`); + // same lang=go-for-runner convention, source under backend/cpp. + if (item.backend === "privacy-filter") { + return `backend/cpp/privacy-filter/`; + } if (!item.lang) { return `backend/python/${item.backend}/`; }