diff --git a/docs/server_endpoints.md b/docs/server_endpoints.md index a596483..44d3fe4 100644 --- a/docs/server_endpoints.md +++ b/docs/server_endpoints.md @@ -180,7 +180,7 @@ Events generated: - Creates a deep copy of the configuration (`self.config`) and assigns it to `self.cp_config`. - Sets the `"model_name"` property of `self.cp_config` to the selected model name. - Tries to create an instance of the binding class (`self.binding`) with `self.cp_config`. - - If successful, assigns the created binding instance to `self.active_model`. + - If successful, assigns the created binding instance to `self.model`. - Emits a `'select_model'` event with a success response, indicating that the model selection was successful. - Returns and exits the function. - If an exception occurs during model creation, prints the exception and emits a `'select_model'` event with a failure response, indicating that a binding needs to be selected first. diff --git a/lollms/.gitignore b/lollms/.gitignore new file mode 100644 index 0000000..b509e76 --- /dev/null +++ b/lollms/.gitignore @@ -0,0 +1,2 @@ +logs +uploads \ No newline at end of file diff --git a/lollms/app.py b/lollms/app.py new file mode 100644 index 0000000..6d9e39c --- /dev/null +++ b/lollms/app.py @@ -0,0 +1,157 @@ +from lollms.main_config import LOLLMSConfig +from lollms.helpers import ASCIIColors +from lollms.paths import LollmsPaths +from lollms.personality import PersonalityBuilder +from lollms.binding import LLMBinding, BindingBuilder, ModelBuilder +from lollms.config import InstallOption +import traceback +from lollms.terminal import MainMenu + +class LollmsApplication: + def __init__(self, app_name:str, config:LOLLMSConfig, lollms_paths:LollmsPaths) -> None: + """ + Creates a LOLLMS Application + """ + self.app_name = app_name + self.config = config + self.lollms_paths = lollms_paths + + self.menu = MainMenu(self) + self.mounted_personalities = [] + self.personality = None + + if self.config.binding_name is None: + ASCIIColors.warning(f"No binding selected") + ASCIIColors.info("Please select a valid model or install a new one from a url") + self.menu.select_binding() + + try: + self.binding = self.load_binding() + except Exception as ex: + ASCIIColors.error(f"Failed to load binding.\nReturned exception: {ex}") + + if self.binding is not None: + if self.config.model_name is None: + ASCIIColors.warning(f"No model selected") + print("Please select a valid binding") + self.menu.select_model() + try: + self.model = self.load_model() + except Exception as ex: + ASCIIColors.error(f"Failed to load model.\nReturned exception: {ex}") + + self.mount_personalities() + + + def trace_exception(self, ex): + # Catch the exception and get the traceback as a list of strings + traceback_lines = traceback.format_exception(type(ex), ex, ex.__traceback__) + + # Join the traceback lines into a single string + traceback_text = ''.join(traceback_lines) + ASCIIColors.error(traceback_text) + + def load_binding(self): + try: + binding = BindingBuilder().build_binding(self.config, self.lollms_paths) + except Exception as ex: + print(ex) + print(f"Couldn't find binding. Please verify your configuration file at {self.configuration_path} or use the next menu to select a valid binding") + print(f"Trying to reinstall binding") + binding = BindingBuilder().build_binding(self.config, self.lollms_paths,installation_option=InstallOption.FORCE_INSTALL) + return binding + + def load_model(self): + try: + model = ModelBuilder(self.binding).get_model() + for personality in self.mounted_personalities: + if personality is not None: + personality.model = model + except Exception as ex: + ASCIIColors.error(f"Couldn't load model. Please verify your configuration file at {self.lollms_paths.personal_configuration_path} or use the next menu to select a valid model") + ASCIIColors.error(f"Binding returned this exception : {ex}") + ASCIIColors.error(f"{self.config.get_model_path_infos()}") + print("Please select a valid model or install a new one from a url") + model = None + + return model + + + def mount_personality(self, id:int): + try: + self.personality = PersonalityBuilder(self.lollms_paths, self.config, self.model).build_personality(id) + self.config.active_personality_id=len(self.config.personalities)-1 + if self.personality.model is not None: + self.cond_tk = self.personality.model.tokenize(self.personality.personality_conditioning) + self.n_cond_tk = len(self.cond_tk) + ASCIIColors.success(f"Personality {self.personality.name} mounted successfully") + else: + ASCIIColors.success(f"Personality {self.personality.name} mounted successfully but no model is selected") + + except Exception as ex: + ASCIIColors.error(f"Couldn't load personality. Please verify your configuration file at {self.lollms_paths.personal_configuration_path} or use the next menu to select a valid personality") + ASCIIColors.error(f"Binding returned this exception : {ex}") + self.trace_exception(ex) + ASCIIColors.error(f"{self.config.get_personality_path_infos()}") + self.personality = None + self.mounted_personalities.append(self.personality) + return self.personality + + def mount_personalities(self): + self.mounted_personalities = [] + to_remove = [] + for i in range(len(self.config["personalities"])): + p = self.mount_personality(i) + if p is None: + to_remove.append(i) + to_remove.sort(reverse=True) + for i in to_remove: + self.unmount_personality(i) + + + def unmount_personality(self, id:int)->bool: + if id=id: + self.config.active_personality_id-=1 + + self.config.save_config() + return True + else: + return False + + + def select_personality(self, id:int): + if id None: - self.config = config - self.lollms_paths = lollms_paths - def load_binding(self): - pass - def load_model(self): - pass - def load_personality(self): - pass - -def reset_paths(lollms_paths:LollmsPaths): - lollms_paths.resetPaths() - -def reset_all_installs(lollms_paths:LollmsPaths): - ASCIIColors.info("Removeing all configuration files to force reinstall") - ASCIIColors.info(f"Searching files from {lollms_paths.personal_configuration_path}") - for file_path in lollms_paths.personal_configuration_path.iterdir(): - if file_path.name!=f"{lollms_paths.tool_prefix}local_config.yaml" and file_path.suffix.lower()==".yaml": - file_path.unlink() - ASCIIColors.info(f"Deleted file: {file_path}") - -class MainMenu: - def __init__(self, lollms_app:LollmsApplication): - self.binding_infs = [] - self.lollms_app = lollms_app - - def show_logo(self): - print(f"{ASCIIColors.color_bright_yellow}") - print(" ___ ___ ___ ___ ___ ___ ") - print(" /\__\ /\ \ /\__\ /\__\ /\__\ /\ \ ") - print(" /:/ / /::\ \ /:/ / /:/ / /::| | /::\ \ ") - print(" /:/ / /:/\:\ \ /:/ / /:/ / /:|:| | /:/\ \ \ ") - print(" /:/ / /:/ \:\ \ /:/ / /:/ / /:/|:|__|__ _\:\~\ \ \ ") - print(" /:/__/ /:/__/ \:\__\ /:/__/ /:/__/ /:/ |::::\__\ /\ \:\ \ \__\ ") - print(" \:\ \ \:\ \ /:/ / \:\ \ \:\ \ \/__/~~/:/ / \:\ \:\ \/__/ ") - print(" \:\ \ \:\ /:/ / \:\ \ \:\ \ /:/ / \:\ \:\__\ ") - print(" \:\ \ \:\/:/ / \:\ \ \:\ \ /:/ / \:\/:/ / ") - print(" \:\__\ \::/ / \:\__\ \:\__\ /:/ / \::/ / ") - print(" \/__/ \/__/ \/__/ \/__/ \/__/ \/__/ ") - - - - - print(f"{ASCIIColors.color_reset}") - print(f"{ASCIIColors.color_red}Version: {ASCIIColors.color_green}{pkg_resources.get_distribution('lollms').version}") - print(f"{ASCIIColors.color_red}By : {ASCIIColors.color_green}ParisNeo") - print(f"{ASCIIColors.color_reset}") - - def show_commands_list(self): - print() - print("Commands:") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} menu: shows main menu") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} help: shows this info") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} reset: resets the context") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} : forces the model to continue generating") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} context_infos: current context size and space left before cropping") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} start_log: starts logging the discussion to a text file") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} stop_log: stops logging the discussion to a text file") - print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} send_file: uploads a file to the AI") - print(f" {ASCIIColors.color_red}└{ASCIIColors.color_reset} exit: exists the console") - - if self.lollms_app.personality.help !="": - print(f"Personality help:") - print(f"{self.lollms_app.personality.help}") - - - - def show_menu(self, options): - print("Menu:") - for index, option in enumerate(options): - print(f"{ASCIIColors.color_green}{index + 1} -{ASCIIColors.color_reset} {option}") - choice = input("Enter your choice: ") - return int(choice) if choice.isdigit() else -1 - - def select_binding(self): - bindings_list = [] - print() - print(f"{ASCIIColors.color_green}Current binding: {ASCIIColors.color_reset}{self.lollms_app.config['binding_name']}") - for p in self.lollms_app.lollms_paths.bindings_zoo_path.iterdir(): - if p.is_dir() and not p.stem.startswith("."): - with open(p/"binding_card.yaml", "r") as f: - card = yaml.safe_load(f) - with open(p/"models.yaml", "r") as f: - models = yaml.safe_load(f) - is_installed = (self.lollms_app.lollms_paths.personal_configuration_path/f"binding_{p.name}.yaml").exists() - entry=f"{ASCIIColors.color_green if is_installed else ''}{'*' if self.lollms_app.config['binding_name']==card['name'] else ''} {card['name']} (by {card['author']})" - bindings_list.append(entry) - entry={ - "name":p.name, - "card":card, - "models":models, - "installed": is_installed - } - self.binding_infs.append(entry) - bindings_list += ["Back"] - choice = self.show_menu(bindings_list) - if 1 <= choice <= len(bindings_list)-1: - print(f"You selected binding: {ASCIIColors.color_green}{self.binding_infs[choice - 1]['name']}{ASCIIColors.color_reset}") - self.lollms_app.config['binding_name']=self.binding_infs[choice - 1]['name'] - self.lollms_app.load_binding() - self.lollms_app.config['model_name']=None - self.lollms_app.config.save_config() - elif choice <= len(bindings_list): - return - else: - print("Invalid choice!") - - def select_model(self): - print() - print(f"{ASCIIColors.color_green}Current binding: {ASCIIColors.color_reset}{self.lollms_app.config['binding_name']}") - print(f"{ASCIIColors.color_green}Current model: {ASCIIColors.color_reset}{self.lollms_app.config['model_name']}") - - models_dir:Path = (self.lollms_app.lollms_paths.personal_models_path/self.lollms_app.config['binding_name']) - models_dir.mkdir(parents=True, exist_ok=True) - - models_list = [v for v in self.lollms_app.binding.list_models(self.lollms_app.config)] + ["Install model", "Change binding", "Back"] - choice = self.show_menu(models_list) - if 1 <= choice <= len(models_list)-3: - print(f"You selected model: {ASCIIColors.color_green}{models_list[choice - 1]}{ASCIIColors.color_reset}") - self.lollms_app.config['model_name']=models_list[choice - 1] - self.lollms_app.config.save_config() - self.lollms_app.load_model() - elif choice <= len(models_list)-2: - self.install_model() - elif choice <= len(models_list)-1: - self.select_binding() - self.select_model() - elif choice <= len(models_list): - return - else: - print("Invalid choice!") - - def install_model(self): - - models_list = ["Install model from internet","Install model from local file","Back"] - choice = self.show_menu(models_list) - if 1 <= choice <= len(models_list)-2: - url = input("Give a URL to the model to be downloaded :") - def progress_callback(blocks, block_size, total_size): - tqdm_bar.total=total_size - tqdm_bar.update(block_size) - - # Usage example - with tqdm(total=100, unit="%", desc="Download Progress", ncols=80) as tqdm_bar: - self.lollms_app.config.download_model(url,self.lollms_app.binding, progress_callback) - self.select_model() - elif choice <= len(models_list)-1: - path = Path(input("Give a path to the model to be used on your PC:")) - if path.exists(): - self.lollms_app.config.reference_model(path) - self.select_model() - elif choice <= len(models_list): - return - else: - print("Invalid choice!") - - def select_personality(self): - print() - print(f"{ASCIIColors.color_green}Current personality: {ASCIIColors.color_reset}{self.lollms_app.config['personalities'][self.lollms_app.config['active_personality_id']]}") - personality_languages = [p.stem for p in self.lollms_app.lollms_paths.personalities_zoo_path.iterdir() if p.is_dir()] + ["Back"] - print("Select language") - choice = self.show_menu(personality_languages) - if 1 <= choice <= len(personality_languages)-1: - language = personality_languages[choice - 1] - print(f"You selected language: {ASCIIColors.color_green}{language}{ASCIIColors.color_reset}") - personality_categories = [p.stem for p in (self.lollms_app.lollms_paths.personalities_zoo_path/language).iterdir() if p.is_dir()]+["Back"] - print("Select category") - choice = self.show_menu(personality_categories) - if 1 <= choice <= len(personality_categories)-1: - category = personality_categories[choice - 1] - print(f"You selected category: {ASCIIColors.color_green}{category}{ASCIIColors.color_reset}") - personality_names = [p.stem for p in (self.lollms_app.lollms_paths.personalities_zoo_path/language/category).iterdir() if p.is_dir()]+["Back"] - print("Select personality") - choice = self.show_menu(personality_names) - if 1 <= choice <= len(personality_names)-1: - name = personality_names[choice - 1] - print(f"You selected personality: {ASCIIColors.color_green}{name}{ASCIIColors.color_reset}") - self.lollms_app.config["personalities"]=[f"{language}/{category}/{name}"] - self.lollms_app.load_personality() - self.lollms_app.config.save_config() - print("Personality saved successfully!") - elif 1 <= choice <= len(personality_names): - return - else: - print("Invalid choice!") - elif 1 <= choice <= len(personality_categories): - return - else: - print("Invalid choice!") - elif 1 <= choice <= len(personality_languages): - return - else: - print("Invalid choice!") - - def reinstall_binding(self): - lollms_app = self.lollms_app - try: - lollms_app.binding = BindingBuilder().build_binding(lollms_app.config, lollms_app.lollms_paths,InstallOption.FORCE_INSTALL) - except Exception as ex: - print(ex) - print(f"Couldn't find binding. Please verify your configuration file at {lollms_app.config.file_path} or use the next menu to select a valid binding") - self.select_binding() - - def reinstall_personality(self): - lollms_app = self.lollms_app - try: - lollms_app.personality = PersonalityBuilder(lollms_app.lollms_paths, lollms_app.config, lollms_app.model, installation_option=InstallOption.FORCE_INSTALL).build_personality() - except Exception as ex: - ASCIIColors.error(f"Couldn't load personality. Please verify your configuration file at {lollms_app.configuration_path} or use the next menu to select a valid personality") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{lollms_app.config.get_personality_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.select_model() - - def main_menu(self): - while True: - print("\nMain Menu:") - print(f"{ASCIIColors.color_green}1 -{ASCIIColors.color_reset} Select Binding") - print(f"{ASCIIColors.color_green}2 -{ASCIIColors.color_reset} Select Model") - print(f"{ASCIIColors.color_green}3 -{ASCIIColors.color_reset} Select Personality") - print(f"{ASCIIColors.color_green}4 -{ASCIIColors.color_reset} Reinstall current Binding") - print(f"{ASCIIColors.color_green}5 -{ASCIIColors.color_reset} Reinstall current Personality") - print(f"{ASCIIColors.color_green}6 -{ASCIIColors.color_reset} Reset all installs") - print(f"{ASCIIColors.color_green}7 -{ASCIIColors.color_reset} Reset paths") - print(f"{ASCIIColors.color_green}0 -{ASCIIColors.color_reset} Back to app") - print(f"{ASCIIColors.color_green}-1 -{ASCIIColors.color_reset} Help") - print(f"{ASCIIColors.color_green}-2 -{ASCIIColors.color_reset} Exit app") - choice = input("Enter your choice: ").strip() - if choice == "1": - self.select_binding() - elif choice == "2": - self.select_model() - elif choice == "3": - self.select_personality() - elif choice == "4": - self.reinstall_binding() - elif choice == "5": - self.reinstall_personality() - elif choice == "6": - reset_all_installs() - elif choice == "6": - reset_paths() - - elif choice == "0": - print("Back to main app...") - break - elif choice == "-1": - self.show_commands_list() - elif choice == "-2": - print("Bye") - sys.exit(0) - else: - print("Invalid choice! Try again.") - class Conversation(LollmsApplication): def __init__( self, @@ -277,42 +22,20 @@ class Conversation(LollmsApplication): show_model_infos:bool=True, show_welcome_message:bool=True ): - # Fore it to be a path self.configuration_path = configuration_path self.is_logging = False self.log_file_path = "" self.bot_says = "" + # get paths lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_") # Configuration loading part config = LOLLMSConfig.autoload(lollms_paths, configuration_path) - super().__init__(config, lollms_paths=lollms_paths) - - # Build menu - self.menu = MainMenu(self) - - - # load binding - self.load_binding() - - if self.config.model_name is None: - self.menu.select_model() - - # Load model - self.load_model() - # cfg.binding_name = llm_binding.binding_folder_name - # cfg.model_name = model_name - - # Load personality - try: - self.load_personality() - except Exception as ex: - print(f"No personality selected. Please select one from the zoo. {ex}") - self.menu.select_personality() + super().__init__("lollms-console",config, lollms_paths) if show_logo: self.menu.show_logo() @@ -321,18 +44,21 @@ class Conversation(LollmsApplication): if show_personality_infos: print() + print(f"{ASCIIColors.color_green}-----------------------------------------------------------------") print(f"{ASCIIColors.color_green}Current personality : {ASCIIColors.color_reset}{self.personality}") print(f"{ASCIIColors.color_green}Version : {ASCIIColors.color_reset}{self.personality.version}") print(f"{ASCIIColors.color_green}Author : {ASCIIColors.color_reset}{self.personality.author}") print(f"{ASCIIColors.color_green}Description : {ASCIIColors.color_reset}{self.personality.personality_description}") + print(f"{ASCIIColors.color_green}-----------------------------------------------------------------") print() if show_model_infos: print() + print(f"{ASCIIColors.color_green}-----------------------------------------------------------------") print(f"{ASCIIColors.color_green}Current binding : {ASCIIColors.color_reset}{self.config['binding_name']}") print(f"{ASCIIColors.color_green}Current model : {ASCIIColors.color_reset}{self.config['model_name']}") print(f"{ASCIIColors.color_green}Personal data path : {ASCIIColors.color_reset}{self.lollms_paths.personal_path}") - + print(f"{ASCIIColors.color_green}-----------------------------------------------------------------") print() @@ -398,45 +124,6 @@ Participating personalities: - def load_binding(self): - if self.config.binding_name is None: - print(f"No bounding selected") - print("Please select a valid model or install a new one from a url") - self.menu.select_binding() - # cfg.download_model(url) - else: - try: - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths) - except Exception as ex: - print(ex) - print(f"Couldn't find binding. Please verify your configuration file at {self.configuration_path} or use the next menu to select a valid binding") - print(f"Trying to reinstall binding") - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths,installation_option=InstallOption.FORCE_INSTALL) - self.menu.select_binding() - - def load_model(self): - try: - self.model = ModelBuilder(self.binding).get_model() - except Exception as ex: - ASCIIColors.error(f"Couldn't load model. Please verify your configuration file at {self.configuration_path} or use the next menu to select a valid model") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_model_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.menu.select_model() - - - def load_personality(self): - try: - self.personality = PersonalityBuilder(self.lollms_paths, self.config, self.model).build_personality() - except Exception as ex: - ASCIIColors.error(f"Couldn't load personality. Please verify your configuration file at {self.configuration_path} or use the next menu to select a valid personality") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_personality_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.menu.select_model() - self.cond_tk = self.personality.model.tokenize(self.personality.personality_conditioning) - self.n_cond_tk = len(self.cond_tk) - def reset_context(self): if self.personality.include_welcome_message_in_disucssion: full_discussion = ( @@ -605,7 +292,7 @@ def main(): args = parser.parse_args() if args.reset_installs: - reset_all_installs() + LollmsApplication.reset_all_installs() if args.reset_personal_path: LollmsPaths.reset_configs() diff --git a/lollms/extension.py b/lollms/extension.py new file mode 100644 index 0000000..98d2c5d --- /dev/null +++ b/lollms/extension.py @@ -0,0 +1,40 @@ +# File : extension.py +# Author : ParisNeo +# Description : +# This is the main class to be imported by the extension +# it gives your code access to the model, the callback functions, the model conditionning etc +from lollms.helpers import ASCIIColors +from lollms.config import BaseConfig +from enum import Enum + + +__author__ = "parisneo" +__github__ = "https://github.com/ParisNeo/lollms-webui" +__copyright__ = "Copyright 2023, " +__license__ = "Apache 2.0" + + +class EXTENSION_TYPE(Enum): + EXTENSION_TYPE_STAND_ALONE = 0 # An extension that has access to the current personality and model but has an independant + + +class Extension(): + def __init__(self, metadata_file_path:str, app) -> None: + self.app = app + self.metadata_file_path = metadata_file_path + self.config = BaseConfig(file_path=metadata_file_path) + self.config.load_config() + + def install(self): + """ + Installation procedure (to be implemented) + """ + ASCIIColors.blue("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") + ASCIIColors.red(f"Installing {self.binding_folder_name}") + ASCIIColors.blue("*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*") + + def get_ui(): + """ + Get user interface of the extension + """ + return "

This is a ui extension template

" \ No newline at end of file diff --git a/lollms/paths.py b/lollms/paths.py index 0ff4414..c6020c1 100644 --- a/lollms/paths.py +++ b/lollms/paths.py @@ -74,6 +74,15 @@ class LollmsPaths: self.personal_log_path.mkdir(parents=True, exist_ok=True) self.personal_uploads_path.mkdir(parents=True, exist_ok=True) + if not self.bindings_zoo_path.exists(): + # Clone the repository to the target path + ASCIIColors.info("No bindings found in your personal space.\nCloning the personalities zoo") + subprocess.run(["git", "clone", bindings_zoo_repo, self.bindings_zoo_path]) + else: + # Pull the repository if it already exists + ASCIIColors.info("Bindings zoo found in your personal space.\nPulling last personalities zoo") + subprocess.run(["git", "-C", self.bindings_zoo_path, "pull"]) + if not self.personalities_zoo_path.exists(): # Clone the repository to the target path ASCIIColors.info("No personalities found in your personal space.\nCloning the personalities zoo") @@ -83,15 +92,6 @@ class LollmsPaths: ASCIIColors.info("Personalities zoo found in your personal space.\nPulling last personalities zoo") subprocess.run(["git", "-C", self.personalities_zoo_path, "pull"]) - if not self.bindings_zoo_path.exists(): - # Clone the repository to the target path - ASCIIColors.info("No personalities found in your personal space.\nCloning the personalities zoo") - subprocess.run(["git", "clone", bindings_zoo_repo, self.bindings_zoo_path]) - else: - # Pull the repository if it already exists - ASCIIColors.info("Personalities zoo found in your personal space.\nPulling last personalities zoo") - subprocess.run(["git", "-C", self.bindings_zoo_path, "pull"]) - def copy_default_config(self): local_config_path = self.personal_configuration_path / f"{self.tool_prefix}local_config.yaml" diff --git a/lollms/personality.py b/lollms/personality.py index c6f1fb4..bb494a3 100644 --- a/lollms/personality.py +++ b/lollms/personality.py @@ -1072,13 +1072,19 @@ class PersonalityBuilder: self.installation_option = installation_option - def build_personality(self): - if self.config["active_personality_id"]>=len(self.config["personalities"]): - ASCIIColors.warning("Personality ID was out of range. Resetting to 0.") - self.config["active_personality_id"]=0 - if len(self.config["personalities"][self.config["active_personality_id"]].split("/"))==3: + def build_personality(self, id:int=None): + if id is None: + id = self.config["active_personality_id"] + if self.config["active_personality_id"]>=len(self.config["personalities"]): + ASCIIColors.warning("Personality ID was out of range. Resetting to 0.") + self.config["active_personality_id"]=0 + id = 0 + else: + if id>len(self.config["personalities"]): + id = len(self.config["personalities"])-1 + if len(self.config["personalities"][id].split("/"))==3: self.personality = AIPersonality( - self.lollms_paths.personalities_zoo_path / self.config["personalities"][self.config["active_personality_id"]], + self.lollms_paths.personalities_zoo_path / self.config["personalities"][id], self.lollms_paths, self.config, self.model, @@ -1086,7 +1092,7 @@ class PersonalityBuilder: ) else: self.personality = AIPersonality( - self.config["personalities"][self.config["active_personality_id"]], + self.config["personalities"][id], self.lollms_paths, self.config, self.model, diff --git a/lollms/server.py b/lollms/server.py index 501bdf2..2ef0e67 100644 --- a/lollms/server.py +++ b/lollms/server.py @@ -11,6 +11,7 @@ from lollms.helpers import ASCIIColors from lollms.console import MainMenu from lollms.paths import LollmsPaths from lollms.console import MainMenu +from lollms.app import LollmsApplication from typing import List, Tuple import importlib from pathlib import Path @@ -28,39 +29,24 @@ def reset_all_installs(lollms_paths:LollmsPaths): ASCIIColors.info(f"Deleted file: {file_path}") -class LoLLMsServer: +class LoLLMsServer(LollmsApplication): def __init__(self): + + host = "localhost" port = "9601" self.clients = {} self.current_binding = None - self.active_model = None + self.model = None self.personalities = [] self.answer = [''] self.is_ready = True - self.lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_") - self.menu = MainMenu(self) parser = argparse.ArgumentParser() parser.add_argument('--host', '-hst', default=host, help='Host name') parser.add_argument('--port', '-prt', default=port, help='Port number') - parser.add_argument('--config', '-cfg', default=None, help='Path to the configuration file') - parser.add_argument('--bindings_path', '-bp', default=str(self.lollms_paths.bindings_zoo_path), - help='The path to the Bindings folder') - parser.add_argument('--personalities_path', '-pp', - default=str(self.lollms_paths.personalities_zoo_path), - help='The path to the personalities folder') - parser.add_argument('--models_path', '-mp', default=str(self.lollms_paths.personal_models_path), - help='The path to the models folder') - - parser.add_argument('--binding_name', '-b', default=None, - help='Binding to be used by default') - parser.add_argument('--model_name', '-m', default=None, - help='Model name') - parser.add_argument('--personality_full_name', '-p', default="personality", - help='Personality path relative to the personalities folder (language/category/name)') parser.add_argument('--reset_personal_path', action='store_true', help='Reset the personal path') parser.add_argument('--reset_config', action='store_true', help='Reset the configurations') @@ -70,7 +56,7 @@ class LoLLMsServer: args = parser.parse_args() if args.reset_installs: - reset_all_installs() + self.reset_all_installs() if args.reset_personal_path: LollmsPaths.reset_configs() @@ -83,50 +69,15 @@ class LoLLMsServer: ASCIIColors.success("LOLLMS configuration reset successfully") except: ASCIIColors.success("Couldn't reset LOLLMS configuration") + else: + lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_") # Configuration loading part - self.config = LOLLMSConfig.autoload(self.lollms_paths, args.config) + config = LOLLMSConfig.autoload(lollms_paths, None) - - if args.binding_name: - self.config.binding_name = args.binding_name - - if args.model_name: - self.config.model_name = args.model_name - - # Recover bindings path - self.personalities_path = Path(args.personalities_path) - self.bindings_path = Path(args.bindings_path) - self.models_path = Path(args.models_path) - if self.config.binding_name is None: - self.menu.select_binding() - else: - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths) - if self.config.model_name is None: - self.menu.select_model() - else: - try: - self.active_model = self.binding.build_model() - except Exception as ex: - print(f"{ASCIIColors.color_red}Couldn't load model Please select a valid model{ASCIIColors.color_reset}") - print(f"{ASCIIColors.color_red}{ex}{ASCIIColors.color_reset}") - self.menu.select_model() - - for p in self.config.personalities: - personality = AIPersonality( - self.config.lollms_paths.personalities_zoo_path/p, - self.lollms_paths, - self.config, - self.active_model - ) - self.personalities.append(personality) - - if self.config.active_personality_id>len(self.personalities): - self.config.active_personality_id = 0 - self.active_personality = self.personalities[self.config.active_personality_id] + super().__init__("lollms-server", config, lollms_paths) self.menu.show_logo() - self.app = Flask("LoLLMsServer") #self.app.config['SECRET_KEY'] = 'lollmssecret' CORS(self.app) # Enable CORS for all routes @@ -144,45 +95,6 @@ class LoLLMsServer: self.run(args.host, args.port) - def load_binding(self): - if self.config.binding_name is None: - print(f"No bounding selected") - print("Please select a valid model or install a new one from a url") - self.menu.select_binding() - # cfg.download_model(url) - else: - try: - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths) - except Exception as ex: - print(ex) - print(f"Couldn't find binding. Please verify your configuration file at {self.config.file_path} or use the next menu to select a valid binding") - print(f"Trying to reinstall binding") - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, InstallOption.FORCE_INSTALL) - self.menu.select_binding() - - def load_model(self): - try: - self.active_model = ModelBuilder(self.binding).get_model() - ASCIIColors.success("Model loaded successfully") - except Exception as ex: - ASCIIColors.error(f"Couldn't load model.") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_model_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.menu.select_model() - - def load_personality(self): - try: - self.personality = PersonalityBuilder(self.lollms_paths, self.config, self.model).build_personality() - except Exception as ex: - ASCIIColors.error(f"Couldn't load personality.") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_personality_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.menu.select_model() - self.cond_tk = self.personality.model.tokenize(self.personality.personality_conditioning) - self.n_cond_tk = len(self.cond_tk) - def initialize_routes(self): @self.socketio.on('connect') def handle_connect(): @@ -369,7 +281,7 @@ class LoLLMsServer: self.cp_config = copy.deepcopy(self.config) self.cp_config["model_name"] = data['model_name'] try: - self.active_model = self.binding.build_model() + self.model = self.binding.build_model() emit('select_model', {'success':True, 'model_name': model_name}, room=request.sid) except Exception as ex: print(ex) @@ -383,7 +295,7 @@ class LoLLMsServer: personality_path, self.lollms_paths, self.config, - self.active_model + self.model ) self.personalities.append(personality) self.config["personalities"].append(personality_path) @@ -413,13 +325,13 @@ class LoLLMsServer: @self.socketio.on('tokenize') def tokenize(data): prompt = data['prompt'] - tk = self.active_model.tokenize(prompt) + tk = self.model.tokenize(prompt) emit("tokenized", {"tokens":tk}) @self.socketio.on('detokenize') def detokenize(data): prompt = data['prompt'] - txt = self.active_model.detokenize(prompt) + txt = self.model.detokenize(prompt) emit("detokenized", {"text":txt}) @self.socketio.on('cancel_generation') @@ -442,7 +354,7 @@ class LoLLMsServer: return def generate_text(): self.is_ready = False - model = self.active_model + model = self.model self.clients[client_id]["is_generating"]=True self.clients[client_id]["requested_stop"]=False prompt = data['prompt'] @@ -462,7 +374,7 @@ class LoLLMsServer: self.answer = {"full_text":""} def callback(text, message_type: MSG_TYPE): if message_type == MSG_TYPE.MSG_TYPE_CHUNK: - ASCIIColors.success(f"generated:{len(self.answer['full_text'])} words", end='\r') + ASCIIColors.success(f"generated:{len(self.answer['full_text'].split())} words", end='\r') self.answer["full_text"] = self.answer["full_text"] + text self.socketio.emit('text_chunk', {'chunk': text, 'type':MSG_TYPE.MSG_TYPE_CHUNK.value}, room=client_id) self.socketio.sleep(0) @@ -479,8 +391,10 @@ class LoLLMsServer: fd = model.detokenize(tk[-min(self.config.ctx_size-n_predicts,n_tokens):]) try: - ASCIIColors.print("warm up", ASCIIColors.color_bright_cyan) - generated_text = model.generate(fd, n_predict=n_predicts, callback=callback, + ASCIIColors.print("warming up", ASCIIColors.color_bright_cyan) + generated_text = model.generate(fd, + n_predict=n_predicts, + callback=callback, temperature = parameters["temperature"], top_k = parameters["top_k"], top_p = parameters["top_p"], @@ -588,7 +502,7 @@ class LoLLMsServer: def run(self, host="localhost", port="9601"): print(f"{ASCIIColors.color_red}Current binding (model) : {ASCIIColors.color_reset}{self.binding}") - print(f"{ASCIIColors.color_red}Current personality : {ASCIIColors.color_reset}{self.active_personality}") + print(f"{ASCIIColors.color_red}Current personality : {ASCIIColors.color_reset}{self.config.personalities[self.config.active_personality_id]}") ASCIIColors.info(f"Serving on address: http://{host}:{port}") self.socketio.run(self.app, host=host, port=port) diff --git a/lollms/settings.py b/lollms/settings.py index 8258670..6165cdb 100644 --- a/lollms/settings.py +++ b/lollms/settings.py @@ -9,17 +9,11 @@ import argparse from tqdm import tqdm from lollms.personality import PersonalityBuilder from lollms.console import MainMenu - -def reset_all_installs(lollms_paths:LollmsPaths): - ASCIIColors.info("Removeing all configuration files to force reinstall") - ASCIIColors.info(f"Searching files from {lollms_paths.personal_configuration_path}") - for file_path in lollms_paths.personal_configuration_path.iterdir(): - if file_path.name!=f"{lollms_paths.tool_prefix}local_config.yaml" and file_path.suffix.lower()==".yaml": - file_path.unlink() - ASCIIColors.info(f"Deleted file: {file_path}") +from lollms.app import LollmsApplication -class Settings: + +class Settings(LollmsApplication): def __init__( self, configuration_path:str|Path=None, @@ -29,46 +23,16 @@ class Settings: show_model_infos:bool=True, show_welcome_message:bool=True ): - - # Fore it to be a path - self.is_logging = False - self.log_file_path = "" - - self.bot_says = "" + # get paths - self.lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_") + lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_") ASCIIColors.yellow("------ Lollms Paths ------") - ASCIIColors.info(self.lollms_paths) + ASCIIColors.info(lollms_paths) ASCIIColors.yellow("------ ------------ ------") + # Load maoin configuration + config = LOLLMSConfig.autoload(lollms_paths) - # Build menu - self.menu = MainMenu(self) - - # Change configuration - original = self.lollms_paths.default_cfg_path - if configuration_path is None: - local = self.lollms_paths.personal_configuration_path / f"{self.lollms_paths.tool_prefix}local_config.yaml" - else: - local = Path(configuration_path) - - if not local.exists(): - shutil.copy(original, local) - self.cfg_path = local - - self.config = LOLLMSConfig(self.cfg_path, self.lollms_paths) - # load binding - if self.config.binding_name is not None: - self.load_binding() - # Load model - if self.config.model_name is not None: - self.load_model() - - # Load personality - try: - self.load_personality() - except Exception as ex: - print(f"No personality selected. Please select one from the zoo. {ex}") - self.menu.select_personality() + super().__init__("lollms-settings", config, lollms_paths) if show_logo: self.menu.show_logo() @@ -145,43 +109,6 @@ Participating personalities: self.is_logging = False - - def load_binding(self): - if self.config.binding_name is None: - print(f"No bounding selected") - print("Please select a valid model or install a new one from a url") - self.menu.select_binding() - # cfg.download_model(url) - else: - try: - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths) - except Exception as ex: - print(ex) - print(f"Couldn't find binding. Please verify your configuration file at {self.cfg_path} or use the next menu to select a valid binding") - print(f"Trying to reinstall binding") - self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths,installation_option=InstallOption.FORCE_INSTALL) - self.menu.select_binding() - - def load_model(self): - try: - self.model = ModelBuilder(self.binding).get_model() - except Exception as ex: - ASCIIColors.error(f"Couldn't load model. Please verify your configuration file at {self.cfg_path} or use the next menu to select a valid model") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_model_path_infos()}") - print("Please select a valid model or install a new one from a url") - self.menu.select_model() - - - def load_personality(self): - try: - self.personality = PersonalityBuilder(self.lollms_paths, self.config, self.model).build_personality() - except Exception as ex: - ASCIIColors.error(f"Couldn't load personality. Please verify your configuration file at {self.cfg_path} or use the next menu to select a valid personality") - ASCIIColors.error(f"Binding returned this exception : {ex}") - ASCIIColors.error(f"{self.config.get_personality_path_infos()}") - print("Please select a valid model or install a new one from a url") - def reset_context(self): if self.personality.include_welcome_message_in_disucssion: full_discussion = ( @@ -248,7 +175,7 @@ def main(): args = parser.parse_args() if args.reset_installs: - reset_all_installs() + LollmsApplication.reset_all_installs() if args.reset_personal_path: LollmsPaths.reset_configs() diff --git a/lollms/terminal.py b/lollms/terminal.py new file mode 100644 index 0000000..a1cffc2 --- /dev/null +++ b/lollms/terminal.py @@ -0,0 +1,332 @@ + +from lollms.helpers import ASCIIColors +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lollms.app import LollmsApplication + +from lollms.binding import BindingBuilder +from lollms.config import InstallOption +from lollms.personality import PersonalityBuilder + +from tqdm import tqdm +import pkg_resources +from pathlib import Path +import yaml +import sys + +class MainMenu: + def __init__(self, lollms_app:'LollmsApplication'): + self.binding_infs = [] + self.lollms_app = lollms_app + + def show_logo(self): + print(f"{ASCIIColors.color_bright_yellow}") + print(" ___ ___ ___ ___ ___ ___ ") + print(" /\__\ /\ \ /\__\ /\__\ /\__\ /\ \ ") + print(" /:/ / /::\ \ /:/ / /:/ / /::| | /::\ \ ") + print(" /:/ / /:/\:\ \ /:/ / /:/ / /:|:| | /:/\ \ \ ") + print(" /:/ / /:/ \:\ \ /:/ / /:/ / /:/|:|__|__ _\:\~\ \ \ ") + print(" /:/__/ /:/__/ \:\__\ /:/__/ /:/__/ /:/ |::::\__\ /\ \:\ \ \__\ ") + print(" \:\ \ \:\ \ /:/ / \:\ \ \:\ \ \/__/~~/:/ / \:\ \:\ \/__/ ") + print(" \:\ \ \:\ /:/ / \:\ \ \:\ \ /:/ / \:\ \:\__\ ") + print(" \:\ \ \:\/:/ / \:\ \ \:\ \ /:/ / \:\/:/ / ") + print(" \:\__\ \::/ / \:\__\ \:\__\ /:/ / \::/ / ") + print(" \/__/ \/__/ \/__/ \/__/ \/__/ \/__/ ") + + + + + print(f"{ASCIIColors.color_reset}") + print(f"{ASCIIColors.color_red}Version: {ASCIIColors.color_green}{pkg_resources.get_distribution('lollms').version}") + print(f"{ASCIIColors.color_red}By : {ASCIIColors.color_green}ParisNeo") + print(f"{ASCIIColors.color_reset}") + + def show_commands_list(self): + print() + print("Commands:") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} menu: shows main menu") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} help: shows this info") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} reset: resets the context") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} : forces the model to continue generating") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} context_infos: current context size and space left before cropping") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} start_log: starts logging the discussion to a text file") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} stop_log: stops logging the discussion to a text file") + print(f" {ASCIIColors.color_red}├{ASCIIColors.color_reset} send_file: uploads a file to the AI") + print(f" {ASCIIColors.color_red}└{ASCIIColors.color_reset} exit: exists the console") + + if self.lollms_app.personality: + if self.lollms_app.personality.help !="": + print(f"Personality help:") + print(f"{self.lollms_app.personality.help}") + + + + def show_menu(self, options, title="Menu:", selection:int=None): + ASCIIColors.yellow(title) + for index, option in enumerate(options): + if selection is not None and index==selection: + print(f"{ASCIIColors.color_green}{index + 1} - {option}{ASCIIColors.color_reset}") + else: + print(f"{ASCIIColors.color_green}{index + 1} -{ASCIIColors.color_reset} {option}") + choice = input("Enter your choice: ") + return int(choice) if choice.isdigit() else -1 + + def select_binding(self): + bindings_list = [] + print() + print(f"{ASCIIColors.color_green}Current binding: {ASCIIColors.color_reset}{self.lollms_app.config['binding_name']}") + for p in self.lollms_app.lollms_paths.bindings_zoo_path.iterdir(): + if p.is_dir() and not p.stem.startswith("."): + with open(p/"binding_card.yaml", "r") as f: + card = yaml.safe_load(f) + with open(p/"models.yaml", "r") as f: + models = yaml.safe_load(f) + is_installed = (self.lollms_app.lollms_paths.personal_configuration_path/f"binding_{p.name}.yaml").exists() + entry=f"{ASCIIColors.color_green if is_installed else ''}{'*' if self.lollms_app.config['binding_name']==card['name'] else ''} {card['name']} (by {card['author']})" + bindings_list.append(entry) + entry={ + "name":p.name, + "card":card, + "models":models, + "installed": is_installed + } + self.binding_infs.append(entry) + bindings_list += ["Back"] + choice = self.show_menu(bindings_list) + if 1 <= choice <= len(bindings_list)-1: + print(f"You selected binding: {ASCIIColors.color_green}{self.binding_infs[choice - 1]['name']}{ASCIIColors.color_reset}") + self.lollms_app.config['binding_name']=self.binding_infs[choice - 1]['name'] + self.lollms_app.load_binding() + self.lollms_app.config['model_name']=None + self.lollms_app.config.save_config() + elif choice <= len(bindings_list): + return + else: + print("Invalid choice!") + + def select_model(self): + print() + print(f"{ASCIIColors.color_green}Current binding: {ASCIIColors.color_reset}{self.lollms_app.config['binding_name']}") + print(f"{ASCIIColors.color_green}Current model: {ASCIIColors.color_reset}{self.lollms_app.config['model_name']}") + + models_dir:Path = (self.lollms_app.lollms_paths.personal_models_path/self.lollms_app.config['binding_name']) + models_dir.mkdir(parents=True, exist_ok=True) + + models_list = [v for v in self.lollms_app.binding.list_models(self.lollms_app.config)] + ["Install model", "Change binding", "Back"] + choice = self.show_menu(models_list) + if 1 <= choice <= len(models_list)-3: + print(f"You selected model: {ASCIIColors.color_green}{models_list[choice - 1]}{ASCIIColors.color_reset}") + self.lollms_app.config['model_name']=models_list[choice - 1] + self.lollms_app.config.save_config() + self.lollms_app.load_model() + elif choice <= len(models_list)-2: + self.install_model() + elif choice <= len(models_list)-1: + self.select_binding() + self.select_model() + elif choice <= len(models_list): + return + else: + print("Invalid choice!") + + def install_model(self): + + models_list = ["Install model from internet","Install model from local file","Back"] + choice = self.show_menu(models_list) + if 1 <= choice <= len(models_list)-2: + url = input("Give a URL to the model to be downloaded :") + def progress_callback(blocks, block_size, total_size): + tqdm_bar.total=total_size + tqdm_bar.update(block_size) + + # Usage example + with tqdm(total=100, unit="%", desc="Download Progress", ncols=80) as tqdm_bar: + self.lollms_app.config.download_model(url,self.lollms_app.binding, progress_callback) + self.select_model() + elif choice <= len(models_list)-1: + path = Path(input("Give a path to the model to be used on your PC:")) + if path.exists(): + self.lollms_app.config.reference_model(path) + self.select_model() + elif choice <= len(models_list): + return + else: + print("Invalid choice!") + + def mount_personality(self): + print() + ASCIIColors.red(f"Mounted personalities:") + for i,personality in enumerate(self.lollms_app.config['personalities']): + if i==self.lollms_app.config['active_personality_id']: + ASCIIColors.green(personality) + else: + ASCIIColors.yellow(personality) + personality_languages = [p.stem for p in self.lollms_app.lollms_paths.personalities_zoo_path.iterdir() if p.is_dir() and not p.name.startswith(".")] + ["Back"] + print("Select language") + choice = self.show_menu(personality_languages) + if 1 <= choice <= len(personality_languages)-1: + language = personality_languages[choice - 1] + print(f"You selected language: {ASCIIColors.color_green}{language}{ASCIIColors.color_reset}") + personality_categories = [p.stem for p in (self.lollms_app.lollms_paths.personalities_zoo_path/language).iterdir() if p.is_dir() and not p.name.startswith(".")]+["Back"] + print("Select category") + choice = self.show_menu(personality_categories) + if 1 <= choice <= len(personality_categories)-1: + category = personality_categories[choice - 1] + print(f"You selected category: {ASCIIColors.color_green}{category}{ASCIIColors.color_reset}") + personality_names = [p.stem for p in (self.lollms_app.lollms_paths.personalities_zoo_path/language/category).iterdir() if p.is_dir() and not p.name.startswith(".")]+["Back"] + print("Select personality") + choice = self.show_menu(personality_names) + if 1 <= choice <= len(personality_names)-1: + name = personality_names[choice - 1] + print(f"You selected personality: {ASCIIColors.color_green}{name}{ASCIIColors.color_reset}") + self.lollms_app.config["personalities"].append(f"{language}/{category}/{name}") + self.lollms_app.mount_personality(len(self.lollms_app.config["personalities"])-1) + self.lollms_app.config.save_config() + print("Personality mounted successfully!") + elif 1 <= choice <= len(personality_names): + return + else: + print("Invalid choice!") + elif 1 <= choice <= len(personality_categories): + return + else: + print("Invalid choice!") + elif 1 <= choice <= len(personality_languages): + return + else: + print("Invalid choice!") + + def vew_mounted_personalities(self): + ASCIIColors.info("Here is the list of mounted personalities") + entries = self.lollms_app.config['personalities'] + for id, entry in enumerate(entries): + if id != self.lollms_app.config.active_personality_id: + ASCIIColors.yellow(f"{id+1} - {entry}") + else: + ASCIIColors.green(f"{id+1} - {entry}") + self.show_menu(["Back"]) + + + def unmount_personality(self): + ASCIIColors.info("Select personality to unmount") + entries = self.lollms_app.config['personalities']+["Back"] + try: + choice = int(self.show_menu(entries, self.lollms_app.config['active_personality_id']))-1 + if choice