Skip to content
Open
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
4 changes: 2 additions & 2 deletions core/gallery/empty_base_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ var _ = Describe("InstallModelFromGallery with an empty base config", func() {
Expect(install(e.Name, gallery.GalleryModel{})).To(Succeed())
cfg := installedConfig(e.Name)
Expect(cfg["name"]).To(Equal(e.Name))
// The catalog's own overrides, verbatim, laid over the empty base.
Expect(cfg["parameters"]).To(Equal(e.Overrides["parameters"]))
// The catalog's model override survives inference-default enrichment.
Expect(cfg["parameters"]).To(HaveKeyWithValue("model", "LiquidAI_LFM2-1.2B-RAG-Q4_K_M.gguf"))
Expect(cfg["known_usecases"]).To(Equal(e.Overrides["known_usecases"]))
})
})
90 changes: 90 additions & 0 deletions core/gallery/inference_defaults_install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package gallery_test

import (
"context"
"os"
"path/filepath"

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

"github.com/mudler/LocalAI/core/gallery"
"github.com/mudler/LocalAI/pkg/modelartifacts"
"github.com/mudler/LocalAI/pkg/system"
)

var _ = Describe("gallery inference defaults", func() {
readPersistedConfig := func(modelsPath, name string) map[string]any {
data, err := os.ReadFile(filepath.Join(modelsPath, name+".yaml"))
Expect(err).NotTo(HaveOccurred())
persisted := map[string]any{}
Expect(yaml.Unmarshal(data, &persisted)).To(Succeed())
return persisted
}

expectNestedDefaults := func(persisted map[string]any) {
Expect(persisted).NotTo(HaveKey("temperature"))
Expect(persisted).NotTo(HaveKey("top_p"))
parameters, ok := persisted["parameters"].(map[string]any)
Expect(ok).To(BeTrue())
Expect(parameters).To(HaveKeyWithValue("temperature", 0.7))
Expect(parameters).To(HaveKeyWithValue("top_p", 0.42))
Expect(parameters).To(HaveKeyWithValue("top_k", 20))
Expect(parameters).To(HaveKeyWithValue("min_p", 0))
Expect(parameters).To(HaveKeyWithValue("repeat_penalty", 1))
Expect(parameters).To(HaveKeyWithValue("presence_penalty", 1.5))
}

It("persists defaults under parameters after artifact binding", func() {
modelsPath := GinkgoT().TempDir()
state, err := system.GetSystemState(system.WithModelPath(modelsPath))
Expect(err).NotTo(HaveOccurred())
resolved := modelartifacts.Spec{
Name: "model", Target: "model",
Source: modelartifacts.Source{Type: "huggingface", Repo: "owner/qwen3.5-model", Revision: "main"},
Resolved: &modelartifacts.Resolved{
Endpoint: "https://huggingface.co",
Revision: "0123456789abcdef0123456789abcdef01234567",
CacheKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
},
}
fake := &fakeArtifactMaterializer{result: modelartifacts.Result{Spec: resolved}}
definition := &gallery.ModelConfig{Name: "qwen3.5-artifact", ConfigFile: `
backend: transformers
artifacts:
- name: model
target: model
source: {type: huggingface, repo: owner/qwen3.5-model}
parameters:
model: owner/qwen3.5-model
top_p: 0.42
`}

_, err = gallery.InstallModel(context.Background(), state, "", definition, nil, nil, false,
gallery.WithArtifactMaterializer(fake))
Expect(err).NotTo(HaveOccurred())
expectNestedDefaults(readPersistedConfig(modelsPath, definition.Name))
})

It("persists defaults under parameters when the entry declares files", func() {
modelsPath := GinkgoT().TempDir()
state, err := system.GetSystemState(system.WithModelPath(modelsPath))
Expect(err).NotTo(HaveOccurred())
Expect(os.WriteFile(filepath.Join(modelsPath, "weights.gguf"), []byte("weights"), 0644)).To(Succeed())
definition := &gallery.ModelConfig{
Name: "qwen3.5-files",
ConfigFile: `
backend: llama-cpp
parameters:
model: weights.gguf
top_p: 0.42
`,
Files: []gallery.File{{Filename: "weights.gguf", URI: "https://example.com/weights.gguf"}},
}

_, err = gallery.InstallModel(context.Background(), state, "", definition, nil, nil, false)
Expect(err).NotTo(HaveOccurred())
expectNestedDefaults(readPersistedConfig(modelsPath, definition.Name))
})
})
40 changes: 23 additions & 17 deletions core/gallery/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,35 +622,41 @@ func InstallModel(ctx context.Context, systemState *system.SystemState, nameOver
lconfig.ApplyInferenceDefaults(&modelConfig, name, modelConfig.Model)

// Merge inference defaults into configMap so they are persisted without losing unknown fields.
defaults := make(map[string]any)
if modelConfig.Temperature != nil {
if _, exists := configMap["temperature"]; !exists {
configMap["temperature"] = *modelConfig.Temperature
}
defaults["temperature"] = *modelConfig.Temperature
}
if modelConfig.TopP != nil {
if _, exists := configMap["top_p"]; !exists {
configMap["top_p"] = *modelConfig.TopP
}
defaults["top_p"] = *modelConfig.TopP
}
if modelConfig.TopK != nil {
if _, exists := configMap["top_k"]; !exists {
configMap["top_k"] = *modelConfig.TopK
}
defaults["top_k"] = *modelConfig.TopK
}
if modelConfig.MinP != nil {
if _, exists := configMap["min_p"]; !exists {
configMap["min_p"] = *modelConfig.MinP
}
defaults["min_p"] = *modelConfig.MinP
}
if modelConfig.RepeatPenalty != 0 {
if _, exists := configMap["repeat_penalty"]; !exists {
configMap["repeat_penalty"] = modelConfig.RepeatPenalty
}
defaults["repeat_penalty"] = modelConfig.RepeatPenalty
}
if modelConfig.PresencePenalty != 0 {
if _, exists := configMap["presence_penalty"]; !exists {
configMap["presence_penalty"] = modelConfig.PresencePenalty
defaults["presence_penalty"] = modelConfig.PresencePenalty
}
if len(defaults) > 0 {
parameters, ok := configMap["parameters"].(map[string]any)
if !ok {
parameters = make(map[string]any)
configMap["parameters"] = parameters
}
for key, value := range defaults {
if _, exists := parameters[key]; !exists {
parameters[key] = value
}
}
}

updatedConfigYAML, err = yaml.Marshal(configMap)
if err != nil {
return nil, fmt.Errorf("failed to marshal config with inference defaults: %v", err)
}

if valid, err := modelConfig.Validate(); !valid {
Expand Down
Loading