File size: 12,334 Bytes
e2769d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2bf3313
e2769d5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
{
 "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://<token>@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
}