From 4ca17fd618e33ec67596fa5a31735ece3d0d3a67 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 7 May 2024 18:37:06 +0200 Subject: [PATCH] feat(ui): allow to set system prompt for chat Make also the models in the index clickable, and display as table Fixes #2257 Signed-off-by: Ettore Di Giacinto --- core/http/routes/ui.go | 19 +++++++---------- core/http/static/chat.js | 33 +++++++++++++++++++++++++---- core/http/views/chat.html | 19 ++++++++++++++++- core/http/views/index.html | 43 ++++++++++++++++++++++++++------------ pkg/gallery/request.go | 21 ++++++++++++++++++- 5 files changed, 105 insertions(+), 30 deletions(-) diff --git a/core/http/routes/ui.go b/core/http/routes/ui.go index 455647e40e3c..475c1e0216fa 100644 --- a/core/http/routes/ui.go +++ b/core/http/routes/ui.go @@ -33,6 +33,8 @@ func RegisterUIRoutes(app *fiber.App, // Show the Models page (all models) app.Get("/browse", auth, func(c *fiber.Ctx) error { + term := c.Query("term") + models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath) // Get all available tags @@ -47,6 +49,11 @@ func RegisterUIRoutes(app *fiber.App, tags = append(tags, t) } sort.Strings(tags) + + if term != "" { + models = gallery.GalleryModels(models).Search(term) + } + summary := fiber.Map{ "Title": "LocalAI - Models", "Version": internal.PrintableVersion(), @@ -72,17 +79,7 @@ func RegisterUIRoutes(app *fiber.App, models, _ := gallery.AvailableGalleryModels(appConfig.Galleries, appConfig.ModelPath) - filteredModels := []*gallery.GalleryModel{} - for _, m := range models { - if strings.Contains(m.Name, form.Search) || - strings.Contains(m.Description, form.Search) || - strings.Contains(m.Gallery.Name, form.Search) || - strings.Contains(strings.Join(m.Tags, ","), form.Search) { - filteredModels = append(filteredModels, m) - } - } - - return c.SendString(elements.ListModels(filteredModels, installingModels)) + return c.SendString(elements.ListModels(gallery.GalleryModels(models).Search(form.Search), installingModels)) }) /* diff --git a/core/http/static/chat.js b/core/http/static/chat.js index db7e7856d099..60cf12d75d4a 100644 --- a/core/http/static/chat.js +++ b/core/http/static/chat.js @@ -26,11 +26,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + function submitKey(event) { event.preventDefault(); localStorage.setItem("key", document.getElementById("apiKey").value); document.getElementById("apiKey").blur(); - } +} + +function submitSystemPrompt(event) { + event.preventDefault(); + localStorage.setItem("system_prompt", document.getElementById("systemPrompt").value); + document.getElementById("systemPrompt").blur(); +} function submitPrompt(event) { event.preventDefault(); @@ -39,12 +46,13 @@ function submitPrompt(event) { Alpine.store("chat").add("user", input); document.getElementById("input").value = ""; const key = localStorage.getItem("key"); + const systemPrompt = localStorage.getItem("system_prompt"); - promptGPT(key, input); + promptGPT(systemPrompt, key, input); } - async function promptGPT(key, input) { + async function promptGPT(systemPrompt, key, input) { const model = document.getElementById("chat-model").value; // Set class "loader" to the element with "loader" id //document.getElementById("loader").classList.add("loader"); @@ -53,6 +61,16 @@ function submitPrompt(event) { document.getElementById("input").disabled = true; document.getElementById('messages').scrollIntoView(false) + messages = Alpine.store("chat").messages(); + + // if systemPrompt isn't empty, push it at the start of messages + if (systemPrompt) { + messages.unshift({ + role: "system", + content: systemPrompt + }); + } + // Source: https://stackoverflow.com/a/75751803/11386095 const response = await fetch("/v1/chat/completions", { method: "POST", @@ -62,7 +80,7 @@ function submitPrompt(event) { }, body: JSON.stringify({ model: model, - messages: Alpine.store("chat").messages(), + messages: messages, stream: true, }), }); @@ -122,6 +140,8 @@ function submitPrompt(event) { } document.getElementById("key").addEventListener("submit", submitKey); + document.getElementById("system_prompt").addEventListener("submit", submitSystemPrompt); + document.getElementById("prompt").addEventListener("submit", submitPrompt); document.getElementById("input").focus(); @@ -129,6 +149,11 @@ function submitPrompt(event) { if (storeKey) { document.getElementById("apiKey").value = storeKey; } + + const storesystemPrompt = localStorage.getItem("system_prompt"); + if (storesystemPrompt) { + document.getElementById("systemPrompt").value = storesystemPrompt; + } marked.setOptions({ highlight: function (code) { diff --git a/core/http/views/chat.html b/core/http/views/chat.html index eebf90837b06..164d6eabd9b3 100644 --- a/core/http/views/chat.html +++ b/core/http/views/chat.html @@ -62,17 +62,34 @@

Chat wit +
+
+
+ +
diff --git a/core/http/views/index.html b/core/http/views/index.html index f8cae175ca73..e9edaff04122 100644 --- a/core/http/views/index.html +++ b/core/http/views/index.html @@ -11,7 +11,6 @@

Welcome to your LocalAI instance!

-

The FOSS alternative to OpenAI, Claude, ...

@@ -19,29 +18,46 @@

Welcome to your LocalAI inst

-
+
{{ if eq (len .ModelsConfig) 0 }}

Ouch! seems you don't have any models installed!

..install something from the 🖼️ Gallery or check the Getting started documentation

{{ else }} +

Installed models

We have {{len .ModelsConfig}} pre-loaded models available.

-
    + + + + + + + + + + {{$galleryConfig:=.GalleryConfig}} + {{$noicon:="https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg"}} {{ range .ModelsConfig }} {{ $cfg:= index $galleryConfig .Name}} -
  • -
    - +
  • + + + + {{ end }} - + +
    Model NameBackendActions
    + {{ with $cfg }} - -

    {{.Name}}

    + {{ else}} + + {{ end }} +
    +

    {{.Name}}

    +
    {{ if .Backend }} @@ -52,15 +68,16 @@

    Installed models {{ end }} +

    - - - +
    {{ end }}
diff --git a/pkg/gallery/request.go b/pkg/gallery/request.go index cd89395ae832..b6efc2ffe1f0 100644 --- a/pkg/gallery/request.go +++ b/pkg/gallery/request.go @@ -1,6 +1,9 @@ package gallery -import "fmt" +import ( + "fmt" + "strings" +) // GalleryModel is the struct used to represent a model in the gallery returned by the endpoint. // It is used to install the model by resolving the URL and downloading the files. @@ -28,3 +31,19 @@ type GalleryModel struct { func (m GalleryModel) ID() string { return fmt.Sprintf("%s@%s", m.Gallery.Name, m.Name) } + +type GalleryModels []*GalleryModel + +func (gm GalleryModels) Search(term string) GalleryModels { + var filteredModels GalleryModels + + for _, m := range gm { + if strings.Contains(m.Name, term) || + strings.Contains(m.Description, term) || + strings.Contains(m.Gallery.Name, term) || + strings.Contains(strings.Join(m.Tags, ","), term) { + filteredModels = append(filteredModels, m) + } + } + return filteredModels +}