This commit is contained in:
saloui 2023-12-18 08:39:35 +01:00
commit 79381b63df
34 changed files with 925 additions and 612 deletions

View File

@ -38,6 +38,15 @@ from datetime import datetime
from typing import List, Tuple
import time
from lollms.utilities import find_first_available_file_index
if not PackageManager.check_package_installed("requests"):
PackageManager.install_package("requests")
if not PackageManager.check_package_installed("bs4"):
PackageManager.install_package("beautifulsoup4")
import requests
from bs4 import BeautifulSoup
def terminate_thread(thread):
if thread:
if not thread.is_alive():
@ -223,6 +232,26 @@ class LoLLMsAPI(LollmsApplication):
ASCIIColors.error(f'Client {request.sid} disconnected')
@socketio.on('add_webpage')
def add_webpage(data):
ASCIIColors.yellow("Scaping web page")
url = data['url']
index = find_first_available_file_index(self.lollms_paths.personal_uploads_path,"web_",".txt")
file_path=self.lollms_paths.personal_uploads_path/f"web_{index}.txt"
self.scrape_and_save(url=url, file_path=file_path)
try:
if not self.personality.processor is None:
self.personality.processor.add_file(file_path, partial(self.process_chunk, client_id = request.sid))
# File saved successfully
socketio.emit('web_page_added', {'status':True,})
else:
self.personality.add_file(file_path, partial(self.process_chunk, client_id = request.sid))
# File saved successfully
socketio.emit('web_page_added', {'status':True})
except Exception as e:
# Error occurred while saving the file
socketio.emit('web_page_added', {'status':False})
@socketio.on('take_picture')
def take_picture():
try:
@ -263,18 +292,22 @@ class LoLLMsAPI(LollmsApplication):
@socketio.on('start_webcam_video_stream')
def start_webcam_video_stream():
self.info("Starting video capture")
self.webcam.start_capture()
@socketio.on('stop_webcam_video_stream')
def stop_webcam_video_stream():
self.info("Stopping video capture")
self.webcam.stop_capture()
@socketio.on('start_audio_stream')
def start_audio_stream():
self.info("Starting audio capture")
self.audio_cap.start_recording()
@socketio.on('stop_audio_stream')
def stop_audio_stream():
self.info("Stopping audio capture")
self.audio_cap.stop_recording()
@ -1097,6 +1130,25 @@ class LoLLMsAPI(LollmsApplication):
else:
if output["text"].lower()=="lollms":
self.summoned = True
def scrape_and_save(self, url, file_path):
# Send a GET request to the URL
response = requests.get(url)
# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(response.content, 'html.parser')
# Find all the text content in the webpage
text_content = soup.get_text()
# Remove extra returns and spaces
text_content = ' '.join(text_content.split())
# Save the text content as a text file
with open(file_path, 'w', encoding="utf-8") as file:
file.write(text_content)
self.info(f"Webpage content saved to {file_path}")
def rebuild_personalities(self, reload_all=False):
if reload_all:
self.mounted_personalities=[]
@ -1960,16 +2012,23 @@ class LoLLMsAPI(LollmsApplication):
self.prepare_reception(client_id)
self.generating = True
self.connections[client_id]["processing"]=True
self.generate(
self.discussion_messages,
self.current_message,
n_predict = self.config.ctx_size-len(tokens)-1,
client_id=client_id,
callback=partial(self.process_chunk,client_id = client_id)
)
print()
print("## Done Generation ##")
print()
try:
self.generate(
self.discussion_messages,
self.current_message,
n_predict = self.config.ctx_size-len(tokens)-1,
client_id=client_id,
callback=partial(self.process_chunk,client_id = client_id)
)
print()
ASCIIColors.success("## Done Generation ##")
print()
except Exception as ex:
trace_exception(ex)
print()
ASCIIColors.error("## Generation Error ##")
print()
self.cancel_gen = False
# Send final message

110
app.py
View File

@ -13,7 +13,7 @@ __github__ = "https://github.com/ParisNeo/lollms-webui"
__copyright__ = "Copyright 2023, "
__license__ = "Apache 2.0"
__version__ ="8.0 (Beta)"
__version__ ="8.0"
main_repo = "https://github.com/ParisNeo/lollms-webui.git"
import os
@ -511,6 +511,39 @@ try:
self.add_endpoint(
"/open_file", "open_file", self.open_file, methods=["GET"]
)
self.add_endpoint(
"/update_binding_settings", "update_binding_settings", self.update_binding_settings, methods=["GET"]
)
def update_binding_settings(self):
if self.binding:
self.binding.settings_updated()
ASCIIColors.green("Binding setting updated successfully")
return jsonify({"status":True})
else:
return jsonify({"status":False, 'error':"no binding found"})
def reload_binding(self, data):
print(f"Roloading binding selected : {data['binding_name']}")
self.config["binding_name"]=data['binding_name']
try:
if self.binding:
self.binding.destroy_model()
self.binding = None
self.model = None
for per in self.mounted_personalities:
per.model = None
gc.collect()
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.INSTALL_IF_NECESSARY, lollmsCom=self)
self.model = None
self.config.save_config()
ASCIIColors.green("Binding loaded successfully")
except Exception as ex:
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
trace_exception(ex)
return jsonify({"status":False, 'error':str(ex)})
def get_model_status(self):
return jsonify({"status":self.model is not None})
@ -1011,7 +1044,8 @@ try:
try:
self.model = None
for per in self.mounted_personalities:
per.model = None
if per is not None:
per.model = None
self.model = self.binding.build_model()
if self.model is not None:
ASCIIColors.yellow("New model OK")
@ -1638,7 +1672,6 @@ try:
self.config.binding_name = data['name']
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.FORCE_INSTALL, lollmsCom=self)
self.success("Binding installed successfully")
self.InfoMessage("Please reboot the application so that the binding installation can be taken into consideration")
del self.binding
self.binding = None
self.config.binding_name = old_bn
@ -1649,7 +1682,7 @@ try:
per.model = self.model
return jsonify({"status": True})
except Exception as ex:
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
self.error(f"Couldn't build binding: [{ex}]")
trace_exception(ex)
return jsonify({"status":False, 'error':str(ex)})
@ -1671,12 +1704,12 @@ try:
self.config.binding_name = data['name']
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.FORCE_INSTALL, lollmsCom=self)
self.success("Binding reinstalled successfully")
self.InfoMessage("Please reboot the application so that the binding installation can be taken into consideration")
self.config.binding_name = old_bn
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, lollmsCom=self)
self.model = self.binding.build_model()
for per in self.mounted_personalities:
per.model = self.model
return jsonify({"status": True})
except Exception as ex:
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
@ -1764,16 +1797,9 @@ try:
return jsonify({'update_availability':False})
def restart_program(self):
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info(" ╔══════════════════════════════════════════════════╗")
ASCIIColors.info(" ║ Restarting backend ║")
ASCIIColors.info(" ╚══════════════════════════════════════════════════╝")
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info("")
run_restart_script(self.args)
socketio.reboot=True
self.socketio.stop()
self.socketio.sleep(1)
def update_software(self):
@ -1786,6 +1812,7 @@ try:
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info("")
self.socketio.stop()
run_update_script(self.args)
sys.exit()
@ -1831,46 +1858,6 @@ try:
version = __version__
ASCIIColors.yellow("Lollms webui version : "+ version)
return jsonify({"version":version})
def reload_binding(self):
try:
data = request.get_json()
# Further processing of the data
except Exception as e:
print(f"Error occurred while parsing JSON: {e}")
return jsonify({"status":False, 'error':str(e)})
ASCIIColors.info(f"- Reloading binding {data['name']}...")
try:
ASCIIColors.info("Unmounting binding and model")
self.binding = None
self.model = None
for personality in self.mounted_personalities:
personality.model = None
gc.collect()
ASCIIColors.info("Reloading binding")
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, lollmsCom=self)
ASCIIColors.info("Binding loaded successfully")
try:
ASCIIColors.info("Reloading model")
self.model = self.binding.build_model()
ASCIIColors.info("Model reloaded successfully")
except Exception as ex:
print(f"Couldn't build model: [{ex}]")
trace_exception(ex)
try:
self.rebuild_personalities(reload_all=True)
except Exception as ex:
print(f"Couldn't reload personalities: [{ex}]")
return jsonify({"status": True})
except Exception as ex:
ASCIIColors.error(f"Couldn't build binding: [{ex}]")
trace_exception(ex)
return jsonify({"status":False, 'error':str(ex)})
def p_mount_personality(self):
print("- Mounting personality")
@ -2637,9 +2624,22 @@ try:
try:
socketio.reboot = False
socketio.run(app, host=config["host"], port=config["port"],
# prevent error: The Werkzeug web server is not designed to run in production
allow_unsafe_werkzeug=True)
if socketio.reboot:
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info(" ╔══════════════════════════════════════════════════╗")
ASCIIColors.info(" ║ Restarting backend ║")
ASCIIColors.info(" ╚══════════════════════════════════════════════════╝")
ASCIIColors.info("")
ASCIIColors.info("")
ASCIIColors.info("")
run_restart_script(args)
except Exception as ex:
trace_exception(ex)
# http_server = WSGIServer((config["host"], config["port"]), app, handler_class=WebSocketHandler)

View File

@ -0,0 +1,16 @@
Hi there, this is a short to show you the potential of the hierarchical contextual summary of documents using lollms.
To do so, we first go to settings page. Under the personalities section, select the category data, and mount the docs_zipper personality.
Now, we go to the personality settings and we set some specific summery parameters. Here we say keep the method description, we select keep document title and authors in the summary. We set the summary size in tokens and we validate.
Now we add the document to summerize and we go to the personality menu and we select start
The document will be decomposed into a certain number of chunks, then each chunk is contextually summerized. After that the summeries are tied together then the operation is repeated until the compressed text is smaller than the maximum number of tokens set in the configuration.
The contextual nature of this algorithm will allow you to have better control over the summary. For example, here we asked for keeping the title, the author names and the results of the paper as well as the method.
As you can see, the summary has respected all the constraints that we did set. We can find the title, the authors names, the method and the numerical results.
Don't forget to like and subscribe
Thanks for watching

View File

@ -0,0 +1,39 @@
Hi there,
In this short I'm going to show you how to install lollms in GPU mode and use the hugging face binding to run inference.
The steps to aceive this are simple:
download the installer from the github of the project to an empty folder with a path that does not contain spaces.
double click the installer and validate.
select the install mode (Cuda, rocm or cpu)
wait
Run the app
Choose the personal data folder
Install a binding then Reboot the app
Select the binding
Install a model
Select a model
And now you are ready to rock
Let me show you how I do it:
Start by opening the lollms web user interface link that you can find in the description. Then go to the latest release page and download the installer suited for your platform. Make sure you put it into a path with no spaces and inside an empty folder as it will fill out the folder with the install files.
Now execute the installer. As it says, make sure the path you are using is not very long and without spaces as conda may have bugs with paths that contains spaces.
Now press enter to continue.
Select the right configuration. if you have an nvisia gpu, select option a. if you have a Rocm compatible GPU like AMD GPUs press B. If you have no GPU or want to use remote generation, then select C.
Now just wait
Once the installation is done, close the installation script and go back to the install folder.
run the win_run.bat file and wait for the program to run for the first time.
Some additional libraries may need to be installed the first time you run it.
The first time you run the app, you will need to specify a folder where to put your personal data. Make sure the disk containing this folder is big enough as it will hold your models as well as the personas outputs and your discussion databases. The default folder is a folder called lollms under your documents folder. Just press enter if it is ok or type another path then press enter.
Now lollms is installed, but there is no binding installed nor a model. To do this, go to settings tab, select binding s zoo and install a binding depending on your preferences. Here I am using a GPU and want local generation, so I will select Hugging face to use full sized models, GPTQ quantized models or AWQ quantized models. The installation may take some while, you can look at the install details in the console window.

@ -1 +1 @@
Subproject commit 691af56f46f4318f21975dff4aea5c1922ad2eb7
Subproject commit 9acdc3e9cd7880451b57e290d08c2957347766bb

View File

@ -1,18 +1,17 @@
tqdm
psutil
setuptools
pyyaml
numpy
flask
flask_socketio
pytest
pyyaml
markdown
gevent
gevent-websocket
langchain
requests
eventlet
websocket-client
GitPython
setuptools
numpy
flask_compress
gevent-websocket
websocket-client
eventlet
tqdm
psutil
pytest
gevent
requests
GitPython
ascii_colors>=0.1.4
beautifulsoup4

View File

@ -7,7 +7,6 @@ pyyaml
markdown
gevent
gevent-websocket
langchain
requests
eventlet
websocket-client

View File

@ -98,13 +98,6 @@ source "$MINICONDA_DIR/bin/activate" || ( echo "Miniconda hook not found." && ex
if [ ! -d "$INSTALL_ENV_DIR" ]; then
echo "Packages to install: $PACKAGES_TO_INSTALL"
conda create -y -k -p "$INSTALL_ENV_DIR" $CHANNEL $PACKAGES_TO_INSTALL || ( echo && echo "Conda environment creation failed." && exit 1 )
if [[ "${gpuchoice^^}" == "A" ]]; then
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 || ( echo && echo "Pytorch installation failed." && exit 1 )
elif [[ "${gpuchoice^^}" == "B" ]]; then
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2 || ( echo && echo "Pytorch installation failed." && exit 1 )
elif [[ "${gpuchoice^^}" == "C" ]]; then
conda run --live-stream -p "$INSTALL_ENV_DIR" python -m pip install torch torchvision torchaudio || ( echo && echo "Pytorch installation failed." && exit 1 )
fi
fi
# Check if conda environment was actually created

View File

@ -95,12 +95,6 @@ if [ ! -d "$INSTALL_ENV_DIR" ]; then
echo "Packages to install: $PACKAGES_TO_INSTALL"
conda create -y -k -n "$ENV_NAME" $CHANNEL $PACKAGES_TO_INSTALL || ( echo && echo "Conda environment creation failed." && exit 1 )
echo "Conda created and using packages: $PACKAGES_TO_INSTALL"
echo "Installing tools"
if [[ "$(echo $gpuchoice | tr '[:lower:]' '[:upper:]')" == "A" ]]; then
conda run --live-stream -n "$ENV_NAME" python -m pip install torch torchvision torchaudio --channel pytorch --channel conda-forge || ( echo && echo "Pytorch installation failed." && exit 1 )
elif [[ "$(echo $gpuchoice | tr '[:lower:]' '[:upper:]')" == "B" ]]; then
conda run --live-stream -n "$ENV_NAME" python -m pip install torch torchvision torchaudio --channel pytorch --channel conda-forge || ( echo && echo "Pytorch installation failed." && exit 1 )
fi
fi
# Activate installer environment

View File

@ -107,9 +107,6 @@ if /I "%gpuchoice%" == "B" (
if not exist "%INSTALL_ENV_DIR%" (
echo Packages to install: %PACKAGES_TO_INSTALL%
call conda create --no-shortcuts -y -k -p "%INSTALL_ENV_DIR%" %CHANNEL% %PACKAGES_TO_INSTALL% || ( echo. && echo Conda environment creation failed. && goto end )
if /I "%gpuchoice%" == "A" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121|| ( echo. && echo Pytorch installation failed.&& goto end )
if /I "%gpuchoice%" == "B" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm5.4.2|| ( echo. && echo Pytorch installation failed.&& goto end )
if /I "%gpuchoice%" == "C" call conda run --live-stream -p "%INSTALL_ENV_DIR%" python -m pip install torch torchvision torchaudio|| ( echo. && echo Pytorch installation failed.&& goto end )
)
@rem check if conda environment was actually created

8
web/dist/assets/index-7674c890.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
web/dist/disconnected.mp3 vendored Normal file

Binary file not shown.

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-715d9baa.js"></script>
<link rel="stylesheet" href="/assets/index-f3c97c3f.css">
<script type="module" crossorigin src="/assets/index-e963577c.js"></script>
<link rel="stylesheet" href="/assets/index-7674c890.css">
</head>
<body>
<div id="app"></div>

BIN
web/dist/rebooting.mp3 vendored Normal file

Binary file not shown.

BIN
web/public/disconnected.mp3 Normal file

Binary file not shown.

BIN
web/public/rebooting.mp3 Normal file

Binary file not shown.

View File

@ -1,8 +1,8 @@
<template>
<div class="flex flex-col h-screen font-sans bg-bg-light text-slate-950 dark:bg-bg-dark dark:text-slate-50 overflow-y-scroll w-full dark:bg-bg-dark scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<div class="flex flex-col h-screen font-sans bg-bg-light text-slate-950 dark:bg-bg-dark dark:text-slate-50 w-full dark:bg-bg-dark overflow-hidden">
<TopBar />
<div class="flex overflow-hidden flex-grow ">
<div class="flex overflow-hidden flex-grow w-full">
<!-- VIEW CONTAINER -->
<RouterView v-slot="{ Component }">
<KeepAlive>

View File

@ -1,63 +1,124 @@
<template>
<div class="floating-frame">
<img v-if="isAudioActive" :src="imageDataUrl" alt="Spectrogram" width="300" height="300" />
<div class="floating-frame bg-white" :style="{ bottom: position.bottom + 'px', right: position.right + 'px', 'z-index': zIndex } " @mousedown.stop="startDrag" @mouseup.stop="stopDrag">
<div class="handle" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">Drag Me</div>
<img v-if="isAudioActive && imageDataUrl != null" :src="imageDataUrl" alt="Spectrogram" width="300" height="300" />
<div class="controls">
<button v-if="!isAudioActive" class="w-full bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startAudioStream">Activate Audio</button>
<button v-if="isAudioActive" class="w-full bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopAudioStream">Deactivate Audio</button>
<button v-if="!isAudioActive" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startAudioStream"><i data-feather="mic"></i> </button>
<button v-if="isAudioActive" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopAudioStream"><i data-feather="mic"></i></button>
<span v-if="isAudioActive">FPS: {{ frameRate }}</span>
</div>
</div>
</template>
<script>
import socket from '@/services/websocket.js';
export default {
data() {
return {
isAudioActive: false,
imageDataUrl: null
};
import socket from '@/services/websocket.js';
import feather from 'feather-icons'
import { nextTick } from 'vue'
export default {
data() {
return {
isAudioActive: false,
imageDataUrl: null,
isDragging: false,
position: { bottom: 0, right: 0 },
dragStart: { x: 0, y: 0 },
zIndex: 0, // Add a data property for z-index
frameRate: 0,
frameCount: 0,
lastFrameTime: Date.now(),
};
},
methods: {
startAudioStream() {
this.isAudioActive = true;
socket.emit('start_audio_stream');
nextTick(() => {
feather.replace()
})
},
methods: {
startAudioStream() {
this.isAudioActive = true;
socket.emit('start_audio_stream');
},
stopAudioStream() {
this.isAudioActive = false;
this.imageData = null;
socket.emit('stop_audio_stream');
stopAudioStream() {
this.isAudioActive = false;
this.imageDataUrl = null;
socket.emit('stop_audio_stream');
nextTick(() => {
feather.replace()
})
},
startDrag(event) {
this.isDragging = true;
this.zIndex = 5001; // Increase z-index when dragging starts
this.dragStart.x = event.clientX;
this.dragStart.y = event.clientY;
document.addEventListener('mousemove', this.drag);
document.addEventListener('mouseup', this.stopDrag);
},
drag(event) {
if (this.isDragging) {
const deltaX = event.clientX - this.dragStart.x;
const deltaY = event.clientY - this.dragStart.y;
this.position.bottom -= deltaY;
this.position.right -= deltaX;
this.dragStart.x = event.clientX;
this.dragStart.y = event.clientY;
}
},
mounted() {
socket.on('update_spectrogram', (imageBase64) => {
if (this.isAudioActive) {
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
}
});
stopDrag() {
this.isDragging = false;
this.zIndex = 0; // Reset z-index when dragging stops
document.removeEventListener('mousemove', this.drag);
document.removeEventListener('mouseup', this.stopDrag);
}
};
</script>
<style>
.floating-frame {
margin: 15px;
float: left;
width: 800px;
height: auto;
border: 1px solid #000;
border-radius: 4px;
overflow: hidden;
z-index: 5000;
},
mounted() {
feather.replace();
socket.on('update_spectrogram', (imageBase64) => {
if (this.isAudioActive) {
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
this.frameCount++;
const now = Date.now();
const delta = now - this.lastFrameTime;
if (delta >= 1000) { // Calculate FPS every second
this.frameRate = this.frameCount;
this.frameCount = 0;
this.lastFrameTime = now;
}
}
});
}
.floating-frame img {
width: 100%;
height: auto;
}
.controls {
margin-top: 10px;
}
</style>
};
</script>
<style>
.floating-frame {
margin: 15px;
float: left;
height: auto;
border: 1px solid #000;
border-radius: 4px;
overflow: hidden;
z-index: 5000;
position: fixed;
cursor: move;
bottom: 0;
right: 0;
}
.handle {
width: 100%;
height: 20px;
background: #ccc;
cursor: move;
text-align: center;
}
.floating-frame img {
width: 100%;
height: auto;
}
.controls {
margin-top: 10px;
}
</style>

View File

@ -212,30 +212,38 @@
</textarea>
<input type="file" ref="fileDialog" style="display: none" @change="addFiles" multiple />
<button type="button" @click.stop="$refs.fileDialog.click()" title="Add files"
class="absolute inset-y-0 right-0 flex items-center mr-2 w-6 hover:text-secondary duration-75 active:scale-90">
<i data-feather="file-plus"></i>
</button>
</div>
<!-- BUTTONS -->
<div class="inline-flex justify-center rounded-full ">
<button v-if="!loading"
type="button"
@click="startSpeechRecognition"
title="Press and talk"
:class="{ 'text-red-500': isLesteningToVoice }"
class="w-6 hover:text-secondary duration-75 active:scale-90 cursor-pointer"
>
<i data-feather="mic"></i>
</button>
<input type="file" ref="fileDialog" style="display: none" @change="addFiles" multiple />
<button type="button" @click.stop="$refs.fileDialog.click()" title="Add files"
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
<i data-feather="file-plus"></i>
</button>
<button type="button" @click.stop="takePicture" title="take a shot from camera"
class=" w-6 hover:text-secondary duration-75 active:scale-90">
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
<i data-feather="camera"></i>
</button>
<button type="button" @click.stop="addWebLink" title="add web link"
class="m-1 w-6 hover:text-secondary duration-75 active:scale-90">
<i data-feather="globe"></i>
</button>
<button v-if="!loading" type="button" @click="makeAnEmptyUserMessage" title="New empty user message"
class=" w-6 text-blue-400 hover:text-secondary duration-75 active:scale-90">
@ -251,7 +259,7 @@
<span class="sr-only">New empty message</span>
</button>
<button v-if="!loading" type="button" @click="submit"
<button v-if="!loading" type="button" @click="submit" title="Send"
class=" w-6 hover:text-secondary duration-75 active:scale-90">
<i data-feather="send"></i>
@ -280,7 +288,6 @@
</div>
</form>
</div>
<Toast ref="toast"/>
<UniversalForm ref="universalForm" class="z-20" />
</template>
<style scoped>
@ -323,14 +330,13 @@ import InteractiveMenu from '@/components/InteractiveMenu.vue';
import { useStore } from 'vuex'; // Import the useStore function
import { inject } from 'vue';
import socket from '@/services/websocket.js'
import Toast from '../components/Toast.vue'
import UniversalForm from '../components/UniversalForm.vue';
import modelImgPlaceholder from "../assets/default_model.png"
console.log("modelImgPlaceholder:",modelImgPlaceholder)
const bUrl = import.meta.env.VITE_LOLLMS_API_BASEURL
export default {
name: 'ChatBox',
emits: ["messageSentEvent", "sendCMDEvent", "stopGenerating", "loaded", "createEmptyUserMessage", "createEmptyAIMessage", "personalitySelected"],
emits: ["messageSentEvent", "sendCMDEvent", "stopGenerating", "loaded", "createEmptyUserMessage", "createEmptyAIMessage", "personalitySelected","addWebLink"],
props: {
onTalk: Function,
discussionList: Array,
@ -339,7 +345,6 @@ export default {
},
components: {
Toast,
UniversalForm,
MountedPersonalities,
MountedPersonalitiesList,
@ -433,17 +438,17 @@ export default {
if (response && response.data) {
console.log('binding set with new settings', response.data)
this.$refs.toast.showToast("Binding settings updated successfully!", 4, true)
this.$store.state.toast.showToast("Binding settings updated successfully!", 4, true)
} else {
this.$refs.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
this.$store.state.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
this.isLoading = false
}
})
} catch (error) {
this.$refs.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
this.$store.state.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
this.isLoading = false
}
@ -451,7 +456,7 @@ export default {
})
} else {
this.$refs.toast.showToast("Binding has no settings", 4, false)
this.$store.state.toast.showToast("Binding has no settings", 4, false)
this.isLoading = false
}
@ -460,7 +465,7 @@ export default {
} catch (error) {
this.isLoading = false
this.$refs.toast.showToast("Could not open binding settings. Endpoint error: " + error.message, 4, false)
this.$store.state.toast.showToast("Could not open binding settings. Endpoint error: " + error.message, 4, false)
}
},
async unmountPersonality(pers) {
@ -472,7 +477,7 @@ export default {
if (res.status) {
this.$store.state.config.personalities = res.personalities
this.$refs.toast.showToast("Personality unmounted", 4, true)
this.$store.state.toast.showToast("Personality unmounted", 4, true)
//pers.isMounted = false
this.$store.dispatch('refreshMountedPersonalities');
@ -483,12 +488,12 @@ export default {
// const res2 = await this.select_personality(lastPers.personality)
const res2 = await this.select_personality(pers.personality)
if (res2.status) {
this.$refs.toast.showToast("Selected personality:\n" + lastPers.name, 4, true)
this.$store.state.toast.showToast("Selected personality:\n" + lastPers.name, 4, true)
}
} else {
this.$refs.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
this.$store.state.toast.showToast("Could not unmount personality\nError: " + res.error, 4, false)
}
this.loading = false
@ -523,7 +528,7 @@ export default {
if (pers) {
if (pers.selected) {
this.$refs.toast.showToast("Personality already selected", 4, true)
this.$store.state.toast.showToast("Personality already selected", 4, true)
return
}
@ -537,10 +542,10 @@ export default {
const res = await this.select_personality(pers)
console.log('pers is mounted', res)
if (res && res.status && res.active_personality_id > -1) {
this.$refs.toast.showToast("Selected personality:\n" + pers.name, 4, true)
this.$store.state.toast.showToast("Selected personality:\n" + pers.name, 4, true)
} else {
this.$refs.toast.showToast("Error on select personality:\n" + pers.name, 4, false)
this.$store.state.toast.showToast("Error on select personality:\n" + pers.name, 4, false)
}
} else {
@ -615,10 +620,10 @@ export default {
console.log(response);
await this.$store.dispatch('refreshConfig');
await this.$store.dispatch('refreshModels');
this.$refs.toast.showToast(`Model changed to ${this.currentModel.name}`,4,true)
this.$store.state.toast.showToast(`Model changed to ${this.currentModel.name}`,4,true)
this.selecting_model=false
}).catch(err=>{
this.$refs.toast.showToast(`Error ${err}`,4,true)
this.$store.state.toast.showToast(`Error ${err}`,4,true)
this.selecting_model=false
});
@ -663,7 +668,7 @@ export default {
console.log('File sent successfully');
this.isFileSentList[this.filesList.length-1]=true;
console.log(this.isFileSentList)
this.onShowToastMessage("File uploaded successfully",4,true);
this.$store.state.toast.showToast("File uploaded successfully",4,true);
this.loading = false
next();
}
@ -769,6 +774,10 @@ export default {
sendCMDEvent(cmd){
this.$emit('sendCMDEvent', cmd)
},
addWebLink(){
console.log("Emitting addWebLink")
this.$emit('addWebLink')
},
takePicture(){
socket.emit('take_picture')
socket.on('picture_taken',()=>{

View File

@ -1,63 +0,0 @@
<template>
<div class="floating-frame">
<img v-if="isVideoActive" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" />
<div class="controls">
<button v-if="!isVideoActive" class="w-full bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startVideoStream">Activate Video</button>
<button v-if="isVideoActive" class="w-full bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopVideoStream">Deactivate Video</button>
</div>
</div>
</template>
<script>
import socket from '@/services/websocket.js';
export default {
data() {
return {
isVideoActive: false,
imageDataUrl: null
};
},
methods: {
startVideoStream() {
this.isVideoActive = true;
socket.emit('start_webcam_video_stream');
},
stopVideoStream() {
this.isVideoActive = false;
this.imageData = null;
socket.emit('stop_webcam_video_stream');
}
},
mounted() {
socket.on('image', (imageBase64) => {
if (this.isVideoActive) {
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
}
});
}
};
</script>
<style>
.floating-frame {
margin: 15px;
float: left;
width: 200px;
height: auto;
border: 1px solid #000;
border-radius: 4px;
overflow: hidden;
z-index: 5000;
}
.floating-frame img {
width: 100%;
height: auto;
}
.controls {
margin-top: 10px;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div
class="relative group rounded-lg m-2 shadow-lg hover:border-primary dark:hover:border-primary hover:border-solid hover:border-2 border-2 border-transparent even:bg-bg-light-discussion-odd dark:even:bg-bg-dark-discussion-odd flex flex-col flex-grow flex-wrap overflow-visible p-4 pb-2 ">
class="relative w-full group rounded-lg m-2 shadow-lg hover:border-primary dark:hover:border-primary hover:border-solid hover:border-2 border-2 border-transparent even:bg-bg-light-discussion-odd dark:even:bg-bg-dark-discussion-odd flex flex-col flex-grow flex-wrap overflow-visible p-4 pb-2 ">
<div class="flex flex-row gap-2 ">
<div class=" flex-shrink-0">
<!-- AVATAR -->

View File

@ -2,39 +2,33 @@
<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 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 flex-grow items-center">
<slot>
<div class="flex flex-row items-center w-full 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 flex-grow items-center h-auto">
<div v-if="t.log_type==0"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
<i data-feather="x"></i>
<span class="sr-only">Cross icon</span>
</div>
<div v-if="t.log_type==1"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
<i data-feather="check"></i>
<span class="sr-only">Check icon</span>
</div>
<div v-if="t.log_type==2"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
<i data-feather="info"></i>
<span class="sr-only"></span>
</div>
<div v-if="t.log_type==3"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-800 dark:text-orange-200">
<i data-feather="alert-triangle"></i>
<span class="sr-only"></span>
</div>
<div class="ml-3 text-sm font-normal whitespace-pre-wrap line-clamp-3 max-w-xs max-h-[400px] overflow-auto break-words" :title="t.message">{{ t.message }}</div>
<div v-if="t.log_type==0"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200">
<i data-feather="x"></i>
<span class="sr-only">Cross icon</span>
</div>
<div v-if="t.log_type==1"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
<i data-feather="check"></i>
<span class="sr-only">Check icon</span>
</div>
<div v-if="t.log_type==2"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200">
<i data-feather="info"></i>
<span class="sr-only"></span>
</div>
<div v-if="t.log_type==3"
class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-orange-500 bg-orange-100 rounded-lg dark:bg-orange-800 dark:text-orange-200">
<i data-feather="alert-triangle"></i>
<span class="sr-only"></span>
</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="flex ">
<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>

View File

@ -35,12 +35,12 @@
<div v-if="!isConnected" title="Connection status: Not connected" class="text-red-500 cursor-pointer">
<i data-feather="zap-off"></i>
</div>
<a href="#" @click="refreshPage">
<div class="text-2xl hover:text-primary duration-150" title="refresh page">
<a href="#" @click="restartProgram">
<div class="text-2xl hover:text-primary duration-150" title="restart program">
<i data-feather="refresh-ccw"></i>
</div>
</a>
<a href="https://github.com/ParisNeo/lollms-webui" target="_blank">
<div class="text-2xl hover:text-primary duration-150" title="Visit repository page">
@ -87,7 +87,8 @@
<div v-show="progress_visibility" role="status" class="fixed m-0 p-2 left-2 bottom-2 min-w-[24rem] max-w-[24rem] h-20 flex flex-col justify-center items-center pb-4 bg-blue-500 rounded-lg shadow-lg z-50 background-a">
<ProgressBar ref="progress" :progress="progress_value" class="w-full h-4"></ProgressBar>
<p class="text-2xl animate-pulse mt-2 text-white">{{ loading_infos }} ...</p>
</div>
</div>
<UniversalForm ref="universalForm" class="z-20" />
</header>
<body>
@ -100,6 +101,8 @@ import Discussion from '../components/Discussion.vue'
import Toast from '../components/Toast.vue'
import MessageBox from "@/components/MessageBox.vue";
import ProgressBar from "@/components/ProgressBar.vue";
import UniversalForm from '../components/UniversalForm.vue';
import { RouterLink } from 'vue-router'
import Navigation from './Navigation.vue'
@ -129,10 +132,15 @@ export default {
Discussion,
Toast,
MessageBox,
ProgressBar
ProgressBar,
UniversalForm,
},
watch:{
isConnected(){
if (!this.isConnected){
this.disconnected_audio.play()
this.$store.state.toast.showToast("Server suddenly disconnected. Please reboot the server", 410, false)
}
nextTick(() => {
feather.replace()
})
@ -141,6 +149,8 @@ export default {
},
data() {
return {
rebooting_the_tool_audio: new Audio("rebooting.mp3"),
disconnected_audio: new Audio("disconnected.mp3"),
database_selectorDialogVisible:false,
progress_visibility:false,
progress_value:0,
@ -153,6 +163,8 @@ export default {
},
mounted() {
this.$store.state.toast = this.$refs.toast
this.$store.state.messageBox = this.$refs.messageBox
this.$store.state.universalForm = this.$refs.universalForm
this.sunIcon = document.querySelector(".sun");
this.moonIcon = document.querySelector(".moon");
this.userTheme = localStorage.getItem("theme");
@ -172,9 +184,19 @@ export default {
this.systemTheme = window.matchMedia("prefers-color-scheme: dark").matches;
},
methods: {
refreshPage(event) {
restartProgram(event) {
event.preventDefault();
window.location.reload();
this.$store.state.api_get_req('restart_program')
this.rebooting_the_tool_audio.play()
this.$store.state.toast.showToast("Rebooting the app. Please wait...", 410, false)
//self.$store.state.toast.showToast("Rebooting the app. Please wait...", 50, true);
console.log("this.$store.state.api_get_req",this.$store.state.api_get_req)
setTimeout(()=>{
window.close();
},2000)
},
handleOk(inputText) {
console.log("Input text:", inputText);
},
// codeBlockTheme(theme) {
// const styleDark = document.createElement('link');

View File

@ -0,0 +1,131 @@
<template>
<div class="floating-frame bg-white" :style="{ bottom: position.bottom + 'px', right: position.right + 'px', 'z-index': zIndex }" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">
<div class="handle" @mousedown.stop="startDrag" @mouseup.stop="stopDrag">Drag Me</div>
<img v-if="isVideoActive && imageDataUrl!=null" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" />
<p v-if="isVideoActive && imageDataUrl==null" :src="imageDataUrl" alt="Webcam Frame" width="300" height="300" >Loading. Please wait...</p>
<div class="controls">
<button v-if="!isVideoActive" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded" @click="startVideoStream"><i data-feather='video'></i></button>
<button v-if="isVideoActive" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded" @click="stopVideoStream"><i data-feather='video'></i></button>
<span v-if="isVideoActive">FPS: {{ frameRate }}</span>
</div>
</div>
</template>
<script>
import socket from '@/services/websocket.js';
import feather from 'feather-icons'
import { nextTick } from 'vue'
export default {
data() {
return {
isVideoActive: false,
imageDataUrl: null,
isDragging: false,
position: { bottom: 0, right: 0 },
dragStart: { x: 0, y: 0 },
zIndex: 0, // Add a data property for z-index
frameRate: 0,
frameCount: 0,
lastFrameTime: Date.now(),
};
},
methods: {
startVideoStream() {
this.isVideoActive = true;
socket.emit('start_webcam_video_stream');
nextTick(() => {
feather.replace()
})
},
stopVideoStream() {
this.isVideoActive = false;
this.imageData = null;
socket.emit('stop_webcam_video_stream');
nextTick(() => {
feather.replace()
})
},
startDrag(event) {
this.isDragging = true;
this.zIndex = 5001; // Increase z-index when dragging starts
this.dragStart.x = event.clientX;
this.dragStart.y = event.clientY;
document.addEventListener('mousemove', this.drag);
document.addEventListener('mouseup', this.stopDrag);
},
drag(event) {
if (this.isDragging) {
const deltaX = event.clientX - this.dragStart.x;
const deltaY = event.clientY - this.dragStart.y;
this.position.bottom -= deltaY;
this.position.right -= deltaX;
this.dragStart.x = event.clientX;
this.dragStart.y = event.clientY;
}
},
stopDrag() {
this.isDragging = false;
this.zIndex = 0; // Reset z-index when dragging stops
document.removeEventListener('mousemove', this.drag);
document.removeEventListener('mouseup', this.stopDrag);
}
},
mounted() {
feather.replace();
socket.on('video_stream_image', (imageBase64) => {
if (this.isVideoActive) {
this.imageDataUrl = 'data:image/jpeg;base64,' + imageBase64;
this.frameCount++;
const now = Date.now();
const delta = now - this.lastFrameTime;
if (delta >= 1000) { // Calculate FPS every second
this.frameRate = this.frameCount;
this.frameCount = 0;
this.lastFrameTime = now;
}
}
});
}
};
</script>
<style>
.floating-frame {
margin: 15px;
float: left;
height: auto;
border: 1px solid #000;
border-radius: 4px;
overflow: hidden;
z-index: 5000;
position: fixed;
cursor: move;
bottom: 0;
right: 0;
}
.handle {
width: 100%;
height: 20px;
background: #ccc;
cursor: move;
text-align: center;
}
.floating-frame img {
width: 100%;
height: auto;
}
.controls {
margin-top: 10px;
}
/* Add a container for the floating frames and apply flexbox */
.container {
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<div>
<div v-if="show" class="fixed top-0 left-0 w-full h-full flex justify-center items-center bg-black bg-opacity-50">
<div class="bg-white p-8 rounded">
<h2 class="text-xl font-bold mb-4">{{ promptText }}</h2>
<input type="text" v-model="inputText" class="border border-gray-300 px-4 py-2 rounded mb-4" />
<button @click="ok" class="bg-blue-500 text-white px-4 py-2 rounded mr-2">OK</button>
<button @click="cancel" class="bg-gray-500 text-white px-4 py-2 rounded">Cancel</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: false,
prompt: "",
inputText: ""
};
},
methods: {
showPanel() {
this.show = true;
},
ok() {
this.show = false;
this.$emit("ok", this.inputText);
},
cancel() {
this.show = false;
this.inputText = "";
}
},
props: {
promptText: {
type: String,
required: true
}
},
watch: {
promptText(newPrompt) {
this.prompt = newPrompt;
}
}
};
</script>

View File

@ -24,6 +24,11 @@ export const store = createStore({
state () {
return {
// count: 0,
universalForm:null,
toast:null,
messageBox:null,
api_get_req:null,
startSpeechRecognition:null,
ready:false,
loading_infos: "",
loading_progress: 0,
@ -498,6 +503,7 @@ let actionsExecuted = false;
app.mixin({
async created() {
if (!actionsExecuted) {
this.$store.state.api_get_req = api_get_req
actionsExecuted = true;
console.log("Calling")
this.$store.state.loading_infos = "Loading Configuration"

View File

@ -191,14 +191,14 @@
</div>
</div>
<div class="relative flex flex-row flex-grow mb-10 z-0">
<div class="relative flex flex-row flex-grow mb-10 z-0 w-full">
<!-- DISCUSSION LIST -->
<div class="mx-4 flex flex-col flex-grow " :class="isDragOverDiscussion ? 'pointer-events-none' : ''">
<div class="mx-4 flex flex-col flex-grow w-full " :class="isDragOverDiscussion ? 'pointer-events-none' : ''">
<div id="dis-list" :class="filterInProgress ? 'opacity-20 pointer-events-none' : ''"
class="flex flex-col flex-grow ">
class="flex flex-col flex-grow w-full">
<TransitionGroup v-if="list.length > 0" name="list">
<Discussion v-for="(item, index) in list" :key="item.id" :id="item.id" :title="item.title"
:selected="currentDiscussion.id == item.id" :loading="item.loading" :isCheckbox="isCheckbox"
@ -226,13 +226,13 @@
</div>
</div>
</transition>
<div v-if="isReady" class="relative flex flex-col flex-grow" >
<div v-if="isReady" class="relative flex flex-col flex-grow w-full" >
<div id="messages-list"
class=" z-0 flex flex-col flex-grow overflow-y-auto scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary"
class="w-full z-0 flex flex-col flex-grow overflow-y-auto scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary"
:class="isDragOverChat ? 'pointer-events-none' : ''">
<!-- CHAT AREA -->
<div class=" container pt-4 pb-10 mb-28">
<div class="container pt-4 pb-50 mb-50 w-full">
<TransitionGroup v-if="discussionArr.length > 0" name="list">
<Message v-for="(msg, index) in discussionArr"
:key="msg.id" :message="msg" :id="'msg-' + msg.id"
@ -249,7 +249,7 @@
</TransitionGroup>
<WelcomeComponent v-if="!currentDiscussion.id" />
<div><br><br><br><br><br><br><br></div>
</div>
<div
@ -261,9 +261,11 @@
:discussionList="discussionArr"
:on-show-toast-message="showToastMessage"
:on-talk="talk"
@personalitySelected="recoverFiles"
@messageSentEvent="sendMsg"
@sendCMDEvent="sendCmd"
@addWebLink="add_webpage"
@createEmptyUserMessage="createEmptyUserMessage"
@createEmptyAIMessage="createEmptyAIMessage"
@stopGenerating="stopGenerating"
@ -275,7 +277,6 @@
</div>
</div>
<MessageBox ref="messageBox" />
<ChoiceDialog reference="database_selector" class="z-20"
:show="database_selectorDialogVisible"
:choices="databases"
@ -287,6 +288,8 @@
<ProgressBar ref="progress" :progress="progress_value" class="w-full h-4"></ProgressBar>
<p class="text-2xl animate-pulse mt-2 text-white">{{ loading_infos }} ...</p>
</div>
<InputBox prompt-text="Enter the url to the page to use as discussion support" @ok="handleOk" ref="web_url_input_box"></InputBox>
</template>
@ -440,6 +443,24 @@ export default {
}
},
methods: {
add_webpage(){
console.log("addWebLink received")
this.$refs.web_url_input_box.showPanel();
},
handleOk(){
console.log("OK")
socket.on('web_page_added',()=>{
axios.get('/get_current_personality_files_list').then(res=>{
this.filesList = res.data.files;
console.log("this.filesList",this.filesList)
this.isFileSentList= res.data.files.map(file => {
return true;
});
console.log(`Files recovered: ${this.filesList}`)
})
});
socket.emit('add_webpage',{'url':this.$refs.web_url_input_box.inputText})
},
show_progress(data){
this.progress_visibility_val = true;
},
@ -463,7 +484,7 @@ export default {
// open form
this.$refs.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
this.$store.state.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
// send new data
try {
axios.post('/set_active_binding_settings',
@ -554,13 +575,13 @@ export default {
this.$store.state.toast.showToast("Settings saved!",4,true)
}
else
this.$refs.messageBox.showMessage("Error: Couldn't save settings!")
this.$store.state.messageBox.showMessage("Error: Couldn't save settings!")
return res.data;
}
})
.catch(error => {
console.log(error.message, 'save_configuration')
this.$refs.messageBox.showMessage("Couldn't save settings!")
this.$store.state.messageBox.showMessage("Couldn't save settings!")
return { 'status': false }
});
@ -1206,7 +1227,7 @@ export default {
this.$store.state.toast.showToast(notif.content, notif.duration, notif.notification_type)
}
else if(notif.display_type==1){
this.$refs.messageBox.showMessage(notif.content)
this.$store.state.messageBox.showMessage(notif.content)
}
this.chime.play()
},
@ -1929,8 +1950,8 @@ export default {
ChatBox,
WelcomeComponent,
ChoiceDialog,
MessageBox,
ProgressBar
ProgressBar,
InputBox
},
watch: {
progress_visibility_val(newVal) {
@ -2062,8 +2083,9 @@ export default {
<script setup>
import Discussion from '../components/Discussion.vue'
import ChoiceDialog from '@/components/ChoiceDialog.vue'
import MessageBox from "@/components/MessageBox.vue";
import ProgressBar from "@/components/ProgressBar.vue";
import InputBox from "@/components/input_box.vue";
import Message from '../components/Message.vue'
import ChatBox from '../components/ChatBox.vue'

View File

@ -1,28 +1,30 @@
<template>
<div class="flex-col w-[800]px y-overflow scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<div v-if="!activePersonality || !activePersonality.scene_path" class="text-center">
<!-- Display text when there's no scene_path or empty avatar -->
Personality does not have a 3d avatar.
</div>
<div v-if="!activePersonality || (!activePersonality.avatar || activePersonality.avatar === '')" class="text-center">
Personality does not have an avatar.
</div>
<FloatingFrame />
<AudioFrame />
<div class="floating-frame2">
<div v-html="htmlContent"></div>
</div>
</div>
<div ref="webglContainer">
</div>
<div ref="webglContainer">
</div>
<div class="flex-col y-overflow scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<div v-if="!activePersonality || !activePersonality.scene_path" class="text-center">
<!-- Display text when there's no scene_path or empty avatar -->
Personality does not have a 3d avatar.
</div>
<div v-if="!activePersonality || (!activePersonality.avatar || activePersonality.avatar === '')" class="text-center">
Personality does not have an avatar.
</div>
<div class="floating-frame2">
<div v-html="htmlContent"></div>
</div>
</div>
<VideoFrame ref="video_frame"/>
<AudioFrame ref="audio_frame"/>
</template>
<script>
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { TextureLoader } from 'three';
import FloatingFrame from '@/components/FloatingFrame.vue';
import VideoFrame from '@/components/VideoFrame.vue';
import AudioFrame from '@/components/AudioFrame.vue';
import feather from 'feather-icons'
import { nextTick } from 'vue'
export default {
@ -38,7 +40,7 @@
},
},
components: {
FloatingFrame,
VideoFrame,
AudioFrame
},
computed: {
@ -59,6 +61,11 @@
console.log("Personality:", this.personality)
this.initWebGLScene();
this.updatePersonality();
nextTick(() => {
feather.replace()
})
this.$refs.video_frame.position = { bottom: 0, right: 0 }
this.$refs.audio_frame.position = { bottom: 0, right: 100 }
},
beforeDestroy() {
// Clean up WebGL resources here
@ -163,7 +170,6 @@
}
.floating-frame2 {
margin: 15px;
float: left;
width: 800px;
height: auto;
border: 1px solid #000;

View File

@ -1,5 +1,5 @@
<template>
<div class="container overflow-y-scroll flex flex-col shadow-lg p-10 pt-0 overflow-y-scroll w-full dark:bg-bg-dark scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<div class="container overflow-y-scroll flex flex-row shadow-lg p-10 pt-0 overflow-y-scroll w-full dark:bg-bg-dark scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary">
<!-- CONTROL PANEL -->
<div
class="sticky top-0 z-10 flex flex-row mb-2 p-3 gap-3 w-full rounded-b-lg bg-bg-light-tone dark:bg-bg-dark-tone shadow-lg">
@ -84,10 +84,9 @@
</div>
</div>
<div :class="isLoading ? 'pointer-events-none opacity-30' : ''">
<div :class="isLoading ? 'pointer-events-none opacity-30 w-full' : 'w-full'">
<!-- DISK AND RAM USAGE -->
<div
class="flex flex-col mb-2 rounded-lg bg-bg-light-tone dark:bg-bg-dark-tone hover:bg-bg-light-tone-panel hover:dark:bg-bg-dark-tone-panel duration-150 shadow-lg">
<div class="flex flex-col mb-2 rounded-lg bg-bg-light-tone dark:bg-bg-dark-tone hover:bg-bg-light-tone-panel hover:dark:bg-bg-dark-tone-panel duration-150 shadow-lg">
<div class="flex flex-row p-3">
<button @click.stop="sc_collapsed = !sc_collapsed"
class="text-2xl hover:text-primary p-2 -m-2 w-full text-left flex flex-row items-center ">
@ -1652,8 +1651,6 @@
</div>
<YesNoDialog ref="yesNoDialog" class="z-20" />
<AddModelDialog ref="addmodeldialog" />
<MessageBox ref="messageBox" />
<UniversalForm ref="universalForm" class="z-20" />
<ChoiceDialog class="z-20"
:show="variantSelectionDialogVisible"
:choices="variant_choices"
@ -1748,7 +1745,6 @@ import filesize from '../plugins/filesize'
import axios from "axios";
import feather from 'feather-icons'
import { nextTick, TransitionGroup } from 'vue'
import MessageBox from "@/components/MessageBox.vue";
import YesNoDialog from "@/components/YesNoDialog.vue";
import ModelEntry from '@/components/ModelEntry.vue';
import PersonalityViewer from '@/components/PersonalityViewer.vue';
@ -1762,7 +1758,6 @@ import defaultExtensionImgPlaceholder from "../assets/extension.png"
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";
import Card from "@/components/Card.vue"
@ -1778,14 +1773,12 @@ axios.defaults.baseURL = import.meta.env.VITE_LOLLMS_API_BASEURL
export default {
components: {
AddModelDialog,
MessageBox,
YesNoDialog,
ModelEntry,
// eslint-disable-next-line vue/no-unused-components
PersonalityViewer,
PersonalityEntry,
BindingEntry,
UniversalForm,
ChoiceDialog,
Card,
RadioOptions,
@ -1881,23 +1874,10 @@ export default {
async created() {
socket.on('loading_text',this.on_loading_text);
this.updateHasUpdates();
socket.on('notification', this.notify)
//await socket.on('install_progress', this.progressListener);
//refreshHardwareUsage()
},
methods: {
notify(notif){
nextTick(() => {
const msgList = document.getElementById('messages-list')
this.scrollBottom(msgList)
})
if(notif.display_type==0){
this.$store.state.toast.showToast(notif.content, notif.duration, notif.notification_type)
}
else if(notif.display_type==1){
this.$refs.messageBox.showMessage(notif.content)
}
},
methods: {
load_more_models(){
if(this.models_zoo_initialLoadCount+10<this.models_zoo.length){
this.models_zoo_initialLoadCount+=10
@ -2616,8 +2596,8 @@ export default {
this.isLoading = false
console.log('install_binding', res)
if (res.data.status) {
this.$store.state.toast.showToast("Installed binding successfully!", 4, true)
this.update_binding(binding_object.binding.folder);
this.$store.state.toast.showToast("Binding installed successfully!", 4, true)
this.$store.state.messageBox.showMessage("It is advised to reboot the application after installing a binding")
} else {
this.$store.state.toast.showToast("Could not reinstall binding", 4, false)
}
@ -2685,7 +2665,9 @@ export default {
this.isLoading = false
console.log('reinstall_binding', res)
if (res.data.status) {
this.$store.state.toast.showToast("Reinstalled binding successfully!", 4, true)
this.$store.state.toast.showToast("Binding reinstalled successfully!", 4, true)
this.$store.state.messageBox.showMessage("It is advised to reboot the application after installing a binding")
} else {
this.$store.state.toast.showToast("Could not reinstall binding", 4, false)
}
@ -2732,7 +2714,7 @@ export default {
console.log('ext sett', res)
if (res.data && Object.keys(res.data).length > 0) {
this.$refs.universalForm.showForm(res.data, "Extension settings - " + extensionEntry.extension.name, "Save changes", "Cancel").then(res => {
this.$store.state.universalForm.showForm(res.data, "Extension settings - " + extensionEntry.extension.name, "Save changes", "Cancel").then(res => {
// send new data
try {
@ -2782,22 +2764,21 @@ export default {
// open form
this.$refs.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
this.$store.state.universalForm.showForm(res.data, "Binding settings - " + bindingEntry.binding.name, "Save changes", "Cancel").then(res => {
// send new data
try {
axios.post('/set_active_binding_settings',
res).then(response => {
if (response && response.data) {
console.log('binding set with new settings', response.data)
this.$store.state.toast.showToast("Binding settings updated successfully!", 4, true)
axios.get('/update_binding_settings').then((res) => {
this.$store.state.toast.showToast("Binding settings committed successfully!", 4, true)
})
} else {
this.$store.state.toast.showToast("Did not get binding settings responses.\n" + response, 4, false)
this.isLoading = false
}
})
} catch (error) {
this.$store.state.toast.showToast("Did not get binding settings responses.\n Endpoint error: " + error.message, 4, false)
@ -2855,7 +2836,7 @@ export default {
console.log('pers sett', res)
if (res.data && Object.keys(res.data).length > 0) {
this.$refs.universalForm.showForm(res.data, "Personality settings - " + persEntry.personality.name, "Save changes", "Cancel").then(res => {
this.$store.state.universalForm.showForm(res.data, "Personality settings - " + persEntry.personality.name, "Save changes", "Cancel").then(res => {
// send new data
try {
@ -3169,16 +3150,16 @@ export default {
.then((res) => {
if (res) {
if (res.status) {
// this.$refs.messageBox.showMessage("Settings saved!")
// this.$store.state.messageBox.showMessage("Settings saved!")
}
else
this.$refs.messageBox.showMessage("Error: Couldn't save settings!")
this.$store.state.messageBox.showMessage("Error: Couldn't save settings!")
return res.data;
}
})
.catch(error => {
console.log(error.message, 'save_configuration')
this.$refs.messageBox.showMessage("Couldn't save settings!")
this.$store.state.messageBox.showMessage("Couldn't save settings!")
return { 'status': false }
});
@ -3191,15 +3172,15 @@ export default {
.then((res) => {
if (res) {
if (res.status)
this.$refs.messageBox.showMessage("Settings have been reset correctly")
this.$store.state.messageBox.showMessage("Settings have been reset correctly")
else
this.$refs.messageBox.showMessage("Couldn't reset settings!")
this.$store.state.messageBox.showMessage("Couldn't reset settings!")
return res.data;
}
})
.catch(error => {
console.log(error.message, 'reset_configuration')
this.$refs.messageBox.showMessage("Couldn't reset settings!")
this.$store.state.messageBox.showMessage("Couldn't reset settings!")
return { 'status': false }
});
// Perform delete operation
@ -3475,7 +3456,7 @@ export default {
console.log('mount pers', pers)
if(pers.personality.disclaimer!=""){
this.$refs.messageBox.showMessage(pers.personality.disclaimer)
this.$store.state.messageBox.showMessage(pers.personality.disclaimer)
}
if (!pers) { return }

@ -1 +1 @@
Subproject commit 7ac68c7e29775c79d97f3d1bd1aa2c7b5a46151f
Subproject commit 0346a1efc2bc16d4e700d603fedc3ecbfe9b58bd

@ -1 +1 @@
Subproject commit 7df292e43dd6f674cee6a620540836b77f0a2380
Subproject commit 5385b1b599c0bbf9fbd71163be2f9a0184f19ab5

@ -1 +1 @@
Subproject commit c1594f8e30bff5e5909f11289311079f13241260
Subproject commit 97ec1416d0af1377a8cfc96d9f55cb3f31681293