Skip to content
Merged
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
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 @@
import (
"fmt"
"html/template"
"math"
"sort"
"strconv"
"strings"

"github.com/mudler/LocalAI/core/config"
Expand Down Expand Up @@ -126,6 +128,8 @@
// 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 @@
// "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 @@

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
Loading