From 056a0f9f1c1bfdd5226d201170ac87185313c88a Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sat, 7 Jan 2023 23:20:04 +0000 Subject: [PATCH 01/21] initial binder example --- notebooks/example/README.md | 6 ++ notebooks/example/example.ipynb | 119 +++++++++++++++++++++++++++++ notebooks/example/requirements.txt | 1 + 3 files changed, 126 insertions(+) create mode 100644 notebooks/example/README.md create mode 100644 notebooks/example/example.ipynb create mode 100644 notebooks/example/requirements.txt diff --git a/notebooks/example/README.md b/notebooks/example/README.md new file mode 100644 index 00000000..e847d343 --- /dev/null +++ b/notebooks/example/README.md @@ -0,0 +1,6 @@ +# Example Notebook + +[![Binder](http://mybinder.org/badge_logo.svg)](http://mybinder.org/v2/gh/andrewm4894/Open-Assistant/blob/example-notebook/notebooks/example/) + +This folder contains an example reference notebook structure and approach for +this project. Please try and follow this structure as closely as possible. diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb new file mode 100644 index 00000000..ff0472b3 --- /dev/null +++ b/notebooks/example/example.ipynb @@ -0,0 +1,119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/andrewm4894/Open-Assistant/blob/example-notebook/notebooks/example/example.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# uncomment below cell to install required packages if running in Google Colab.\n", + "#!pip install transformers" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example Notebook" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Try to add a markdown section to the notebook that explains what the notebook is about and what it does. This will help people understand what the notebook is for and how to use it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# import required packages\n", + "from transformers import pipeline" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use Headings\n", + "\n", + "(it will help with link sharing to specific sections of the notebook)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make fancy markdown cells if you want." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Do cool stuff here" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Binder" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We want to have every notebook folder be easily runnable on Binder. This means that every notebook folder should have a `requirements.txt` file that contains all the dependencies for the notebook. This is so that Binder can install all the dependencies for the notebook when it is run.\n", + "\n", + "If you have more complex dependencies then there are a number of ways to add additional configuration files in the folder that Binder will use to install the dependencies. See the [Binder documentation](https://mybinder.readthedocs.io/en/latest/config_files.html) for more information." + ] + } + ], + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "3ad933181bd8a04b432d3370b9dc3b0662ad032c4dfaa4e4f1596c548f763858" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/example/requirements.txt b/notebooks/example/requirements.txt new file mode 100644 index 00000000..976a2b1f --- /dev/null +++ b/notebooks/example/requirements.txt @@ -0,0 +1 @@ +transformers From 834d393950d26730b2d5613c37b8d9e6c2d04cec Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sat, 7 Jan 2023 23:43:57 +0000 Subject: [PATCH 02/21] dev --- notebooks/example/README.md | 2 +- notebooks/example/example.ipynb | 59 +++++++++++++++++++++++++++--- notebooks/example/requirements.txt | 1 - 3 files changed, 54 insertions(+), 8 deletions(-) delete mode 100644 notebooks/example/requirements.txt diff --git a/notebooks/example/README.md b/notebooks/example/README.md index e847d343..b2d2eb22 100644 --- a/notebooks/example/README.md +++ b/notebooks/example/README.md @@ -1,6 +1,6 @@ # Example Notebook -[![Binder](http://mybinder.org/badge_logo.svg)](http://mybinder.org/v2/gh/andrewm4894/Open-Assistant/blob/example-notebook/notebooks/example/) +[![Binder](http://mybinder.org/badge_logo.svg)](http://mybinder.org/v2/gh/andrewm4894/Open-Assistant/example-notebook?urlpath=/notebooks/example/example.ipynb) This folder contains an example reference notebook structure and approach for this project. Please try and follow this structure as closely as possible. diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index ff0472b3..de199a60 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -9,12 +9,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting transformers\n", + " Downloading transformers-4.25.1-py3-none-any.whl (5.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.8/5.8 MB\u001b[0m \u001b[31m51.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n", + "\u001b[?25hCollecting huggingface-hub<1.0,>=0.10.0\n", + " Downloading huggingface_hub-0.11.1-py3-none-any.whl (182 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m182.4/182.4 kB\u001b[0m \u001b[31m10.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: packaging>=20.0 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (22.0)\n", + "Collecting regex!=2019.12.17\n", + " Downloading regex-2022.10.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (770 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m770.5/770.5 kB\u001b[0m \u001b[31m29.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting tokenizers!=0.11.3,<0.14,>=0.11.1\n", + " Downloading tokenizers-0.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.6/7.6 MB\u001b[0m \u001b[31m65.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m\n", + "\u001b[?25hCollecting filelock\n", + " Downloading filelock-3.9.0-py3-none-any.whl (9.7 kB)\n", + "Collecting tqdm>=4.27\n", + " Downloading tqdm-4.64.1-py2.py3-none-any.whl (78 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.5/78.5 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pyyaml>=5.1 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (6.0)\n", + "Requirement already satisfied: numpy>=1.17 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (1.24.0)\n", + "Requirement already satisfied: requests in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (2.28.1)\n", + "Requirement already satisfied: typing-extensions>=3.7.4.3 in /home/codespace/.local/lib/python3.10/site-packages (from huggingface-hub<1.0,>=0.10.0->transformers) (4.4.0)\n", + "Requirement already satisfied: charset-normalizer<3,>=2 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (2.1.1)\n", + "Requirement already satisfied: idna<4,>=2.5 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (3.4)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (2022.12.7)\n", + "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (1.26.13)\n", + "Installing collected packages: tokenizers, tqdm, regex, filelock, huggingface-hub, transformers\n", + "Successfully installed filelock-3.9.0 huggingface-hub-0.11.1 regex-2022.10.31 tokenizers-0.13.2 tqdm-4.64.1 transformers-4.25.1\n" + ] + } + ], "source": [ "# uncomment below cell to install required packages if running in Google Colab.\n", - "#!pip install transformers" + "!pip install transformers\n", + "# if needed you can git clone the repo\n", + "!git clone https://github.com/andrewm4894/Open-Assistant.git\n", + "!cd Open-Assistant/notebooks/example" ] }, { @@ -35,9 +73,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/codespace/.python/current/lib/python3.10/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], "source": [ "# import required packages\n", "from transformers import pipeline" @@ -63,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ diff --git a/notebooks/example/requirements.txt b/notebooks/example/requirements.txt deleted file mode 100644 index 976a2b1f..00000000 --- a/notebooks/example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -transformers From 04c7ea031f7c6cb23b3fec7c0a801d7da36e6fa6 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:06:17 +0000 Subject: [PATCH 03/21] dev --- notebooks/example/data/data.csv | 3 +++ notebooks/example/example.ipynb | 33 ++++++++---------------------- notebooks/example/requirements.txt | 1 + 3 files changed, 13 insertions(+), 24 deletions(-) create mode 100644 notebooks/example/data/data.csv create mode 100644 notebooks/example/requirements.txt diff --git a/notebooks/example/data/data.csv b/notebooks/example/data/data.csv new file mode 100644 index 00000000..126a03bb --- /dev/null +++ b/notebooks/example/data/data.csv @@ -0,0 +1,3 @@ +row,text,label +1,some example data,1 +2,some more data,0 diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index de199a60..0d8e1fcb 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -48,11 +48,12 @@ } ], "source": [ - "# uncomment below cell to install required packages if running in Google Colab.\n", - "!pip install transformers\n", - "# if needed you can git clone the repo\n", - "!git clone https://github.com/andrewm4894/Open-Assistant.git\n", - "!cd Open-Assistant/notebooks/example" + "# uncomment and run below lines to set up if running in colab\n", + "#%%bash\n", + "# git clone https://github.com/andrewm4894/Open-Assistant.git\n", + "# git checkout example-notebook\n", + "# cd Open-Assistant/notebooks/example\n", + "# pip install -r requirements.txt" ] }, { @@ -87,6 +88,7 @@ ], "source": [ "# import required packages\n", + "import pandas as pd\n", "from transformers import pipeline" ] }, @@ -114,25 +116,8 @@ "metadata": {}, "outputs": [], "source": [ - "# Do cool stuff here" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Binder" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to have every notebook folder be easily runnable on Binder. This means that every notebook folder should have a `requirements.txt` file that contains all the dependencies for the notebook. This is so that Binder can install all the dependencies for the notebook when it is run.\n", - "\n", - "If you have more complex dependencies then there are a number of ways to add additional configuration files in the folder that Binder will use to install the dependencies. See the [Binder documentation](https://mybinder.readthedocs.io/en/latest/config_files.html) for more information." + "# Do cool stuff here\n", + "df = pd.read_csv(\"data/data.csv\")" ] } ], diff --git a/notebooks/example/requirements.txt b/notebooks/example/requirements.txt new file mode 100644 index 00000000..976a2b1f --- /dev/null +++ b/notebooks/example/requirements.txt @@ -0,0 +1 @@ +transformers From 4953c698aec23df078470d138c107d2cee0874ac Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:09:13 +0000 Subject: [PATCH 04/21] dev --- notebooks/example/example.ipynb | 122 +++++++++++++++++--------------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index 0d8e1fcb..d65c2e51 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -9,51 +9,15 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting transformers\n", - " Downloading transformers-4.25.1-py3-none-any.whl (5.8 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.8/5.8 MB\u001b[0m \u001b[31m51.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n", - "\u001b[?25hCollecting huggingface-hub<1.0,>=0.10.0\n", - " Downloading huggingface_hub-0.11.1-py3-none-any.whl (182 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m182.4/182.4 kB\u001b[0m \u001b[31m10.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: packaging>=20.0 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (22.0)\n", - "Collecting regex!=2019.12.17\n", - " Downloading regex-2022.10.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (770 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m770.5/770.5 kB\u001b[0m \u001b[31m29.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hCollecting tokenizers!=0.11.3,<0.14,>=0.11.1\n", - " Downloading tokenizers-0.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.6/7.6 MB\u001b[0m \u001b[31m65.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m\n", - "\u001b[?25hCollecting filelock\n", - " Downloading filelock-3.9.0-py3-none-any.whl (9.7 kB)\n", - "Collecting tqdm>=4.27\n", - " Downloading tqdm-4.64.1-py2.py3-none-any.whl (78 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.5/78.5 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: pyyaml>=5.1 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (6.0)\n", - "Requirement already satisfied: numpy>=1.17 in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (1.24.0)\n", - "Requirement already satisfied: requests in /home/codespace/.local/lib/python3.10/site-packages (from transformers) (2.28.1)\n", - "Requirement already satisfied: typing-extensions>=3.7.4.3 in /home/codespace/.local/lib/python3.10/site-packages (from huggingface-hub<1.0,>=0.10.0->transformers) (4.4.0)\n", - "Requirement already satisfied: charset-normalizer<3,>=2 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (2.1.1)\n", - "Requirement already satisfied: idna<4,>=2.5 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (3.4)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (2022.12.7)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /home/codespace/.local/lib/python3.10/site-packages (from requests->transformers) (1.26.13)\n", - "Installing collected packages: tokenizers, tqdm, regex, filelock, huggingface-hub, transformers\n", - "Successfully installed filelock-3.9.0 huggingface-hub-0.11.1 regex-2022.10.31 tokenizers-0.13.2 tqdm-4.64.1 transformers-4.25.1\n" - ] - } - ], + "outputs": [], "source": [ "# uncomment and run below lines to set up if running in colab\n", - "#%%bash\n", - "# git clone https://github.com/andrewm4894/Open-Assistant.git\n", - "# git checkout example-notebook\n", - "# cd Open-Assistant/notebooks/example\n", - "# pip install -r requirements.txt" + "# !git clone https://github.com/andrewm4894/Open-Assistant.git\n", + "# !git checkout example-notebook\n", + "# %cd Open-Assistant/notebooks/example\n", + "# !pip install -r requirements.txt" ] }, { @@ -74,18 +38,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/codespace/.python/current/lib/python3.10/site-packages/tqdm/auto.py:22: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" - ] - } - ], + "outputs": [], "source": [ "# import required packages\n", "import pandas as pd\n", @@ -112,12 +67,67 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
rowtextlabel
01some example data1
12some more data0
\n", + "
" + ], + "text/plain": [ + " row text label\n", + "0 1 some example data 1\n", + "1 2 some more data 0" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Do cool stuff here\n", - "df = pd.read_csv(\"data/data.csv\")" + "df = pd.read_csv(\"data/data.csv\")\n", + "df.head()" ] } ], From 8a1b1e46e75e0a6b421bbd6b0b3918b60070078f Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:09:50 +0000 Subject: [PATCH 05/21] dev --- notebooks/example/example.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index d65c2e51..dab70cb8 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -14,8 +14,7 @@ "outputs": [], "source": [ "# uncomment and run below lines to set up if running in colab\n", - "# !git clone https://github.com/andrewm4894/Open-Assistant.git\n", - "# !git checkout example-notebook\n", + "# !git clone https://github.com/andrewm4894/Open-Assistant.git && git checkout example-notebook\n", "# %cd Open-Assistant/notebooks/example\n", "# !pip install -r requirements.txt" ] From 4e9bedca4b7c022d9c7c9abbb34291c962dd27fe Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:13:35 +0000 Subject: [PATCH 06/21] dev --- notebooks/example/example.ipynb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index dab70cb8..88796e0a 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -14,7 +14,8 @@ "outputs": [], "source": [ "# uncomment and run below lines to set up if running in colab\n", - "# !git clone https://github.com/andrewm4894/Open-Assistant.git && git checkout example-notebook\n", + "# !git clone https://github.com/andrewm4894/Open-Assistant.git\n", + "# %cd Open-Assistant && git checkout example-notebook\n", "# %cd Open-Assistant/notebooks/example\n", "# !pip install -r requirements.txt" ] From 11f52d8d584b85933d4f2edde59f7c5a4178164f Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:15:29 +0000 Subject: [PATCH 07/21] dev --- notebooks/example/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/example/README.md b/notebooks/example/README.md index b2d2eb22..f33af8fc 100644 --- a/notebooks/example/README.md +++ b/notebooks/example/README.md @@ -1,6 +1,6 @@ # Example Notebook -[![Binder](http://mybinder.org/badge_logo.svg)](http://mybinder.org/v2/gh/andrewm4894/Open-Assistant/example-notebook?urlpath=/notebooks/example/example.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/andrewm4894/Open-Assistant/blob/example-notebook/notebooks/example/example.ipynb) This folder contains an example reference notebook structure and approach for this project. Please try and follow this structure as closely as possible. From ff399ef87517b9d1d33aa1f184f80c37d4638a7d Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:21:36 +0000 Subject: [PATCH 08/21] dev --- notebooks/example/example.ipynb | 1 - 1 file changed, 1 deletion(-) diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index 88796e0a..c9b873aa 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -15,7 +15,6 @@ "source": [ "# uncomment and run below lines to set up if running in colab\n", "# !git clone https://github.com/andrewm4894/Open-Assistant.git\n", - "# %cd Open-Assistant && git checkout example-notebook\n", "# %cd Open-Assistant/notebooks/example\n", "# !pip install -r requirements.txt" ] From 55cb144471155cb1edcc8e065576291d06616bf4 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:47:14 +0000 Subject: [PATCH 09/21] dev --- notebooks/README.md | 6 +++++- notebooks/example/README.md | 41 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/notebooks/README.md b/notebooks/README.md index edb5da33..fbe3a193 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -1,10 +1,14 @@ # Notebooks This is a folders with some useful notebooks, all the notebooks have a markdown -file with the same name explaining what they do. +file with the same name explaining what they do (or a README.md if its a single +notebook folder). ## Contributing Contributing to both notebooks and making new notebooks is very welcome. If you do so, make sure to make a markdown (.md) file to go with your notebook, makes it easier for people to know what your notebook is about. + +Check out the [example notebook](example/) for a reference example you can use +as a starting point. diff --git a/notebooks/example/README.md b/notebooks/example/README.md index f33af8fc..b975254a 100644 --- a/notebooks/example/README.md +++ b/notebooks/example/README.md @@ -1,6 +1,43 @@ # Example Notebook -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/andrewm4894/Open-Assistant/blob/example-notebook/notebooks/example/example.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/andrewm4894/Open-Assistant/blob/main/notebooks/example/example.ipynb) This folder contains an example reference notebook structure and approach for -this project. Please try and follow this structure as closely as possible. +this project. Please try and follow this structure as closely as possible. While +things will not exactly be the same for each notebook some principles we would +like to try ensure are: + +1. Each notebook or collection of related or dependant notebooks should live in + its own folder. +1. Each notebook should have a markdown file with the same name as the notebook + (or README.md if it's a single notebook folder) that explains what the + notebook does and how to use it. +1. Add an "Open in Colab" badge to the top of the notebook. +1. Make it as easy as possible for someone to run the notebook in Google Colab + or some other environment based on standard practices like providing a + `requirements.txt` file or anything else needed to successfully run the + notebook. + +## Running in Google Colab + +At the top of the notebook there is a code cell that will (once uncommented): + +1. clone the repository into your colab instance. +1. `cd` into the relevant notebook directory. +1. run `pip install -r requirements.txt` to install the required packages. + +At this point you can run the notebook as normal and the folder structure will +match that of the repository and the colab notebook will be running from the +same directory that the notebook lives in so relative links etc should work as +expected (for example `example.ipynb` will read some sample data from +`data/data.csv`). + +If you are adding a notebook please try and add a similar cell to the top of the +notebook so that it is easy for others to run the notebook in colab. + +## example.ipynb + +This notebook contains an example "Open In Colab" badge and a code cell to +prepare the colab environment to run the notebook. It also contains a code cell +that will read in some sample data from the `data` folder in the repository and +display it as a pandas dataframe. From bb469b9cd7fea873600fda91b04306c295d24f4f Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:51:50 +0000 Subject: [PATCH 10/21] dev --- notebooks/example/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/notebooks/example/README.md b/notebooks/example/README.md index b975254a..e3af2b7b 100644 --- a/notebooks/example/README.md +++ b/notebooks/example/README.md @@ -20,7 +20,8 @@ like to try ensure are: ## Running in Google Colab -At the top of the notebook there is a code cell that will (once uncommented): +At the top of the [example notebook](example.ipynb) there is a code cell that +will (once uncommented): 1. clone the repository into your colab instance. 1. `cd` into the relevant notebook directory. From bc365cb46fd65519ac1e4b9131c8514e11f8baa6 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 00:59:18 +0000 Subject: [PATCH 11/21] dev --- notebooks/example/README.md | 8 ++++++-- notebooks/example/example.ipynb | 16 ++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/notebooks/example/README.md b/notebooks/example/README.md index e3af2b7b..2136834d 100644 --- a/notebooks/example/README.md +++ b/notebooks/example/README.md @@ -12,7 +12,8 @@ like to try ensure are: 1. Each notebook should have a markdown file with the same name as the notebook (or README.md if it's a single notebook folder) that explains what the notebook does and how to use it. -1. Add an "Open in Colab" badge to the top of the notebook. +1. Add an "Open in Colab" badge to the top of the notebook (see the markdown + cell near the top of `example.ipynb` as an example you can adapt). 1. Make it as easy as possible for someone to run the notebook in Google Colab or some other environment based on standard practices like providing a `requirements.txt` file or anything else needed to successfully run the @@ -34,7 +35,10 @@ expected (for example `example.ipynb` will read some sample data from `data/data.csv`). If you are adding a notebook please try and add a similar cell to the top of the -notebook so that it is easy for others to run the notebook in colab. +notebook so that it is easy for others to run the notebook in colab. If your +notebook does not have any dependencies beyond what already comes as standard in +Google Colab then you do not need such a cell, just an "Open in Colab" badge +will suffice. ## example.ipynb diff --git a/notebooks/example/example.ipynb b/notebooks/example/example.ipynb index c9b873aa..2c6b1e01 100644 --- a/notebooks/example/example.ipynb +++ b/notebooks/example/example.ipynb @@ -1,5 +1,13 @@ { "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Example Notebook" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -19,14 +27,6 @@ "# !pip install -r requirements.txt" ] }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example Notebook" - ] - }, { "attachments": {}, "cell_type": "markdown", From 0c58b5d01f7c8b24af681ba063d5b746039b6fe8 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 01:01:33 +0000 Subject: [PATCH 12/21] typo --- notebooks/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/README.md b/notebooks/README.md index fbe3a193..af8f24bc 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -1,6 +1,6 @@ # Notebooks -This is a folders with some useful notebooks, all the notebooks have a markdown +This is a folder with some useful notebooks, all the notebooks have a markdown file with the same name explaining what they do (or a README.md if its a single notebook folder). From 191ef2eeb2049e618ab66c170efb3802b27f4ff4 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 01:02:23 +0000 Subject: [PATCH 13/21] dev --- notebooks/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebooks/README.md b/notebooks/README.md index af8f24bc..931f2fe5 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -1,8 +1,8 @@ # Notebooks This is a folder with some useful notebooks, all the notebooks have a markdown -file with the same name explaining what they do (or a README.md if its a single -notebook folder). +file with the same name (or a README.md if its a single notebook folder) +explaining what they do. ## Contributing From 1082c278793566a3a5efb04097c9b3acf4728cfc Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Sun, 8 Jan 2023 01:03:54 +0000 Subject: [PATCH 14/21] typo --- notebooks/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebooks/README.md b/notebooks/README.md index 931f2fe5..2eea7a15 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -6,8 +6,8 @@ explaining what they do. ## Contributing -Contributing to both notebooks and making new notebooks is very welcome. If you -do so, make sure to make a markdown (.md) file to go with your notebook, makes +Contributing to notebooks and making new notebooks is very welcome. If you do +so, make sure to make a markdown (.md) file to go with your notebook, it makes it easier for people to know what your notebook is about. Check out the [example notebook](example/) for a reference example you can use From d94cb4b2d6c94ac026f8abdb145ac384ce8ff171 Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Tue, 10 Jan 2023 19:16:13 +0900 Subject: [PATCH 15/21] Ensure all the API routes reject banned users --- website/src/lib/auth.ts | 19 +++++++++++++++++-- website/src/pages/api/admin/update_user.ts | 2 +- website/src/pages/api/admin/users.ts | 2 +- .../src/pages/api/messages/[id]/children.ts | 14 +++----------- .../pages/api/messages/[id]/conversation.ts | 14 +++----------- website/src/pages/api/messages/[id]/index.ts | 14 +++----------- website/src/pages/api/messages/[id]/parent.ts | 14 +++----------- website/src/pages/api/messages/index.ts | 14 +++----------- website/src/pages/api/messages/user.ts | 14 +++----------- website/src/pages/api/new_task/[task_type].ts | 19 +++++-------------- website/src/pages/api/reject_task.ts | 14 +++----------- website/src/pages/api/set_label.ts | 14 +++----------- website/src/pages/api/update_task.ts | 14 +++----------- 13 files changed, 51 insertions(+), 117 deletions(-) diff --git a/website/src/lib/auth.ts b/website/src/lib/auth.ts index 5fa20f48..803550a5 100644 --- a/website/src/lib/auth.ts +++ b/website/src/lib/auth.ts @@ -1,5 +1,20 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import { getToken } from "next-auth/jwt"; +import { getToken, JWT } from "next-auth/jwt"; + +/** + * Wraps any API Route handler and verifies that the user does not have the + * specified role. Returns a 403 if they do, otherwise runs the handler. + */ +const withoutRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse, arg2: JWT) => void) => { + return async (req: NextApiRequest, res: NextApiResponse) => { + const token = await getToken({ req }); + if (!token || token.role === role) { + res.status(403).end(); + return; + } + return handler(req, res, token); + }; +}; /** * Wraps any API Route handler and verifies that the user has the appropriate @@ -16,4 +31,4 @@ const withRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiRes }; }; -export default withRole; +export { withoutRole, withRole }; diff --git a/website/src/pages/api/admin/update_user.ts b/website/src/pages/api/admin/update_user.ts index a717e3d8..3a309b7e 100644 --- a/website/src/pages/api/admin/update_user.ts +++ b/website/src/pages/api/admin/update_user.ts @@ -1,4 +1,4 @@ -import withRole from "src/lib/auth"; +import { withRole } from "src/lib/auth"; import prisma from "src/lib/prismadb"; /** diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index ea8d59d9..7c71b667 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -1,4 +1,4 @@ -import withRole from "src/lib/auth"; +import { withRole } from "src/lib/auth"; import prisma from "src/lib/prismadb"; // The number of users to fetch in any request. diff --git a/website/src/pages/api/messages/[id]/children.ts b/website/src/pages/api/messages/[id]/children.ts index 9c8fb84a..4a184c11 100644 --- a/website/src/pages/api/messages/[id]/children.ts +++ b/website/src/pages/api/messages/[id]/children.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res) => { const { id } = req.query; const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}/children`, { @@ -22,6 +14,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(messages); -}; +}); export default handler; diff --git a/website/src/pages/api/messages/[id]/conversation.ts b/website/src/pages/api/messages/[id]/conversation.ts index 6fa8feb9..0c401883 100644 --- a/website/src/pages/api/messages/[id]/conversation.ts +++ b/website/src/pages/api/messages/[id]/conversation.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res) => { const { id } = req.query; const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}/conversation`, { @@ -22,6 +14,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(messages); -}; +}); export default handler; diff --git a/website/src/pages/api/messages/[id]/index.ts b/website/src/pages/api/messages/[id]/index.ts index 8e056532..d9c2bc4a 100644 --- a/website/src/pages/api/messages/[id]/index.ts +++ b/website/src/pages/api/messages/[id]/index.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res) => { const { id } = req.query; const messageRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages/${id}`, { @@ -22,6 +14,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(message); -}; +}); export default handler; diff --git a/website/src/pages/api/messages/[id]/parent.ts b/website/src/pages/api/messages/[id]/parent.ts index de6fbac6..ba332be9 100644 --- a/website/src/pages/api/messages/[id]/parent.ts +++ b/website/src/pages/api/messages/[id]/parent.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res) => { const { id } = req.query; if (!id) { @@ -43,6 +35,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(parent); -}; +}); export default handler; diff --git a/website/src/pages/api/messages/index.ts b/website/src/pages/api/messages/index.ts index 3c8d1c17..cb9728fe 100644 --- a/website/src/pages/api/messages/index.ts +++ b/website/src/pages/api/messages/index.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res) => { const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages`, { method: "GET", headers: { @@ -19,6 +11,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(messages); -}; +}); export default handler; diff --git a/website/src/pages/api/messages/user.ts b/website/src/pages/api/messages/user.ts index e3d22f3c..e5f361b8 100644 --- a/website/src/pages/api/messages/user.ts +++ b/website/src/pages/api/messages/user.ts @@ -1,14 +1,6 @@ -import { getToken } from "next-auth/jwt"; - -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } +import { withoutRole } from "src/lib/auth"; +const handler = withoutRole("banned", async (req, res, token) => { //TODO: add params if needed const params = new URLSearchParams({ username: token.sub, @@ -24,6 +16,6 @@ const handler = async (req, res) => { // Send recieved messages to the client. res.status(200).json(messages); -}; +}); export default handler; diff --git a/website/src/pages/api/new_task/[task_type].ts b/website/src/pages/api/new_task/[task_type].ts index 80334f76..d8c00a47 100644 --- a/website/src/pages/api/new_task/[task_type].ts +++ b/website/src/pages/api/new_task/[task_type].ts @@ -1,4 +1,4 @@ -import { getToken } from "next-auth/jwt"; +import { withoutRole } from "src/lib/auth"; import { oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; @@ -10,19 +10,10 @@ import prisma from "src/lib/prismadb"; * 3) Send and Ack to the Task Backend with our local id for the task. * 4) Return everything to the client. */ -const handler = async (req, res) => { - const { task_type } = req.query; - - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } - +const handler = withoutRole("banned", async (req, res, token) => { // Fetch the new task. - const task = await oasstApiClient.fetchTask(task_type, token); + const { task_type } = req.query; + const task = await oasstApiClient.fetchTask(task_type as string, token); const valid_labels = await oasstApiClient.fetch_valid_text(); // Store the task and link it to the user.. @@ -42,6 +33,6 @@ const handler = async (req, res) => { // Send the results to the client. res.status(200).json(registeredTask); -}; +}); export default handler; diff --git a/website/src/pages/api/reject_task.ts b/website/src/pages/api/reject_task.ts index fc807b67..d5185827 100644 --- a/website/src/pages/api/reject_task.ts +++ b/website/src/pages/api/reject_task.ts @@ -1,17 +1,9 @@ import { Prisma } from "@prisma/client"; -import { getToken } from "next-auth/jwt"; +import { withoutRole } from "src/lib/auth"; import { oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } - +const handler = withoutRole("banned", async (req, res) => { // Parse out the local task ID and the interaction contents. const { id: frontendId, reason } = await JSON.parse(req.body); @@ -25,6 +17,6 @@ const handler = async (req, res) => { // Send the results to the client. res.status(200).json({}); -}; +}); export default handler; diff --git a/website/src/pages/api/set_label.ts b/website/src/pages/api/set_label.ts index cfda114b..7e50fd44 100644 --- a/website/src/pages/api/set_label.ts +++ b/website/src/pages/api/set_label.ts @@ -1,18 +1,10 @@ -import { getToken } from "next-auth/jwt"; +import { withoutRole } from "src/lib/auth"; /** * Sets the Label in the Backend. * */ -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } - +const handler = withoutRole("banned", async (req, res, token) => { // Parse out the local message_id, task ID and the interaction contents. const { message_id, label_map, text } = await JSON.parse(req.body); @@ -35,6 +27,6 @@ const handler = async (req, res) => { }), }); res.status(interactionRes.status).end(); -}; +}); export default handler; diff --git a/website/src/pages/api/update_task.ts b/website/src/pages/api/update_task.ts index e8e21ca9..7d166e85 100644 --- a/website/src/pages/api/update_task.ts +++ b/website/src/pages/api/update_task.ts @@ -1,5 +1,5 @@ import { Prisma } from "@prisma/client"; -import { getToken } from "next-auth/jwt"; +import { withoutRole } from "src/lib/auth"; import { oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; @@ -13,15 +13,7 @@ import prisma from "src/lib/prismadb"; * 4) Records the new task in our local database. * 5) Returns the newly created task to the client. */ -const handler = async (req, res) => { - const token = await getToken({ req }); - - // Return nothing if the user isn't registered. - if (!token) { - res.status(401).end(); - return; - } - +const handler = withoutRole("banned", async (req, res, token) => { // Parse out the local task ID and the interaction contents. const { id: frontendId, content, update_type } = await JSON.parse(req.body); @@ -65,6 +57,6 @@ const handler = async (req, res) => { // Send the next task in the sequence to the client. res.status(200).json(newRegisteredTask); -}; +}); export default handler; From 15e1203be91934a6509ecb692b37e37d61df7723 Mon Sep 17 00:00:00 2001 From: Adrian Cowan Date: Tue, 10 Jan 2023 21:08:40 +1100 Subject: [PATCH 16/21] website: Refactor remaining task pages to use Task.tsx --- website/src/components/Tasks/LabelTask.tsx | 86 ++++++++++++------- website/src/components/Tasks/Task.tsx | 18 +++- website/src/components/Tasks/TaskTypes.tsx | 3 + website/src/hooks/tasks/useLabelingTask.ts | 31 +------ website/src/pages/create/assistant_reply.tsx | 6 +- website/src/pages/create/initial_prompt.tsx | 6 +- website/src/pages/create/user_reply.tsx | 6 +- .../pages/evaluate/rank_assistant_replies.tsx | 6 +- .../pages/evaluate/rank_initial_prompts.tsx | 6 +- .../src/pages/evaluate/rank_user_replies.tsx | 6 +- .../src/pages/label/label_assistant_reply.tsx | 48 ++++------- .../src/pages/label/label_initial_prompt.tsx | 42 ++++----- .../src/pages/label/label_prompter_reply.tsx | 48 ++++------- 13 files changed, 135 insertions(+), 177 deletions(-) diff --git a/website/src/components/Tasks/LabelTask.tsx b/website/src/components/Tasks/LabelTask.tsx index bb9d417c..966c0a53 100644 --- a/website/src/components/Tasks/LabelTask.tsx +++ b/website/src/components/Tasks/LabelTask.tsx @@ -1,43 +1,71 @@ import { Grid, Slider, SliderFilledTrack, SliderThumb, SliderTrack } from "@chakra-ui/react"; import { useColorMode } from "@chakra-ui/react"; -import { ReactNode, useEffect, useId, useMemo, useState } from "react"; +import { useEffect, useId, useState } from "react"; +import { MessageView } from "src/components/Messages"; +import { MessageTable } from "src/components/Messages/MessageTable"; +import { TaskControls } from "src/components/Survey/TaskControls"; import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; +import { TaskInfo } from "src/components/Tasks/TaskTypes"; +import { TaskType } from "src/types/Task"; import { colors } from "styles/Theme/colors"; -export const LabelTask = ({ - title, - desc, - messages, - inputs, - controls, -}: { - title: string; - desc: string; - messages: ReactNode; - inputs: ReactNode; - controls: ReactNode; -}) => { - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; +export interface LabelTaskProps { + // we need a task type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tasks: any[]; + taskType: TaskInfo; + trigger: (update: { + id: string; + update_type: string; + content: { text: string; labels: { [k: string]: number }; message_id: string }; + }) => void; + onSkipTask: (task: { id: string }, reason: string) => void; + onNextTask: () => void; + mainBgClasses: string; +} +export const LabelTask = ({ tasks, taskType, trigger, onSkipTask, onNextTask, mainBgClasses }: LabelTaskProps) => { + const task = tasks[0].task; + const valid_labels = tasks[0].valid_labels; - const card = useMemo( - () => ( - <> -
{title}
-

{desc}

- {messages} - - ), - [title, desc, messages] - ); + const [sliderValues, setSliderValues] = useState([]); + + const submitResponse = (task: { id: string; reply: string; message_id: string }) => { + console.assert(valid_labels.length === sliderValues.length); + const labels = Object.fromEntries(valid_labels.valid_labels.map((label, i) => [label, sliderValues[i]])); + trigger({ + id: task.id, + update_type: "text_labels", + content: { labels, text: task.reply, message_id: task.message_id }, + }); + }; return (
- {card} - {inputs} + <> +
{taskType.label}
+

{taskType.overview}

+ + {task.conversation ? ( + + ) : ( + + )} + +
- {controls} + +
); }; diff --git a/website/src/components/Tasks/Task.tsx b/website/src/components/Tasks/Task.tsx index 777f5dd5..e95fe3e2 100644 --- a/website/src/components/Tasks/Task.tsx +++ b/website/src/components/Tasks/Task.tsx @@ -1,12 +1,17 @@ +import { useColorMode } from "@chakra-ui/react"; import { CreateTask } from "src/components/Tasks/CreateTask"; import { EvaluateTask } from "src/components/Tasks/EvaluateTask"; +import { LabelTask } from "src/components/Tasks/LabelTask"; import { TaskCategory, TaskTypes } from "src/components/Tasks/TaskTypes"; import poster from "src/lib/poster"; import useSWRMutation from "swr/mutation"; -export const Task = ({ tasks, trigger, mutate, mainBgClasses }) => { +export const Task = ({ tasks, trigger, mutate }) => { const task = tasks[0].task; + const { colorMode } = useColorMode(); + const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; + const { trigger: sendRejection } = useSWRMutation("/api/reject_task", poster, { onSuccess: async () => { mutate(); @@ -45,6 +50,17 @@ export const Task = ({ tasks, trigger, mutate, mainBgClasses }) => { mainBgClasses={mainBgClasses} /> ); + case TaskCategory.Label: + return ( + + ); } } diff --git a/website/src/components/Tasks/TaskTypes.tsx b/website/src/components/Tasks/TaskTypes.tsx index d255756a..c9a978d6 100644 --- a/website/src/components/Tasks/TaskTypes.tsx +++ b/website/src/components/Tasks/TaskTypes.tsx @@ -71,6 +71,7 @@ export const TaskTypes: TaskInfo[] = [ desc: "Provide labels for a prompt.", category: TaskCategory.Label, pathname: "/label/label_initial_prompt", + overview: "Provide labels for the following prompt", type: "label_initial_prompt", }, { @@ -78,6 +79,7 @@ export const TaskTypes: TaskInfo[] = [ desc: "Provide labels for a prompt.", category: TaskCategory.Label, pathname: "/label/label_prompter_reply", + overview: "Given the following discussion, provide labels for the final promp", type: "label_prompter_reply", }, { @@ -85,6 +87,7 @@ export const TaskTypes: TaskInfo[] = [ desc: "Provide labels for a prompt.", category: TaskCategory.Label, pathname: "/label/label_assistant_reply", + overview: "Given the following discussion, provide labels for the final prompt.", type: "label_assistant_reply", }, ]; diff --git a/website/src/hooks/tasks/useLabelingTask.ts b/website/src/hooks/tasks/useLabelingTask.ts index 5e5050ab..3782c7a3 100644 --- a/website/src/hooks/tasks/useLabelingTask.ts +++ b/website/src/hooks/tasks/useLabelingTask.ts @@ -1,32 +1,9 @@ -import { BaseTask, TaskResponse, TaskType } from "src/types/Task"; +import { TaskType } from "src/types/Task"; import { LabelAssistantReplyTask, LabelInitialPromptTask, LabelPrompterReplyTask } from "src/types/Tasks"; import { useGenericTaskAPI } from "./useGenericTaskAPI"; -const useLabelingTask = ( - endpoint: TaskType.label_assistant_reply | TaskType.label_prompter_reply | TaskType.label_initial_prompt -) => { - const { tasks, isLoading, trigger, reset, error } = useGenericTaskAPI(endpoint); - - const submit = (id: string, message_id: string, text: string, validLabels: string[], labelWeights: number[]) => { - console.assert(validLabels.length === labelWeights.length); - const labels = Object.fromEntries(validLabels.map((label, i) => [label, labelWeights[i]])); - - return trigger({ id, update_type: "text_labels", content: { labels, text, message_id } }); - }; - - return { tasks, isLoading, submit, reset, error }; -}; - -export type LabelAssistantReplyTaskResponse = TaskResponse; - export const useLabelAssistantReplyTask = () => - useLabelingTask(TaskType.label_assistant_reply); - -export type LabelInitialPromptTaskResponse = TaskResponse; - -export const useLabelInitialPromptTask = () => useLabelingTask(TaskType.label_initial_prompt); - -export type LabelPrompterReplyTaskResponse = TaskResponse; - -export const useLabelPrompterReplyTask = () => useLabelingTask(TaskType.label_prompter_reply); + useGenericTaskAPI(TaskType.label_assistant_reply); +export const useLabelInitialPromptTask = () => useGenericTaskAPI(TaskType.label_initial_prompt); +export const useLabelPrompterReplyTask = () => useGenericTaskAPI(TaskType.label_prompter_reply); diff --git a/website/src/pages/create/assistant_reply.tsx b/website/src/pages/create/assistant_reply.tsx index 17facd5d..6e389190 100644 --- a/website/src/pages/create/assistant_reply.tsx +++ b/website/src/pages/create/assistant_reply.tsx @@ -1,5 +1,4 @@ import { Container } from "@chakra-ui/react"; -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; import { Task } from "src/components/Tasks/Task"; @@ -8,9 +7,6 @@ import { useCreateAssistantReply } from "src/hooks/tasks/useCreateReply"; const AssistantReply = () => { const { tasks, isLoading, reset, trigger } = useCreateAssistantReply(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const AssistantReply = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/create/initial_prompt.tsx b/website/src/pages/create/initial_prompt.tsx index 57f0dabd..20b36467 100644 --- a/website/src/pages/create/initial_prompt.tsx +++ b/website/src/pages/create/initial_prompt.tsx @@ -1,5 +1,4 @@ import { Container } from "@chakra-ui/react"; -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; import { Task } from "src/components/Tasks/Task"; @@ -8,9 +7,6 @@ import { useCreateInitialPrompt } from "src/hooks/tasks/useCreateReply"; const InitialPrompt = () => { const { tasks, isLoading, reset, trigger } = useCreateInitialPrompt(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const InitialPrompt = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/create/user_reply.tsx b/website/src/pages/create/user_reply.tsx index a0af0e95..41969e54 100644 --- a/website/src/pages/create/user_reply.tsx +++ b/website/src/pages/create/user_reply.tsx @@ -1,4 +1,3 @@ -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { Container } from "src/components/Container"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; @@ -8,9 +7,6 @@ import { useCreatePrompterReply } from "src/hooks/tasks/useCreateReply"; const UserReply = () => { const { tasks, isLoading, reset, trigger } = useCreatePrompterReply(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const UserReply = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/evaluate/rank_assistant_replies.tsx b/website/src/pages/evaluate/rank_assistant_replies.tsx index 8546e7a6..16eee130 100644 --- a/website/src/pages/evaluate/rank_assistant_replies.tsx +++ b/website/src/pages/evaluate/rank_assistant_replies.tsx @@ -1,4 +1,3 @@ -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { Container } from "src/components/Container"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; @@ -8,9 +7,6 @@ import { useRankAssistantRepliesTask } from "src/hooks/tasks/useRankReplies"; const RankAssistantReplies = () => { const { tasks, isLoading, reset, trigger } = useRankAssistantRepliesTask(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const RankAssistantReplies = () => { Rank Assistant Replies - + ); }; diff --git a/website/src/pages/evaluate/rank_initial_prompts.tsx b/website/src/pages/evaluate/rank_initial_prompts.tsx index 1898a93a..0b305192 100644 --- a/website/src/pages/evaluate/rank_initial_prompts.tsx +++ b/website/src/pages/evaluate/rank_initial_prompts.tsx @@ -1,4 +1,3 @@ -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { Container } from "src/components/Container"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; @@ -8,9 +7,6 @@ import { useRankInitialPromptsTask } from "src/hooks/tasks/useRankReplies"; const RankInitialPrompts = () => { const { tasks, isLoading, reset, trigger } = useRankInitialPromptsTask(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const RankInitialPrompts = () => { Rank Initial Prompts - + ); }; diff --git a/website/src/pages/evaluate/rank_user_replies.tsx b/website/src/pages/evaluate/rank_user_replies.tsx index e2a39977..c269745f 100644 --- a/website/src/pages/evaluate/rank_user_replies.tsx +++ b/website/src/pages/evaluate/rank_user_replies.tsx @@ -1,4 +1,3 @@ -import { useColorMode } from "@chakra-ui/react"; import Head from "next/head"; import { Container } from "src/components/Container"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; @@ -8,9 +7,6 @@ import { useRankPrompterRepliesTask } from "src/hooks/tasks/useRankReplies"; const RankUserReplies = () => { const { tasks, isLoading, reset, trigger } = useRankPrompterRepliesTask(); - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; - if (isLoading) { return ; } @@ -25,7 +21,7 @@ const RankUserReplies = () => { Rank User Replies - + ); }; diff --git a/website/src/pages/label/label_assistant_reply.tsx b/website/src/pages/label/label_assistant_reply.tsx index 99c10f56..945a612e 100644 --- a/website/src/pages/label/label_assistant_reply.tsx +++ b/website/src/pages/label/label_assistant_reply.tsx @@ -1,44 +1,28 @@ -import { useState } from "react"; +import { Container } from "@chakra-ui/react"; +import Head from "next/head"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; -import { MessageTable } from "src/components/Messages/MessageTable"; -import { TaskControls } from "src/components/Survey/TaskControls"; -import { LabelSliderGroup, LabelTask } from "src/components/Tasks/LabelTask"; -import { LabelAssistantReplyTaskResponse, useLabelAssistantReplyTask } from "src/hooks/tasks/useLabelingTask"; -import { Message } from "src/types/Conversation"; +import { Task } from "src/components/Tasks/Task"; +import { useLabelAssistantReplyTask } from "src/hooks/tasks/useLabelingTask"; const LabelAssistantReply = () => { - const [sliderValues, setSliderValues] = useState([]); + const { tasks, isLoading, trigger, reset } = useLabelAssistantReplyTask(); - const { tasks, isLoading, submit, reset } = useLabelAssistantReplyTask(); - - if (isLoading || tasks.length === 0) { + if (isLoading) { return ; } - const task = tasks[0].task; - const valid_labels = tasks[0].valid_labels; - const messages: Message[] = [ - ...task.conversation.messages, - { text: task.reply, is_assistant: true, message_id: task.message_id }, - ]; + if (tasks.length === 0) { + return No tasks found...; + } return ( - } - inputs={} - controls={ - reset()} - onNextTask={reset} - onSubmitResponse={({ id, task }: LabelAssistantReplyTaskResponse) => - submit(id, task.message_id, task.reply, task.valid_labels, sliderValues) - } - /> - } - /> + <> + + Label Assistant Reply + + + + ); }; diff --git a/website/src/pages/label/label_initial_prompt.tsx b/website/src/pages/label/label_initial_prompt.tsx index 4cd4343b..bfacfdbe 100644 --- a/website/src/pages/label/label_initial_prompt.tsx +++ b/website/src/pages/label/label_initial_prompt.tsx @@ -1,38 +1,28 @@ -import { useState } from "react"; +import { Container } from "@chakra-ui/react"; +import Head from "next/head"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; -import { MessageView } from "src/components/Messages"; -import { TaskControls } from "src/components/Survey/TaskControls"; -import { LabelSliderGroup, LabelTask } from "src/components/Tasks/LabelTask"; -import { LabelInitialPromptTaskResponse, useLabelInitialPromptTask } from "src/hooks/tasks/useLabelingTask"; +import { Task } from "src/components/Tasks/Task"; +import { useLabelInitialPromptTask } from "src/hooks/tasks/useLabelingTask"; const LabelInitialPrompt = () => { - const [sliderValues, setSliderValues] = useState([]); + const { tasks, isLoading, trigger, reset } = useLabelInitialPromptTask(); - const { tasks, isLoading, submit, reset } = useLabelInitialPromptTask(); - - if (isLoading || tasks.length === 0) { + if (isLoading) { return ; } - const task = tasks[0].task; + if (tasks.length === 0) { + return No tasks found...; + } return ( - } - inputs={} - controls={ - reset()} - onNextTask={reset} - onSubmitResponse={({ id, task }: LabelInitialPromptTaskResponse) => - submit(id, task.message_id, task.prompt, task.valid_labels, sliderValues) - } - /> - } - /> + <> + + Label Initial Prompt + + + + ); }; diff --git a/website/src/pages/label/label_prompter_reply.tsx b/website/src/pages/label/label_prompter_reply.tsx index 35654a47..3d47f74b 100644 --- a/website/src/pages/label/label_prompter_reply.tsx +++ b/website/src/pages/label/label_prompter_reply.tsx @@ -1,44 +1,28 @@ -import { useState } from "react"; +import { Container } from "@chakra-ui/react"; +import Head from "next/head"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; -import { MessageTable } from "src/components/Messages/MessageTable"; -import { TaskControls } from "src/components/Survey/TaskControls"; -import { LabelSliderGroup, LabelTask } from "src/components/Tasks/LabelTask"; -import { LabelPrompterReplyTaskResponse, useLabelPrompterReplyTask } from "src/hooks/tasks/useLabelingTask"; -import { Message } from "src/types/Conversation"; +import { Task } from "src/components/Tasks/Task"; +import { useLabelPrompterReplyTask } from "src/hooks/tasks/useLabelingTask"; const LabelPrompterReply = () => { - const [sliderValues, setSliderValues] = useState([]); + const { tasks, isLoading, trigger, reset } = useLabelPrompterReplyTask(); - const { tasks, isLoading, submit, reset } = useLabelPrompterReplyTask(); - - if (isLoading || tasks.length === 0) { + if (isLoading) { return ; } - const task = tasks[0].task; - const valid_labels = tasks[0].valid_labels; - const messages: Message[] = [ - ...task.conversation.messages, - { text: task.reply, is_assistant: false, message_id: task.message_id }, - ]; + if (tasks.length === 0) { + return No tasks found...; + } return ( - } - inputs={} - controls={ - reset()} - onNextTask={reset} - onSubmitResponse={({ id, task }: LabelPrompterReplyTaskResponse) => - submit(id, task.message_id, task.reply, task.valid_labels, sliderValues) - } - /> - } - /> + <> + + Label Prompter Reply + + + + ); }; From a1fd2cc6380d03b062965f012363f988f2d7778a Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Tue, 10 Jan 2023 12:04:10 +0100 Subject: [PATCH 17/21] added to readme --- CONTRIBUTING.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 428f6a50..b290f5fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,3 +110,8 @@ Upon making a release on GitHub, all docker images are automatically built and pushed to ghcr.io. The docker images are tagged with the release version, and the `latest` tag. Further, the ansible playbook in `ansible/dev.yaml` is run to automatically deploy the built release to the dev machine. + +### Contribute a Dataset + +See +[here](https://github.com/LAION-AI/Open-Assistant/blob/main/docs/docs/data/datasets.md) From 35ebefb64719581153cc4da4d55f3f37f9269856 Mon Sep 17 00:00:00 2001 From: Andrew Maguire Date: Tue, 10 Jan 2023 14:59:50 +0000 Subject: [PATCH 18/21] add `venv` to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9cdabc03..701701fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .venv +venv .env *.pyc *.swp From 805bc33ba42ea74ec0e471f26d7d5f947d92559e Mon Sep 17 00:00:00 2001 From: Adrian Cowan Date: Wed, 11 Jan 2023 01:53:51 +1100 Subject: [PATCH 19/21] website: Unify task controls Also happens to fix some issues with the no-changes are-you-sure dialog only appearing once and other form values not being reset correctly. --- .../src/components/Survey/TaskControls.tsx | 26 ++-- .../Survey/TaskControlsOverridable.tsx | 60 -------- website/src/components/Tasks/CreateTask.tsx | 80 ++++------- website/src/components/Tasks/EvaluateTask.tsx | 83 ++++------- website/src/components/Tasks/LabelTask.tsx | 96 ++++++------- website/src/components/Tasks/Task.tsx | 129 ++++++++++++------ website/src/components/Tasks/TaskTypes.tsx | 18 +++ .../src/components/Tasks/UnchangedWarning.tsx | 42 ++++++ website/src/pages/create/assistant_reply.tsx | 2 +- website/src/pages/create/initial_prompt.tsx | 2 +- website/src/pages/create/summarize_story.tsx | 11 +- website/src/pages/create/user_reply.tsx | 2 +- .../pages/evaluate/rank_assistant_replies.tsx | 2 +- .../pages/evaluate/rank_initial_prompts.tsx | 2 +- .../src/pages/evaluate/rank_user_replies.tsx | 2 +- website/src/pages/evaluate/rate_summary.tsx | 11 +- .../src/pages/label/label_assistant_reply.tsx | 2 +- .../src/pages/label/label_initial_prompt.tsx | 2 +- .../src/pages/label/label_prompter_reply.tsx | 2 +- website/src/types/Task.ts | 4 + website/src/types/TaskReplyState.ts | 10 ++ 21 files changed, 293 insertions(+), 295 deletions(-) delete mode 100644 website/src/components/Survey/TaskControlsOverridable.tsx create mode 100644 website/src/components/Tasks/UnchangedWarning.tsx create mode 100644 website/src/types/TaskReplyState.ts diff --git a/website/src/components/Survey/TaskControls.tsx b/website/src/components/Survey/TaskControls.tsx index 7f419fcb..f451d871 100644 --- a/website/src/components/Survey/TaskControls.tsx +++ b/website/src/components/Survey/TaskControls.tsx @@ -4,21 +4,22 @@ import clsx from "clsx"; import { SkipButton } from "src/components/Buttons/Skip"; import { SubmitButton } from "src/components/Buttons/Submit"; import { TaskInfo } from "src/components/TaskInfo/TaskInfo"; +import { TaskStatus } from "src/components/Tasks/Task"; export interface TaskControlsProps { // we need a task type // eslint-disable-next-line @typescript-eslint/no-explicit-any - tasks: any[]; + task: any; className?: string; - onSubmitResponse: (task: { id: string }) => void; - onSkipTask: (task: { id: string }, reason: string) => void; + taskStatus: TaskStatus; + onSubmit: () => void; + onSkip: (reason: string) => void; onNextTask: () => void; } export const TaskControls = (props: TaskControlsProps) => { const { colorMode } = useColorMode(); const isLightMode = colorMode === "light"; - const endTask = props.tasks[props.tasks.length - 1]; return (
{ } )} > - + - { - props.onSkipTask(props.tasks[0], reason); - }} - /> - {endTask.task.type !== "task_done" ? ( - props.onSubmitResponse(props.tasks[0])}> + + {props.taskStatus !== "SUBMITTED" ? ( + Submit ) : ( diff --git a/website/src/components/Survey/TaskControlsOverridable.tsx b/website/src/components/Survey/TaskControlsOverridable.tsx deleted file mode 100644 index 25e973fb..00000000 --- a/website/src/components/Survey/TaskControlsOverridable.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { - Button, - Flex, - Modal, - ModalBody, - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - useDisclosure, -} from "@chakra-ui/react"; -import { TaskControls, TaskControlsProps } from "src/components/Survey/TaskControls"; - -interface TaskControlsOverridableProps extends TaskControlsProps { - isValid: boolean; - prepareForSubmit: () => void; -} - -export const TaskControlsOverridable = (props: TaskControlsOverridableProps) => { - const { isValid, onSubmitResponse, ...rest } = props; - const { isOpen: isModalOpen, onOpen: onOpenModal, onClose: onModalClose } = useDisclosure(); - - const unchangedResponsePrompt = () => { - onOpenModal(); - - // Ideally this happens when the user clicks submit, but we can't - // reliably wait for it to be executed before submitting the response - // without significant refactoring. - // As a result, modal will only display once even if the user doesn't proceed - props.prepareForSubmit(); - }; - - const onSubmitResponseOverride = () => { - onSubmitResponse(props.tasks[0]); - onModalClose(); - }; - - return ( - <> - - - - - Order Unchanged - You have not changed the order of the prompts. Are you sure you would like to submit? - - - - - - - - - - - ); -}; diff --git a/website/src/components/Tasks/CreateTask.tsx b/website/src/components/Tasks/CreateTask.tsx index a424315a..ad1aca3b 100644 --- a/website/src/components/Tasks/CreateTask.tsx +++ b/website/src/components/Tasks/CreateTask.tsx @@ -1,70 +1,38 @@ import { useState } from "react"; import { Messages } from "src/components/Messages"; -import { TaskControls } from "src/components/Survey/TaskControls"; import { TrackedTextarea } from "src/components/Survey/TrackedTextarea"; import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; -import { TaskInfo } from "src/components/Tasks/TaskTypes"; +import { TaskSurveyProps } from "src/components/Tasks/Task"; -export interface CreateTaskProps { - // we need a task type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - tasks: any[]; - taskType: TaskInfo; - trigger: (update: { id: string; update_type: string; content: { text: string } }) => void; - onSkipTask: (task: { id: string }, reason: string) => void; - onNextTask: () => void; - mainBgClasses: string; -} -export const CreateTask = ({ tasks, taskType, trigger, onSkipTask, onNextTask, mainBgClasses }: CreateTaskProps) => { - const task = tasks[0].task; - const valid_labels = tasks[0].valid_labels; +export const CreateTask = ({ task, taskType, onReplyChanged }: TaskSurveyProps<{ text: string }>) => { const [inputText, setInputText] = useState(""); - const submitResponse = (task: { id: string }) => { - const text = inputText.trim(); - trigger({ - id: task.id, - update_type: "text_reply_to_message", - content: { - text, - }, - }); - }; + const valid_labels = task.valid_labels; const textChangeHandler = (event: React.ChangeEvent) => { - setInputText(event.target.value); + const text = event.target.value; + onReplyChanged({ content: { text }, state: "VALID" }); + setInputText(text); }; return ( -
- - <> -
{taskType.label}
-

{taskType.overview}

- {task.conversation ? ( - - ) : null} - - <> -
{taskType.instruction}
- - -
- - { - setInputText(""); - onSkipTask(task, reason); - }} - onNextTask={onNextTask} - /> -
+ + <> +
{taskType.label}
+

{taskType.overview}

+ {task.conversation ? ( + + ) : null} + + <> +
{taskType.instruction}
+ + +
); }; diff --git a/website/src/components/Tasks/EvaluateTask.tsx b/website/src/components/Tasks/EvaluateTask.tsx index 61ed3889..9d69d73c 100644 --- a/website/src/components/Tasks/EvaluateTask.tsx +++ b/website/src/components/Tasks/EvaluateTask.tsx @@ -1,63 +1,40 @@ -import { useState } from "react"; +import { useEffect } from "react"; +import { MessageTable } from "src/components/Messages/MessageTable"; import { Sortable } from "src/components/Sortable/Sortable"; import { SurveyCard } from "src/components/Survey/SurveyCard"; -import { TaskControlsOverridable } from "src/components/Survey/TaskControlsOverridable"; +import { TaskSurveyProps } from "src/components/Tasks/Task"; -import { MessageTable } from "../Messages/MessageTable"; - -export interface EvaluateTaskProps { - // we need a task type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - tasks: any[]; - trigger: (update: { id: string; update_type: string; content: { ranking: number[] } }) => void; - onSkipTask: (task: { id: string }, reason: string) => void; - onNextTask: () => void; - mainBgClasses: string; -} - -export const EvaluateTask = ({ tasks, trigger, onSkipTask, onNextTask, mainBgClasses }: EvaluateTaskProps) => { - const [ranking, setRanking] = useState([]); - const submitResponse = (task) => { - trigger({ - id: task.id, - update_type: "message_ranking", - content: { - ranking, - }, - }); - }; - - let messages = null; - if (tasks[0].task.conversation) { - messages = tasks[0].task.conversation.messages; +export const EvaluateTask = ({ task, onReplyChanged }: TaskSurveyProps<{ ranking: number[] }>) => { + let messages = []; + if (task.conversation) { + messages = task.conversation.messages; messages = messages.map((message, index) => ({ ...message, id: index })); } - const valid_labels = tasks[0].valid_labels; - const sortables = tasks[0].task.replies ? "replies" : "prompts"; + useEffect(() => { + const conversationMsgs = task.conversation ? task.conversation.messages : []; + const defaultRanking = conversationMsgs.map((message, index) => index); + onReplyChanged({ + content: { ranking: defaultRanking }, + state: "DEFAULT", + }); + }, [task.conversation, onReplyChanged]); + + const onRank = (newRanking: number[]) => { + onReplyChanged({ content: { ranking: newRanking }, state: "VALID" }); + }; + + const valid_labels = task.valid_labels; + const sortables = task.replies ? "replies" : "prompts"; return ( -
- -
Instructions
-

- Given the following {sortables}, sort them from best to worst, best being first, worst being last. -

- {messages ? : null} - -
- - setRanking(tasks[0].task[sortables].map((_, idx) => idx))} - onSubmitResponse={submitResponse} - onSkipTask={(task, reason) => { - setRanking([]); - onSkipTask(task, reason); - }} - onNextTask={onNextTask} - /> -
+ +
Instructions
+

+ Given the following {sortables}, sort them from best to worst, best being first, worst being last. +

+ + +
); }; diff --git a/website/src/components/Tasks/LabelTask.tsx b/website/src/components/Tasks/LabelTask.tsx index 966c0a53..4b94c947 100644 --- a/website/src/components/Tasks/LabelTask.tsx +++ b/website/src/components/Tasks/LabelTask.tsx @@ -3,70 +3,55 @@ import { useColorMode } from "@chakra-ui/react"; import { useEffect, useId, useState } from "react"; import { MessageView } from "src/components/Messages"; import { MessageTable } from "src/components/Messages/MessageTable"; -import { TaskControls } from "src/components/Survey/TaskControls"; import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; -import { TaskInfo } from "src/components/Tasks/TaskTypes"; +import { TaskSurveyProps } from "src/components/Tasks/Task"; import { TaskType } from "src/types/Task"; import { colors } from "styles/Theme/colors"; -export interface LabelTaskProps { - // we need a task type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - tasks: any[]; - taskType: TaskInfo; - trigger: (update: { - id: string; - update_type: string; - content: { text: string; labels: { [k: string]: number }; message_id: string }; - }) => void; - onSkipTask: (task: { id: string }, reason: string) => void; - onNextTask: () => void; - mainBgClasses: string; -} -export const LabelTask = ({ tasks, taskType, trigger, onSkipTask, onNextTask, mainBgClasses }: LabelTaskProps) => { - const task = tasks[0].task; - const valid_labels = tasks[0].valid_labels; - +export const LabelTask = ({ + task, + taskType, + onReplyChanged, +}: TaskSurveyProps<{ text: string; labels: { [k: string]: number }; message_id: string }>) => { const [sliderValues, setSliderValues] = useState([]); - const submitResponse = (task: { id: string; reply: string; message_id: string }) => { + const valid_labels = task.valid_labels; + + useEffect(() => { + onReplyChanged({ content: { labels: {}, text: task.reply, message_id: task.message_id }, state: "DEFAULT" }); + }, [task.reply, task.message_id, onReplyChanged]); + + const onSliderChange = (values: number[]) => { console.assert(valid_labels.length === sliderValues.length); - const labels = Object.fromEntries(valid_labels.valid_labels.map((label, i) => [label, sliderValues[i]])); - trigger({ - id: task.id, - update_type: "text_labels", - content: { labels, text: task.reply, message_id: task.message_id }, - }); + const labels = Object.fromEntries(valid_labels.map((label, i) => [label, sliderValues[i]])); + onReplyChanged({ content: { labels, text: task.reply, message_id: task.message_id }, state: "VALID" }); + setSliderValues(values); }; return ( -
- - <> -
{taskType.label}
-

{taskType.overview}

+ + <> +
{taskType.label}
+

{taskType.overview}

- {task.conversation ? ( - - ) : ( - - )} - - -
- - -
+ {task.conversation ? ( + + ) : ( + + )} + + + ); }; @@ -79,10 +64,6 @@ interface LabelSliderGroupProps { export const LabelSliderGroup = ({ labelIDs, onChange }: LabelSliderGroupProps) => { const [sliderValues, setSliderValues] = useState(Array.from({ length: labelIDs.length }).map(() => 0)); - useEffect(() => { - onChange(sliderValues); - }, [sliderValues, onChange]); - return ( {labelIDs.map((labelId, idx) => ( @@ -93,6 +74,7 @@ export const LabelSliderGroup = ({ labelIDs, onChange }: LabelSliderGroupProps) sliderHandler={(sliderValue) => { const newState = sliderValues.slice(); newState[idx] = sliderValue; + onChange(sliderValues); setSliderValues(newState); }} /> diff --git a/website/src/components/Tasks/Task.tsx b/website/src/components/Tasks/Task.tsx index e95fe3e2..cd4ddeed 100644 --- a/website/src/components/Tasks/Task.tsx +++ b/website/src/components/Tasks/Task.tsx @@ -1,13 +1,32 @@ import { useColorMode } from "@chakra-ui/react"; +import { useRef, useState } from "react"; +import { TaskControls } from "src/components/Survey/TaskControls"; import { CreateTask } from "src/components/Tasks/CreateTask"; import { EvaluateTask } from "src/components/Tasks/EvaluateTask"; import { LabelTask } from "src/components/Tasks/LabelTask"; -import { TaskCategory, TaskTypes } from "src/components/Tasks/TaskTypes"; +import { TaskCategory, TaskInfo, TaskTypes } from "src/components/Tasks/TaskTypes"; +import { UnchangedWarning } from "src/components/Tasks/UnchangedWarning"; import poster from "src/lib/poster"; +import { TaskContent } from "src/types/Task"; +import { TaskReplyState } from "src/types/TaskReplyState"; import useSWRMutation from "swr/mutation"; -export const Task = ({ tasks, trigger, mutate }) => { - const task = tasks[0].task; +export type TaskStatus = "NOT_SUBMITTABLE" | "DEFAULT" | "SUBMITABLE" | "SUBMITTED"; + +export interface TaskSurveyProps { + // we need a task type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + task: any; + taskType: TaskInfo; + onReplyChanged: (state: TaskReplyState) => void; +} + +export const Task = ({ task, trigger, mutate }) => { + const [taskStatus, setTaskStatus] = useState("NOT_SUBMITTABLE"); + const replyContent = useRef(null); + const [showUnchangedWarning, setShowUnchangedWarning] = useState(false); + + const taskType = TaskTypes.find((taskType) => taskType.type === task.type); const { colorMode } = useColorMode(); const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white"; @@ -18,51 +37,85 @@ export const Task = ({ tasks, trigger, mutate }) => { }, }); - const rejectTask = (task: { id: string }, reason: string) => { + const rejectTask = (reason: string) => { sendRejection({ id: task.id, reason, }); }; - function taskTypeComponent(type) { - const taskType = TaskTypes.find((taskType) => taskType.type === type); - const category = taskType.category; - switch (category) { + const onReplyChanged = useRef((state: TaskReplyState) => { + if (taskStatus === "SUBMITTED") return; + + replyContent.current = state?.content; + if (state === null) { + if (taskStatus !== "NOT_SUBMITTABLE") setTaskStatus("NOT_SUBMITTABLE"); + } else if (state.state === "DEFAULT") { + if (taskStatus !== "DEFAULT") setTaskStatus("DEFAULT"); + } else if (state.state === "VALID") { + if (taskStatus !== "SUBMITABLE") setTaskStatus("SUBMITABLE"); + } + }).current; + + const submitResponse = () => { + switch (taskStatus) { + case "NOT_SUBMITTABLE": + return; + case "DEFAULT": + setShowUnchangedWarning(true); + break; + case "SUBMITABLE": { + trigger({ + id: task.id, + update_type: taskType.update_type, + content: replyContent.current, + }); + setTaskStatus("SUBMITTED"); + break; + } + case "SUBMITTED": + return; + } + }; + + function taskTypeComponent() { + switch (taskType.category) { case TaskCategory.Create: - return ( - - ); + return ; case TaskCategory.Evaluate: - return ( - - ); + return ; case TaskCategory.Label: - return ( - - ); + return ; } } - return taskTypeComponent(task.type); + return ( +
+ {taskTypeComponent()} + + setShowUnchangedWarning(false)} + onSubmitAnyway={() => { + if (taskStatus === "DEFAULT") { + trigger({ + id: task.id, + update_type: taskType.update_type, + content: replyContent.current, + }); + setTaskStatus("SUBMITTED"); + setShowUnchangedWarning(false); + } + }} + /> +
+ ); }; diff --git a/website/src/components/Tasks/TaskTypes.tsx b/website/src/components/Tasks/TaskTypes.tsx index c9a978d6..f2e00df5 100644 --- a/website/src/components/Tasks/TaskTypes.tsx +++ b/website/src/components/Tasks/TaskTypes.tsx @@ -12,6 +12,9 @@ export interface TaskInfo { type: string; overview?: string; instruction?: string; + update_type: string; + unchanged_title?: string; + unchanged_message?: string; } export const TaskTypes: TaskInfo[] = [ @@ -24,6 +27,7 @@ export const TaskTypes: TaskInfo[] = [ type: "initial_prompt", overview: "Create an initial message to send to the assistant", instruction: "Provide the initial prompt", + update_type: "text_reply_to_message", }, { label: "Reply as User", @@ -33,6 +37,7 @@ export const TaskTypes: TaskInfo[] = [ type: "prompter_reply", overview: "Given the following conversation, provide an adequate reply", instruction: "Provide the user`s reply", + update_type: "text_reply_to_message", }, { label: "Reply as Assistant", @@ -42,6 +47,7 @@ export const TaskTypes: TaskInfo[] = [ type: "assistant_reply", overview: "Given the following conversation, provide an adequate reply", instruction: "Provide the assistant`s reply", + update_type: "text_reply_to_message", }, // evaluate { @@ -50,6 +56,9 @@ export const TaskTypes: TaskInfo[] = [ desc: "Help Open Assistant improve its responses to conversations with other users.", pathname: "/evaluate/rank_user_replies", type: "rank_prompter_replies", + update_type: "message_ranking", + unchanged_title: "Order Unchanged", + unchanged_message: "You have not changed the order of the prompts. Are you sure you would like to submit?", }, { label: "Rank Assistant Replies", @@ -57,6 +66,9 @@ export const TaskTypes: TaskInfo[] = [ category: TaskCategory.Evaluate, pathname: "/evaluate/rank_assistant_replies", type: "rank_assistant_replies", + update_type: "message_ranking", + unchanged_title: "Order Unchanged", + unchanged_message: "You have not changed the order of the prompts. Are you sure you would like to submit?", }, { label: "Rank Initial Prompts", @@ -64,6 +76,9 @@ export const TaskTypes: TaskInfo[] = [ category: TaskCategory.Evaluate, pathname: "/evaluate/rank_initial_prompts", type: "rank_initial_prompts", + update_type: "message_ranking", + unchanged_title: "Order Unchanged", + unchanged_message: "You have not changed the order of the prompts. Are you sure you would like to submit?", }, // label { @@ -73,6 +88,7 @@ export const TaskTypes: TaskInfo[] = [ pathname: "/label/label_initial_prompt", overview: "Provide labels for the following prompt", type: "label_initial_prompt", + update_type: "text_labels", }, { label: "Label Prompter Reply", @@ -81,6 +97,7 @@ export const TaskTypes: TaskInfo[] = [ pathname: "/label/label_prompter_reply", overview: "Given the following discussion, provide labels for the final promp", type: "label_prompter_reply", + update_type: "text_labels", }, { label: "Label Assistant Reply", @@ -89,5 +106,6 @@ export const TaskTypes: TaskInfo[] = [ pathname: "/label/label_assistant_reply", overview: "Given the following discussion, provide labels for the final prompt.", type: "label_assistant_reply", + update_type: "text_labels", }, ]; diff --git a/website/src/components/Tasks/UnchangedWarning.tsx b/website/src/components/Tasks/UnchangedWarning.tsx new file mode 100644 index 00000000..65d7b50d --- /dev/null +++ b/website/src/components/Tasks/UnchangedWarning.tsx @@ -0,0 +1,42 @@ +import { + Button, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from "@chakra-ui/react"; + +interface UnchangedWarningProps { + show: boolean; + title: string; + message: string; + onClose: () => void; + onSubmitAnyway: () => void; +} + +export const UnchangedWarning = (props: UnchangedWarningProps) => { + return ( + <> + + + + + {props.title} + {props.message} + + + + + + + + + + ); +}; diff --git a/website/src/pages/create/assistant_reply.tsx b/website/src/pages/create/assistant_reply.tsx index 6e389190..f078fa40 100644 --- a/website/src/pages/create/assistant_reply.tsx +++ b/website/src/pages/create/assistant_reply.tsx @@ -21,7 +21,7 @@ const AssistantReply = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/create/initial_prompt.tsx b/website/src/pages/create/initial_prompt.tsx index 20b36467..01b882f9 100644 --- a/website/src/pages/create/initial_prompt.tsx +++ b/website/src/pages/create/initial_prompt.tsx @@ -21,7 +21,7 @@ const InitialPrompt = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/create/summarize_story.tsx b/website/src/pages/create/summarize_story.tsx index 61415962..d551b8e6 100644 --- a/website/src/pages/create/summarize_story.tsx +++ b/website/src/pages/create/summarize_story.tsx @@ -36,10 +36,10 @@ const SummarizeStory = () => { // Trigger a mutation that updates the current task. We should probably // signal somewhere that this interaction is being processed. - const submitResponse = (task: { id: string }) => { + const submitResponse = () => { const text = inputText.trim(); trigger({ - id: task.id, + id: tasks[0].task.id, update_type: "text_reply_to_message", content: { text, @@ -88,9 +88,10 @@ const SummarizeStory = () => { diff --git a/website/src/pages/create/user_reply.tsx b/website/src/pages/create/user_reply.tsx index 41969e54..51a54b0e 100644 --- a/website/src/pages/create/user_reply.tsx +++ b/website/src/pages/create/user_reply.tsx @@ -21,7 +21,7 @@ const UserReply = () => { Reply as Assistant - + ); }; diff --git a/website/src/pages/evaluate/rank_assistant_replies.tsx b/website/src/pages/evaluate/rank_assistant_replies.tsx index 16eee130..6686a42f 100644 --- a/website/src/pages/evaluate/rank_assistant_replies.tsx +++ b/website/src/pages/evaluate/rank_assistant_replies.tsx @@ -21,7 +21,7 @@ const RankAssistantReplies = () => { Rank Assistant Replies - + ); }; diff --git a/website/src/pages/evaluate/rank_initial_prompts.tsx b/website/src/pages/evaluate/rank_initial_prompts.tsx index 0b305192..1350305b 100644 --- a/website/src/pages/evaluate/rank_initial_prompts.tsx +++ b/website/src/pages/evaluate/rank_initial_prompts.tsx @@ -21,7 +21,7 @@ const RankInitialPrompts = () => { Rank Initial Prompts - + ); }; diff --git a/website/src/pages/evaluate/rank_user_replies.tsx b/website/src/pages/evaluate/rank_user_replies.tsx index c269745f..0bd23c64 100644 --- a/website/src/pages/evaluate/rank_user_replies.tsx +++ b/website/src/pages/evaluate/rank_user_replies.tsx @@ -21,7 +21,7 @@ const RankUserReplies = () => { Rank User Replies - + ); }; diff --git a/website/src/pages/evaluate/rate_summary.tsx b/website/src/pages/evaluate/rate_summary.tsx index e9da4a63..3ea2448e 100644 --- a/website/src/pages/evaluate/rate_summary.tsx +++ b/website/src/pages/evaluate/rate_summary.tsx @@ -39,9 +39,9 @@ const RateSummary = () => { // Trigger a mutation that updates the current task. We should probably // signal somewhere that this interaction is being processed. - const submitResponse = (t) => { + const submitResponse = () => { trigger({ - id: t.id, + id: tasks[0].task.id, update_type: "message_rating", content: { rating: rating, @@ -103,9 +103,10 @@ const RateSummary = () => { diff --git a/website/src/pages/label/label_assistant_reply.tsx b/website/src/pages/label/label_assistant_reply.tsx index 945a612e..ffc3af4d 100644 --- a/website/src/pages/label/label_assistant_reply.tsx +++ b/website/src/pages/label/label_assistant_reply.tsx @@ -21,7 +21,7 @@ const LabelAssistantReply = () => { Label Assistant Reply - + ); }; diff --git a/website/src/pages/label/label_initial_prompt.tsx b/website/src/pages/label/label_initial_prompt.tsx index bfacfdbe..beea5ee8 100644 --- a/website/src/pages/label/label_initial_prompt.tsx +++ b/website/src/pages/label/label_initial_prompt.tsx @@ -21,7 +21,7 @@ const LabelInitialPrompt = () => { Label Initial Prompt - + ); }; diff --git a/website/src/pages/label/label_prompter_reply.tsx b/website/src/pages/label/label_prompter_reply.tsx index 3d47f74b..0eeaad33 100644 --- a/website/src/pages/label/label_prompter_reply.tsx +++ b/website/src/pages/label/label_prompter_reply.tsx @@ -21,7 +21,7 @@ const LabelPrompterReply = () => { Label Prompter Reply - + ); }; diff --git a/website/src/types/Task.ts b/website/src/types/Task.ts index 6975fa14..a681b2e5 100644 --- a/website/src/types/Task.ts +++ b/website/src/types/Task.ts @@ -12,6 +12,10 @@ export const enum TaskType { label_assistant_reply = "label_assistant_reply", } +// we need to reconsider how to handle task content types +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type TaskContent = any; + export interface ValidLabel { name: string; display_text: string; diff --git a/website/src/types/TaskReplyState.ts b/website/src/types/TaskReplyState.ts new file mode 100644 index 00000000..602446f9 --- /dev/null +++ b/website/src/types/TaskReplyState.ts @@ -0,0 +1,10 @@ +export interface TaskReplyValid { + content: T; + state: "VALID"; +} +export interface TaskReplyDefault { + content: T; + state: "DEFAULT"; +} + +export type TaskReplyState = TaskReplyValid | TaskReplyDefault; From e5abb2dc85b7cca615c1642bb1807aed70555ce8 Mon Sep 17 00:00:00 2001 From: Oliver Stanley Date: Tue, 10 Jan 2023 17:20:56 +0000 Subject: [PATCH 20/21] 536: Add endpoint to resolve frontend user by auth method and username (#539) * Add endpoint to resolve frontend user by auth method and username * Require client ID for frontend user lookup * Remove unnecessary if check * Fix PromptRepository -> UserRepository * Convert to protocol User * Move User prep * Address review comments * 404 -> HTTP_404_NOT_FOUND --- .../oasst_backend/api/v1/frontend_users.py | 18 ++++++++++++++ backend/oasst_backend/user_repository.py | 24 +++++++++++++++++++ .../exceptions/oasst_api_error.py | 3 ++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/backend/oasst_backend/api/v1/frontend_users.py b/backend/oasst_backend/api/v1/frontend_users.py index 8d56b7f9..31f14c64 100644 --- a/backend/oasst_backend/api/v1/frontend_users.py +++ b/backend/oasst_backend/api/v1/frontend_users.py @@ -1,4 +1,5 @@ import datetime +from typing import Optional from uuid import UUID from fastapi import APIRouter, Depends, Query @@ -6,6 +7,7 @@ from oasst_backend.api import deps from oasst_backend.api.v1 import utils from oasst_backend.models import ApiClient from oasst_backend.prompt_repository import PromptRepository +from oasst_backend.user_repository import UserRepository from oasst_shared.schemas import protocol from sqlmodel import Session from starlette.status import HTTP_204_NO_CONTENT @@ -13,6 +15,22 @@ from starlette.status import HTTP_204_NO_CONTENT router = APIRouter() +@router.get("/{auth_method}/{username}", response_model=protocol.User) +def query_frontend_user( + auth_method: str, + username: str, + api_client_id: Optional[UUID] = None, + api_client: ApiClient = Depends(deps.get_api_client), + db: Session = Depends(deps.get_db), +): + """ + Query frontend user. + """ + ur = UserRepository(db, api_client) + user = ur.query_frontend_user(auth_method, username, api_client_id) + return protocol.User(id=user.username, display_name=user.display_name, auth_method=user.auth_method) + + @router.get("/{username}/messages", response_model=list[protocol.Message]) def query_frontend_user_messages( username: str, diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index b5508899..3acb1751 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -1,9 +1,12 @@ from typing import Optional +from uuid import UUID from oasst_backend.models import ApiClient, Message, User +from oasst_shared.exceptions import OasstError, OasstErrorCode from oasst_shared.schemas import protocol as protocol_schema from oasst_shared.schemas.protocol import LeaderboardStats from sqlmodel import Session, func +from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND class UserRepository: @@ -11,6 +14,27 @@ class UserRepository: self.db = db self.api_client = api_client + def query_frontend_user( + self, auth_method: str, username: str, api_client_id: Optional[UUID] = None + ) -> Optional[User]: + if not api_client_id: + api_client_id = self.api_client.id + + if not self.api_client.trusted and api_client_id != self.api_client.id: + # Unprivileged API client asks for foreign user + raise OasstError("Forbidden", OasstErrorCode.API_CLIENT_NOT_AUTHORIZED, HTTP_403_FORBIDDEN) + + user: User = ( + self.db.query(User) + .filter(User.auth_method == auth_method, User.username == username, User.api_client_id == api_client_id) + .first() + ) + + if user is None: + raise OasstError("User not found", OasstErrorCode.USER_NOT_FOUND, HTTP_404_NOT_FOUND) + + return user + def lookup_client_user(self, client_user: protocol_schema.User, create_missing: bool = True) -> Optional[User]: if not client_user: return None diff --git a/oasst-shared/oasst_shared/exceptions/oasst_api_error.py b/oasst-shared/oasst_shared/exceptions/oasst_api_error.py index 6cc25918..ce08d31d 100644 --- a/oasst-shared/oasst_shared/exceptions/oasst_api_error.py +++ b/oasst-shared/oasst_shared/exceptions/oasst_api_error.py @@ -9,7 +9,7 @@ class OasstErrorCode(IntEnum): Ranges: 0-1000: general errors 1000-2000: tasks endpoint - 2000-3000: prompt_repository + 2000-3000: prompt_repository, task_repository, user_repository 3000-4000: external resources """ @@ -45,6 +45,7 @@ class OasstErrorCode(IntEnum): TASK_NOT_ACK = 2104 TASK_ALREADY_DONE = 2105 TASK_NOT_COLLECTIVE = 2106 + USER_NOT_FOUND = 2200 # 3000-4000: external resources HUGGINGFACE_API_ERROR = 3001 From 8b441838431b5382d9293c3541263f2c03518012 Mon Sep 17 00:00:00 2001 From: Oliver Stanley Date: Tue, 10 Jan 2023 20:05:30 +0000 Subject: [PATCH 21/21] Add note on Python version to backend README.md --- backend/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/README.md b/backend/README.md index a9507d5e..80c39dfd 100644 --- a/backend/README.md +++ b/backend/README.md @@ -7,6 +7,9 @@ In root directory, run database. The default settings are already configured to connect to the database at `localhost:5432`. +Python 3.10 is required. It is recommended to use `pyenv` which will recognise +the `.python-version` in the project root directory. + Make sure you have all requirements installed. You can do this by running `pip install -r requirements.txt` inside the `backend` folder and `pip install -e .` inside the `oasst-shared` folder. Then, run the backend using