diff --git a/jupyter_mentor/_modidx.py b/jupyter_mentor/_modidx.py index 9b52b28..2935d13 100644 --- a/jupyter_mentor/_modidx.py +++ b/jupyter_mentor/_modidx.py @@ -15,21 +15,25 @@ 'jupyter_mentor/chatbot.py'), 'jupyter_mentor.chatbot.ChatBotModel.prompt': ( 'chatbot.html#chatbotmodel.prompt', 'jupyter_mentor/chatbot.py'), + 'jupyter_mentor.chatbot.ChatBotModel.prompt_course_files': ( 'chatbot.html#chatbotmodel.prompt_course_files', + 'jupyter_mentor/chatbot.py'), + 'jupyter_mentor.chatbot.ChatBotModel.prompt_with_template': ( 'chatbot.html#chatbotmodel.prompt_with_template', + 'jupyter_mentor/chatbot.py'), 'jupyter_mentor.chatbot.ChatBotModel.update_bot_template': ( 'chatbot.html#chatbotmodel.update_bot_template', 'jupyter_mentor/chatbot.py'), 'jupyter_mentor.chatbot.ChatBotModel.update_human_template': ( 'chatbot.html#chatbotmodel.update_human_template', 'jupyter_mentor/chatbot.py'), 'jupyter_mentor.chatbot.ChatBotView': ('chatbot.html#chatbotview', 'jupyter_mentor/chatbot.py'), 'jupyter_mentor.chatbot.ChatBotView.__init__': ( 'chatbot.html#chatbotview.__init__', - 'jupyter_mentor/chatbot.py'), - 'jupyter_mentor.chatbot.EducatorChatBot': ( 'chatbot.html#educatorchatbot', - 'jupyter_mentor/chatbot.py'), - 'jupyter_mentor.chatbot.EducatorChatBot.__init__': ( 'chatbot.html#educatorchatbot.__init__', - 'jupyter_mentor/chatbot.py'), - 'jupyter_mentor.chatbot.StudentChatBot': ( 'chatbot.html#studentchatbot', - 'jupyter_mentor/chatbot.py'), - 'jupyter_mentor.chatbot.StudentChatBot.__init__': ( 'chatbot.html#studentchatbot.__init__', - 'jupyter_mentor/chatbot.py')}, + 'jupyter_mentor/chatbot.py')}, + 'jupyter_mentor.chatbot_tab': { 'jupyter_mentor.chatbot_tab.EducatorChatBot': ( 'chatbot.html#educatorchatbot', + 'jupyter_mentor/chatbot_tab.py'), + 'jupyter_mentor.chatbot_tab.EducatorChatBot.__init__': ( 'chatbot.html#educatorchatbot.__init__', + 'jupyter_mentor/chatbot_tab.py'), + 'jupyter_mentor.chatbot_tab.StudentChatBot': ( 'chatbot.html#studentchatbot', + 'jupyter_mentor/chatbot_tab.py'), + 'jupyter_mentor.chatbot_tab.StudentChatBot.__init__': ( 'chatbot.html#studentchatbot.__init__', + 'jupyter_mentor/chatbot_tab.py')}, 'jupyter_mentor.educator_course_overview': { 'jupyter_mentor.educator_course_overview.EducatorCourseOverview': ( 'educator_course_overview.html#educatorcourseoverview', 'jupyter_mentor/educator_course_overview.py'), 'jupyter_mentor.educator_course_overview.EducatorCourseOverview.__init__': ( 'educator_course_overview.html#educatorcourseoverview.__init__', @@ -50,10 +54,12 @@ 'jupyter_mentor/file_viewer.py')}, 'jupyter_mentor.llm': { 'jupyter_mentor.llm.FileModel': ('llm.html#filemodel', 'jupyter_mentor/llm.py'), 'jupyter_mentor.llm.FileModel.__init__': ('llm.html#filemodel.__init__', 'jupyter_mentor/llm.py'), - 'jupyter_mentor.llm.FileModel.load_markdown': ( 'llm.html#filemodel.load_markdown', - 'jupyter_mentor/llm.py'), - 'jupyter_mentor.llm.FileModel.load_pdf': ('llm.html#filemodel.load_pdf', 'jupyter_mentor/llm.py'), - 'jupyter_mentor.llm.FileModel.load_text': ('llm.html#filemodel.load_text', 'jupyter_mentor/llm.py'), + 'jupyter_mentor.llm.FileModel.load_markdown_to_db': ( 'llm.html#filemodel.load_markdown_to_db', + 'jupyter_mentor/llm.py'), + 'jupyter_mentor.llm.FileModel.load_pdf_to_db': ( 'llm.html#filemodel.load_pdf_to_db', + 'jupyter_mentor/llm.py'), + 'jupyter_mentor.llm.FileModel.load_text_to_db': ( 'llm.html#filemodel.load_text_to_db', + 'jupyter_mentor/llm.py'), 'jupyter_mentor.llm.FileModel.save_content_from_upload': ( 'llm.html#filemodel.save_content_from_upload', 'jupyter_mentor/llm.py'), 'jupyter_mentor.llm.LLM': ('llm.html#llm', 'jupyter_mentor/llm.py'), diff --git a/jupyter_mentor/chatbot.py b/jupyter_mentor/chatbot.py index c1eced3..6065a1f 100644 --- a/jupyter_mentor/chatbot.py +++ b/jupyter_mentor/chatbot.py @@ -1,9 +1,9 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06_chatbot.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_chatbot.ipynb. # %% auto 0 -__all__ = ['ChatBotModel', 'ChatBotView', 'ChatBot', 'StudentChatBot', 'EducatorChatBot'] +__all__ = ['ChatBotModel', 'ChatBotView', 'ChatBot'] -# %% ../nbs/06_chatbot.ipynb 1 +# %% ../nbs/04_chatbot.ipynb 1 import ipywidgets as widgets import traitlets from ipywidgets import Textarea, Text, Layout, HBox, Stack, Layout @@ -17,13 +17,13 @@ SystemMessagePromptTemplate, ) from langchain_openai import ChatOpenAI +from .llm import FileModel -# %% ../nbs/06_chatbot.ipynb 5 -class ChatBotModel(HasTraits): +# %% ../nbs/04_chatbot.ipynb 2 +class ChatBotModel(FileModel): - def __init__(self, llm, bot_template="You are playing the role of a tutor/educator", human_template="{input_text}"): + def __init__(self, bot_template="You are playing the role of a tutor/educator", human_template="{input_text}"): super().__init__() - self.llm = llm self.human_template = human_template self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template) self.update_bot_template(bot_template) @@ -37,13 +37,29 @@ def update_human_template(self, human_template): self.human_template = human_template self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template) self.chat_prompt = ChatPromptTemplate.from_messages([self.bot_message_prompt, self.human_message_prompt]) - - def prompt(self, kwargs): + + def prompt(self, input_text): + ret = self.llm.invoke(input_text) + return ret.content + + def prompt_with_template(self, kwargs): ret = self.llm.invoke(self.chat_prompt.format_prompt(**kwargs)) - #ret = self.llm.invoke(self.chat_prompt.format_prompt(input_text=input_text)) return ret.content -# %% ../nbs/06_chatbot.ipynb 8 + def prompt_course_files(self, input_text, k = 4): + if self.db: + try: + docs = self.db.similarity_search(input_text, k=1) + docs_content = " ".join([d.page_content for d in docs]) + self.update_bot_template("You are an educator/tutor. Answer the question based on the following information: {docs}") + ret = self.llm.invoke(self.chat_prompt.format_prompt(docs=docs, input_text=input_text)) + return "\n\n".join([ret.content + '\n\nSourced from ' + docs[0].metadata['source'] + ': \n', docs_content]) + except Exception as e: + return docs[0].page_content + else: + return self.prompt(input_text) + +# %% ../nbs/04_chatbot.ipynb 6 class ChatBotView(widgets.VBox): def __init__(self): @@ -70,7 +86,7 @@ def __init__(self): self.children = (self.chat, self.user_input_and_submit) -# %% ../nbs/06_chatbot.ipynb 10 +# %% ../nbs/04_chatbot.ipynb 8 class ChatBot(ChatBotView): def __init__(self, model): @@ -83,77 +99,5 @@ def __init__(self, model): def on_click(self, change): self.chat.value = self.chat.value + "USER: " + self.user_input.value + '\n\n' self.user_input.value = '' - ret = self.model.prompt(self.user_input.value) + ret = self.model.prompt_course_files(self.user_input.value) self.chat.value = self.chat.value + "CHATBOT: " + ret + '\n\n' - -# %% ../nbs/06_chatbot.ipynb 12 -class StudentChatBot(widgets.VBox): - - #user = traitlets.CUnicode() - #response = traitlets.CUnicode() - #step_by_step = traitlets.Bool() - #metaphor = traitlets.Bool() - #hints = traitlets.Bool() - #guided_questions = traitlets.Bool() - - def __init__(self, chatbot_model): - # If you forget to call the superconstructor on an extended widget - # you will get an AttributeError: object has no attribute '_model_id' - super().__init__() - - self.chat_bot = ChatBot(chatbot_model) - - self.suggestion_buttons = HBox() - self.step_by_step = widgets.Button( - description='Step-by-Step', - button_style='info', - tooltip='Description', - ) - self.metaphor = widgets.Button( - description='Metaphor', - button_style='info', - tooltip='Description', - ) - self.hints = widgets.Button( - description='Hints', - button_style='info', - tooltip='Description', - ) - self.guided_questions = widgets.Button( - description='AI Guided Questions', - button_style='info', - tooltip='Description', - ) - self.suggestion_buttons.children = (self.step_by_step, self.metaphor, self.hints, self.guided_questions) - self.children = (self.chat_bot, self.suggestion_buttons) - -# %% ../nbs/06_chatbot.ipynb 14 -class EducatorChatBot(widgets.VBox): - - #user = traitlets.CUnicode() - #response = traitlets.CUnicode() - #step_by_step = traitlets.Bool() - #metaphor = traitlets.Bool() - #hints = traitlets.Bool() - #guided_questions = traitlets.Bool() - - def __init__(self, chatbot_model): - # If you forget to call the superconstructor on an extended widget - # you will get an AttributeError: object has no attribute '_model_id' - super().__init__() - - self.chat_bot = ChatBot(chatbot_model) - - self.suggestion_buttons = HBox() - self.exam_questions = widgets.Button( - description='Exam Questions', - button_style='info', - tooltip='Description', - ) - self.lesson_plan = widgets.Button( - description='Lesson Plan', - button_style='info', - tooltip='Description', - ) - self.suggestion_buttons.children = (self.exam_questions, self.lesson_plan ) - self.children = (self.chat_bot, self.suggestion_buttons) diff --git a/jupyter_mentor/chatbot_tab.py b/jupyter_mentor/chatbot_tab.py new file mode 100644 index 0000000..614668e --- /dev/null +++ b/jupyter_mentor/chatbot_tab.py @@ -0,0 +1,92 @@ +# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06_chatbot.ipynb. + +# %% auto 0 +__all__ = ['StudentChatBot', 'EducatorChatBot'] + +# %% ../nbs/06_chatbot.ipynb 1 +import ipywidgets as widgets +import traitlets +from ipywidgets import Textarea, Text, Layout, HBox, Stack, Layout +from traitlets import HasTraits +import os +from langchain_openai import ChatOpenAI +from langchain_core.messages import HumanMessage, SystemMessage +from langchain_core.prompts.chat import ( + ChatPromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate, +) +from langchain_openai import ChatOpenAI +from .chatbot import ChatBot, ChatBotModel + +# %% ../nbs/06_chatbot.ipynb 7 +class StudentChatBot(widgets.VBox): + + #user = traitlets.CUnicode() + #response = traitlets.CUnicode() + #step_by_step = traitlets.Bool() + #metaphor = traitlets.Bool() + #hints = traitlets.Bool() + #guided_questions = traitlets.Bool() + + def __init__(self, chatbot_model): + # If you forget to call the superconstructor on an extended widget + # you will get an AttributeError: object has no attribute '_model_id' + super().__init__() + + self.chat_bot = ChatBot(chatbot_model) + + self.suggestion_buttons = HBox() + self.step_by_step = widgets.Button( + description='Step-by-Step', + button_style='info', + tooltip='Description', + ) + self.metaphor = widgets.Button( + description='Metaphor', + button_style='info', + tooltip='Description', + ) + self.hints = widgets.Button( + description='Hints', + button_style='info', + tooltip='Description', + ) + self.guided_questions = widgets.Button( + description='AI Guided Questions', + button_style='info', + tooltip='Description', + ) + self.suggestion_buttons.children = (self.step_by_step, self.metaphor, self.hints, self.guided_questions) + self.children = (self.chat_bot, self.suggestion_buttons) + +# %% ../nbs/06_chatbot.ipynb 9 +class EducatorChatBot(widgets.VBox): + + #user = traitlets.CUnicode() + #response = traitlets.CUnicode() + #step_by_step = traitlets.Bool() + #metaphor = traitlets.Bool() + #hints = traitlets.Bool() + #guided_questions = traitlets.Bool() + + def __init__(self, chatbot_model): + # If you forget to call the superconstructor on an extended widget + # you will get an AttributeError: object has no attribute '_model_id' + super().__init__() + + self.chat_bot = ChatBot(chatbot_model) + + self.suggestion_buttons = HBox() + self.exam_questions = widgets.Button( + description='Exam Questions', + button_style='info', + tooltip='Description', + ) + self.lesson_plan = widgets.Button( + description='Lesson Plan', + button_style='info', + tooltip='Description', + ) + self.suggestion_buttons.children = (self.exam_questions, self.lesson_plan ) + self.children = (self.chat_bot, self.suggestion_buttons) diff --git a/jupyter_mentor/llm.py b/jupyter_mentor/llm.py index a25e944..f209913 100644 --- a/jupyter_mentor/llm.py +++ b/jupyter_mentor/llm.py @@ -1,10 +1,10 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_llm.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_llm.ipynb. # %% auto 0 __all__ = ['LLM', 'FileModel'] -# %% ../nbs/01_llm.ipynb 1 -from traitlets import HasTraits, Unicode +# %% ../nbs/02_llm.ipynb 1 +from traitlets import HasTraits, Unicode, List from langchain_openai import ChatOpenAI import os from langchain.docstore.document import Document @@ -14,7 +14,7 @@ from langchain_community.vectorstores import FAISS from langchain_openai import OpenAIEmbeddings -# %% ../nbs/01_llm.ipynb 2 +# %% ../nbs/02_llm.ipynb 2 class LLM(HasTraits): def __init__(self, filepath='OPENAI_API_KEY'): @@ -25,38 +25,50 @@ def __init__(self, filepath='OPENAI_API_KEY'): os.environ['OPENAI_API_KEY'] = openai_api_key self.llm = ChatOpenAI(model_name="gpt-3.5-turbo") -# %% ../nbs/01_llm.ipynb 4 +# %% ../nbs/02_llm.ipynb 4 class FileModel(LLM): # Define a Unicode string trait select = Unicode() + files = List() - def __init__(self): + def __init__(self, course_file_dir = 'course_files/'): super().__init__() - #self.embeddings = OpenAIEmbeddings() - #doc = Document(page_content='Course') - #self.db = FAISS.from_documents(doc, self.embeddings) + self.course_file_dir = course_file_dir + self.embeddings = OpenAIEmbeddings() + self.db = None def save_content_from_upload(values): for value in values: - with open('course_files/' + value['name'], "wb") as fp: + with open(filepath + value['name'], "wb") as fp: fp.write(value['content']) - def load_text(self, text): + def load_text_to_db(self, text): doc = Document(page_content=text) db = FAISS.from_documents(doc, self.embeddings) - self.db.merge_from(db) + if self.db: + self.db.merge_from(db) + else: + self.db = db - def load_pdf(self, filepath): + def load_pdf_to_db(self, filepath): loader = PyPDFLoader(filepath) pages = loader.load_and_split() db = FAISS.from_documents(pages, self.embeddings) - self.db.merge_from(db) + if self.db: + self.db.merge_from(db) + else: + self.db = db + self.files.append(filepath) - def load_markdown(filepath): + def load_markdown_to_db(filepath): loader = UnstructuredMarkdownLoader(filepath, mode="elements") #mode=elements breaks up the text into chunks doc = loader.load() db = FAISS.from_documents(doc, embeddings) - self.db.merge_from(db) + if self.db: + self.db.merge_from(db) + else: + self.db = db + self.files.append(filepath) def save_content_from_upload(values): for value in values: diff --git a/jupyter_mentor/login.py b/jupyter_mentor/login.py index 2f2e3ae..115d5ab 100644 --- a/jupyter_mentor/login.py +++ b/jupyter_mentor/login.py @@ -9,7 +9,6 @@ from IPython.display import display, clear_output import ipyvuetify as v - # %% ../nbs/01_login.ipynb 3 class Login(VBox): @@ -38,4 +37,3 @@ def __init__(self): self.key_box, # Password label and input box HBox([self.login_button], layout={'justify_content': 'flex-end'}), # Login button aligned to the right ] - diff --git a/nbs/00_main.ipynb b/nbs/00_main.ipynb index 4ad5fd4..5110299 100644 --- a/nbs/00_main.ipynb +++ b/nbs/00_main.ipynb @@ -32,7 +32,7 @@ "from jupyter_mentor.student_profile import StudentProfile\n", "from jupyter_mentor.educator_course_overview import EducatorCourseOverview\n", "from jupyter_mentor.student_course_overview import StudentCourseOverview\n", - "from jupyter_mentor.chatbot import StudentChatBot, EducatorChatBot, ChatBotModel\n", + "from jupyter_mentor.chatbot import ChatBot, ChatBotModel # StudentChatBot, EducatorChatBot\n", "from jupyter_mentor.file_viewer import FileViewerView" ] }, @@ -53,23 +53,6 @@ "import os" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "94f92367-e2c2-4ce5-9c19-48ac5ae357e7", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_openai import ChatOpenAI\n", - "import os\n", - "# read openapi key and set model\n", - "with open('../OPENAI_API_KEY', 'r') as file:\n", - " env_value = file.read().strip()\n", - "\n", - "os.environ['OPENAI_API_KEY'] = env_value\n", - "llm = ChatOpenAI(model_name=\"gpt-3.5-turbo\")" - ] - }, { "cell_type": "code", "execution_count": null, @@ -80,13 +63,12 @@ "#| export\n", "class EducatorMain(widgets.Tab):\n", " \n", - " def __init__(self, llm):\n", + " def __init__(self):\n", " super().__init__()\n", " \n", " # initialize models\n", - " self.llm = llm\n", " # self.model = DataModel(FILENAME)\n", - " self.educator_chatbot_model = ChatBotModel(llm)\n", + " self.educator_chatbot_model = ChatBotModel()\n", "\n", " \n", " # initialize views\n", @@ -94,7 +76,7 @@ " self.second = EducatorProfileView()\n", " self.file_viewer = FileViewerView()\n", " self.third = EducatorCourseOverview(self.file_viewer)\n", - " self.fourth = EducatorChatBot(self.educator_chatbot_model)\n", + " self.fourth = ChatBot(self.educator_chatbot_model)\n", " \n", " # Add tabs to the Tab widget\n", " self.children = (self.first, self.second, self.third, self.fourth)\n", @@ -126,25 +108,9 @@ "execution_count": null, "id": "9ca28623-ed1f-4f1c-b671-462ee582040f", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "25b9cc1a7b794da19f64ac0d5a0d2d57", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "EducatorMain(children=(Login(children=(HTML(value='<h2>Login</h2>'), HBox(children=(Label(value='Username:'), …" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "educator_main = EducatorMain(llm)\n", + "educator_main = EducatorMain()\n", "educator_main" ] }, @@ -162,16 +128,15 @@ " super().__init__()\n", " \n", " # initialize models\n", - " self.llm = llm\n", " # self.model = EducatorModel(FILENAME)\n", - " self.student_chatbot_model = ChatBotModel(llm)\n", + " self.student_chatbot_model = ChatBotModel()\n", "\n", " # initialize views\n", " self.first = Login()\n", " self.second = StudentProfile()\n", " self.file_viewer = FileViewerView()\n", " self.third = StudentCourseOverview(self.file_viewer)\n", - " self.fourth = StudentChatBot(self.student_chatbot_model)\n", + " self.fourth = ChatBot(self.student_chatbot_model)\n", "\n", " \n", " # Add tabs to the Tab widget\n", @@ -203,23 +168,7 @@ "execution_count": null, "id": "36cabf8d-427b-4d24-9c5d-8a6f8e4ab6b5", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ae14a13795ac4ffb9105d9c90b859bca", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "StudentMain(children=(Login(children=(HTML(value='<h2>Login</h2>'), HBox(children=(Label(value='Username:'), T…" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "student_main = StudentMain()\n", "student_main" diff --git a/nbs/01_llm.ipynb b/nbs/02_llm.ipynb similarity index 67% rename from nbs/01_llm.ipynb rename to nbs/02_llm.ipynb index c53d21e..3422a14 100644 --- a/nbs/01_llm.ipynb +++ b/nbs/02_llm.ipynb @@ -18,7 +18,7 @@ "outputs": [], "source": [ "#|export\n", - "from traitlets import HasTraits, Unicode\n", + "from traitlets import HasTraits, Unicode, List\n", "from langchain_openai import ChatOpenAI\n", "import os\n", "from langchain.docstore.document import Document\n", @@ -77,38 +77,50 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#|export\n", "class FileModel(LLM):\n", " # Define a Unicode string trait\n", " select = Unicode()\n", + " files = List()\n", "\n", - " def __init__(self):\n", + " def __init__(self, course_file_dir = 'course_files/'):\n", " super().__init__()\n", - " #self.embeddings = OpenAIEmbeddings()\n", - " #doc = Document(page_content='Course')\n", - " #self.db = FAISS.from_documents(doc, self.embeddings)\n", + " self.course_file_dir = course_file_dir\n", + " self.embeddings = OpenAIEmbeddings()\n", + " self.db = None\n", "\n", " def save_content_from_upload(values):\n", " for value in values:\n", - " with open('course_files/' + value['name'], \"wb\") as fp:\n", + " with open(filepath + value['name'], \"wb\") as fp:\n", " fp.write(value['content'])\n", "\n", - " def load_text(self, text):\n", + " def load_text_to_db(self, text):\n", " doc = Document(page_content=text)\n", " db = FAISS.from_documents(doc, self.embeddings)\n", - " self.db.merge_from(db)\n", + " if self.db:\n", + " self.db.merge_from(db)\n", + " else: \n", + " self.db = db\n", "\n", - " def load_pdf(self, filepath):\n", + " def load_pdf_to_db(self, filepath):\n", " loader = PyPDFLoader(filepath) \n", " pages = loader.load_and_split()\n", " db = FAISS.from_documents(pages, self.embeddings)\n", - " self.db.merge_from(db)\n", + " if self.db:\n", + " self.db.merge_from(db)\n", + " else: \n", + " self.db = db\n", + " self.files.append(filepath)\n", "\n", - " def load_markdown(filepath):\n", + " def load_markdown_to_db(filepath):\n", " loader = UnstructuredMarkdownLoader(filepath, mode=\"elements\") #mode=elements breaks up the text into chunks\n", " doc = loader.load()\n", " db = FAISS.from_documents(doc, embeddings)\n", - " self.db.merge_from(db)\n", + " if self.db:\n", + " self.db.merge_from(db)\n", + " else: \n", + " self.db = db\n", + " self.files.append(filepath)\n", "\n", " def save_content_from_upload(values):\n", " for value in values:\n", @@ -124,7 +136,7 @@ "outputs": [], "source": [ "file_model = FileModel()\n", - "#file_model.load_pdf(\"STP 420 spring 2024 course syllabus.pdf\") #file as input" + "file_model.load_pdf_to_db(\"course_files/STP 420 spring 2024 course syllabus.pdf\") #file as input" ] }, { @@ -132,7 +144,18 @@ "execution_count": null, "id": "ea39baff-3a1a-4a9b-999c-ef433ea2897a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/opt/homebrew/Caskroom/miniconda/base/envs/jupyter-mentor/lib/python3.11/site-packages/nbdev/export.py:54: UserWarning: Notebook '/Users/nabrewer/Repos/Jupyter4Science/jupyter-mentor/nbs/00_main.ipynb' uses `#|export` without `#|default_exp` cell.\n", + "Note nbdev2 no longer supports nbdev1 syntax. Run `nbdev_migrate` to upgrade.\n", + "See https://nbdev.fast.ai/getting_started.html for more information.\n", + " warn(f\"Notebook '{nbname}' uses `#|export` without `#|default_exp` cell.\\n\"\n" + ] + } + ], "source": [ "#|hide\n", "import nbdev; nbdev.nbdev_export()" @@ -141,7 +164,7 @@ { "cell_type": "code", "execution_count": null, - "id": "980459d7-b53c-4773-b528-e7c24f338ec2", + "id": "a735546f-3dae-4a4a-b993-37d16faca5ce", "metadata": {}, "outputs": [], "source": [] diff --git a/nbs/04_chatbot.ipynb b/nbs/04_chatbot.ipynb new file mode 100644 index 0000000..576216d --- /dev/null +++ b/nbs/04_chatbot.ipynb @@ -0,0 +1,238 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2fb38978-5490-48f8-8a5d-af97e10ea493", + "metadata": {}, + "outputs": [], + "source": [ + "#| default_exp chatbot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1321e10-d2e8-4cbb-bfa2-8b9e57dcdb24", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "import ipywidgets as widgets\n", + "import traitlets\n", + "from ipywidgets import Textarea, Text, Layout, HBox, Stack, Layout\n", + "from traitlets import HasTraits\n", + "import os\n", + "from langchain_openai import ChatOpenAI\n", + "from langchain_core.messages import HumanMessage, SystemMessage\n", + "from langchain_core.prompts.chat import (\n", + " ChatPromptTemplate,\n", + " HumanMessagePromptTemplate,\n", + " SystemMessagePromptTemplate,\n", + ")\n", + "from langchain_openai import ChatOpenAI\n", + "from jupyter_mentor.llm import FileModel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e51169c6-346e-4b2c-94d0-dd529156e3ba", + "metadata": {}, + "outputs": [], + "source": [ + "#|export\n", + "class ChatBotModel(FileModel):\n", + "\n", + " def __init__(self, bot_template=\"You are playing the role of a tutor/educator\", human_template=\"{input_text}\"):\n", + " super().__init__()\n", + " self.human_template = human_template\n", + " self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template)\n", + " self.update_bot_template(bot_template)\n", + "\n", + " def update_bot_template(self, bot_template):\n", + " self.bot_template = bot_template\n", + " self.bot_message_prompt = SystemMessagePromptTemplate.from_template(self.bot_template)\n", + " self.chat_prompt = ChatPromptTemplate.from_messages([self.bot_message_prompt, self.human_message_prompt])\n", + "\n", + " def update_human_template(self, human_template):\n", + " self.human_template = human_template\n", + " self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template)\n", + " self.chat_prompt = ChatPromptTemplate.from_messages([self.bot_message_prompt, self.human_message_prompt]) \n", + "\n", + " def prompt(self, input_text):\n", + " ret = self.llm.invoke(input_text)\n", + " return ret.content\n", + " \n", + " def prompt_with_template(self, kwargs):\n", + " ret = self.llm.invoke(self.chat_prompt.format_prompt(**kwargs))\n", + " return ret.content\n", + "\n", + " def prompt_course_files(self, input_text, k = 4):\n", + " if self.db:\n", + " try:\n", + " docs = self.db.similarity_search(input_text, k=1)\n", + " docs_content = \" \".join([d.page_content for d in docs])\n", + " self.update_bot_template(\"You are an educator/tutor. Answer the question based on the following information: {docs}\")\n", + " ret = self.llm.invoke(self.chat_prompt.format_prompt(docs=docs, input_text=input_text)) \n", + " return \"\\n\\n\".join([ret.content + '\\n\\nSourced from ' + docs[0].metadata['source'] + ': \\n', docs_content])\n", + " except Exception as e: \n", + " return docs[0].page_content\n", + " else:\n", + " return self.prompt(input_text)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61e4604a-0885-49a8-a8eb-36994a8fddbb", + "metadata": {}, + "outputs": [], + "source": [ + "chatbot_model = ChatBotModel()\n", + "#chatbot_model.prompt('hello')\n", + "#echatbot_model.prompt_with_template({'input_text': 'hi'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6428f443-ca45-47ae-80b3-755f010c89e6", + "metadata": {}, + "outputs": [], + "source": [ + "#chatbot_model.load_pdf_to_db(\"course_files/STP 420 spring 2024 course syllabus.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eb37adf-e4d1-45bb-a481-9123d23be622", + "metadata": {}, + "outputs": [], + "source": [ + "#chatbot_model.prompt_course_files(\"what is this course about?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "950062ca-2dda-4c7f-a00e-fc0fde477b0a", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "class ChatBotView(widgets.VBox):\n", + " \n", + " def __init__(self):\n", + " # If you forget to call the superconstructor on an extended widget\n", + " # you will get an AttributeError: object has no attribute '_model_id'\n", + " super().__init__()\n", + "\n", + " self.chat = Textarea(\n", + " disabled = True,\n", + " layout=Layout(width='90%', height='400px')\n", + " )\n", + " self.user_input_and_submit = HBox()\n", + " self.user_input = widgets.Text(\n", + " placeholder='Message AI chatbot...',\n", + " #layout=Layout(width='100%')\n", + " )\n", + " self.submit_button = widgets.Button(\n", + " value=False,\n", + " disabled=False,\n", + " button_style='success',\n", + " icon='arrow-circle-right' \n", + " )\n", + " self.user_input_and_submit.children = (self.user_input, self.submit_button)\n", + "\n", + " self.children = (self.chat, self.user_input_and_submit) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a72c24e-e38a-4039-984a-ba35048a11d5", + "metadata": {}, + "outputs": [], + "source": [ + "chatbot_view = ChatBotView()\n", + "chatbot_view" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbdc999b-878e-4a25-90c0-9ec3740eb609", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "class ChatBot(ChatBotView):\n", + "\n", + " def __init__(self, model):\n", + " # If you forget to call the superconstructor on an extended widget\n", + " # you will get an AttributeError: object has no attribute '_model_id'\n", + " super().__init__()\n", + " self.submit_button.on_click(self.on_click)\n", + " self.model =model\n", + "\n", + " def on_click(self, change):\n", + " self.chat.value = self.chat.value + \"USER: \" + self.user_input.value + '\\n\\n'\n", + " self.user_input.value = ''\n", + " ret = self.model.prompt_course_files(self.user_input.value)\n", + " self.chat.value = self.chat.value + \"CHATBOT: \" + ret + '\\n\\n'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7f53d71-bebb-43d8-aafe-66396cf2bb66", + "metadata": {}, + "outputs": [], + "source": [ + "chatbot = ChatBot(chatbot_model)\n", + "chatbot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3796974-098d-417a-ad2f-f5cc9a95bbcd", + "metadata": {}, + "outputs": [], + "source": [ + "file_model = FileModel()\n", + "file_model.load_pdf_to_db(\"course_files/STP 420 spring 2024 course syllabus.pdf\") #file as input" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ac4fdf23-fb71-411a-bff7-0fd3504d230c", + "metadata": {}, + "outputs": [], + "source": [ + "#|hide\n", + "import nbdev; nbdev.nbdev_export()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61eb7eed-5429-43d4-bf69-d0647fc33d5d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python3", + "language": "python", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/nbs/06_chatbot.ipynb b/nbs/06_chatbot.ipynb index 29f15b3..70ac595 100644 --- a/nbs/06_chatbot.ipynb +++ b/nbs/06_chatbot.ipynb @@ -7,7 +7,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| default_exp chatbot" + "#| default_exp chatbot_tab" ] }, { @@ -30,64 +30,20 @@ " HumanMessagePromptTemplate,\n", " SystemMessagePromptTemplate,\n", ")\n", - "from langchain_openai import ChatOpenAI" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a57225dd-d6de-43ee-982f-c1467203dcd3", - "metadata": {}, - "outputs": [], - "source": [ - "# read openapi key and set model\n", - "with open('OPENAI_API_KEY', 'r') as file:\n", - " openai_api_key = file.read().strip()\n", - "\n", - "llm = ChatOpenAI(openai_api_key = openai_api_key, model_name=\"gpt-3.5-turbo\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a553a55b-ddce-4c09-b5d5-f5cac9a6fbd9", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "da85d2db19e6493bb04a95078a493d0d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "RadioButtons(description='Prompt Options:', options={'Open Prompt': '{input_text}', 'Metaphor': \"I'm having un…" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "student_template_options = widgets.RadioButtons(\n", - " options={'Open Prompt': '{input_text}',\n", - " 'Metaphor':\"I'm having trouble understanding {input_text}. Explain it as a metaphor.\"},\n", - " description='Prompt Options:',\n", - ")\n", - "student_template_options" + "from langchain_openai import ChatOpenAI\n", + "from jupyter_mentor.chatbot import ChatBot, ChatBotModel" ] }, { "cell_type": "code", "execution_count": null, - "id": "5a2e9467-8af1-4f42-83ff-27c77d9133ef", + "id": "246e149a-2461-4b40-8efb-0a476be12b80", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\"I'm having understanding {input_text}. Explain it as a metaphor.\"" + "'Hello! How can I assist you today?'" ] }, "execution_count": null, @@ -96,22 +52,25 @@ } ], "source": [ - "student_template_options.value" + "from jupyter_mentor.chatbot import ChatBotModel\n", + "chatbot_model = ChatBotModel()\n", + "chatbot_model.prompt('hello')\n", + "\n", + "## Extend ChatBotModel below" ] }, { "cell_type": "code", "execution_count": null, - "id": "964405d5-115e-47a8-88ff-8746627ee0b1", + "id": "d16df572-7edc-401c-beb4-4d5f53b28bae", "metadata": {}, "outputs": [], "source": [ - "#| export\n", - "class ChatBotModel(HasTraits):\n", "\n", - " def __init__(self, llm, bot_template=\"You are playing the role of a tutor/educator\", human_template=\"{input_text}\"):\n", + "class ChatBotModel(FileModel):\n", + "\n", + " def __init__(self, bot_template=\"You are playing the role of a tutor/educator\", human_template=\"{input_text}\"):\n", " super().__init__()\n", - " self.llm = llm\n", " self.human_template = human_template\n", " self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template)\n", " self.update_bot_template(bot_template)\n", @@ -125,112 +84,65 @@ " self.human_template = human_template\n", " self.human_message_prompt = HumanMessagePromptTemplate.from_template(self.human_template)\n", " self.chat_prompt = ChatPromptTemplate.from_messages([self.bot_message_prompt, self.human_message_prompt]) \n", - " \n", - " def prompt(self, kwargs):\n", + "\n", + " def prompt(self, input_text):\n", + " ret = self.llm.invoke(input_text)\n", + " return ret.content\n", + " \n", + " def prompt_with_template(self, kwargs):\n", " ret = self.llm.invoke(self.chat_prompt.format_prompt(**kwargs))\n", - " #ret = self.llm.invoke(self.chat_prompt.format_prompt(input_text=input_text))\n", " return ret.content" ] }, { "cell_type": "code", "execution_count": null, - "id": "1d24447e-7e51-4b99-a1c3-d35e59b7a998", - "metadata": {}, - "outputs": [], - "source": [ - "chatbot_model = ChatBotModel(llm)\n", - "chatbot_model.prompt({'input_text': 'hi'})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "907bbba3-e59c-4245-a673-3639916cdd64", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "190256e8-d786-4f6e-963e-7cd4a91a9d82", + "id": "a57225dd-d6de-43ee-982f-c1467203dcd3", "metadata": {}, "outputs": [], "source": [ - "#| export\n", - "\n", - "class ChatBotView(widgets.VBox):\n", - " \n", - " def __init__(self):\n", - " # If you forget to call the superconstructor on an extended widget\n", - " # you will get an AttributeError: object has no attribute '_model_id'\n", - " super().__init__()\n", - "\n", - " self.chat = Textarea(\n", - " disabled = True,\n", - " layout=Layout(width='90%', height='400px')\n", - " )\n", - " self.user_input_and_submit = HBox()\n", - " self.user_input = widgets.Text(\n", - " placeholder='Message AI chatbot...',\n", - " #layout=Layout(width='100%')\n", - " )\n", - " self.submit_button = widgets.Button(\n", - " value=False,\n", - " disabled=False,\n", - " button_style='success',\n", - " icon='arrow-circle-right' \n", - " )\n", - " self.user_input_and_submit.children = (self.user_input, self.submit_button)\n", + "# read openapi key and set model\n", + "with open('OPENAI_API_KEY', 'r') as file:\n", + " openai_api_key = file.read().strip()\n", "\n", - " self.children = (self.chat, self.user_input_and_submit) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8f6730f-d2ab-4d2f-b70a-77a4a4ca3e0b", - "metadata": {}, - "outputs": [], - "source": [ - "chatbot_view = ChatBotView()\n", - "chatbot_view" + "llm = ChatOpenAI(openai_api_key = openai_api_key, model_name=\"gpt-3.5-turbo\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "c637bb15-d20d-4440-936e-8956ac712494", + "id": "a553a55b-ddce-4c09-b5d5-f5cac9a6fbd9", "metadata": {}, "outputs": [], "source": [ - "#| export\n", - "class ChatBot(ChatBotView):\n", - "\n", - " def __init__(self, model):\n", - " # If you forget to call the superconstructor on an extended widget\n", - " # you will get an AttributeError: object has no attribute '_model_id'\n", - " super().__init__()\n", - " self.submit_button.on_click(self.on_click)\n", - " self.model =model\n", - "\n", - " def on_click(self, change):\n", - " self.chat.value = self.chat.value + \"USER: \" + self.user_input.value + '\\n\\n'\n", - " self.user_input.value = ''\n", - " ret = self.model.prompt(self.user_input.value)\n", - " self.chat.value = self.chat.value + \"CHATBOT: \" + ret + '\\n\\n'" + "student_template_options = widgets.RadioButtons(\n", + " options={'Open Prompt': '{input_text}',\n", + " 'Metaphor':\"I'm having trouble understanding {input_text}. Explain it as a metaphor.\"},\n", + " description='Prompt Options:',\n", + ")\n", + "student_template_options" ] }, { "cell_type": "code", "execution_count": null, - "id": "78f6b62f-0c95-4534-b53d-de957d72284d", + "id": "5a2e9467-8af1-4f42-83ff-27c77d9133ef", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'student_template_options' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mstudent_template_options\u001b[49m\u001b[38;5;241m.\u001b[39mvalue\n", + "\u001b[0;31mNameError\u001b[0m: name 'student_template_options' is not defined" + ] + } + ], "source": [ - "chatbot = ChatBot(chatbot_model)\n", - "chatbot" + "student_template_options.value" ] }, { @@ -287,7 +199,23 @@ "execution_count": null, "id": "b4e598d6-0f71-423e-afa6-723d52a68926", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "77cb790799a34780b325d893189111e7", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "StudentChatBot(children=(ChatBot(children=(Textarea(value='', disabled=True, layout=Layout(height='400px', wid…" + ] + }, + "execution_count": null, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "student_chatbot = StudentChatBot(chatbot_model)\n", "student_chatbot" diff --git a/nbs/2017-01-29-policies.md b/nbs/course_files/2017-01-29-policies.md similarity index 100% rename from nbs/2017-01-29-policies.md rename to nbs/course_files/2017-01-29-policies.md diff --git a/nbs/STP 420 spring 2024 course syllabus.pdf b/nbs/course_files/STP 420 spring 2024 course syllabus.pdf similarity index 100% rename from nbs/STP 420 spring 2024 course syllabus.pdf rename to nbs/course_files/STP 420 spring 2024 course syllabus.pdf diff --git a/nbs/course_files/hussey2015.pdf b/nbs/course_files/hussey2015.pdf new file mode 100644 index 0000000..5f2fcd0 Binary files /dev/null and b/nbs/course_files/hussey2015.pdf differ