enhanced code

This commit is contained in:
Saifeddine ALOUI 2023-07-20 02:12:54 +02:00
parent f855b10a0d
commit 94b1e5ca2d
14 changed files with 300 additions and 306 deletions

View File

@ -172,17 +172,26 @@ class LoLLMsAPPI(LollmsApplication):
@socketio.on('cancel_install')
def cancel_install(data):
model_name = data["model_name"]
binding_folder = data["binding_folder"]
model_url = data["model_url"]
signature = f"{model_name}_{binding_folder}_{model_url}"
self.download_infos[signature]["cancel"]=True
self.socketio.emit('canceled', {
'status': True
},
room=request.sid
)
try:
model_name = data["model_name"]
binding_folder = data["binding_folder"]
model_url = data["model_url"]
signature = f"{model_name}_{binding_folder}_{model_url}"
self.download_infos[signature]["cancel"]=True
self.socketio.emit('canceled', {
'status': True
},
room=request.sid
)
except Exception as ex:
trace_exception(ex)
self.socketio.emit('canceled', {
'status': False,
'error':str(ex)
},
room=request.sid
)
@socketio.on('install_model')
def install_model(data):
room_id = request.sid
@ -395,6 +404,14 @@ class LoLLMsAPPI(LollmsApplication):
self.connections[client_id]["generated_text"]=""
self.connections[client_id]["cancel_generation"]=False
if not self.model:
self.socketio.emit('model_not_selected',
{
"status":False,
"error":"Model not selected. Please select a model"
}, room=client_id
)
return
if self.is_ready:
if self.current_discussion is None:

View File

@ -300,7 +300,7 @@ class DiscussionsDB:
# Insert message into the database
self.insert("INSERT INTO message (sender, content, type, rank, parent, binding, model, personality, created_at, finished_generating_at, discussion_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(sender, content, content_type, rank, parent, model, personality, created_at, finished_generating_at, discussion_id))
(sender, content, content_type, rank, parent, binding, model, personality, created_at, finished_generating_at, discussion_id))
discussions.append(discussion)

2
app.py
View File

@ -1447,6 +1447,7 @@ class LoLLMsWebUI(LoLLMsAPPI):
try:
filename = model.get('filename',"")
server = model.get('server',"")
variants = model.get('variants',[])
image_url = model.get("icon", '/images/default_model.png')
license = model.get("license", 'unknown')
owner = model.get("owner", 'unknown')
@ -1462,6 +1463,7 @@ class LoLLMsWebUI(LoLLMsAPPI):
is_installed = local_path.exists() or model_type.lower()=="api"
models.append({
'title': filename,
'variants': variants,
'icon': image_url, # Replace with the path to the model icon
'license': license,
'owner': owner,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
web/dist/assets/index-61c53820.css vendored Normal file

File diff suppressed because one or more lines are too long

4
web/dist/index.html vendored
View File

@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMS WebUI - Welcome</title>
<script type="module" crossorigin src="/assets/index-6e792e12.js"></script>
<link rel="stylesheet" href="/assets/index-4f9d4363.css">
<script type="module" crossorigin src="/assets/index-12bf0526.js"></script>
<link rel="stylesheet" href="/assets/index-61c53820.css">
</head>
<body>
<div id="app"></div>

View File

@ -1,80 +1,93 @@
<!-- ChoiceDialog.vue -->
<template>
<transition name="fade">
<div v-if="show" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-96">
<h2 class="text-xl font-semibold mb-4">{{ title }}</h2>
<div class="h-48 overflow-y-auto">
<ul>
<li
v-for="(choice, index) in choices"
:key="index"
@click="selectChoice(choice)"
class="py-2 px-4 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700"
>
{{ choice }}
</li>
</ul>
</div>
<div class="flex justify-end mt-4">
<button
@click="closeDialog"
class="py-2 px-4 mr-2 bg-red-500 hover:bg-red-600 text-white rounded-lg transition duration-300"
<transition name="fade">
<div v-if="show" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-96">
<h2 class="text-xl font-semibold mb-4">{{ title }}</h2>
<div class="h-48 overflow-y-auto">
<ul>
<li
v-for="(choice, index) in choices"
:key="index"
@click="selectChoice(choice)"
:class="{'selected-choice': choice === selectedChoice}"
class="py-2 px-4 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700"
>
Cancel
</button>
<button
@click="validateChoice"
class="py-2 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition duration-300"
>
Validate
</button>
</div>
<span class="font-bold"> {{ choice.name }} </span><br>
<span class="text-xs text-gray-500"> {{ this.formatSize(choice.size) }}</span>
</li>
</ul>
</div>
<div class="flex justify-end mt-4">
<button
@click="closeDialog"
class="py-2 px-4 mr-2 bg-red-500 hover:bg-red-600 text-white rounded-lg transition duration-300"
>
Cancel
</button>
<button
@click="validateChoice"
class="py-2 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition duration-300"
>
Validate
</button>
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
required: true,
},
title: {
type: String,
default: "Select an option",
},
choices: {
type: Array,
required: true,
},
</div>
</transition>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
required: true,
},
methods: {
selectChoice(choice) {
this.$emit("choice-selected", choice);
},
closeDialog() {
this.$emit("close-dialog");
},
validateChoice() {
// Perform validation if needed
this.$emit("choice-validated");
},
title: {
type: String,
default: "Select an option",
},
};
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
choices: {
type: Array,
required: true,
},
},
data() {
return {
selectedChoice: null,
};
},
methods: {
selectChoice(choice) {
this.selectedChoice = choice; // Update the selectedChoice when a choice is clicked
this.$emit("choice-selected", choice);
},
closeDialog() {
this.$emit("close-dialog");
},
validateChoice() {
// Perform validation if needed
this.$emit("choice-validated");
},
formatSize(size) {
if (size < 1024) {
return size + " bytes";
} else if (size < 1024 * 1024) {
return (size / 1024).toFixed(2) + " KB";
} else if (size < 1024 * 1024 * 1024) {
return (size / (1024 * 1024)).toFixed(2) + " MB";
} else {
return (size / (1024 * 1024 * 1024)).toFixed(2) + " GB";
}
},
},
};
</script>
<style>
/* ... (unchanged) */
.selected-choice {
background-color: #bde4ff; /* Change this color as per your preference */
}
</style>

View File

@ -1,80 +0,0 @@
<!-- ChoiceDialog.vue -->
<template>
<transition name="fade">
<div v-if="show" class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 w-96">
<h2 class="text-xl font-semibold mb-4">{{ title }}</h2>
<div class="h-48 overflow-y-auto">
<ul>
<li
v-for="(choice, index) in choices"
:key="index"
@click="selectChoice(choice)"
class="py-2 px-4 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700"
>
{{ choice }}
</li>
</ul>
</div>
<div class="flex justify-end mt-4">
<button
@click="closeDialog"
class="py-2 px-4 mr-2 bg-red-500 hover:bg-red-600 text-white rounded-lg transition duration-300"
>
Cancel
</button>
<button
@click="validateChoice"
class="py-2 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition duration-300"
>
Validate
</button>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
show: {
type: Boolean,
required: true,
},
title: {
type: String,
default: "Select an option",
},
choices: {
type: Array,
required: true,
},
},
methods: {
selectChoice(choice) {
this.$emit("choice-selected", choice);
},
closeDialog() {
this.$emit("close-dialog");
},
validateChoice() {
// Perform validation if needed
this.$emit("choice-validated");
},
},
};
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

View File

@ -198,8 +198,6 @@ export default {
}
}, mounted() {
console.log("Mounted message")
console.log(this.message)
this.new_message_content = this.message.content
nextTick(() => {
feather.replace()

View File

@ -244,6 +244,7 @@
</div>
</div>
</div>
</template>
@ -254,6 +255,7 @@ import axios from "axios";
import { nextTick } from 'vue'
import feather from 'feather-icons'
import defaultImgPlaceholder from "../assets/default_model.png"
const bUrl = import.meta.env.VITE_LOLLMS_API_BASEURL
export default {
props: {
@ -285,8 +287,8 @@ export default {
installing: false,
uninstalling: false,
failedToLoad: false,
fileSize: '',
linkNotValid: false,
selected_variant: ''
};
},
async mounted() {
@ -299,15 +301,24 @@ export default {
})
},
methods: {
formatFileSize(sizeInBytes) {
if (sizeInBytes < 1024) {
return sizeInBytes + " bytes";
} else if (sizeInBytes < 1024 * 1024) {
return (sizeInBytes / 1024).toFixed(2) + " KB";
} else if (sizeInBytes < 1024 * 1024 * 1024) {
return (sizeInBytes / (1024 * 1024)).toFixed(2) + " MB";
} else {
return (sizeInBytes / (1024 * 1024 * 1024)).toFixed(2) + " GB";
}
},
computedFileSize(size) {
return filesize(size)
},
async getFileSize(url) {
//console.log(this.model_type);
if (this.model_type != "api") {
try {
const res = await axios.head(url)
//console.log("addddd",url, res.headers)
if (res) {
if (res.headers["content-length"]) {
@ -367,24 +378,17 @@ export default {
event.target.src = defaultImgPlaceholder
},
toggleInstall() {
this.getFileSize(this.model.path).then(data=>{
this.fileSize = data
})
if (this.isInstalled) {
this.uninstalling = true;
// Simulate uninstallation delay (replace this with your WebSocket logic)
this.onUninstall(this);
} else {
this.installing = true;
//this.installing = true;
this.onInstall(this);
}
},
toggleSelected() {
this.getFileSize(this.model.path).then(data=>{
this.fileSize = data
})
this.onSelected(this)
},
toggleCopy() {
@ -405,12 +409,21 @@ export default {
}
},
copyContentToClipboard() {
console.log('asdasdas')
this.$emit('copy', 'this.message.content')
},
},
computed: {
fileSize: {
get() {
// console.log(this.model)
if (this.model && this.model.variants && this.model.variants.length > 0) {
const sizeInBytes = this.model.variants[0]["size"];
return this.formatFileSize(sizeInBytes);
}
return null; // Return null if the conditions are not met
},
},
speed_computed() {
return filesize(this.speed)
},

View File

@ -25,7 +25,7 @@
fill="currentFill" />
</svg>
</div>
<div class="content flex-1" :class="{'text-green-500': done, 'text-yellow-500': !done}">{{ message }}</div>
<div class="content flex-1 px-2" :class="{'text-green-500': done, 'text-yellow-500': !done}">{{ message }}</div>
</div>
</template>

View File

@ -808,7 +808,8 @@ export default {
// Create response message
let responseMessage = {
content:msgObj.data, //content: " please stand by ...",//msgObj.message,
//content:msgObj.data,
content: "✍ please stand by ...",//msgObj.message,
created_at:msgObj.created_at,
binding:msgObj.binding,
model:msgObj.model,
@ -834,7 +835,6 @@ export default {
if (msgObj.type == "input_message_infos") {
// This is a user input
this.changeTitleUsingUserMSG(this.currentDiscussion.id, msgObj.message)
}
}
console.log("infos", msgObj)
@ -896,7 +896,7 @@ export default {
message: msg,
id: lastmsgid,
rank: 0,
user: "user",
user: this.$store.config.user_name,
created_at: new Date().toLocaleString(),
};
@ -913,7 +913,7 @@ export default {
},
streamMessageContent(msgObj) {
// Streams response message content from binding
console.log("Received message",msgObj)
//console.log("Received message",msgObj)
const parent = msgObj.user_message_id
const discussion_id = msgObj.discussion_id
this.setDiscussionLoading(discussion_id, true);

View File

@ -866,7 +866,7 @@
</div>
<div v-if="!searchModel">
<div v-if="models.length > 0" class="mb-2">
<div v-if="models && models.length > 0" class="mb-2">
<label for="model" class="block ml-2 mb-2 text-sm font-medium text-gray-900 dark:text-white">
Models: ({{ models.length }})
</label>
@ -1395,6 +1395,13 @@
<MessageBox ref="messageBox" />
<Toast ref="toast" />
<UniversalForm ref="universalForm" class="z-20" />
<ChoiceDialog class="z-20"
:show="variantSelectionDialogVisible"
:choices="variant_choices"
@choice-selected="onVariantChoiceSelected"
@close-dialog="oncloseVariantChoiceDialog"
@choice-validated="onvalidateVariantChoice"
/>
</template>
<style scoped>
/* THESE ARE FOR TransitionGroup components */
@ -1475,6 +1482,9 @@ import defaultImgPlaceholder from "../assets/default_model.png"
import AddModelDialog from "@/components/AddModelDialog.vue";
import UniversalForm from '../components/UniversalForm.vue';
import ChoiceDialog from "@/components/ChoiceDialog.vue";
const bUrl = import.meta.env.VITE_LOLLMS_API_BASEURL
axios.defaults.baseURL = import.meta.env.VITE_LOLLMS_API_BASEURL
export default {
@ -1488,11 +1498,17 @@ export default {
Toast,
PersonalityEntry,
BindingEntry,
UniversalForm
UniversalForm,
ChoiceDialog
},
data() {
return {
// Variant selection
variant_choices:[],
variantSelectionDialogVisible:false,
currenModelToInstall:null,
// Loading text
loading_text:"",
// Current personality language
personality_language:null,
@ -1551,8 +1567,75 @@ export default {
},
methods: {
onVariantChoiceSelected(choice){
this.selected_variant = choice
},
oncloseVariantChoiceDialog(){
this.variantSelectionDialogVisible=false;
},
onvalidateVariantChoice(){
this.variantSelectionDialogVisible=false;
this.currenModelToInstall.installing=true;
let model_object = this.currenModelToInstall;
if (model_object.linkNotValid) {
model_object.installing = false
this.$refs.toast.showToast("Link is not valid, file does not exist", 4, false)
return
}
let path = model_object.path;
this.showProgress = true;
this.progress = 0;
this.addModel = { model_name: this.selected_variant.name, binding_folder: this.configFile.binding_name, model_url: model_object.path }
console.log("installing...", this.addModel);
// Use an arrow function for progressListener
const progressListener = (response) => {
console.log("received something");
if (response.status && response.progress <= 100) {
this.addModel = response
console.log(`Progress`, response);
model_object.progress = response.progress
model_object.speed = response.speed
model_object.total_size = response.total_size
model_object.downloaded_size = response.downloaded_size
model_object.start_time = response.start_time
model_object.installing = true
if (model_object.progress == 100) {
const index = this.models.findIndex((model) => model.path === path);
this.models[index].isInstalled = true;
this.showProgress = false;
model_object.installing = false
console.log("Received succeeded")
socket.off('install_progress', progressListener);
console.log("Installed successfully")
// Update the isInstalled property of the corresponding model
this.$refs.toast.showToast("Model:\n" + model_object.title + "\ninstalled!", 4, true)
this.$store.dispatch('refreshDiskUsage');
}
} else {
socket.off('install_progress', progressListener);
console.log("Install failed")
// Installation failed or encountered an error
model_object.installing = false;
this.showProgress = false;
console.error('Installation failed:', response.error);
this.$refs.toast.showToast("Model:\n" + model_object.title + "\nfailed to install!", 4, false)
this.$store.dispatch('refreshDiskUsage');
}
};
socket.on('install_progress', progressListener);
socket.emit('install_model', { path: path });
console.log("Started installation, please wait");
},
uploadAvatar(event){
console.log("here")
const file = event.target.files[0]; // Get the selected file
const formData = new FormData(); // Create a FormData object
formData.append('avatar', file); // Add the file to the form data with the key 'avatar'
@ -1870,61 +1953,10 @@ export default {
// Model installation
onInstall(model_object) {
if (model_object.linkNotValid) {
model_object.installing = false
this.$refs.toast.showToast("Link is not valid, file does not exist", 4, false)
return
}
let path = model_object.path;
this.showProgress = true;
this.progress = 0;
this.addModel = { model_name: model_object.model.title, binding_folder: this.configFile.binding_name, model_url: model_object.path }
console.log("installing...", this.addModel);
// Use an arrow function for progressListener
const progressListener = (response) => {
console.log("received something");
if (response.status && response.progress <= 100) {
this.addModel = response
console.log(`Progress`, response);
model_object.progress = response.progress
model_object.speed = response.speed
model_object.total_size = response.total_size
model_object.downloaded_size = response.downloaded_size
model_object.start_time = response.start_time
model_object.installing = true
if (model_object.progress == 100) {
const index = this.models.findIndex((model) => model.path === path);
this.models[index].isInstalled = true;
this.showProgress = false;
model_object.installing = false
console.log("Received succeeded")
socket.off('install_progress', progressListener);
console.log("Installed successfully")
// Update the isInstalled property of the corresponding model
this.$refs.toast.showToast("Model:\n" + model_object.title + "\ninstalled!", 4, true)
this.$store.dispatch('refreshDiskUsage');
}
} else {
socket.off('install_progress', progressListener);
console.log("Install failed")
// Installation failed or encountered an error
model_object.installing = false;
this.showProgress = false;
console.error('Installation failed:', response.error);
this.$refs.toast.showToast("Model:\n" + model_object.title + "\nfailed to install!", 4, false)
this.$store.dispatch('refreshDiskUsage');
}
};
socket.on('install_progress', progressListener);
socket.emit('install_model', { path: path });
console.log("Started installation, please wait");
this.variant_choices = model_object.model.variants;
this.currenModelToInstall = model_object;
console.log(this.variant_choices)
this.variantSelectionDialogVisible=true;
},
onInstallAddModel() {
@ -2371,7 +2403,6 @@ export default {
},
async update_model(value) {
console.log("hjsdfhksdjufkdshf")
if (!value) this.isModelSelected = false
// eslint-disable-next-line no-unused-vars
this.isLoading = true