2023-04-15 11:30:08 +00:00
|
|
|
######
|
|
|
|
# Project : GPT4ALL-UI
|
|
|
|
# File : api.py
|
|
|
|
# Author : ParisNeo with the help of the community
|
|
|
|
# Supported by Nomic-AI
|
|
|
|
# Licence : Apache 2.0
|
|
|
|
# Description :
|
|
|
|
# A simple api to communicate with gpt4all-ui and its models.
|
|
|
|
######
|
|
|
|
import gc
|
|
|
|
import sys
|
|
|
|
from datetime import datetime
|
|
|
|
from pyGpt4All.db import DiscussionsDB
|
2023-04-23 18:28:24 +00:00
|
|
|
from pathlib import Path
|
|
|
|
import importlib
|
2023-04-30 20:40:19 +00:00
|
|
|
from pyaipersonality import AIPersonality
|
2023-04-23 18:28:24 +00:00
|
|
|
|
2023-04-20 17:30:03 +00:00
|
|
|
__author__ = "parisneo"
|
|
|
|
__github__ = "https://github.com/nomic-ai/gpt4all-ui"
|
|
|
|
__copyright__ = "Copyright 2023, "
|
|
|
|
__license__ = "Apache 2.0"
|
2023-04-15 11:30:08 +00:00
|
|
|
|
|
|
|
class GPT4AllAPI():
|
2023-04-30 20:40:19 +00:00
|
|
|
def __init__(self, config:dict, personality:AIPersonality, config_file_path:str) -> None:
|
2023-04-15 11:30:08 +00:00
|
|
|
self.config = config
|
|
|
|
self.personality = personality
|
|
|
|
self.config_file_path = config_file_path
|
2023-04-23 22:19:15 +00:00
|
|
|
self.cancel_gen = False
|
2023-04-15 11:30:08 +00:00
|
|
|
|
|
|
|
# Keeping track of current discussion and message
|
|
|
|
self.current_discussion = None
|
|
|
|
self.current_message_id = 0
|
|
|
|
|
|
|
|
self.db_path = config["db_path"]
|
|
|
|
|
|
|
|
# Create database object
|
|
|
|
self.db = DiscussionsDB(self.db_path)
|
|
|
|
|
|
|
|
# If the database is empty, populate it with tables
|
|
|
|
self.db.populate()
|
|
|
|
|
|
|
|
# This is used to keep track of messages
|
|
|
|
self.full_message_list = []
|
|
|
|
|
2023-04-20 17:30:03 +00:00
|
|
|
# Select backend
|
2023-04-24 21:58:50 +00:00
|
|
|
self.BACKENDS_LIST = {f.stem:f for f in Path("backends").iterdir() if f.is_dir() and f.stem!="__pycache__"}
|
2023-04-23 18:28:24 +00:00
|
|
|
|
2023-05-02 14:49:13 +00:00
|
|
|
self.backend =self.load_backend(self.BACKENDS_LIST[self.config["backend"]])
|
2023-04-20 17:30:03 +00:00
|
|
|
|
2023-04-15 11:30:08 +00:00
|
|
|
# Build chatbot
|
|
|
|
self.chatbot_bindings = self.create_chatbot()
|
|
|
|
print("Chatbot created successfully")
|
|
|
|
# generation status
|
|
|
|
self.generating=False
|
|
|
|
|
2023-04-23 18:28:24 +00:00
|
|
|
def load_backend(self, backend_path):
|
|
|
|
|
|
|
|
# define the full absolute path to the module
|
|
|
|
absolute_path = backend_path.resolve()
|
|
|
|
|
|
|
|
# infer the module name from the file path
|
|
|
|
module_name = backend_path.stem
|
|
|
|
|
|
|
|
# use importlib to load the module from the file path
|
2023-04-24 19:24:18 +00:00
|
|
|
loader = importlib.machinery.SourceFileLoader(module_name, str(absolute_path/"__init__.py"))
|
2023-04-23 18:28:24 +00:00
|
|
|
backend_module = loader.load_module()
|
|
|
|
backend_class = getattr(backend_module, backend_module.backend_name)
|
2023-05-02 14:49:13 +00:00
|
|
|
return backend_class
|
2023-04-23 18:28:24 +00:00
|
|
|
|
2023-04-15 11:30:08 +00:00
|
|
|
def create_chatbot(self):
|
2023-04-23 22:19:15 +00:00
|
|
|
return self.backend(self.config)
|
2023-04-15 11:30:08 +00:00
|
|
|
|
|
|
|
def condition_chatbot(self, conditionning_message):
|
|
|
|
if self.current_discussion is None:
|
|
|
|
self.current_discussion = self.db.load_last_discussion()
|
|
|
|
|
|
|
|
message_id = self.current_discussion.add_message(
|
|
|
|
"conditionner",
|
|
|
|
conditionning_message,
|
|
|
|
DiscussionsDB.MSG_TYPE_CONDITIONNING,
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
)
|
|
|
|
self.current_message_id = message_id
|
2023-04-30 20:40:19 +00:00
|
|
|
if self.personality.welcome_message!="":
|
|
|
|
if self.personality.welcome_message!="":
|
2023-04-27 23:39:57 +00:00
|
|
|
message_id = self.current_discussion.add_message(
|
2023-04-30 20:40:19 +00:00
|
|
|
self.personality.name, self.personality.welcome_message,
|
2023-04-27 23:39:57 +00:00
|
|
|
DiscussionsDB.MSG_TYPE_NORMAL,
|
|
|
|
0,
|
|
|
|
self.current_message_id
|
|
|
|
)
|
2023-04-15 11:30:08 +00:00
|
|
|
|
|
|
|
self.current_message_id = message_id
|
|
|
|
return message_id
|
|
|
|
|
|
|
|
def prepare_reception(self):
|
|
|
|
self.bot_says = ""
|
|
|
|
self.full_text = ""
|
|
|
|
self.is_bot_text_started = False
|
|
|
|
#self.current_message = message
|
|
|
|
|
|
|
|
def create_new_discussion(self, title):
|
|
|
|
self.current_discussion = self.db.create_discussion(title)
|
|
|
|
# Get the current timestamp
|
|
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
|
|
# Chatbot conditionning
|
2023-05-01 22:09:57 +00:00
|
|
|
self.condition_chatbot(self.personality.personality_conditioning)
|
2023-04-15 11:30:08 +00:00
|
|
|
return timestamp
|
|
|
|
|
|
|
|
def prepare_query(self, message_id=-1):
|
|
|
|
messages = self.current_discussion.get_messages()
|
|
|
|
self.full_message_list = []
|
|
|
|
for message in messages:
|
|
|
|
if message["id"]<= message_id or message_id==-1:
|
|
|
|
if message["type"]!=self.db.MSG_TYPE_CONDITIONNING:
|
2023-04-30 20:40:19 +00:00
|
|
|
if message["sender"]==self.personality.name:
|
|
|
|
self.full_message_list.append(self.personality.ai_message_prefix+message["content"])
|
2023-04-15 11:30:08 +00:00
|
|
|
else:
|
2023-04-30 20:40:19 +00:00
|
|
|
self.full_message_list.append(self.personality.user_message_prefix + message["content"])
|
2023-04-16 11:47:39 +00:00
|
|
|
|
2023-04-30 20:40:19 +00:00
|
|
|
link_text = self.personality.link_text
|
2023-04-16 11:47:39 +00:00
|
|
|
|
2023-04-15 11:30:08 +00:00
|
|
|
if len(self.full_message_list) > self.config["nb_messages_to_remember"]:
|
2023-04-30 20:40:19 +00:00
|
|
|
discussion_messages = self.personality.personality_conditioning+ link_text.join(self.full_message_list[-self.config["nb_messages_to_remember"]:])
|
2023-04-15 11:30:08 +00:00
|
|
|
else:
|
2023-04-30 20:40:19 +00:00
|
|
|
discussion_messages = self.personality.personality_conditioning+ link_text.join(self.full_message_list)
|
2023-04-16 11:47:39 +00:00
|
|
|
|
2023-04-30 20:40:19 +00:00
|
|
|
discussion_messages += link_text + self.personality.ai_message_prefix
|
2023-04-16 11:47:39 +00:00
|
|
|
return discussion_messages # Removes the last return
|
2023-04-17 22:23:31 +00:00
|
|
|
|
|
|
|
def get_discussion_to(self, message_id=-1):
|
|
|
|
messages = self.current_discussion.get_messages()
|
|
|
|
self.full_message_list = []
|
|
|
|
for message in messages:
|
|
|
|
if message["id"]<= message_id or message_id==-1:
|
|
|
|
if message["type"]!=self.db.MSG_TYPE_CONDITIONNING:
|
2023-04-30 20:40:19 +00:00
|
|
|
if message["sender"]==self.personality.name:
|
|
|
|
self.full_message_list.append(self.personality.ai_message_prefix+message["content"])
|
2023-04-17 22:23:31 +00:00
|
|
|
else:
|
2023-04-30 20:40:19 +00:00
|
|
|
self.full_message_list.append(self.personality.user_message_prefix + message["content"])
|
2023-04-17 22:23:31 +00:00
|
|
|
|
2023-04-30 20:40:19 +00:00
|
|
|
link_text = self.personality.link_text
|
2023-04-17 22:23:31 +00:00
|
|
|
|
|
|
|
if len(self.full_message_list) > self.config["nb_messages_to_remember"]:
|
2023-05-01 22:09:57 +00:00
|
|
|
discussion_messages = self.personality.personality_conditioning+ link_text.join(self.full_message_list[-self.config["nb_messages_to_remember"]:])
|
2023-04-17 22:23:31 +00:00
|
|
|
else:
|
2023-05-01 22:09:57 +00:00
|
|
|
discussion_messages = self.personality.personality_conditioning+ link_text.join(self.full_message_list)
|
2023-04-17 22:23:31 +00:00
|
|
|
|
|
|
|
return discussion_messages # Removes the last return
|
|
|
|
|
2023-04-23 22:19:15 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
2023-04-15 11:30:08 +00:00
|
|
|
def new_text_callback(self, text: str):
|
2023-04-23 22:19:15 +00:00
|
|
|
if self.cancel_gen:
|
|
|
|
return False
|
2023-04-15 11:30:08 +00:00
|
|
|
print(text, end="")
|
|
|
|
sys.stdout.flush()
|
2023-04-27 23:39:57 +00:00
|
|
|
|
2023-04-30 01:15:11 +00:00
|
|
|
self.bot_says += text
|
2023-05-04 23:05:02 +00:00
|
|
|
if not self.personality.detect_antiprompt(self.bot_says):
|
2023-05-04 23:50:43 +00:00
|
|
|
self.socketio.emit('message', {'data': self.bot_says})
|
2023-04-30 01:15:11 +00:00
|
|
|
if self.cancel_gen:
|
|
|
|
print("Generation canceled")
|
2023-05-02 12:16:10 +00:00
|
|
|
self.cancel_gen = False
|
2023-04-23 22:19:15 +00:00
|
|
|
return False
|
2023-04-27 23:39:57 +00:00
|
|
|
else:
|
2023-04-30 01:15:11 +00:00
|
|
|
return True
|
|
|
|
else:
|
2023-04-30 20:40:19 +00:00
|
|
|
self.bot_says = self.remove_text_from_string(self.bot_says, self.personality.user_message_prefix.strip())
|
2023-04-30 01:15:11 +00:00
|
|
|
print("The model is halucinating")
|
|
|
|
return False
|
2023-04-15 11:30:08 +00:00
|
|
|
|
|
|
|
def generate_message(self):
|
|
|
|
self.generating=True
|
|
|
|
gc.collect()
|
2023-04-23 22:19:15 +00:00
|
|
|
total_n_predict = self.config['n_predict']
|
|
|
|
print(f"Generating {total_n_predict} outputs... ")
|
|
|
|
print(f"Input text : {self.discussion_messages}")
|
2023-04-15 11:30:08 +00:00
|
|
|
self.chatbot_bindings.generate(
|
|
|
|
self.discussion_messages,
|
|
|
|
new_text_callback=self.new_text_callback,
|
|
|
|
n_predict=total_n_predict,
|
2023-05-04 23:50:43 +00:00
|
|
|
temp=self.config['temperature'],
|
2023-04-15 11:30:08 +00:00
|
|
|
top_k=self.config['top_k'],
|
|
|
|
top_p=self.config['top_p'],
|
|
|
|
repeat_penalty=self.config['repeat_penalty'],
|
|
|
|
repeat_last_n = self.config['repeat_last_n'],
|
|
|
|
#seed=self.config['seed'],
|
|
|
|
n_threads=self.config['n_threads']
|
|
|
|
)
|
|
|
|
self.generating=False
|