Upgraded to V7.0

This commit is contained in:
Saifeddine ALOUI 2023-11-09 02:00:49 +01:00
parent 5f67de4868
commit 70a65f84f2
14 changed files with 110 additions and 123 deletions

8
CHANGELOG.md Normal file
View File

@ -0,0 +1,8 @@
# V 7.0:
Added changelog
Separated images from text in file upload
Added support for multimodal models
Added Dalle and gpt4 vision
enhanced interface
Upgraded the code execution
Now it is possible to execute lollms multiple times on the same PC. You can now have multiple instances with different port numbers and so a work in parallel.

View File

@ -726,6 +726,7 @@ class LoLLMsAPPI(LollmsApplication):
try:
ASCIIColors.print("warming up", ASCIIColors.color_bright_cyan)
generated_text = model.generate(fd,
n_predict=n_predicts,
callback=callback,
@ -1192,7 +1193,8 @@ class LoLLMsAPPI(LollmsApplication):
chunk:str,
message_type:MSG_TYPE
):
title[0] += chunk
if chunk:
title[0] += chunk
antiprompt = self.personality.detect_antiprompt(title[0])
if antiprompt:
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")
@ -1239,7 +1241,7 @@ class LoLLMsAPPI(LollmsApplication):
# Check if there are document files to add to the prompt
documentation = ""
if len(self.personality.files) > 0 and self.personality.vectorizer:
if len(self.personality.text_files) > 0 and self.personality.vectorizer:
if documentation=="":
documentation="Documentation:\n"
docs, sorted_similarities = self.personality.vectorizer.recover_text(current_message.content, top_k=self.config.data_vectorization_nb_chunks)
@ -1344,11 +1346,9 @@ class LoLLMsAPPI(LollmsApplication):
ASCIIColors.yellow(history)
ASCIIColors.bold("DISCUSSION")
# TODO: upghrade to asciicolors 0.1.4
# ASCIIColors.hilight(discussion_messages,"!@>",ASCIIColors.color_yellow,ASCIIColors.color_bright_red,False)
ASCIIColors.yellow(discussion_messages)
ASCIIColors.hilight(discussion_messages,"!@>",ASCIIColors.color_yellow,ASCIIColors.color_bright_red,False)
ASCIIColors.bold("Final prompt")
#ASCIIColors.hilight(prompt_data,"!@>",ASCIIColors.color_yellow,ASCIIColors.color_bright_red,False)
ASCIIColors.yellow(prompt_data)
ASCIIColors.hilight(prompt_data,"!@>",ASCIIColors.color_yellow,ASCIIColors.color_bright_red,False)
ASCIIColors.info(f"prompt size:{len(tokens)} tokens")
ASCIIColors.info(f"available space after doc and history:{available_space} tokens")
@ -1356,81 +1356,6 @@ class LoLLMsAPPI(LollmsApplication):
return prompt_data, current_message.content, tokens
"""
def prepare_query(self, client_id, message_id=-1, is_continue=False):
messages = self.connections[client_id]["current_discussion"].get_messages()
full_message_list = []
for i, message in enumerate(messages):
if message.id< message_id or (message_id==-1 and i<len(messages)-1):
if message.content!='' and (message.message_type<=MSG_TYPE.MSG_TYPE_FULL_INVISIBLE_TO_USER.value and message.message_type!=MSG_TYPE.MSG_TYPE_FULL_INVISIBLE_TO_AI.value):
full_message_list.append("\n"+self.config.discussion_prompt_separator+message.sender+": "+message.content.strip())
else:
break
link_text = "\n" #self.personality.link_text
if not is_continue:
full_message_list.append(self.config.discussion_prompt_separator +message.sender.strip().replace(":","")+": "+message.content.strip()+link_text+self.personality.ai_message_prefix.strip())
else:
full_message_list.append(self.config.discussion_prompt_separator +message.sender.strip().replace(":","")+": "+message.content.strip())
composed_messages = link_text.join(full_message_list)
t = self.model.tokenize(composed_messages)
cond_tk = self.model.tokenize(self.personality.personality_conditioning)
n_t = len(t)
n_cond_tk = len(cond_tk)
max_prompt_stx_size = 3*int(self.config.ctx_size/4)
if n_cond_tk+n_t>max_prompt_stx_size:
nb_tk = max_prompt_stx_size-n_cond_tk
composed_messages = self.model.detokenize(t[-nb_tk:])
ASCIIColors.warning(f"Cropping discussion to fit context [using {nb_tk} tokens/{self.config.ctx_size}]")
discussion_messages = composed_messages
conditionning = self.personality.personality_conditioning
if self.config["override_personality_model_parameters"]:
conditionning = conditionning+ "\n!@>user description:\nName:"+self.config["user_name"]+"\n"+self.config["user_description"]+"\n"
str_docs = ""
if self.config.use_discussions_history:
if self.discussions_store is not None:
pr = PromptReshaper("{{conditionning}}\n!@>document chunks:\n{{doc}}\n{{content}}")
docs, sorted_similarities = self.discussions_store.recover_text(message.content, top_k=self.config.data_vectorization_nb_chunks)
for doc, infos in zip(docs, sorted_similarities):
str_docs+=f"discussion chunk:\ndiscussion title: {infos[0]}\nchunk content:{doc}"
if len(self.personality.files)>0 and self.personality.vectorizer:
docs, sorted_similarities = self.personality.vectorizer.recover_text(message.content, top_k=self.config.data_vectorization_nb_chunks)
for doc, infos in zip(docs, sorted_similarities):
str_docs+=f"document chunk:\nchunk path: {infos[0]}\nchunk content:{doc}"
if str_docs!="":
pr = PromptReshaper("{{conditionning}}\n!@>document chunks:\n{{doc}}\n{{content}}")
discussion_messages = pr.build({
"doc":str_docs,
"conditionning":conditionning,
"content":discussion_messages
}, self.model.tokenize, self.model.detokenize, self.config.ctx_size-self.config.min_n_predict, place_holders_to_sacrifice=["content"])
else:
pr = PromptReshaper("{{conditionning}}\n{{content}}")
discussion_messages = pr.build({
"conditionning":conditionning,
"content":discussion_messages
}, self.model.tokenize, self.model.detokenize, self.config.ctx_size-self.config.min_n_predict, place_holders_to_sacrifice=["content"])
# remove extra returns
discussion_messages = self.clean_string(discussion_messages)
tokens = self.model.tokenize(discussion_messages)
if self.config["debug"]:
ASCIIColors.yellow(discussion_messages)
ASCIIColors.info(f"prompt size:{len(tokens)} tokens")
return discussion_messages, message.content, tokens
"""
def get_discussion_to(self, client_id, message_id=-1):
messages = self.connections[client_id]["current_discussion"].get_messages()
full_message_list = []
@ -1625,7 +1550,7 @@ class LoLLMsAPPI(LollmsApplication):
self.close_message(client_id)
elif message_type == MSG_TYPE.MSG_TYPE_CHUNK:
if self.nb_received_tokens:
if self.nb_received_tokens==0:
self.start_time = datetime.now()
dt =(datetime.now() - self.start_time).seconds
if dt==0:
@ -1634,7 +1559,8 @@ class LoLLMsAPPI(LollmsApplication):
ASCIIColors.green(f"Received {self.nb_received_tokens} tokens (speed: {spd:.2f}t/s) ",end="\r",flush=True)
sys.stdout = sys.__stdout__
sys.stdout.flush()
self.connections[client_id]["generated_text"] += chunk
if chunk:
self.connections[client_id]["generated_text"] += chunk
antiprompt = self.personality.detect_antiprompt(self.connections[client_id]["generated_text"])
if antiprompt:
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")

View File

@ -243,16 +243,16 @@ class DiscussionsDB:
discussion = {"id": discussion_id, "title":discussion_title, "messages": []}
rows = self.select(f"SELECT sender, content, message_type, rank, parent_message_id, binding, model, personality, created_at, finished_generating_at FROM message WHERE discussion_id=?",(discussion_id,))
for message_row in rows:
sender = message_row[1]
content = message_row[2]
content_type = message_row[3]
rank = message_row[4]
parent_message_id = message_row[5]
binding = message_row[6]
model = message_row[7]
personality = message_row[8]
created_at = message_row[9]
finished_generating_at = message_row[10]
sender = message_row[0]
content = message_row[1]
content_type = message_row[2]
rank = message_row[3]
parent_message_id = message_row[4]
binding = message_row[5]
model = message_row[6]
personality = message_row[7]
created_at = message_row[8]
finished_generating_at = message_row[9]
discussion["messages"].append(
{"sender": sender, "content": content, "message_type": content_type, "rank": rank, "parent_message_id": parent_message_id, "binding": binding, "model":model, "personality":personality, "created_at":created_at, "finished_generating_at":finished_generating_at}

24
app.py
View File

@ -13,7 +13,7 @@ __github__ = "https://github.com/ParisNeo/lollms-webui"
__copyright__ = "Copyright 2023, "
__license__ = "Apache 2.0"
__version__ ="6.8"
__version__ ="7.0 (Alpha)"
main_repo = "https://github.com/ParisNeo/lollms-webui.git"
import os
@ -144,7 +144,6 @@ try:
logging.basicConfig(level=logging.WARNING)
def get_ip_address():
# Create a socket object
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
@ -194,7 +193,7 @@ try:
if config.auto_update:
if check_update_():
ASCIIColors.info("New version found. Updating!")
self.update_software()
run_update_script()
if len(config.personalities)==0:
config.personalities.append("generic/lollms")
@ -360,6 +359,8 @@ try:
self.add_endpoint("/edit_title", "edit_title", self.edit_title, methods=["POST"])
self.add_endpoint("/make_title", "make_title", self.make_title, methods=["POST"])
self.add_endpoint("/get_server_address", "get_server_address", self.get_server_address, methods=["GET"])
self.add_endpoint(
"/delete_discussion",
@ -481,6 +482,10 @@ try:
def get_server_address(self):
server_address = request.host_url
return server_address
def execute_python(self, code, discussion_id, message_id):
def spawn_process(code):
"""Executes Python code and returns the output as JSON."""
@ -1716,7 +1721,7 @@ try:
def get_current_personality_files_list(self):
if self.personality is None:
return jsonify({"state":False, "error":"No personality selected"})
return jsonify({"state":True, "files":[{"name":Path(f).name, "size":Path(f).stat().st_size} for f in self.personality.files]})
return jsonify({"state":True, "files":[{"name":Path(f).name, "size":Path(f).stat().st_size} for f in self.personality.text_files]})
def clear_personality_files_list(self):
if self.personality is None:
@ -2540,9 +2545,14 @@ try:
# if autoshow
if config.auto_show_browser:
webbrowser.open(f"http://{config['host']}:{config['port']}")
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)
try:
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)
except Exception as ex:
trace_exception(ex)
# http_server = WSGIServer((config["host"], config["port"]), app, handler_class=WebSocketHandler)
# http_server.serve_forever()
except Exception as ex:

View File

@ -15,4 +15,4 @@ GitPython
setuptools
numpy==1.24
flask_compress
ascii_colors>=0.1.3
ascii_colors>=0.1.4

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-5d95834b.js"></script>
<link rel="stylesheet" href="/assets/index-f06a4c0c.css">
<script type="module" crossorigin src="/assets/index-6d02e1a1.js"></script>
<link rel="stylesheet" href="/assets/index-78770f39.css">
</head>
<body>
<div id="app"></div>

View File

@ -43,6 +43,11 @@ hljs.configure({ languages: ['bash'] }); // Set bash as the default language
hljs.highlightAll();
export default {
props: {
host: {
type: String,
required: false,
default: "http://localhost:9600",
},
language: {
type: String,
required: true,
@ -120,7 +125,7 @@ export default {
executeCode() {
const json = JSON.stringify({ 'code': this.code, 'discussion_id': this.discussion_id, 'message_id': this.message_id, 'language': this.language})
console.log(json)
fetch('http://localhost:9600/execute_code', {
fetch(`${this.host}/execute_code`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: json
@ -141,7 +146,7 @@ export default {
openFolder() {
const json = JSON.stringify({ 'discussion_id': this.discussion_id })
console.log(json)
fetch('http://localhost:9600/open_code_folder', {
fetch(`${this.host}/open_code_folder`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: json

View File

@ -4,6 +4,7 @@
<div v-for="(item, index) in markdownItems" :key="index">
<code-block
v-if="item.type === 'code'"
:host="host"
:language="item.language"
:code="item.code"
:discussion_id="discussion_id"
@ -40,6 +41,11 @@ function escapeHtml(unsafe) {
export default {
name: 'MarkdownRenderer',
props: {
host: {
type: String,
required: false,
default: "http://localhost:9600",
},
markdownText: {
type: String,
required: true,

View File

@ -144,7 +144,7 @@
</div>
</div>
<MarkdownRenderer ref="mdRender" v-if="!editMsgMode" :markdown-text="message.content" :message_id="message.id">
<MarkdownRenderer ref="mdRender" v-if="!editMsgMode" :host="host" :markdown-text="message.content" :message_id="message.id">
</MarkdownRenderer>
<div >
<textarea v-if="editMsgMode" ref="mdTextarea" @keydown.tab.prevent="insertTab"
@ -224,6 +224,11 @@ export default {
DynamicUIRenderer,
},
props: {
host: {
type: String,
required: false,
default: "http://localhost:9600",
},
message: Object,
avatar: ''
},

View File

@ -230,8 +230,12 @@
<!-- CHAT AREA -->
<div class=" container pt-4 pb-10 mb-28">
<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"
<Message v-for="(msg, index) in discussionArr"
:key="msg.id" :message="msg" :id="'msg-' + msg.id"
:host="host"
ref="messages"
@copy="copyToClipBoard" @delete="deleteMessage" @rankUp="rankUpMessage"
@rankDown="rankDownMessage" @updateMessage="updateMessage" @resendMessage="resendMessage" @continueMessage="continueMessage"
:avatar="getAvatar(msg.sender)" />
@ -362,6 +366,7 @@ export default {
data() {
return {
host:"",
// To be synced with the backend database types
msgTypes: {
// Messaging
@ -545,8 +550,6 @@ export default {
this.loading = false
this.setDiscussionLoading(id, this.loading)
if (data) {
console.log("received discussion")
console.log(data)
// Filter out the user and bot entries
this.discussionArr = data.filter((item) =>
item.message_type == this.msgTypes.MSG_TYPE_CHUNK ||
@ -1128,7 +1131,6 @@ export default {
},
streamMessageContent(msgObj) {
// Streams response message content from binding
console.log("Received message",msgObj)
const discussion_id = msgObj.discussion_id
this.setDiscussionLoading(discussion_id, true);
if (this.currentDiscussion.id == discussion_id) {
@ -1724,6 +1726,16 @@ export default {
this.isCreated = true
},
async mounted() {
try {
const response = await fetch('/get_server_address'); // Replace with the actual endpoint on your Flask server
const serverAddress = await response.text();
console.log(`Server address: ${serverAddress}`)
this.host = `${serverAddress}`; // Construct the full server address dynamically
} catch (error) {
console.error('Error fetching server address:', error);
// Handle error if necessary
this.host = process.env.VITE_LOLLMS_API
}
//console.log('chatbox mnt',this.$refs)
this.$nextTick(() => {
feather.replace();

View File

@ -3,8 +3,23 @@ import { fileURLToPath, URL } from 'node:url'
import { defineConfig,loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default ({ mode }) => {
export default async ({ mode }) => {
async function getFlaskServerURL() {
try {
const response = await fetch('/get_server_address'); // Replace with the actual endpoint on your Flask server
const serverAddress = await response.text();
console.log(`Server address: ${serverAddress}`)
return `${serverAddress}`; // Construct the full server address dynamically
} catch (error) {
// console.error('Error fetching server address:', error);
// Handle error if necessary
return process.env.VITE_LOLLMS_API
}
}
const serverURL = await getFlaskServerURL()
console.log(serverURL)
// Load app-level env vars to node-level env vars.
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
@ -21,7 +36,7 @@ export default ({ mode }) => {
server: {
proxy: {
"/api/": {
target: process.env.VITE_LOLLMS_API,
target: serverURL,//process.env.VITE_LOLLMS_API,//getFlaskServerURL(),// process.env.VITE_LOLLMS_API,
changeOrigin: process.env.VITE_LOLLMS_API_CHANGE_ORIGIN,
secure: process.env.VITE_LOLLMS_API_SECURE,
rewrite: (path) => path.replace(/^\/api/, ""),

@ -1 +1 @@
Subproject commit fdfae6890b123726e6f86fcaa845a6cb5f32dc85
Subproject commit dd3ace5dd06cb02b1c13b83aa785266f50ff663c