mirror of
https://github.com/mudler/LocalAI.git
synced 2025-05-31 07:54:59 +00:00
feat(ui): prompt for chat, support vision, enhancements (#2259)
* 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 <mudler@localai.io> * feat(vision): support also png with base64 input Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * feat(ui): support vision and upload of files Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * display the processed image Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * make trust remote code stand out Signed-off-by: mudler <mudler@localai.io> * feat(ui): track in progress job across index/model gallery Signed-off-by: mudler <mudler@localai.io> * minor fixups Signed-off-by: mudler <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Signed-off-by: mudler <mudler@localai.io>
This commit is contained in:
parent
02ec546dd6
commit
6559ac11b1
11 changed files with 344 additions and 82 deletions
|
@ -62,17 +62,34 @@ SOFTWARE.
|
|||
<button @click="component = 'key'" title="Update API key"
|
||||
class="m-2 float-right inline-block rounded bg-primary px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
||||
>Set API Key🔑</button>
|
||||
<button @click="component = 'system_prompt'" title="System Prompt"
|
||||
class="m-2 float-right inline-block rounded bg-primary px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
||||
>Set system prompt</button>
|
||||
</div>
|
||||
<form x-show="component === 'key'" id="key">
|
||||
<input
|
||||
type="password"
|
||||
id="apiKey"
|
||||
name="apiKey"
|
||||
class="bg-gray-800 text-white border border-gray-600 focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 rounded-md shadow-sm p-2 appearance-none"
|
||||
placeholder="OpenAI API Key"
|
||||
x-model.lazy="key"
|
||||
/>
|
||||
<button @click="component = 'menu'" type="submit" title="Save API key">
|
||||
🔒
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form x-show="component === 'system_prompt'" id="system_prompt">
|
||||
<textarea
|
||||
type="text"
|
||||
id="systemPrompt"
|
||||
name="systemPrompt"
|
||||
class="bg-gray-800 text-white border border-gray-600 focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50 rounded-md shadow-sm p-2 appearance-none"
|
||||
placeholder="System prompt"
|
||||
x-model.lazy="system_prompt"
|
||||
></textarea>
|
||||
<button @click="component = 'menu'" type="submit" title="Save Prompt">
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
@ -111,15 +128,19 @@ SOFTWARE.
|
|||
<template x-if="message.role === 'assistant'">
|
||||
<div class="p-2 flex-1 rounded" :class="message.role" x-html="message.html"></div>
|
||||
</template>
|
||||
<template x-if="message.image">
|
||||
<img :src="message.image" alt="Image" class="rounded-lg mt-2 h-36 w-36">
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 border-t border-gray-700" x-data="{ inputValue: '', shiftPressed: false }">
|
||||
<div class="p-4 border-t border-gray-700" x-data="{ inputValue: '', shiftPressed: false, fileName: '' }">
|
||||
<div id="loader" class="my-2 loader" style="display: none;"></div>
|
||||
<input id="chat-model" type="hidden" value="{{.Model}}">
|
||||
<input id="input_image" type="file" style="display: none;" @change="fileName = $event.target.files[0].name">
|
||||
<form id="prompt" action="/chat/{{.Model}}" method="get" @submit.prevent="submitPrompt">
|
||||
<div class="relative w-full">
|
||||
<textarea
|
||||
|
@ -134,7 +155,10 @@ SOFTWARE.
|
|||
@keydown.enter="if (!shiftPressed) { submitPrompt($event); }"
|
||||
style="padding-right: 4rem;"
|
||||
></textarea>
|
||||
<button type=submit><i class="fa-solid fa-circle-up text-gray-300 absolute right-2 top-3 text-lg p-2 ml-2"></i></button>
|
||||
<span x-text="fileName" id="fileName" class="absolute right-16 top-5 text-gray-300 text-sm mr-2"></span>
|
||||
<button type="button" onclick="document.getElementById('input_image').click()" class="fa-solid fa-paperclip text-gray-300 ml-2 absolute right-10 top-3 text-lg p-2">
|
||||
</button>
|
||||
<button type=submit><i class="fa-solid fa-circle-up text-gray-300 absolute right-2 top-3 text-lg p-2"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -146,7 +170,7 @@ SOFTWARE.
|
|||
clear() {
|
||||
this.history.length = 0;
|
||||
},
|
||||
add(role, content) {
|
||||
add(role, content, image) {
|
||||
const N = this.history.length - 1;
|
||||
if (this.history.length && this.history[N].role === role) {
|
||||
this.history[N].content += content;
|
||||
|
@ -167,6 +191,7 @@ SOFTWARE.
|
|||
role: role,
|
||||
content: content,
|
||||
html: c,
|
||||
image: image,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -191,6 +216,7 @@ SOFTWARE.
|
|||
return {
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
image: message.image,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
|
|
@ -10,38 +10,76 @@
|
|||
<div class="container mx-auto px-4 flex-grow">
|
||||
<div class="header text-center py-12">
|
||||
<h1 class="text-5xl font-bold text-gray-100">Welcome to <i>your</i> LocalAI instance!</h1>
|
||||
<div class="mt-6">
|
||||
<!-- Logo can be uncommented and updated with a valid URL -->
|
||||
</div>
|
||||
<p class="mt-4 text-lg">The FOSS alternative to OpenAI, Claude, ...</p>
|
||||
<a href="https://localai.io" target="_blank" class="mt-4 inline-block bg-blue-500 text-white py-2 px-4 rounded-lg shadow transition duration-300 ease-in-out hover:bg-blue-700 hover:shadow-lg">
|
||||
<i class="fas fa-book-reader pr-2"></i>Documentation
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="models mt-12">
|
||||
<div class="models mt-4">
|
||||
|
||||
<!-- Show in progress operations-->
|
||||
{{ if .ProcessingModels }}
|
||||
<h3
|
||||
class="mt-4 mb-4 text-center text-3xl font-semibold text-gray-100">Operations in progress</h2>
|
||||
{{end}}
|
||||
{{$taskType:=.TaskTypes}}
|
||||
{{ range $key,$value:=.ProcessingModels }}
|
||||
{{ $op := index $taskType $key}}
|
||||
{{$parts := split "@" $key}}
|
||||
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
||||
<div class="flex items center">
|
||||
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||
>{{$parts._1}}</a> (from the '{{$parts._0}}' repository)</span>
|
||||
</div>
|
||||
<div hx-get="/browse/job/{{$value}}" hx-swap="innerHTML" hx-target="this" hx-trigger="done">
|
||||
<h3 role="status" id="pblabel" >{{$op}}
|
||||
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms" hx-target="this"
|
||||
hx-swap= "innerHTML" ></div></h3>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<!-- END Show in progress operations-->
|
||||
|
||||
{{ if eq (len .ModelsConfig) 0 }}
|
||||
<h2 class="text-center text-3xl font-semibold text-gray-100"> <i class="text-yellow-200 ml-2 fa-solid fa-triangle-exclamation animate-pulse"></i> Ouch! seems you don't have any models installed!</h2>
|
||||
<p class="text-center mt-4 text-xl">..install something from the <a class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded" href="/browse">🖼️ Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-gray-400 hover:text-white ml-1 px-3 py-2 rounded"> <i class="fa-solid fa-book"></i> Getting started documentation </a></p>
|
||||
{{ else }}
|
||||
<h2 class="text-center text-3xl font-semibold text-gray-100">Installed models</h2>
|
||||
<p class="text-center mt-4 text-xl">We have {{len .ModelsConfig}} pre-loaded models available.</p>
|
||||
<ul class="mt-8 space-y-4">
|
||||
<table class="table-auto mt-4 w-full text-left text-gray-200">
|
||||
<thead class="text-xs text-gray-400 uppercase bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-4 py-2"></th>
|
||||
<th class="px-4 py-2">Model Name</th>
|
||||
<th class="px-4 py-2">Backend</th>
|
||||
<th class="px-4 py-2 float-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{$galleryConfig:=.GalleryConfig}}
|
||||
{{$noicon:="https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg"}}
|
||||
{{ range .ModelsConfig }}
|
||||
{{ $cfg:= index $galleryConfig .Name}}
|
||||
<li class="bg-gray-800 border border-gray-700 p-4 rounded-lg">
|
||||
<div class="flex justify-between items-center">
|
||||
|
||||
<tr class="bg-gray-800 border-b border-gray-700">
|
||||
<td class="px-4 py-3">
|
||||
{{ with $cfg }}
|
||||
<img {{ if $cfg.Icon }}
|
||||
src="{{$cfg.Icon}}"
|
||||
{{ else }}
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/6/65/No-Image-Placeholder.svg"
|
||||
src="{{$noicon}}"
|
||||
{{ end }}
|
||||
class="rounded-t-lg max-h-24 max-w-24 object-cover mt-3"
|
||||
>
|
||||
|
||||
<p class="font-bold text-white flex items-center"><i class="fas fa-brain pr-2"></i>{{.Name}}</p>
|
||||
{{ else}}
|
||||
<img src="{{$noicon}}" class="rounded-t-lg max-h-24 max-w-24 object-cover mt-3">
|
||||
{{ end }}
|
||||
</td>
|
||||
<td class="px-4 py-3 font-bold">
|
||||
<p class="font-bold text-white flex items-center"><i class="fas fa-brain pr-2"></i><a href="/browse?term={{.Name}}">{{.Name}}</a></p>
|
||||
</td>
|
||||
<td class="px-4 py-3 font-bold">
|
||||
{{ if .Backend }}
|
||||
<!-- Badge for Backend -->
|
||||
<span class="inline-block bg-blue-500 text-white py-1 px-3 rounded-full text-xs">
|
||||
|
@ -52,16 +90,20 @@
|
|||
auto
|
||||
</span>
|
||||
{{ end }}
|
||||
</td>
|
||||
|
||||
<td class="px-4 py-3">
|
||||
<button
|
||||
class="float-right inline-block rounded bg-red-800 px-6 pb-2.5 mb-3 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-red-accent-300 hover:shadow-red-2 focus:bg-red-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-red-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"
|
||||
data-twe-ripple-color="light" data-twe-ripple-init="" hx-confirm="Are you sure you wish to delete the model?" hx-post="/browse/delete/model/{{.Name}}" hx-swap="outerHTML"><i class="fa-solid fa-cancel pr-2"></i>Delete</button>
|
||||
</div>
|
||||
<!-- Additional details can go here -->
|
||||
</li>
|
||||
</td>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -63,8 +63,33 @@
|
|||
{{ end }}
|
||||
</div>
|
||||
|
||||
|
||||
<span class="htmx-indicator loader"></span>
|
||||
<input class="form-control appearance-none block w-full px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
||||
<!-- Show in progress operations-->
|
||||
{{ if .ProcessingModels }}
|
||||
<h2
|
||||
class="mt-4 mb-4 text-center text-3xl font-semibold text-gray-100">Operations in progress</h2>
|
||||
{{end}}
|
||||
{{$taskType:=.TaskTypes}}
|
||||
{{ range $key,$value:=.ProcessingModels }}
|
||||
{{ $op := index $taskType $key}}
|
||||
{{$parts := split "@" $key}}
|
||||
<div class="flex items-center justify-between bg-slate-600 p-2 mb-2 rounded-md">
|
||||
<div class="flex items center">
|
||||
<span class="text-gray-300"><a href="/browse?term={{$parts._1}}"
|
||||
class="text-white-500 inline-block bg-blue-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2 hover:bg-gray-300 hover:shadow-gray-2"
|
||||
>{{$parts._1}}</a> (from the '{{$parts._0}}' repository)</span>
|
||||
</div>
|
||||
<div hx-get="/browse/job/{{$value}}" hx-swap="innerHTML" hx-target="this" hx-trigger="done">
|
||||
<h3 role="status" id="pblabel" >{{$op}}
|
||||
<div hx-get="/browse/job/progress/{{$value}}" hx-trigger="every 600ms" hx-target="this"
|
||||
hx-swap= "innerHTML" ></div></h3>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<!-- END Show in progress operations-->
|
||||
|
||||
<input class="form-control appearance-none block w-full mt-5 px-3 py-2 text-base font-normal text-gray-300 pb-2 mb-5 bg-gray-800 bg-clip-padding border border-solid border-gray-600 rounded transition ease-in-out m-0 focus:text-gray-300 focus:bg-gray-900 focus:border-blue-500 focus:outline-none" type="search"
|
||||
name="search" placeholder="Begin Typing To Search models..."
|
||||
hx-post="/browse/search/models"
|
||||
hx-trigger="input changed delay:500ms, search"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue