{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Blocher Basic Interaction Exercise\n", "\n", "In this notebook, we create a minimized version of the current app to be deployed onto Huggingface Space, which has the following components: \n", "\n", "- Student interface: \n", " A chat interface where students interact with the chatbot that has had the instructor-designed prompt given to it but they cannot see the prompt. At the end of the conversation, entire convo is made available as downloadable JSON such that students can download it and turn it in to Brightspace.\n", "\n", "- Instructor interface:\n", " A file upload and check content interface which allows instructor to involve files in the HuggingFace Space - i.e. copying and pasting hidden prompt in a txt file or .py file. The prompt will serve as the input to the chatbot, and is not visible by students. " ] }, { "cell_type": "code", # "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| default_exp BasicInteraction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we'll start by loading our own libraries. Keep in mind that if you're on Colab, you need to replace \"token\" below with your GitHub token." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# run this code only if you're using Google Colab\n", "#! pip install pip install git+https://@github.com/vanderbilt-data-science/lo-achievement.git" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## run this code only if you're local and developing\n", "import os, sys\n", "\n", "# get parent of current working directory\n", "parent_dir = os.path.dirname(os.getcwd())\n", "\n", "#append to path\n", "sys.path.append(parent_dir)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| export\n", "import os\n", "import gradio as gr\n", "import pandas as pd\n", "from functools import partial\n", "from ai_classroom_suite.UIBaseComponents import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interface Helpers and Functionality\n", "\n", "In the next section, we'll create some helper functions to make sure we're able to create the interface we want, and that it behaves nicely. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Student Interface Chatbot Functions ###" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| export\n", "# overwrites the original method since we don't deal with any vector stores here\n", "def get_tutor_reply(chat_tutor):\n", " chat_tutor.get_tutor_reply()\n", " return gr.update(value=\"\", interactive=True), chat_tutor.conversation_memory, chat_tutor\n", "\n", "def get_conversation_history(chat_tutor):\n", " return chat_tutor.conversation_memory, chat_tutor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Instructor Interface Helper Functions ###\n", "\n", "Instructor needs to upload a .txt or .py file as the secret prompt provided to the chatbot.\n", "\n", "- **get_instructor_prompt(fileobj)** receives the instructor uploaded file and return the content of the file. \n", "\n", "- **embed_prompt(prompt)** receives a prompt and update the ``SECRET_PROMPT`` secret and the learning objectives of the chatbot with this prompt. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| export\n", "def get_instructor_prompt(fileobj):\n", " # get file path\n", " file_path = fileobj.name\n", " # read file content\n", " with open(file_path, \"r\") as f: \n", " instructor_prompt = f.read()\n", " return instructor_prompt\n", "\n", "def embed_prompt(prompt, chat_tutor):\n", " # update secret\n", " os.environ[\"SECRET_PROMPT\"] = prompt\n", " # update tutor\n", " chat_tutor.learning_objectives = prompt\n", " return os.environ.get(\"SECRET_PROMPT\"), chat_tutor" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# The User Interface\n", "\n", "Below, we put all of this information together with the actual formatting of the user interface." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Note for Instructors ###\n", "\n", "You need to provide an OpenAI API Key to initialize the model. If you haven't created one already, visit [platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys) to sign up for an account and get your personal API key. \n", "\n", "To permanently set the key, in the hosted app on Huggingface Space, go to ``Settings -> Variables and Secrets -> Secrets``, then replace ``OPENAI_API_KEY`` value with your key." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#| export\n", "with gr.Blocks() as BasicInteractionDemo:\n", " #initialize tutor (with state)\n", " study_tutor = gr.State(SlightlyDelusionalTutor())\n", "\n", " # Student interface\n", " with gr.Tab(\"For Students\"): \n", " \n", " # Chatbot interface\n", " gr.Markdown(\"\"\"\n", " ## Chat with the Model\n", " Description here\n", " \"\"\")\n", " \n", " with gr.Row(equal_height=True):\n", " with gr.Column(scale=2):\n", " chatbot = gr.Chatbot()\n", " with gr.Row():\n", " user_chat_input = gr.Textbox(label=\"User input\", scale=9)\n", " user_chat_submit = gr.Button(\"Ask/answer model\", scale=1)\n", "\n", " # First add user's message to the conversation history\n", " # Then get reply from the tutor and add that to the conversation history\n", " user_chat_submit.click(\n", " fn = add_user_message, inputs = [user_chat_input, study_tutor], outputs = [user_chat_input, chatbot, study_tutor], queue=False\n", " ).then(\n", " fn = get_tutor_reply, inputs = [study_tutor], outputs = [user_chat_input, chatbot, study_tutor], queue=True\n", " )\n", " \n", " # Testing the chat history storage, can be deleted at deployment\n", " with gr.Blocks():\n", " test_btn = gr.Button(\"View your chat history\")\n", " chat_history = gr.JSON(label = \"conversation history\")\n", " test_btn.click(get_conversation_history, inputs=[study_tutor], outputs=[chat_history, study_tutor])\n", "\n", " # Download conversation history file\n", " with gr.Blocks():\n", " gr.Markdown(\"\"\"\n", " ## Export Your Chat History\n", " Export your chat history as a .json, .txt, or .csv file\n", " \"\"\")\n", " with gr.Row():\n", " export_dialogue_button_json = gr.Button(\"JSON\")\n", " export_dialogue_button_txt = gr.Button(\"TXT\")\n", " export_dialogue_button_csv = gr.Button(\"CSV\")\n", " \n", " file_download = gr.Files(label=\"Download here\", file_types=['.json', '.txt', '.csv'], type=\"file\", visible=False)\n", " \n", " export_dialogue_button_json.click(save_json, study_tutor, file_download, show_progress=True)\n", " export_dialogue_button_txt.click(save_txt, study_tutor, file_download, show_progress=True)\n", " export_dialogue_button_csv.click(save_csv, study_tutor, file_download, show_progress=True)\n", "\n", "\n", " # Instructor interface\n", " with gr.Tab(\"Instructor Only\"):\n", " # API Authentication functionality\n", " # Instead of ask students to provide key, the key is now provided by the instructor. \n", " api_input = gr.Textbox(show_label=False, type=\"password\", visible=False, value=os.environ.get(\"OPENAI_API_KEY\"))\n", "\n", " # Upload secret prompt functionality\n", " # The instructor will provide a secret prompt/persona to the tutor\n", " with gr.Blocks():\n", " # testing purpose, change visible to False at deployment\n", " view_secret = gr.Textbox(label=\"Current secret prompt\", value=os.environ.get(\"SECRET_PROMPT\"), visible=False)\n", "\n", " # Prompt instructor to upload the secret file\n", " file_input = gr.File(label=\"Load a .txt or .py file\", file_types=['.py', '.txt'], type=\"file\", elem_classes=\"short-height\")\n", " \n", " # Verify prompt content\n", " instructor_prompt = gr.Textbox(label=\"Verify your prompt content\", visible=True)\n", " file_input.upload(fn=get_instructor_prompt, inputs=file_input, outputs=instructor_prompt)\n", "\n", " # Placeholders components\n", " text_input_none = gr.Textbox(visible=False)\n", " file_input_none = gr.File(visible=False)\n", " instructor_input_none = gr.TextArea(visible=False)\n", " learning_objectives_none = gr.Textbox(visible=False)\n", "\n", " # Set the secret prompt in this session and embed it to the study tutor\n", " prompt_submit_btn = gr.Button(\"Submit\")\n", " prompt_submit_btn.click(\n", " fn=embed_prompt, inputs=[instructor_prompt, study_tutor], outputs=[view_secret, study_tutor]\n", " ).then(\n", " fn=create_reference_store, \n", " inputs=[study_tutor, prompt_submit_btn, instructor_prompt, file_input_none, instructor_input_none, api_input, instructor_prompt],\n", " outputs=[study_tutor, prompt_submit_btn]\n", " )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "BasicInteractionDemo.queue().launch(server_name='0.0.0.0', server_port=7860)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A little helper in case your ports are open and you just want to close them all. If this doesn't work, restart your IDE." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gr.close_all()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# TODO: The instructor prompt is now only set in session if not go to Settings/secret, \n", "# to \"permanently\" set the secret prompt not seen by the students who use this space, \n", "# one possible way is to recreate the instructor interface in another space, \n", "# and load it here to chain with the student interface. \n", " \n", "# TODO: Currently, the instructor prompt is handled as text input and stored in the vector store (and in the learning objective),\n", "# which means the tutor now is still a question-answering tutor who viewed the prompt as context (but not really acting based on it). \n", "# We need to find a way to provide the prompt directly to the model and set its status. " ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 2 }