Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/gallery/backends_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/mudler/LocalAI/pkg/system"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)

const (
Expand Down
2 changes: 1 addition & 1 deletion core/gallery/gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/mudler/LocalAI/pkg/xsync"
"github.com/mudler/xlog"

"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)

func GetGalleryConfigFromURL[T any](url string, basePath string) (T, error) {
Expand Down
59 changes: 58 additions & 1 deletion core/gallery/gallery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"os"
"path/filepath"

"dario.cat/mergo"
"github.com/mudler/LocalAI/core/config"
. "github.com/mudler/LocalAI/core/gallery"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)

var _ = Describe("Gallery", func() {
Expand Down Expand Up @@ -462,4 +463,60 @@ var _ = Describe("Gallery", func() {
Expect(result).To(BeNil())
})
})

Describe("YAML merge with nested maps", func() {
It("should handle YAML anchors and merges with nested overrides (regression test for nanbeige4.1)", func() {
// This tests the fix for the panic that occurred with yaml.v2:
// yaml.v2 produces map[interface{}]interface{} for nested maps
// which caused mergo.Merge to panic with "value of type interface {} is not assignable to type string"
// The exact YAML structure from gallery/index.yaml nanbeige4.1 entries
yamlContent := `---
- &nanbeige4
name: "nanbeige4.1-3b-q8"
overrides:
parameters:
model: nanbeige4.1-3b-q8_0.gguf
- !!merge <<: *nanbeige4
name: "nanbeige4.1-3b-q4"
overrides:
parameters:
model: nanbeige4.1-3b-q4_k_m.gguf
`
var models []GalleryModel
err := yaml.Unmarshal([]byte(yamlContent), &models)
Expect(err).NotTo(HaveOccurred())
Expect(models).To(HaveLen(2))

// Verify first model
Expect(models[0].Name).To(Equal("nanbeige4.1-3b-q8"))
Expect(models[0].Overrides).NotTo(BeNil())
Expect(models[0].Overrides["parameters"]).To(BeAssignableToTypeOf(map[string]interface{}{}))
params := models[0].Overrides["parameters"].(map[string]interface{})
Expect(params["model"]).To(Equal("nanbeige4.1-3b-q8_0.gguf"))

// Verify second model (merged)
Expect(models[1].Name).To(Equal("nanbeige4.1-3b-q4"))
Expect(models[1].Overrides).NotTo(BeNil())
Expect(models[1].Overrides["parameters"]).To(BeAssignableToTypeOf(map[string]interface{}{}))
params = models[1].Overrides["parameters"].(map[string]interface{})
Expect(params["model"]).To(Equal("nanbeige4.1-3b-q4_k_m.gguf"))

// Simulate the mergo.Merge call that was failing in models.go:251
// This should not panic with yaml.v3
configMap := make(map[string]interface{})
configMap["name"] = "test"
configMap["backend"] = "llama-cpp"
configMap["parameters"] = map[string]interface{}{
"model": "original.gguf",
}

err = mergo.Merge(&configMap, models[1].Overrides, mergo.WithOverride)
Expect(err).NotTo(HaveOccurred())
Expect(configMap["parameters"]).NotTo(BeNil())

// Verify the merge worked correctly
mergedParams := configMap["parameters"].(map[string]interface{})
Expect(mergedParams["model"]).To(Equal("nanbeige4.1-3b-q4_k_m.gguf"))
})
})
})
2 changes: 1 addition & 1 deletion core/services/backends_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)

var _ = Describe("InstallExternalBackend", func() {
Expand Down
2 changes: 1 addition & 1 deletion core/services/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/mudler/LocalAI/pkg/system"
"github.com/mudler/LocalAI/pkg/utils"
"github.com/mudler/xlog"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)

const (
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ require (
go.opentelemetry.io/otel/metric v1.40.0
go.opentelemetry.io/otel/sdk/metric v1.40.0
google.golang.org/grpc v1.78.0
gopkg.in/yaml.v2 v2.4.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
oras.land/oras-go/v2 v2.6.0
)
Expand All @@ -75,7 +75,7 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

require (
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mudler/cogito v0.8.1 h1:66qPJkAMrq/Vo8AC/PvXWuVxYPhi7X2DQuJIilL8+3I=
github.com/mudler/cogito v0.8.1/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44 h1:joGszpItINnZdoL/0p2077Wz2xnxMGRSRgYN5mS7I4c=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/edgevpn v0.31.1 h1:7qegiDWd0kAg6ljhNHxqvp8hbo/6BbzSdbb7/2WZfiY=
Expand Down
Loading