This commit is contained in:
Saifeddine ALOUI 2023-07-14 01:42:29 +02:00
parent 34fb6a8a83
commit b72542c319
14 changed files with 275 additions and 108 deletions

View File

@ -458,7 +458,7 @@ class LoLLMsAPPI(LollmsApplication):
message_id = int(data['id'])
message = data["prompt"]
self.current_user_message_id = message_id
self.connections[client_id]['generation_thread'] = threading.Thread(target=self.start_message_generation, args=(message, message_id, client_id))
self.connections[client_id]['generation_thread'] = threading.Thread(target=self.start_message_generation, args=(message, message_id, client_id, True))
self.connections[client_id]['generation_thread'].start()
# generation status
@ -615,11 +615,9 @@ class LoLLMsAPPI(LollmsApplication):
message_id = 0
return message_id
def prepare_reception(self):
self.current_generated_text = ""
def prepare_reception(self, client_id):
self.connections[client_id]["generated_text"] = ""
self.nb_received_tokens = 0
self.full_text = ""
self.is_bot_text_started = False
def create_new_discussion(self, title):
self.current_discussion = self.db.create_discussion(title)
@ -719,7 +717,7 @@ class LoLLMsAPPI(LollmsApplication):
if message_type == MSG_TYPE.MSG_TYPE_CHUNK:
self.current_generated_text += chunk
self.connections[client_id]["generated_text"] += chunk
self.nb_received_tokens += 1
ASCIIColors.green(f"Received {self.nb_received_tokens} tokens",end="\r")
sys.stdout = sys.__stdout__
@ -727,13 +725,13 @@ class LoLLMsAPPI(LollmsApplication):
detected_anti_prompt = False
anti_prompt_to_remove=""
for prompt in self.personality.anti_prompts:
if prompt.lower() in self.current_generated_text.lower():
if prompt.lower() in self.connections[client_id]["generated_text"].lower():
detected_anti_prompt=True
anti_prompt_to_remove = prompt.lower()
if not detected_anti_prompt:
self.socketio.emit('message', {
'data': self.current_generated_text,
'data': self.connections[client_id]["generated_text"],
'user_message_id':self.current_user_message_id,
'ai_message_id':self.current_ai_message_id,
'discussion_id':self.current_discussion.discussion_id,
@ -741,7 +739,7 @@ class LoLLMsAPPI(LollmsApplication):
}, room=client_id
)
self.socketio.sleep(0.01)
self.current_discussion.update_message(self.current_ai_message_id, self.current_generated_text)
self.current_discussion.update_message(self.current_ai_message_id, self.connections[client_id]["generated_text"])
# if stop generation is detected then stop
if not self.cancel_gen:
return True
@ -750,17 +748,17 @@ class LoLLMsAPPI(LollmsApplication):
ASCIIColors.warning("Generation canceled")
return False
else:
self.current_generated_text = self.remove_text_from_string(self.current_generated_text, anti_prompt_to_remove)
self.connections[client_id]["generated_text"] = self.remove_text_from_string(self.connections[client_id]["generated_text"], anti_prompt_to_remove)
ASCIIColors.warning("The model is halucinating")
return False
# Stream the generated text to the main process
elif message_type == MSG_TYPE.MSG_TYPE_FULL:
self.current_generated_text = chunk
self.connections[client_id]["generated_text"] = chunk
self.nb_received_tokens += 1
ASCIIColors.green(f"Received {self.nb_received_tokens} tokens",end="\r",flush=True)
self.socketio.emit('message', {
'data': self.current_generated_text,
'data': self.connections[client_id]["generated_text"],
'user_message_id':self.current_user_message_id,
'ai_message_id':self.current_ai_message_id,
'discussion_id':self.current_discussion.discussion_id,
@ -784,7 +782,7 @@ class LoLLMsAPPI(LollmsApplication):
return True
def generate(self, full_prompt, prompt, n_predict=50, callback=None):
def generate(self, full_prompt, prompt, n_predict, client_id, callback=None):
if self.personality.processor is not None:
ASCIIColors.success("Running workflow")
try:
@ -803,11 +801,10 @@ class LoLLMsAPPI(LollmsApplication):
print("Finished executing the workflow")
return
self._generate(full_prompt, n_predict, callback)
self._generate(full_prompt, n_predict, client_id, callback)
ASCIIColors.success("\nFinished executing the generation")
def _generate(self, prompt, n_predict=1024, callback=None):
self.current_generated_text = ""
def _generate(self, prompt, n_predict, client_id, callback=None):
self.nb_received_tokens = 0
if self.model is not None:
ASCIIColors.info(f"warmup for generating {n_predict} tokens")
@ -853,7 +850,10 @@ class LoLLMsAPPI(LollmsApplication):
# First we need to send the new message ID to the client
if is_continue:
self.current_ai_message_id = message_id
self.current_discussion.load_message(message_id)
self.connections[client_id]["generated_text"] = self.current_discussion.content
else:
self.connections[client_id]["generated_text"] = ""
self.current_ai_message_id = self.current_discussion.add_message(
self.personality.name,
"",
@ -884,12 +884,13 @@ class LoLLMsAPPI(LollmsApplication):
# prepare query and reception
self.discussion_messages, self.current_message, tokens = self.prepare_query(message_id, is_continue)
self.prepare_reception()
self.prepare_reception(client_id)
self.generating = True
self.generate(
self.discussion_messages,
self.current_message,
n_predict = self.config.ctx_size-len(tokens)-1,
n_predict = self.config.ctx_size-len(tokens)-1,
client_id=client_id,
callback=partial(self.process_chunk,client_id = client_id)
)
@ -897,13 +898,13 @@ class LoLLMsAPPI(LollmsApplication):
print("## Done Generation ##")
print()
self.current_discussion.update_message(self.current_ai_message_id, self.current_generated_text)
self.full_message_list.append(self.current_generated_text)
self.current_discussion.update_message(self.current_ai_message_id, self.connections[client_id]["generated_text"].strip())
self.full_message_list.append(self.connections[client_id]["generated_text"])
self.cancel_gen = False
# Send final message
self.socketio.emit('final', {
'data': self.current_generated_text,
'data': self.connections[client_id]["generated_text"],
'ai_message_id':self.current_ai_message_id,
'parent':self.current_user_message_id, 'discussion_id':self.current_discussion.discussion_id,
"status":'model_not_ready',
@ -911,7 +912,7 @@ class LoLLMsAPPI(LollmsApplication):
'logo': "",
"bot": self.personality.name,
"user": self.personality.user_name,
"message":self.current_generated_text,
"message":self.connections[client_id]["generated_text"],
"user_message_id": self.current_user_message_id,
"ai_message_id": self.current_ai_message_id,

View File

@ -312,10 +312,47 @@ class Discussion:
self.discussions_db = discussions_db
self.current_message_binding = ""
self.current_message_model = ""
self.content = ""
self.current_message_personality = ""
self.current_message_created_at = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
self.current_message_finished_generating_at=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def load_message(self, id):
"""Gets a list of messages information
Returns:
list: List of entries in the format {"id":message id, "sender":sender name, "content":message content, "type":message type, "rank": message rank}
"""
rows = self.discussions_db.select(
"SELECT id, sender, content, type, rank, parent, binding, model, personality, created_at, finished_generating_at FROM message WHERE id=?", (id,)
)
if len(rows)>0:
row = rows[0]
self.created_at = row[9]
self.current_message_binding = row[6]
self.current_message_model = row[7]
self.current_message_personality = row[8]
self.content = row[2]
self.current_message_created_at = row[9]
self.current_message_finished_generating_at = row[10]
return {
"id": row[0],
"sender": row[1],
"content": row[2],
"type": row[3],
"rank": row[4],
"parent": row[5],
"binding":row[6],
"model": row[7],
"personality": row[8],
"created_at": row[9],
"finished_generating_at": row[10]
}
def add_message(self, sender, content, message_type=0, rank=0, parent=0, binding="", model ="", personality="", created_at=None, finished_generating_at=None):
"""Adds a new message to the discussion
@ -334,6 +371,7 @@ class Discussion:
self.current_message_binding = binding
self.current_message_model = model
self.current_message_personality = personality
self.content = content
self.current_message_created_at = created_at
self.current_message_finished_generating_at = finished_generating_at

37
app.py
View File

@ -121,6 +121,8 @@ class LoLLMsWebUI(LoLLMsAPPI):
# Endpoints
# =========================================================================================
self.add_endpoint("/reload_binding", "reload_binding", self.reload_binding, methods=["POST"])
self.add_endpoint("/install_model_from_path", "install_model_from_path", self.install_model_from_path, methods=["GET"])
@ -911,6 +913,41 @@ class LoLLMsWebUI(LoLLMsAPPI):
print(f"Couldn't build binding: [{ex}]")
return jsonify({"status":False, 'error':str(ex)})
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)
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()
except Exception as ex:
print(f"Couldn't reload personalities: [{ex}]")
return jsonify({"status": True})
except Exception as ex:
print(f"Couldn't build binding: [{ex}]")
return jsonify({"status":False, 'error':str(ex)})
def p_mount_personality(self):
print("- Mounting personality")

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-0016d472.js"></script>
<link rel="stylesheet" href="/assets/index-4747dbaa.css">
<script type="module" crossorigin src="/assets/index-dafce67e.js"></script>
<link rel="stylesheet" href="/assets/index-3b60082b.css">
</head>
<body>
<div id="app"></div>

View File

@ -37,12 +37,13 @@
<span class="sr-only">Settings</span>
</button> -->
<!-- - NOT IMPLEMENTED -->
<!--
<button type="button" title="Help - Not implemented"
<button v-if="selected" type="button" title="Reload binding"
@click="toggleReloadBinding"
class="hover:text-secondary duration-75 active:scale-90 font-medium rounded-lg text-sm p-2 text-center inline-flex items-center " @click.stop="">
<i data-feather="help-circle" class="w-5"></i>
<i data-feather="refresh-cw" class="w-5"></i>
<span class="sr-only">Help</span>
</button> -->
</button>
</div>
</div>
@ -126,6 +127,7 @@ export default {
onReinstall: Function,
onInstall: Function,
onSettings: Function,
onReloadBinding: Function,
selected: Boolean,
},
@ -162,6 +164,9 @@ export default {
toggleReinstall() {
this.onReinstall(this)
},
toggleReloadBinding(){
this.onReloadBinding(this)
},
toggleSettings() {
this.onSettings(this)
},

View File

@ -76,10 +76,14 @@
title="Copy message to clipboard" @click.stop="copyContentToClipboard()">
<i data-feather="copy"></i>
</div>
<div class="text-lg hover:text-secondary duration-75 active:scale-90 p-2" title="Resend message"
<div v-if="message.sender!=this.$store.state.mountedPers.name" class="text-lg hover:text-secondary duration-75 active:scale-90 p-2" title="Resend message"
@click.stop="resendMessage()">
<i data-feather="refresh-cw"></i>
</div>
<div v-if="message.sender==this.$store.state.mountedPers.name" class="text-lg hover:text-secondary duration-75 active:scale-90 p-2" title="Resend message"
@click.stop="continueMessage()">
<i data-feather="fast-forward"></i>
</div>
<!-- DELETE CONFIRMATION -->
<div v-if="deleteMsgMode" class="flex items-center duration-75">
<button class="text-2xl hover:text-red-600 duration-75 active:scale-90 p-2"
@ -174,7 +178,7 @@ import Step from './Step.vue';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Message',
emits: ['copy', 'delete', 'rankUp', 'rankDown', 'updateMessage', 'resendMessage'],
emits: ['copy', 'delete', 'rankUp', 'rankDown', 'updateMessage', 'resendMessage', 'continueMessage'],
components: {
MarkdownRenderer,
Step
@ -232,6 +236,9 @@ export default {
resendMessage() {
this.$emit('resendMessage', this.message.id, this.new_message_content)
},
continueMessage() {
this.$emit('continueMessage', this.message.id, this.new_message_content)
},
getImgUrl() {
if (this.message.sender == "user") {

View File

@ -196,7 +196,7 @@ export default {
},
async onPersonalitySelected(pers) {
// eslint-disable-next-line no-unused-vars
feather.replace()
console.log('ppa', pers)
if (pers) {

View File

@ -28,6 +28,12 @@
<a href="#" class=" hover:text-primary duration-150">Training</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'quantizing' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">
<a href="#" class=" hover:text-primary duration-150">Quantizing</a>
</RouterLink>
</li>
<li>
<RouterLink :to="{ name: 'help' }" class="p-2"
active-class="p-2 bg-bg-light-tone dark:bg-bg-dark-tone rounded-t-lg ">

View File

@ -3,6 +3,7 @@ import ExtensionsView from '../views/ExtensionsView.vue'
import HelpView from '../views/HelpView.vue'
import SettingsView from '../views/SettingsView.vue'
import TrainingView from '../views/TrainingView.vue'
import QuantizingView from '../views/QuantizingView.vue'
import DiscussionsView from '../views/DiscussionsView.vue'
@ -29,6 +30,11 @@ const router = createRouter({
name: 'training',
component: TrainingView
},
{
path: '/quantizing/',
name: 'quantizing',
component: QuantizingView
},
{
path: '/',
name: 'discussions',

View File

@ -193,7 +193,7 @@
<TransitionGroup v-if="discussionArr.length > 0" name="list">
<Message v-for="(msg, index) in discussionArr" :key="msg.id" :message="msg" :id="'msg-' + msg.id"
ref="messages" @copy="copyToClipBoard" @delete="deleteMessage" @rankUp="rankUpMessage"
@rankDown="rankDownMessage" @updateMessage="updateMessage" @resendMessage="resendMessage"
@rankDown="rankDownMessage" @updateMessage="updateMessage" @resendMessage="resendMessage" @continueMessage="continueMessage"
:avatar="getAvatar(msg.sender)" />
<!-- REMOVED FOR NOW, NEED MORE TESTING -->
@ -352,16 +352,6 @@ export default {
togglePanel() {
this.panelCollapsed = !this.panelCollapsed;
},
socketIOConnected() {
console.log("socketIOConnected")
this.$store.state.isConnected=true;
return true
},
socketIODisonnected() {
console.log("socketIOConnected")
this.$store.state.isConnected=false;
return true
},
async api_get_req(endpoint) {
try {
const res = await axios.get("/" + endpoint);
@ -633,17 +623,23 @@ export default {
}
},
scrollToElementInContainer(el, containerId) {
const topPos = el.offsetTop; //+ el.clientHeight
const container = document.getElementById(containerId)
// console.log(el.offsetTop , el.clientHeight, container.clientHeight)
try{
const topPos = el.offsetTop; //+ el.clientHeight
const container = document.getElementById(containerId)
// console.log(el.offsetTop , el.clientHeight, container.clientHeight)
container.scrollTo(
{
top: topPos,
behavior: "smooth",
}
)
container.scrollTo(
{
top: topPos,
behavior: "smooth",
}
)
}
catch{
}
},
scrollBottom(el) {
@ -715,7 +711,7 @@ export default {
model: msgObj.model,
personality: msgObj.personality,
sender: msgObj.user,
steps:[]
}
@ -723,9 +719,16 @@ export default {
this.discussionArr[index] = newMessage;
}
},
socketIOConnected() {
console.log("socketIOConnected")
this.$store.dispatch('setIsConnected',true);
return true
},
socketIODisonnected() {
console.log("socketIOConnected")
this.$store.dispatch('setIsConnected',false);
return true
},
createBotMsg(msgObj) {
// Update previous message with reponse user data
@ -820,11 +823,11 @@ export default {
},
streamMessageContent(msgObj) {
// Streams response message content from binding
console.log("Received message",msgObj)
const parent = msgObj.user_message_id
const discussion_id = msgObj.discussion_id
this.setDiscussionLoading(discussion_id, true);
if (this.currentDiscussion.id == discussion_id) {
this.isGenerating = true;
const index = this.discussionArr.findIndex((x) => x.parent == parent && x.id == msgObj.ai_message_id)
const messageItem = this.discussionArr[index]
@ -852,6 +855,7 @@ export default {
// Force an immediate UI update
this.$nextTick(() => {
// UI updates are rendered here
feather.replace()
});
},
@ -1050,6 +1054,10 @@ export default {
},
resendMessage(msgId, msg) {
nextTick(() => {
feather.replace()
})
// Resend message
this.isGenerating = true;
this.setDiscussionLoading(this.currentDiscussion.id, this.isGenerating);
@ -1060,6 +1068,30 @@ export default {
socket.emit('generate_msg_from', { prompt: msg, id: msgId });
}
else {
console.log("Already generating");
}
}
}).catch((error) => {
console.log("Error: Could not get generation status", error);
});
},
continueMessage(msgId, msg) {
nextTick(() => {
feather.replace()
})
// Resend message
this.isGenerating = true;
this.setDiscussionLoading(this.currentDiscussion.id, this.isGenerating);
axios.get('/get_generation_status', {}).then((res) => {
if (res) {
console.log(res);
if (!res.data.status) {
socket.emit('continue_generate_msg_from', { prompt: msg, id: msgId });
}
else {
console.log("Already generating");
@ -1097,7 +1129,7 @@ export default {
parent: msgObj.user_message_id,
personality:msgObj.personality,
rank:0,
steps:this.discussionArr[index].steps,
steps:msgObj.steps,
sender:msgObj.bot,
type:msgObj.type
}

View File

@ -43,6 +43,7 @@
<!-- SPINNER -->
<div v-if="isLoading" role="status">
<p>{{ loading_text }}</p>
<svg aria-hidden="true" class="w-6 h-6 animate-spin fill-secondary" viewBox="0 0 100 101"
fill="none" xmlns="http://www.w3.org/2000/svg">
<path
@ -558,6 +559,7 @@
:key="'index-' + index + '-' + binding.folder" :binding="binding"
:on-selected="onSelectedBinding" :on-reinstall="onReinstallBinding"
:on-install="onInstallBinding" :on-settings="onSettingsBinding"
:on-reload-binding="onReloadBinding"
:selected="binding.folder === configFile.binding_name">
</BindingEntry>
</TransitionGroup>
@ -1321,6 +1323,7 @@ export default {
data() {
return {
loading_text:"",
// Current personality language
personality_language:null,
// Current personality category
@ -1372,10 +1375,16 @@ export default {
}
},
async created() {
socket.on('loading_text',this.on_loading_text);
//await socket.on('install_progress', this.progressListener);
}, methods: {
on_loading_text(text){
console.log("Loading text",text)
this.loading_text = text
},
async constructor() {
this.isLoading = true
nextTick(() => {
@ -1986,6 +1995,31 @@ export default {
this.$refs.toast.showToast("Could not open binding settings. Endpoint error: " + error.message, 4, false)
}
},
onReloadBinding(binding_object){
this.isLoading = true
axios.post('/reload_binding', { name: binding_object.binding.folder }).then((res) => {
if (res) {
this.isLoading = false
console.log('reload_binding', res)
if (res.data.status) {
this.$refs.toast.showToast("Binding reloaded successfully!", 4, true)
} else {
this.$refs.toast.showToast("Could not reinstall binding", 4, false)
}
return res.data;
}
this.isLoading = false
})
// eslint-disable-next-line no-unused-vars
.catch(error => {
this.isLoading = false
this.$refs.toast.showToast("Could not reinstall binding\n" + error.message, 4, false)
return { 'status': false }
});
},
onSettingsPersonality(persEntry) {
try {
this.isLoading = true

View File

@ -9,7 +9,7 @@ export default ({ mode }) => {
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
return defineConfig({
plugins: [
vue()
],