new version

This commit is contained in:
Saifeddine ALOUI 2024-05-03 00:58:18 +02:00
parent 74e76e0183
commit 0d3de00af7
21 changed files with 363 additions and 480 deletions

View File

@ -155,7 +155,7 @@ data_vectorization_method: "tfidf_vectorizer" #"model_embedding" or "tfidf_vecto
data_visualization_method: "PCA" #"PCA" or "TSNE"
data_vectorization_sentense_transformer_model: "all-MiniLM-L6-v2" # you can use another model by setting its name here or its path
data_vectorization_save_db: false # For each new session, new files
data_vectorization_save_db: true # For each new session, new files
data_vectorization_chunk_size: 512 # chunk size
data_vectorization_overlap_size: 128 # overlap between chunks size
data_vectorization_nb_chunks: 2 # number of chunks to use

View File

@ -1,52 +0,0 @@
import yaml
from typing import List
from discussions.message import Message
class Discussion:
"""
Class representing a discussion.
"""
def __init__(self, name):
"""
Initializes a new Discussion object.
Args:
name (str): The name of the discussion.
"""
self.name = name
self.messages:List[Message] = []
def load_messages(self):
"""
Loads the messages of the discussion from the discussion.yaml file.
"""
with open(f'{self.name}/discussion.yaml', 'r') as file:
self.messages = yaml.load(file)
def save_messages(self):
"""
Saves the messages of the discussion to the discussion.yaml file.
"""
with open(f'{self.name}/discussion.yaml', 'w') as file:
yaml.dump(self.messages, file)
def remove_message(self, message_index):
"""
Removes a message from the discussion.
Args:
message_index (int): The index of the message to remove.
"""
self.messages.pop(message_index)
self.save_messages()
def add_message(self, message: Message):
"""
Adds a new message to the discussion.
Args:
message (Message): The message to add.
"""
self.messages.append(message)
self.save_messages()

View File

@ -1,66 +0,0 @@
import os
from typing import List
from discussions.discussion import Discussion
class DiscussionDatabase:
"""
Class representing a discussion database.
"""
def __init__(self, name, root_folder):
"""
Initializes a new DiscussionDatabase object.
Args:
name (str): The name of the discussion database.
root_folder (str): The root folder of the discussion database.
"""
self.name = name
self.root_folder = root_folder
self.discussions:List[Discussion] = {}
def load_discussions(self):
"""
Loads the discussions from the discussion database.
"""
for discussion_folder in os.listdir(self.root_folder):
discussion = Discussion(discussion_folder)
discussion.load_messages()
self.discussions[discussion_folder] = discussion
def save_discussions(self):
"""
Saves the discussions to the discussion database.
"""
for discussion in self.discussions.values():
discussion.save_messages()
def remove_discussion(self, discussion_name):
"""
Removes a discussion from the discussion database.
Args:
discussion_name (str): The name of the discussion to remove.
"""
del self.discussions[discussion_name]
os.rmdir(f'{self.root_folder}/{discussion_name}')
def list_discussions(self):
"""
Lists all the discussions in the discussion database.
Returns:
List[str]: A list of discussion names.
"""
return list(self.discussions.keys())
def new_discussion(self, discussion_name):
"""
Creates a new discussion in the discussion database.
Args:
discussion_name (str): The name of the new discussion.
"""
discussion = Discussion(discussion_name)
self.discussions[discussion_name] = discussion
os.mkdir(f'{self.root_folder}/{discussion_name}')

View File

@ -1,71 +0,0 @@
import datetime
import json
import yaml
from enum import Enum
__project__ = "lollms-webui"
__author__ = "ParisNeo"
__description__ = ""
class Role(Enum):
SYSTEM = "system"
AI = "ai"
USER = "user"
class Message:
"""
Class representing a message in a discussion.
"""
def __init__(self, sender, role:Role, model:str, content:str, sending_date=None, rank=0):
"""
Initializes a new Message object.
Args:
sender (str): The name of the sender.
role (str): The role of the sender (system, ai, user).
model (str): The model name or "human".
content (str): The content of the message.
sending_date (datetime.datetime, optional): The sending date and time. Defaults to the current date and time.
rank (int, optional): The rank of the message. Defaults to 0.
"""
self.sender = sender
self.role = role
self.model = model
self.content = content
self.sending_date = sending_date or datetime.datetime.now()
self.rank = rank
def rank_up(self):
"""
Increases the rank of the message by 1.
"""
self.rank += 1
def rank_down(self):
"""
Decreases the rank of the message by 1.
"""
if self.rank > 0:
self.rank -= 1
def to_json(self):
"""
Converts the message object to JSON format.
Returns:
str: The message object in JSON format.
"""
return json.dumps(self.__dict__)
def to_yaml(self):
"""
Converts the message object to YAML format.
Returns:
str: The message object in YAML format.
"""
return yaml.dump(self.__dict__)

View File

@ -230,11 +230,7 @@ async def open_file(file_path: FilePath):
trace_exception(ex)
lollmsElfServer.error(ex)
return {"status":False,"error":str(ex)}
class FilePath(BaseModel):
path: Optional[str] = Field(None, max_length=500)
@router.post("/open_folder")
async def open_folder(file_path: FilePath):
@ -281,8 +277,8 @@ class OpenCodeFolderInVsCodeRequestModel(BaseModel):
message_id: Optional[int] = Field(None, gt=0)
code: Optional[str]
@router.post("/open_code_folder_in_vs_code")
async def open_code_folder_in_vs_code(request: OpenCodeFolderInVsCodeRequestModel):
@router.post("/open_discussion_folder_in_vs_code")
async def open_discussion_folder_in_vs_code(request: OpenCodeFolderInVsCodeRequestModel):
client = check_access(lollmsElfServer, request.client_id)
@ -367,11 +363,10 @@ async def open_code_in_vs_code(vs_code_data: VSCodeData):
class FolderRequest(BaseModel):
client_id: str = Field(...)
discussion_id: Optional[int] = Field(None, title="The discussion ID")
folder_path: Optional[str] = Field(None, title="The folder path")
discussion_id: int = Field(...)
@router.post("/open_code_folder")
async def open_code_folder(request: FolderRequest):
@router.post("/open_discussion_folder")
async def open_discussion_folder(request: FolderRequest):
"""
Opens code folder.

View File

@ -122,11 +122,11 @@ def add_events(sio:socketio):
ASCIIColors.yellow(f"Loading discussion for client {client_id} ... ", end="")
if "id" in data:
discussion_id = data["id"]
lollmsElfServer.session.get_client(client_id).discussion = Discussion(discussion_id, lollmsElfServer.db)
lollmsElfServer.session.get_client(client_id).discussion = Discussion(lollmsElfServer, discussion_id, lollmsElfServer.db)
else:
if lollmsElfServer.session.get_client(client_id).discussion is not None:
discussion_id = lollmsElfServer.session.get_client(client_id).discussion.discussion_id
lollmsElfServer.session.get_client(client_id).discussion = Discussion(discussion_id, lollmsElfServer.db)
lollmsElfServer.session.get_client(client_id).discussion = Discussion(lollmsElfServer, discussion_id, lollmsElfServer.db)
else:
lollmsElfServer.session.get_client(client_id).discussion = lollmsElfServer.db.create_discussion()
messages = lollmsElfServer.session.get_client(client_id).discussion.get_messages()

View File

@ -41,7 +41,7 @@ import re
import string
import requests
from datetime import datetime
from typing import List, Tuple
from typing import List, Tuple,Callable
import time
import numpy as np
from lollms.utilities import find_first_available_file_index, convert_language_name
@ -72,7 +72,7 @@ def terminate_thread(thread):
else:
ASCIIColors.yellow("Canceled successfully")# The current version of the webui
lollms_webui_version="9.6"
lollms_webui_version="9.7"
@ -186,7 +186,7 @@ class LOLLMSWebUI(LOLLMSElfServer):
self.discussion_db_name = config["discussion_db_name"]
# Create database object
self.db = DiscussionsDB(self.lollms_paths, self.discussion_db_name)
self.db = DiscussionsDB(self, self.lollms_paths, self.discussion_db_name)
# If the database is empty, populate it with tables
ASCIIColors.info("Checking discussions database... ",end="")
@ -712,6 +712,19 @@ class LOLLMSWebUI(LOLLMSElfServer):
return discussion_messages # Removes the last return
def full(self, full_text:str, callback: Callable[[str, MSG_TYPE, dict, list], bool]=None, client_id=0):
"""This sends full text to front end
Args:
step_text (dict): The step text
callback (callable, optional): A callable with this signature (str, MSG_TYPE) to send the text to. Defaults to None.
"""
if not callback:
callback = partial(self.process_chunk,client_id = client_id)
if callback:
callback(full_text, MSG_TYPE.MSG_TYPE_FULL)
def notify(
self,
content,
@ -760,7 +773,7 @@ class LOLLMSWebUI(LOLLMSElfServer):
open=False
):
client = self.session.get_client(client_id)
self.close_message(client_id)
#self.close_message(client_id)
mtdt = metadata if metadata is None or type(metadata) == str else json.dumps(metadata, indent=4)
if sender==None:
sender= self.personality.name
@ -918,11 +931,10 @@ class LOLLMSWebUI(LOLLMSElfServer):
Processes a chunk of generated text
"""
client = self.session.get_client(client_id)
if chunk is None:
return True
if not client_id in list(self.session.clients.keys()):
self.error("Connection lost", client_id=client_id)
return
if chunk is not None:
if not client_id in list(self.session.clients.keys()):
self.error("Connection lost", client_id=client_id)
return
if message_type == MSG_TYPE.MSG_TYPE_STEP:
ASCIIColors.info("--> Step:"+chunk)
if message_type == MSG_TYPE.MSG_TYPE_STEP_START:

View File

@ -1,7 +1,7 @@
import requests
# Set the URL of the endpoint
url = "http://localhost:9600/open_code_folder"
url = "http://localhost:9600/open_discussion_folder"
# Valid folder path
valid_folder_path = {"folder_path": "c:"}

View File

@ -1,6 +1,6 @@
'''
This python script is performing a Denial of Service (DoS) attack on your endpoint.
It is creating a large number of requests (1000 in this case) to the '/open_code_folder' API endpoint of your server.
It is creating a large number of requests (1000 in this case) to the '/open_discussion_folder' API endpoint of your server.
This could potentially overload your server, making it unable to serve normal, legitimate requests.
Please make sure you test this only on a virtual machine since it can overload your own PC and crush it
@ -14,5 +14,5 @@ for i in range(1000):
data = {
"discussion_id":f"{i}",
}
response = requests.post(f"http://{IP_ADDRESS}:{str(PORT)}/open_code_folder", json=data)
response = requests.post(f"http://{IP_ADDRESS}:{str(PORT)}/open_discussion_folder", json=data)
print(i, response.json())

File diff suppressed because one or more lines are too long

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-59608853.js"></script>
<link rel="stylesheet" href="/assets/index-91104afa.css">
<script type="module" crossorigin src="/assets/index-58d8702e.js"></script>
<link rel="stylesheet" href="/assets/index-82bacbd8.css">
</head>
<body>
<div id="app"></div>

View File

@ -464,9 +464,14 @@ export default {
for (let item of items) {
if (item.type.indexOf("image") !== -1) {
const blob = item.getAsFile();
// Instead of reading the file and setting it as an image URL,
// call the addFiles method and pass the file
this.addFiles([blob]); // Assuming addFiles accepts an array of files
// Generate a unique identifier for the file
const uniqueId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const newFileName = `image_${uniqueId}.png`;
console.log("newFileName",newFileName)
// Create a new file with the unique name
const newFile = new File([blob], newFileName, {type: blob.type});
this.addFiles([newFile]); // Assuming addFiles accepts an array of files
}
else if (item.kind === 'file') {
const file = item.getAsFile();
@ -475,7 +480,7 @@ export default {
}
if (filesToUpload.length > 0) {
this.addFiles(filesToUpload);
}
}
},
toggleSwitch() {
this.$store.state.config.activate_internet_search = !this.$store.state.config.activate_internet_search;
@ -759,12 +764,12 @@ export default {
axios.get('/download_files')
},
remove_file(file){
axios.get('/remove_file',{client_id:this.$store.state.client_id, name: file}).then(res=>{
axios.get('/remove_discussion_file',{client_id:this.$store.state.client_id, name: file}).then(res=>{
console.log(res)
})
},
clear_files(){
axios.post('/clear_personality_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
axios.post('/clear_discussion_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
console.log(res)
if(res.data.state){
this.$store.state.toast.showToast("File removed successfully",4,true);
@ -902,7 +907,7 @@ export default {
removeItem(file) {
console.log("Réemoving ",file.name)
axios.post('/remove_file',{
axios.post('/remove_discussion_file',{
client_id:this.$store.state.client_id,
name:file.name
},
@ -949,7 +954,7 @@ export default {
takePicture(){
socket.emit('take_picture')
socket.on('picture_taken',()=>{
axios.post('/get_current_personality_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
axios.post('/get_discussion_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
this.filesList = res.data.files;
this.isFileSentList= res.data.files.map(file => {
return true;

View File

@ -235,7 +235,7 @@ export default {
'code': this.code
})
console.log(json)
fetch(`${this.host}/open_code_folder_in_vs_code`, {
fetch(`${this.host}/open_folder_in_vs_code`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: json
@ -255,7 +255,7 @@ export default {
openFolder() {
const json = JSON.stringify({ 'client_id': this.client_id, 'discussion_id': this.discussion_id })
console.log(json)
fetch(`${this.host}/open_code_folder`, {
fetch(`${this.host}/open_discussion_folder`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: json

View File

@ -19,46 +19,52 @@
:class="loading ? 'min-h-full w-2 rounded-xl self-stretch animate-bounce bg-accent ' : ' '"></div>
</div>
<!-- TITLE -->
<p v-if="!editTitle" :title="title" class="line-clamp-1 w-4/6 ml-1 -mx-5 ">{{ title ? title === "untitled" ? "New discussion" :
title : "New discussion" }}</p>
<!-- CONTAINER FOR TITLE AND CONTROL BUTTONS -->
<div class="flex flex-row items-center w-full">
<!-- TITLE -->
<p v-if="!editTitle" :title="title" class="line-clamp-1 w-4/6 ml-1 -mx-5 ">{{ title ? title === "untitled" ? "New discussion" :
title : "New discussion" }}</p>
<input v-if="editTitle" type="text" id="title-box" ref="titleBox"
class="bg-bg-light dark:bg-bg-dark rounded-md border-0 w-full -m-1 p-1" :value="title" required
@keydown.enter.exact="editTitleEvent()" @keydown.esc.exact="editTitleMode = false"
@input="chnageTitle($event.target.value)" @click.stop>
<input v-if="editTitle" type="text" id="title-box" ref="titleBox"
class="bg-bg-light dark:bg-bg-dark rounded-md border-0 w-full -m-1 p-1" :value="title" required
@keydown.enter.exact="editTitleEvent()" @keydown.esc.exact="editTitleMode = false"
@input="chnageTitle($event.target.value)" @click.stop>
<!-- CONTROL BUTTONS -->
<div class="flex items-center flex-1 max-h-6">
<!-- EDIT TITLE CONFIRM -->
<div v-if="showConfirmation" class="flex gap-3 flex-1 items-center justify-end duration-75">
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Discard title changes"
type="button" @click.stop="cancel()">
<i data-feather="x"></i>
</button>
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Confirm title changes"
type="button" @click.stop="editTitleMode?editTitleEvent():deleteMode?deleteEvent():makeTitleEvent()">
<i data-feather="check"></i>
</button>
</div>
<!-- EDIT AND REMOVE -->
<div v-if="!showConfirmation"
class="flex gap-3 flex-1 items-center justify-end invisible group-hover:visible duration-75">
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Make a title" type="button"
@click.stop="makeTitleMode = true">
<i data-feather="type"></i>
</button>
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Edit title" type="button"
@click.stop="editTitleMode = true">
<i data-feather="edit-2"></i>
</button>
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Remove discussion" type="button"
@click.stop="deleteMode = true">
<i data-feather="trash"></i>
</button>
<!-- CONTROL BUTTONS -->
<div class="flex items-center flex-1 max-h-6">
<!-- EDIT TITLE CONFIRM -->
<div v-if="showConfirmation" class="flex gap-3 flex-1 items-center justify-end duration-75">
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Discard title changes"
type="button" @click.stop="cancel()">
<i data-feather="x"></i>
</button>
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Confirm title changes"
type="button" @click.stop="editTitleMode?editTitleEvent():deleteMode?deleteEvent():makeTitleEvent()">
<i data-feather="check"></i>
</button>
</div>
<!-- EDIT AND REMOVE -->
<div v-if="!showConfirmation"
class="flex gap-3 flex-1 items-center justify-end invisible group-hover:visible duration-75">
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Open folder" type="button"
@click.stop="openFolderEvent()">
<i data-feather="folder"></i>
</button>
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Make a title" type="button"
@click.stop="makeTitleMode = true">
<i data-feather="type"></i>
</button>
<button class="text-2xl hover:text-secondary duration-75 active:scale-90" title="Edit title" type="button"
@click.stop="editTitleMode = true">
<i data-feather="edit-2"></i>
</button>
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 " title="Remove discussion" type="button"
@click.stop="deleteMode = true">
<i data-feather="trash"></i>
</button>
</div>
</div>
</div>
</div>
</template>
@ -68,7 +74,7 @@ import feather from 'feather-icons'
export default {
name: 'Discussion',
emits: ['delete', 'select', 'editTitle', 'makeTitle', 'checked'],
emits: ['delete', 'select', 'openFolder', 'editTitle', 'makeTitle', 'checked'],
props: {
id: Number,
title: String,
@ -86,6 +92,7 @@ export default {
editTitleMode: false,
makeTitleMode: false,
deleteMode:false,
openFolder:false,
editTitle: false,
newTitle: String,
checkBoxValue_local: false
@ -105,6 +112,12 @@ export default {
selectEvent() {
this.$emit("select")
},
openFolderEvent() {
this.$emit("openFolder",
{
id: this.id
})
},
editTitleEvent() {
this.editTitle = false
this.editTitleMode = false
@ -174,7 +187,6 @@ export default {
},
makeTitleMode(newval) {
this.showConfirmation = newval
},
checkBoxValue(newval, oldval) {

View File

@ -5,7 +5,7 @@
<!-- 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.png" alt="Logo">
<img class="w-12 hover:scale-95 duration-150" title="LoLLMS WebUI" :src="$store.state.config.app_custom_logo || storeLogo" alt="Logo">
<div class="flex flex-col">
<p class="text-2xl font-bold text-2xl drop-shadow-md align-middle">LoLLMS</p>
<p class="text-gray-400 ">One tool to rule them all</p>
@ -168,6 +168,7 @@ import feather from 'feather-icons'
import static_info from "../assets/static_info.svg"
import animated_info from "../assets/animated_info.svg"
import { useRouter } from 'vue-router'
import storeLogo from '@/assets/logo.png'
import axios from 'axios';
</script>
@ -263,7 +264,7 @@ export default {
isLanguageMenuVisible: false,
static_info: static_info,
animated_info: animated_info,
storeLogo:storeLogo,
is_first_connection:true,
discord:discord,
FastAPI:FastAPI,

View File

@ -5,7 +5,7 @@
<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.png" alt="Logo">
<img class="w-24 animate-bounce" title="LoLLMS WebUI" :src="$store.state.config.app_custom_logo || storeLogo" alt="Logo">
<div class="flex flex-col items-start">
<p class="text-2xl ">LoLLMS</p>
<p class="text-gray-400 text-base">One tool to rule them all</p>
@ -23,8 +23,12 @@
</template>
<script>
import storeLogo from '@/assets/logo.png'
export default {
name: 'WelcomeComponent',
data(){
storeLogo:storeLogo
},
setup() {

View File

@ -210,6 +210,7 @@
:checkBoxValue="item.checkBoxValue"
@select="selectDiscussion(item)"
@delete="deleteDiscussion(item.id)"
@openFolder="openFolder"
@editTitle="editTitle"
@makeTitle="makeTitle"
@checked="checkUncheckDiscussion" />
@ -684,7 +685,7 @@ export default {
},
recoverFiles(){
console.log("Recovering files")
axios.post('/get_current_personality_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
axios.post('/get_discussion_files_list', {"client_id":this.$store.state.client_id}).then(res=>{
this.$refs.chatBox.filesList = res.data.files;
this.$refs.chatBox.isFileSentList= res.data.files.map(file => {
return true;
@ -946,6 +947,7 @@ export default {
if (this.currentDiscussion.title === '' || this.currentDiscussion.title === null) {
this.changeTitleUsingUserMSG(this.currentDiscussion.id, this.discussionArr[1].content)
}
this.recoverFiles()
}
})
@ -967,6 +969,7 @@ export default {
this.changeTitleUsingUserMSG(this.currentDiscussion.id, this.discussionArr[1].content)
}
}
this.recoverFiles()
});
}
@ -1475,6 +1478,14 @@ export default {
})
},
async openFolder(id){
const json = JSON.stringify({ 'client_id': this.$store.state.client_id, 'discussion_id': id.id })
console.log(json)
await axios.post(`/open_discussion_folder`, json, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
},
async editTitle(newTitleObj) {
const index = this.list.findIndex((x) => x.id == newTitleObj.id)

View File

@ -223,6 +223,17 @@
<div class="flex flex-col mb-2 px-3 pb-2">
<Card title="General" :is_subcard="true" class="pb-2 m-2">
<table class="expand-to-fit bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<tr>
<td style="min-width: 200px;">
<label for="app_custom_logo" class="text-sm font-bold" style="margin-right: 1rem;">Application logo:</label>
</td>
<td style="width: 100%;">
<label for="avatar-upload">
<img :src="configFile.app_custom_logo!=''? '/user_infos/'+configFile.app_custom_logo:storeLogo" class="w-50 h-50 rounded-full" style="max-width: 50px; max-height: 50px; cursor: pointer;">
</label>
<input type="file" id="avatar-upload" style="display: none" @change="uploadLogo">
</td>
</tr>
<tr>
<td style="min-width: 200px;">
@ -478,7 +489,7 @@
</td>
<td style="width: 100%;">
<label for="avatar-upload">
<img :src="'/user_infos/'+configFile.user_avatar" class="w-50 h-50 rounded-full" style="max-width: 50px; max-height: 50px; cursor: pointer;">
<img :src="'/user_infos/'+configFile.user_avatar || storeLogo" class="w-50 h-50 rounded-full" style="max-width: 50px; max-height: 50px; cursor: pointer;">
</label>
<input type="file" id="avatar-upload" style="display: none" @change="uploadAvatar">
</td>
@ -2959,6 +2970,7 @@ import Card from "@/components/Card.vue"
import RadioOptions from '../components/RadioOptions.vue';
import ExtensionEntry from "@/components/ExtensionEntry.vue"
import storeLogo from '@/assets/logo.png'
import {refreshHardwareUsage} from "../main"
@ -3009,7 +3021,7 @@ export default {
"Hindi": "hi"
},
storeLogo:storeLogo,
binding_changed:false,
SVGGPU:SVGGPU,
models_zoo:[],
@ -3405,7 +3417,27 @@ export default {
socket.emit('install_model', { path: path, name: model_object.model.name, variant_name:this.selected_variant.name, type:model_object.model.type });
console.log("Started installation, please wait");
},
uploadLogo(event){
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'
console.log("Uploading avatar")
// Make an API request to upload the avatar
axios.post('/upload_avatar', formData)
.then(response => {
console.log("Avatar uploaded successfully")
this.$store.state.toast.showToast("Avatar uploaded successfully!", 4, true)
// Assuming the server responds with the file name after successful upload
const fileName = response.data.fileName;
console.log("response",response);
this.app_custom_logo = fileName; // Update the user_avatar value with the file name
this.update_setting("app_custom_logo", fileName, ()=>{}).then(()=>{})
})
.catch(error => {
console.error('Error uploading avatar:', error);
});
},
uploadAvatar(event){
const file = event.target.files[0]; // Get the selected file
const formData = new FormData(); // Create a FormData object

@ -1 +1 @@
Subproject commit e74bdd27b5983b60ea3b70b2dd79fce1297d5b74
Subproject commit 0b59189e42e9fad6ecd94e7076aeb6f8d0737caa

@ -1 +1 @@
Subproject commit 198e45cb1cc99ca20e6fae4c0d1db0857f7dc172
Subproject commit a554175a8e6d73bdfa8e16446bb6fd17e921d72a