From d0b6aa3f7dbafe2bb190fdedb8de5ae55cfb2711 Mon Sep 17 00:00:00 2001 From: Matt Cowger Date: Thu, 28 Aug 2025 09:32:49 -0700 Subject: [PATCH] feat(gallery): Add 'Get Config' button for models (#6154) * feat(gallery): Add 'Get Config' button for models This commit introduces a 'Get Config' button to the model gallery UI. This allows users to download and save the configuration file for a model without installing the model's weights. Key changes: - Added a getConfigButton element and integrated it into the gallery card. - Created a new API endpoint /browse/config/model/:id to handle fetching and saving the model configuration. - Refactored the InstallModel function to allow saving only the configuration file without downloading model weights. - Added a ToYAML method on ModelConfig for serialization. - Fixed button spacing in the gallery UI. Signed-off-by: Matt Cowger * Update for reviewer comments Signed-off-by: Matt Cowger --------- Signed-off-by: Matt Cowger --- core/http/elements/buttons.go | 18 ++++++++++++++++++ core/http/elements/gallery.go | 7 ++++++- core/http/routes/ui_gallery.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/core/http/elements/buttons.go b/core/http/elements/buttons.go index b2ce904be..b6ac018c4 100644 --- a/core/http/elements/buttons.go +++ b/core/http/elements/buttons.go @@ -70,6 +70,24 @@ func infoButton(m *gallery.GalleryModel) elem.Node { ) } +func getConfigButton(galleryName string) elem.Node { + return elem.Button( + attrs.Props{ + "data-twe-ripple-init": "", + "data-twe-ripple-color": "light", + "class": "float-right ml-2 inline-flex items-center rounded-lg bg-gray-700 hover:bg-gray-600 px-4 py-2 text-sm font-medium text-white transition duration-300 ease-in-out", + "hx-swap": "outerHTML", + "hx-post": "browse/config/model/" + galleryName, + }, + elem.I( + attrs.Props{ + "class": "fa-solid fa-download pr-2", + }, + ), + elem.Text("Get Config"), + ) +} + func deleteButton(galleryID string) elem.Node { return elem.Button( attrs.Props{ diff --git a/core/http/elements/gallery.go b/core/http/elements/gallery.go index 02fc6ca51..071dad566 100644 --- a/core/http/elements/gallery.go +++ b/core/http/elements/gallery.go @@ -339,7 +339,12 @@ func modelActionItems(m *gallery.GalleryModel, processTracker ProcessTracker, ga reInstallButton(m.ID()), deleteButton(m.ID()), )), - installButton(m.ID()), + // otherwise, show the install button, and the get config button + elem.Node(elem.Div( + attrs.Props{}, + getConfigButton(m.ID()), + installButton(m.ID()), + )), ), ), ), diff --git a/core/http/routes/ui_gallery.go b/core/http/routes/ui_gallery.go index 47bd5d4c7..3182fea51 100644 --- a/core/http/routes/ui_gallery.go +++ b/core/http/routes/ui_gallery.go @@ -189,6 +189,34 @@ func registerGalleryRoutes(app *fiber.App, cl *config.ModelConfigLoader, appConf return c.SendString(elements.StartModelProgressBar(uid, "0", "Installation")) }) + app.Post("/browse/config/model/:id", func(c *fiber.Ctx) error { + galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests! + log.Debug().Msgf("UI job submitted to get config for : %+v\n", galleryID) + + models, err := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.SystemState) + if err != nil { + return err + } + + model := gallery.FindGalleryElement(models, galleryID) + if model == nil { + return fmt.Errorf("model not found") + } + + config, err := gallery.GetGalleryConfigFromURL[gallery.ModelConfig](model.URL, appConfig.SystemState.Model.ModelsPath) + if err != nil { + return err + } + + // Save the config file + _, err = gallery.InstallModel(appConfig.SystemState, model.Name, &config, model.Overrides, nil, false) + if err != nil { + return err + } + + return c.SendString("Configuration file saved.") + }) + // This route is used when the "Install" button is pressed, we submit here a new job to the gallery service // https://htmx.org/examples/progress-bar/ app.Post("/browse/delete/model/:id", func(c *fiber.Ctx) error {