diff --git a/README.md b/README.md index aa4a017..c86f2c6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Baker -[![Downloads](https://static.pepy.tech/badge/baker-python)](https://pepy.tech/project/baker-python) - *Bot-Maker* Baker! Is a framework to create chatbots with Python in the easiest and simplest route, train your chatbot by texting or adding data in XML, JSON or YAML files. # Installation @@ -72,9 +70,11 @@ The files can also be empty for example a JSON file can be like this: To train the chatbot, use the `Trainer` class. Here is an example of basic training: ```py -from baker import trainer +import baker.trainer +import baker.bparser +import baker.chatbot -bot = trainer.Trainer('database.yaml') +bot = baker.trainer.Trainer('data.json') user_input = input("You: ") response = bot.get_response(user_input) @@ -91,7 +91,9 @@ from this route the keyword (user's question) must be already created in the fil But with this way to train you can train the chatbot as long you want to with custom keywords (no need to define them in the data file) and their infinite responses: ```py -trainer = Trainer("database.json") +import baker.trainer + +trainer = baker.trainer.Trainer('data.json') trainer.loop_training() ``` @@ -102,7 +104,7 @@ The data file can either be empty or it can have keywords, pre-defined keyowrds To parse the chatbot to run and test it use the `Parser` class: ```py -from baker import parser +import baker.bparser def test_chatbot(bot): while True: @@ -113,7 +115,7 @@ def test_chatbot(bot): response = bot.get_response(user_input) print("Bot:", response) -bot = parser.Parser('database.json') +bot = baker.bparser.Parser('data.json') test_chatbot(bot) ``` @@ -121,14 +123,14 @@ test_chatbot(bot) The above code will run the chatbot, but there is anther simpler way to run the chatbot with it's specified name which is to use the `Chatbot` class: ```py -from baker import trainer -from baker import parser -from baker import chatbot - -trainer = trainer.Trainer('database.json') -parser = parser.Parser('database.json') -my_chatbot = chatbot.Chatbot("MyChatbot") -my_chatbot.interactive_session(trainer, parser) +import baker.trainer +import baker.bparser +import baker.chatbot + +trainer = baker.trainer.Trainer('data.json') +parser = baker.bparser.Parser('data.json') +my_chatbot = baker.chatbot.Chatbot("MyChatbot") +my_chatbot.session(trainer, parser) ``` `Parser` class has more functions regarding the data file: @@ -136,40 +138,37 @@ my_chatbot.interactive_session(trainer, parser) - Exporting responses : ```py -response_file_name = "responses.json" -Parser = Parser(response_file_name) -parser.export_responses("exported_responses.xml") +import baker.bparser + +response_file_name = "data.json" +parser_instance = baker.bparser.Parser(response_file_name) +parser_instance.export_responses(export_file_name="data2.json") ``` - Reset resposes ```py -parser.reset_responses("A_User_Question") +baker.bparser.Parser.reset_responses("A_User_Question") ``` - Removing responses: ```py -user_input = "What is the weather like?" -response_to_remove = "I'm not sure." -parser.remove_response(user_input, response_to_remove) -``` +parser_instance2 = baker.bparser.Parser("data.json") -- List questions: - -```py -key_questions = parser.list_questions() -print("List of Key Questions:") -for question in key_questions: - print(question) +user_input = "Hello" +response_to_remove = "Heyy" +parser_instance2.remove_response(user_input, response_to_remove) ``` - Count responses: ```py -user_input = "Tell me a joke" -response_count = parser.count_responses(user_input) -print(f"Number of Responses for '{user_input}': {response_count}") +parser_instance3 = baker.bparser.Parser("data.json") +user_input = "Hello" + +count = parser_instance3.count_responses(user_input) +print(f"Number of Responses for '{user_input}': {count}") ``` -Keep training your chatbot by texting or adding words in the database and then run it! +Keep training your chatbot by texting or adding words in the database and then run it! \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..c80a814 --- /dev/null +++ b/__init__.py @@ -0,0 +1,8 @@ +from baker import bparser +from baker import chatbot +from baker import trainer +from tests import test_chatbot +from tests import test_functions +from tests import test_loop_trainer +from tests import test_parser +from tests import test_trainer \ No newline at end of file diff --git a/baker/__pycache__/__init__.cpython-312.pyc b/baker/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..08d4473 Binary files /dev/null and b/baker/__pycache__/__init__.cpython-312.pyc differ diff --git a/baker/__pycache__/bparser.cpython-312.pyc b/baker/__pycache__/bparser.cpython-312.pyc new file mode 100644 index 0000000..1704397 Binary files /dev/null and b/baker/__pycache__/bparser.cpython-312.pyc differ diff --git a/baker/__pycache__/chatbot.cpython-312.pyc b/baker/__pycache__/chatbot.cpython-312.pyc new file mode 100644 index 0000000..d379dc4 Binary files /dev/null and b/baker/__pycache__/chatbot.cpython-312.pyc differ diff --git a/baker/__pycache__/trainer.cpython-312.pyc b/baker/__pycache__/trainer.cpython-312.pyc new file mode 100644 index 0000000..2ff22e8 Binary files /dev/null and b/baker/__pycache__/trainer.cpython-312.pyc differ diff --git a/baker/bparser.py b/baker/bparser.py new file mode 100644 index 0000000..7b935ac --- /dev/null +++ b/baker/bparser.py @@ -0,0 +1,97 @@ +import json +import random +import os +import yaml +import xml.etree.ElementTree as ET + +class Parser: + def __init__(self, response_file_name): + self.file_path = response_file_name + self.responses = self.load_responses(self.file_path) + + def load_responses(self, file_path): + _, file_extension = os.path.splitext(file_path) + with open(file_path, 'r') as file: + if file_extension == '.json': + responses = json.load(file) + elif file_extension == '.yaml' or file_extension == '.yml': + responses = yaml.safe_load(file) + elif file_extension == '.xml': + root = ET.parse(file).getroot() + responses = {} + for item in root: + key = item.tag + value = [child.text for child in item] + responses[key] = value + else: + raise ValueError(f"Unsupported file format: {file_extension}") + return responses + + def save_responses(self): + _, file_extension = os.path.splitext(self.file_path) + with open(self.file_path, 'w') as file: + if file_extension == '.json': + json.dump(self.responses, file, indent=4) + elif file_extension == '.yaml' or file_extension == '.yml': + yaml.dump(self.responses, file, default_flow_style=False) + elif file_extension == '.xml': + root = ET.Element('responses') + for key, values in self.responses.items(): + item = ET.SubElement(root, key) + for value in values: + ET.SubElement(item, 'response').text = value + tree = ET.ElementTree(root) + tree.write(file, encoding="utf-8", xml_declaration=True) + else: + raise ValueError(f"Unsupported file format: {file_extension}") + + def train_response(self, user_input, new_response): + if user_input in self.responses: + self.responses[user_input].append(new_response) + else: + self.responses[user_input] = [new_response] + self.save_responses() + + def get_response(self, user_input): + if user_input in self.responses: + return random.choice(self.responses[user_input]) + else: + return "I'm not sure how to respond to that." + + def remove_response(self, user_input, response): + if user_input in self.responses and response in self.responses[user_input]: + self.responses[user_input].remove(response) + self.save_responses() + + def list_key_questions(self): + return list(self.responses.keys()) + + def count_responses(self, user_input): + if user_input in self.responses: + return len(self.responses[user_input]) + else: + return 0 + + def reset_responses(self, user_input): + if user_input in self.responses: + self.responses[user_input] = [] + self.save_responses() + + def export_responses(self, export_file_name): + export_extension = os.path.splitext(export_file_name)[1] + + with open(export_file_name, 'w') as export_file: + if export_extension == '.json': + json.dump(self.responses, export_file, indent=4) + elif export_extension == '.yaml' or export_extension == '.yml': + yaml.dump(self.responses, export_file, default_flow_style=False) + elif export_extension == '.xml': + root = ET.Element('responses') + for key, values in self.responses.items(): + item = ET.SubElement(root, key) + for value in values: + ET.SubElement(item, 'response').text = value + tree = ET.ElementTree(root) + tree.write(export_file, encoding="utf-8", xml_declaration=True) + else: + raise ValueError(f"Unsupported export file format: {export_extension}") \ No newline at end of file diff --git a/baker/chatbot.py b/baker/chatbot.py index 1160cf1..64ffb4c 100644 --- a/baker/chatbot.py +++ b/baker/chatbot.py @@ -1,6 +1,6 @@ import re -from parser import Parser -from trainer import Trainer +import baker.trainer +import baker.bparser as bparser class Chatbot: def __init__(self, name): @@ -13,5 +13,5 @@ def session(self, trainer, parser): if user_input.lower() == "exit": print("Session ended.") break - response = parser.get_response(user_input) - print("Bot:", response) \ No newline at end of file + response = parser.get_response(user_input) # Corrected line + print("Bot:", response) diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..5ac0d3e --- /dev/null +++ b/demo.py @@ -0,0 +1,74 @@ +#test-chatbot.py + +import baker.trainer +import baker.bparser +import baker.chatbot + +trainer = baker.trainer.Trainer('data.json') +parser = baker.bparser.Parser('data.json') +my_chatbot = baker.chatbot.Chatbot("MyChatbot") +my_chatbot.session(trainer, parser) + +#test-functions.py + +import baker.bparser + +response_file_name = "data.json" +parser_instance = baker.bparser.Parser(response_file_name) +parser_instance.export_responses(export_file_name="data2.json") + +baker.bparser.Parser.reset_responses("A_User_Question") + + +parser_instance2 = baker.bparser.Parser("data.json") + +user_input = "Hello" +response_to_remove = "Heyy" +parser_instance2.remove_response(user_input, response_to_remove) + +parser_instance3 = baker.bparser.Parser("data.json") +user_input = "Hello" + +count = parser_instance3.count_responses(user_input) +print(f"Number of Responses for '{user_input}': {count}") + +#test-loop-trainer.py + +import baker.trainer + +trainer = baker.trainer.Trainer('data.json') +trainer.loop_training() + +#test-parser.py + +import baker.bparser + +def test_chatbot(bot): + while True: + user_input = input("You: ") + if user_input.lower() == "exit": + print("Testing session ended.") + break + response = bot.get_response(user_input) + print("Bot:", response) + +bot = baker.bparser.Parser('data.json') + +test_chatbot(bot) + +#test-trainer.py + +import baker.trainer +import baker.bparser +import baker.chatbot + +bot = baker.trainer.Trainer('data.json') + +user_input = input("You: ") +response = bot.get_response(user_input) +print("Bot:", response) + +# Train the bot with a new response +new_response = input("New response: ") +bot.train_response(user_input, new_response) +print("Bot has been trained with the new response!") \ No newline at end of file diff --git a/setup.py b/setup.py index d5f8f29..1c91835 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name='baker-python', - version='1.1', + version='2.0', packages=['baker'], url='https://github.com/enginestein/Baker', license='GPL-3.0-only', diff --git a/test.py b/test.py new file mode 100644 index 0000000..618f9ee --- /dev/null +++ b/test.py @@ -0,0 +1,16 @@ +import unittest + +import tests.test_chatbot +import tests.test_functions +import tests.test_loop_trainer +import tests.test_parser +import tests.test_trainer + +if __name__ == "__main__": + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(tests.test_chatbot.TestChatbot)) + #test_suite.addTest(unittest.makeSuite(tests.test_functions.TestFunctions)) + #test_suite.addTest(unittest.makeSuite(tests.test_loop_trainer.TestLoopTrainer)) + #test_suite.addTest(unittest.makeSuite(tests.test_parser.TestParser)) + #test_suite.addTest(unittest.makeSuite(tests.test_trainer.TestTrainer)) + unittest.TextTestRunner().run(test_suite) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__pycache__/__init__.cpython-312.pyc b/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..29312d2 Binary files /dev/null and b/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/__pycache__/test_chatbot.cpython-312.pyc b/tests/__pycache__/test_chatbot.cpython-312.pyc new file mode 100644 index 0000000..80d5bb9 Binary files /dev/null and b/tests/__pycache__/test_chatbot.cpython-312.pyc differ diff --git a/tests/__pycache__/test_functions.cpython-312.pyc b/tests/__pycache__/test_functions.cpython-312.pyc new file mode 100644 index 0000000..2e43419 Binary files /dev/null and b/tests/__pycache__/test_functions.cpython-312.pyc differ diff --git a/tests/__pycache__/test_loop_trainer.cpython-312.pyc b/tests/__pycache__/test_loop_trainer.cpython-312.pyc new file mode 100644 index 0000000..3d261bc Binary files /dev/null and b/tests/__pycache__/test_loop_trainer.cpython-312.pyc differ diff --git a/tests/__pycache__/test_parser.cpython-312.pyc b/tests/__pycache__/test_parser.cpython-312.pyc new file mode 100644 index 0000000..fed8437 Binary files /dev/null and b/tests/__pycache__/test_parser.cpython-312.pyc differ diff --git a/tests/__pycache__/test_trainer.cpython-312.pyc b/tests/__pycache__/test_trainer.cpython-312.pyc new file mode 100644 index 0000000..f80e16a Binary files /dev/null and b/tests/__pycache__/test_trainer.cpython-312.pyc differ diff --git a/tests/data-empty.json b/tests/data-empty.json new file mode 100644 index 0000000..cb92a2e --- /dev/null +++ b/tests/data-empty.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/tests/data.json b/tests/data.json new file mode 100644 index 0000000..c18e8e9 --- /dev/null +++ b/tests/data.json @@ -0,0 +1,14 @@ +{ + "Hello": [ + "Hi", + "Hello", + "Hey mister, how are you?" + ], + "How are you": [ + "I'm good sir what about you? " + ], + "I am good too": [ + "that is good to know! ", + "that is very good to know! " + ] +} \ No newline at end of file diff --git a/tests/test_chatbot.py b/tests/test_chatbot.py new file mode 100644 index 0000000..382dc5b --- /dev/null +++ b/tests/test_chatbot.py @@ -0,0 +1,18 @@ +import os +import unittest +import baker.chatbot +import baker.trainer +import baker.bparser + +class TestChatbot(unittest.TestCase): + def test_chatbot(self): + script_dir = os.path.dirname(os.path.abspath(__file__)) + data_file = os.path.join(script_dir, 'data.json') + + traine = baker.trainer.Trainer(data_file) + parser = baker.bparser.Parser(data_file) + my_chatbot = baker.chatbot.Chatbot("MyChatbot") + my_chatbot.session(traine, parser) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_functions.py b/tests/test_functions.py new file mode 100644 index 0000000..6c86872 --- /dev/null +++ b/tests/test_functions.py @@ -0,0 +1,26 @@ +import unittest +import baker.bparser + +class TestFunctions(unittest.TestCase): + def test_export_responses(self): + response_file_name = "data.json" + parser_instance = baker.bparser.Parser(response_file_name) + parser_instance.export_responses(export_file_name="data2.json") + + def test_reset_responses(self): + baker.bparser.Parser.reset_responses("A_User_Question") + + def test_remove_response(self): + parser_instance2 = baker.bparser.Parser("data.json") + user_input = "Hello" + response_to_remove = "Heyy" + parser_instance2.remove_response(user_input, response_to_remove) + + def test_count_responses(self): + parser_instance3 = baker.bparser.Parser("data.json") + user_input = "Hello" + count = parser_instance3.count_responses(user_input) + self.assertTrue(isinstance(count, int)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_loop_trainer.py b/tests/test_loop_trainer.py new file mode 100644 index 0000000..bf05dc3 --- /dev/null +++ b/tests/test_loop_trainer.py @@ -0,0 +1,10 @@ +import unittest +import baker.trainer + +class TestLoopTrainer(unittest.TestCase): + def test_loop_training(self): + trainer = baker.trainer.Trainer('data.json') + trainer.loop_training() + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_parser.py b/tests/test_parser.py new file mode 100644 index 0000000..8026787 --- /dev/null +++ b/tests/test_parser.py @@ -0,0 +1,13 @@ +import unittest +import baker.bparser + +class TestParser(unittest.TestCase): + def test_chatbot(self): + def test_chatbot(bot): + pass + + bot = baker.bparser.Parser('data.json') + test_chatbot(bot) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_trainer.py b/tests/test_trainer.py new file mode 100644 index 0000000..341739c --- /dev/null +++ b/tests/test_trainer.py @@ -0,0 +1,16 @@ +import unittest +import baker.trainer + +class TestTrainer(unittest.TestCase): + def test_trainer(self): + bot = baker.trainer.Trainer('data.json') + user_input = input("You: ") + response = bot.get_response(user_input) + self.assertIsNotNone(response) + + new_response = input("New response: ") + bot.train_response(user_input, new_response) + self.assertIn(new_response, bot.get_response(user_input)) + +if __name__ == '__main__': + unittest.main()