From 068d397acf38355bb0f3501dd03237b15a4fe76c Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Fri, 26 Jun 2026 15:10:15 +0200 Subject: [PATCH 1/4] fix(backends): set rpath on the piper darwin binary so it can load its bundled libs (#10525) The metal-darwin-arm64-piper backend crashed at launch on macOS: DYLD "Library missing" Library not loaded: @rpath/libucd.dylib Referenced from: .../piper Reason: no LC_RPATH's found The piper binary links libucd, libespeak-ng, libpiper_phonemize and libonnxruntime via @rpath, but ships with no LC_RPATH, so dyld cannot expand @rpath and aborts before piper runs. The libraries themselves are already bundled in package/lib/ by package.sh. Additionally, package.sh's architecture detection only handled the Linux glibc loaders (/lib64/ld-linux-x86-64.so.2, /lib/ld-linux-aarch64.so.1) and otherwise hit `echo "Error: Could not detect architecture"; exit 1`, so on macOS packaging failed outright. Add a Darwin branch (before the Linux checks) that skips the glibc/ld.so bundling macOS has no use for and instead runs `install_name_tool -add_rpath @loader_path/lib` on the piper binary, so @rpath resolves to the bundled package/lib/ directory. Also mirror sherpa-onnx/opus in run.sh: export DYLD_LIBRARY_PATH on Darwin (LD_LIBRARY_PATH is Linux-only) as a defensive fallback. Validated by hand on Apple Silicon: with the rpath added, piper synthesized a real WAV. The darwin build is validated in CI. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ettore Di Giacinto Co-authored-by: Ettore Di Giacinto --- backend/go/piper/package.sh | 10 +++++++++- backend/go/piper/run.sh | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/go/piper/package.sh b/backend/go/piper/package.sh index 1b6760256..a3f59c95f 100755 --- a/backend/go/piper/package.sh +++ b/backend/go/piper/package.sh @@ -16,7 +16,15 @@ cp -rfv $CURDIR/run.sh $CURDIR/package/ cp -rfLv $CURDIR/sources/go-piper/piper-phonemize/pi/lib/* $CURDIR/package/lib/ # Detect architecture and copy appropriate libraries -if [ -f "/lib64/ld-linux-x86-64.so.2" ]; then +if [ "$(uname)" = "Darwin" ]; then + # macOS has no glibc loader to bundle. The piper binary links its bundled + # libs (libucd, libespeak-ng, libpiper_phonemize, libonnxruntime) via + # @rpath but ships with no LC_RPATH, so dyld aborts at launch with + # "Library not loaded: @rpath/libucd.dylib ... no LC_RPATH's found". + # Add an @loader_path/lib rpath so @rpath resolves to package/lib/. + echo "Detected macOS; adding @loader_path/lib rpath so bundled libs resolve via @rpath..." + install_name_tool -add_rpath @loader_path/lib "$CURDIR/package/piper" +elif [ -f "/lib64/ld-linux-x86-64.so.2" ]; then # x86_64 architecture echo "Detected x86_64 architecture, copying x86_64 libraries..." cp -arfLv /lib64/ld-linux-x86-64.so.2 $CURDIR/package/lib/ld.so diff --git a/backend/go/piper/run.sh b/backend/go/piper/run.sh index 6e04021e3..49d336097 100755 --- a/backend/go/piper/run.sh +++ b/backend/go/piper/run.sh @@ -4,7 +4,12 @@ set -ex CURDIR=$(dirname "$(realpath "$0")") export ESPEAK_NG_DATA="$CURDIR"/espeak-ng-data -export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH + +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 there is a lib/ld.so, use it if [ -f "$CURDIR"/lib/ld.so ]; then From 17c1fc74b2210f9a17b6721370abc07376b1a81b Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Fri, 26 Jun 2026 22:31:06 +0200 Subject: [PATCH 2/4] fix(backends): darwin packaging for silero-vad (last Linux-only Go backend) (#10528) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(backends): darwin packaging for silero-vad silero-vad was the last Go backend with Linux-only darwin packaging: - package.sh fell through to "Could not detect architecture" -> exit 1 on macOS (no Darwin branch), so its darwin image never packaged. - run.sh exported LD_LIBRARY_PATH, which macOS dyld ignores, so the bundled libonnxruntime.dylib couldn't be found at runtime. Add a Darwin branch to package.sh (skip the glibc/ld.so bundling; add an @loader_path/lib rpath so @rpath resolves to package/lib/) and a DYLD_LIBRARY_PATH branch to run.sh — mirroring the piper darwin fix (#10525). Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Ettore Di Giacinto Co-authored-by: Ettore Di Giacinto --- backend/go/silero-vad/package.sh | 9 ++++++++- backend/go/silero-vad/run.sh | 6 +++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/backend/go/silero-vad/package.sh b/backend/go/silero-vad/package.sh index bec3150d5..a96ff4c8b 100755 --- a/backend/go/silero-vad/package.sh +++ b/backend/go/silero-vad/package.sh @@ -15,7 +15,14 @@ cp -avf $CURDIR/run.sh $CURDIR/package/ cp -rfLv $CURDIR/backend-assets/lib/* $CURDIR/package/lib/ # Detect architecture and copy appropriate libraries -if [ -f "/lib64/ld-linux-x86-64.so.2" ]; then +if [ "$(uname)" = "Darwin" ]; then + # macOS has no glibc loader to bundle. silero-vad links its bundled + # libonnxruntime via @rpath but ships with no LC_RPATH, so dyld can't find + # it at runtime. Add an @loader_path/lib rpath so @rpath resolves to + # package/lib/ (matching the piper darwin fix, #10525). + echo "Detected macOS; adding @loader_path/lib rpath so bundled libs resolve via @rpath..." + install_name_tool -add_rpath @loader_path/lib "$CURDIR/package/silero-vad" +elif [ -f "/lib64/ld-linux-x86-64.so.2" ]; then # x86_64 architecture echo "Detected x86_64 architecture, copying x86_64 libraries..." cp -arfLv /lib64/ld-linux-x86-64.so.2 $CURDIR/package/lib/ld.so diff --git a/backend/go/silero-vad/run.sh b/backend/go/silero-vad/run.sh index d8c343223..0508420e5 100755 --- a/backend/go/silero-vad/run.sh +++ b/backend/go/silero-vad/run.sh @@ -3,7 +3,11 @@ set -ex CURDIR=$(dirname "$(realpath "$0")") -export LD_LIBRARY_PATH="$CURDIR"/lib:$LD_LIBRARY_PATH +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 there is a lib/ld.so, use it if [ -f "$CURDIR"/lib/ld.so ]; then From c4fa256cdfb996cb217f1bcc70f91c1f9096702a Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Fri, 26 Jun 2026 22:31:22 +0200 Subject: [PATCH 3/4] chore(model gallery): :robot: add 1 new models via gallery agent (#10526) chore(model gallery): :robot: add new models via gallery agent Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: mudler <2420543+mudler@users.noreply.github.com> --- gallery/index.yaml | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/gallery/index.yaml b/gallery/index.yaml index 15c97b190..ffac8e85f 100644 --- a/gallery/index.yaml +++ b/gallery/index.yaml @@ -1,4 +1,58 @@ --- +- name: "ornith-1.0-9b" + url: "github:mudler/LocalAI/gallery/virtual.yaml@master" + urls: + - https://huggingface.co/deepreinforce-ai/Ornith-1.0-9B-GGUF + description: | + [](https://deep-reinforce.com/ornith.html) + + # Ornith-1.0-9B-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 9B + + This model card documents **Ornith-1.0-9B**, the most lightweight member of the Ornith family, designed for efficient single-GPU deployment. + + ### Benchmarks + + Ornith-1.0-9B + Qwen3.5-9B + Qwen3.5-35B + Gemma4-12B + Gemma4-31B + + 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-9B-GGUF/ornith-1.0-9b-Q4_K_M.gguf + template: + use_tokenizer_template: true + files: + - filename: llama-cpp/models/Ornith-1.0-9B-GGUF/ornith-1.0-9b-Q4_K_M.gguf + sha256: 5720d1f671b4996481274fffe01868c3c36e87c135cc8538471cc7bd6087b106 + uri: https://huggingface.co/deepreinforce-ai/Ornith-1.0-9B-GGUF/resolve/main/ornith-1.0-9b-Q4_K_M.gguf - name: "ornith-1.0-35b" url: "github:mudler/LocalAI/gallery/virtual.yaml@master" urls: From 56600eec3e1506a742664680e9b18a6d1e7e1504 Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Fri, 26 Jun 2026 23:06:42 +0200 Subject: [PATCH 4/4] fix(nodes): show a node's existing labels on the detail view (#10529) fix(nodes): return labels in single-node GET so the detail view shows them The node detail view (/app/nodes/:id) reads `node.labels` to render a node's existing labels, but the single-node GET endpoint returned a bare BackendNode whose Labels live in a separate table - so the list was always empty and operators could only add labels, never see what was already set (#10527). The same response also lacked in_flight_count and model_count. Add NodeRegistry.GetWithExtras, mirroring the existing List vs ListWithExtras split: bare Get stays cheap for the routing hot paths and existence checks, while the detail endpoint uses the enriched variant to attach the labels map and live counts. No frontend change is needed - the UI already renders existing labels once the data is present. Closes #10527 Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Ettore Di Giacinto Co-authored-by: Ettore Di Giacinto --- core/http/endpoints/localai/nodes.go | 5 +++- core/services/nodes/registry.go | 43 ++++++++++++++++++++++++++++ core/services/nodes/registry_test.go | 32 +++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/core/http/endpoints/localai/nodes.go b/core/http/endpoints/localai/nodes.go index d6c44e383..e91eda6f4 100644 --- a/core/http/endpoints/localai/nodes.go +++ b/core/http/endpoints/localai/nodes.go @@ -60,7 +60,10 @@ func GetNodeEndpoint(registry *nodes.NodeRegistry) echo.HandlerFunc { return func(c echo.Context) error { ctx := c.Request().Context() id := c.Param("id") - node, err := registry.Get(ctx, id) + // GetWithExtras (not Get) so the response carries the node's labels, + // loaded-model count, and in-flight total — the bare BackendNode keeps + // labels in a separate table, leaving the detail view's label list empty. + node, err := registry.GetWithExtras(ctx, id) if err != nil { return c.JSON(http.StatusNotFound, nodeError(http.StatusNotFound, "node not found")) } diff --git a/core/services/nodes/registry.go b/core/services/nodes/registry.go index aafee13cb..8470c67ed 100644 --- a/core/services/nodes/registry.go +++ b/core/services/nodes/registry.go @@ -673,6 +673,49 @@ func (r *NodeRegistry) Get(ctx context.Context, nodeID string) (*BackendNode, er return &node, nil } +// GetWithExtras returns a single node enriched with the same computed fields as +// ListWithExtras (labels, loaded-model count, in-flight total). The plain Get +// returns a bare BackendNode whose Labels live in a separate table, so the node +// detail view needs this to show a node's existing labels and live counts. +func (r *NodeRegistry) GetWithExtras(ctx context.Context, nodeID string) (*NodeWithExtras, error) { + node, err := r.Get(ctx, nodeID) + if err != nil { + return nil, err + } + + labels := make(map[string]string) + nodeLabels, err := r.GetNodeLabels(ctx, nodeID) + if err != nil { + xlog.Warn("GetWithExtras: failed to get labels", "node", nodeID, "error", err) + } else { + for _, l := range nodeLabels { + labels[l.Key] = l.Value + } + } + + var modelCount int64 + if err := r.db.WithContext(ctx).Model(&NodeModel{}). + Where("node_id = ? AND state = ?", nodeID, "loaded"). + Count(&modelCount).Error; err != nil { + xlog.Warn("GetWithExtras: failed to get model count", "node", nodeID, "error", err) + } + + var inFlight struct{ Total int } + if err := r.db.WithContext(ctx).Model(&NodeModel{}). + Select("COALESCE(SUM(in_flight), 0) as total"). + Where("node_id = ? AND state IN ?", nodeID, []string{"loaded", "unloading"}). + Scan(&inFlight).Error; err != nil { + xlog.Warn("GetWithExtras: failed to get in-flight count", "node", nodeID, "error", err) + } + + return &NodeWithExtras{ + BackendNode: *node, + ModelCount: int(modelCount), + InFlightCount: inFlight.Total, + Labels: labels, + }, nil +} + // GetByName returns a single node by name. func (r *NodeRegistry) GetByName(ctx context.Context, name string) (*BackendNode, error) { var node BackendNode diff --git a/core/services/nodes/registry_test.go b/core/services/nodes/registry_test.go index 6f43706db..589a62fa3 100644 --- a/core/services/nodes/registry_test.go +++ b/core/services/nodes/registry_test.go @@ -646,6 +646,38 @@ var _ = Describe("NodeRegistry", func() { }) }) + Describe("GetWithExtras", func() { + It("returns the node enriched with its labels map", func() { + node := makeNode("extras-node", "10.0.0.80:50051", 8_000_000_000) + Expect(registry.Register(context.Background(), node, true)).To(Succeed()) + Expect(registry.SetNodeLabel(context.Background(), node.ID, "env", "prod")).To(Succeed()) + Expect(registry.SetNodeLabel(context.Background(), node.ID, "region", "us-east")).To(Succeed()) + + got, err := registry.GetWithExtras(context.Background(), node.ID) + Expect(err).ToNot(HaveOccurred()) + Expect(got).ToNot(BeNil()) + Expect(got.ID).To(Equal(node.ID)) + Expect(got.Name).To(Equal("extras-node")) + Expect(got.Labels).To(Equal(map[string]string{"env": "prod", "region": "us-east"})) + }) + + It("returns an empty (non-nil) labels map when the node has none", func() { + node := makeNode("extras-no-labels", "10.0.0.81:50051", 8_000_000_000) + Expect(registry.Register(context.Background(), node, true)).To(Succeed()) + + got, err := registry.GetWithExtras(context.Background(), node.ID) + Expect(err).ToNot(HaveOccurred()) + Expect(got).ToNot(BeNil()) + Expect(got.Labels).ToNot(BeNil()) + Expect(got.Labels).To(BeEmpty()) + }) + + It("returns an error for an unknown node", func() { + _, err := registry.GetWithExtras(context.Background(), "does-not-exist") + Expect(err).To(HaveOccurred()) + }) + }) + Describe("FindNodesBySelector", func() { It("returns nodes matching all labels in selector", func() { n1 := makeNode("sel-match", "10.0.0.80:50051", 8_000_000_000)