From bf4f8da26640dbdadbe7739d2d56ac45272fb7e8 Mon Sep 17 00:00:00 2001 From: "LocalAI [bot]" <139863280+localai-bot@users.noreply.github.com> Date: Wed, 11 Mar 2026 10:28:37 +0100 Subject: [PATCH] fix: include model name in mmproj file path to prevent model isolation (#8937) (#8940) * fix: include model name in mmproj file path to prevent model isolation issues This fix addresses issue #8937 where different models with mmproj files having the same filename (e.g., mmproj-F32.gguf) would overwrite each other. By including the model name in the path (llama-cpp/mmproj//), each model's mmproj files are now stored in separate directories, preventing the collision that caused conversations to fail when switching between models. Fixes #8937 Signed-off-by: LocalAI Bot * test: update test expectations for model name in mmproj path The test file had hardcoded expectations for the old mmproj path format. Updated the test expectations to include the model name subdirectory to match the new path structure introduced in the fix. Fixes CI failures on tests-apple and tests-linux * fix: add model name to model path for consistency with mmproj path This change makes the model path consistent with the mmproj path by including the model name subdirectory in both paths: - mmproj: llama-cpp/mmproj// - model: llama-cpp/models// This addresses the reviewer's feedback that the model config generation needs to correctly reference the mmproj file path. Fixes the issue where the model path didn't include the model name subdirectory while the mmproj path did. Signed-off-by: team-coding-agent-1 --------- Signed-off-by: LocalAI Bot Signed-off-by: team-coding-agent-1 Co-authored-by: team-coding-agent-1 --- core/gallery/importers/importers_test.go | 18 +++++++++--------- core/gallery/importers/llama-cpp.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/gallery/importers/importers_test.go b/core/gallery/importers/importers_test.go index c009e51da..c0b848074 100644 --- a/core/gallery/importers/importers_test.go +++ b/core/gallery/importers/importers_test.go @@ -25,7 +25,7 @@ var _ = Describe("DiscoverModelConfig", func() { Expect(modelConfig.Description).To(Equal("Imported from https://huggingface.co/mudler/LocalAI-functioncall-qwen2.5-7b-v0.5-Q4_K_M-GGUF"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.ConfigFile).To(ContainSubstring("backend: llama-cpp"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(len(modelConfig.Files)).To(Equal(1), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/localai-functioncall-qwen2.5-7b-v0.5-q4_k_m.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/LocalAI-functioncall-qwen2.5-7b-v0.5-Q4_K_M-GGUF/localai-functioncall-qwen2.5-7b-v0.5-q4_k_m.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].URI).To(Equal("https://huggingface.co/mudler/LocalAI-functioncall-qwen2.5-7b-v0.5-Q4_K_M-GGUF/resolve/main/localai-functioncall-qwen2.5-7b-v0.5-q4_k_m.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].SHA256).To(Equal("4e7b7fe1d54b881f1ef90799219dc6cc285d29db24f559c8998d1addb35713d4"), fmt.Sprintf("Model config: %+v", modelConfig)) }) @@ -40,13 +40,13 @@ var _ = Describe("DiscoverModelConfig", func() { Expect(modelConfig.Name).To(Equal("Qwen3-VL-2B-Instruct-GGUF"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Description).To(Equal("Imported from https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.ConfigFile).To(ContainSubstring("backend: llama-cpp"), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.ConfigFile).To(ContainSubstring("mmproj: llama-cpp/mmproj/mmproj-Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.ConfigFile).To(ContainSubstring("model: llama-cpp/models/Qwen3VL-2B-Instruct-Q4_K_M.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.ConfigFile).To(ContainSubstring("mmproj: llama-cpp/mmproj/Qwen3-VL-2B-Instruct-GGUF/mmproj-Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.ConfigFile).To(ContainSubstring("model: llama-cpp/models/Qwen3-VL-2B-Instruct-GGUF/Qwen3VL-2B-Instruct-Q4_K_M.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(len(modelConfig.Files)).To(Equal(2), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/Qwen3VL-2B-Instruct-Q4_K_M.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/Qwen3-VL-2B-Instruct-GGUF/Qwen3VL-2B-Instruct-Q4_K_M.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].URI).To(Equal("https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF/resolve/main/Qwen3VL-2B-Instruct-Q4_K_M.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].SHA256).ToNot(BeEmpty(), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.Files[1].Filename).To(Equal("llama-cpp/mmproj/mmproj-Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.Files[1].Filename).To(Equal("llama-cpp/mmproj/Qwen3-VL-2B-Instruct-GGUF/mmproj-Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[1].URI).To(Equal("https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF/resolve/main/mmproj-Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[1].SHA256).ToNot(BeEmpty(), fmt.Sprintf("Model config: %+v", modelConfig)) }) @@ -61,13 +61,13 @@ var _ = Describe("DiscoverModelConfig", func() { Expect(modelConfig.Name).To(Equal("Qwen3-VL-2B-Instruct-GGUF"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Description).To(Equal("Imported from https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.ConfigFile).To(ContainSubstring("backend: llama-cpp"), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.ConfigFile).To(ContainSubstring("mmproj: llama-cpp/mmproj/mmproj-Qwen3VL-2B-Instruct-F16.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.ConfigFile).To(ContainSubstring("model: llama-cpp/models/Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.ConfigFile).To(ContainSubstring("mmproj: llama-cpp/mmproj/Qwen3-VL-2B-Instruct-GGUF/mmproj-Qwen3VL-2B-Instruct-F16.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.ConfigFile).To(ContainSubstring("model: llama-cpp/models/Qwen3-VL-2B-Instruct-GGUF/Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(len(modelConfig.Files)).To(Equal(2), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.Files[0].Filename).To(Equal("llama-cpp/models/Qwen3-VL-2B-Instruct-GGUF/Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].URI).To(Equal("https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF/resolve/main/Qwen3VL-2B-Instruct-Q8_0.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[0].SHA256).ToNot(BeEmpty(), fmt.Sprintf("Model config: %+v", modelConfig)) - Expect(modelConfig.Files[1].Filename).To(Equal("llama-cpp/mmproj/mmproj-Qwen3VL-2B-Instruct-F16.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) + Expect(modelConfig.Files[1].Filename).To(Equal("llama-cpp/mmproj/Qwen3-VL-2B-Instruct-GGUF/mmproj-Qwen3VL-2B-Instruct-F16.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[1].URI).To(Equal("https://huggingface.co/Qwen/Qwen3-VL-2B-Instruct-GGUF/resolve/main/mmproj-Qwen3VL-2B-Instruct-F16.gguf"), fmt.Sprintf("Model config: %+v", modelConfig)) Expect(modelConfig.Files[1].SHA256).ToNot(BeEmpty(), fmt.Sprintf("Model config: %+v", modelConfig)) }) diff --git a/core/gallery/importers/llama-cpp.go b/core/gallery/importers/llama-cpp.go index ae9ec042d..f0d5915c5 100644 --- a/core/gallery/importers/llama-cpp.go +++ b/core/gallery/importers/llama-cpp.go @@ -184,7 +184,7 @@ func (i *LlamaCPPImporter) Import(details Details) (gallery.ModelConfig, error) if strings.Contains(strings.ToLower(file.Path), "mmproj") { lastMMProjFile = &gallery.File{ URI: file.URL, - Filename: filepath.Join("llama-cpp", "mmproj", filepath.Base(file.Path)), + Filename: filepath.Join("llama-cpp", "mmproj", name, filepath.Base(file.Path)), SHA256: file.SHA256, } if slices.ContainsFunc(mmprojQuantsList, func(quant string) bool { @@ -196,7 +196,7 @@ func (i *LlamaCPPImporter) Import(details Details) (gallery.ModelConfig, error) } else if strings.HasSuffix(strings.ToLower(file.Path), "gguf") { lastGGUFFile = &gallery.File{ URI: file.URL, - Filename: filepath.Join("llama-cpp", "models", filepath.Base(file.Path)), + Filename: filepath.Join("llama-cpp", "models", name, filepath.Base(file.Path)), SHA256: file.SHA256, } // get the files of the prefered quants