fix(gallery): be consistent and disable UI routes as well (#3262)

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto 2024-08-18 09:26:29 +02:00 committed by GitHub
parent 5d416006ae
commit 1dbb3b8abc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -119,185 +119,188 @@ func RegisterUIRoutes(app *fiber.App,
}) })
} }
// Show the Models page (all models) if !appConfig.DisableGalleryEndpoint {
app.Get("/browse", auth, func(c *fiber.Ctx) error {
term := c.Query("term")
models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath) // Show the Models page (all models)
app.Get("/browse", auth, func(c *fiber.Ctx) error {
term := c.Query("term")
// Get all available tags models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath)
allTags := map[string]struct{}{}
tags := []string{} // Get all available tags
for _, m := range models { allTags := map[string]struct{}{}
for _, t := range m.Tags { tags := []string{}
allTags[t] = struct{}{} for _, m := range models {
for _, t := range m.Tags {
allTags[t] = struct{}{}
}
} }
} for t := range allTags {
for t := range allTags { tags = append(tags, t)
tags = append(tags, t) }
} sort.Strings(tags)
sort.Strings(tags)
if term != "" { if term != "" {
models = gallery.GalleryModels(models).Search(term) models = gallery.GalleryModels(models).Search(term)
} }
// Get model statuses // Get model statuses
processingModelsData, taskTypes := modelStatus() processingModelsData, taskTypes := modelStatus()
summary := fiber.Map{ summary := fiber.Map{
"Title": "LocalAI - Models", "Title": "LocalAI - Models",
"Version": internal.PrintableVersion(), "Version": internal.PrintableVersion(),
"Models": template.HTML(elements.ListModels(models, processingModels, galleryService)), "Models": template.HTML(elements.ListModels(models, processingModels, galleryService)),
"Repositories": appConfig.Galleries, "Repositories": appConfig.Galleries,
"AllTags": tags, "AllTags": tags,
"ProcessingModels": processingModelsData, "ProcessingModels": processingModelsData,
"AvailableModels": len(models), "AvailableModels": len(models),
"IsP2PEnabled": p2p.IsP2PEnabled(), "IsP2PEnabled": p2p.IsP2PEnabled(),
"TaskTypes": taskTypes, "TaskTypes": taskTypes,
// "ApplicationConfig": appConfig, // "ApplicationConfig": appConfig,
} }
// Render index // Render index
return c.Render("views/models", summary) return c.Render("views/models", summary)
}) })
// Show the models, filtered from the user input // Show the models, filtered from the user input
// https://htmx.org/examples/active-search/ // https://htmx.org/examples/active-search/
app.Post("/browse/search/models", auth, func(c *fiber.Ctx) error { app.Post("/browse/search/models", auth, func(c *fiber.Ctx) error {
form := struct { form := struct {
Search string `form:"search"` Search string `form:"search"`
}{} }{}
if err := c.BodyParser(&form); err != nil { if err := c.BodyParser(&form); err != nil {
return c.Status(fiber.StatusBadRequest).SendString(err.Error()) return c.Status(fiber.StatusBadRequest).SendString(err.Error())
} }
models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath) models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath)
return c.SendString(elements.ListModels(gallery.GalleryModels(models).Search(form.Search), processingModels, galleryService)) return c.SendString(elements.ListModels(gallery.GalleryModels(models).Search(form.Search), processingModels, galleryService))
}) })
/* /*
Install routes Install routes
*/ */
// This route is used when the "Install" button is pressed, we submit here a new job to the gallery service // 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/ // https://htmx.org/examples/progress-bar/
app.Post("/browse/install/model/:id", auth, func(c *fiber.Ctx) error { app.Post("/browse/install/model/:id", auth, func(c *fiber.Ctx) error {
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests! galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
log.Debug().Msgf("UI job submitted to install : %+v\n", galleryID) log.Debug().Msgf("UI job submitted to install : %+v\n", galleryID)
id, err := uuid.NewUUID() id, err := uuid.NewUUID()
if err != nil { if err != nil {
return err return err
} }
uid := id.String() uid := id.String()
processingModels.Set(galleryID, uid) processingModels.Set(galleryID, uid)
op := gallery.GalleryOp{ op := gallery.GalleryOp{
Id: uid, Id: uid,
GalleryModelName: galleryID, GalleryModelName: galleryID,
Galleries: appConfig.Galleries, Galleries: appConfig.Galleries,
} }
go func() { go func() {
galleryService.C <- op galleryService.C <- op
}() }()
return c.SendString(elements.StartProgressBar(uid, "0", "Installation")) return c.SendString(elements.StartProgressBar(uid, "0", "Installation"))
}) })
// This route is used when the "Install" button is pressed, we submit here a new job to the gallery service // 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/ // https://htmx.org/examples/progress-bar/
app.Post("/browse/delete/model/:id", auth, func(c *fiber.Ctx) error { app.Post("/browse/delete/model/:id", auth, func(c *fiber.Ctx) error {
galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests! galleryID := strings.Clone(c.Params("id")) // note: strings.Clone is required for multiple requests!
log.Debug().Msgf("UI job submitted to delete : %+v\n", galleryID) log.Debug().Msgf("UI job submitted to delete : %+v\n", galleryID)
var galleryName = galleryID var galleryName = galleryID
if strings.Contains(galleryID, "@") { if strings.Contains(galleryID, "@") {
// if the galleryID contains a @ it means that it's a model from a gallery // if the galleryID contains a @ it means that it's a model from a gallery
// but we want to delete it from the local models which does not need // but we want to delete it from the local models which does not need
// a repository ID // a repository ID
galleryName = strings.Split(galleryID, "@")[1] galleryName = strings.Split(galleryID, "@")[1]
} }
id, err := uuid.NewUUID() id, err := uuid.NewUUID()
if err != nil { if err != nil {
return err return err
} }
uid := id.String() uid := id.String()
// Track the deletion job by galleryID and galleryName // Track the deletion job by galleryID and galleryName
// The GalleryID contains information about the repository, // The GalleryID contains information about the repository,
// while the GalleryName is ONLY the name of the model // while the GalleryName is ONLY the name of the model
processingModels.Set(galleryName, uid) processingModels.Set(galleryName, uid)
processingModels.Set(galleryID, uid) processingModels.Set(galleryID, uid)
op := gallery.GalleryOp{ op := gallery.GalleryOp{
Id: uid, Id: uid,
Delete: true, Delete: true,
GalleryModelName: galleryName, GalleryModelName: galleryName,
} }
go func() { go func() {
galleryService.C <- op galleryService.C <- op
cl.RemoveBackendConfig(galleryName) cl.RemoveBackendConfig(galleryName)
}() }()
return c.SendString(elements.StartProgressBar(uid, "0", "Deletion")) return c.SendString(elements.StartProgressBar(uid, "0", "Deletion"))
}) })
// Display the job current progress status // Display the job current progress status
// If the job is done, we trigger the /browse/job/:uid route // If the job is done, we trigger the /browse/job/:uid route
// https://htmx.org/examples/progress-bar/ // https://htmx.org/examples/progress-bar/
app.Get("/browse/job/progress/:uid", auth, func(c *fiber.Ctx) error { app.Get("/browse/job/progress/:uid", auth, func(c *fiber.Ctx) error {
jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests! jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests!
status := galleryService.GetStatus(jobUID) status := galleryService.GetStatus(jobUID)
if status == nil { if status == nil {
//fmt.Errorf("could not find any status for ID") //fmt.Errorf("could not find any status for ID")
return c.SendString(elements.ProgressBar("0")) return c.SendString(elements.ProgressBar("0"))
} }
if status.Progress == 100 { if status.Progress == 100 {
c.Set("HX-Trigger", "done") // this triggers /browse/job/:uid (which is when the job is done) c.Set("HX-Trigger", "done") // this triggers /browse/job/:uid (which is when the job is done)
return c.SendString(elements.ProgressBar("100")) return c.SendString(elements.ProgressBar("100"))
} }
if status.Error != nil { if status.Error != nil {
// TODO: instead of deleting the job, we should keep it in the cache and make it dismissable by the user // TODO: instead of deleting the job, we should keep it in the cache and make it dismissable by the user
processingModels.DeleteUUID(jobUID)
return c.SendString(elements.ErrorProgress(status.Error.Error(), status.GalleryModelName))
}
return c.SendString(elements.ProgressBar(fmt.Sprint(status.Progress)))
})
// this route is hit when the job is done, and we display the
// final state (for now just displays "Installation completed")
app.Get("/browse/job/:uid", auth, func(c *fiber.Ctx) error {
jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests!
status := galleryService.GetStatus(jobUID)
galleryID := ""
processingModels.DeleteUUID(jobUID) processingModels.DeleteUUID(jobUID)
return c.SendString(elements.ErrorProgress(status.Error.Error(), status.GalleryModelName)) if galleryID == "" {
} log.Debug().Msgf("no processing model found for job : %+v\n", jobUID)
}
return c.SendString(elements.ProgressBar(fmt.Sprint(status.Progress))) log.Debug().Msgf("JOB finished : %+v\n", status)
}) showDelete := true
displayText := "Installation completed"
if status.Deletion {
showDelete = false
displayText = "Deletion completed"
}
// this route is hit when the job is done, and we display the return c.SendString(elements.DoneProgress(galleryID, displayText, showDelete))
// final state (for now just displays "Installation completed") })
app.Get("/browse/job/:uid", auth, func(c *fiber.Ctx) error { }
jobUID := strings.Clone(c.Params("uid")) // note: strings.Clone is required for multiple requests!
status := galleryService.GetStatus(jobUID)
galleryID := ""
processingModels.DeleteUUID(jobUID)
if galleryID == "" {
log.Debug().Msgf("no processing model found for job : %+v\n", jobUID)
}
log.Debug().Msgf("JOB finished : %+v\n", status)
showDelete := true
displayText := "Installation completed"
if status.Deletion {
showDelete = false
displayText = "Deletion completed"
}
return c.SendString(elements.DoneProgress(galleryID, displayText, showDelete))
})
// Show the Chat page // Show the Chat page
app.Get("/chat/:model", auth, func(c *fiber.Ctx) error { app.Get("/chat/:model", auth, func(c *fiber.Ctx) error {