|
|
package gallery_test |
|
|
|
|
|
import ( |
|
|
"context" |
|
|
"errors" |
|
|
"os" |
|
|
"path/filepath" |
|
|
|
|
|
"github.com/mudler/LocalAI/core/config" |
|
|
. "github.com/mudler/LocalAI/core/gallery" |
|
|
"github.com/mudler/LocalAI/pkg/system" |
|
|
. "github.com/onsi/ginkgo/v2" |
|
|
. "github.com/onsi/gomega" |
|
|
"gopkg.in/yaml.v3" |
|
|
) |
|
|
|
|
|
const bertEmbeddingsURL = `https://gist.githubusercontent.com/mudler/0a080b166b87640e8644b09c2aee6e3b/raw/f0e8c26bb72edc16d9fbafbfd6638072126ff225/bert-embeddings-gallery.yaml` |
|
|
|
|
|
var _ = Describe("Model test", func() { |
|
|
|
|
|
BeforeEach(func() { |
|
|
if os.Getenv("FIXTURES") == "" { |
|
|
Skip("FIXTURES env var not set, skipping model tests") |
|
|
} |
|
|
}) |
|
|
|
|
|
Context("Downloading", func() { |
|
|
It("applies model correctly", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
c, err := ReadConfigFile[ModelConfig](filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = InstallModel(context.TODO(), systemState, "", c, map[string]interface{}{}, func(string, string, string, float64) {}, true) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "cerebras.yaml"} { |
|
|
_, err = os.Stat(filepath.Join(tempdir, f)) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
} |
|
|
|
|
|
content := map[string]interface{}{} |
|
|
|
|
|
dat, err := os.ReadFile(filepath.Join(tempdir, "cerebras.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
err = yaml.Unmarshal(dat, content) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
Expect(content["context_size"]).To(Equal(1024)) |
|
|
}) |
|
|
|
|
|
It("applies model from gallery correctly", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
|
|
|
gallery := []GalleryModel{{ |
|
|
Metadata: Metadata{ |
|
|
Name: "bert", |
|
|
URL: bertEmbeddingsURL, |
|
|
}, |
|
|
}} |
|
|
out, err := yaml.Marshal(gallery) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
galleryFilePath := filepath.Join(tempdir, "gallery_simple.yaml") |
|
|
err = os.WriteFile(galleryFilePath, out, 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
Expect(filepath.IsAbs(galleryFilePath)).To(BeTrue(), galleryFilePath) |
|
|
galleries := []config.Gallery{ |
|
|
{ |
|
|
Name: "test", |
|
|
URL: "file://" + galleryFilePath, |
|
|
}, |
|
|
} |
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
models, err := AvailableGalleryModels(galleries, systemState) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
Expect(len(models)).To(Equal(1)) |
|
|
Expect(models[0].Name).To(Equal("bert")) |
|
|
Expect(models[0].URL).To(Equal(bertEmbeddingsURL)) |
|
|
Expect(models[0].Installed).To(BeFalse()) |
|
|
|
|
|
err = InstallModelFromGallery(context.TODO(), galleries, []config.Gallery{}, systemState, nil, "test@bert", GalleryModel{}, func(s1, s2, s3 string, f float64) {}, true, true) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
dat, err := os.ReadFile(filepath.Join(tempdir, "bert.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
content := map[string]interface{}{} |
|
|
err = yaml.Unmarshal(dat, &content) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
Expect(content["usage"]).To(ContainSubstring("You can test this model with curl like this")) |
|
|
|
|
|
models, err = AvailableGalleryModels(galleries, systemState) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
Expect(len(models)).To(Equal(1)) |
|
|
Expect(models[0].Installed).To(BeTrue()) |
|
|
|
|
|
|
|
|
err = DeleteModelFromSystem(systemState, "bert") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
models, err = AvailableGalleryModels(galleries, systemState) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
Expect(len(models)).To(Equal(1)) |
|
|
Expect(models[0].Installed).To(BeFalse()) |
|
|
|
|
|
_, err = os.Stat(filepath.Join(tempdir, "bert.yaml")) |
|
|
Expect(err).To(HaveOccurred()) |
|
|
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue()) |
|
|
}) |
|
|
|
|
|
It("renames model correctly", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
c, err := ReadConfigFile[ModelConfig](filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = InstallModel(context.TODO(), systemState, "foo", c, map[string]interface{}{}, func(string, string, string, float64) {}, true) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "foo.yaml"} { |
|
|
_, err = os.Stat(filepath.Join(tempdir, f)) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
} |
|
|
}) |
|
|
|
|
|
It("overrides parameters", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
c, err := ReadConfigFile[ModelConfig](filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = InstallModel(context.TODO(), systemState, "foo", c, map[string]interface{}{"backend": "foo"}, func(string, string, string, float64) {}, true) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "foo.yaml"} { |
|
|
_, err = os.Stat(filepath.Join(tempdir, f)) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
} |
|
|
|
|
|
content := map[string]interface{}{} |
|
|
|
|
|
dat, err := os.ReadFile(filepath.Join(tempdir, "foo.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
err = yaml.Unmarshal(dat, content) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
Expect(content["backend"]).To(Equal("foo")) |
|
|
}) |
|
|
|
|
|
It("catches path traversals", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
c, err := ReadConfigFile[ModelConfig](filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = InstallModel(context.TODO(), systemState, "../../../foo", c, map[string]interface{}{}, func(string, string, string, float64) {}, true) |
|
|
Expect(err).To(HaveOccurred()) |
|
|
}) |
|
|
|
|
|
It("handles nil configOverrides without panic", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
c, err := ReadConfigFile[ModelConfig](filepath.Join(os.Getenv("FIXTURES"), "gallery_simple.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = InstallModel(context.TODO(), systemState, "test-model", c, nil, func(string, string, string, float64) {}, true) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
for _, f := range []string{"cerebras", "cerebras-completion.tmpl", "cerebras-chat.tmpl", "test-model.yaml"} { |
|
|
_, err = os.Stat(filepath.Join(tempdir, f)) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
} |
|
|
}) |
|
|
|
|
|
It("does not delete shared model files when one config is deleted", func() { |
|
|
tempdir, err := os.MkdirTemp("", "test") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
defer os.RemoveAll(tempdir) |
|
|
|
|
|
systemState, err := system.GetSystemState( |
|
|
system.WithModelPath(tempdir), |
|
|
) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
sharedModelFile := filepath.Join(tempdir, "shared_model.bin") |
|
|
err = os.WriteFile(sharedModelFile, []byte("fake model content"), 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
config1 := `name: model1 |
|
|
model: shared_model.bin` |
|
|
err = os.WriteFile(filepath.Join(tempdir, "model1.yaml"), []byte(config1), 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
galleryConfig1 := ModelConfig{ |
|
|
Name: "model1", |
|
|
Files: []File{ |
|
|
{Filename: "shared_model.bin"}, |
|
|
}, |
|
|
} |
|
|
galleryData1, err := yaml.Marshal(galleryConfig1) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
err = os.WriteFile(filepath.Join(tempdir, "._gallery_model1.yaml"), galleryData1, 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
config2 := `name: model2 |
|
|
model: shared_model.bin` |
|
|
err = os.WriteFile(filepath.Join(tempdir, "model2.yaml"), []byte(config2), 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
galleryConfig2 := ModelConfig{ |
|
|
Name: "model2", |
|
|
Files: []File{ |
|
|
{Filename: "shared_model.bin"}, |
|
|
}, |
|
|
} |
|
|
galleryData2, err := yaml.Marshal(galleryConfig2) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
err = os.WriteFile(filepath.Join(tempdir, "._gallery_model2.yaml"), galleryData2, 0600) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(filepath.Join(tempdir, "model1.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(sharedModelFile) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
err = DeleteModelFromSystem(systemState, "model1") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(filepath.Join(tempdir, "model1.yaml")) |
|
|
Expect(err).To(HaveOccurred()) |
|
|
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(sharedModelFile) |
|
|
Expect(err).ToNot(HaveOccurred(), "shared model file should not be deleted when used by other configs") |
|
|
|
|
|
|
|
|
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml")) |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
err = DeleteModelFromSystem(systemState, "model2") |
|
|
Expect(err).ToNot(HaveOccurred()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml")) |
|
|
Expect(err).To(HaveOccurred()) |
|
|
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue()) |
|
|
|
|
|
|
|
|
_, err = os.Stat(sharedModelFile) |
|
|
Expect(err).To(HaveOccurred(), "shared model file should be deleted when no configs reference it") |
|
|
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue()) |
|
|
}) |
|
|
}) |
|
|
}) |
|
|
|