This commit is contained in:
Saifeddine ALOUI 2023-06-17 18:56:13 +02:00
commit f055ad414d
15 changed files with 326 additions and 232 deletions

View File

@ -5,7 +5,7 @@
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GPT4All - WEBUI</title>
<title>LoLLMS WebUI - Welcome</title>
</head>
<body>
<div id="app"></div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 66 KiB

BIN
web/src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

View File

@ -1,5 +1,5 @@
<template>
<div class=" items-start p-4 hover:bg-primary-light rounded-lg mb-2 shadow-lg border-2 cursor-pointer active:scale-95 duration-75 select-none"
<div class=" items-start p-4 hover:bg-primary-light rounded-lg mb-2 shadow-lg border-2 cursor-pointer select-none"
@click.stop="toggleSelected" :class="selected ? ' border-primary-light' : 'border-transparent'">
<div :class="isTemplate ? 'opacity-50' : ''">
@ -15,13 +15,24 @@
<div class="grow">
<!-- EMPTY SPACE FILLER -->
</div>
<!-- ADVANCED OPTIONS - NOT IMPLEMENTED -->
<div v-if="hasAdvancedSettings" class="flex-none">
<button type="button" title="Not implemented"
class="hover:text-secondary duration-75 active:scale-90 font-medium rounded-lg text-sm p-2 text-center inline-flex items-center " @click.stop="">
<i data-feather="sliders" class="w-5 m-1"></i>
<span class="sr-only">Icon description</span>
<!-- ADVANCED OPTIONS -->
<div class="flex-none gap-1">
<button type="button" title="Reinstall binding"
class="hover:text-secondary duration-75 active:scale-90 font-medium rounded-lg text-sm p-2 text-center inline-flex items-center " @click.stop="toggleReinstall">
<i data-feather="tool" class="w-5"></i>
<span class="sr-only">Reinstall binding</span>
</button>
<!-- - NOT IMPLEMENTED -->
<!-- <button type="button" title="Settings - Not implemented"
class="hover:text-secondary duration-75 active:scale-90 font-medium rounded-lg text-sm p-2 text-center inline-flex items-center " @click.stop="">
<i data-feather="sliders" class="w-5"></i>
<span class="sr-only">Settings</span>
</button>
<button type="button" title="Help - Not implemented"
class="hover:text-secondary duration-75 active:scale-90 font-medium rounded-lg text-sm p-2 text-center inline-flex items-center " @click.stop="">
<i data-feather="help-circle" class="w-5"></i>
<span class="sr-only">Help</span>
</button> -->
</div>
</div>
@ -49,12 +60,15 @@
<b>Version:&nbsp;</b>
{{ binding.version }}
</div>
<a :href="binding.link" target="_blank" class="flex items-center">
<div class="flex items-center">
<i data-feather="github" class="w-5 m-1"></i>
<b>Link:&nbsp;</b>
<a :href="binding.link" target="_blank" class="flex items-center hover:text-secondary duration-75 active:scale-90">
{{ binding.link }}
</a>
</div>
</div>
<div class="flex items-center">
<i data-feather="info" class="w-5 m-1"></i>
<b>Description:&nbsp;</b><br>
@ -77,13 +91,14 @@ export default {
props: {
binding: {},
onSelected: Function,
onReinstall: Function,
selected: Boolean,
},
data() {
return {
isTemplate: false,
hasAdvancedSettings: false,
};
},
mounted() {
@ -107,6 +122,9 @@ export default {
toggleSelected() {
this.onSelected(this)
},
toggleReinstall() {
this.onReinstall(this)
},
getStatus() {
if (this.binding.folder === 'backend_template' || this.binding.folder === 'binding_template') {
this.isTemplate = true

View File

@ -1,5 +1,5 @@
<template>
<div class="absolute bottom-0 w-96 justify-center text-center p-4 ">
<div class="absolute bottom-0 min-w-96 w-full justify-center text-center p-4 ">
<div v-if="loading" class="flex items-center justify-center w-full">
<div class="flex flex-row p-2 rounded-t-lg ">
@ -89,13 +89,17 @@
<!-- CHAT BOX -->
<div class="flex flex-row flex-grow items-center gap-2 ">
<!-- <div class="w-24">
<MountedPersonalitiesComponent />
<MountedPersonalities />
</div> -->
<div class="relative grow">
<textarea id="chat" rows="1" v-model="message"
class="block min-h-11 no-scrollbar p-2.5 w-full text-sm text-gray-900 bg-bg-light rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-bg-dark dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
<textarea id="chat" rows="1" v-model="message" title="Hold SHIFT + ENTER to add new line"
class="inline-block no-scrollbar p-2.5 w-full text-sm text-gray-900 bg-bg-light rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-bg-dark dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Send message..." @keydown.enter.exact="submitOnEnter($event)">
</textarea>
<input type="file" ref="fileDialog" style="display: none" @change="addFiles" multiple />
<button type="button" @click.stop="$refs.fileDialog.click()" title="Add files"
@ -162,15 +166,15 @@
position: absolute;
}
</style>
<script setup>
<!-- <script setup>
import MountedPersonalitiesComponent from './MountedPersonalitiesComponent.vue'
</script>
</script> -->
<script>
import { nextTick, TransitionGroup } from 'vue'
import feather from 'feather-icons'
import filesize from '../plugins/filesize'
import MountedPersonalities from './MountedPersonalities.vue'
export default {
name: 'ChatBox',
emits: ["messageSentEvent", "stopGenerating"],
@ -180,10 +184,12 @@ export default {
},
components: {
MountedPersonalitiesComponent
MountedPersonalities
},
setup() {
return {}
},
data() {
return {
@ -252,17 +258,15 @@ export default {
}
}
this.totalSize = filesize(total, false)
this.totalSize = filesize(total, true)
},
deep: true
},
},
computed: {
},
mounted() {
nextTick(() => {
feather.replace()
})

View File

@ -19,7 +19,8 @@
</p> -->
</div>
<div class="text-sm text-gray-400 font-thin" v-if="message.created_at" :title="'Created at: '+created_at_parsed">
<div class="text-sm text-gray-400 font-thin" v-if="message.created_at"
:title="'Created at: ' + created_at_parsed">
{{ created_at }}
</div>
@ -105,8 +106,8 @@
<p v-if="message.binding">Binding: <span class="font-thin">{{ message.binding }}</span></p>
<p v-if="message.model">Model: <span class="font-thin">{{ message.model }}</span></p>
<p v-if="message.seed">Seed: <span class="font-thin">{{ message.seed }}</span></p>
<p v-if="message.finished_generating_at">Time spent: <span class="font-thin"
:title="'Finished generating: '+finished_generating_at_parsed">{{ time_spent }}</span></p>
<p v-if="time_spent">Time spent: <span class="font-thin"
:title="'Finished generating: ' + finished_generating_at_parsed">{{ time_spent }}</span></p>
</div>
</div>
@ -323,9 +324,18 @@ export default {
const startTime = new Date(Date.parse(this.message.created_at))
const endTime = new Date(Date.parse(this.message.finished_generating_at))
//const spentTime = new Date(endTime - startTime)
const same = endTime.getTime() === startTime.getTime();
if(same){
return undefined
}
if(!endTime.getTime()){
return undefined
}
let timeDiff = endTime.getTime() - startTime.getTime();
const hours = Math.floor(timeDiff / (1000 * 60 * 60));
timeDiff -= hours * (1000 * 60 * 60);
@ -353,9 +363,11 @@ export default {
// let h = addZero(spentTime.getHours());
// let m = addZero(spentTime.getMinutes());
// let s = addZero(spentTime.getSeconds());
const time = addZero(hours) + "h:" + addZero(mins) + "m:" + addZero(secs)+'s';
const time = addZero(hours) + "h:" + addZero(mins) + "m:" + addZero(secs) + 's';
return time
return time
}
}

View File

@ -1,42 +1,40 @@
<template>
<!-- LIST OF MOUNTED PERSONALITIES -->
<div
class=" text-base font-semibold cursor-pointer select-none items-center flex flex-row overflow-visible overflow-x-auto scrollbar-thin scrollbar-track-bg-light scrollbar-thumb-bg-light-tone hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark dark:scrollbar-thumb-bg-dark-tone dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<!-- LIST -->
<div class="flex -space-x-4 items-center ">
<!-- ITEM -->
<div class="relative hover:-translate-y-2 duration-300 hover:z-10 shrink-0 "
v-for="(item, index) in mountedPersArr" :key="index + '-' + item.name" >
<div class="group items-center flex flex-row">
<button @click.stop="onPersonalitySelected(item)">
<!-- LIST OF MOUNTED PERSONALITIES -->
<div
class=" text-base font-semibold cursor-pointer select-none items-center flex flex-row overflow-visible overflow-x-auto scrollbar-thin scrollbar-track-bg-light scrollbar-thumb-bg-light-tone hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark dark:scrollbar-thumb-bg-dark-tone dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<!-- LIST -->
<div class="flex -space-x-4 items-center ">
<!-- ITEM -->
<div class="relative hover:-translate-y-2 duration-300 hover:z-10 shrink-0 "
v-for="(item, index) in mountedPersArr" :key="index + '-' + item.name">
<div class="group items-center flex flex-row">
<button @click.stop="onPersonalitySelected(item)">
<img :src="bUrl + item.avatar" @error="personalityImgPlacehodler"
class="w-8 h-8 rounded-full object-fill text-red-700 border-2 active:scale-90 group-hover:border-secondary "
:class="configFile.active_personality_id == configFile.personalities.indexOf(item.full_path) ? 'border-secondary' : 'border-transparent z-0'"
:title="item.name">
</button>
<button @click.stop="onPersonalityMounted(item)">
<img :src="bUrl + item.avatar" @error="personalityImgPlacehodler"
class="w-8 h-8 rounded-full object-fill text-red-700 border-2 active:scale-90 group-hover:border-secondary "
:class="configFile.active_personality_id == configFile.personalities.indexOf(item.full_path) ? 'border-secondary' : 'border-transparent z-0'"
:title="item.name">
</button>
<button @click.stop="onPersonalityMounted(item)">
<span
class="hidden group-hover:block top-0 left-7 absolute active:scale-90 bg-bg-light dark:bg-bg-dark rounded-full border-2 border-transparent"
title="Unmount personality">
<!-- UNMOUNT BUTTON -->
<svg aria-hidden="true" class="w-4 h-4 text-red-600 hover:text-red-500 " fill="currentColor"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
<span
class="hidden group-hover:block top-0 left-7 absolute active:scale-90 bg-bg-light dark:bg-bg-dark rounded-full border-2 border-transparent"
title="Unmount personality">
<!-- UNMOUNT BUTTON -->
<svg aria-hidden="true" class="w-4 h-4 text-red-600 hover:text-red-500 " fill="currentColor"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</span>
</button>
</div>
</span>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
@ -46,40 +44,40 @@ import defaultPersonalityImgPlaceholder from "../assets/logo.svg"
const bUrl = import.meta.env.VITE_GPT4ALL_API_BASEURL
axios.defaults.baseURL = import.meta.env.VITE_GPT4ALL_API_BASEURL
export default {
name: 'MountedPersonalitiesComponent',
name: 'MountedPersonalities',
setup() {
return {
configFile: {},
mountedPersArr:[],
personalities:[],
bUrl:bUrl,
isMounted:false
mountedPersArr: [],
personalities: [],
bUrl: bUrl,
isMounted: false
}
},
async mounted(){
async mounted() {
await this.constructor()
this.isMounted=true
this.isMounted = true
},
async activated(){
if(this.isMounted){
async activated() {
if (this.isMounted) {
await this.constructor()
}
},
methods:{
async constructor(){
this.configFile = await this.api_get_req("get_config")
let personality_path_infos = await this.api_get_req("get_current_personality_path_infos")
this.configFile.personality_language = personality_path_infos["personality_language"]
this.configFile.personality_category = personality_path_infos["personality_category"]
this.configFile.personality_folder = personality_path_infos["personality_name"]
await this.getPersonalitiesArr().then(()=>{
this.getMountedPersonalities()
this.$forceUpdate()
})
},
methods: {
async constructor() {
this.configFile = await this.api_get_req("get_config")
let personality_path_infos = await this.api_get_req("get_current_personality_path_infos")
this.configFile.personality_language = personality_path_infos["personality_language"]
this.configFile.personality_category = personality_path_infos["personality_category"]
this.configFile.personality_folder = personality_path_infos["personality_name"]
await this.getPersonalitiesArr().then(() => {
this.getMountedPersonalities()
this.$forceUpdate()
})
},
async api_get_req(endpoint) {
try {
@ -169,15 +167,15 @@ export default {
}
this.isLoading=true
this.isLoading = true
},
async onPersonalitySelected(pers) {
// eslint-disable-next-line no-unused-vars
if (this.isLoading) {
this.$refs.toast.showToast("Loading... please wait", 4, false)
}
this.isLoading=true
console.log('ppa',pers)
this.isLoading = true
console.log('ppa', pers)
if (pers) {
if (pers.selected) {
@ -204,10 +202,10 @@ export default {
nextTick(() => {
})
this.isLoading=false
this.isLoading = false
}
},
@ -290,7 +288,7 @@ export default {
},
async mountPersonality(pers) {
this.isLoading=true
this.isLoading = true
console.log('mount pers', pers)
if (!pers) { return }
@ -317,11 +315,11 @@ export default {
pers.isMounted = false
this.$refs.toast.showToast("Could not mount personality\nError: " + res.error, 4, false)
}
this.isLoading=false
this.isLoading = false
},
async unmountPersonality(pers) {
this.isLoading=true
this.isLoading = true
if (!pers) { return }
const res = await this.unmount_personality(pers.personality || pers)
@ -366,7 +364,7 @@ export default {
this.$refs.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
}
this.isLoading=false
this.isLoading = false
},
getMountedPersonalities() {
@ -381,7 +379,7 @@ export default {
if (pers) {
mountedPersArr.push(pers)
}
else{
else {
mountedPersArr.push(this.personalities[this.personalities.findIndex(item => item.full_path == "english/generic/lollms")])
}
}

View File

@ -4,28 +4,33 @@
<ul class="flex flex-col font-medium p-4 md:p-0 mt-4 md:flex-row md:space-x-8 md:mt-0 ">
<li>
<RouterLink :to="{ name: 'discussions' }" class="p-2" active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<RouterLink :to="{ name: 'discussions' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Discussions</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'settings' }" class="p-2" active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<RouterLink :to="{ name: 'settings' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Settings</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'extensions' }" class="p-2" active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<RouterLink :to="{ name: 'extensions' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Extensions</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'training' }" class="p-2" active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<RouterLink :to="{ name: 'training' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Training</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'help' }" class="p-2" active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<RouterLink :to="{ name: 'help' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Help</a>
</RouterLink>
</li>
@ -35,7 +40,25 @@
</template>
<script setup>
import {nextTick} from 'vue'
import { nextTick } from 'vue'
import feather from 'feather-icons'
import { RouterLink } from 'vue-router'
</script>
<script>
export default {
data() {
return {
};
},
activated() {
},
methods: {
},
};
</script>

View File

@ -2,9 +2,9 @@
<div class="absolute bottom-16 right-2 z-20 flex flex-col gap-3 min-w-[300px]">
<TransitionGroup name="toastItem" tag="div">
<div v-for=" t in toastArr" :key="t.id" class="relative">
<div class="flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800"
<div class="flex flex-row items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800"
role="alert">
<div class="flex flex-row items-center">
<div class="flex flex-row flex-grow items-center">
<slot>
<div v-if="t.success"
@ -17,30 +17,31 @@
<i data-feather="x"></i>
<span class="sr-only">Cross icon</span>
</div>
<div class="ml-3 text-sm font-normal whitespace-pre-wrap">{{ t.message }}</div>
<div class="ml-3 text-sm font-normal whitespace-pre-wrap line-clamp-3" :title="t.message">{{ t.message }}</div>
</slot>
</div>
<div class="absolute top-2 right-2">
<div class="flex ">
<button type="button" @click.stop="t.copy(t.message)"
class=" bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
<span class="sr-only">Copy message</span>
<i data-feather="clipboard" class="w-5 h-5"></i>
</button>
<button type="button" @click="close(t.id)"
class=" bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
<span class="sr-only">Close</span>
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</button>
</div>
<button type="button" @click.stop="copyToClipBoard(t.message)" title="Copy message"
class=" bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
<span class="sr-only">Copy message</span>
<i data-feather="clipboard" class="w-5 h-5"></i>
</button>
<button type="button" @click="close(t.id)" title="Close"
class=" bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
<span class="sr-only">Close</span>
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clip-rule="evenodd"></path>
</svg>
</button>
</div>
</div>
</div>
@ -72,30 +73,14 @@ export default {
},
copyToClipBoard(content) {
// const id = parseInt(((new Date()).getTime() * Math.random()).toString()).toString()
// const toastObj = {
// id: id,
// success: true,
// message: "Copied to clipboard successfully",
// show: true,
// copy: this.copyToClipBoard(this.message)
// }
navigator.clipboard.writeText(content);
// this.toastArr.push(toastObj)
nextTick(() => {
feather.replace()
})
// setTimeout(() => {
// this.toastArr = this.toastArr.filter(item => item.id != id)
// }, duration_s * 1000);
},
@ -106,7 +91,7 @@ export default {
success: success,
message: message,
show: true,
copy: this.copyToClipBoard(message)
//copy: this.copyToClipBoard(message)
}

View File

@ -5,8 +5,12 @@
<!-- LOGO -->
<RouterLink :to="{ name: 'discussions' }">
<div class="flex items-center gap-3 flex-1">
<img class="w-12 hover:scale-95 duration-150 " title="lollms-webui" src="@/assets/logo.svg" alt="Logo">
<p class="text-2xl ">lollms-webui</p>
<img class="w-12 hover:scale-95 duration-150 " title="LoLLMS WebUI" src="@/assets/logo.png" alt="Logo">
<div class="flex flex-col">
<p class="text-2xl ">Lord of Large Language Models</p>
<p class="text-gray-400 ">One tool to rule them all</p>
</div>
</div>
</RouterLink>

View File

@ -2,16 +2,20 @@
<!-- DEFAULT FIRST PAGE LOAD VIEW -->
<div class="flex flex-col text-center">
<div class="flex flex-col text-center items-center">
<div class="flex items-center gap-3 text-5xl drop-shadow-md align-middle pt-24 ">
<img class="w-24 animate-bounce" title="lollms-webui" src="@/assets/logo.svg" alt="Logo">
<p class=" ">lollms-webui</p>
<img class="w-24 animate-bounce" title="LoLLMS WebUI" src="@/assets/logo.png" alt="Logo">
<div class="flex flex-col items-start">
<p class="text-2xl ">Lord of Large Language Models</p>
<p class="text-gray-400 text-base">One tool to rule them all</p>
</div>
</div>
<hr
class=" mt-1 w-96 h-1 mx-auto my-2 md:my-2 dark:bg-bg-dark-tone-panel bg-bg-light-tone-panel border-0 rounded ">
<p class="text-lg ">Welcome, please create a new discussion or select existing one to start</p>
<p class="text-2xl">Welcome</p>
<p class="text-lg ">Please create a new discussion or select existing one to start</p>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div v-if="show" class="fixed top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-black bg-opacity-50">
<div class="relative w-full max-w-md max-h-full">
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
<div class="relative w-full max-w-md max-h-full ">
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 ">
<button type="button" @click="hide(false)"
class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white">
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"
@ -21,11 +21,10 @@
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400 select-none">{{ message }}</h3>
<button @click="hide(true)" type="button"
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
Yes, I'm sure
{{ConfirmButtonText}}
</button>
<button @click="hide(false)" type="button"
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">No,
cancel</button>
class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">{{DenyButtonText}}</button>
</div>
</div>
</div>
@ -39,6 +38,8 @@ export default {
show: false,
message: "",
resolve: null,
ConfirmButtonText: "Yes, I'm sure",
DenyButtonText: "No, cancel",
};
},
methods: {
@ -49,7 +50,9 @@ export default {
this.resolve = null;
}
},
askQuestion(message) {
askQuestion(message, ConfirmButtonText, DenyButtonText) {
this.ConfirmButtonText =ConfirmButtonText || this.ConfirmButtonText
this.DenyButtonText =DenyButtonText || this.DenyButtonText
return new Promise((resolve) => {
this.message = message;
this.show = true;

View File

@ -11,14 +11,15 @@ const app = createApp(App)
const store = createStore({
state () {
return {
count: 0,
mountedPersonalities: {}
// count: 0,
mountedPersonalities: {},
settingsChanged:false
}
},
mutations: {
increment (state) {
state.count++
}
// increment (state) {
// state.count++
// }
}
})

View File

@ -188,9 +188,8 @@
class="absolute w-full bottom-0 bg-transparent p-10 pt-16 bg-gradient-to-t from-bg-light dark:from-bg-dark from-5% via-bg-light dark:via-bg-dark via-10% to-transparent to-100%">
</div>
<div class=" bottom-0 container flex flex-row items-center justify-center ">
<ChatBox ref="chatBox" v-if="currentDiscussion.id" @messageSentEvent="sendMsg" :loading="isGenerating"
@stopGenerating="stopGenerating" />
<div class=" bottom-0 container flex flex-row items-center justify-center " v-if="currentDiscussion.id">
<ChatBox ref="chatBox" @messageSentEvent="sendMsg" :loading="isGenerating" @stopGenerating="stopGenerating" ></ChatBox>
</div>
<!-- CAN ADD FOOTER PANEL HERE -->
</div>
@ -199,6 +198,7 @@
<Toast ref="toast">
</Toast>
</template>
@ -840,14 +840,14 @@ export default {
if (item) {
if (item.id) {
const realTitle = item.title ? item.title === "untitled" ? "New discussion" : item.title : "New discussion"
document.title = 'GPT4ALL - WEBUI - ' + realTitle
document.title = 'LoLLMS WebUI - ' + realTitle
} else {
const title = item || "Welcome"
document.title = 'GPT4ALL - WEBUI - ' + title
document.title = 'LoLLMS WebUI - ' + title
}
} else {
const title = item || "Welcome"
document.title = 'GPT4ALL - WEBUI - ' + title
document.title = 'LoLLMS WebUI - ' + title
}
},
@ -1128,10 +1128,17 @@ export default {
setFileListChat(files) {
//this.fileList = files
this.$refs.chatBox.fileList = this.$refs.chatBox.fileList.concat(files)
try {
this.$refs.chatBox.fileList = this.$refs.chatBox.fileList.concat(files)
} catch (error) {
this.$refs.toast.showToast("Failed to set filelist in chatbox\n"+error.message, 4, false)
}
this.isDragOverChat = false
},
setDropZoneChat() {
@ -1189,6 +1196,9 @@ export default {
socket.on("final", this.finalMsgEvent)
},
mounted(){
//console.log('chatbox mnt',this.$refs)
},
async activated() {
//console.log('settings changed', this.$store.state.mountedPersonalities)
@ -1196,7 +1206,7 @@ export default {
// To fix scrolling back to last message, this hook is needed.
// If anyone knows hor to fix scroll issue when changing pages, please do fix it :D
console.log("Websocket connected (activated)", this.socketConnected)
//console.log('settings changed acc', this.$store.state.settingsChanged)
await this.getPersonalityAvatars()
if (this.isCreated) {

View File

@ -75,11 +75,12 @@
<div class=" text-base font-semibold cursor-pointer select-none items-center">
<div class="flex gap-2 items-center ">
<svg class="flex-shrink-0" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
<!-- <svg class="flex-shrink-0" xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24">
<path fill="currentColor"
d="M17 17H7V7h10m4 4V9h-2V7a2 2 0 0 0-2-2h-2V3h-2v2h-2V3H9v2H7c-1.11 0-2 .89-2 2v2H3v2h2v2H3v2h2v2a2 2 0 0 0 2 2h2v2h2v-2h2v2h2v-2h2a2 2 0 0 0 2-2v-2h2v-2h-2v-2m-6 2h-2v-2h2m2-2H9v6h6V9Z" />
</svg>
</svg> -->
<i data-feather="cpu" class="w-5 h-5 mx-1 flex-shrink-0"></i>
<h3 class="font-bold font-large text-lg">
<div>{{ ram_usage }} / {{ ram_total_space }}</div>
</h3>
@ -163,7 +164,7 @@
<img :src="imgBinding" class="w-8 h-8 rounded-full object-fill text-blue-700">
<h3 class="font-bold font-large text-lg line-clamp-1">
<!-- {{ configFile.binding_name }} -->
{{binding_name}}
{{ binding_name }}
</h3>
</div>
</div>
@ -180,7 +181,8 @@
<TransitionGroup name="list">
<BindingEntry ref="bindingZoo" v-for="(binding, index) in bindings"
:key="'index-' + index + '-' + binding.folder" :binding="binding"
:on-selected="onSelectedBinding" :selected="binding.folder === configFile.binding_name">
:on-selected="onSelectedBinding" :on-reinstall="onReinstallBinding"
:selected="binding.folder === configFile.binding_name">
</BindingEntry>
</TransitionGroup>
</div>
@ -331,17 +333,18 @@
<div v-if="configFile.personalities" class="mr-2">|</div>
<!-- LIST OF MOUNTED PERSONALITIES -->
<div class="mr-2 font-bold font-large text-lg line-clamp-1">
{{active_pesonality }}
{{ active_pesonality }}
</div>
</div>
<div v-if="configFile.personalities" class="mr-2">|</div>
<div v-if="configFile.personalities"
class=" text-base font-semibold cursor-pointer select-none items-center flex flex-row">
<!-- LIST -->
<div class="flex -space-x-4 items-center " v-if="mountedPersArr.length > 0" >
<div class="flex -space-x-4 items-center " v-if="mountedPersArr.length > 0">
<!-- ITEM -->
<div class="relative hover:-translate-y-2 duration-300 hover:z-10 shrink-0 "
v-for="(item, index) in mountedPersArr" :key="index + '-' + item.name" ref="mountedPersonalities">
v-for="(item, index) in mountedPersArr" :key="index + '-' + item.name"
ref="mountedPersonalities">
<div class="group items-center flex flex-row">
<button @click.stop="onPersonalitySelected(item)">
@ -620,7 +623,7 @@
</div>
<YesNoDialog ref="yesNoDialog" />
<YesNoDialog ref="yesNoDialog" class="z-20" />
<AddModelDialog ref="addmodeldialog" />
<MessageBox ref="messageBox" />
<Toast ref="toast" />
@ -837,17 +840,18 @@ export default {
if (this.isLoading) {
this.$refs.toast.showToast("Loading... please wait", 4, false)
}
this.isLoading=true
console.log('ppa',pers)
this.isLoading = true
console.log('ppa', pers)
if (pers) {
if (pers.selected) {
this.$refs.toast.showToast("Personality already selected", 4, true)
this.isLoading = false
return
}
this.settingsChanged = true
//this.settingsChanged = true
if (pers.isMounted) {
@ -868,7 +872,7 @@ export default {
feather.replace()
})
this.isLoading=false
this.isLoading = false
}
},
@ -1023,6 +1027,29 @@ export default {
//console.log('lol',binding_object)
}
},
onReinstallBinding(binding_object) {
this.isLoading = true
axios.post('/reinstall_binding', {name: binding_object.binding.folder}).then((res) => {
if (res) {
this.isLoading = false
console.log('reinstall_binding', res)
if(res.data.status){
this.$refs.toast.showToast("Reinstalled binding successfully!", 4, true)
}else{
this.$refs.toast.showToast("Could not reinstall binding", 4, false)
}
return res.data;
}
this.isLoading = false
})
// eslint-disable-next-line no-unused-vars
.catch(error => {
this.isLoading = false
this.$refs.toast.showToast("Could not reinstall binding\n"+error.message, 4, false)
return { 'status': false } });
},
// messagebox ok stuff
onMessageBoxOk() {
console.log("OK button clicked");
@ -1095,6 +1122,7 @@ export default {
this.showAccordion = !this.showAccordion;
},
update_setting(setting_name_val, setting_value_val, next) {
this.isLoading = true
const obj = {
setting_name: setting_name_val,
setting_value: setting_value_val
@ -1103,6 +1131,7 @@ export default {
axios.post('/update_setting', obj).then((res) => {
if (res) {
this.isLoading = false
console.log('update_setting', res)
if (next !== undefined) {
@ -1110,9 +1139,13 @@ export default {
}
return res.data;
}
this.isLoading = false
})
// eslint-disable-next-line no-unused-vars
.catch(error => { return { 'status': false } });
// eslint-disable-next-line no-unused-vars
.catch(error => {
this.isLoading = false
return { 'status': false } });
},
update_binding(value) {
@ -1149,23 +1182,16 @@ export default {
})
},
applyConfiguration() {
// if (!this.configFile.model_name) {
// this.$refs.toast.showToast("Configuration changed failed.\nPlease select model first", 4, false)
// nextTick(() => {
// feather.replace()
// })
// return
// }
this.isLoading = true;
axios.post('/apply_settings').then((res) => {
this.isLoading = false;
if (res.data.status === "succeeded") {
//console.log('apply-res',res)
if (res.data.status) {
this.$refs.toast.showToast("Configuration changed successfully.", 4, true)
this.settingsChanged = false
this.save_configuration()
//this.save_configuration()
} else {
this.$refs.toast.showToast("Configuration change failed.", 4, false)
@ -1374,12 +1400,14 @@ export default {
},
async mountPersonality(pers) {
this.isLoading=true
this.isLoading = true
console.log('mount pers', pers)
if (!pers) { return }
if (this.configFile.personalities.includes(pers.personality.full_path)) {
this.isLoading = false
this.$refs.toast.showToast("Personality already mounted", 4, false)
return
}
@ -1401,11 +1429,11 @@ export default {
pers.isMounted = false
this.$refs.toast.showToast("Could not mount personality\nError: " + res.error, 4, false)
}
this.isLoading=false
this.isLoading = false
},
async unmountPersonality(pers) {
this.isLoading=true
this.isLoading = true
if (!pers) { return }
const res = await this.unmount_personality(pers.personality || pers)
@ -1450,7 +1478,7 @@ export default {
this.$refs.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
}
this.isLoading=false
this.isLoading = false
},
getMountedPersonalities() {
@ -1460,13 +1488,13 @@ export default {
for (let i = 0; i < this.configFile.personalities.length; i++) {
const full_path_item = this.configFile.personalities[i]
const index = this.personalities.findIndex(item => item.full_path == full_path_item)
console.log('index',index)
console.log("i:",i)
console.log('index', index)
console.log("i:", i)
const pers = this.personalities[index]
if (pers) {
mountedPersArr.push(pers)
}
else{
else {
mountedPersArr.push(this.personalities[this.personalities.findIndex(item => item.full_path == "english/generic/lollms")])
}
}
@ -1475,14 +1503,14 @@ export default {
//this.mountedPersArr = mountedPersArr
console.log('getMountedPersonalities', mountedPersArr)
console.log('fig', this.configFile.personality_category)
nextTick(()=>{
console.log('accc',this.$refs.mountedPersonalities)
//this.$store.state.mountedPersonalities = this.$refs.mountedPersonalities
nextTick(() => {
console.log('accc', this.$refs.mountedPersonalities)
//this.$store.state.mountedPersonalities = this.$refs.mountedPersonalities
})
},
onPersonalityMounted(persItem) {
this.isLoading=true
this.isLoading = true
console.log('on sel ', persItem)
if (this.configFile.personalities.includes(persItem.full_path)) {
@ -1502,7 +1530,7 @@ export default {
}
this.isLoading=true
this.isLoading = false
},
personalityImgPlacehodler(event) {
event.target.src = defaultPersonalityImgPlaceholder
@ -1510,7 +1538,7 @@ export default {
}, async mounted() {
//this.$refs.mountedPersonalities
this.isLoading = true
nextTick(() => {
@ -1557,12 +1585,11 @@ export default {
this.ramUsage = await this.api_get_req("ram_usage")
this.getMountedPersonalities()
this.isMounted = true
},
activated(){
// console.log('accc',this.$refs.mountedPersonalities)
// this.$store.state.mountedPersonalities = this.$refs.mountedPersonalities
activated() {
},
computed: {
disk_available_space() {
@ -1615,45 +1642,34 @@ export default {
return defaultModelImgPlaceholder
}
},
binding_name(){
binding_name() {
if (!this.isMounted) {
return
}
const index =this.bindingsArr.findIndex(item=>item.folder === this.configFile.binding_name)
if(index>-1){
const index = this.bindingsArr.findIndex(item => item.folder === this.configFile.binding_name)
if (index > -1) {
return this.bindingsArr[index].name
}else{
} else {
return
}
},
active_pesonality(){
active_pesonality() {
if (!this.isMounted) {
return
}
const index =this.personalities.findIndex(item => item.full_path ===this.configFile.personalities[this.configFile.active_personality_id])
if (index>-1){
const index = this.personalities.findIndex(item => item.full_path === this.configFile.personalities[this.configFile.active_personality_id])
if (index > -1) {
return this.personalities[index].name
}else{
} else {
return
}
}
// imgPersonality() {
// if (!this.isMounted) {
// return
// }
// try {
// return this.$refs.personalitiesZoo[this.$refs.personalitiesZoo.findIndex(item => item.personality.folder == this.configFile.personality_folder)].$refs.imgElement.src
// }
// catch (error) {
// return defaultPersonalityImgPlaceholder
// }
// },
},
@ -1718,8 +1734,8 @@ export default {
})
},
settingsChanged(val) {
//this.$store.settingsChanged=val
this.$store.state.settingsChanged = val
nextTick(() => {
feather.replace()
@ -1732,7 +1748,23 @@ export default {
})
},
}
},
async beforeRouteLeave(to) {
// console.log('did settings?',this.settingsChanged)
await this.$router.isReady()
if (this.settingsChanged) {
const res = await this.$refs.yesNoDialog.askQuestion("You forgot to apply changes?\nYou need to apply changes before you leave, or else.", 'Apply configuration', 'Cancel')
if (res) {
this.applyConfiguration()
}
return false
}
},
}
</script>