working model and backend selection

This commit is contained in:
Saifeddine ALOUI 2023-05-18 20:00:48 +02:00 committed by ParisNeo
parent d633aa9d4a
commit 1c9307a23e
5 changed files with 158 additions and 88 deletions

61
app.py
View File

@ -51,9 +51,8 @@ app.config['SECRET_KEY'] = 'secret!'
# Set the logging level to WARNING or higher
logging.getLogger('socketio').setLevel(logging.WARNING)
logging.getLogger('engineio').setLevel(logging.WARNING)
# Suppress Flask's default console output
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
logging.getLogger('werkzeug').setLevel(logging.ERROR)
logging.basicConfig(level=logging.WARNING)
import time
from gpt4all_api.config import load_config, save_config
@ -192,6 +191,10 @@ class Gpt4AllWebUI(GPT4AllAPI):
self.add_endpoint(
"/update_setting", "update_setting", self.update_setting, methods=["POST"]
)
self.add_endpoint(
"/apply_settings", "apply_settings", self.apply_settings, methods=["POST"]
)
self.add_endpoint(
"/save_settings", "save_settings", self.save_settings, methods=["POST"]
@ -293,29 +296,16 @@ class Gpt4AllWebUI(GPT4AllAPI):
elif setting_name== "model":
self.config["model"]=data['setting_value']
print("update_settings : New model selected")
# Build chatbot
self.process.set_config(self.config)
elif setting_name== "backend":
print("New backend selected")
if self.config['backend']!= data['setting_value']:
print("New backend selected")
self.config["backend"]=data['setting_value']
backend_ =self.process.rebuild_backend(self.config)
models = backend_.list_models(self.config)
if len(models)>0:
self.backend = backend_
self.config['model'] = models[0]
# Build chatbot
self.process.set_config(self.config)
if self.config["debug"]:
print(f"Configuration {data['setting_name']} set to {data['setting_value']}")
return jsonify({'setting_name': data['setting_name'], "status":True})
else:
if self.config["debug"]:
print(f"Configuration {data['setting_name']} couldn't be set to {data['setting_value']}")
return jsonify({'setting_name': data['setting_name'], "status":False})
try:
self.backend = self.process.load_backend(self.config["backend"])
except Exception as ex:
print("Couldn't build backend")
return jsonify({'setting_name': data['setting_name'], "status":False, 'error':str(ex)})
else:
if self.config["debug"]:
print(f"Configuration {data['setting_name']} set to {data['setting_value']}")
@ -330,10 +320,13 @@ class Gpt4AllWebUI(GPT4AllAPI):
print(f"Configuration {data['setting_name']} set to {data['setting_value']}")
print("Configuration updated")
self.process.set_config(self.config)
# Tell that the setting was changed
return jsonify({'setting_name': data['setting_name'], "status":True})
def apply_settings(self):
return jsonify(self.process.set_config(self.config))
def list_backends(self):
backends_dir = Path('./backends') # replace with the actual path to the models folder
backends = [f.stem for f in backends_dir.iterdir() if f.is_dir() and f.stem!="__pycache__"]
@ -527,7 +520,7 @@ class Gpt4AllWebUI(GPT4AllAPI):
print("New backend selected")
self.config['backend'] = backend
backend_ =self.process.load_backend(Path("backends")/config["backend"])
backend_ =self.process.load_backend(config["backend"])
models = backend_.list_models(self.config)
if len(models)>0:
self.backend = backend_
@ -619,15 +612,18 @@ class Gpt4AllWebUI(GPT4AllAPI):
Returns:
_type_: _description_
"""
if self.backend is None:
return jsonify([])
model_list = self.backend.get_available_models()
models = []
for model in model_list:
try:
filename = model['filename']
server = model['server']
filesize = model['filesize']
filename = model.get('filename',"")
server = model.get('server',"")
image_url = model.get("image_url", '/icons/default.png')
filesize = int(model.get('filesize',0))
description = model.get('description',"")
if server.endswith("/"):
path = f'{server}{filename}'
else:
@ -635,14 +631,17 @@ class Gpt4AllWebUI(GPT4AllAPI):
local_path = Path(f'./models/{self.config["backend"]}/{filename}')
is_installed = local_path.exists()
models.append({
'title': model['filename'],
'icon': '/icons/default.png', # Replace with the path to the model icon
'description': model['description'],
'title': filename,
'icon': image_url, # Replace with the path to the model icon
'description': description,
'isInstalled': is_installed,
'path': path,
'filesize': filesize,
})
except:
except Exception as ex:
print("#################################")
print(ex)
print("#################################")
print(f"Problem with model : {model}")
return jsonify(models)

View File

@ -88,13 +88,26 @@ class ModelProcess:
self.cancel_queue = mp.Queue(maxsize=1)
self.clear_queue_queue = mp.Queue(maxsize=1)
self.set_config_queue = mp.Queue(maxsize=1)
self.set_config_result_queue = mp.Queue(maxsize=1)
self.started_queue = mp.Queue()
self.process = None
self.is_generating = mp.Value('i', 0)
self.model_ready = mp.Value('i', 0)
self.ready = False
def load_backend(self, backend_path:Path):
self.reset_config_result()
def reset_config_result(self):
self._set_config_result = {
'status': 'succeeded',
'backend_status':'ok',
'model_status':'ok',
'personality_status':'ok',
'errors':[]
}
def load_backend(self, backend_name:str):
backend_path = Path("backends")/backend_name
# first find out if there is a requirements.txt file
requirements_file = backend_path/"requirements.txt"
if requirements_file.exists():
@ -131,7 +144,10 @@ class ModelProcess:
def set_config(self, config):
self.set_config_queue.put(config)
# Wait for it t o be consumed
while self.set_config_result_queue.empty():
time.sleep(0.5)
return self.set_config_result_queue.get()
def generate(self, prompt, id, n_predict):
self.generate_queue.put((prompt, id, n_predict))
@ -144,19 +160,20 @@ class ModelProcess:
def rebuild_backend(self, config):
try:
backend = self.load_backend(Path("backends")/config["backend"])
backend = self.load_backend(config["backend"])
print("Backend loaded successfully")
except Exception as ex:
print("Couldn't build backend")
print(ex)
backend = None
self._set_config_result['backend_status'] ='failed'
self._set_config_result['errors'].append(f"couldn't build backend:{ex}")
return backend
def _rebuild_model(self):
try:
print("Rebuilding model")
self.backend = self.load_backend(Path("backends")/self.config["backend"])
self.backend = self.load_backend(self.config["backend"])
print("Backend loaded successfully")
try:
model_file = Path("models")/self.config["backend"]/self.config["model"]
@ -168,6 +185,8 @@ class ModelProcess:
print("Couldn't build model")
print(ex)
self.model = None
self._set_config_result['model_status'] ='failed'
self._set_config_result['errors'].append(f"couldn't build model:{ex}")
except Exception as ex:
print("Couldn't build backend")
print(ex)
@ -183,6 +202,7 @@ class ModelProcess:
if self.config["debug"]:
print(ex)
personality = AIPersonality()
return personality
def _rebuild_personality(self):
@ -194,6 +214,8 @@ class ModelProcess:
if self.config["debug"]:
print(ex)
self.personality = AIPersonality()
self._set_config_result['personality_status'] ='failed'
self._set_config_result['errors'].append(f"couldn't load personality:{ex}")
def _run(self):
self._rebuild_model()
@ -297,7 +319,9 @@ class ModelProcess:
config = self.set_config_queue.get()
if config is not None:
print("Inference process : Setting configuration")
self.reset_config_result()
self._set_config(config)
self.set_config_result_queue.put(self._set_config_result)
def _cancel_generation(self):
self.is_generating.value = 0
@ -502,7 +526,8 @@ class GPT4AllAPI():
def load_backend(self, backend_path):
def load_backend(self, backend_name):
backend_path = Path("backends")/backend_name
# define the full absolute path to the module
absolute_path = backend_path.resolve()

View File

View File

@ -119,7 +119,7 @@
<!-- REMOVED @click="scrollToElement($event.target)" -->
<!-- Removed reference to copyToClipBoard() ; function was moved to Message.vue -->
<Message v-for="(msg, index) in discussionArr" :key="index" :message="msg" :id="'msg-' + msg.id" ref="messages"
@delete="deleteMessage" @rankUp="rankUpMessage" @rankDown="rankDownMessage"
@copy="copyToClipBoard" @delete="deleteMessage" @rankUp="rankUpMessage" @rankDown="rankDownMessage"
@updateMessage="updateMessage" @resendMessage="resendMessage" />
<WelcomeComponent v-if="!currentDiscussion.id" />
@ -749,14 +749,14 @@ export default {
this.setDiscussionLoading(this.currentDiscussion.id, this.isGenerating)
this.chime.play()
},
//copyToClipBoard(content) {
copyToClipBoard(content) {
// this.$refs.toast.showToast("Copied to clipboard successfully")
// nextTick(() => {
// feather.replace()
this.$refs.toast.showToast("Copied to clipboard successfully")
nextTick(() => {
feather.replace()
// })
//},
})
},
closeToast() {
this.showToast = false
},

View File

@ -37,11 +37,9 @@
class="flex flex-col mb-2 p-3 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 ">
<button @click.stop="bec_collapsed = !bec_collapsed"
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1">
<!-- <i data-feather="chevron-right"></i> -->
<h3 class="text-lg font-semibold cursor-pointer select-none "
@click.stop="bec_collapsed = !bec_collapsed">
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1 flex items-center">
<i :data-feather="mzc_collapsed ? 'chevron-right' : 'chevron-down'" class="mr-2"></i>
<h3 class="text-lg font-semibold cursor-pointer select-none">
Backend Configuration</h3>
</button>
</div>
@ -53,9 +51,7 @@
<select id="backend" @change="update_backend($event.target.value)"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<!-- <option v-for="item in backendsArr" :selected="item === configFile.backend">{{ item }}</option> -->
<option v-for="item in backendsArr" :key="item === configFile.backend">{{ item }}</option>
<option v-for="item in backendsArr" :selected="item === configFile.backend">{{ item }}</option>
</select>
</div>
<div class="m-2">
@ -65,22 +61,25 @@
<select id="model" @change="update_model($event.target.value)"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<!-- <option v-for="item in modelsArr" :selected="item === configFile.model">{{ item }}</option> -->
<option v-for="item in modelsArr" :key="item === configFile.model">{{ item }}</option>
<option v-for="item in modelsArr" :selected="item === configFile.model">{{ item }}</option>
</select>
</div>
</div>
<div class="m-2">
<button @click="applyConfiguration" class="bg-blue-500 text-white py-2 px-4 rounded">
Apply Configuration
</button>
<div v-if="isLoading" class="loader"></div>
</div>
</div>
</div>
<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="mzc_collapsed = !mzc_collapsed"
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1">
<h3 class="text-lg font-semibold cursor-pointer select-none "
@click.stop="mzc_collapsed = !mzc_collapsed">
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1 flex items-center">
<i :data-feather="mzc_collapsed ? 'chevron-right' : 'chevron-down'" class="mr-2"></i>
<h3 class="text-lg font-semibold cursor-pointer select-none">
Models zoo</h3>
</button>
</div>
@ -114,11 +113,9 @@
class="flex flex-col mb-2 p-3 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">
<button @click.stop="pc_collapsed = !pc_collapsed"
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1">
<!-- <i data-feather="chevron-right"></i> -->
<h3 class="text-lg font-semibold cursor-pointer select-none" @click.stop="pc_collapsed = !pc_collapsed">
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1 flex items-center">
<i :data-feather="pc_collapsed ? 'chevron-right' : 'chevron-down'" class="mr-2"></i>
<h3 class="text-lg font-semibold cursor-pointer select-none">
Personality Configuration</h3>
</button>
</div>
@ -130,8 +127,7 @@
<select id="persLang" @change="update_setting('personality_language', $event.target.value, refresh)"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<!-- <option v-for="item in persLangArr" :selected="item === configFile.personality_language">{{ item }} -->
<option v-for="item in persLangArr" :key="item === configFile.personality_language">{{ item }}
<option v-for="item in persLangArr" :selected="item === configFile.personality_language">{{ item }}
</option>
@ -144,8 +140,7 @@
<select id="persCat" @change="update_setting('personality_category', $event.target.value, refresh)"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<!-- <option v-for="item in persCatgArr" :selected="item === configFile.personality_category">{{ item }} -->
<option v-for="item in persCatgArr" :key="item === configFile.personality_category">{{ item }}
<option v-for="item in persCatgArr" :selected="item === configFile.personality_category">{{ item }}
</option>
@ -158,8 +153,7 @@
<select id="persona" @change="update_setting('personality', $event.target.value, refresh)"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<!-- <option v-for="item in persArr" :selected="item === configFile.personality">{{ item }}</option> -->
<option v-for="item in persArr" :key="item === configFile.personality">{{ item }}</option>
<option v-for="item in persArr" :selected="item === configFile.personality">{{ item }}</option>
</select>
</div>
@ -169,12 +163,11 @@
class="flex flex-col mb-2 p-3 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 ">
<button @click.stop="pzc_collapsed = !pzc_collapsed"
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1">
<!-- <i data-feather="chevron-right"></i> -->
<h3 class="text-lg font-semibold cursor-pointer select-none "
@click.stop="pzc_collapsed = !pzc_collapsed">
Personalities zoo</h3>
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1 flex items-center">
<i :data-feather="mc_collapsed ? 'chevron-right' : 'chevron-down'" class="mr-2"></i>
<h3 class="text-lg font-semibold cursor-pointer select-none">
Personalities zoo
</h3>
</button>
</div>
<div :class="{ 'hidden': pzc_collapsed }" class="flex flex-col mb-2 p-2">
@ -193,11 +186,9 @@
class="flex flex-col mb-2 p-3 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">
<button @click.stop="mc_collapsed = !mc_collapsed"
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1">
<!-- <i data-feather="chevron-right"></i> -->
<h3 class="text-lg font-semibold cursor-pointer select-none "
@click.stop="mc_collapsed = !mc_collapsed">
class="text-2xl hover:text-primary duration-75 p-2 -m-2 w-full text-left active:translate-y-1 flex items-center">
<i :data-feather="mc_collapsed ? 'chevron-right' : 'chevron-down'" class="mr-2"></i>
<h3 class="text-lg font-semibold cursor-pointer select-none">
Model Configuration</h3>
</button>
</div>
@ -343,6 +334,8 @@
<YesNoDialog ref="yesNoDialog" />
<MessageBox ref="messageBox" />
<Toast ref="toast" />
</template>
<script>
@ -351,6 +344,7 @@ import feather from 'feather-icons'
import { nextTick } from 'vue'
import MessageBox from "@/components/MessageBox.vue";
import YesNoDialog from "@/components/YesNoDialog.vue";
import Toast from '../components/Toast.vue'
import ModelEntry from '@/components/ModelEntry.vue';
import PersonalityViewer from '@/components/PersonalityViewer.vue';
import socket from '@/services/websocket.js'
@ -361,7 +355,8 @@ export default {
YesNoDialog,
ModelEntry,
// eslint-disable-next-line vue/no-unused-components
PersonalityViewer
PersonalityViewer,
Toast
},
setup() {
@ -394,7 +389,9 @@ export default {
persArr: [],
langArr: [],
configFile: {},
showConfirmation: false
showConfirmation: false,
showToast: false,
isLoading: false
}
},
@ -409,8 +406,10 @@ export default {
this.mc_collapsed=val
},
fetchModels() {
console.log("Fetching models")
axios.get('/get_available_models')
.then(response => {
console.log(`Models list recovered successfuly: ${response.data}`)
this.models = response.data;
})
.catch(error => {
@ -510,6 +509,7 @@ export default {
}
});
})
this.fetchModels();
},
// Accordeon stuff
toggleAccordion() {
@ -522,26 +522,45 @@ export default {
}
console.log("change", setting_name_val, setting_value_val, obj)
axios.post('/update_setting', obj).then((res) => {
console.log("Update setting done")
if (res) {
console.log("res is ok")
if (next !== undefined) {
console.log("Calling next")
next(res)
}
return res.data;
}
})
// eslint-disable-next-line no-unused-vars
.catch(error => { return { 'status': false } });
// eslint-disable-next-line no-unused-vars
.catch(error => { return { 'status': false } });
},
update_backend(value) {
console.log("Upgrading backend")
// eslint-disable-next-line no-unused-vars
this.update_setting('backend', value, (res)=>{console.log("Backend changed"); this.fetchModels(); })
this.update_setting('backend', value, (res)=>{
this.refresh();
console.log("Backend changed");
console.log(res);
this.$refs.toast.showToast("Backend changed.",4, true)
})
},
update_model(value) {
console.log("Upgrading model")
// eslint-disable-next-line no-unused-vars
this.update_setting('model', value, (res)=>{console.log("Model changed"); this.fetchModels(); })
},
applyConfiguration() {
this.isLoading = true;
axios.post('/apply_settings').then((res) => {
this.isLoading = false;
console.log(res.data)
if(res.data.status==="succeeded"){
console.log("applying configuration succeeded")
this.$refs.toast.showToast("Configuration changed successfully.",4, true)
}
})
},
save_configuration() {
this.showConfirmation = false
axios.post('/save_settings', {})
@ -604,6 +623,9 @@ export default {
},
closeToast() {
this.showToast = false
},
}, async mounted() {
nextTick(() => {
@ -664,3 +686,27 @@ export default {
}
</script>
<style>
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 16px;
height: 16px;
animation: spin 2s linear infinite;
margin-left: 8px;
display: inline-block;
}
.height-64 {
min-height: 64px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>