Skip to content

Commit

Permalink
feat(ui): paginate model gallery (mudler#4886)
Browse files Browse the repository at this point in the history
Signed-off-by: Ettore Di Giacinto <[email protected]>
  • Loading branch information
mudler authored Feb 22, 2025
1 parent 5b59b5e commit e9971b1
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 3 deletions.
2 changes: 1 addition & 1 deletion core/gallery/gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func FindModel(models []*GalleryModel, name string, basePath string) *GalleryMod
// List available models
// Models galleries are a list of yaml files that are hosted on a remote server (for example github).
// Each yaml file contains a list of models that can be downloaded and optionally overrides to define a new model setting.
func AvailableGalleryModels(galleries []config.Gallery, basePath string) ([]*GalleryModel, error) {
func AvailableGalleryModels(galleries []config.Gallery, basePath string) (GalleryModels, error) {
var models []*GalleryModel

// Get models from galleries
Expand Down
12 changes: 12 additions & 0 deletions core/gallery/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,15 @@ func (gm GalleryModels) FindByName(name string) *GalleryModel {
}
return nil
}

func (gm GalleryModels) Paginate(pageNum int, itemsNum int) GalleryModels {
start := (pageNum - 1) * itemsNum
end := start + itemsNum
if start > len(gm) {
start = len(gm)
}
if end > len(gm) {
end = len(gm)
}
return gm[start:end]
}
76 changes: 75 additions & 1 deletion core/http/routes/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package routes
import (
"fmt"
"html/template"
"math"
"sort"
"strconv"
"strings"

"github.com/mudler/LocalAI/core/config"
Expand Down Expand Up @@ -126,6 +128,8 @@ func RegisterUIRoutes(app *fiber.App,
// Show the Models page (all models)
app.Get("/browse", func(c *fiber.Ctx) error {
term := c.Query("term")
page := c.Query("page")
items := c.Query("items")

models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath)

Expand Down Expand Up @@ -164,13 +168,64 @@ func RegisterUIRoutes(app *fiber.App,
// "ApplicationConfig": appConfig,
}

if page == "" {
page = "1"
}

if page != "" {
log.Debug().Msgf("page : %+v\n", page)
// return a subset of the models
pageNum, err := strconv.Atoi(page)
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Invalid page number")
}

if pageNum == 0 {
return c.Render("views/models", summary)
}

itemsNum, err := strconv.Atoi(items)
if err != nil {
itemsNum = 21
}

totalPages := int(math.Ceil(float64(len(models)) / float64(itemsNum)))

models = models.Paginate(pageNum, itemsNum)

log.Debug().Msgf("number of models : %+v\n", len(models))
prevPage := pageNum - 1
nextPage := pageNum + 1
if prevPage < 1 {
prevPage = 1
}
if nextPage > totalPages {
nextPage = totalPages
}
if prevPage != pageNum {
summary["PrevPage"] = prevPage
}
summary["NextPage"] = nextPage
summary["TotalPages"] = totalPages
summary["CurrentPage"] = pageNum
summary["Models"] = template.HTML(elements.ListModels(models, processingModels, galleryService))

log.Debug().Msgf("totalPages : %+v\n", totalPages)
log.Debug().Msgf("prevPage : %+v\n", prevPage)
log.Debug().Msgf("nextPage : %+v\n", nextPage)
log.Debug().Msgf("CurrentPage : %+v\n", pageNum)
}

// Render index
return c.Render("views/models", summary)
})

// Show the models, filtered from the user input
// https://htmx.org/examples/active-search/
app.Post("/browse/search/models", func(c *fiber.Ctx) error {
page := c.Query("page")
items := c.Query("items")

form := struct {
Search string `form:"search"`
}{}
Expand All @@ -180,7 +235,26 @@ func RegisterUIRoutes(app *fiber.App,

models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath)

return c.SendString(elements.ListModels(gallery.GalleryModels(models).Search(form.Search), processingModels, galleryService))
if page != "" {
// return a subset of the models
pageNum, err := strconv.Atoi(page)
if err != nil {
return c.Status(fiber.StatusBadRequest).SendString("Invalid page number")
}

itemsNum, err := strconv.Atoi(items)
if err != nil {
itemsNum = 21
}

models = models.Paginate(pageNum, itemsNum)
}

if form.Search != "" {
models = models.Search(form.Search)
}

return c.SendString(elements.ListModels(models, processingModels, galleryService))
})

/*
Expand Down
30 changes: 29 additions & 1 deletion core/http/views/models.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,37 @@ <h2>Filter by type:</h2>
hx-indicator=".htmx-indicator">

<div id="search-results">{{.Models}}</div>
<!-- Pagination -->
<div class="flex justify-center mt-5">
<div class="flex items
-center">
<button onclick="window.location.href='browse?page={{.PrevPage}}'" class="bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-gray-200 px-3 py-1 rounded-l-md" {{if not .PrevPage}}disabled{{end}}
><i class="fas fa-arrow-left"></i></button>
<button onclick="window.location.href='browse?page={{.NextPage}}'" class="bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-gray-200 px-3 py-1 rounded-r-md" {{if not .NextPage}}disabled{{end}}
><i class="fas fa-arrow-right"></i></button>
<!--
TODO: do not refresh the page, but use htmx.
This however requires the page calculation to be here instead that in the golang backend.
<button class="bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-gray-200 px-3 py-1 rounded-l-md"
hx-post="browse/search/models?page={{.PrevPage}}"
hx-target="#search-results"
hx-indicator=".htmx-indicator"
{{if not .PrevPage}}disabled{{end}}
>
<i class="fas fa-arrow-left"></i>
</button>
<button class="bg-gray-800 text-gray-300 hover:bg-gray-700 hover:text-gray-200 px-3 py-1 rounded-r-md"
hx-post="browse/search/models?page={{.NextPage}}"
hx-target="#search-results"
hx-indicator=".htmx-indicator"
{{if not .NextPage}}disabled{{end}}
>
<i class="fas fa-arrow-right"></i>
</button>
-->
</div>
</div>
</div>

{{template "views/partials/footer" .}}
</div>

Expand Down

0 comments on commit e9971b1

Please sign in to comment.