mirror of
https://github.com/ParisNeo/lollms.git
synced 2024-12-18 20:27:58 +00:00
removed useless junk
This commit is contained in:
parent
f1b081dc3a
commit
d100194b70
@ -1,319 +0,0 @@
|
||||
from lollms.config import InstallOption
|
||||
from lollms.binding import BindingBuilder, ModelBuilder
|
||||
from lollms.personality import MSG_TYPE, PersonalityBuilder
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from ascii_colors import ASCIIColors
|
||||
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.app import LollmsApplication
|
||||
from lollms.terminal import MainMenu
|
||||
|
||||
from typing import Callable
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import yaml
|
||||
import time
|
||||
import sys
|
||||
|
||||
class Conversation(LollmsApplication):
|
||||
def __init__(
|
||||
self,
|
||||
configuration_path:str|Path=None,
|
||||
show_logo:bool=True,
|
||||
show_commands_list:bool=False,
|
||||
show_personality_infos:bool=True,
|
||||
show_model_infos:bool=True,
|
||||
show_welcome_message:bool=True,
|
||||
show_time_elapsed:bool = False
|
||||
):
|
||||
# Fore it to be a path
|
||||
self.configuration_path = configuration_path
|
||||
self.is_logging = False
|
||||
self.log_file_path = ""
|
||||
self.show_time_elapsed = show_time_elapsed
|
||||
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__("lollms-console",config, lollms_paths)
|
||||
|
||||
if show_logo:
|
||||
self.menu.show_logo()
|
||||
if show_commands_list:
|
||||
self.menu.show_commands_list()
|
||||
|
||||
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()
|
||||
|
||||
|
||||
# If there is a disclaimer, show it
|
||||
if self.personality.disclaimer != "":
|
||||
print(f"\n{ASCIIColors.color_red}Disclaimer")
|
||||
print(self.personality.disclaimer)
|
||||
print(f"{ASCIIColors.color_reset}")
|
||||
|
||||
if show_welcome_message and self.personality.welcome_message:
|
||||
ASCIIColors.red(self.personality.name+": ", end="")
|
||||
print(self.personality.welcome_message)
|
||||
|
||||
def ask_override_file(self):
|
||||
user_input = input("Would you like to override the existing file? (Y/N): ")
|
||||
user_input = user_input.lower()
|
||||
if user_input == "y" or user_input == "yes":
|
||||
print("File will be overridden.")
|
||||
return True
|
||||
elif user_input == "n" or user_input == "no":
|
||||
print("File will not be overridden.")
|
||||
return False
|
||||
else:
|
||||
print("Invalid input. Please enter 'Y' or 'N'.")
|
||||
# Call the function again recursively to prompt the user for valid input
|
||||
return self.ask_override_file()
|
||||
|
||||
def start_log(self, file_name):
|
||||
if Path(file_name).is_absolute():
|
||||
self.log_file_path = Path(file_name)
|
||||
else:
|
||||
home_dir = self.lollms_paths.personal_log_path
|
||||
home_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.log_file_path = home_dir/file_name
|
||||
if self.log_file_path.exists():
|
||||
if not self.ask_override_file():
|
||||
print("Cancelled")
|
||||
return
|
||||
try:
|
||||
with(open(self.log_file_path, "w") as f):
|
||||
self.header = f"""------------------------
|
||||
Log file for lollms discussion
|
||||
Participating personalities:
|
||||
{self.config['personalities']}
|
||||
------------------------
|
||||
"""
|
||||
f.write(self.header)
|
||||
self.is_logging = True
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def log(self, text, append=False):
|
||||
try:
|
||||
with(open(self.log_file_path, "a" if append else "w") as f):
|
||||
f.write(text) if append else f.write(self.header+self.personality.personality_conditioning+text)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def stop_log(self):
|
||||
self.is_logging = False
|
||||
|
||||
def reset_context(self):
|
||||
if self.personality.include_welcome_message_in_disucssion:
|
||||
full_discussion = (
|
||||
self.personality.ai_message_prefix +
|
||||
self.personality.welcome_message +
|
||||
self.personality.link_text
|
||||
)
|
||||
else:
|
||||
full_discussion = ""
|
||||
return full_discussion
|
||||
|
||||
|
||||
|
||||
def remove_text_from_string(self, string, text_to_find):
|
||||
"""
|
||||
Removes everything from the first occurrence of the specified text in the string (case-insensitive).
|
||||
|
||||
Parameters:
|
||||
string (str): The original string.
|
||||
text_to_find (str): The text to find in the string.
|
||||
|
||||
Returns:
|
||||
str: The updated string.
|
||||
"""
|
||||
index = string.lower().find(text_to_find.lower())
|
||||
|
||||
if index != -1:
|
||||
string = string[:index]
|
||||
|
||||
return string
|
||||
|
||||
def start_conversation(self):
|
||||
full_discussion = self.reset_context()
|
||||
while True:
|
||||
try:
|
||||
ump = self.config.discussion_prompt_separator +self.config.user_name+": " if self.config.use_user_name_in_discussions else self.personality.user_message_prefix
|
||||
if self.config.use_user_name_in_discussions:
|
||||
prompt = input(f"{ASCIIColors.color_green}{self.config.user_name}: {ASCIIColors.color_reset}")
|
||||
else:
|
||||
prompt = input(f"{ASCIIColors.color_green}You: {ASCIIColors.color_reset}")
|
||||
if self.show_time_elapsed:
|
||||
t0 = time.time() #Time at start of request
|
||||
if prompt == "exit":
|
||||
return
|
||||
if prompt == "menu":
|
||||
self.menu.main_menu()
|
||||
continue
|
||||
if prompt == "reset":
|
||||
self.reset_context()
|
||||
print(f"{ASCIIColors.color_red}Context reset issued{ASCIIColors.color_reset}")
|
||||
continue
|
||||
if prompt == "start_log":
|
||||
fp = input("Please enter a log file path (ex: log.txt): ")
|
||||
self.start_log(fp)
|
||||
print(f"{ASCIIColors.color_red}Started logging to : {self.log_file_path}{ASCIIColors.color_reset}")
|
||||
continue
|
||||
if prompt == "stop_log":
|
||||
self.stop_log()
|
||||
print(f"{ASCIIColors.color_red}Log stopped{ASCIIColors.color_reset}")
|
||||
continue
|
||||
if prompt == "send_file":
|
||||
if self.personality.processor is None:
|
||||
print(f"{ASCIIColors.color_red}This personality doesn't support file reception{ASCIIColors.color_reset}")
|
||||
continue
|
||||
fp = input("Please enter a file path: ")
|
||||
# Remove double quotes using string slicing
|
||||
if fp.startswith('"') and fp.endswith('"'):
|
||||
fp = fp[1:-1]
|
||||
if self.personality.processor.add_file(fp):
|
||||
print(f"{ASCIIColors.color_green}File imported{ASCIIColors.color_reset}")
|
||||
else:
|
||||
print(f"{ASCIIColors.color_red}Couldn't load file{ASCIIColors.color_reset}")
|
||||
continue
|
||||
|
||||
|
||||
if prompt == "context_infos":
|
||||
tokens = self.personality.model.tokenize(full_discussion)
|
||||
print(f"{ASCIIColors.color_green}Current context has {len(tokens)} tokens/ {self.config.ctx_size}{ASCIIColors.color_reset}")
|
||||
continue
|
||||
|
||||
if prompt != '':
|
||||
if self.personality.processor is not None and self.personality.processor_cfg["process_model_input"]:
|
||||
preprocessed_prompt = self.personality.processor.process_model_input(prompt)
|
||||
else:
|
||||
preprocessed_prompt = prompt
|
||||
|
||||
if self.personality.processor is not None and self.personality.processor_cfg["custom_workflow"]:
|
||||
full_discussion += (
|
||||
ump +
|
||||
preprocessed_prompt
|
||||
)
|
||||
else:
|
||||
full_discussion += (
|
||||
ump +
|
||||
preprocessed_prompt +
|
||||
self.personality.link_text +
|
||||
self.personality.ai_message_prefix
|
||||
)
|
||||
|
||||
def callback(text, type:MSG_TYPE=None, metadata:dict={}):
|
||||
if type == MSG_TYPE.MSG_TYPE_CHUNK:
|
||||
# Replace stdout with the default stdout
|
||||
sys.stdout = sys.__stdout__
|
||||
print(text, end="", flush=True)
|
||||
bot_says = self.bot_says + text
|
||||
antiprompt = self.personality.detect_antiprompt(bot_says)
|
||||
if antiprompt:
|
||||
self.bot_says = self.remove_text_from_string(bot_says,antiprompt)
|
||||
ASCIIColors.warning(f"Detected hallucination with antiprompt {antiprompt}")
|
||||
return False
|
||||
else:
|
||||
self.bot_says = bot_says
|
||||
return True
|
||||
|
||||
tk = self.personality.model.tokenize(full_discussion)
|
||||
n_tokens = len(tk)
|
||||
fd = self.personality.model.detokenize(tk[-min(self.config.ctx_size-self.n_cond_tk,n_tokens):])
|
||||
|
||||
print(f"{ASCIIColors.color_red}{self.personality.name}:{ASCIIColors.color_reset}", end='', flush=True)
|
||||
if self.personality.processor is not None and self.personality.processor_cfg["custom_workflow"]:
|
||||
output = self.personality.processor.run_workflow(prompt, previous_discussion_text=self.personality.personality_conditioning+fd, callback=callback)
|
||||
print(output)
|
||||
|
||||
else:
|
||||
output = self.personality.model.generate(self.personality.personality_conditioning+fd, n_predict=self.personality.model_n_predicts, callback=callback)
|
||||
full_discussion += output.strip()
|
||||
print()
|
||||
|
||||
if self.personality.processor is not None and self.personality.processor_cfg["process_model_output"]:
|
||||
output = self.personality.processor.process_model_output(output)
|
||||
|
||||
self.log(full_discussion)
|
||||
|
||||
if self.show_time_elapsed:
|
||||
t1 = time.time() # Time at end of response
|
||||
print(f"{ASCIIColors.color_cyan}Time Elapsed: {ASCIIColors.color_reset}",str(int((t1-t0)*1000)),"ms\n") # Total time elapsed since t0 in ms
|
||||
except KeyboardInterrupt:
|
||||
print("Keyboard interrupt detected.\nBye")
|
||||
break
|
||||
|
||||
print("Done")
|
||||
print(f"{self.personality}")
|
||||
|
||||
def main():
|
||||
# Create the argument parser
|
||||
parser = argparse.ArgumentParser(description='App Description')
|
||||
|
||||
# Add the configuration path argument
|
||||
parser.add_argument('--configuration_path', default=None,
|
||||
help='Path to the configuration file')
|
||||
|
||||
|
||||
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')
|
||||
parser.add_argument('--reset_installs', action='store_true', help='Reset all installation status')
|
||||
parser.add_argument('--show_time_elapsed', action='store_true', help='Enables response time')
|
||||
|
||||
|
||||
# Parse the command-line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.reset_installs:
|
||||
LollmsApplication.reset_all_installs()
|
||||
|
||||
if args.reset_personal_path:
|
||||
LollmsPaths.reset_configs()
|
||||
|
||||
if args.reset_config:
|
||||
lollms_paths = LollmsPaths.find_paths(tool_prefix="lollms_server_")
|
||||
cfg_path = lollms_paths.personal_configuration_path / f"{lollms_paths.tool_prefix}local_config.yaml"
|
||||
try:
|
||||
cfg_path.unlink()
|
||||
ASCIIColors.success("LOLLMS configuration reset successfully")
|
||||
except:
|
||||
ASCIIColors.success("Couldn't reset LOLLMS configuration")
|
||||
if args.show_time_elapsed:
|
||||
show_time_elapsed = True
|
||||
else:
|
||||
show_time_elapsed = False
|
||||
|
||||
# Parse the command-line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
configuration_path = args.configuration_path
|
||||
|
||||
lollms_app = Conversation(configuration_path=configuration_path, show_commands_list=True, show_time_elapsed=show_time_elapsed)
|
||||
lollms_app.start_conversation()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
2
lollms/apps/discord_bot/.gitignore
vendored
2
lollms/apps/discord_bot/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
discord_config.yaml
|
||||
data
|
@ -1,31 +0,0 @@
|
||||
# Project Name
|
||||
|
||||
## Description
|
||||
This project is a Discord bot that utilizes the lollms library to provide assistance and generate responses based on user input.
|
||||
|
||||
## Installation
|
||||
1. Clone the repository:
|
||||
```shell
|
||||
git clone https://github.com/your-username/your-repository.git
|
||||
```
|
||||
2. Install the required dependencies:
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
3. Create a `discord_config.yaml` file in the project directory and add your bot token to it.
|
||||
|
||||
## Usage
|
||||
1. Run the bot:
|
||||
```shell
|
||||
python main.py
|
||||
```
|
||||
2. The bot will join your Discord server and send a welcome message to new members.
|
||||
3. Use the following commands to interact with the bot:
|
||||
- `!lollms <prompt>`: Chat with the bot and receive generated responses based on the input prompt.
|
||||
- `!install`: Get instructions on how to install the lollms library.
|
||||
|
||||
## Contributing
|
||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||
|
||||
## License
|
||||
[Apache 2.0](https://choosealicense.com/licenses/apache-2.0/)
|
@ -1,267 +0,0 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from lollms.app import LollmsApplication
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
from ascii_colors import ASCIIColors
|
||||
from safe_store import TextVectorizer, GenericDataLoader, VisualizationMethod, VectorizationMethod
|
||||
from typing import Tuple, List
|
||||
|
||||
context={
|
||||
"discussion":""
|
||||
}
|
||||
|
||||
client = commands.Bot(command_prefix='!', intents=discord.Intents.all())
|
||||
|
||||
lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_discord_")
|
||||
config = LOLLMSConfig.autoload(lollms_paths)
|
||||
lollms_app = LollmsApplication("lollms_bot", config=config, lollms_paths=lollms_paths)
|
||||
|
||||
current_path = Path(__file__).resolve().parent
|
||||
root_config_path = lollms_app.lollms_paths.personal_configuration_path / "discord_bot"
|
||||
root_config_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
root_data_path = lollms_app.lollms_paths.personal_data_path / "discord_bot"
|
||||
root_data_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
config_path = root_config_path / 'config.yaml'
|
||||
text_vectorzer = TextVectorizer(
|
||||
database_path=root_data_path / "db.json",
|
||||
vectorization_method=VectorizationMethod.TFIDF_VECTORIZER,
|
||||
save_db=True
|
||||
)
|
||||
files = [f for f in root_data_path.iterdir() if f.suffix in [".txt", ".md", ".pdf"]]
|
||||
for f in files:
|
||||
txt = GenericDataLoader.read_file(f)
|
||||
text_vectorzer.add_document(f, txt, 512, 128, False)
|
||||
text_vectorzer.index()
|
||||
|
||||
if not config_path.exists():
|
||||
bot_token = input("Please enter your bot token: ")
|
||||
config = {'bot_token': bot_token, "summoning_word":"!lollms"}
|
||||
with open(config_path, 'w') as config_file:
|
||||
yaml.safe_dump(config, config_file)
|
||||
else:
|
||||
with open(config_path, 'r') as config_file:
|
||||
config = yaml.safe_load(config_file)
|
||||
bot_token = config['bot_token']
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
print(f'Logged in as {client.user}')
|
||||
print('------')
|
||||
|
||||
@client.event
|
||||
async def on_member_join(member):
|
||||
channel = member.guild.system_channel
|
||||
if channel is not None:
|
||||
await channel.send(f'Welcome {member.mention} to the server! How can I assist you today?')
|
||||
|
||||
def prepare_query(discussion, prompt, n_tokens: int = 0) -> Tuple[str, str, List[str]]:
|
||||
"""
|
||||
Prepares the query for the model.
|
||||
|
||||
Args:
|
||||
client_id (str): The client ID.
|
||||
message_id (int): The message ID. Default is -1.
|
||||
is_continue (bool): Whether the query is a continuation. Default is False.
|
||||
n_tokens (int): The number of tokens. Default is 0.
|
||||
|
||||
Returns:
|
||||
Tuple[str, str, List[str]]: The prepared query, original message content, and tokenized query.
|
||||
"""
|
||||
|
||||
# Define current message
|
||||
current_message = prompt
|
||||
|
||||
# Build the conditionning text block
|
||||
conditionning = lollms_app.personality.personality_conditioning
|
||||
|
||||
# Check if there are document files to add to the prompt
|
||||
documentation = ""
|
||||
if lollms_app.personality.persona_data_vectorizer:
|
||||
if documentation=="":
|
||||
documentation="!@>Documentation:\n"
|
||||
docs, sorted_similarities = lollms_app.personality.persona_data_vectorizer.recover_text(current_message, top_k=lollms_app.config.data_vectorization_nb_chunks)
|
||||
for doc, infos in zip(docs, sorted_similarities):
|
||||
documentation += f"document chunk:\n{doc}"
|
||||
|
||||
|
||||
if len(lollms_app.personality.text_files) > 0 and lollms_app.personality.vectorizer:
|
||||
if documentation=="":
|
||||
documentation="!@>Documentation:\n"
|
||||
docs, sorted_similarities = lollms_app.personality.vectorizer.recover_text(current_message.content, top_k=lollms_app.config.data_vectorization_nb_chunks)
|
||||
for doc, infos in zip(docs, sorted_similarities):
|
||||
documentation += f"document chunk:\nchunk path: {infos[0]}\nchunk content:{doc}"
|
||||
|
||||
# Check if there is discussion history to add to the prompt
|
||||
history = ""
|
||||
if lollms_app.config.use_discussions_history and lollms_app.long_term_memory is not None:
|
||||
if history=="":
|
||||
documentation="!@>History:\n"
|
||||
docs, sorted_similarities = lollms_app.long_term_memory.recover_text(current_message.content, top_k=lollms_app.config.data_vectorization_nb_chunks)
|
||||
for doc, infos in zip(docs, sorted_similarities):
|
||||
history += f"discussion chunk:\ndiscussion title: {infos[0]}\nchunk content:{doc}"
|
||||
|
||||
# Add information about the user
|
||||
user_description=""
|
||||
if lollms_app.config.use_user_name_in_discussions:
|
||||
user_description="!@>User description:\n"+lollms_app.config.user_description
|
||||
|
||||
|
||||
# Tokenize the conditionning text and calculate its number of tokens
|
||||
tokens_conditionning = lollms_app.model.tokenize(conditionning)
|
||||
n_cond_tk = len(tokens_conditionning)
|
||||
|
||||
# Tokenize the documentation text and calculate its number of tokens
|
||||
if len(documentation)>0:
|
||||
tokens_documentation = lollms_app.model.tokenize(documentation)
|
||||
n_doc_tk = len(tokens_documentation)
|
||||
else:
|
||||
tokens_documentation = []
|
||||
n_doc_tk = 0
|
||||
|
||||
# Tokenize the history text and calculate its number of tokens
|
||||
if len(history)>0:
|
||||
tokens_history = lollms_app.model.tokenize(history)
|
||||
n_history_tk = len(tokens_history)
|
||||
else:
|
||||
tokens_history = []
|
||||
n_history_tk = 0
|
||||
|
||||
|
||||
# Tokenize user description
|
||||
if len(user_description)>0:
|
||||
tokens_user_description = lollms_app.model.tokenize(user_description)
|
||||
n_user_description_tk = len(tokens_user_description)
|
||||
else:
|
||||
tokens_user_description = []
|
||||
n_user_description_tk = 0
|
||||
|
||||
|
||||
# Calculate the total number of tokens between conditionning, documentation, and history
|
||||
total_tokens = n_cond_tk + n_doc_tk + n_history_tk + n_user_description_tk
|
||||
|
||||
# Calculate the available space for the messages
|
||||
available_space = lollms_app.config.ctx_size - n_tokens - total_tokens
|
||||
|
||||
# Raise an error if the available space is 0 or less
|
||||
if available_space<1:
|
||||
raise Exception("Not enough space in context!!")
|
||||
|
||||
# Accumulate messages until the cumulative number of tokens exceeds available_space
|
||||
tokens_accumulated = 0
|
||||
|
||||
|
||||
# Initialize a list to store the full messages
|
||||
full_message_list = []
|
||||
# If this is not a continue request, we add the AI prompt
|
||||
message_tokenized = lollms_app.model.tokenize(
|
||||
"\n" +lollms_app.personality.ai_message_prefix.strip()
|
||||
)
|
||||
full_message_list.append(message_tokenized)
|
||||
# Update the cumulative number of tokens
|
||||
tokens_accumulated += len(message_tokenized)
|
||||
|
||||
|
||||
message_tokenized = lollms_app.model.tokenize(discussion)
|
||||
if len(message_tokenized)>lollms_app.config.ctx_size-1024:
|
||||
pos = message_tokenized[-(lollms_app.config.ctx_size-1024)]
|
||||
detokenized = lollms_app.model.detokenize(message_tokenized[pos:pos+20])
|
||||
position = discussion.find(detokenized)
|
||||
if position!=-1 and position>0:
|
||||
discussion_messages = discussion[-position:]
|
||||
else:
|
||||
discussion_messages = discussion[-(lollms_app.config.ctx_size-1024):]
|
||||
else:
|
||||
discussion_messages = discussion
|
||||
|
||||
# Build the final prompt by concatenating the conditionning and discussion messages
|
||||
prompt_data = conditionning + documentation + history + user_description + discussion_messages
|
||||
|
||||
# Tokenize the prompt data
|
||||
tokens = lollms_app.model.tokenize(prompt_data)
|
||||
|
||||
# if this is a debug then show prompt construction details
|
||||
if lollms_app.config["debug"]:
|
||||
ASCIIColors.bold("CONDITIONNING")
|
||||
ASCIIColors.yellow(conditionning)
|
||||
ASCIIColors.bold("DOC")
|
||||
ASCIIColors.yellow(documentation)
|
||||
ASCIIColors.bold("HISTORY")
|
||||
ASCIIColors.yellow(history)
|
||||
ASCIIColors.bold("DISCUSSION")
|
||||
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.info(f"prompt size:{len(tokens)} tokens")
|
||||
ASCIIColors.info(f"available space after doc and history:{available_space} tokens")
|
||||
|
||||
# Return the prepared query, original message content, and tokenized query
|
||||
return prompt_data
|
||||
|
||||
@client.event
|
||||
async def on_message(message):
|
||||
if message.author == client.user:
|
||||
return
|
||||
if message.content.startswith(config["summoning_word"]):
|
||||
prompt = message.content[len(config["summoning_word"])+1:]
|
||||
|
||||
context['discussion'] = prepare_query(context['discussion'], prompt, 1024)
|
||||
context['discussion']+= "\n!@>" + message.author.name +": "+ prompt + "\n" + f"{lollms_app.personality.ai_message_prefix}"
|
||||
context['current_response']=""
|
||||
def callback(text, type=None):
|
||||
antiprompt = lollms_app.personality.detect_antiprompt(context['current_response'])
|
||||
if antiprompt:
|
||||
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")
|
||||
context['current_response'] = lollms_app.remove_text_from_string(context['current_response'],antiprompt)
|
||||
return False
|
||||
|
||||
context['current_response'] += text
|
||||
print(text,end="")
|
||||
return True
|
||||
|
||||
ASCIIColors.green("Warming up ...")
|
||||
lollms_app.safe_generate(context['discussion'], n_predict=1024, callback=callback, placeholder={"discussion":context['discussion']},place_holders_to_sacrifice=["discussion"], debug=True)
|
||||
|
||||
context['discussion'] += context['current_response']
|
||||
await message.channel.send(context['current_response'])
|
||||
elif message.content.startswith('!mount'):
|
||||
personality_name = message.content[len('!mount')+1:]
|
||||
lollms_app.config.personalities.append(personality_name)
|
||||
lollms_app.personality = lollms_app.mount_personality(len(lollms_app.config.personalities)-1)
|
||||
lollms_app.config.active_personality_id = len(lollms_app.config.personalities)-1
|
||||
|
||||
await message.channel.send(f"Personality {personality_name} mounted successfuly")
|
||||
elif message.content.startswith('!list'):
|
||||
await message.channel.send(f"Mounted personalities:\n{[p.name for p in lollms_app.mounted_personalities]}")
|
||||
elif message.content.startswith('!select'):
|
||||
personality_name = message.content[len('!select')+1:]
|
||||
index = 0
|
||||
for i in range(len(lollms_app.mounted_personalities)):
|
||||
if lollms_app.mounted_personalities[i].name.lower().strip()==personality_name.lower():
|
||||
index = i
|
||||
lollms_app.config.active_personality_id = index
|
||||
await message.channel.send(f"Activated personality:\n{personality_name}")
|
||||
elif message.content.startswith('!reset'):
|
||||
context["discussion"]=""
|
||||
await message.channel.send(f"Content reset")
|
||||
|
||||
elif message.content.startswith('!install'):
|
||||
response = "To install lollms, make sure you have installed the currently supported python version (consult the repository to verify what version is currently supported, but as of 10/22/2023, the version is 3.10).\nThen you can follow these steps:\n1. Open your command line interface.\n2. Navigate to the directory where you want to install lollms.\n3. Run the following command to clone the lollms repository: `git clone https://github.com/lollms/lollms.git`.\n4. Once the repository is cloned, navigate into the lollms directory.\n5. Run `pip install -r requirements.txt` to install the required dependencies.\n6. You can now use lollms by importing it in your Python code."
|
||||
await message.channel.send(response)
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
print(f'Logged in as {client.user}')
|
||||
print('------')
|
||||
|
||||
@client.event
|
||||
async def on_member_join(member):
|
||||
channel = member.guild.system_channel
|
||||
if channel is not None:
|
||||
await channel.send(f'Welcome {member.mention} to the server! How can I assist you today?')
|
||||
|
||||
client.run(bot_token)
|
@ -1,533 +0,0 @@
|
||||
from lollms.app import LollmsApplication
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
import sys
|
||||
import time
|
||||
maxtry=10
|
||||
from pathlib import Path
|
||||
import json
|
||||
import re
|
||||
import random
|
||||
from flask import Flask, make_response, request, abort
|
||||
from flask.json import jsonify
|
||||
from typing import Callable
|
||||
import string
|
||||
import argparse
|
||||
from ascii_colors import ASCIIColors
|
||||
BUNDLES=4
|
||||
MAXWORDS=1048
|
||||
DEBUG=True
|
||||
class Elf(LollmsApplication):
|
||||
def __init__(self):
|
||||
pass
|
||||
def init(self, custom_default_cfg_path):
|
||||
lollms_paths = LollmsPaths.find_paths(custom_global_paths_cfg_path=custom_default_cfg_path, tool_prefix="lollms_elf_")
|
||||
config = LOLLMSConfig.autoload(lollms_paths, None)
|
||||
super().__init__("Elf", config, lollms_paths)
|
||||
|
||||
def split_fibers(self,fibers, max_words=MAXWORDS):
|
||||
# Split each fiber into chunks of up to max_words words
|
||||
sfibers = []
|
||||
for fiber in fibers:
|
||||
words = fiber.split()
|
||||
for i in range(0, len(words), max_words):
|
||||
chunk = " ".join(words[i : i + max_words])
|
||||
sfibers.append(chunk)
|
||||
return sfibers
|
||||
|
||||
def refactor_into_fiber_bundles(self, lines, bundle_size):
|
||||
bundles = []
|
||||
temp = []
|
||||
for line in lines:
|
||||
# Split the line into fibers
|
||||
# fibers = line.split('.')
|
||||
fibers = re.split(r"[\.\n]", line)
|
||||
|
||||
# Filter out empty lines or lines with only whitespace
|
||||
fibers = [fiber.strip() for fiber in fibers if re.search(r"\S", fiber)]
|
||||
|
||||
# Add filtered fibers to the current bundle
|
||||
temp.extend(self.split_fibers(fibers))
|
||||
# now lete
|
||||
current_bundle = []
|
||||
# print(temp)
|
||||
for line in temp:
|
||||
current_bundle.append(line)
|
||||
|
||||
# Check if the current bundle size exceeds the desired bundle size
|
||||
if len(current_bundle) >= bundle_size:
|
||||
# Add the current bundle to the list of bundles
|
||||
bundles.append(current_bundle)
|
||||
# Start a new bundle
|
||||
current_bundle = []
|
||||
|
||||
# Add the last bundle if it's not empty
|
||||
if current_bundle:
|
||||
bundles.append(current_bundle)
|
||||
|
||||
return bundles
|
||||
|
||||
|
||||
def read_input_file(self, file_path):
|
||||
with open(file_path, "r") as file:
|
||||
lines = file.readlines()
|
||||
|
||||
lines = self.refactor_into_fiber_bundles(lines, BUNDLES)
|
||||
|
||||
with open("debug.txt", "w") as fo:
|
||||
for line in lines:
|
||||
fo.write("|\n".join(line))
|
||||
for line in lines:
|
||||
self.text_ring_buffer.append(
|
||||
self.personality.user_message_prefix + "\n".join(line)
|
||||
)
|
||||
print("start COUNT",len(self.text_ring_buffer))
|
||||
|
||||
def safe_generate(
|
||||
self,
|
||||
full_discussion:str,
|
||||
n_predict=None,
|
||||
callback: Callable[[str, int, dict], bool]=None,
|
||||
temperature=0.1,
|
||||
top_k=50,
|
||||
top_p=0.9,
|
||||
repeat_penalty=1.3,
|
||||
last_n_tokens=60,
|
||||
seed=-1,
|
||||
n_threads=8,
|
||||
batch_size=1,
|
||||
stream=None):
|
||||
"""safe_generate
|
||||
|
||||
Args:
|
||||
full_discussion (string): A prompt or a long discussion to use for generation
|
||||
callback (_type_, optional): A callback to call for each received token. Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: Model output
|
||||
"""
|
||||
if n_predict == None:
|
||||
n_predict =self.config.n_predict
|
||||
tk = self.personality.model.tokenize(full_discussion)
|
||||
n_tokens = len(tk)
|
||||
fd = self.personality.model.detokenize(tk[-min(self.config.ctx_size-self.n_cond_tk,n_tokens):])
|
||||
self.bot_says = ""
|
||||
output = self.personality.model.generate(
|
||||
fd,
|
||||
n_predict=n_predict,
|
||||
temperature=temperature,
|
||||
top_k=top_k,
|
||||
top_p=top_p,
|
||||
repeat_penalty=repeat_penalty,
|
||||
last_n_tokens=last_n_tokens,
|
||||
seed=seed,
|
||||
n_threads=n_threads,
|
||||
batch_size=batch_size,
|
||||
callback=callback)
|
||||
return output
|
||||
|
||||
def gen_rewrite(self):
|
||||
topic = "Snippet"
|
||||
target= "Protobuf Server"
|
||||
return random.choice(
|
||||
[
|
||||
f"Transform this {topic} into a Python code representation of a {target}.",
|
||||
f"Generate a Python code snippet for a {target} that implements this {topic}.",
|
||||
f"Craft a Python implementation of a {target} that embodies the essence of this {topic}.",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def callback(self, text, type=None, metadata: dict = {}):
|
||||
if DEBUG:
|
||||
print("DBG:" + text, end="")
|
||||
sys.stdout.flush()
|
||||
return True
|
||||
|
||||
# set up the Flask application
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
cv = Elf()
|
||||
# input_file_path = "user_input.txt"
|
||||
# try:
|
||||
# cv.read_input_file(input_file_path)
|
||||
|
||||
# cv.start_conversation2()
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
# raise e
|
||||
models = {}
|
||||
|
||||
|
||||
@app.route("/v1/models", methods=['GET'])
|
||||
def models():
|
||||
data = [
|
||||
{
|
||||
"id": model,
|
||||
"object": "model",
|
||||
"owned_by": "",
|
||||
"tokens": 99999,
|
||||
"fallbacks": None,
|
||||
"endpoints": [
|
||||
"/v1/chat/completions"
|
||||
],
|
||||
"limits": None,
|
||||
"permission": []
|
||||
}
|
||||
for model in cv.binding.list_models()
|
||||
]
|
||||
return {'data': data, 'object': 'list'}
|
||||
|
||||
@app.route("/v1/completions", methods=['POST'])
|
||||
def completions():
|
||||
request_data = request.get_json()
|
||||
model = request_data.get('model', None).replace("neuro-", "")
|
||||
prompt = request_data.get('prompt')
|
||||
temperature = request_data.get('temperature')
|
||||
max_tokens = request_data.get('max_tokens', 1024)
|
||||
|
||||
prompt_tokens = cv.model.tokenize(prompt)
|
||||
n_prompt_tokens = len(prompt_tokens)
|
||||
|
||||
if model is not None:
|
||||
# TODO add model selection
|
||||
pass
|
||||
|
||||
completion_id = "".join(random.choices(string.ascii_letters + string.digits, k=28))
|
||||
completion_timestamp = int(time.time())
|
||||
|
||||
response = cv.safe_generate(full_discussion=prompt, temperature=temperature, n_predict=max_tokens)
|
||||
completion_tokens = cv.model.tokenize(response)
|
||||
n_completion_tokens = len(completion_tokens)
|
||||
|
||||
completion_timestamp = int(time.time())
|
||||
completion_id = ''.join(random.choices(
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', k=28))
|
||||
system_fingerprint = ''.join(random.choices(
|
||||
'abcdefghijklmnopqrstuvwxyz0123456789', k=10))
|
||||
return {
|
||||
"id": f"cmpl-{completion_id}",
|
||||
"object": "text_completion",
|
||||
"created": completion_timestamp,
|
||||
"model": model,
|
||||
"system_fingerprint": "fp_"+system_fingerprint,
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"text": response,
|
||||
"logprobs": None,
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": n_prompt_tokens,
|
||||
"completion_tokens": n_completion_tokens,
|
||||
"total_tokens": n_prompt_tokens + n_completion_tokens,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.route("/chat/completions", methods=['POST'])
|
||||
@app.route("/v1/chat/completions", methods=['POST'])
|
||||
@app.route("/", methods=['POST'])
|
||||
def chat_completions():
|
||||
request_data = request.get_json()
|
||||
model = request_data.get('model', None).replace("neuro-", "")
|
||||
messages = request_data.get('messages')
|
||||
stream = request_data.get('stream', False)
|
||||
streaming_ = request_data.get('stream', False)
|
||||
temperature = request_data.get('temperature', 1.0)
|
||||
top_p = request_data.get('top_p', 1.0)
|
||||
max_tokens = request_data.get('max_tokens', 1024)
|
||||
|
||||
if model is not None:
|
||||
# TODO add model selection
|
||||
pass
|
||||
|
||||
full_discussion = ""
|
||||
for message in messages:
|
||||
if message["role"]:
|
||||
full_discussion += f'{message["role"]}: {message["content"]}\n'
|
||||
else:
|
||||
full_discussion += f'{message["content"]}\n'
|
||||
|
||||
completion_id = "".join(random.choices(string.ascii_letters + string.digits, k=28))
|
||||
completion_timestamp = int(time.time())
|
||||
|
||||
prompt_tokens = cv.model.tokenize(full_discussion)
|
||||
n_prompt_tokens = len(prompt_tokens)
|
||||
|
||||
|
||||
if not streaming_:
|
||||
response = cv.safe_generate(full_discussion=full_discussion, temperature=temperature, top_p=top_p, n_predict=max_tokens)
|
||||
completion_tokens = cv.model.tokenize(response)
|
||||
n_completion_tokens = len(completion_tokens)
|
||||
|
||||
completion_timestamp = int(time.time())
|
||||
completion_id = ''.join(random.choices(
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', k=28))
|
||||
|
||||
return {
|
||||
"id": f"chatcmpl-{completion_id}",
|
||||
"object": "chat.completion",
|
||||
"created": completion_timestamp,
|
||||
"model": model,
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": response,
|
||||
},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"prompt_tokens": n_prompt_tokens,
|
||||
"completion_tokens": n_completion_tokens,
|
||||
"total_tokens": n_prompt_tokens + n_completion_tokens,
|
||||
},
|
||||
}
|
||||
else:
|
||||
print('Streaming ...')
|
||||
if True:
|
||||
def callback(token, data_type):
|
||||
print(token,end="",flush=True)
|
||||
|
||||
return True
|
||||
response = cv.safe_generate(
|
||||
full_discussion=full_discussion,
|
||||
temperature=temperature,
|
||||
top_p=top_p,
|
||||
n_predict=max_tokens,
|
||||
callback=callback
|
||||
|
||||
)
|
||||
def stream():
|
||||
nonlocal response
|
||||
for token in response.split(" "):
|
||||
completion_timestamp = int(time.time())
|
||||
completion_id = ''.join(random.choices(
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', k=28))
|
||||
|
||||
completion_data = {
|
||||
'id': f'chatcmpl-{completion_id}',
|
||||
'object': 'chat.completion.chunk',
|
||||
'created': completion_timestamp,
|
||||
'choices': [
|
||||
{
|
||||
'delta': {
|
||||
'content': token+" "
|
||||
},
|
||||
'index': 0,
|
||||
'finish_reason': None
|
||||
}
|
||||
]
|
||||
}
|
||||
yield 'data: %s\n\n' % json.dumps(completion_data, separators=(',' ':'))
|
||||
time.sleep(0.02)
|
||||
|
||||
return app.response_class(
|
||||
stream(),
|
||||
mimetype='text/event-stream'
|
||||
)
|
||||
else:
|
||||
def stream_callback(token, message_type=None):
|
||||
print(token)
|
||||
completion_timestamp = int(time.time())
|
||||
completion_id = ''.join(random.choices(
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', k=28))
|
||||
|
||||
completion_data = {
|
||||
'id': f'chatcmpl-{completion_id}',
|
||||
'object': 'chat.completion.chunk',
|
||||
'created': completion_timestamp,
|
||||
'choices': [
|
||||
{
|
||||
'delta': {
|
||||
'content': token
|
||||
},
|
||||
'index': 0,
|
||||
'finish_reason': None
|
||||
}
|
||||
]
|
||||
}
|
||||
#yield 'data: %s\n\n' % json.dumps(completion_data, separators=(',' ':'))
|
||||
time.sleep(0.02)
|
||||
return True
|
||||
return app.response_class(
|
||||
cv.safe_generate(
|
||||
full_discussion=full_discussion,
|
||||
temperature=temperature,
|
||||
top_p=top_p,
|
||||
n_predict=max_tokens,
|
||||
callback=lambda x,y:stream_callback(x,y)
|
||||
),
|
||||
mimetype='text/event-stream'
|
||||
)
|
||||
|
||||
# define the engines endpoint
|
||||
@app.route('/v1/engines')
|
||||
@app.route('/v1/models')
|
||||
@app.route("/models", methods=['GET'])
|
||||
def v1_engines():
|
||||
return make_response(jsonify({
|
||||
'data': [{
|
||||
'object': 'engine',
|
||||
'id': id,
|
||||
'ready': True,
|
||||
'owner': 'huggingface',
|
||||
'permissions': None,
|
||||
'created': None
|
||||
} for id in models.keys()]
|
||||
}))
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route('/v1/embeddings', methods=['POST'])
|
||||
@app.route('/embeddings', methods=['POST'])
|
||||
def create_embedding():
|
||||
j_input = request.get_json()
|
||||
#model = embedding_processing()
|
||||
embedding = cv.model.embedding(text_list=j_input['input'])
|
||||
return jsonify(
|
||||
embedding
|
||||
)
|
||||
|
||||
|
||||
@app.route("/v1/dashboard/billing/subscription", methods=['GET'])
|
||||
@app.route("/dashboard/billing/subscription", methods=['GET'])
|
||||
def billing_subscription():
|
||||
return jsonify({
|
||||
"object": "billing_subscription",
|
||||
"has_payment_method": True,
|
||||
"canceled": False,
|
||||
"canceled_at": None,
|
||||
"delinquent": None,
|
||||
"access_until": 2556028800,
|
||||
"soft_limit": 6944500,
|
||||
"hard_limit": 166666666,
|
||||
"system_hard_limit": 166666666,
|
||||
"soft_limit_usd": 416.67,
|
||||
"hard_limit_usd": 9999.99996,
|
||||
"system_hard_limit_usd": 9999.99996,
|
||||
"plan": {
|
||||
"title": "Pay-as-you-go",
|
||||
"id": "payg"
|
||||
},
|
||||
"primary": True,
|
||||
"account_name": "OpenAI",
|
||||
"po_number": None,
|
||||
"billing_email": None,
|
||||
"tax_ids": None,
|
||||
"billing_address": {
|
||||
"city": "New York",
|
||||
"line1": "OpenAI",
|
||||
"country": "US",
|
||||
"postal_code": "NY10031"
|
||||
},
|
||||
"business_address": None
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@app.route("/v1/dashboard/billing/usage", methods=['GET'])
|
||||
@app.route("/dashboard/billing/usage", methods=['GET'])
|
||||
def billing_usage():
|
||||
return jsonify({
|
||||
"object": "list",
|
||||
"daily_costs": [
|
||||
{
|
||||
"timestamp": time.time(),
|
||||
"line_items": [
|
||||
{
|
||||
"name": "GPT-4",
|
||||
"cost": 0.0
|
||||
},
|
||||
{
|
||||
"name": "Chat models",
|
||||
"cost": 1.01
|
||||
},
|
||||
{
|
||||
"name": "InstructGPT",
|
||||
"cost": 0.0
|
||||
},
|
||||
{
|
||||
"name": "Fine-tuning models",
|
||||
"cost": 0.0
|
||||
},
|
||||
{
|
||||
"name": "Embedding models",
|
||||
"cost": 0.0
|
||||
},
|
||||
{
|
||||
"name": "Image models",
|
||||
"cost": 16.0
|
||||
},
|
||||
{
|
||||
"name": "Audio models",
|
||||
"cost": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_usage": 1.01
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@app.route("/v1/providers", methods=['GET'])
|
||||
@app.route("/providers", methods=['GET'])
|
||||
def providers():
|
||||
#todo : implement
|
||||
return jsonify({})
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--host', '-hst', default=None, help='Host name')
|
||||
parser.add_argument('--port', '-prt', default=None, help='Port number')
|
||||
|
||||
|
||||
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')
|
||||
parser.add_argument('--reset_installs', action='store_true', help='Reset all installation status')
|
||||
parser.add_argument('--default_cfg_path', type=str, default=None, help='Reset all installation status')
|
||||
|
||||
ASCIIColors.yellow(" _ _ _ _ _ _ _ ")
|
||||
ASCIIColors.yellow(" _\ \ /\ \ _\ \ _\ \ /\_\/\_\ _ / /\ ")
|
||||
ASCIIColors.yellow(" /\__ \ / \ \ /\__ \ /\__ \ / / / / //\_\ / / \ ")
|
||||
ASCIIColors.yellow(" / /_ \_\ / /\ \ \ / /_ \_\ / /_ \_\ /\ \/ \ \/ / // / /\ \__ ")
|
||||
ASCIIColors.yellow(" / / /\/_/ / / /\ \ \ / / /\/_/ / / /\/_/ / \____\__/ // / /\ \___\ ")
|
||||
ASCIIColors.yellow(" / / / / / / \ \_\ / / / / / / / /\/________/ \ \ \ \/___/ ")
|
||||
ASCIIColors.yellow(" / / / / / / / / // / / / / / / / /\/_// / / \ \ \ ")
|
||||
ASCIIColors.yellow(" / / / ____ / / / / / // / / ____ / / / ____ / / / / / /_ \ \ \ ")
|
||||
ASCIIColors.yellow(" / /_/_/ ___/\ / / /___/ / // /_/_/ ___/\ / /_/_/ ___/\/ / / / / //_/\__/ / / ")
|
||||
ASCIIColors.yellow("/_______/\__\// / /____\/ //_______/\__\//_______/\__\/\/_/ / / / \ \/___/ / ")
|
||||
ASCIIColors.yellow("\_______\/ \/_________/ \_______\/ \_______\/ \/_/ \_____\/ ")
|
||||
ASCIIColors.yellow(" _ _ _ ")
|
||||
ASCIIColors.yellow(" /\ \ _\ \ /\ \ ")
|
||||
ASCIIColors.yellow(" / \ \ /\__ \ / \ \ ")
|
||||
ASCIIColors.yellow(" / /\ \ \ / /_ \_\ / /\ \ \ ")
|
||||
ASCIIColors.yellow(" / / /\ \_\ / / /\/_/ / / /\ \_\ ")
|
||||
ASCIIColors.yellow(" / /_/_ \/_/ / / / / /_/_ \/_/ ")
|
||||
ASCIIColors.yellow(" / /____/\ / / / / /____/\ ")
|
||||
ASCIIColors.yellow(" / /\____\/ / / / ____ / /\____\/ ")
|
||||
ASCIIColors.yellow(" / / /______ / /_/_/ ___/\ / / / ")
|
||||
ASCIIColors.yellow("/ / /_______\/_______/\__\// / / ")
|
||||
ASCIIColors.yellow("\/__________/\_______\/ \/_/ ")
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.reset_personal_path:
|
||||
LollmsPaths.reset_configs()
|
||||
|
||||
cv.init(args.default_cfg_path)
|
||||
app.run(host=args.host, port=args.port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,4 +0,0 @@
|
||||
VITE_LOLLMS_API = http://127.0.0.1:9601 # http://localhost:9600
|
||||
VITE_LOLLMS_API_CHANGE_ORIGIN = 1 # FALSE
|
||||
VITE_LOLLMS_API_SECURE = 0 # FALSE
|
||||
VITE_LOLLMS_API_BASEURL = /api/ # FALSE
|
@ -1,14 +0,0 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
30
lollms/apps/playground/.gitignore
vendored
30
lollms/apps/playground/.gitignore
vendored
@ -1,30 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# REST Client files (VSCODE extension for making GET POST requests easy and fst from text files)
|
||||
*.http
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Saifeddine ALOUI (aka parisneo)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,160 +0,0 @@
|
||||
from flask import Flask, send_from_directory, request, jsonify
|
||||
from lollms.helpers import get_trace_exception
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from pathlib import Path
|
||||
import requests
|
||||
import json
|
||||
import shutil
|
||||
import yaml
|
||||
import os
|
||||
|
||||
|
||||
lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_")
|
||||
config = LOLLMSConfig.autoload(lollms_paths)
|
||||
|
||||
app = Flask(__name__, static_folder='dist/')
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return app.send_static_file('index.html')
|
||||
|
||||
@app.route('/<path:filename>')
|
||||
def serve_file(filename):
|
||||
root_dir = "dist/"
|
||||
return send_from_directory(root_dir, filename)
|
||||
|
||||
|
||||
@app.route("/execute_python_code", methods=["POST"])
|
||||
def execute_python_code():
|
||||
"""Executes Python code and returns the output."""
|
||||
data = request.get_json()
|
||||
code = data["code"]
|
||||
# Import the necessary modules.
|
||||
import io
|
||||
import sys
|
||||
import time
|
||||
|
||||
# Create a Python interpreter.
|
||||
interpreter = io.StringIO()
|
||||
sys.stdout = interpreter
|
||||
|
||||
# Execute the code.
|
||||
start_time = time.time()
|
||||
try:
|
||||
exec(code)
|
||||
# Get the output.
|
||||
output = interpreter.getvalue()
|
||||
except Exception as ex:
|
||||
output = str(ex)+"\n"+get_trace_exception(ex)
|
||||
end_time = time.time()
|
||||
|
||||
return jsonify({"output":output,"execution_time":end_time - start_time})
|
||||
|
||||
|
||||
|
||||
def copy_files(src, dest):
|
||||
for item in os.listdir(src):
|
||||
src_file = os.path.join(src, item)
|
||||
dest_file = os.path.join(dest, item)
|
||||
|
||||
if os.path.isfile(src_file):
|
||||
shutil.copy2(src_file, dest_file)
|
||||
|
||||
@app.route("/get_presets", methods=["GET"])
|
||||
def get_presets():
|
||||
presets_folder = lollms_paths.personal_databases_path/"lollms_playground_presets"
|
||||
if not presets_folder.exists():
|
||||
presets_folder.mkdir(exist_ok=True, parents=True)
|
||||
path = Path(__file__).parent/"presets"
|
||||
copy_files(str(path),presets_folder)
|
||||
presets = []
|
||||
for filename in presets_folder.glob('*.yaml'):
|
||||
print(filename)
|
||||
with open(filename, 'r', encoding='utf-8') as file:
|
||||
preset = yaml.safe_load(file)
|
||||
if preset is not None:
|
||||
presets.append(preset)
|
||||
return jsonify(presets)
|
||||
|
||||
@app.route("/add_preset", methods=["POST"])
|
||||
def add_preset():
|
||||
# Get the JSON data from the POST request.
|
||||
preset_data = request.get_json()
|
||||
presets_folder = lollms_paths.personal_databases_path/"lollms_playground_presets"
|
||||
if not presets_folder.exists():
|
||||
presets_folder.mkdir(exist_ok=True, parents=True)
|
||||
copy_files("presets",presets_folder)
|
||||
fn = preset_data.name.lower().replace(" ","_")
|
||||
filename = presets_folder/f"{fn}.yaml"
|
||||
with open(filename, 'w', encoding='utf-8') as file:
|
||||
yaml.dump(preset_data)
|
||||
return jsonify({"status": True})
|
||||
|
||||
@app.route("/del_preset", methods=["POST"])
|
||||
def del_preset():
|
||||
presets_folder = lollms_paths.personal_databases_path/"lollms_playground_presets"
|
||||
if not presets_folder.exists():
|
||||
presets_folder.mkdir(exist_ok=True, parents=True)
|
||||
copy_files("presets",presets_folder)
|
||||
presets = []
|
||||
for filename in presets_folder.glob('*.yaml'):
|
||||
print(filename)
|
||||
with open(filename, 'r') as file:
|
||||
preset = yaml.safe_load(file)
|
||||
if preset is not None:
|
||||
presets.append(preset)
|
||||
return jsonify(presets)
|
||||
|
||||
@app.route("/save_preset", methods=["POST"])
|
||||
def save_presets():
|
||||
"""Saves a preset to a file.
|
||||
|
||||
Args:
|
||||
None.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
|
||||
# Get the JSON data from the POST request.
|
||||
preset_data = request.get_json()
|
||||
|
||||
presets_file = lollms_paths.personal_databases_path/"presets.json"
|
||||
# Save the JSON data to a file.
|
||||
with open(presets_file, "w") as f:
|
||||
json.dump(preset_data, f, indent=4)
|
||||
|
||||
return jsonify({"status":True,"message":"Preset saved successfully!"})
|
||||
|
||||
@app.route("/list_models", methods=["GET"])
|
||||
def list_models():
|
||||
host = f"http://{config.host}:{config.port}"
|
||||
response = requests.get(f"{host}/list_models")
|
||||
data = response.json()
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@app.route("/get_active_model", methods=["GET"])
|
||||
def get_active_model():
|
||||
host = f"http://{config.host}:{config.port}"
|
||||
response = requests.get(f"{host}/get_active_model")
|
||||
data = response.json()
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@app.route("/update_setting", methods=["POST"])
|
||||
def update_setting():
|
||||
host = f"http://{config.host}:{config.port}"
|
||||
response = requests.post(f"{host}/update_setting", headers={'Content-Type': 'application/json'}, data=request.data)
|
||||
data = response.json()
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
app.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LoLLMS Playground WebUI - Welcome</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
3363
lollms/apps/playground/package-lock.json
generated
3363
lollms/apps/playground/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "lollms-playground-vue",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.3.6",
|
||||
"feather-icons": "^4.29.0",
|
||||
"flowbite": "^1.6.5",
|
||||
"flowbite-vue": "^0.0.10",
|
||||
"highlight.js": "^11.8.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-anchor": "^8.6.7",
|
||||
"markdown-it-attrs": "^4.1.6",
|
||||
"markdown-it-emoji": "^2.0.2",
|
||||
"markdown-it-implicit-figures": "^0.11.0",
|
||||
"markdown-it-multimd-table": "^4.2.2",
|
||||
"papaparse": "^5.4.1",
|
||||
"prismjs": "^1.29.0",
|
||||
"socket.io-client": "^4.6.1",
|
||||
"vue": "^3.2.47",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.2.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.1.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"postcss": "^8.4.23",
|
||||
"prettier": "^2.8.4",
|
||||
"tailwind-scrollbar": "^3.0.1",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"vite": "^4.1.4"
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"preset1" : "Once apon a time",
|
||||
"preset2" : "1+1="
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
@ -1,39 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
'node_modules/flowbite-vue/**/*.{js,jsx,ts,tsx}'
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#0e8ef0',
|
||||
'primary-light': '#3dabff',
|
||||
secondary: '#0fd974',
|
||||
accent: '#f0700e',
|
||||
'bg-dark': '#132e59',
|
||||
'bg-dark-tone': '#25477d',
|
||||
'bg-dark-tone-panel': '#4367a3',
|
||||
'bg-dark-code-block': '#2254a7',
|
||||
'bg-light': '#e2edff',
|
||||
'bg-light-tone': '#b9d2f7',
|
||||
'bg-light-code-block': '#cad7ed',
|
||||
'bg-light-tone-panel': '#8fb5ef',
|
||||
'bg-dark-discussion': '#435E8A',
|
||||
'bg-dark-discussion-odd': '#284471',
|
||||
'bg-light-discussion': '#c5d8f8',
|
||||
'bg-light-discussion-odd': '#d6e7ff'
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['PTSans', 'Roboto', 'sans-serif']
|
||||
},
|
||||
container: {
|
||||
padding: '2rem',
|
||||
center: true
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [require('flowbite/plugin'), require('tailwind-scrollbar')]
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig,loadEnv } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ mode }) => {
|
||||
// Load app-level env vars to node-level env vars.
|
||||
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
|
||||
|
||||
return defineConfig({
|
||||
|
||||
plugins: [
|
||||
vue()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
serveStatic: {
|
||||
// This will force Vite to serve the YAML file.
|
||||
paths: ['./presets.json'],
|
||||
},
|
||||
proxy: {
|
||||
"/api/": {
|
||||
target: 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/, ""),
|
||||
},
|
||||
// "/": {
|
||||
// target: process.env.VITE_LOLLMS_API,
|
||||
// changeOrigin: process.env.VITE_LOLLMS_API_CHANGE_ORIGIN,
|
||||
// secure: process.env.VITE_LOLLMS_API_SECURE,
|
||||
|
||||
// },
|
||||
|
||||
},
|
||||
}
|
||||
})}
|
@ -1,823 +0,0 @@
|
||||
from lollms.config import InstallOption
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_socketio import SocketIO, emit
|
||||
from flask_cors import CORS
|
||||
from lollms.types import MSG_TYPE
|
||||
from lollms.personality import AIPersonality
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from lollms.binding import LLMBinding, BindingBuilder, ModelBuilder
|
||||
from lollms.personality import PersonalityBuilder
|
||||
from lollms.apps.console import MainMenu
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.apps.console import MainMenu
|
||||
from lollms.app import LollmsApplication
|
||||
from safe_store import TextVectorizer
|
||||
from ascii_colors import ASCIIColors, trace_exception
|
||||
from typing import List, Tuple
|
||||
from typing import Callable
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import logging
|
||||
import yaml
|
||||
import copy
|
||||
import gc
|
||||
import json
|
||||
import traceback
|
||||
import shutil
|
||||
import psutil
|
||||
import subprocess
|
||||
import pkg_resources
|
||||
|
||||
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 LoLLMsServer(LollmsApplication):
|
||||
def __init__(self):
|
||||
|
||||
self.clients = {}
|
||||
self.current_binding = None
|
||||
self.model = None
|
||||
self.personalities = []
|
||||
self.answer = ['']
|
||||
self.busy = False
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--host', '-hst', default=None, help='Host name')
|
||||
parser.add_argument('--port', '-prt', default=None, help='Port number')
|
||||
|
||||
|
||||
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')
|
||||
parser.add_argument('--reset_installs', action='store_true', help='Reset all installation status')
|
||||
parser.add_argument('--default_cfg_path', type=str, default=None, help='Reset all installation status')
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.reset_installs:
|
||||
self.reset_all_installs()
|
||||
|
||||
if args.reset_personal_path:
|
||||
LollmsPaths.reset_configs()
|
||||
|
||||
if args.reset_config:
|
||||
lollms_paths = LollmsPaths.find_paths(custom_default_cfg_path=args.default_cfg_path, tool_prefix="lollms_server_")
|
||||
cfg_path = lollms_paths.personal_configuration_path / f"{lollms_paths.tool_prefix}local_config.yaml"
|
||||
try:
|
||||
cfg_path.unlink()
|
||||
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
|
||||
config = LOLLMSConfig.autoload(lollms_paths, None)
|
||||
|
||||
if args.host is not None:
|
||||
config.host = args.host
|
||||
|
||||
if args.port is not None:
|
||||
config.port = args.port
|
||||
|
||||
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
|
||||
|
||||
def get_config():
|
||||
ASCIIColors.yellow("Requested configuration")
|
||||
return jsonify(self.config.to_dict())
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/get_config", "get_config", get_config, methods=["GET"]
|
||||
)
|
||||
|
||||
def list_models():
|
||||
if self.binding is not None:
|
||||
ASCIIColors.yellow("Listing models")
|
||||
models = self.binding.list_models()
|
||||
ASCIIColors.green("ok")
|
||||
return jsonify(models)
|
||||
else:
|
||||
return jsonify([])
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/list_models", "list_models", list_models, methods=["GET"]
|
||||
)
|
||||
|
||||
def get_active_model():
|
||||
if self.binding is not None:
|
||||
models = self.binding.list_models()
|
||||
index = models.index(self.config.model_name)
|
||||
ASCIIColors.yellow(f"Recovering active model: {models[index]}")
|
||||
return jsonify({"model":models[index],"index":index})
|
||||
else:
|
||||
return jsonify(None)
|
||||
|
||||
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/get_active_model", "get_active_model", get_active_model, methods=["GET"]
|
||||
)
|
||||
|
||||
|
||||
def get_server_ifos(self):
|
||||
"""
|
||||
Returns information about the server.
|
||||
"""
|
||||
server_infos = {}
|
||||
if self.binding is not None:
|
||||
models = self.binding.list_models()
|
||||
index = models.index(self.config.model_name)
|
||||
ASCIIColors.yellow(f"Recovering active model: {models[index]}")
|
||||
server_infos["binding"]=self.binding.name
|
||||
server_infos["models_list"]=models
|
||||
server_infos["model"]=models[index]
|
||||
server_infos["model_index"]=index
|
||||
else:
|
||||
server_infos["models_list"]=[]
|
||||
server_infos["model"]=""
|
||||
server_infos["model_index"]=-1
|
||||
|
||||
ram = psutil.virtual_memory()
|
||||
server_infos["lollms_version"]= pkg_resources.get_distribution('lollms').version
|
||||
server_infos["total_space"]=ram.total
|
||||
server_infos["available_space"]=ram.free
|
||||
|
||||
server_infos["percent_usage"]=ram.percent
|
||||
server_infos["ram_usage"]=ram.used
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(['nvidia-smi', '--query-gpu=memory.total,memory.used,gpu_name', '--format=csv,nounits,noheader'])
|
||||
lines = output.decode().strip().split('\n')
|
||||
vram_info = [line.split(',') for line in lines]
|
||||
server_infos["nb_gpus"]= len(vram_info)
|
||||
if vram_info is not None:
|
||||
for i, gpu in enumerate(vram_info):
|
||||
server_infos[f"gpu_{i}_total_vram"] = int(gpu[0])*1024*1024
|
||||
server_infos[f"gpu_{i}_used_vram"] = int(gpu[1])*1024*1024
|
||||
server_infos[f"gpu_{i}_model"] = gpu[2].strip()
|
||||
else:
|
||||
# Set all VRAM-related entries to None
|
||||
server_infos["gpu_0_total_vram"] = None
|
||||
server_infos["gpu_0_used_vram"] = None
|
||||
server_infos["gpu_0_model"] = None
|
||||
|
||||
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
server_infos["nb_gpus"]= 0
|
||||
server_infos["gpu_0_total_vram"] = None
|
||||
server_infos["gpu_0_used_vram"] = None
|
||||
server_infos["gpu_0_model"] = None
|
||||
|
||||
current_drive = Path.cwd().anchor
|
||||
drive_disk_usage = psutil.disk_usage(current_drive)
|
||||
server_infos["system_disk_total_space"]=drive_disk_usage.total
|
||||
server_infos["system_disk_available_space"]=drive_disk_usage.free
|
||||
try:
|
||||
models_folder_disk_usage = psutil.disk_usage(str(self.lollms_paths.binding_models_paths[0]))
|
||||
server_infos["binding_disk_total_space"]=models_folder_disk_usage.total
|
||||
server_infos["binding_disk_available_space"]=models_folder_disk_usage.free
|
||||
|
||||
except Exception as ex:
|
||||
server_infos["binding_disk_total_space"]=None
|
||||
server_infos["binding_disk_available_space"]=None
|
||||
|
||||
return jsonify(server_infos)
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/get_server_ifos", "get_server_ifos", get_server_ifos, methods=["GET"]
|
||||
)
|
||||
|
||||
def update_setting():
|
||||
data = request.get_json()
|
||||
setting_name = data['setting_name']
|
||||
|
||||
if setting_name== "model_name":
|
||||
self.config["model_name"]=data['setting_value']
|
||||
if self.config["model_name"] is not None:
|
||||
try:
|
||||
self.model = None
|
||||
if self.binding:
|
||||
del self.binding
|
||||
|
||||
self.binding = None
|
||||
for per in self.mounted_personalities:
|
||||
if per is not None:
|
||||
per.model = None
|
||||
gc.collect()
|
||||
self.binding = BindingBuilder().build_binding(self.config, self.lollms_paths, lollmsCom=self)
|
||||
self.model = self.binding.build_model()
|
||||
for per in self.mounted_personalities:
|
||||
if per is not None:
|
||||
per.model = self.model
|
||||
except Exception as 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(f"Couldn't load model: [{ex}]")
|
||||
ASCIIColors.error(traceback_text)
|
||||
return jsonify({ "status":False, 'error':str(ex)})
|
||||
else:
|
||||
ASCIIColors.warning("Trying to set a None model. Please select a model for the binding")
|
||||
print("update_settings : New model selected")
|
||||
|
||||
return jsonify({'setting_name': data['setting_name'], "status":True})
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/update_setting", "update_setting", update_setting, methods=["POST"]
|
||||
)
|
||||
|
||||
|
||||
|
||||
def list_mounted_personalities():
|
||||
if self.binding is not None:
|
||||
ASCIIColors.yellow("Listing mounted personalities")
|
||||
return jsonify(self.config.personalities)
|
||||
else:
|
||||
return jsonify([])
|
||||
|
||||
self.app.add_url_rule(
|
||||
"/list_mounted_personalities", "list_mounted_personalities", list_mounted_personalities, methods=["GET"]
|
||||
)
|
||||
|
||||
|
||||
self.sio = SocketIO(self.app, cors_allowed_origins='*', ping_timeout=1200, ping_interval=4000)
|
||||
|
||||
# Set log level to warning
|
||||
self.app.logger.setLevel(logging.WARNING)
|
||||
# Configure a custom logger for Flask-SocketIO
|
||||
self.sio_log = logging.getLogger('socketio')
|
||||
self.sio_log.setLevel(logging.WARNING)
|
||||
self.sio_log.addHandler(logging.StreamHandler())
|
||||
|
||||
self.initialize_routes()
|
||||
self.run(self.config.host, self.config.port)
|
||||
|
||||
|
||||
def initialize_routes(self):
|
||||
@self.sio.on('connect')
|
||||
def handle_connect():
|
||||
client_id = request.sid
|
||||
self.clients[client_id] = {
|
||||
"namespace": request.namespace,
|
||||
"full_discussion_blocks": [],
|
||||
"is_generating":False,
|
||||
"requested_stop":False
|
||||
}
|
||||
ASCIIColors.success(f'Client connected with session ID: {client_id}')
|
||||
|
||||
@self.sio.on('disconnect')
|
||||
def handle_disconnect():
|
||||
client_id = request.sid
|
||||
if client_id in self.clients:
|
||||
del self.clients[client_id]
|
||||
print(f'Client disconnected with session ID: {client_id}')
|
||||
|
||||
|
||||
|
||||
@self.sio.on('list_available_bindings')
|
||||
def handle_list_bindings():
|
||||
binding_infs = []
|
||||
for p in self.bindings_path.iterdir():
|
||||
if p.is_dir():
|
||||
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)
|
||||
entry={
|
||||
"name":p.name,
|
||||
"card":card,
|
||||
"models":models
|
||||
}
|
||||
binding_infs.append(entry)
|
||||
|
||||
emit('bindings_list', {'success':True, 'bindings': binding_infs}, room=request.sid)
|
||||
|
||||
@self.sio.on('list_available_personalities')
|
||||
def handle_list_available_personalities():
|
||||
personalities_folder = self.personalities_path
|
||||
personalities = {}
|
||||
for language_folder in personalities_folder.iterdir():
|
||||
if language_folder.is_dir():
|
||||
personalities[language_folder.name] = {}
|
||||
for category_folder in language_folder.iterdir():
|
||||
if category_folder.is_dir():
|
||||
personalities[language_folder.name][category_folder.name] = []
|
||||
for personality_folder in category_folder.iterdir():
|
||||
if personality_folder.is_dir():
|
||||
try:
|
||||
personality_info = {"folder":personality_folder.stem}
|
||||
config_path = personality_folder / 'config.yaml'
|
||||
with open(config_path) as config_file:
|
||||
config_data = yaml.load(config_file, Loader=yaml.FullLoader)
|
||||
personality_info['name'] = config_data.get('name',"No Name")
|
||||
personality_info['description'] = config_data.get('personality_description',"")
|
||||
personality_info['author'] = config_data.get('author', 'ParisNeo')
|
||||
personality_info['version'] = config_data.get('version', '1.0.0')
|
||||
scripts_path = personality_folder / 'scripts'
|
||||
personality_info['has_scripts'] = scripts_path.is_dir()
|
||||
assets_path = personality_folder / 'assets'
|
||||
gif_logo_path = assets_path / 'logo.gif'
|
||||
webp_logo_path = assets_path / 'logo.webp'
|
||||
png_logo_path = assets_path / 'logo.png'
|
||||
jpg_logo_path = assets_path / 'logo.jpg'
|
||||
jpeg_logo_path = assets_path / 'logo.jpeg'
|
||||
bmp_logo_path = assets_path / 'logo.bmp'
|
||||
|
||||
personality_info['has_logo'] = png_logo_path.is_file() or gif_logo_path.is_file()
|
||||
|
||||
if gif_logo_path.exists():
|
||||
personality_info['avatar'] = str(gif_logo_path).replace("\\","/")
|
||||
elif webp_logo_path.exists():
|
||||
personality_info['avatar'] = str(webp_logo_path).replace("\\","/")
|
||||
elif png_logo_path.exists():
|
||||
personality_info['avatar'] = str(png_logo_path).replace("\\","/")
|
||||
elif jpg_logo_path.exists():
|
||||
personality_info['avatar'] = str(jpg_logo_path).replace("\\","/")
|
||||
elif jpeg_logo_path.exists():
|
||||
personality_info['avatar'] = str(jpeg_logo_path).replace("\\","/")
|
||||
elif bmp_logo_path.exists():
|
||||
personality_info['avatar'] = str(bmp_logo_path).replace("\\","/")
|
||||
else:
|
||||
personality_info['avatar'] = ""
|
||||
personalities[language_folder.name][category_folder.name].append(personality_info)
|
||||
except Exception as ex:
|
||||
print(f"Couldn't load personality from {personality_folder} [{ex}]")
|
||||
emit('personalities_list', {'personalities': personalities}, room=request.sid)
|
||||
|
||||
@self.sio.on('list_available_models')
|
||||
def handle_list_available_models():
|
||||
"""List the available models
|
||||
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
if self.binding is None:
|
||||
emit('available_models_list', {'success':False, 'error': "No binding selected"}, room=request.sid)
|
||||
model_list = self.binding.get_available_models(self)
|
||||
|
||||
models = []
|
||||
for model in model_list:
|
||||
try:
|
||||
filename = model.get('filename',"")
|
||||
server = model.get('server',"")
|
||||
image_url = model.get("icon", '/images/default_model.png')
|
||||
license = model.get("license", 'unknown')
|
||||
owner = model.get("owner", 'unknown')
|
||||
owner_link = model.get("owner_link", 'https://github.com/ParisNeo')
|
||||
filesize = int(model.get('filesize',0))
|
||||
description = model.get('description',"")
|
||||
model_type = model.get("model_type","")
|
||||
if server.endswith("/"):
|
||||
path = f'{server}{filename}'
|
||||
else:
|
||||
path = f'{server}/{filename}'
|
||||
local_path = self.models_path/f'{self.config["binding_name"]}/{filename}'
|
||||
is_installed = local_path.exists() or model_type.lower()=="api"
|
||||
models.append({
|
||||
'title': filename,
|
||||
'icon': image_url, # Replace with the path to the model icon
|
||||
'license': license,
|
||||
'owner': owner,
|
||||
'owner_link': owner_link,
|
||||
'description': description,
|
||||
'isInstalled': is_installed,
|
||||
'path': path,
|
||||
'filesize': filesize,
|
||||
'model_type': model_type
|
||||
})
|
||||
except Exception as ex:
|
||||
print("#################################")
|
||||
print(ex)
|
||||
print("#################################")
|
||||
print(f"Problem with model : {model}")
|
||||
emit('available_models_list', {'success':True, 'available_models': models}, room=request.sid)
|
||||
|
||||
|
||||
@self.sio.on('list_available_personalities_categories')
|
||||
def handle_list_available_personalities_categories(data):
|
||||
try:
|
||||
categories = [l for l in (self.personalities_path).iterdir()]
|
||||
emit('available_personalities_categories_list', {'success': True, 'available_personalities_categories': categories})
|
||||
except Exception as ex:
|
||||
emit('available_personalities_categories_list', {'success': False, 'error':str(ex)})
|
||||
|
||||
@self.sio.on('list_available_personalities_names')
|
||||
def handle_list_available_personalities_names(data):
|
||||
try:
|
||||
category = data["category"]
|
||||
personalities = [l for l in (self.personalities_path/category).iterdir()]
|
||||
emit('list_available_personalities_names_list', {'success': True, 'list_available_personalities_names': personalities})
|
||||
except Exception as ex:
|
||||
emit('list_available_personalities_names_list', {'success': False, 'error':str(ex)})
|
||||
|
||||
@self.sio.on('select_binding')
|
||||
def handle_select_binding(data):
|
||||
self.cp_config = copy.deepcopy(self.config)
|
||||
self.cp_config["binding_name"] = data['binding_name']
|
||||
try:
|
||||
del self.model
|
||||
del self.binding
|
||||
self.model = None
|
||||
self.binding = None
|
||||
gc.collect()
|
||||
for personality in self.mount_personalities:
|
||||
personality.model = None
|
||||
self.binding = self.build_binding(self.bindings_path, self.cp_config)
|
||||
self.config = self.cp_config
|
||||
self.mount_personalities()
|
||||
gc.collect()
|
||||
emit('select_binding', {'success':True, 'binding_name': self.cp_config["binding_name"]}, room=request.sid)
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
emit('select_binding', {'success':False, 'binding_name': self.cp_config["binding_name"], 'error':f"Couldn't load binding:\n{ex}"}, room=request.sid)
|
||||
|
||||
@self.sio.on('select_model')
|
||||
def handle_select_model(data):
|
||||
model_name = data['model_name']
|
||||
if self.binding is None:
|
||||
emit('select_model', {'success':False, 'model_name': model_name, 'error':f"Please select a binding first"}, room=request.sid)
|
||||
return
|
||||
self.cp_config = copy.deepcopy(self.config)
|
||||
self.cp_config["model_name"] = data['model_name']
|
||||
try:
|
||||
del self.model
|
||||
self.model = None
|
||||
gc.collect()
|
||||
for personality in self.mount_personalities:
|
||||
personality.model = None
|
||||
self.model = self.binding.build_model()
|
||||
self.mount_personalities()
|
||||
gc.collect()
|
||||
emit('select_model', {'success':True, 'model_name': model_name}, room=request.sid)
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
emit('select_model', {'success':False, 'model_name': model_name, 'error':f"Please select a binding first"}, room=request.sid)
|
||||
|
||||
@self.sio.on('add_personality')
|
||||
def handle_add_personality(data):
|
||||
personality_path = data['path']
|
||||
try:
|
||||
personality = AIPersonality(
|
||||
personality_path,
|
||||
self.lollms_paths,
|
||||
self.config,
|
||||
self.model,
|
||||
self
|
||||
)
|
||||
self.personalities.append(personality)
|
||||
self.config["personalities"].append(personality_path)
|
||||
emit('personality_added', {'success':True, 'name': personality.name, 'id':len(self.personalities)-1}, room=request.sid)
|
||||
self.config.save_config()
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
emit('personality_add_failed', {'success':False, 'error': error_message}, room=request.sid)
|
||||
|
||||
|
||||
|
||||
@self.sio.on('vectorize_text')
|
||||
def vectorize_text(parameters:dict):
|
||||
"""Vectorizes text
|
||||
|
||||
Args:
|
||||
parameters (dict): contains
|
||||
'chunk_size': the maximum size of a text chunk (512 by default)
|
||||
'vectorization_method': can be either "model_embedding" or "ftidf_vectorizer" (default is "ftidf_vectorizer")
|
||||
'payloads': a list of dicts. each entry has the following format
|
||||
{
|
||||
"path": the path to the document
|
||||
"text": the text of the document
|
||||
},
|
||||
'return_database': If true the vectorized database will be sent to the client (default is True)
|
||||
'database_path': the path to store the database (default is none)
|
||||
|
||||
returns a dict
|
||||
status: True if success and false if not
|
||||
if you asked for the database to be sent back you will ahve those fields too:
|
||||
embeddings: a dictionary containing the text chunks with their ids and embeddings
|
||||
"texts": a dictionary of text chunks for each embedding (use index for correspondance)
|
||||
"infos": extra information
|
||||
"vectorizer": The vectorize if this is using tfidf or none if it uses model
|
||||
|
||||
"""
|
||||
vectorization_method = parameters.get('vectorization_method',"ftidf_vectorizer")
|
||||
chunk_size = parameters.get("chunk_size",512)
|
||||
payloads = parameters["payloads"]
|
||||
database_path = parameters.get("database_path",None)
|
||||
return_database = parameters.get("return_database",True)
|
||||
if database_path is None and return_database is None:
|
||||
ASCIIColors.warning("Vectorization should either ask to save the database or to recover it. You didn't ask for any one!")
|
||||
emit('vectorized_db',{"status":False, "error":"Vectorization should either ask to save the database or to recover it. You didn't ask for any one!"})
|
||||
return
|
||||
tv = TextVectorizer(vectorization_method, self.model)
|
||||
for payload in payloads:
|
||||
tv.add_document(payload["path"],payload["text"],chunk_size=chunk_size)
|
||||
json_db = tv.toJson()
|
||||
if return_database:
|
||||
emit('vectorized_db',{**{"status":True}, **json_db})
|
||||
else:
|
||||
emit('vectorized_db',{"status":True})
|
||||
with open(database_path, "w") as file:
|
||||
json.dump(json_db, file, indent=4)
|
||||
|
||||
|
||||
@self.sio.on('query_database')
|
||||
def query_database(parameters:dict):
|
||||
"""queries a database
|
||||
|
||||
Args:
|
||||
parameters (dict): contains
|
||||
'vectorization_method': can be either "model_embedding" or "ftidf_vectorizer"
|
||||
'database': a list of dicts. each entry has the following format
|
||||
{
|
||||
embeddings: a dictionary containing the text chunks with their ids and embeddings
|
||||
"texts": a dictionary of text chunks for each embedding (use index for correspondance)
|
||||
"infos": extra information
|
||||
"vectorizer": The vectorize if this is using tfidf or none if it uses model
|
||||
}
|
||||
'database_path': If supplied, the database is loaded from a path
|
||||
'query': a query to search in the database
|
||||
"""
|
||||
vectorization_method = parameters['vectorization_method']
|
||||
database = parameters.get("database",None)
|
||||
query = parameters.get("query",None)
|
||||
if query is None:
|
||||
ASCIIColors.error("No query given!")
|
||||
emit('vector_db_query',{"status":False, "error":"Please supply a query"})
|
||||
return
|
||||
|
||||
if database is None:
|
||||
database_path = parameters.get("database_path",None)
|
||||
if database_path is None:
|
||||
ASCIIColors.error("No database given!")
|
||||
emit('vector_db_query',{"status":False, "error":"You did not supply a database file nor a database content"})
|
||||
return
|
||||
else:
|
||||
with open(database_path, "r") as file:
|
||||
database = json.load(file)
|
||||
|
||||
tv = TextVectorizer(vectorization_method, self.model, database_dict=database)
|
||||
docs, sorted_similarities = tv.recover_text(tv.embed_query(query))
|
||||
emit('vectorized_db',{
|
||||
"chunks":docs,
|
||||
"refs":sorted_similarities
|
||||
})
|
||||
|
||||
|
||||
@self.sio.on('list_active_personalities')
|
||||
def handle_list_active_personalities():
|
||||
personality_names = [p.name for p in self.personalities]
|
||||
emit('active_personalities_list', {'success':True, 'personalities': personality_names}, room=request.sid)
|
||||
|
||||
@self.sio.on('activate_personality')
|
||||
def handle_activate_personality(data):
|
||||
personality_id = data['id']
|
||||
if personality_id<len(self.personalities):
|
||||
self.active_personality=self.personalities[personality_id]
|
||||
emit('activate_personality', {'success':True, 'name': self.active_personality, 'id':len(self.personalities)-1}, room=request.sid)
|
||||
self.config["active_personality_id"]=personality_id
|
||||
self.config.save_config()
|
||||
else:
|
||||
emit('personality_add_failed', {'success':False, 'error': "Personality ID not valid"}, room=request.sid)
|
||||
|
||||
@self.sio.on('tokenize')
|
||||
def tokenize(data):
|
||||
client_id = request.sid
|
||||
prompt = data['prompt']
|
||||
tk = self.model.tokenize(prompt)
|
||||
emit("tokenized", {"tokens":tk}, room=client_id)
|
||||
|
||||
@self.sio.on('detokenize')
|
||||
def detokenize(data):
|
||||
client_id = request.sid
|
||||
prompt = data['prompt']
|
||||
txt = self.model.detokenize(prompt)
|
||||
emit("detokenized", {"text":txt}, room=client_id)
|
||||
|
||||
@self.sio.on('embed')
|
||||
def detokenize(data):
|
||||
client_id = request.sid
|
||||
prompt = data['prompt']
|
||||
txt = self.model.embed(prompt)
|
||||
self.sio.emit("embeded", {"text":txt}, room=client_id)
|
||||
|
||||
@self.sio.on('cancel_text_generation')
|
||||
def cancel_text_generation(data):
|
||||
client_id = request.sid
|
||||
self.clients[client_id]["requested_stop"]=True
|
||||
print(f"Client {client_id} requested canceling generation")
|
||||
self.sio.emit("generation_canceled", {"message":"Generation is canceled."}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
self.busy = False
|
||||
|
||||
|
||||
# A copy of the original lollms-server generation code needed for playground
|
||||
@self.sio.on('generate_text')
|
||||
def handle_generate_text(data):
|
||||
client_id = request.sid
|
||||
ASCIIColors.info(f"Text generation requested by client: {client_id}")
|
||||
if self.busy:
|
||||
self.sio.emit("busy", {"message":"I am busy. Come back later."}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
ASCIIColors.warning(f"OOps request {client_id} refused!! Server busy")
|
||||
return
|
||||
def generate_text():
|
||||
self.busy = True
|
||||
try:
|
||||
model = self.model
|
||||
self.clients[client_id]["is_generating"]=True
|
||||
self.clients[client_id]["requested_stop"]=False
|
||||
prompt = data['prompt']
|
||||
tokenized = model.tokenize(prompt)
|
||||
personality_id = int(data.get('personality', -1))
|
||||
|
||||
n_crop = int(data.get('n_crop', len(tokenized)))
|
||||
if n_crop!=-1:
|
||||
prompt = model.detokenize(tokenized[-n_crop:])
|
||||
|
||||
n_predicts = int(data.get("n_predicts",1024))
|
||||
parameters = data.get("parameters",{
|
||||
"temperature":data.get("temperature",self.config["temperature"]),
|
||||
"top_k":data.get("top_k",self.config["top_k"]),
|
||||
"top_p":data.get("top_p",self.config["top_p"]),
|
||||
"repeat_penalty":data.get("repeat_penalty",self.config["repeat_penalty"]),
|
||||
"repeat_last_n":data.get("repeat_last_n",self.config["repeat_last_n"]),
|
||||
"seed":data.get("seed",self.config["seed"])
|
||||
})
|
||||
|
||||
if personality_id==-1:
|
||||
# Raw text generation
|
||||
self.answer = {"full_text":""}
|
||||
def callback(text, message_type: MSG_TYPE, metadata:dict={}):
|
||||
if message_type == MSG_TYPE.MSG_TYPE_CHUNK:
|
||||
ASCIIColors.success(f"generated:{len(self.answer['full_text'].split())} words", end='\r')
|
||||
self.answer["full_text"] = self.answer["full_text"] + text
|
||||
self.sio.emit('text_chunk', {'chunk': text, 'type':MSG_TYPE.MSG_TYPE_CHUNK.value}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
if client_id in self.clients:# Client disconnected
|
||||
if self.clients[client_id]["requested_stop"]:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
tk = model.tokenize(prompt)
|
||||
n_tokens = len(tk)
|
||||
fd = model.detokenize(tk[-min(self.config.ctx_size-n_predicts,n_tokens):])
|
||||
|
||||
try:
|
||||
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"],
|
||||
repeat_penalty = parameters["repeat_penalty"],
|
||||
repeat_last_n = parameters["repeat_last_n"],
|
||||
seed = parameters["seed"]
|
||||
)
|
||||
ASCIIColors.success(f"\ndone")
|
||||
if client_id in self.clients:
|
||||
if not self.clients[client_id]["requested_stop"]:
|
||||
# Emit the generated text to the client
|
||||
self.sio.emit('text_generated', {'text': generated_text}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
except Exception as ex:
|
||||
self.sio.emit('generation_error', {'error': str(ex)}, room=client_id)
|
||||
ASCIIColors.error(f"\ndone")
|
||||
self.busy = False
|
||||
else:
|
||||
try:
|
||||
personality: AIPersonality = self.personalities[personality_id]
|
||||
ump = self.config.discussion_prompt_separator +self.config.user_name+": " if self.config.use_user_name_in_discussions else self.personality.user_message_prefix
|
||||
personality.model = model
|
||||
cond_tk = personality.model.tokenize(personality.personality_conditioning)
|
||||
n_cond_tk = len(cond_tk)
|
||||
# Placeholder code for text generation
|
||||
# Replace this with your actual text generation logic
|
||||
print(f"Text generation requested by client: {client_id}")
|
||||
|
||||
self.answer["full_text"] = ''
|
||||
full_discussion_blocks = self.clients[client_id]["full_discussion_blocks"]
|
||||
|
||||
if prompt != '':
|
||||
if personality.processor is not None and personality.processor_cfg["process_model_input"]:
|
||||
preprocessed_prompt = personality.processor.process_model_input(prompt)
|
||||
else:
|
||||
preprocessed_prompt = prompt
|
||||
|
||||
if personality.processor is not None and personality.processor_cfg["custom_workflow"]:
|
||||
full_discussion_blocks.append(ump)
|
||||
full_discussion_blocks.append(preprocessed_prompt)
|
||||
|
||||
else:
|
||||
|
||||
full_discussion_blocks.append(ump)
|
||||
full_discussion_blocks.append(preprocessed_prompt)
|
||||
full_discussion_blocks.append(personality.link_text)
|
||||
full_discussion_blocks.append(personality.ai_message_prefix)
|
||||
|
||||
full_discussion = personality.personality_conditioning + ''.join(full_discussion_blocks)
|
||||
|
||||
def callback(text, message_type: MSG_TYPE, metadata:dict={}):
|
||||
if message_type == MSG_TYPE.MSG_TYPE_CHUNK:
|
||||
self.answer["full_text"] = self.answer["full_text"] + text
|
||||
self.sio.emit('text_chunk', {'chunk': text}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
try:
|
||||
if self.clients[client_id]["requested_stop"]:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
except: # If the client is disconnected then we stop talking to it
|
||||
return False
|
||||
|
||||
tk = personality.model.tokenize(full_discussion)
|
||||
n_tokens = len(tk)
|
||||
fd = personality.model.detokenize(tk[-min(self.config.ctx_size-n_cond_tk-personality.model_n_predicts,n_tokens):])
|
||||
|
||||
if personality.processor is not None and personality.processor_cfg["custom_workflow"]:
|
||||
ASCIIColors.info("processing...")
|
||||
generated_text = personality.processor.run_workflow(prompt, previous_discussion_text=personality.personality_conditioning+fd, callback=callback)
|
||||
else:
|
||||
ASCIIColors.info("generating...")
|
||||
generated_text = personality.model.generate(
|
||||
personality.personality_conditioning+fd,
|
||||
n_predict=personality.model_n_predicts,
|
||||
callback=callback)
|
||||
|
||||
if personality.processor is not None and personality.processor_cfg["process_model_output"]:
|
||||
generated_text = personality.processor.process_model_output(generated_text)
|
||||
|
||||
full_discussion_blocks.append(generated_text.strip())
|
||||
ASCIIColors.success("\ndone")
|
||||
|
||||
# Emit the generated text to the client
|
||||
self.sio.emit('text_generated', {'text': generated_text}, room=client_id)
|
||||
self.sio.sleep(0)
|
||||
except Exception as ex:
|
||||
self.sio.emit('generation_error', {'error': str(ex)}, room=client_id)
|
||||
ASCIIColors.error(f"\ndone")
|
||||
self.busy = False
|
||||
except Exception as ex:
|
||||
trace_exception(ex)
|
||||
self.sio.emit('generation_error', {'error': str(ex)}, room=client_id)
|
||||
self.busy = False
|
||||
|
||||
# Start the text generation task in a separate thread
|
||||
task = self.sio.start_background_task(target=generate_text)
|
||||
|
||||
def build_binding(self, bindings_path: Path, cfg: LOLLMSConfig)->LLMBinding:
|
||||
binding_path = Path(bindings_path) / cfg["binding_name"]
|
||||
# define the full absolute path to the module
|
||||
absolute_path = binding_path.resolve()
|
||||
# infer the module name from the file path
|
||||
module_name = binding_path.stem
|
||||
# use importlib to load the module from the file path
|
||||
loader = importlib.machinery.SourceFileLoader(module_name, str(absolute_path / "__init__.py"))
|
||||
binding_module = loader.load_module()
|
||||
binding = getattr(binding_module, binding_module.binding_name)
|
||||
return binding
|
||||
|
||||
|
||||
def run(self, host="localhost", port="9601"):
|
||||
if self.binding is None:
|
||||
ASCIIColors.warning("No binding selected. Please select one")
|
||||
self.menu.select_binding()
|
||||
|
||||
print(f"{ASCIIColors.color_red}Current binding (model) : {ASCIIColors.color_reset}{self.binding}")
|
||||
print(f"{ASCIIColors.color_red}Mounted personalities : {ASCIIColors.color_reset}{self.config.personalities}")
|
||||
if len(self.config.personalities)==0:
|
||||
ASCIIColors.warning("No personality selected. Selecting lollms. You can mount other personalities using lollms-settings application")
|
||||
self.config.personalities = ["generic/lollms"]
|
||||
self.config.save_config()
|
||||
|
||||
if self.config.active_personality_id>=len(self.config.personalities):
|
||||
self.config.active_personality_id = 0
|
||||
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.sio.run(self.app, host=host, port=port)
|
||||
|
||||
def main():
|
||||
LoLLMsServer()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,73 +0,0 @@
|
||||
# lollms-settings
|
||||
|
||||
## Description
|
||||
The `lollms-settings` tool is used to configure multiple aspects of the lollms project. Lollms is a multi-bindings LLM service that serves multiple LLM models that can generate text out of a prompt.
|
||||
|
||||
## Usage
|
||||
To use the `lollms-settings` tool, you can run the following command:
|
||||
|
||||
```
|
||||
python lollms_settings.py [--configuration_path CONFIGURATION_PATH] [--reset_personal_path] [--reset_config] [--reset_installs] [--default_cfg_path DEFAULT_CFG_PATH] [--tool_prefix TOOL_PREFIX] [--set_personal_folder_path SET_PERSONAL_FOLDER_PATH] [--install_binding INSTALL_BINDING] [--install_model INSTALL_MODEL] [--select_model SELECT_MODEL] [--mount_personalities MOUNT_PERSONALITIES] [--set_personal_foldrer SET_PERSONAL_FOLDRE] [--silent]
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
- `--configuration_path`: Path to the configuration file.
|
||||
- `--reset_personal_path`: Reset the personal path.
|
||||
- `--reset_config`: Reset the configurations.
|
||||
- `--reset_installs`: Reset all installation status.
|
||||
- `--default_cfg_path`: Reset all installation status.
|
||||
- `--tool_prefix`: A prefix to define what tool is being used (default `lollms_server_`).
|
||||
- lollms applications prefixes:
|
||||
- lollms server: `lollms_server_`
|
||||
- lollms-elf: `lollms_elf_`
|
||||
- lollms-webui: `""`
|
||||
- lollms-discord-bot: `lollms_discord_`
|
||||
- `--set_personal_folder_path`: Forces installing and selecting a specific binding.
|
||||
- `--install_binding`: Forces installing and selecting a specific binding.
|
||||
- `--install_model`: Forces installing and selecting a specific model.
|
||||
- `--select_model`: Forces selecting a specific model.
|
||||
- `--mount_personalities`: Forces mounting a list of personas.
|
||||
- `--set_personal_foldrer`: Forces setting personal folder to a specific value.
|
||||
- `--silent`: This will operate in silent mode, no menu will be shown.
|
||||
|
||||
### Examples
|
||||
Here are some examples of how to use the `lollms-settings` tool:
|
||||
|
||||
1. Reset the configurations:
|
||||
```
|
||||
python lollms_settings.py --reset_config
|
||||
```
|
||||
|
||||
2. Install and select a specific binding:
|
||||
```
|
||||
python lollms_settings.py --install_binding <binding_name>
|
||||
```
|
||||
|
||||
3. Install and select a specific model:
|
||||
```
|
||||
python lollms_settings.py --install_model <model_path>
|
||||
```
|
||||
|
||||
4. Select a specific model:
|
||||
```
|
||||
python lollms_settings.py --select_model <model_name>
|
||||
```
|
||||
|
||||
5. Mount a list of personas:
|
||||
```
|
||||
python lollms_settings.py --mount_personalities <persona1> <persona2> ...
|
||||
```
|
||||
|
||||
6. Set personal folder to a specific value:
|
||||
```
|
||||
python lollms_settings.py --set_personal_foldrer <folder_path>
|
||||
```
|
||||
|
||||
7. Run in silent mode:
|
||||
```
|
||||
python lollms_settings.py --silent
|
||||
```
|
||||
|
||||
## License
|
||||
This project is licensed under the Apache 2.0 License.
|
@ -1,272 +0,0 @@
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from ascii_colors import ASCIIColors
|
||||
|
||||
from lollms.paths import LollmsPaths
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
from lollms.app import LollmsApplication
|
||||
from typing import Callable
|
||||
|
||||
from lollms.config import BaseConfig
|
||||
from lollms.binding import BindingBuilder, InstallOption
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
class Settings(LollmsApplication):
|
||||
def __init__(
|
||||
self,
|
||||
lollms_paths:LollmsPaths=None,
|
||||
configuration_path:str|Path=None,
|
||||
show_logo:bool=True,
|
||||
show_commands_list:bool=False,
|
||||
show_personality_infos:bool=True,
|
||||
show_model_infos:bool=True,
|
||||
show_welcome_message:bool=True
|
||||
):
|
||||
|
||||
# get paths
|
||||
if lollms_paths is None:
|
||||
lollms_paths = LollmsPaths.find_paths(force_local=False, tool_prefix="lollms_server_")
|
||||
# Load maoin configuration
|
||||
config = LOLLMSConfig.autoload(lollms_paths)
|
||||
|
||||
super().__init__("lollms-settings", config, lollms_paths, load_model=False)
|
||||
|
||||
if show_logo:
|
||||
self.menu.show_logo()
|
||||
|
||||
if show_personality_infos:
|
||||
try:
|
||||
print()
|
||||
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()
|
||||
except:
|
||||
pass
|
||||
if show_model_infos:
|
||||
try:
|
||||
print()
|
||||
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()
|
||||
except:
|
||||
pass
|
||||
|
||||
def start(self):
|
||||
self.menu.main_menu()
|
||||
|
||||
def ask_override_file(self):
|
||||
user_input = input("Would you like to override the existing file? (Y/N): ")
|
||||
user_input = user_input.lower()
|
||||
if user_input == "y" or user_input == "yes":
|
||||
print("File will be overridden.")
|
||||
return True
|
||||
elif user_input == "n" or user_input == "no":
|
||||
print("File will not be overridden.")
|
||||
return False
|
||||
else:
|
||||
print("Invalid input. Please enter 'Y' or 'N'.")
|
||||
# Call the function again recursively to prompt the user for valid input
|
||||
return self.ask_override_file()
|
||||
|
||||
def start_log(self, file_name):
|
||||
if Path(file_name).is_absolute():
|
||||
self.log_file_path = Path(file_name)
|
||||
else:
|
||||
home_dir = self.lollms_paths.personal_path/"logs"
|
||||
home_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.log_file_path = home_dir/file_name
|
||||
if self.log_file_path.exists():
|
||||
if not self.ask_override_file():
|
||||
print("Canceled")
|
||||
return
|
||||
try:
|
||||
with(open(self.log_file_path, "w") as f):
|
||||
self.header = f"""------------------------
|
||||
Log file for lollms discussion
|
||||
Participating personalities:
|
||||
{self.config['personalities']}
|
||||
------------------------
|
||||
"""
|
||||
f.write(self.header)
|
||||
self.is_logging = True
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def log(self, text, append=False):
|
||||
try:
|
||||
with(open(self.log_file_path, "a" if append else "w") as f):
|
||||
f.write(text) if append else f.write(self.header+self.personality.personality_conditioning+text)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def stop_log(self):
|
||||
self.is_logging = False
|
||||
|
||||
|
||||
def reset_context(self):
|
||||
if self.personality.include_welcome_message_in_disucssion:
|
||||
full_discussion = (
|
||||
self.personality.ai_message_prefix +
|
||||
self.personality.welcome_message +
|
||||
self.personality.link_text
|
||||
)
|
||||
else:
|
||||
full_discussion = ""
|
||||
return full_discussion
|
||||
|
||||
def safe_generate(self, full_discussion:str, n_predict=None, callback: Callable[[str, int, dict], bool]=None):
|
||||
"""safe_generate
|
||||
|
||||
Args:
|
||||
full_discussion (string): A prompt or a long discussion to use for generation
|
||||
callback (_type_, optional): A callback to call for each received token. Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: Model output
|
||||
"""
|
||||
if n_predict == None:
|
||||
n_predict =self.personality.model_n_predicts
|
||||
tk = self.personality.model.tokenize(full_discussion)
|
||||
n_tokens = len(tk)
|
||||
fd = self.personality.model.detokenize(tk[-min(self.config.ctx_size-self.n_cond_tk,n_tokens):])
|
||||
self.bot_says = ""
|
||||
output = self.personality.model.generate(self.personality.personality_conditioning+fd, n_predict=n_predict, callback=callback)
|
||||
return output
|
||||
|
||||
def remove_text_from_string(self, string, text_to_find):
|
||||
"""
|
||||
Removes everything from the first occurrence of the specified text in the string (case-insensitive).
|
||||
|
||||
Parameters:
|
||||
string (str): The original string.
|
||||
text_to_find (str): The text to find in the string.
|
||||
|
||||
Returns:
|
||||
str: The updated string.
|
||||
"""
|
||||
index = string.lower().find(text_to_find.lower())
|
||||
|
||||
if index != -1:
|
||||
string = string[:index]
|
||||
|
||||
return string
|
||||
|
||||
def main():
|
||||
# Create the argument parser
|
||||
parser = argparse.ArgumentParser(description='The lollms-settings app is used to configure multiple aspects of the lollms project. Lollms is a multi bindings LLM service that serves multiple LLM models that can generate text out of a prompt.')
|
||||
|
||||
# Add the configuration path argument
|
||||
parser.add_argument('--configuration_path', default=None,
|
||||
help='Path to the configuration file')
|
||||
|
||||
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')
|
||||
parser.add_argument('--reset_installs', action='store_true', help='Reset all installation status')
|
||||
parser.add_argument('--default_cfg_path', type=str, default=None, help='Reset all installation status')
|
||||
|
||||
|
||||
parser.add_argument('--tool_prefix', type=str, default="lollms_server_", help='A prefix to define what tool is being used (default lollms_server_)\nlollms applications prefixes:\n lollms server: lollms_server_\nlollms-elf: lollms_elf_\nlollms-webui: ""\nlollms-discord-bot: lollms_discord_')
|
||||
parser.add_argument('--set_personal_folder_path', type=str, default=None, help='Forces installing and selecting a specific binding')
|
||||
parser.add_argument('--install_binding', type=str, default=None, help='Forces installing and selecting a specific binding')
|
||||
parser.add_argument('--install_model', type=str, default=None, help='Forces installing and selecting a specific model')
|
||||
parser.add_argument('--select_model', type=str, default=None, help='Forces selecting a specific model')
|
||||
parser.add_argument('--mount_personalities', nargs='+', type=str, default=None, help='Forces mounting a list of personas')
|
||||
parser.add_argument('--set_personal_foldrer', type=str, default=None, help='Forces setting personal folder to a specific value')
|
||||
|
||||
parser.add_argument('--silent', action='store_true', help='This will operate in scilent mode, no menu will be shown')
|
||||
|
||||
# Parse the command-line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
tool_prefix = args.tool_prefix
|
||||
|
||||
if args.reset_installs:
|
||||
LollmsApplication.reset_all_installs()
|
||||
|
||||
if args.reset_personal_path:
|
||||
LollmsPaths.reset_configs()
|
||||
|
||||
if args.reset_config:
|
||||
lollms_paths = LollmsPaths.find_paths(custom_default_cfg_path=args.default_cfg_path, tool_prefix=tool_prefix, force_personal_path=args.set_personal_folder_path)
|
||||
cfg_path = lollms_paths.personal_configuration_path / f"{lollms_paths.tool_prefix}local_config.yaml"
|
||||
try:
|
||||
cfg_path.unlink()
|
||||
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=tool_prefix, force_personal_path=args.set_personal_folder_path)
|
||||
|
||||
configuration_path = args.configuration_path
|
||||
|
||||
if args.set_personal_folder_path:
|
||||
cfg = BaseConfig(config={
|
||||
"lollms_path":str(Path(__file__).parent),
|
||||
"lollms_personal_path":args.set_personal_folder_path
|
||||
})
|
||||
ASCIIColors.green(f"Selected personal path: {cfg.lollms_personal_path}")
|
||||
pp= Path(cfg.lollms_personal_path)
|
||||
if not pp.exists():
|
||||
try:
|
||||
pp.mkdir(parents=True)
|
||||
ASCIIColors.green(f"Personal path set to {pp}")
|
||||
except:
|
||||
print(f"{ASCIIColors.color_red}It seams there is an error in the path you rovided{ASCIIColors.color_reset}")
|
||||
global_paths_cfg_path = Path.home()/f"{tool_prefix}global_paths_cfg.yaml"
|
||||
lollms_paths = LollmsPaths.find_paths(force_local=False, custom_global_paths_cfg_path=global_paths_cfg_path, tool_prefix=tool_prefix)
|
||||
cfg.save_config(global_paths_cfg_path)
|
||||
|
||||
|
||||
settings_app = Settings(configuration_path=configuration_path, lollms_paths=lollms_paths, show_commands_list=True)
|
||||
|
||||
if args.install_binding:
|
||||
settings_app.config.binding_name= args.install_binding
|
||||
settings_app.binding = BindingBuilder().build_binding(settings_app.config, settings_app.lollms_paths,InstallOption.FORCE_INSTALL, lollmsCom=settings_app)
|
||||
settings_app.config.save_config()
|
||||
|
||||
|
||||
if args.install_model:
|
||||
if not settings_app.binding:
|
||||
settings_app.binding = BindingBuilder().build_binding(settings_app.config, settings_app.lollms_paths,InstallOption.FORCE_INSTALL, lollmsCom=settings_app)
|
||||
|
||||
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:
|
||||
settings_app.config.download_model(args.install_model,settings_app.binding, progress_callback)
|
||||
|
||||
settings_app.config.model_name = args.install_model.split("/")[-1]
|
||||
settings_app.model = settings_app.binding.build_model()
|
||||
settings_app.config.save_config()
|
||||
|
||||
if args.select_model:
|
||||
settings_app.config.model_name = args.select_model
|
||||
if not settings_app.binding:
|
||||
settings_app.binding = BindingBuilder().build_binding(settings_app.config, settings_app.lollms_paths,InstallOption.FORCE_INSTALL, lollmsCom=settings_app)
|
||||
settings_app.model = settings_app.binding.build_model()
|
||||
settings_app.config.save_config()
|
||||
|
||||
if args.mount_personalities:
|
||||
for entry in args.mount_personalities:
|
||||
try:
|
||||
settings_app.config.personalities.append(entry)
|
||||
settings_app.mount_personality(-1)
|
||||
ASCIIColors.green(f"Personality : {entry} mounted")
|
||||
except:
|
||||
settings_app.config.personalities.pop()
|
||||
ASCIIColors.red(f"Personality : {entry} couldn't be mounted")
|
||||
|
||||
if not args.silent:
|
||||
settings_app.start()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,68 +0,0 @@
|
||||
from lollms.config import InstallOption
|
||||
from lollms.binding import BindingBuilder, ModelBuilder
|
||||
from lollms.personality import MSG_TYPE, PersonalityBuilder
|
||||
from lollms.main_config import LOLLMSConfig
|
||||
from lollms.paths import LollmsPaths
|
||||
from lollms.app import LollmsApplication
|
||||
from lollms.terminal import MainMenu
|
||||
from ascii_colors import ASCIIColors
|
||||
|
||||
from typing import Callable
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import yaml
|
||||
import time
|
||||
import sys
|
||||
|
||||
|
||||
class Trainer(LollmsApplication):
|
||||
def __init__(
|
||||
self,
|
||||
configuration_path:str|Path=None,
|
||||
show_logo:bool=True,
|
||||
show_time_elapsed:bool = False
|
||||
):
|
||||
# Fore it to be a path
|
||||
self.configuration_path = configuration_path
|
||||
self.is_logging = False
|
||||
self.log_file_path = ""
|
||||
self.show_time_elapsed = show_time_elapsed
|
||||
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__("lollms-console",config, lollms_paths)
|
||||
|
||||
if show_logo:
|
||||
self.menu.show_logo()
|
||||
|
||||
def start_training(self):
|
||||
print(self.model)
|
||||
|
||||
def main():
|
||||
# Create the argument parser
|
||||
parser = argparse.ArgumentParser(description='App Description')
|
||||
|
||||
# Add the configuration path argument
|
||||
parser.add_argument('--configuration_path', default=None,
|
||||
help='Path to the configuration file')
|
||||
|
||||
# Parse the command-line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Parse the command-line arguments
|
||||
args = parser.parse_args()
|
||||
|
||||
configuration_path = args.configuration_path
|
||||
|
||||
lollms_app = Trainer(configuration_path=configuration_path)
|
||||
lollms_app.start_training()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
50
lollms/generation.py
Normal file
50
lollms/generation.py
Normal file
@ -0,0 +1,50 @@
|
||||
from enum import Enum
|
||||
from ascii_colors import ASCIIColors
|
||||
class ROLE_CHANGE_DECISION(Enum):
|
||||
"""Roles change detection."""
|
||||
|
||||
MOVE_ON = 0
|
||||
"""The received chunk is a normal chunk so move on."""
|
||||
|
||||
PROGRESSING = 1
|
||||
"""Started receiving Role change."""
|
||||
|
||||
ROLE_CHANGED = 2
|
||||
"""Role change detected."""
|
||||
|
||||
FALSE_ALERT = 3
|
||||
"""False alert (didn't detect the full role change)."""
|
||||
|
||||
class ROLE_CHANGE_OURTPUT:
|
||||
status:ROLE_CHANGE_DECISION
|
||||
value:str=""
|
||||
def __init__(self, status, value='') -> None:
|
||||
self.status = status
|
||||
self.value = value
|
||||
|
||||
class RECPTION_MANAGER:
|
||||
done:bool=False
|
||||
chunk:str=""
|
||||
new_role:str=""
|
||||
reception_buffer:str=""
|
||||
def new_chunk(self, chunk):
|
||||
self.chunk = chunk
|
||||
if chunk=="!" and self.new_role == "":
|
||||
self.new_role+=chunk
|
||||
return ROLE_CHANGE_OURTPUT(ROLE_CHANGE_DECISION.PROGRESSING)
|
||||
elif self.new_role != "":
|
||||
if self.new_role=="!" and chunk=="@":
|
||||
self.new_role+=chunk
|
||||
return ROLE_CHANGE_OURTPUT(ROLE_CHANGE_DECISION.PROGRESSING)
|
||||
elif self.new_role=="!@" and chunk==">":
|
||||
self.new_role=""
|
||||
self.done=True
|
||||
ASCIIColors.yellow("Delected end of sentence")
|
||||
return ROLE_CHANGE_OURTPUT(ROLE_CHANGE_DECISION.ROLE_CHANGED, self.reception_buffer)
|
||||
else:
|
||||
rc = ROLE_CHANGE_OURTPUT(ROLE_CHANGE_DECISION.FALSE_ALERT, self.reception_buffer)
|
||||
self.reception_buffer += self.new_role
|
||||
return rc
|
||||
self.reception_buffer += chunk
|
||||
return ROLE_CHANGE_OURTPUT(ROLE_CHANGE_DECISION.MOVE_ON)
|
||||
|
@ -8,12 +8,13 @@ description:
|
||||
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Request, Body
|
||||
from fastapi import APIRouter, Request, Body, Response
|
||||
from lollms.server.elf_server import LOLLMSElfServer
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import StreamingResponse
|
||||
from lollms.types import MSG_TYPE
|
||||
from lollms.utilities import detect_antiprompt, remove_text_from_string, trace_exception
|
||||
from lollms.generation import RECPTION_MANAGER, ROLE_CHANGE_DECISION, ROLE_CHANGE_OURTPUT
|
||||
from ascii_colors import ASCIIColors
|
||||
import time
|
||||
import threading
|
||||
@ -21,60 +22,15 @@ from typing import List, Optional, Union
|
||||
import random
|
||||
import string
|
||||
import json
|
||||
from enum import Enum
|
||||
import asyncio
|
||||
|
||||
|
||||
def _generate_id(length=10):
|
||||
letters_and_digits = string.ascii_letters + string.digits
|
||||
random_id = ''.join(random.choice(letters_and_digits) for _ in range(length))
|
||||
return random_id
|
||||
|
||||
class GenerateRequest(BaseModel):
|
||||
|
||||
text: str
|
||||
n_predict: int = 1024
|
||||
stream: bool = False
|
||||
temperature: float = 0.4
|
||||
top_k: int = 50
|
||||
top_p: float = 0.6
|
||||
repeat_penalty: float = 1.3
|
||||
repeat_last_n: int = 40
|
||||
seed: int = -1
|
||||
n_threads: int = 1
|
||||
|
||||
class V1ChatGenerateRequest(BaseModel):
|
||||
"""
|
||||
Data model for the V1 Chat Generate Request.
|
||||
|
||||
Attributes:
|
||||
- model: str representing the model to be used for text generation.
|
||||
- messages: list of messages to be used as prompts for text generation.
|
||||
- stream: bool indicating whether to stream the generated text or not.
|
||||
- temperature: float representing the temperature parameter for text generation.
|
||||
- max_tokens: float representing the maximum number of tokens to generate.
|
||||
"""
|
||||
model: str
|
||||
messages: list
|
||||
stream: bool
|
||||
temperature: float
|
||||
max_tokens: float
|
||||
|
||||
|
||||
class V1InstructGenerateRequest(BaseModel):
|
||||
"""
|
||||
Data model for the V1 Chat Generate Request.
|
||||
|
||||
Attributes:
|
||||
- model: str representing the model to be used for text generation.
|
||||
- messages: list of messages to be used as prompts for text generation.
|
||||
- stream: bool indicating whether to stream the generated text or not.
|
||||
- temperature: float representing the temperature parameter for text generation.
|
||||
- max_tokens: float representing the maximum number of tokens to generate.
|
||||
"""
|
||||
model: str
|
||||
prompt: str
|
||||
stream: bool
|
||||
temperature: float
|
||||
max_tokens: float
|
||||
|
||||
# ----------------------- Defining router and main class ------------------------------
|
||||
|
||||
router = APIRouter()
|
||||
@ -240,7 +196,7 @@ class Delta(BaseModel):
|
||||
class Choices(BaseModel):
|
||||
finish_reason: Optional[str] = None,
|
||||
index: Optional[int] = 0,
|
||||
message: Optional[str] = "",
|
||||
message: Optional[Message] = None,
|
||||
logprobs: Optional[float] = None
|
||||
|
||||
|
||||
@ -283,11 +239,6 @@ class StreamingModelResponse(BaseModel):
|
||||
usage: Optional[Usage] = None
|
||||
"""Usage statistics for the completion request."""
|
||||
|
||||
_hidden_params: dict = {}
|
||||
def encode(self, charset):
|
||||
encoded = json.dumps(self.dict()).encode(charset)
|
||||
return encoded
|
||||
|
||||
class ModelResponse(BaseModel):
|
||||
id: str
|
||||
"""A unique identifier for the completion."""
|
||||
@ -314,105 +265,121 @@ class ModelResponse(BaseModel):
|
||||
usage: Optional[Usage] = None
|
||||
"""Usage statistics for the completion request."""
|
||||
|
||||
_hidden_params: dict = {}
|
||||
|
||||
class GenerationRequest(BaseModel):
|
||||
model: str = ""
|
||||
messages: List[Message]
|
||||
max_tokens: Optional[int] = 1024
|
||||
stream: Optional[bool] = False
|
||||
temperature: Optional[float] = 0.1
|
||||
|
||||
|
||||
@router.post("/v1/chat/completions")
|
||||
@router.post("/v1/chat/completions", response_class=StreamingModelResponse|ModelResponse)
|
||||
async def v1_chat_completions(request: GenerationRequest):
|
||||
try:
|
||||
reception_manager=RECPTION_MANAGER()
|
||||
messages = request.messages
|
||||
text = ""
|
||||
prompt = ""
|
||||
roles= False
|
||||
for message in messages:
|
||||
text += f"{message.role}: {message.content}\n"
|
||||
if message.role!="":
|
||||
prompt += f"!@>{message.role}: {message.content}\n"
|
||||
roles = True
|
||||
else:
|
||||
prompt += f"{message.content}\n"
|
||||
if roles:
|
||||
prompt += "!@>assistant:"
|
||||
n_predict = request.max_tokens if request.max_tokens>0 else 1024
|
||||
stream = request.stream
|
||||
|
||||
prompt_tokens = len(elf_server.binding.tokenize(prompt))
|
||||
if elf_server.binding is not None:
|
||||
if stream:
|
||||
output = {"text":"","waiting":True,"new":[]}
|
||||
def generate_chunks():
|
||||
new_output={"new_values":[]}
|
||||
async def generate_chunks():
|
||||
lk = threading.Lock()
|
||||
|
||||
def callback(chunk, chunk_type:MSG_TYPE=MSG_TYPE.MSG_TYPE_CHUNK):
|
||||
if elf_server.cancel_gen:
|
||||
return False
|
||||
|
||||
if chunk is None:
|
||||
return
|
||||
output["text"] += chunk
|
||||
|
||||
rx = reception_manager.new_chunk(chunk)
|
||||
if rx.status!=ROLE_CHANGE_DECISION.MOVE_ON:
|
||||
if rx.status==ROLE_CHANGE_DECISION.PROGRESSING:
|
||||
return True
|
||||
elif rx.status==ROLE_CHANGE_DECISION.ROLE_CHANGED:
|
||||
return False
|
||||
else:
|
||||
chunk = chunk + rx.value
|
||||
|
||||
# Yield each chunk of data
|
||||
lk.acquire()
|
||||
try:
|
||||
antiprompt = detect_antiprompt(output["text"])
|
||||
if antiprompt:
|
||||
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")
|
||||
output["text"] = remove_text_from_string(output["text"],antiprompt)
|
||||
lk.release()
|
||||
return False
|
||||
else:
|
||||
output["new"].append(chunk)
|
||||
lk.release()
|
||||
return True
|
||||
new_output["new_values"].append(reception_manager.chunk)
|
||||
lk.release()
|
||||
return True
|
||||
except Exception as ex:
|
||||
trace_exception(ex)
|
||||
lk.release()
|
||||
return True
|
||||
return False
|
||||
|
||||
def chunks_builder():
|
||||
elf_server.binding.generate(
|
||||
text,
|
||||
prompt,
|
||||
n_predict,
|
||||
callback=callback,
|
||||
temperature=request.temperature or elf_server.config.temperature
|
||||
)
|
||||
output["waiting"] = False
|
||||
reception_manager.done = True
|
||||
thread = threading.Thread(target=chunks_builder)
|
||||
thread.start()
|
||||
current_index = 0
|
||||
while (output["waiting"] and elf_server.cancel_gen == False):
|
||||
while (output["waiting"] and len(output["new"])==0):
|
||||
while (not reception_manager.done and elf_server.cancel_gen == False):
|
||||
while (not reception_manager.done and len(new_output["new_values"])==0):
|
||||
time.sleep(0.001)
|
||||
lk.acquire()
|
||||
for i in range(len(output["new"])):
|
||||
for i in range(len(new_output["new_values"])):
|
||||
output_val = StreamingModelResponse(
|
||||
id = _generate_id(),
|
||||
choices = [StreamingChoices(index= current_index, delta=Delta(content=output["new"][i]))],
|
||||
choices = [StreamingChoices(index= current_index, delta=Delta(content=new_output["new_values"][i]))],
|
||||
created=int(time.time()),
|
||||
model=elf_server.config.model_name,
|
||||
usage=Usage(prompt_tokens= 0, completion_tokens= 10)
|
||||
object="chat.completion.chunk",
|
||||
usage=Usage(prompt_tokens= prompt_tokens, completion_tokens= 1)
|
||||
)
|
||||
current_index += 1
|
||||
yield output_val
|
||||
output["new"]=[]
|
||||
yield (output_val.json() + '\n')
|
||||
new_output["new_values"]=[]
|
||||
lk.release()
|
||||
elf_server.cancel_gen = False
|
||||
|
||||
return StreamingResponse(iter(generate_chunks()))
|
||||
elf_server.cancel_gen = False
|
||||
return StreamingResponse(generate_chunks(), media_type="application/json")
|
||||
else:
|
||||
output = {"text":""}
|
||||
def callback(chunk, chunk_type:MSG_TYPE=MSG_TYPE.MSG_TYPE_CHUNK):
|
||||
# Yield each chunk of data
|
||||
if chunk is None:
|
||||
return
|
||||
output["text"] += chunk
|
||||
antiprompt = detect_antiprompt(output["text"])
|
||||
if antiprompt:
|
||||
ASCIIColors.warning(f"\nDetected hallucination with antiprompt: {antiprompt}")
|
||||
output["text"] = remove_text_from_string(output["text"],antiprompt)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
rx = reception_manager.new_chunk(chunk)
|
||||
if rx.status!=ROLE_CHANGE_DECISION.MOVE_ON:
|
||||
if rx.status==ROLE_CHANGE_DECISION.PROGRESSING:
|
||||
return True
|
||||
elif rx.status==ROLE_CHANGE_DECISION.ROLE_CHANGED:
|
||||
return False
|
||||
else:
|
||||
chunk = chunk + rx.value
|
||||
|
||||
|
||||
return True
|
||||
elf_server.binding.generate(
|
||||
text,
|
||||
prompt,
|
||||
n_predict,
|
||||
callback=callback,
|
||||
temperature=request.temperature or elf_server.config.temperature
|
||||
)
|
||||
return ModelResponse(id = _generate_id(), choices = [Choices(message=output["text"])], created=int(time.time()))
|
||||
completion_tokens = len(elf_server.binding.tokenize(reception_manager.reception_buffer))
|
||||
return ModelResponse(id = _generate_id(), choices = [Choices(message=Message(role="assistant", content=reception_manager.reception_buffer), finish_reason="stop", index=0)], created=int(time.time()), model=request.model,usage=Usage(prompt_tokens=prompt_tokens, completion_tokens=completion_tokens))
|
||||
else:
|
||||
return None
|
||||
except Exception as ex:
|
||||
|
@ -14,3 +14,10 @@ safe_store
|
||||
ascii_colors>=0.1.3
|
||||
|
||||
autopep8
|
||||
|
||||
fastapi
|
||||
uvicorn
|
||||
python-multipart
|
||||
python-socketio
|
||||
python-socketio[client]
|
||||
python-socketio[asyncio_client]
|
@ -10,4 +10,11 @@ setuptools
|
||||
requests
|
||||
safe_store
|
||||
|
||||
autopep8
|
||||
autopep8
|
||||
|
||||
fastapi
|
||||
uvicorn
|
||||
python-multipart
|
||||
python-socketio
|
||||
python-socketio[client]
|
||||
python-socketio[asyncio_client]
|
Loading…
Reference in New Issue
Block a user