From 10cbd335a2ec560ddbe72792f0ea66d274c417fc Mon Sep 17 00:00:00 2001 From: mudler Date: Thu, 2 May 2024 00:44:43 +0200 Subject: [PATCH] feat(webui): Add image-gen page Signed-off-by: Ettore Di Giacinto --- core/http/routes/ui.go | 37 +++++++++++ core/http/static/{main.js => chat.js} | 51 +++++--------- core/http/static/general.css | 12 +++- core/http/static/image.js | 96 +++++++++++++++++++++++++++ core/http/static/styles.css | 0 core/http/views/chat.html | 21 ++++-- core/http/views/partials/head.html | 4 -- core/http/views/partials/navbar.html | 1 + core/http/views/text2image.html | 78 ++++++++++++++++++++++ 9 files changed, 254 insertions(+), 46 deletions(-) rename core/http/static/{main.js => chat.js} (82%) create mode 100644 core/http/static/image.js delete mode 100644 core/http/static/styles.css create mode 100644 core/http/views/text2image.html diff --git a/core/http/routes/ui.go b/core/http/routes/ui.go index b6bd5b2626f..c12f40f6505 100644 --- a/core/http/routes/ui.go +++ b/core/http/routes/ui.go @@ -9,6 +9,7 @@ import ( "github.com/go-skynet/LocalAI/core/http/elements" "github.com/go-skynet/LocalAI/core/http/endpoints/localai" "github.com/go-skynet/LocalAI/core/services" + "github.com/go-skynet/LocalAI/internal" "github.com/go-skynet/LocalAI/pkg/gallery" "github.com/go-skynet/LocalAI/pkg/model" "github.com/go-skynet/LocalAI/pkg/xsync" @@ -35,6 +36,7 @@ func RegisterUIRoutes(app *fiber.App, summary := fiber.Map{ "Title": "LocalAI - Models", + "Version": internal.PrintableVersion(), "Models": template.HTML(elements.ListModels(models, installingModels)), "Repositories": appConfig.Galleries, // "ApplicationConfig": appConfig, @@ -178,6 +180,7 @@ func RegisterUIRoutes(app *fiber.App, "Title": "LocalAI - Chat with " + c.Params("model"), "ModelsConfig": backendConfigs, "Model": c.Params("model"), + "Version": internal.PrintableVersion(), } // Render index @@ -195,9 +198,43 @@ func RegisterUIRoutes(app *fiber.App, "Title": "LocalAI - Chat with " + backendConfigs[0].Name, "ModelsConfig": backendConfigs, "Model": backendConfigs[0].Name, + "Version": internal.PrintableVersion(), } // Render index return c.Render("views/chat", summary) }) + + app.Get("/text2image/:model", auth, func(c *fiber.Ctx) error { + backendConfigs := cl.GetAllBackendConfigs() + + summary := fiber.Map{ + "Title": "LocalAI - Generate images with " + c.Params("model"), + "ModelsConfig": backendConfigs, + "Model": c.Params("model"), + "Version": internal.PrintableVersion(), + } + + // Render index + return c.Render("views/text2image", summary) + }) + + app.Get("/text2image/", auth, func(c *fiber.Ctx) error { + + backendConfigs := cl.GetAllBackendConfigs() + + if len(backendConfigs) == 0 { + return c.SendString("No models available") + } + + summary := fiber.Map{ + "Title": "LocalAI - Generate images with " + backendConfigs[0].Name, + "ModelsConfig": backendConfigs, + "Model": backendConfigs[0].Name, + "Version": internal.PrintableVersion(), + } + + // Render index + return c.Render("views/text2image", summary) + }) } diff --git a/core/http/static/main.js b/core/http/static/chat.js similarity index 82% rename from core/http/static/main.js rename to core/http/static/chat.js index 75bf56da1e9..48017d60b91 100644 --- a/core/http/static/main.js +++ b/core/http/static/chat.js @@ -5,6 +5,7 @@ https://github.com/david-haerer/chatapi MIT License Copyright (c) 2023 David Härer +Copyright (c) 2024 Ettore Di Giacinto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -31,40 +32,22 @@ function submitKey(event) { document.getElementById("apiKey").blur(); } - function submitPrompt(event) { - event.preventDefault(); - - const input = document.getElementById("input").value; - Alpine.store("chat").add("user", input); - document.getElementById("input").value = ""; - const key = localStorage.getItem("key"); - - if (input.startsWith("!img")) { - promptDallE(key, input.slice(4)); - } else { - promptGPT(key, input); - } - } - - async function promptDallE(key, input) { - const response = await fetch("/v1/images/generations", { - method: "POST", - headers: { - Authorization: `Bearer ${key}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - model: "dall-e-3", - prompt: input, - n: 1, - size: "1792x1024", - }), - }); - const json = await response.json(); - const url = json.data[0].url; - Alpine.store("chat").add("assistant", `![${input}](${url})`); +function submitPrompt(event) { + event.preventDefault(); + + const input = document.getElementById("input").value; + Alpine.store("chat").add("user", input); + document.getElementById("input").value = ""; + const key = localStorage.getItem("key"); + + if (input.startsWith("!img")) { + promptDallE(key, input.slice(4)); + } else { + promptGPT(key, input); } - +} + + async function promptGPT(key, input) { const model = document.getElementById("chat-model").value; // Set class "loader" to the element with "loader" id @@ -145,7 +128,7 @@ function submitKey(event) { document.getElementById("key").addEventListener("submit", submitKey); document.getElementById("prompt").addEventListener("submit", submitPrompt); document.getElementById("input").focus(); - + const storeKey = localStorage.getItem("key"); if (storeKey) { document.getElementById("apiKey").value = storeKey; diff --git a/core/http/static/general.css b/core/http/static/general.css index 61774f14c84..40d67fb41a6 100644 --- a/core/http/static/general.css +++ b/core/http/static/general.css @@ -60,4 +60,14 @@ body { .assistant { background-color: #28a745; -} \ No newline at end of file +} + +.message { + display: flex; + align-items: center; +} + +.user, .assistant { + flex-grow: 1; + margin: 0.5rem; +} diff --git a/core/http/static/image.js b/core/http/static/image.js new file mode 100644 index 00000000000..315bdda089b --- /dev/null +++ b/core/http/static/image.js @@ -0,0 +1,96 @@ +/* + +https://github.com/david-haerer/chatapi + +MIT License + +Copyright (c) 2023 David Härer +Copyright (c) 2024 Ettore Di Giacinto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +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 genImage(event) { + event.preventDefault(); + const input = document.getElementById("input").value; + const key = localStorage.getItem("key"); + + promptDallE(key, input); + +} + +async function promptDallE(key, input) { + document.getElementById("loader").style.display = "block"; + document.getElementById("input").value = ""; + document.getElementById("input").disabled = true; + + const model = document.getElementById("image-model").value; + const response = await fetch("/v1/images/generations", { + method: "POST", + headers: { + Authorization: `Bearer ${key}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: model, + steps: 10, + prompt: input, + n: 1, + size: "512x512", + }), + }); + const json = await response.json(); + if (json.error) { + // Display error if there is one + var div = document.getElementById('result'); // Get the div by its ID + div.innerHTML = '

' + json.error.message + '

'; + return; + } + const url = json.data[0].url; + + var div = document.getElementById('result'); // Get the div by its ID + var img = document.createElement('img'); // Create a new img element + img.src = url; // Set the source of the image + img.alt = 'Generated image'; // Set the alt text of the image + + div.innerHTML = ''; // Clear the existing content of the div + div.appendChild(img); // Add the new img element to the div + + document.getElementById("loader").style.display = "none"; + document.getElementById("input").disabled = false; + document.getElementById("input").focus(); +} + +document.getElementById("key").addEventListener("submit", submitKey); +document.getElementById("input").focus(); +document.getElementById("genimage").addEventListener("submit", genImage); +document.getElementById("loader").style.display = "none"; + +const storeKey = localStorage.getItem("key"); +if (storeKey) { + document.getElementById("apiKey").value = storeKey; +} + diff --git a/core/http/static/styles.css b/core/http/static/styles.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/core/http/views/chat.html b/core/http/views/chat.html index 59b2af34c5a..17ba282256b 100644 --- a/core/http/views/chat.html +++ b/core/http/views/chat.html @@ -4,6 +4,7 @@ https://github.com/david-haerer/chatapi MIT License Copyright (c) 2023 David Härer + Copyright (c) 2024 Ettore Di Giacinto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,6 +28,7 @@ {{template "views/partials/head" .}} +