Compare commits

..

1 Commits

Author SHA1 Message Date
Ettore Di Giacinto
abffce14bd fix(neutts): pin torchaudio to match torch to avoid ABI mismatch (#9798)
neucodec pulls torchaudio transitively but it was unpinned, so an
incompatible torchaudio could be resolved against the pinned torch==2.8.0,
producing the 'undefined symbol: torch_library_impl' load failure. Pin
torchaudio==2.8.0 alongside torch in the cpu and cublas12 requirements.

Assisted-by: claude:claude-opus-4-8 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
2026-06-12 22:23:56 +00:00
4 changed files with 7 additions and 95 deletions

View File

@@ -1,6 +1,7 @@
--extra-index-url https://download.pytorch.org/whl/cpu
accelerate
torch==2.8.0
torchaudio==2.8.0
transformers==4.56.1
librosa==0.11.0
neucodec>=0.0.4

View File

@@ -3,6 +3,7 @@ neucodec>=0.0.4
phonemizer==3.3.0
soundfile==0.13.1
torch==2.8.0
torchaudio==2.8.0
transformers==4.56.1
resemble-perth==1.0.1
accelerate

View File

@@ -2,8 +2,6 @@ package openai
import (
"net/http"
"runtime"
"strings"
"time"
"github.com/labstack/echo/v4"
@@ -13,52 +11,6 @@ import (
"github.com/pion/webrtc/v4"
)
// opusBackendName is the canonical gallery name/alias of the opus audio codec
// backend that the realtime WebRTC transport needs.
const opusBackendName = "opus"
// resolveOpusBackend picks which installed opus-codec backend the realtime
// WebRTC transport should load. The transport historically hardcoded the
// literal "opus" backend name, but on darwin/arm64 the only installable opus
// codec is "metal-opus" (it shares the gallery alias "opus"). When that
// platform-specific variant is registered under its concrete directory name
// rather than the "opus" alias key, loading the literal "opus" fails with
// "opus backend not available" (issue #9813). Given the set of currently
// loadable backend names, this returns the best opus codec to load for the
// running platform, falling back to the literal name so the caller surfaces
// the same error as before when no opus codec is installed at all.
func resolveOpusBackend(installed []string, goos, goarch string) string {
// An exact match wins: this covers the plain "opus" backend as well as the
// "opus" alias key registered by gallery alias resolution for a
// user-installed platform variant.
for _, b := range installed {
if b == opusBackendName {
return opusBackendName
}
}
// No "opus" key is registered (e.g. a system-path metal-opus whose alias
// was never collected). Fall back to a platform-appropriate "*opus*" codec
// backend; on darwin/arm64 prefer the metal build.
var fallback string
for _, b := range installed {
if !strings.Contains(strings.ToLower(b), opusBackendName) {
continue
}
if goos == "darwin" && goarch == "arm64" && strings.Contains(strings.ToLower(b), "metal") {
return b
}
if fallback == "" {
fallback = b
}
}
if fallback != "" {
return fallback
}
return opusBackendName
}
// RealtimeCallRequest is the JSON body for POST /v1/realtime/calls.
type RealtimeCallRequest struct {
SDP string `json:"sdp"`
@@ -142,25 +94,15 @@ func RealtimeCalls(application *application.Application) echo.HandlerFunc {
}
}()
// Load the Opus backend. The opus codec ships under different backend
// names per platform (e.g. "metal-opus" on darwin/arm64), so resolve the
// platform-appropriate variant from the installed backends instead of
// hardcoding the literal "opus" name (issue #9813).
ml := application.ModelLoader()
installed := make([]string, 0)
for name := range ml.GetAllExternalBackends(nil) {
installed = append(installed, name)
}
opusName := resolveOpusBackend(installed, runtime.GOOS, runtime.GOARCH)
opusBackend, err := ml.Load(
model.WithBackendString(opusName),
// Load the Opus backend
opusBackend, err := application.ModelLoader().Load(
model.WithBackendString("opus"),
model.WithModelID("__opus_codec__"),
model.WithModel(opusName),
model.WithModel("opus"),
)
if err != nil {
pc.Close()
xlog.Error("failed to load opus backend", "error", err, "backend", opusName)
xlog.Error("failed to load opus backend", "error", err)
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "opus backend not available"})
}

View File

@@ -1,32 +0,0 @@
package openai
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("resolveOpusBackend", func() {
It("prefers the exact opus backend when it is installed", func() {
Expect(resolveOpusBackend([]string{"opus", "metal-opus"}, "linux", "amd64")).To(Equal("opus"))
})
It("resolves to the opus alias key on linux", func() {
Expect(resolveOpusBackend([]string{"opus"}, "linux", "amd64")).To(Equal("opus"))
})
It("selects metal-opus on darwin/arm64 when no plain opus is installed", func() {
Expect(resolveOpusBackend([]string{"metal-opus"}, "darwin", "arm64")).To(Equal("metal-opus"))
})
It("selects metal-opus on darwin/arm64 even when other backends are present", func() {
Expect(resolveOpusBackend([]string{"silero-vad", "metal-opus", "whisper"}, "darwin", "arm64")).To(Equal("metal-opus"))
})
It("falls back to any opus codec backend when there is no exact match (non-darwin)", func() {
Expect(resolveOpusBackend([]string{"metal-opus"}, "linux", "amd64")).To(Equal("metal-opus"))
})
It("returns the literal opus name when no opus codec is installed", func() {
Expect(resolveOpusBackend([]string{"silero-vad", "whisper"}, "darwin", "arm64")).To(Equal("opus"))
})
})