From 6c41d7afdec77a106e625562e5bdf62bb1ef17a8 Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Tue, 13 Dec 2022 11:24:22 -0800 Subject: [PATCH 001/119] Add dockerfile for bot and build/publish actions --- .github/workflows/docker/backend.yaml | 14 +++++++ .github/workflows/docker/bot.yaml | 14 +++++++ .github/workflows/docker/build.yaml | 53 +++++++++++++++++++++++++++ bot/Dockerfile | 7 ++++ 4 files changed, 88 insertions(+) create mode 100644 .github/workflows/docker/backend.yaml create mode 100644 .github/workflows/docker/bot.yaml create mode 100644 .github/workflows/docker/build.yaml create mode 100644 bot/Dockerfile diff --git a/.github/workflows/docker/backend.yaml b/.github/workflows/docker/backend.yaml new file mode 100644 index 00000000..b054e819 --- /dev/null +++ b/.github/workflows/docker/backend.yaml @@ -0,0 +1,14 @@ +name: (Backend) Publish Docker Image + +on: + push: + paths: + - 'backend/**' + +jobs: + build: + uses: ./.github/workflows/docker/build.yaml + with: + image-name: backend + folder: backend + build-args: "" \ No newline at end of file diff --git a/.github/workflows/docker/bot.yaml b/.github/workflows/docker/bot.yaml new file mode 100644 index 00000000..a0ec3a5f --- /dev/null +++ b/.github/workflows/docker/bot.yaml @@ -0,0 +1,14 @@ +name: (Bot) Publish Docker Image + +on: + push: + paths: + - 'bot/**' + +jobs: + build: + uses: ./.github/workflows/docker/build.yaml + with: + image-name: bot + folder: bot + build-args: "" \ No newline at end of file diff --git a/.github/workflows/docker/build.yaml b/.github/workflows/docker/build.yaml new file mode 100644 index 00000000..8ba7bd26 --- /dev/null +++ b/.github/workflows/docker/build.yaml @@ -0,0 +1,53 @@ +name: Build + +on: + workflow_call: + inputs: + folder: + required: true + type: string + image-name: + required: true + type: string + build-args: + required: false + type: string + +jobs: + build: + name: Build Images + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2.2.1 + - name: Login to container registry + uses: docker/login-action@v2.1.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Get base registry + run: | + echo "REGISTRY=ghcr.io/${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV + - name: Set tag prefix + if: github.ref_name != 'main' + run: | + echo "TAG_PREFIX=${{ github.ref_name }}-" >> $GITHUB_ENV + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4.1.1 + with: + images: ${{ env.REGISTRY }}/${{ inputs.image-name }} + tags: | + type=sha,prefix=${{ env.TAG_PREFIX }},format=short + - name: Build and push Docker image + uses: docker/build-push-action@v3.2.0 + with: + context: ${{ inputs.folder }} + build-args: ${{ inputs.build-args }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image-name }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image-name }}:buildcache,mode=max diff --git a/bot/Dockerfile b/bot/Dockerfile new file mode 100644 index 00000000..9cff3dc2 --- /dev/null +++ b/bot/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.10-slim-bullseye +RUN mkdir /app +ADD requirements.txt /app/requirements.txt +WORKDIR /app +RUN pip install -r requirements.txt +ADD . /app +CMD ["python", "bot.py"] \ No newline at end of file From 57cf7d2a72eb87c037315362e0f2b1b7dca5043c Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Tue, 13 Dec 2022 18:02:09 -0800 Subject: [PATCH 002/119] use gha cache instead of registry --- .github/workflows/docker/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker/build.yaml b/.github/workflows/docker/build.yaml index 8ba7bd26..ee32d374 100644 --- a/.github/workflows/docker/build.yaml +++ b/.github/workflows/docker/build.yaml @@ -49,5 +49,5 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image-name }}:buildcache - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ inputs.image-name }}:buildcache,mode=max + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file From df5fcc873ca0002b3827cb4894d81aae29613ab5 Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Tue, 13 Dec 2022 18:35:22 -0800 Subject: [PATCH 003/119] reorg --- .github/workflows/{docker/backend.yaml => docker-backend.yaml} | 2 +- .github/workflows/{docker/bot.yaml => docker-bot.yaml} | 2 +- .github/workflows/{docker/build.yaml => docker-build.yaml} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{docker/backend.yaml => docker-backend.yaml} (78%) rename .github/workflows/{docker/bot.yaml => docker-bot.yaml} (76%) rename .github/workflows/{docker/build.yaml => docker-build.yaml} (100%) diff --git a/.github/workflows/docker/backend.yaml b/.github/workflows/docker-backend.yaml similarity index 78% rename from .github/workflows/docker/backend.yaml rename to .github/workflows/docker-backend.yaml index b054e819..12f6665c 100644 --- a/.github/workflows/docker/backend.yaml +++ b/.github/workflows/docker-backend.yaml @@ -7,7 +7,7 @@ on: jobs: build: - uses: ./.github/workflows/docker/build.yaml + uses: ./.github/workflows/docker-build.yaml with: image-name: backend folder: backend diff --git a/.github/workflows/docker/bot.yaml b/.github/workflows/docker-bot.yaml similarity index 76% rename from .github/workflows/docker/bot.yaml rename to .github/workflows/docker-bot.yaml index a0ec3a5f..4b1a0d97 100644 --- a/.github/workflows/docker/bot.yaml +++ b/.github/workflows/docker-bot.yaml @@ -7,7 +7,7 @@ on: jobs: build: - uses: ./.github/workflows/docker/build.yaml + uses: ./.github/workflows/docker-build.yaml with: image-name: bot folder: bot diff --git a/.github/workflows/docker/build.yaml b/.github/workflows/docker-build.yaml similarity index 100% rename from .github/workflows/docker/build.yaml rename to .github/workflows/docker-build.yaml From 2634f27008ab821f16ddc0633b36b6d796cb7ec2 Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Tue, 13 Dec 2022 18:37:21 -0800 Subject: [PATCH 004/119] newlines --- .github/workflows/docker-backend.yaml | 2 +- .github/workflows/docker-bot.yaml | 2 +- .github/workflows/docker-build.yaml | 2 +- bot/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-backend.yaml b/.github/workflows/docker-backend.yaml index 12f6665c..976a5602 100644 --- a/.github/workflows/docker-backend.yaml +++ b/.github/workflows/docker-backend.yaml @@ -11,4 +11,4 @@ jobs: with: image-name: backend folder: backend - build-args: "" \ No newline at end of file + build-args: "" diff --git a/.github/workflows/docker-bot.yaml b/.github/workflows/docker-bot.yaml index 4b1a0d97..2b34ebeb 100644 --- a/.github/workflows/docker-bot.yaml +++ b/.github/workflows/docker-bot.yaml @@ -11,4 +11,4 @@ jobs: with: image-name: bot folder: bot - build-args: "" \ No newline at end of file + build-args: "" diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index ee32d374..9d5c0106 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -50,4 +50,4 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha - cache-to: type=gha,mode=max \ No newline at end of file + cache-to: type=gha,mode=max diff --git a/bot/Dockerfile b/bot/Dockerfile index 9cff3dc2..ab215b5b 100644 --- a/bot/Dockerfile +++ b/bot/Dockerfile @@ -4,4 +4,4 @@ ADD requirements.txt /app/requirements.txt WORKDIR /app RUN pip install -r requirements.txt ADD . /app -CMD ["python", "bot.py"] \ No newline at end of file +CMD ["python", "bot.py"] From c3f406925285f2cfb46f4584d2f5a9000cc160f8 Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Tue, 13 Dec 2022 18:39:03 -0800 Subject: [PATCH 005/119] quotes --- .github/workflows/docker-backend.yaml | 2 +- .github/workflows/docker-bot.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-backend.yaml b/.github/workflows/docker-backend.yaml index 976a5602..026002e8 100644 --- a/.github/workflows/docker-backend.yaml +++ b/.github/workflows/docker-backend.yaml @@ -3,7 +3,7 @@ name: (Backend) Publish Docker Image on: push: paths: - - 'backend/**' + - "backend/**" jobs: build: diff --git a/.github/workflows/docker-bot.yaml b/.github/workflows/docker-bot.yaml index 2b34ebeb..d97bf757 100644 --- a/.github/workflows/docker-bot.yaml +++ b/.github/workflows/docker-bot.yaml @@ -3,7 +3,7 @@ name: (Bot) Publish Docker Image on: push: paths: - - 'bot/**' + - "bot/**" jobs: build: From 1f5e563b1dabc839f8ccf5d20ca6cba48f16555e Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Wed, 14 Dec 2022 11:49:42 +0900 Subject: [PATCH 006/119] Adding a basic version of next-auth-js using Discord and ensuring an authenticated user can fetch prompts from the real backend using an API middlelayer --- website/.env.example | 8 + website/package-lock.json | 394 +++++++++++++++++++++++- website/package.json | 5 +- website/pages/_app.js | 14 +- website/pages/api/auth/[...nextauth].js | 19 ++ website/pages/api/prompts.js | 19 ++ website/pages/index.js | 30 +- 7 files changed, 463 insertions(+), 26 deletions(-) create mode 100644 website/.env.example create mode 100644 website/pages/api/auth/[...nextauth].js create mode 100644 website/pages/api/prompts.js diff --git a/website/.env.example b/website/.env.example new file mode 100644 index 00000000..f0db36e7 --- /dev/null +++ b/website/.env.example @@ -0,0 +1,8 @@ +FASTAPI_URL=http://xamla.com:8000 +FASTAPI_KEY=magic_key + +DISCORD_CLIENT_ID=your_discord_bot_client_id +DISCORD_CLIENT_SECRET=your_discord_bot_client_secret + +# Run "openssl rand -base64 32" to get this. +NEXTAUTH_SECRET=openssl_result diff --git a/website/package-lock.json b/website/package-lock.json index 5623182e..486bddff 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -12,11 +12,14 @@ "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", "@supabase/supabase-js": "^2.1.4", + "axios": "^1.2.1", "eslint": "8.29.0", "eslint-config-next": "13.0.6", "next": "13.0.6", + "next-auth": "^4.18.6", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "swr": "^2.0.0" } }, "node_modules/@babel/runtime": { @@ -334,6 +337,14 @@ "node": ">= 8" } }, + "node_modules/@panva/hkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", + "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgr/utils": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", @@ -733,6 +744,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/axe-core": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", @@ -741,6 +757,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -854,11 +880,30 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/core-js-pure": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", @@ -948,6 +993,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1640,6 +1693,38 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2161,6 +2246,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/jose": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-sdsl": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", @@ -2305,6 +2398,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2396,6 +2508,36 @@ } } }, + "node_modules/next-auth": { + "version": "4.18.6", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.18.6.tgz", + "integrity": "sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==", + "dependencies": { + "@babel/runtime": "^7.16.3", + "@panva/hkdf": "^1.0.1", + "cookie": "^0.5.0", + "jose": "^4.9.3", + "oauth": "^0.9.15", + "openid-client": "^5.1.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "engines": { + "node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0" + }, + "peerDependencies": { + "next": "^12.2.5 || ^13", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18", + "react-dom": "^17.0.2 || ^18" + }, + "peerDependenciesMeta": { + "nodemailer": { + "optional": true + } + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -2430,6 +2572,11 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2438,6 +2585,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -2528,6 +2683,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2552,6 +2715,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openid-client": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.1.tgz", + "integrity": "sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw==", + "dependencies": { + "jose": "^4.10.0", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -2683,6 +2860,26 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2691,6 +2888,11 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -2701,6 +2903,11 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3058,6 +3265,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.0.0.tgz", + "integrity": "sha512-IhUx5yPkX+Fut3h0SqZycnaNLXLXsb2ECFq0Y29cxnK7d8r7auY2JWNbCW3IX+EqXUg3rwNJFlhrw5Ye/b6k7w==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "engines": { + "pnpm": "7" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/synckit": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", @@ -3216,6 +3437,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -3228,6 +3457,14 @@ "node": ">=6.14.2" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -3506,6 +3743,11 @@ "fastq": "^1.6.0" } }, + "@panva/hkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.2.tgz", + "integrity": "sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA==" + }, "@pkgr/utils": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", @@ -3791,11 +4033,26 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "axe-core": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", "integrity": "sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw==" }, + "axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -3877,11 +4134,24 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, "core-js-pure": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", @@ -3946,6 +4216,11 @@ "object-keys": "^1.1.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4479,6 +4754,21 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4825,6 +5115,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "jose": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==" + }, "js-sdsl": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", @@ -4935,6 +5230,19 @@ "picomatch": "^2.3.1" } }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4988,6 +5296,22 @@ "styled-jsx": "5.1.0" } }, + "next-auth": { + "version": "4.18.6", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.18.6.tgz", + "integrity": "sha512-0TQwbq5X9Jyd1wUVYUoyvHJh4JWXeW9UOcMEl245Er/Y5vsSbyGJHt8M7xjRMzk9mORVMYehoMdERgyiq/jCgA==", + "requires": { + "@babel/runtime": "^7.16.3", + "@panva/hkdf": "^1.0.1", + "cookie": "^0.5.0", + "jose": "^4.9.3", + "oauth": "^0.9.15", + "openid-client": "^5.1.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + } + }, "next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -5006,11 +5330,21 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -5071,6 +5405,11 @@ "es-abstract": "^1.20.4" } }, + "oidc-token-hash": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz", + "integrity": "sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5089,6 +5428,17 @@ "is-wsl": "^2.2.0" } }, + "openid-client": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.3.1.tgz", + "integrity": "sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw==", + "requires": { + "jose": "^4.10.0", + "lru-cache": "^6.0.0", + "object-hash": "^2.0.1", + "oidc-token-hash": "^5.0.1" + } + }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -5171,11 +5521,29 @@ "source-map-js": "^1.0.2" } }, + "preact": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", + "integrity": "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==" + }, + "preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "requires": { + "pretty-format": "^3.8.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5186,6 +5554,11 @@ "react-is": "^16.13.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -5407,6 +5780,14 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "swr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.0.0.tgz", + "integrity": "sha512-IhUx5yPkX+Fut3h0SqZycnaNLXLXsb2ECFq0Y29cxnK7d8r7auY2JWNbCW3IX+EqXUg3rwNJFlhrw5Ye/b6k7w==", + "requires": { + "use-sync-external-store": "^1.2.0" + } + }, "synckit": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.4.tgz", @@ -5530,6 +5911,12 @@ "punycode": "^2.1.0" } }, + "use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "requires": {} + }, "utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -5538,6 +5925,11 @@ "node-gyp-build": "^4.3.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/website/package.json b/website/package.json index 03af2bfa..ab8ac35c 100644 --- a/website/package.json +++ b/website/package.json @@ -14,10 +14,13 @@ "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", "@supabase/supabase-js": "^2.1.4", + "axios": "^1.2.1", "eslint": "8.29.0", "eslint-config-next": "13.0.6", "next": "13.0.6", + "next-auth": "^4.18.6", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "swr": "^2.0.0" } } diff --git a/website/pages/_app.js b/website/pages/_app.js index 0bf2e144..5122f011 100644 --- a/website/pages/_app.js +++ b/website/pages/_app.js @@ -1,19 +1,13 @@ -import { createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; -import { SessionContextProvider } from "@supabase/auth-helpers-react"; - +import { SessionProvider } from "next-auth/react"; import { useState } from "react"; import "../styles/globals.css"; -function MyApp({ Component, pageProps }) { - const [supabase] = useState(() => createBrowserSupabaseClient()); +function MyApp({ Component, pageProps: { session, ...pageProps } }) { return ( - + - + ); } diff --git a/website/pages/api/auth/[...nextauth].js b/website/pages/api/auth/[...nextauth].js new file mode 100644 index 00000000..baea95a5 --- /dev/null +++ b/website/pages/api/auth/[...nextauth].js @@ -0,0 +1,19 @@ +import NextAuth from "next-auth"; +import DiscordProvider from "next-auth/providers/discord"; + +export const authOptions = { + providers: [ + DiscordProvider({ + clientId: process.env.DISCORD_CLIENT_ID, + clientSecret: process.env.DISCORD_CLIENT_SECRET, + }), + ], + callbacks: { + async session({ session, token, user }) { + session.user.sub = token.sub; + return session; + }, + }, +}; + +export default NextAuth(authOptions); diff --git a/website/pages/api/prompts.js b/website/pages/api/prompts.js new file mode 100644 index 00000000..20e35f73 --- /dev/null +++ b/website/pages/api/prompts.js @@ -0,0 +1,19 @@ +import { unstable_getServerSession } from "next-auth/next"; +import { authOptions } from "./auth/[...nextauth]"; + +export default async (req, res) => { + const session = await unstable_getServerSession(req, res, authOptions); + + if (!session) { + res.status(200).json([{ name: "cat" }]); + return; + } + const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, { + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + }, + }); + const prompts = await promptRes.json(); + + res.status(200).json(prompts); +}; diff --git a/website/pages/index.js b/website/pages/index.js index eb83ff6e..d150d6c7 100644 --- a/website/pages/index.js +++ b/website/pages/index.js @@ -1,22 +1,20 @@ -import { Auth, ThemeSupa } from "@supabase/auth-ui-react"; -import { useSession, useSupabaseClient } from "@supabase/auth-helpers-react"; - -import { useState } from "react"; +import axios from "axios"; import Head from "next/head"; import Image from "next/image"; +import { useSession, signIn, signOut } from "next-auth/react"; +import { useEffect, useState } from "react"; +import useSWR from "swr"; import styles from "../styles/Home.module.css"; +const fetcher = (url) => axios.get(url).then((res) => res.data); + export default function Home() { - const session = useSession(); - const supabase = useSupabaseClient(); - - const signinWithDiscord = async () => { - const { data, error } = await supabase.auth.signInWithOAuth({ - provider: "discord", - }); - }; - + const { data: session, status } = useSession(); + const { data: prompts, errors } = useSWR( + session ? "/api/prompts" : null, + fetcher + ); if (!session) { return (
@@ -28,7 +26,7 @@ export default function Home() { chat based large language model.

- +

We believe that by doing this we will create a revolution in innovation in language. In the same way that stable-diffusion helped @@ -57,6 +55,8 @@ export default function Home() {

); } + + console.log(prompts); return (
@@ -65,6 +65,8 @@ export default function Home() {

Open Chat Gpt

You are logged in

+ +
); From f04e0efd4934441b4e6bbe6b73ac8f2f763a050e Mon Sep 17 00:00:00 2001 From: Birger Moell Date: Wed, 14 Dec 2022 21:55:33 +0100 Subject: [PATCH 007/119] Updated code to work on LAION server --- bot/bot.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 2509138a..6f7ede73 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -27,7 +27,9 @@ headers = {"X-API-Key": API_SERVER_KEY} # For testing only. TEST_GUILD = os.getenv("TEST_GUILD") - +TEST_GUILD_LAION = os.getenv("TEST_GUILD_LAION") +# TEST_GUILD = False +guild_ids = [TEST_GUILD, TEST_GUILD_LAION] # Initiate the client and command tree to create slash commands. class OpenChatGPTClient(discord.Client): @@ -39,10 +41,16 @@ class OpenChatGPTClient(discord.Client): if TEST_GUILD: # When testing the bot it's handy to run in a single server (called a # Guide in the API). This is relatively fast. - guild = discord.Object(id=TEST_GUILD) - self.tree.copy_global_to(guild=guild) - await self.tree.sync(guild=guild) - else: + for guild_id in guild_ids: + guild = discord.Object(id=guild_id) + self.tree.copy_global_to(guild=guild) + await self.tree.sync(guild=guild) + + + # guild = discord.Object(id=TEST_GUILD) + # self.tree.copy_global_to(guild=guild) + # await self.tree.sync(guild=guild) + else: # This can take up to an hour for the commands to be registered. await self.tree.sync() logger.debug("Ready!") From 7dc714fc30acbb02a30fb174f9315ae9fe218628 Mon Sep 17 00:00:00 2001 From: Birger Moell Date: Wed, 14 Dec 2022 21:56:32 +0100 Subject: [PATCH 008/119] Lint fix --- bot/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 6f7ede73..aff74b26 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -31,6 +31,7 @@ TEST_GUILD_LAION = os.getenv("TEST_GUILD_LAION") # TEST_GUILD = False guild_ids = [TEST_GUILD, TEST_GUILD_LAION] + # Initiate the client and command tree to create slash commands. class OpenChatGPTClient(discord.Client): def __init__(self, *, intents: discord.Intents): @@ -45,12 +46,11 @@ class OpenChatGPTClient(discord.Client): guild = discord.Object(id=guild_id) self.tree.copy_global_to(guild=guild) await self.tree.sync(guild=guild) - - + # guild = discord.Object(id=TEST_GUILD) # self.tree.copy_global_to(guild=guild) # await self.tree.sync(guild=guild) - else: + else: # This can take up to an hour for the commands to be registered. await self.tree.sync() logger.debug("Ready!") From 96d668fa4b08724ed8b7d5c1f925e1c8bd542c44 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Wed, 14 Dec 2022 23:08:08 +0100 Subject: [PATCH 009/119] Rough draft of story summarization interaction --- backend/app/api/v1/api.py | 3 +- backend/app/api/v1/tasks.py | 106 ++++++++++++++++++++++++++++++++ backend/app/schemas/protocol.py | 63 +++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 backend/app/api/v1/tasks.py create mode 100644 backend/app/schemas/protocol.py diff --git a/backend/app/api/v1/api.py b/backend/app/api/v1/api.py index 7c97c9cb..5a704c2d 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/api/v1/api.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -from app.api.v1 import labelers, prompts +from app.api.v1 import labelers, prompts, tasks from fastapi import APIRouter api_router = APIRouter() api_router.include_router(labelers.router, prefix="/labelers", tags=["labelers"]) api_router.include_router(prompts.router, prefix="/prompts", tags=["prompts"]) +api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py new file mode 100644 index 00000000..494281d0 --- /dev/null +++ b/backend/app/api/v1/tasks.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +from typing import Any, List +from uuid import UUID + +from app.api import deps +from app.schemas import protocol as protocol_schema +from fastapi import APIRouter, Depends, HTTPException +from fastapi.security.api_key import APIKey +from loguru import logger +from sqlmodel import Session +from starlette.status import HTTP_400_BAD_REQUEST + +router = APIRouter() + + +@router.post("/", response_model=List[protocol_schema.SummarizeStoryTask]) # work with Union once more types are added +def request_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + request: protocol_schema.GenericTaskRequest, # work with Union once more types are added +) -> Any: + """ + Create new task. + """ + deps.api_auth(api_key, db, create=True) + + # TODO: Create a task and store it in the database. + + match (request.type): + case "generic": + # here we create a task at random (and store it in the database) + logger.info("Frontend requested a generic task.") + task = protocol_schema.SummarizeStoryTask( + story="This is a story. A very long story. So long, it needs to be summarized.", + ) + + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid request type.", + ) + if request.user_id is not None: + task.addressed_users = [request.user_id] + + return [task] + + +@router.post("/{task_id}/ack") +def acknowledge_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + response: protocol_schema.PostCreatedTaskResponse, +) -> Any: + """ + The frontend acknowledges a task. + """ + deps.api_auth(api_key, db, create=True) + + match (response.type): + case "post_created": + logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") + # here we would store the post id in the database for the task + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + return {} + + +@router.post("/interaction") +def post_interaction( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + interaction: protocol_schema.TextReplyToPost, +) -> Any: + """ + The frontend acknowledges a task. + """ + deps.api_auth(api_key, db, create=True) + + response = [] + match (interaction.type): + case "text_reply_to_post": + logger.info( + f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user_id=}." + ) + # here we would store the text reply in the database + response.append( + protocol_schema.TaskDone( + reply_to_post_id=interaction.user_post_id, + addressed_users=[interaction.user_id], + ) + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + return response diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py new file mode 100644 index 00000000..a63b9c11 --- /dev/null +++ b/backend/app/schemas/protocol.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from typing import Literal, Optional +from uuid import UUID + +import pydantic +from pydantic import BaseModel + + +class TaskRequest(BaseModel): + """The frontend asks the backend for a task.""" + + type: Literal + user_id: Optional[str] = None + + +class GenericTaskRequest(TaskRequest): + type: Literal["generic"] = "generic" + + +class Task(BaseModel): + """A task is a unit of work that the backend gives to the frontend.""" + + id: UUID = pydantic.Field(default_factory=UUID) + type: Literal + addressed_users: Optional[list[str]] = None + + +class TaskResponse(BaseModel): + """A task response is a message from the frontend to acknowledge the given task.""" + + type: Literal + status: Literal["success", "failure"] + + +class PostCreatedTaskResponse(TaskResponse): + type: Literal["post_created"] = "post_created" + post_id: UUID + + +class SummarizeStoryTask(Task): + type: Literal["summarize_story"] = "summarize_story" + story: str + + +class TaskDone(Task): + type: Literal["task_done"] = "task_done" + reply_to_post_id: UUID + + +class Interaction(BaseModel): + """An interaction is a message from the frontend to the backend.""" + + type: Literal + user_id: str + + +class TextReplyToPost(Interaction): + """A user has replied to a post with text.""" + + type: Literal["text_reply_to_post"] = "text_reply_to_post" + post_id: UUID + user_post_id: UUID + text: str From 2b02ef2ddcab57c431035edd316051f2a15f87e6 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Wed, 14 Dec 2022 23:51:42 +0100 Subject: [PATCH 010/119] docstring fix --- backend/app/api/v1/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 494281d0..2a5397e5 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -80,7 +80,7 @@ def post_interaction( interaction: protocol_schema.TextReplyToPost, ) -> Any: """ - The frontend acknowledges a task. + The frontend reports an interaction. """ deps.api_auth(api_key, db, create=True) From 1f286196f97b6c940c204ee827c8eefe5d004c02 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Wed, 14 Dec 2022 23:52:36 +0100 Subject: [PATCH 011/119] added gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc From c7d4fbb96f9af2694212c9fe35ee169a79ed274b Mon Sep 17 00:00:00 2001 From: Stephan Auerhahn Date: Wed, 14 Dec 2022 16:26:00 -0800 Subject: [PATCH 012/119] restrict docker builds to versioned tags --- .github/workflows/docker-backend.yaml | 2 ++ .github/workflows/docker-bot.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/docker-backend.yaml b/.github/workflows/docker-backend.yaml index 026002e8..027d8e03 100644 --- a/.github/workflows/docker-backend.yaml +++ b/.github/workflows/docker-backend.yaml @@ -4,6 +4,8 @@ on: push: paths: - "backend/**" + tags: + - v* jobs: build: diff --git a/.github/workflows/docker-bot.yaml b/.github/workflows/docker-bot.yaml index d97bf757..589499a0 100644 --- a/.github/workflows/docker-bot.yaml +++ b/.github/workflows/docker-bot.yaml @@ -4,6 +4,8 @@ on: push: paths: - "bot/**" + tags: + - v* jobs: build: From 15d47e2fd0b759cc6630e4f16cecac7f2811142d Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Thu, 15 Dec 2022 15:30:46 +0900 Subject: [PATCH 013/119] Including vim files in gitignore for web --- website/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/.gitignore b/website/.gitignore index c87c9b39..265e0054 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -34,3 +34,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Vim files +*.swp From 1f670cebd5b35c97fad5e63e16a53c572d06bb9c Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Thu, 15 Dec 2022 15:45:00 +0900 Subject: [PATCH 014/119] Cleaning up some not-used files and adding in the prisma database adapater --- website/.gitignore | 1 + website/lib/prismadb.ts | 12 +++ website/package-lock.json | 97 +++++++++++++++++++ website/package.json | 5 + website/pages/api/auth/[...nextauth].js | 4 + .../20221215064332_initialized/migration.sql | 66 +++++++++++++ website/prisma/migrations/migration_lock.toml | 3 + website/prisma/schema.prisma | 54 +++++++++++ website/src/App.js | 43 -------- website/src/App.test.js | 8 -- website/src/index.css | 13 --- website/src/index.js | 17 ---- website/src/logo.svg | 1 - website/src/reportWebVitals.js | 13 --- website/src/setupTests.js | 5 - 15 files changed, 242 insertions(+), 100 deletions(-) create mode 100644 website/lib/prismadb.ts create mode 100644 website/prisma/migrations/20221215064332_initialized/migration.sql create mode 100644 website/prisma/migrations/migration_lock.toml create mode 100644 website/prisma/schema.prisma delete mode 100644 website/src/App.js delete mode 100644 website/src/App.test.js delete mode 100644 website/src/index.css delete mode 100644 website/src/index.js delete mode 100644 website/src/logo.svg delete mode 100644 website/src/reportWebVitals.js delete mode 100644 website/src/setupTests.js diff --git a/website/.gitignore b/website/.gitignore index 265e0054..d61f5cb6 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -26,6 +26,7 @@ yarn-error.log* .pnpm-debug.log* # local env files +.env .env*.local # vercel diff --git a/website/lib/prismadb.ts b/website/lib/prismadb.ts new file mode 100644 index 00000000..32fb23d0 --- /dev/null +++ b/website/lib/prismadb.ts @@ -0,0 +1,12 @@ +import { PrismaClient } from "@prisma/client"; + +declare global { + var prisma; +} + +const client = globalThis.prisma || new PrismaClient(); +if (process.env.NODE_ENV !== "production") { + globalThis.prisma = client; +} + +export default client; diff --git a/website/package-lock.json b/website/package-lock.json index 486bddff..75f46ee5 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -8,6 +8,8 @@ "name": "website", "version": "0.1.0", "dependencies": { + "@next-auth/prisma-adapter": "^1.0.5", + "@prisma/client": "^4.7.1", "@supabase/auth-helpers-nextjs": "^0.5.2", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", @@ -20,6 +22,9 @@ "react": "18.2.0", "react-dom": "18.2.0", "swr": "^2.0.0" + }, + "devDependencies": { + "prisma": "^4.7.1" } }, "node_modules/@babel/runtime": { @@ -97,6 +102,15 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@next-auth/prisma-adapter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", + "integrity": "sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==", + "peerDependencies": { + "@prisma/client": ">=2.26.0 || >=3", + "next-auth": "^4" + } + }, "node_modules/@next/env": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.6.tgz", @@ -364,6 +378,38 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@prisma/client": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz", + "integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==", + "hasInstallScript": true, + "dependencies": { + "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/engines": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz", + "integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==", + "devOptional": true, + "hasInstallScript": true + }, + "node_modules/@prisma/engines-version": { + "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz", + "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==" + }, "node_modules/@rushstack/eslint-patch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", @@ -2893,6 +2939,23 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, + "node_modules/prisma": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz", + "integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "4.7.1" + }, + "bin": { + "prisma": "build/index.js", + "prisma2": "build/index.js" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3629,6 +3692,12 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@next-auth/prisma-adapter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", + "integrity": "sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==", + "requires": {} + }, "@next/env": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.6.tgz", @@ -3761,6 +3830,25 @@ "tslib": "^2.4.0" } }, + "@prisma/client": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.7.1.tgz", + "integrity": "sha512-/GbnOwIPtjiveZNUzGXOdp7RxTEkHL4DZP3vBaFNadfr6Sf0RshU5EULFzVaSi9i9PIK9PYd+1Rn7z2B2npb9w==", + "requires": { + "@prisma/engines-version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c" + } + }, + "@prisma/engines": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.7.1.tgz", + "integrity": "sha512-zWabHosTdLpXXlMefHmnouhXMoTB1+SCbUU3t4FCmdrtIOZcarPKU3Alto7gm/pZ9vHlGOXHCfVZ1G7OIrSbog==", + "devOptional": true + }, + "@prisma/engines-version": { + "version": "4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.7.1-1.272861e07ab64f234d3ffc4094e32bd61775599c.tgz", + "integrity": "sha512-Bd4LZ+WAnUHOq31e9X/ihi5zPlr4SzTRwUZZYxvWOxlerIZ7HJlVa9zXpuKTKLpI9O1l8Ec4OYCKsivWCs5a3Q==" + }, "@rushstack/eslint-patch": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", @@ -5544,6 +5632,15 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" }, + "prisma": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.7.1.tgz", + "integrity": "sha512-CCQP+m+1qZOGIZlvnL6T3ZwaU0LAleIHYFPN9tFSzjs/KL6vH9rlYbGOkTuG9Q1s6Ki5D0LJlYlW18Z9EBUpGg==", + "devOptional": true, + "requires": { + "@prisma/engines": "4.7.1" + } + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/website/package.json b/website/package.json index ab8ac35c..2afe3321 100644 --- a/website/package.json +++ b/website/package.json @@ -10,6 +10,8 @@ "lint": "next lint" }, "dependencies": { + "@next-auth/prisma-adapter": "^1.0.5", + "@prisma/client": "^4.7.1", "@supabase/auth-helpers-nextjs": "^0.5.2", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", @@ -22,5 +24,8 @@ "react": "18.2.0", "react-dom": "18.2.0", "swr": "^2.0.0" + }, + "devDependencies": { + "prisma": "^4.7.1" } } diff --git a/website/pages/api/auth/[...nextauth].js b/website/pages/api/auth/[...nextauth].js index baea95a5..45fcd57e 100644 --- a/website/pages/api/auth/[...nextauth].js +++ b/website/pages/api/auth/[...nextauth].js @@ -1,7 +1,11 @@ import NextAuth from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; +import { PrismaAdapter } from "@next-auth/prisma-adapter"; + +import prisma from "../../../lib/prismadb"; export const authOptions = { + adapter: PrismaAdapter(prisma), providers: [ DiscordProvider({ clientId: process.env.DISCORD_CLIENT_ID, diff --git a/website/prisma/migrations/20221215064332_initialized/migration.sql b/website/prisma/migrations/20221215064332_initialized/migration.sql new file mode 100644 index 00000000..7ae4196d --- /dev/null +++ b/website/prisma/migrations/20221215064332_initialized/migration.sql @@ -0,0 +1,66 @@ +-- CreateTable +CREATE TABLE "Account" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "provider" TEXT NOT NULL, + "providerAccountId" TEXT NOT NULL, + "refresh_token" TEXT, + "access_token" TEXT, + "expires_at" INTEGER, + "token_type" TEXT, + "scope" TEXT, + "id_token" TEXT, + "session_state" TEXT, + + CONSTRAINT "Account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" TEXT NOT NULL, + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "name" TEXT, + "email" TEXT, + "emailVerified" TIMESTAMP(3), + "image" TEXT, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "VerificationToken" ( + "identifier" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token"); + +-- AddForeignKey +ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/website/prisma/migrations/migration_lock.toml b/website/prisma/migrations/migration_lock.toml new file mode 100644 index 00000000..99e4f200 --- /dev/null +++ b/website/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" diff --git a/website/prisma/schema.prisma b/website/prisma/schema.prisma new file mode 100644 index 00000000..9f2669f3 --- /dev/null +++ b/website/prisma/schema.prisma @@ -0,0 +1,54 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["referentialActions"] +} + +model Account { + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? @db.Text + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) +} + +model Session { + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model User { + id String @id @default(cuid()) + name String? + email String? @unique + emailVerified DateTime? + image String? + accounts Account[] + sessions Session[] +} + +model VerificationToken { + identifier String + token String @unique + expires DateTime + + @@unique([identifier, token]) +} diff --git a/website/src/App.js b/website/src/App.js deleted file mode 100644 index f6282e63..00000000 --- a/website/src/App.js +++ /dev/null @@ -1,43 +0,0 @@ -import logo from "./logo.svg"; -import "./App.css"; - -function App() { - return ( -
-
- {/* logo */} -

Open Chat Gpt

-

- Open chat gpt is a project meant to give everyone access to a great - chat based large language model. -

-

- We believe that by doing this we will create a revolution in - innovation in language. In the same way that stable-diffusion helped - the world make art and images in new ways we hope open chat gpt can - help improve the world by improving language itself. -

- -

How can you help?

-

- All open source projects begins with people like you. Open source is - the belief that if we collaborate we can together gift our knowledge - and technology to the world for the benefit of humanity. -

- -

I'm in! Now what?

-

We live and collaborate the work in the LAION discord. Join us!

- - Join us on Discord - -
-
- ); -} - -export default App; diff --git a/website/src/App.test.js b/website/src/App.test.js deleted file mode 100644 index 9382b9ad..00000000 --- a/website/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import App from "./App"; - -test("renders learn react link", () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/website/src/index.css b/website/src/index.css deleted file mode 100644 index 4a1df4db..00000000 --- a/website/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} diff --git a/website/src/index.js b/website/src/index.js deleted file mode 100644 index 770ee7d6..00000000 --- a/website/src/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import App from "./App"; -import reportWebVitals from "./reportWebVitals"; - -const root = ReactDOM.createRoot(document.getElementById("root")); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/website/src/logo.svg b/website/src/logo.svg deleted file mode 100644 index 71694760..00000000 --- a/website/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/website/src/reportWebVitals.js b/website/src/reportWebVitals.js deleted file mode 100644 index 9ecd33f9..00000000 --- a/website/src/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = (onPerfEntry) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/website/src/setupTests.js b/website/src/setupTests.js deleted file mode 100644 index 1dd407a6..00000000 --- a/website/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import "@testing-library/jest-dom"; From c0e9f037c673d0db8f0877bd4885cf016b36fc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 10:39:36 +0100 Subject: [PATCH 015/119] add config defaults for postgres dev docker image, support setting POSTGRES_PASSWORD --- .gitignore | 1 + backend/app/config.py | 20 ++++++++++++++++++-- backend/app/schemas/protocol.py | 8 ++++---- backend/scripts/run-local.sh | 2 -- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b648..eb3a37f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.venv *.pyc diff --git a/backend/app/config.py b/backend/app/config.py index 85cd8631..f0218bee 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# touch -from typing import List, Optional, Union +from typing import Any, Dict, List, Optional, Union from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator @@ -8,8 +7,25 @@ from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator class Settings(BaseSettings): PROJECT_NAME: str = "open-chatGPT backend" API_V1_STR: str = "/api/v1" + + POSTGRES_SERVER: str = "localhost:5432" + POSTGRES_USER: str = "postgres" + POSTGRES_PASSWORD: str = "postgres" + POSTGRES_DB: str = "postgres" DATABASE_URI: Optional[PostgresDsn] = None + @validator("DATABASE_URI", pre=True) + def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: + if isinstance(v, str): + return v + return PostgresDsn.build( + scheme="postgresql", + user=values.get("POSTGRES_USER"), + password=values.get("POSTGRES_PASSWORD"), + host=values.get("POSTGRES_SERVER"), + path=f"/{values.get('POSTGRES_DB') or ''}", + ) + BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = [] UPDATE_ALEMBIC: bool = True diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index a63b9c11..554c49e1 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -9,7 +9,7 @@ from pydantic import BaseModel class TaskRequest(BaseModel): """The frontend asks the backend for a task.""" - type: Literal + type: str user_id: Optional[str] = None @@ -21,14 +21,14 @@ class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" id: UUID = pydantic.Field(default_factory=UUID) - type: Literal + type: str addressed_users: Optional[list[str]] = None class TaskResponse(BaseModel): """A task response is a message from the frontend to acknowledge the given task.""" - type: Literal + type: str status: Literal["success", "failure"] @@ -50,7 +50,7 @@ class TaskDone(Task): class Interaction(BaseModel): """An interaction is a message from the frontend to the backend.""" - type: Literal + type: str user_id: str diff --git a/backend/scripts/run-local.sh b/backend/scripts/run-local.sh index 7c6efabc..d8bd86c6 100755 --- a/backend/scripts/run-local.sh +++ b/backend/scripts/run-local.sh @@ -1,5 +1,3 @@ #!/usr/bin/env bash -export DATABASE_URI=postgresql://postgres:postgres@localhost:5432/postgres - uvicorn app.main:app --reload From 2d7c7f317d3c1be23c1138428cc89299ed02782c Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Thu, 15 Dec 2022 19:25:31 +0900 Subject: [PATCH 016/119] Adding support for an email login method and registering new users with the labeler backend. --- backend/app/api/v1/labelers.py | 3 + backend/scripts/run-local.sh | 2 +- website/package-lock.json | 14 +++++ website/package.json | 1 + website/pages/api/auth/[...nextauth].js | 61 ++++++++++++++++++- website/pages/api/prompts.js | 25 +++++--- .../migration.sql | 2 + website/prisma/schema.prisma | 5 +- 8 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 website/prisma/migrations/20221215101838_record_labeler_id/migration.sql diff --git a/backend/app/api/v1/labelers.py b/backend/app/api/v1/labelers.py index c2a89181..e4613e81 100644 --- a/backend/app/api/v1/labelers.py +++ b/backend/app/api/v1/labelers.py @@ -38,8 +38,11 @@ def create_labeler( """ Create new labeler. """ + print("create_labelere") deps.api_auth(api_key, db, create=True) + print(item_in) item = crud.labeler.create(db=db, obj_in=item_in) + print(item) return item diff --git a/backend/scripts/run-local.sh b/backend/scripts/run-local.sh index 7c6efabc..6e89ddf0 100755 --- a/backend/scripts/run-local.sh +++ b/backend/scripts/run-local.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -export DATABASE_URI=postgresql://postgres:postgres@localhost:5432/postgres +export DATABASE_URI=postgresql://fozziethebeat@localhost:5432/ocgpt_backend uvicorn app.main:app --reload diff --git a/website/package-lock.json b/website/package-lock.json index 75f46ee5..58e33695 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -19,6 +19,7 @@ "eslint-config-next": "13.0.6", "next": "13.0.6", "next-auth": "^4.18.6", + "nodemailer": "^6.8.0", "react": "18.2.0", "react-dom": "18.2.0", "swr": "^2.0.0" @@ -2618,6 +2619,14 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/nodemailer": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", + "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -5418,6 +5427,11 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" }, + "nodemailer": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", + "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==" + }, "oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", diff --git a/website/package.json b/website/package.json index 2afe3321..aa06bb05 100644 --- a/website/package.json +++ b/website/package.json @@ -21,6 +21,7 @@ "eslint-config-next": "13.0.6", "next": "13.0.6", "next-auth": "^4.18.6", + "nodemailer": "^6.8.0", "react": "18.2.0", "react-dom": "18.2.0", "swr": "^2.0.0" diff --git a/website/pages/api/auth/[...nextauth].js b/website/pages/api/auth/[...nextauth].js index 45fcd57e..06044ac1 100644 --- a/website/pages/api/auth/[...nextauth].js +++ b/website/pages/api/auth/[...nextauth].js @@ -1,23 +1,82 @@ import NextAuth from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; +import EmailProvider from "next-auth/providers/email"; import { PrismaAdapter } from "@next-auth/prisma-adapter"; import prisma from "../../../lib/prismadb"; export const authOptions = { + // Ensure we can store user data in a database. adapter: PrismaAdapter(prisma), providers: [ + // Register a Discord auth method. DiscordProvider({ clientId: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET, }), + // Register an email magic link auth method. + EmailProvider({ + server: { + host: process.env.EMAIL_SERVER_HOST, + port: process.env.EMAIL_SERVER_PORT, + auth: { + user: process.env.EMAIL_SERVER_USER, + pass: process.env.EMAIL_SERVER_PASSWORD, + }, + }, + from: process.env.EMAIL_FROM, + }), ], callbacks: { + /** + * Includes the raw user id in the session object. + */ async session({ session, token, user }) { - session.user.sub = token.sub; + session.user.id = user.id; return session; }, }, + events: { + /** + * When a new user signs in, we register them with the Labeler backend. + */ + async signIn({ user, account, profile, isNewUser }) { + if (!isNewUser) { + return; + } + try { + // Register the new user with the Labeler Backend. + const res = await fetch(`${process.env.FASTAPI_URL}/api/v1/labelers`, { + method: "POST", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + discord_username: user.id, + display_name: user.name || user.email, + is_enabled: true, + notes: account.provider, + }), + }); + if (res.status !== 200) { + console.error(res.statusText); + return; + } + // Update the User entry with the Labeler Backend's ID so we can + // reference it later. + const { id: labelerId } = await res.json(); + await prisma.user.update({ + where: { id: user.id }, + data: { + labelerId, + }, + }); + } catch (error) { + console.error(error); + } + }, + }, }; export default NextAuth(authOptions); diff --git a/website/pages/api/prompts.js b/website/pages/api/prompts.js index 20e35f73..9d09641c 100644 --- a/website/pages/api/prompts.js +++ b/website/pages/api/prompts.js @@ -1,19 +1,28 @@ import { unstable_getServerSession } from "next-auth/next"; import { authOptions } from "./auth/[...nextauth]"; +/** + * Returns a list of prompts from the Labeler Backend. + */ export default async (req, res) => { const session = await unstable_getServerSession(req, res, authOptions); if (!session) { - res.status(200).json([{ name: "cat" }]); + res.status(401).end(); return; } - const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, { - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - }, - }); - const prompts = await promptRes.json(); + try { + const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, { + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + }, + }); + const prompts = await promptRes.json(); - res.status(200).json(prompts); + res.status(200).json(prompts); + } catch (error) { + console.error(error); + res.status(500); + } + res.end(); }; diff --git a/website/prisma/migrations/20221215101838_record_labeler_id/migration.sql b/website/prisma/migrations/20221215101838_record_labeler_id/migration.sql new file mode 100644 index 00000000..acbdbf8d --- /dev/null +++ b/website/prisma/migrations/20221215101838_record_labeler_id/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "labelerId" INTEGER; diff --git a/website/prisma/schema.prisma b/website/prisma/schema.prisma index 9f2669f3..5ef3e0f0 100644 --- a/website/prisma/schema.prisma +++ b/website/prisma/schema.prisma @@ -5,7 +5,6 @@ datasource db { generator client { provider = "prisma-client-js" - previewFeatures = ["referentialActions"] } model Account { @@ -41,6 +40,10 @@ model User { email String? @unique emailVerified DateTime? image String? + + // Records the unique user id stored in the Labeler Backend. + labelerId Int? + accounts Account[] sessions Session[] } From 34dcf81594e4b6266731fa8bfc1f19329c14ea0d Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Thu, 15 Dec 2022 19:28:27 +0900 Subject: [PATCH 017/119] Undoing some debug statements --- backend/app/api/v1/labelers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/app/api/v1/labelers.py b/backend/app/api/v1/labelers.py index e4613e81..c2a89181 100644 --- a/backend/app/api/v1/labelers.py +++ b/backend/app/api/v1/labelers.py @@ -38,11 +38,8 @@ def create_labeler( """ Create new labeler. """ - print("create_labelere") deps.api_auth(api_key, db, create=True) - print(item_in) item = crud.labeler.create(db=db, obj_in=item_in) - print(item) return item From 8515d09bd764d89fc8c7caa08861b163452335af Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Thu, 15 Dec 2022 12:38:15 +0100 Subject: [PATCH 018/119] removed duplicate api_key entry --- backend/app/models/service_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/app/models/service_client.py b/backend/app/models/service_client.py index f7b8295a..1a60b0be 100644 --- a/backend/app/models/service_client.py +++ b/backend/app/models/service_client.py @@ -10,7 +10,6 @@ class ServiceClient(SQLModel, table=True): name: str api_key: str service_admin_email: Optional[str] = None - api_key: str can_append: bool = True can_write: bool = False can_delete: bool = False From e444343fadbdea91521d7abdf53c5ba7c4444453 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Thu, 15 Dec 2022 12:50:53 +0100 Subject: [PATCH 019/119] protocol uuid factory fix --- backend/app/schemas/protocol.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 554c49e1..72f91e51 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from typing import Literal, Optional -from uuid import UUID +from uuid import UUID, uuid4 import pydantic from pydantic import BaseModel @@ -20,7 +20,7 @@ class GenericTaskRequest(TaskRequest): class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" - id: UUID = pydantic.Field(default_factory=UUID) + id: UUID = pydantic.Field(default_factory=uuid4) type: str addressed_users: Optional[list[str]] = None From 95a1ec9771a4f8cba5bdcba146db378281fa10cc Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Thu, 15 Dec 2022 12:58:45 +0100 Subject: [PATCH 020/119] changed post IDs to strings --- backend/app/schemas/protocol.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 72f91e51..f1400a96 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -34,7 +34,7 @@ class TaskResponse(BaseModel): class PostCreatedTaskResponse(TaskResponse): type: Literal["post_created"] = "post_created" - post_id: UUID + post_id: str class SummarizeStoryTask(Task): @@ -44,7 +44,7 @@ class SummarizeStoryTask(Task): class TaskDone(Task): type: Literal["task_done"] = "task_done" - reply_to_post_id: UUID + reply_to_post_id: str class Interaction(BaseModel): @@ -58,6 +58,6 @@ class TextReplyToPost(Interaction): """A user has replied to a post with text.""" type: Literal["text_reply_to_post"] = "text_reply_to_post" - post_id: UUID - user_post_id: UUID + post_id: str + user_post_id: str text: str From 7ccb55e7560f1cde7279e6c6a90d36b92dd1aec0 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Thu, 15 Dec 2022 13:03:44 +0100 Subject: [PATCH 021/119] implemented a simple text-based frontend --- backend/app/api/deps.py | 5 ++++ backend/app/api/v1/tasks.py | 9 ++++-- backend/app/config.py | 2 ++ backend/app/schemas/protocol.py | 14 ++++----- backend/scripts/run-local.sh | 2 ++ text-frontend/__main__.py | 52 +++++++++++++++++++++++++++++++++ text-frontend/requirements.txt | 2 ++ 7 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 text-frontend/__main__.py create mode 100644 text-frontend/requirements.txt diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index deed0a3d..cecb5860 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from typing import Generator +from app.config import settings from app.database import engine from app.models import ServiceClient from fastapi import HTTPException, Security @@ -37,6 +38,10 @@ def api_auth( delete: bool = False, ) -> ServiceClient: if api_key is not None: + if settings.ALLOW_ANY_API_KEY: + return ServiceClient( + api_key=api_key, name=api_key, can_append=True, can_read=True, can_write=True, can_delete=True + ) api_client = db.query(ServiceClient).filter(ServiceClient.api_key == api_key).first() if api_client is not None: if ( diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 2a5397e5..07d0f9d1 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -31,9 +31,12 @@ def request_task( case "generic": # here we create a task at random (and store it in the database) logger.info("Frontend requested a generic task.") - task = protocol_schema.SummarizeStoryTask( - story="This is a story. A very long story. So long, it needs to be summarized.", - ) + try: + task = protocol_schema.SummarizeStoryTask( + story="This is a story. A very long story. So long, it needs to be summarized.", + ) + except Exception: + logger.exception("Failed to create task.") case _: raise HTTPException( diff --git a/backend/app/config.py b/backend/app/config.py index f0218bee..0780caf9 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -14,6 +14,8 @@ class Settings(BaseSettings): POSTGRES_DB: str = "postgres" DATABASE_URI: Optional[PostgresDsn] = None + ALLOW_ANY_API_KEY: bool = False + @validator("DATABASE_URI", pre=True) def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: if isinstance(v, str): diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 554c49e1..c1976bf7 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from typing import Literal, Optional -from uuid import UUID +from uuid import UUID, uuid4 import pydantic from pydantic import BaseModel @@ -20,7 +20,7 @@ class GenericTaskRequest(TaskRequest): class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" - id: UUID = pydantic.Field(default_factory=UUID) + id: UUID = pydantic.Field(default_factory=uuid4) type: str addressed_users: Optional[list[str]] = None @@ -29,12 +29,12 @@ class TaskResponse(BaseModel): """A task response is a message from the frontend to acknowledge the given task.""" type: str - status: Literal["success", "failure"] + status: Literal["success", "failure"] = "success" class PostCreatedTaskResponse(TaskResponse): type: Literal["post_created"] = "post_created" - post_id: UUID + post_id: str class SummarizeStoryTask(Task): @@ -44,7 +44,7 @@ class SummarizeStoryTask(Task): class TaskDone(Task): type: Literal["task_done"] = "task_done" - reply_to_post_id: UUID + reply_to_post_id: str class Interaction(BaseModel): @@ -58,6 +58,6 @@ class TextReplyToPost(Interaction): """A user has replied to a post with text.""" type: Literal["text_reply_to_post"] = "text_reply_to_post" - post_id: UUID - user_post_id: UUID + post_id: str + user_post_id: str text: str diff --git a/backend/scripts/run-local.sh b/backend/scripts/run-local.sh index d8bd86c6..bf45e870 100755 --- a/backend/scripts/run-local.sh +++ b/backend/scripts/run-local.sh @@ -1,3 +1,5 @@ #!/usr/bin/env bash +export ALLOW_ANY_API_KEY=True + uvicorn app.main:app --reload diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py new file mode 100644 index 00000000..dab0c745 --- /dev/null +++ b/text-frontend/__main__.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +"""Simple REPL frontend.""" + +import requests +import typer + +app = typer.Typer() + + +@app.command() +def main(backend_url: str, api_key: str): + """Simple REPL frontend.""" + + def _post(path: str, json: dict) -> dict: + response = requests.post(f"{backend_url}{path}", json=json, headers={"X-API-Key": api_key}) + response.raise_for_status() + return response.json() + + typer.echo("Requesting work...") + tasks = _post("/api/v1/tasks/", {"type": "generic"}) + while tasks: + task = tasks.pop(0) + match (task["type"]): + case "summarize_story": + typer.echo("Summarize the following story:") + typer.echo(task["story"]) + + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": "1234"}) + + summary = typer.prompt("Enter your summary") + + # send interaction + new_tasks = _post( + "/api/v1/tasks/interaction", + { + "type": "text_reply_to_post", + "post_id": "1234", + "user_post_id": "5678", + "text": summary, + "user_id": "1234", + }, + ) + tasks.extend(new_tasks) + case "task_done": + typer.echo("Task done!") + case _: + typer.echo(f"Unknown task type {task['type']}") + + +if __name__ == "__main__": + app() diff --git a/text-frontend/requirements.txt b/text-frontend/requirements.txt new file mode 100644 index 00000000..f48a93ad --- /dev/null +++ b/text-frontend/requirements.txt @@ -0,0 +1,2 @@ +requests==2.18.1 +typer==0.7.0 From e1a7ae42aa59d2f4792627169229b4aab7cd748e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 12:58:36 +0100 Subject: [PATCH 022/119] add v1 database alembic migration script --- .../versions/cd7de470586e_v1_db_structure.py | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 backend/alembic/versions/cd7de470586e_v1_db_structure.py diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py new file mode 100644 index 00000000..f1477937 --- /dev/null +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +"""v1 db structure + +Revision ID: cd7de470586e +Revises: 23e5fea252dd +Create Date: 2022-12-15 11:15:32.830225 + +""" +import uuid + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects.postgresql import UUID + +# revision identifiers, used by Alembic. +revision = "cd7de470586e" +down_revision = "23e5fea252dd" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # remove database objects + op.drop_index(op.f("prompt_labeler_id"), table_name="prompt") + op.drop_table("prompt") + op.drop_table("labeler") + op.drop_index(op.f("ix_service_client_api_key"), table_name="service_client") + op.drop_table("service_client") + + # wreate new database structure + op.create_table( + "api_client", + sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), + sa.Column("api_key", sa.String(512), nullable=False), + sa.Column("description", sa.String(256), nullable=False), + sa.Column("admin_email", sa.String(256), nullable=True), + sa.Column("enabled", sa.Boolean, default=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_api_client_api_key"), "api_client", ["api_key"], unique=True) + + op.create_table( + "person", + sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), + sa.Column("username", sa.String(128), nullable=False), # unique in combination with api_client_id + sa.Column("display_name", sa.String(256), nullable=False), # cached last seen display_name + sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), + ) + op.create_index(op.f("ix_person_username"), "person", ["api_client_id", "username"], unique=True) + + op.create_table( + "person_stats", + sa.Column("person_id", UUID(as_uuid=True)), + sa.Column("leader_score", sa.Integer, default=0, nullable=False), # determines position on leader board + sa.Column("modified_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + sa.Column("reactions", sa.Integer, default=0, nullable=False), # reactions sent by user + sa.Column("posts", sa.Integer, default=0, nullable=False), # posts sent by user + sa.Column("upvotes", sa.Integer, default=0, nullable=False), # received upvotes (form other users) + sa.Column("downvotes", sa.Integer, default=0, nullable=False), # received downvotes (from other users) + sa.Column("work_reward", sa.Integer, default=0, nullable=False), # reward for workpackage completions + sa.Column("compare_wins", sa.Integer, default=0, nullable=False), # num times user's post won compare tasks + sa.Column("compare_losses", sa.Integer, default=0, nullable=False), # num times users's post lost compare tasks + sa.PrimaryKeyConstraint("person_id"), + sa.ForeignKeyConstraint(["person_id"], ["person.id"]), + ) + + op.create_table( + "work_package", + sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), + sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + sa.Column("expiry_date", sa.DateTime(), nullable=True), + sa.Column("person_id", UUID(as_uuid=True), nullable=False), + sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid + sa.Column("payload", sa.Text, nullable=False), + sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.ForeignKeyConstraint(["person_id"], ["person.id"]), + sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), + ) + op.create_index(op.f("ix_work_package_person_id"), "work_package", ["person_id"], unique=False) + + op.create_table( + "post", + sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), + sa.Column("parent_id", UUID(as_uuid=True), nullable=True), # root posts have NULL parent + sa.Column("thread_id", UUID(as_uuid=True), nullable=False), # id of thread root + sa.Column("workpackage_id", UUID(as_uuid=True), nullable=True), # workpackage id to pass to handler on reply + sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) + sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), + sa.Column("role", sa.String(128), nullable=False), # 'assistant', 'user' or something else + sa.Column("frontend_post_id", sa.String(200)), # unique together with api_client_id + sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid + sa.Column("payload", sa.Text, nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.ForeignKeyConstraint(["person_id"], ["person.id"]), + sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), + ) + op.create_index(op.f("ix_post_frontend_post_id"), "post", ["api_client_id", "frontend_post_id"], unique=True) + op.create_index(op.f("ix_post_thread_id"), "post", ["thread_id"], unique=False) + op.create_index(op.f("ix_post_workpackage_id"), "post", ["workpackage_id"], unique=False) + op.create_index(op.f("ix_post_person"), "post", ["person_id"], unique=False) + + op.create_table( + "post_reaction", + sa.Column("post_id", UUID(as_uuid=True), nullable=False), + sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) + sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid + sa.Column("payload", sa.String(1024), nullable=False), + sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), + sa.PrimaryKeyConstraint("post_id", "person_id"), + sa.ForeignKeyConstraint(["person_id"], ["person.id"]), + sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), + ) + + +def downgrade() -> None: + op.drop_table("post_reaction") + + op.drop_index("ix_post_person") + op.drop_index("ix_post_workpackage_id") + op.drop_index("ix_post_thread_id") + op.drop_index("ix_post_frontend_post_id") + op.drop_table("post") + + op.drop_index("ix_work_package_person_id") + op.drop_table("work_package") + + op.drop_table("person_stats") + + op.drop_index("ix_person_username") + op.drop_table("person") + + op.drop_index("ix_api_client_api_key") + op.drop_table("api_client") + + op.create_table( + "service_client", + sa.Column("id", sa.Integer, sa.Identity()), + sa.Column("name", sa.String(200), nullable=False), + sa.Column("service_admin_email", sa.String(128), nullable=True), + sa.Column("api_key", sa.String(300), nullable=False), + sa.Column("can_append", sa.Boolean, nullable=False, server_default="true"), + sa.Column("can_write", sa.Boolean, nullable=False, server_default="false"), + sa.Column("can_delete", sa.Boolean, nullable=False, server_default="false"), + sa.Column("can_read", sa.Boolean, nullable=False, server_default="true"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_service_client_api_key"), "service_client", ["api_key"], unique=True) + + op.create_table( + "labeler", + sa.Column("id", sa.Integer, sa.Identity()), + sa.Column("display_name", sa.String(96), nullable=False), + sa.Column("discord_username", sa.String(96), nullable=True), + sa.Column( + "created_date", + sa.DateTime, + nullable=False, + server_default=sa.func.current_timestamp(), + ), + sa.Column("is_enabled", sa.Boolean, nullable=False, server_default="true"), + sa.Column("notes", sa.String(10 * 1024), nullable=True), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("discord_username"), + ) + + op.create_table( + "prompt", + sa.Column("id", sa.Integer, sa.Identity()), + sa.Column("labeler_id", sa.Integer, nullable=False), + sa.Column("prompt", sa.Text, nullable=False), + sa.Column("response", sa.Text, nullable=True), + sa.Column("lang", sa.String(32), nullable=True), + sa.Column( + "created_date", + sa.DateTime(), + nullable=False, + server_default=sa.func.current_timestamp(), + ), + sa.ForeignKeyConstraint( + ["labeler_id"], + ["labeler.id"], + ), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("prompt_labeler_id"), "prompt", ["labeler_id"], unique=False) From 7d88ff4a8c3e56abb0ac1e97eab5a94516b7b7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 13:20:36 +0100 Subject: [PATCH 023/119] use JSONB instead of Text of payload columns --- backend/alembic/versions/cd7de470586e_v1_db_structure.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py index f1477937..8fab7b94 100644 --- a/backend/alembic/versions/cd7de470586e_v1_db_structure.py +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -10,7 +10,7 @@ import uuid import sqlalchemy as sa from alembic import op -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.dialects.postgresql import JSONB, UUID # revision identifiers, used by Alembic. revision = "cd7de470586e" @@ -73,7 +73,7 @@ def upgrade() -> None: sa.Column("expiry_date", sa.DateTime(), nullable=True), sa.Column("person_id", UUID(as_uuid=True), nullable=False), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", sa.Text, nullable=False), + sa.Column("payload", JSONB(), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), @@ -93,7 +93,7 @@ def upgrade() -> None: sa.Column("frontend_post_id", sa.String(200)), # unique together with api_client_id sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", sa.Text, nullable=False), + sa.Column("payload", JSONB(), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), From a827b7f19ea6a1763d73e55f3eaf2917ced66739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 16:13:36 +0100 Subject: [PATCH 024/119] add sqlmodel classes --- .../versions/cd7de470586e_v1_db_structure.py | 16 ++++++---- backend/app/models/__init__.py | 18 ++++++++++- backend/app/models/api_client.py | 21 ++++++++++++ backend/app/models/labeler.py | 3 +- backend/app/models/person.py | 26 +++++++++++++++ backend/app/models/person_stats.py | 28 ++++++++++++++++ backend/app/models/post.py | 32 +++++++++++++++++++ backend/app/models/post_reaction.py | 26 +++++++++++++++ backend/app/models/prompt.py | 3 +- backend/app/models/work_package.py | 27 ++++++++++++++++ 10 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 backend/app/models/api_client.py create mode 100644 backend/app/models/person.py create mode 100644 backend/app/models/person_stats.py create mode 100644 backend/app/models/post.py create mode 100644 backend/app/models/post_reaction.py create mode 100644 backend/app/models/work_package.py diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py index 8fab7b94..c394fe4f 100644 --- a/backend/alembic/versions/cd7de470586e_v1_db_structure.py +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -34,7 +34,7 @@ def upgrade() -> None: sa.Column("api_key", sa.String(512), nullable=False), sa.Column("description", sa.String(256), nullable=False), sa.Column("admin_email", sa.String(256), nullable=True), - sa.Column("enabled", sa.Boolean, default=True), + sa.Column("enabled", sa.Boolean, default=True, nullable=False), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_api_client_api_key"), "api_client", ["api_key"], unique=True) @@ -44,6 +44,7 @@ def upgrade() -> None: sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), sa.Column("username", sa.String(128), nullable=False), # unique in combination with api_client_id sa.Column("display_name", sa.String(256), nullable=False), # cached last seen display_name + sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), @@ -73,7 +74,7 @@ def upgrade() -> None: sa.Column("expiry_date", sa.DateTime(), nullable=True), sa.Column("person_id", UUID(as_uuid=True), nullable=False), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", JSONB(), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), @@ -90,10 +91,10 @@ def upgrade() -> None: sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.Column("role", sa.String(128), nullable=False), # 'assistant', 'user' or something else - sa.Column("frontend_post_id", sa.String(200)), # unique together with api_client_id + sa.Column("frontend_post_id", sa.String(200), nullable=False), # unique together with api_client_id sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", JSONB(), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), @@ -101,7 +102,7 @@ def upgrade() -> None: op.create_index(op.f("ix_post_frontend_post_id"), "post", ["api_client_id", "frontend_post_id"], unique=True) op.create_index(op.f("ix_post_thread_id"), "post", ["thread_id"], unique=False) op.create_index(op.f("ix_post_workpackage_id"), "post", ["workpackage_id"], unique=False) - op.create_index(op.f("ix_post_person"), "post", ["person_id"], unique=False) + op.create_index(op.f("ix_post_person_id"), "post", ["person_id"], unique=False) op.create_table( "post_reaction", @@ -109,9 +110,10 @@ def upgrade() -> None: sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", sa.String(1024), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("post_id", "person_id"), + sa.ForeignKeyConstraint(["post_id"], ["post.id"]), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), ) @@ -120,7 +122,7 @@ def upgrade() -> None: def downgrade() -> None: op.drop_table("post_reaction") - op.drop_index("ix_post_person") + op.drop_index("ix_post_person_id") op.drop_index("ix_post_workpackage_id") op.drop_index("ix_post_thread_id") op.drop_index("ix_post_frontend_post_id") diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index f12c41f3..e05e4936 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -1,6 +1,22 @@ # -*- coding: utf-8 -*- +from .api_client import ApiClient from .labeler import Labeler +from .person import Person +from .person_stats import PersonStats +from .post import Post +from .post_reaction import PostReaction from .prompt import Prompt from .service_client import ServiceClient +from .work_package import WorkPackage -__all__ = ["Labeler", "Prompt", "ServiceClient"] +__all__ = [ + "ApiClient", + "Person", + "PersonStats", + "Post", + "PostReaction", + "WorkPackage", + "Labeler", + "Prompt", + "ServiceClient", +] diff --git a/backend/app/models/api_client.py b/backend/app/models/api_client.py new file mode 100644 index 00000000..f0050a90 --- /dev/null +++ b/backend/app/models/api_client.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, SQLModel + + +class ApiClient(SQLModel, table=True): + __tablename__ = "api_client" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + api_key: str = Field(max_length=512, index=True, unique=True) + description: str = Field(max_length=256) + admin_email: Optional[str] = Field(max_length=256, nullable=True) + enabled: bool = Field(default=True) diff --git a/backend/app/models/labeler.py b/backend/app/models/labeler.py index 9dd775b1..9ea4251b 100644 --- a/backend/app/models/labeler.py +++ b/backend/app/models/labeler.py @@ -12,8 +12,7 @@ class Labeler(SQLModel, table=True): display_name: str discord_username: str created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), - nullable=False, + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) is_enabled: bool notes: str diff --git a/backend/app/models/person.py b/backend/app/models/person.py new file mode 100644 index 00000000..57719a4a --- /dev/null +++ b/backend/app/models/person.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, Index, SQLModel + + +class Person(SQLModel, table=True): + __tablename__ = "person" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + username: str = Field(nullable=False, max_length=128) + display_name: str = Field(nullable=False, max_length=256) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + api_client_id: UUID = Field(foreign_key="api_client.id") + + __table_args__ = (Index("ix_person_username", "api_client_id", "username", unique=True),) diff --git a/backend/app/models/person_stats.py b/backend/app/models/person_stats.py new file mode 100644 index 00000000..d05e0047 --- /dev/null +++ b/backend/app/models/person_stats.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, SQLModel + + +class PersonStats(SQLModel, table=True): + __tablename__ = "person_stats" + + person_id: Optional[UUID] = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("person.id"), primary_key=True) + ) + leader_score: int = 0 + modified_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + + reactions: int = 0 # reactions sent by user + posts: int = 0 # posts sent by user + upvotes: int = 0 # received upvotes (form other users) + downvotes: int = 0 # received downvotes (from other users) + work_reward: int = 0 # reward for workpackage completions + compare_wins: int = 0 # num times user's post won compare tasks + compare_losses: int = 0 # num times users's post lost compare tasks diff --git a/backend/app/models/post.py b/backend/app/models/post.py new file mode 100644 index 00000000..f73fd34b --- /dev/null +++ b/backend/app/models/post.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, Index, SQLModel + + +class Post(SQLModel, table=True): + __tablename__ = "post" + __table_args__ = (Index("ix_post_frontend_post_id", "api_client_id", "frontend_post_id", unique=True),) + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + parent_id: UUID = Field(nullable=True) + thread_id: UUID = Field(nullable=False, index=True) + workpackage_id: UUID = Field(nullable=True, index=True) + person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + role: str = Field(nullable=False, max_length=128) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") + frontend_post_id: str = Field(max_length=200, nullable=False) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) diff --git a/backend/app/models/post_reaction.py b/backend/app/models/post_reaction.py new file mode 100644 index 00000000..7a7c9269 --- /dev/null +++ b/backend/app/models/post_reaction.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, SQLModel + + +class PostReaction(SQLModel, table=True): + __tablename__ = "post_reaction" + + post_id: Optional[UUID] = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("post.id"), nullable=False, primary_key=True) + ) + person_id: UUID = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("person.id"), nullable=False, primary_key=True) + ) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") diff --git a/backend/app/models/prompt.py b/backend/app/models/prompt.py index 71ec956a..edd1bde1 100644 --- a/backend/app/models/prompt.py +++ b/backend/app/models/prompt.py @@ -14,6 +14,5 @@ class Prompt(SQLModel, table=True): response: Optional[str] lang: Optional[str] created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), - nullable=False, + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) diff --git a/backend/app/models/work_package.py b/backend/app/models/work_package.py new file mode 100644 index 00000000..cfa7dc77 --- /dev/null +++ b/backend/app/models/work_package.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, SQLModel + + +class WorkPackage(SQLModel, table=True): + __tablename__ = "work_package" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + ) + expiry_date: Optional[datetime] = Field(sa_column=sa.Column(sa.DateTime(), nullable=True)) + person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") From bb1a0fe43247acbaaca3a0a414cfae35e3cf9385 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 00:03:06 +0100 Subject: [PATCH 025/119] added rating workflow --- backend/app/api/v1/tasks.py | 104 ++++++++++++++++++++------------ backend/app/schemas/protocol.py | 59 ++++++++++++++++-- text-frontend/__main__.py | 29 ++++++++- 3 files changed, 144 insertions(+), 48 deletions(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 07d0f9d1..0d587ca8 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -from typing import Any, List +import random +from typing import Any from uuid import UUID from app.api import deps @@ -13,40 +14,58 @@ from starlette.status import HTTP_400_BAD_REQUEST router = APIRouter() -@router.post("/", response_model=List[protocol_schema.SummarizeStoryTask]) # work with Union once more types are added +def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: + match (request.type): + case protocol_schema.TaskRequestType.generic: + logger.info("Frontend requested a generic task.") + while request.type == protocol_schema.TaskRequestType.generic: + request.type = random.choice(list(protocol_schema.TaskRequestType)).value + return generate_task(request) + case protocol_schema.TaskRequestType.summarize_story: + logger.info("Generating a SummarizeStoryTask.") + task = protocol_schema.SummarizeStoryTask( + story="This is a story. A very long story. So long, it needs to be summarized.", + ) + case protocol_schema.TaskRequestType.rate_summary: + logger.info("Generating a RateSummaryTask.") + task = protocol_schema.RateSummaryTask( + full_text="This is a story. A very long story. So long, it needs to be summarized.", + summary="This is a summary.", + scale=protocol_schema.RatingScale(min=1, max=5), + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid request type.", + ) + logger.info(f"Generated {task=}.") + if request.user_id is not None: + task.addressed_users = [request.user_id] + + return task + + +@router.post("/", response_model=protocol_schema.AnyTask) # work with Union once more types are added def request_task( *, db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), - request: protocol_schema.GenericTaskRequest, # work with Union once more types are added + request: protocol_schema.TaskRequest, ) -> Any: """ Create new task. """ deps.api_auth(api_key, db, create=True) - # TODO: Create a task and store it in the database. - - match (request.type): - case "generic": - # here we create a task at random (and store it in the database) - logger.info("Frontend requested a generic task.") - try: - task = protocol_schema.SummarizeStoryTask( - story="This is a story. A very long story. So long, it needs to be summarized.", - ) - except Exception: - logger.exception("Failed to create task.") - - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid request type.", - ) - if request.user_id is not None: - task.addressed_users = [request.user_id] - - return [task] + try: + task = generate_task(request) + # TODO: store task in database + except Exception: + logger.exception("Failed to generate task.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) + return task @router.post("/{task_id}/ack") @@ -55,17 +74,20 @@ def acknowledge_task( db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), task_id: UUID, - response: protocol_schema.PostCreatedTaskResponse, + response: protocol_schema.AnyTaskResponse, ) -> Any: """ The frontend acknowledges a task. """ deps.api_auth(api_key, db, create=True) - match (response.type): - case "post_created": + match (type(response)): + case protocol_schema.PostCreatedTaskResponse: logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") # here we would store the post id in the database for the task + case protocol_schema.RatingCreatedTaskResponse: + logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") + # here we would store the rating id in the database for the task case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, @@ -80,30 +102,34 @@ def post_interaction( *, db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), - interaction: protocol_schema.TextReplyToPost, + interaction: protocol_schema.AnyInteraction, ) -> Any: """ The frontend reports an interaction. """ deps.api_auth(api_key, db, create=True) - response = [] - match (interaction.type): - case "text_reply_to_post": + match (type(interaction)): + case protocol_schema.TextReplyToPost: logger.info( f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user_id=}." ) # here we would store the text reply in the database - response.append( - protocol_schema.TaskDone( - reply_to_post_id=interaction.user_post_id, - addressed_users=[interaction.user_id], - ) + return protocol_schema.TaskDone( + reply_to_post_id=interaction.user_post_id, + addressed_users=[interaction.user_id], + ) + case protocol_schema.PostRating: + logger.info( + f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user_id=}." + ) + # here we would store the rating in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_users=[interaction.user_id], ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, detail="Invalid response type.", ) - - return response diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index c1976bf7..34ef571f 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -1,22 +1,25 @@ # -*- coding: utf-8 -*- -from typing import Literal, Optional +import enum +from typing import Literal, Optional, Union from uuid import UUID, uuid4 import pydantic from pydantic import BaseModel +class TaskRequestType(str, enum.Enum): + generic = "generic" + summarize_story = "summarize_story" + rate_summary = "rate_summary" + + class TaskRequest(BaseModel): """The frontend asks the backend for a task.""" - type: str + type: TaskRequestType = TaskRequestType.generic user_id: Optional[str] = None -class GenericTaskRequest(TaskRequest): - type: Literal["generic"] = "generic" - - class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" @@ -37,16 +40,46 @@ class PostCreatedTaskResponse(TaskResponse): post_id: str +class RatingCreatedTaskResponse(TaskResponse): + type: Literal["rating_created"] = "rating_created" + post_id: str + + +AnyTaskResponse = Union[ + PostCreatedTaskResponse, + RatingCreatedTaskResponse, +] + + class SummarizeStoryTask(Task): type: Literal["summarize_story"] = "summarize_story" story: str +class RatingScale(BaseModel): + min: int + max: int + + +class RateSummaryTask(Task): + type: Literal["rate_summary"] = "rate_summary" + full_text: str + summary: str + scale: RatingScale = RatingScale(min=1, max=5) + + class TaskDone(Task): type: Literal["task_done"] = "task_done" reply_to_post_id: str +AnyTask = Union[ + SummarizeStoryTask, + RateSummaryTask, + TaskDone, +] + + class Interaction(BaseModel): """An interaction is a message from the frontend to the backend.""" @@ -61,3 +94,17 @@ class TextReplyToPost(Interaction): post_id: str user_post_id: str text: str + + +class PostRating(Interaction): + """A user has replied to a post with text.""" + + type: Literal["post_rating"] = "post_rating" + post_id: str + rating: int + + +AnyInteraction = Union[ + TextReplyToPost, + PostRating, +] diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index dab0c745..f68b4c35 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -17,7 +17,8 @@ def main(backend_url: str, api_key: str): return response.json() typer.echo("Requesting work...") - tasks = _post("/api/v1/tasks/", {"type": "generic"}) + # tasks = [_post("/api/v1/tasks/", {"type": "generic"})] + tasks = [_post("/api/v1/tasks/", {"type": "rate_summary"})] while tasks: task = tasks.pop(0) match (task["type"]): @@ -31,7 +32,7 @@ def main(backend_url: str, api_key: str): summary = typer.prompt("Enter your summary") # send interaction - new_tasks = _post( + new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", @@ -41,7 +42,29 @@ def main(backend_url: str, api_key: str): "user_id": "1234", }, ) - tasks.extend(new_tasks) + tasks.append(new_task) + case "rate_summary": + typer.echo("Rate the following summary:") + typer.echo(task["summary"]) + typer.echo("Full text:") + typer.echo(task["full_text"]) + typer.echo(f"Rating scale: {task['scale']['min']} - {task['scale']['max']}") + + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "rating_created", "post_id": "1234"}) + + rating = typer.prompt("Enter your rating", type=int) + # send interaction + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "post_rating", + "post_id": "1234", + "rating": rating, + "user_id": "1234", + }, + ) + tasks.append(new_task) case "task_done": typer.echo("Task done!") case _: From 348f81e4e7066bd610bf9226ce3e6a0599d9d19d Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 00:09:12 +0100 Subject: [PATCH 026/119] frontend fix --- text-frontend/__main__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index f68b4c35..5810d436 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -17,8 +17,7 @@ def main(backend_url: str, api_key: str): return response.json() typer.echo("Requesting work...") - # tasks = [_post("/api/v1/tasks/", {"type": "generic"})] - tasks = [_post("/api/v1/tasks/", {"type": "rate_summary"})] + tasks = [_post("/api/v1/tasks/", {"type": "generic"})] while tasks: task = tasks.pop(0) match (task["type"]): From f32600888d8f187c814227dcb29f6d4971924da2 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 00:41:43 +0100 Subject: [PATCH 027/119] made user structured --- backend/app/api/v1/tasks.py | 12 ++++++------ backend/app/schemas/protocol.py | 13 +++++++++---- text-frontend/__main__.py | 4 ++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 0d587ca8..92ee9b8e 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -39,8 +39,8 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: detail="Invalid request type.", ) logger.info(f"Generated {task=}.") - if request.user_id is not None: - task.addressed_users = [request.user_id] + if request.user is not None: + task.addressed_users = [request.user] return task @@ -112,21 +112,21 @@ def post_interaction( match (type(interaction)): case protocol_schema.TextReplyToPost: logger.info( - f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user_id=}." + f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." ) # here we would store the text reply in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.user_post_id, - addressed_users=[interaction.user_id], + addressed_users=[interaction.user], ) case protocol_schema.PostRating: logger.info( - f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user_id=}." + f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." ) # here we would store the rating in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.post_id, - addressed_users=[interaction.user_id], + addressed_users=[interaction.user], ) case _: raise HTTPException( diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 34ef571f..e72a7388 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -13,11 +13,16 @@ class TaskRequestType(str, enum.Enum): rate_summary = "rate_summary" +class User(BaseModel): + id: str + name: str + + class TaskRequest(BaseModel): """The frontend asks the backend for a task.""" type: TaskRequestType = TaskRequestType.generic - user_id: Optional[str] = None + user: Optional[User] = None class Task(BaseModel): @@ -25,7 +30,7 @@ class Task(BaseModel): id: UUID = pydantic.Field(default_factory=uuid4) type: str - addressed_users: Optional[list[str]] = None + addressed_users: Optional[list[User]] = None class TaskResponse(BaseModel): @@ -81,10 +86,10 @@ AnyTask = Union[ class Interaction(BaseModel): - """An interaction is a message from the frontend to the backend.""" + """An interaction is a user-generated action in the frontend.""" type: str - user_id: str + user: User class TextReplyToPost(Interaction): diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 5810d436..0029031f 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -38,7 +38,7 @@ def main(backend_url: str, api_key: str): "post_id": "1234", "user_post_id": "5678", "text": summary, - "user_id": "1234", + "user": {"id": "1234", "name": "John Doe"}, }, ) tasks.append(new_task) @@ -60,7 +60,7 @@ def main(backend_url: str, api_key: str): "type": "post_rating", "post_id": "1234", "rating": rating, - "user_id": "1234", + "user": {"id": "1234", "name": "John Doe"}, }, ) tasks.append(new_task) From b38c14233fa3428e7a4a4a4198be868be453f3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 23:35:23 +0100 Subject: [PATCH 028/119] add polymorphic JSONB payload support update nullable fields, new auth check --- .../versions/cd7de470586e_v1_db_structure.py | 4 +- backend/app/api/deps.py | 21 ++-- backend/app/api/v1/labelers.py | 12 +-- backend/app/api/v1/prompts.py | 8 +- backend/app/api/v1/tasks.py | 6 +- backend/app/models/payload_column_type.py | 102 ++++++++++++++++++ backend/app/models/person.py | 3 +- backend/app/models/post.py | 7 +- backend/app/models/post_reaction.py | 5 +- backend/app/models/work_package.py | 7 +- 10 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 backend/app/models/payload_column_type.py diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py index c394fe4f..d1eac36f 100644 --- a/backend/alembic/versions/cd7de470586e_v1_db_structure.py +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -72,7 +72,7 @@ def upgrade() -> None: sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("expiry_date", sa.DateTime(), nullable=True), - sa.Column("person_id", UUID(as_uuid=True), nullable=False), + sa.Column("person_id", UUID(as_uuid=True), nullable=True), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), @@ -88,7 +88,7 @@ def upgrade() -> None: sa.Column("parent_id", UUID(as_uuid=True), nullable=True), # root posts have NULL parent sa.Column("thread_id", UUID(as_uuid=True), nullable=False), # id of thread root sa.Column("workpackage_id", UUID(as_uuid=True), nullable=True), # workpackage id to pass to handler on reply - sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) + sa.Column("person_id", UUID(as_uuid=True), nullable=True), # sender (recipients are part of payload) sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.Column("role", sa.String(128), nullable=False), # 'assistant', 'user' or something else sa.Column("frontend_post_id", sa.String(200), nullable=False), # unique together with api_client_id diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index deed0a3d..3e4cb3d1 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -2,7 +2,7 @@ from typing import Generator from app.database import engine -from app.models import ServiceClient +from app.models import ApiClient from fastapi import HTTPException, Security from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery from sqlmodel import Session @@ -31,20 +31,11 @@ async def get_api_key( def api_auth( api_key: APIKey, db: Session, - create: bool = False, - read: bool = True, - update: bool = False, - delete: bool = False, -) -> ServiceClient: +) -> ApiClient: + if api_key is not None: - api_client = db.query(ServiceClient).filter(ServiceClient.api_key == api_key).first() - if api_client is not None: - if ( - (create is False or api_client.can_append) - and (read is False or api_client.can_read) - and (update is False or api_client.can_write) - and (delete is False or api_client.can_delete) - ): - return api_client + api_client = db.query(ApiClient).filter(ApiClient.api_key == api_key).first() + if api_client is not None and api_client.enabled: + return api_client raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials") diff --git a/backend/app/api/v1/labelers.py b/backend/app/api/v1/labelers.py index c2a89181..d583da65 100644 --- a/backend/app/api/v1/labelers.py +++ b/backend/app/api/v1/labelers.py @@ -21,7 +21,7 @@ def read_labelers( """ Retrieve labelers. """ - deps.api_auth(api_key, db, read=True) + deps.api_auth(api_key, db) if limit > 10000: raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") labelers = crud.labeler.get_multi(db, begin_id=begin_id, limit=limit) @@ -38,7 +38,7 @@ def create_labeler( """ Create new labeler. """ - deps.api_auth(api_key, db, create=True) + deps.api_auth(api_key, db) item = crud.labeler.create(db=db, obj_in=item_in) return item @@ -54,7 +54,7 @@ def update_labeler( """ Update a labeler. """ - deps.api_auth(api_key, db, update=True, read=True) + deps.api_auth(api_key, db) item = crud.labeler.get(db=db, id=id) if not item: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") @@ -72,7 +72,7 @@ def read_labeler_by_username( """ Get labeler by ID. """ - deps.api_auth(api_key, db, read=True) + deps.api_auth(api_key, db) item = crud.labeler.get_by_discord_username(db=db, discord_username=discord_username) if not item: raise HTTPException(status_code=404, detail="Item not found") @@ -89,7 +89,7 @@ def read_labeler( """ Get labeler by ID. """ - deps.api_auth(api_key, db, read=True) + deps.api_auth(api_key, db) item = crud.labeler.get(db=db, id=id) if not item: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") @@ -106,7 +106,7 @@ def delete_labeler( """ Delete a labeler. """ - deps.api_auth(api_key, db, delete=True) + deps.api_auth(api_key, db) labeler = crud.labeler.get(db=db, id=id) if not labeler: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") diff --git a/backend/app/api/v1/prompts.py b/backend/app/api/v1/prompts.py index b9cf2ce7..2e0947e5 100644 --- a/backend/app/api/v1/prompts.py +++ b/backend/app/api/v1/prompts.py @@ -21,7 +21,7 @@ def read_prompts( """ Retrieve prompts. """ - deps.api_auth(api_key, db, read=True) + deps.api_auth(api_key, db) if limit > 10000: raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") return crud.prompt.get_multi(db, begin_id=begin_id, limit=limit) @@ -37,7 +37,7 @@ def create_prompt( """ Create new prompt. """ - deps.api_auth(api_key, db, create=True) + deps.api_auth(api_key, db) if item_in.labeler_id is None: if item_in.discord_username is None: raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") @@ -66,7 +66,7 @@ def read_prompt( """ Get prompt by ID. """ - deps.api_auth(api_key, db, read=True) + deps.api_auth(api_key, db) item = crud.prompt.get(db=db, id=id) if not item: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") @@ -83,7 +83,7 @@ def delete_prompt( """ Delete a prompt. """ - deps.api_auth(api_key, db, delete=True) + deps.api_auth(api_key, db) item = crud.prompt.get(db=db, id=id) if not item: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 2a5397e5..55d93261 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -23,7 +23,7 @@ def request_task( """ Create new task. """ - deps.api_auth(api_key, db, create=True) + deps.api_auth(api_key, db) # TODO: Create a task and store it in the database. @@ -57,7 +57,7 @@ def acknowledge_task( """ The frontend acknowledges a task. """ - deps.api_auth(api_key, db, create=True) + deps.api_auth(api_key, db) match (response.type): case "post_created": @@ -82,7 +82,7 @@ def post_interaction( """ The frontend reports an interaction. """ - deps.api_auth(api_key, db, create=True) + deps.api_auth(api_key, db) response = [] match (interaction.type): diff --git a/backend/app/models/payload_column_type.py b/backend/app/models/payload_column_type.py new file mode 100644 index 00000000..a95ccb53 --- /dev/null +++ b/backend/app/models/payload_column_type.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +import json +from typing import Any, Generic, Type, TypeVar + +import sqlalchemy.dialects.postgresql as pg +from fastapi.encoders import jsonable_encoder +from pydantic import BaseModel, parse_obj_as, validator +from pydantic.main import ModelMetaclass +from sqlalchemy.types import TypeDecorator + +payload_type_registry = {} + + +P = TypeVar("P", bound=BaseModel) + + +def payload_tpye(cls: Type[P]) -> Type[P]: + payload_type_registry[cls.__name__] = cls + return cls + + +class PayloadContainer(BaseModel): + payload_type: str = "" + payload: BaseModel = None + + def __init__(self, **v): + p = v["payload"] + if isinstance(p, dict): + t = v["payload_type"] + if t not in payload_type_registry: + raise RuntimeError(f"Payload type '{t}' not registered") + cls = payload_type_registry[t] + v["payload"] = cls(**p) + super().__init__(**v) + + @validator("payload", pre=True) + def check_payload(cls, v: BaseModel, values: dict[str, Any]) -> BaseModel: + values["payload_type"] = type(v).__name__ + return v + + class Config: + orm_mode = True + + +T = TypeVar("T") + + +def payload_column_type(pydantic_type): + class PayloadJSONBType(TypeDecorator, Generic[T]): + impl = pg.JSONB() + + def __init__( + self, + json_encoder=json, + ): + self.json_encoder = json_encoder + super(PayloadJSONBType, self).__init__() + + # serialize + def bind_processor(self, dialect): + impl_processor = self.impl.bind_processor(dialect) + dumps = self.json_encoder.dumps + + def process(value: T): + if value is not None: + if isinstance(pydantic_type, ModelMetaclass): + # This allows to assign non-InDB models and if they're + # compatible, they're directly parsed into the InDB + # representation, thus hiding the implementation in the + # background. However, the InDB model will still be returned + value_to_dump = pydantic_type.from_orm(value) + else: + value_to_dump = value + + value = jsonable_encoder(value_to_dump) + + if impl_processor: + return impl_processor(value) + else: + return dumps(jsonable_encoder(value_to_dump)) + + return process + + # deserialize + def result_processor(self, dialect, coltype) -> T: + impl_processor = self.impl.result_processor(dialect, coltype) + + def process(value): + if impl_processor: + value = impl_processor(value) + if value is None: + return None + # Explicitly use the generic directly, not type(T) + full_obj = parse_obj_as(pydantic_type, value) + return full_obj + + return process + + def compare_values(self, x, y): + return x == y + + return PayloadJSONBType diff --git a/backend/app/models/person.py b/backend/app/models/person.py index 57719a4a..e66db5e3 100644 --- a/backend/app/models/person.py +++ b/backend/app/models/person.py @@ -10,6 +10,7 @@ from sqlmodel import Field, Index, SQLModel class Person(SQLModel, table=True): __tablename__ = "person" + __table_args__ = (Index("ix_person_username", "api_client_id", "username", unique=True),) id: Optional[UUID] = Field( sa_column=sa.Column( @@ -22,5 +23,3 @@ class Person(SQLModel, table=True): sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) api_client_id: UUID = Field(foreign_key="api_client.id") - - __table_args__ = (Index("ix_person_username", "api_client_id", "username", unique=True),) diff --git a/backend/app/models/post.py b/backend/app/models/post.py index f73fd34b..fb6d5160 100644 --- a/backend/app/models/post.py +++ b/backend/app/models/post.py @@ -5,9 +5,10 @@ from uuid import UUID, uuid4 import sqlalchemy as sa import sqlalchemy.dialects.postgresql as pg -from pydantic import BaseModel from sqlmodel import Field, Index, SQLModel +from .payload_column_type import PayloadContainer, payload_column_type + class Post(SQLModel, table=True): __tablename__ = "post" @@ -21,7 +22,7 @@ class Post(SQLModel, table=True): parent_id: UUID = Field(nullable=True) thread_id: UUID = Field(nullable=False, index=True) workpackage_id: UUID = Field(nullable=True, index=True) - person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + person_id: UUID = Field(nullable=True, foreign_key="person.id", index=True) role: str = Field(nullable=False, max_length=128) api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") frontend_post_id: str = Field(max_length=200, nullable=False) @@ -29,4 +30,4 @@ class Post(SQLModel, table=True): sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) payload_type: str = Field(nullable=False, max_length=200) - payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False)) diff --git a/backend/app/models/post_reaction.py b/backend/app/models/post_reaction.py index 7a7c9269..fb8b0fcb 100644 --- a/backend/app/models/post_reaction.py +++ b/backend/app/models/post_reaction.py @@ -5,9 +5,10 @@ from uuid import UUID import sqlalchemy as sa import sqlalchemy.dialects.postgresql as pg -from pydantic import BaseModel from sqlmodel import Field, SQLModel +from .payload_column_type import PayloadContainer, payload_column_type + class PostReaction(SQLModel, table=True): __tablename__ = "post_reaction" @@ -22,5 +23,5 @@ class PostReaction(SQLModel, table=True): sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) payload_type: str = Field(nullable=False, max_length=200) - payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False)) api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") diff --git a/backend/app/models/work_package.py b/backend/app/models/work_package.py index cfa7dc77..0a452a71 100644 --- a/backend/app/models/work_package.py +++ b/backend/app/models/work_package.py @@ -5,9 +5,10 @@ from uuid import UUID, uuid4 import sqlalchemy as sa import sqlalchemy.dialects.postgresql as pg -from pydantic import BaseModel from sqlmodel import Field, SQLModel +from .payload_column_type import PayloadContainer, payload_column_type + class WorkPackage(SQLModel, table=True): __tablename__ = "work_package" @@ -21,7 +22,7 @@ class WorkPackage(SQLModel, table=True): sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), ) expiry_date: Optional[datetime] = Field(sa_column=sa.Column(sa.DateTime(), nullable=True)) - person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + person_id: UUID = Field(nullable=True, foreign_key="person.id", index=True) payload_type: str = Field(nullable=False, max_length=200) - payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False)) api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") From 5c9af18aca87e65d0267de4d60e7d4303c398a36 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 09:31:42 +0100 Subject: [PATCH 029/119] added docstrings --- .vscode/settings.json | 3 +++ backend/app/schemas/protocol.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..b7368caa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index e72a7388..2a6609d4 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -34,18 +34,22 @@ class Task(BaseModel): class TaskResponse(BaseModel): - """A task response is a message from the frontend to acknowledge the given task.""" + """A task response is a message from the frontend to acknowledge that an initial piece of work has been done on the task.""" type: str status: Literal["success", "failure"] = "success" class PostCreatedTaskResponse(TaskResponse): + """The frontend signals to the backend that a post has been created.""" + type: Literal["post_created"] = "post_created" post_id: str class RatingCreatedTaskResponse(TaskResponse): + """The frontend signals to the backend that a rating input has been created for a given post.""" + type: Literal["rating_created"] = "rating_created" post_id: str @@ -57,6 +61,8 @@ AnyTaskResponse = Union[ class SummarizeStoryTask(Task): + """A task to summarize a story.""" + type: Literal["summarize_story"] = "summarize_story" story: str @@ -67,6 +73,8 @@ class RatingScale(BaseModel): class RateSummaryTask(Task): + """A task to rate a summary.""" + type: Literal["rate_summary"] = "rate_summary" full_text: str summary: str @@ -74,6 +82,8 @@ class RateSummaryTask(Task): class TaskDone(Task): + """Signals to the frontend that the task is done.""" + type: Literal["task_done"] = "task_done" reply_to_post_id: str @@ -102,7 +112,7 @@ class TextReplyToPost(Interaction): class PostRating(Interaction): - """A user has replied to a post with text.""" + """A user has rated a post.""" type: Literal["post_rating"] = "post_rating" post_id: str From 084668294bff7f697f13898ba41e58bd0f15a6e9 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 09:40:27 +0100 Subject: [PATCH 030/119] implemented asking for initial prompt --- backend/app/api/v1/tasks.py | 6 ++++++ backend/app/schemas/protocol.py | 11 +++++++++++ text-frontend/__main__.py | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 92ee9b8e..145ba4af 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -33,6 +33,11 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: summary="This is a summary.", scale=protocol_schema.RatingScale(min=1, max=5), ) + case protocol_schema.TaskRequestType.initial_prompt: + logger.info("Generating an InitialPromptTask.") + task = protocol_schema.InitialPromptTask( + hint="Ask the assistant about a current event." # this is optional + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, @@ -123,6 +128,7 @@ def post_interaction( logger.info( f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." ) + # check if rating in range # here we would store the rating in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.post_id, diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 2a6609d4..6086bc4a 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -11,6 +11,7 @@ class TaskRequestType(str, enum.Enum): generic = "generic" summarize_story = "summarize_story" rate_summary = "rate_summary" + initial_prompt = "initial_prompt" class User(BaseModel): @@ -81,6 +82,15 @@ class RateSummaryTask(Task): scale: RatingScale = RatingScale(min=1, max=5) +class InitialPromptTask(Task): + """A task to prompt the user to submit an initial prompt to the assistant.""" + + type: Literal["initial_prompt"] = "initial_prompt" + hint: str | None = ( + None # provide a hint to the user to guide them a bit (i.e. "Ask the assistant to summarize something.") + ) + + class TaskDone(Task): """Signals to the frontend that the task is done.""" @@ -91,6 +101,7 @@ class TaskDone(Task): AnyTask = Union[ SummarizeStoryTask, RateSummaryTask, + InitialPromptTask, TaskDone, ] diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 0029031f..d912fd60 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -64,6 +64,26 @@ def main(backend_url: str, api_key: str): }, ) tasks.append(new_task) + case "initial_prompt": + typer.echo("Please provide an initial prompt to the assistant.") + if task["hint"]: + typer.echo(f"Hint: {task['hint']}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": "1234"}) + prompt = typer.prompt("Enter your prompt") + # send interaction + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "text_reply_to_post", + "post_id": "1234", + "user_post_id": "5678", + "text": prompt, + "user": {"id": "1234", "name": "John Doe"}, + }, + ) + tasks.append(new_task) + case "task_done": typer.echo("Task done!") case _: From ac563592e0644bb16ed887bc5833ecb6b671f3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 10:01:02 +0100 Subject: [PATCH 031/119] pre-commit install :-) --- backend/app/api/deps.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index 662fa0e2..e8b780c1 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from typing import Generator from uuid import UUID + from app.config import settings from app.database import engine from app.models import ApiClient @@ -36,10 +37,7 @@ def api_auth( if api_key is not None: if settings.ALLOW_ANY_API_KEY: - return ApiClient( - id=UUID('00000000-1111-2222-3333-444444444444'), - api_key=api_key, name=api_key - ) + return ApiClient(id=UUID("00000000-1111-2222-3333-444444444444"), api_key=api_key, name=api_key) api_client = db.query(ApiClient).filter(ApiClient.api_key == api_key).first() if api_client is not None and api_client.enabled: return api_client From 0f2a8971e5796d045adafdd174f9f37af229957a Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 10:36:40 +0100 Subject: [PATCH 032/119] added tasks to act as user or assistant --- backend/app/api/v1/tasks.py | 40 ++++++++++++--- backend/app/schemas/protocol.py | 43 +++++++++++++++-- text-frontend/__main__.py | 86 ++++++++++++++++++++++++++++----- 3 files changed, 145 insertions(+), 24 deletions(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 145ba4af..2431b70d 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -16,9 +16,9 @@ router = APIRouter() def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: match (request.type): - case protocol_schema.TaskRequestType.generic: - logger.info("Frontend requested a generic task.") - while request.type == protocol_schema.TaskRequestType.generic: + case protocol_schema.TaskRequestType.random: + logger.info("Frontend requested a random task.") + while request.type == protocol_schema.TaskRequestType.random: request.type = random.choice(list(protocol_schema.TaskRequestType)).value return generate_task(request) case protocol_schema.TaskRequestType.summarize_story: @@ -38,6 +38,34 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: task = protocol_schema.InitialPromptTask( hint="Ask the assistant about a current event." # this is optional ) + case protocol_schema.TaskRequestType.user_reply: + logger.info("Generating a UserReplyTask.") + task = protocol_schema.UserReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ) + ) + case protocol_schema.TaskRequestType.assistant_reply: + logger.info("Generating a AssistantReplyTask.") + task = protocol_schema.AssistantReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, write me an English essay about water.", + is_assistant=False, + ), + ], + ) + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, @@ -45,7 +73,7 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: ) logger.info(f"Generated {task=}.") if request.user is not None: - task.addressed_users = [request.user] + task.addressed_user = request.user return task @@ -122,7 +150,7 @@ def post_interaction( # here we would store the text reply in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.user_post_id, - addressed_users=[interaction.user], + addressed_user=interaction.user, ) case protocol_schema.PostRating: logger.info( @@ -132,7 +160,7 @@ def post_interaction( # here we would store the rating in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.post_id, - addressed_users=[interaction.user], + addressed_user=interaction.user, ) case _: raise HTTPException( diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 6086bc4a..0abbf142 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -8,21 +8,37 @@ from pydantic import BaseModel class TaskRequestType(str, enum.Enum): - generic = "generic" + random = "random" summarize_story = "summarize_story" rate_summary = "rate_summary" initial_prompt = "initial_prompt" + user_reply = "user_reply" + assistant_reply = "assistant_reply" class User(BaseModel): id: str - name: str + display_name: str + auth_method: Literal["discord", "local"] + + +class ConversationMessage(BaseModel): + """Represents a message in a conversation between the user and the assistant.""" + + text: str + is_assistant: bool + + +class Conversation(BaseModel): + """Represents a conversation between the user and the assistant.""" + + messages: list[ConversationMessage] = [] class TaskRequest(BaseModel): """The frontend asks the backend for a task.""" - type: TaskRequestType = TaskRequestType.generic + type: TaskRequestType = TaskRequestType.random user: Optional[User] = None @@ -31,7 +47,7 @@ class Task(BaseModel): id: UUID = pydantic.Field(default_factory=uuid4) type: str - addressed_users: Optional[list[User]] = None + addressed_user: Optional[User] = None class TaskResponse(BaseModel): @@ -91,6 +107,21 @@ class InitialPromptTask(Task): ) +class UserReplyTask(Task): + """A task to prompt the user to submit a reply to the assistant.""" + + type: Literal["user_reply"] = "user_reply" + conversation: Conversation # the conversation so far + hint: str | None = None # e.g. "Try to ask for clarification." + + +class AssistantReplyTask(Task): + """A task to prompt the user to act as the assistant.""" + + type: Literal["assistant_reply"] = "assistant_reply" + conversation: Conversation # the conversation so far + + class TaskDone(Task): """Signals to the frontend that the task is done.""" @@ -99,10 +130,12 @@ class TaskDone(Task): AnyTask = Union[ + TaskDone, SummarizeStoryTask, RateSummaryTask, InitialPromptTask, - TaskDone, + UserReplyTask, + AssistantReplyTask, ] diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index d912fd60..44b7cbc5 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -7,6 +7,19 @@ import typer app = typer.Typer() +# debug constants +POST_ID = "1234" +USER_POST_ID = "5678" +USER = {"id": "1234", "display_name": "John Doe", "auth_method": "local"} + + +def _render_message(message: dict) -> str: + """Render a message to the user.""" + if message["is_assistant"]: + return f"Assistant: {message['text']}" + return f"User: {message['text']}" + + @app.command() def main(backend_url: str, api_key: str): """Simple REPL frontend.""" @@ -17,7 +30,7 @@ def main(backend_url: str, api_key: str): return response.json() typer.echo("Requesting work...") - tasks = [_post("/api/v1/tasks/", {"type": "generic"})] + tasks = [_post("/api/v1/tasks/", {"type": "random"})] while tasks: task = tasks.pop(0) match (task["type"]): @@ -26,7 +39,7 @@ def main(backend_url: str, api_key: str): typer.echo(task["story"]) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": "1234"}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) summary = typer.prompt("Enter your summary") @@ -35,10 +48,10 @@ def main(backend_url: str, api_key: str): "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": "1234", - "user_post_id": "5678", + "post_id": POST_ID, + "user_post_id": USER_POST_ID, "text": summary, - "user": {"id": "1234", "name": "John Doe"}, + "user": USER, }, ) tasks.append(new_task) @@ -50,7 +63,7 @@ def main(backend_url: str, api_key: str): typer.echo(f"Rating scale: {task['scale']['min']} - {task['scale']['max']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "rating_created", "post_id": "1234"}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "rating_created", "post_id": POST_ID}) rating = typer.prompt("Enter your rating", type=int) # send interaction @@ -58,9 +71,9 @@ def main(backend_url: str, api_key: str): "/api/v1/tasks/interaction", { "type": "post_rating", - "post_id": "1234", + "post_id": POST_ID, "rating": rating, - "user": {"id": "1234", "name": "John Doe"}, + "user": USER, }, ) tasks.append(new_task) @@ -69,23 +82,70 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": "1234"}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) prompt = typer.prompt("Enter your prompt") # send interaction new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": "1234", - "user_post_id": "5678", + "post_id": POST_ID, + "user_post_id": USER_POST_ID, "text": prompt, - "user": {"id": "1234", "name": "John Doe"}, + "user": USER, + }, + ) + tasks.append(new_task) + + case "user_reply": + typer.echo("Please provide a reply to the assistant.") + typer.echo("Here is the conversation so far:") + for message in task["conversation"]["messages"]: + typer.echo(_render_message(message)) + if task["hint"]: + typer.echo(f"Hint: {task['hint']}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + reply = typer.prompt("Enter your reply") + # send interaction + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "text_reply_to_post", + "post_id": POST_ID, + "user_post_id": USER_POST_ID, + "text": reply, + "user": USER, + }, + ) + tasks.append(new_task) + + case "assistant_reply": + typer.echo("Act as the assistant and reply to the user.") + typer.echo("Here is the conversation so far:") + for message in task["conversation"]["messages"]: + typer.echo(_render_message(message)) + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + reply = typer.prompt("Enter your reply") + # send interaction + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "text_reply_to_post", + "post_id": POST_ID, + "user_post_id": USER_POST_ID, + "text": reply, + "user": USER, }, ) tasks.append(new_task) case "task_done": - typer.echo("Task done!") + if addressed_user := task["addressed_user"]: + typer.echo(f"Hey, {addressed_user['display_name']}! Thank you!") + else: + typer.echo("Task done!") case _: typer.echo(f"Unknown task type {task['type']}") From b5d19107e334115531c47d766629875367c0c8f4 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 11:40:59 +0100 Subject: [PATCH 033/119] added ranking tasks to protocol --- backend/app/schemas/protocol.py | 82 +++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 0abbf142..059031af 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -14,6 +14,9 @@ class TaskRequestType(str, enum.Enum): initial_prompt = "initial_prompt" user_reply = "user_reply" assistant_reply = "assistant_reply" + rank_initial_prompts = "rank_initial_prompts" + rank_user_replies = "rank_user_replies" + rank_assistant_replies = "rank_assistant_replies" class User(BaseModel): @@ -71,9 +74,17 @@ class RatingCreatedTaskResponse(TaskResponse): post_id: str +class RankingCreatedTaskResponse(TaskResponse): + """The frontend signals to the backend that a ranking input has been created for a given post.""" + + type: Literal["ranking_created"] = "ranking_created" + post_id: str + + AnyTaskResponse = Union[ PostCreatedTaskResponse, RatingCreatedTaskResponse, + RankingCreatedTaskResponse, ] @@ -89,37 +100,74 @@ class RatingScale(BaseModel): max: int -class RateSummaryTask(Task): +class AbstractRatingTask(Task): + """A task to rate something.""" + + scale: RatingScale = RatingScale(min=1, max=5) + + +class RateSummaryTask(AbstractRatingTask): """A task to rate a summary.""" type: Literal["rate_summary"] = "rate_summary" full_text: str summary: str - scale: RatingScale = RatingScale(min=1, max=5) -class InitialPromptTask(Task): +class WithHintMixin(BaseModel): + hint: str | None = None # provide a hint to the user to spark their imagination + + +class InitialPromptTask(Task, WithHintMixin): """A task to prompt the user to submit an initial prompt to the assistant.""" type: Literal["initial_prompt"] = "initial_prompt" - hint: str | None = ( - None # provide a hint to the user to guide them a bit (i.e. "Ask the assistant to summarize something.") - ) -class UserReplyTask(Task): +class ReplyToConversationTask(Task): + """A task to prompt the user to submit a reply to a conversation.""" + + type: Literal["reply_to_conversation"] = "reply_to_conversation" + conversation: Conversation # the conversation so far + + +class UserReplyTask(ReplyToConversationTask, WithHintMixin): """A task to prompt the user to submit a reply to the assistant.""" type: Literal["user_reply"] = "user_reply" - conversation: Conversation # the conversation so far - hint: str | None = None # e.g. "Try to ask for clarification." -class AssistantReplyTask(Task): +class AssistantReplyTask(ReplyToConversationTask): """A task to prompt the user to act as the assistant.""" type: Literal["assistant_reply"] = "assistant_reply" + + +class RankInitialPromptsTask(Task): + """A task to rank a set of initial prompts.""" + + type: Literal["rank_initial_prompts"] = "rank_initial_prompts" + prompts: list[str] + + +class RankConversationRepliesTask(Task): + """A task to rank a set of replies to a conversation.""" + + type: Literal["rank_conversation_replies"] = "rank_conversation_replies" conversation: Conversation # the conversation so far + replies: list[str] + + +class RankUserRepliesTask(RankConversationRepliesTask): + """A task to rank a set of user replies to a conversation.""" + + type: Literal["rank_user_replies"] = "rank_user_replies" + + +class RankAssistantRepliesTask(RankConversationRepliesTask): + """A task to rank a set of assistant replies to a conversation.""" + + type: Literal["rank_assistant_replies"] = "rank_assistant_replies" class TaskDone(Task): @@ -134,8 +182,13 @@ AnyTask = Union[ SummarizeStoryTask, RateSummaryTask, InitialPromptTask, + ReplyToConversationTask, UserReplyTask, AssistantReplyTask, + RankInitialPromptsTask, + RankConversationRepliesTask, + RankUserRepliesTask, + RankAssistantRepliesTask, ] @@ -163,7 +216,16 @@ class PostRating(Interaction): rating: int +class PostRanking(Interaction): + """A user has given a ranking for a post.""" + + type: Literal["post_ranking"] = "post_ranking" + post_id: str + ranking: list[int] + + AnyInteraction = Union[ TextReplyToPost, PostRating, + PostRanking, ] From 082981b9d92cb5164829ec3ab2e8a148ba7c6771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 12:26:52 +0100 Subject: [PATCH 034/119] insert tasks into work_package table, add PromptRepository --- backend/alembic.ini | 2 +- backend/app/api/deps.py | 14 ++- backend/app/api/v1/api.py | 4 +- backend/app/api/v1/tasks2.py | 173 +++++++++++++++++++++++++++++++ backend/app/prompt_repository.py | 116 +++++++++++++++++++++ 5 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 backend/app/api/v1/tasks2.py create mode 100644 backend/app/prompt_repository.py diff --git a/backend/alembic.ini b/backend/alembic.ini index 39874aee..36a8c3ae 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -56,7 +56,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # output_encoding = utf-8 # sqlalchemy.url = postgresql://:@/ - +sqlalchemy.url = postgresql://postgres:postgres@localhost:5432/postgres [post_write_hooks] # post_write_hooks defines scripts or Python functions that are run diff --git a/backend/app/api/deps.py b/backend/app/api/deps.py index e8b780c1..0790cd4d 100644 --- a/backend/app/api/deps.py +++ b/backend/app/api/deps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from secrets import token_hex from typing import Generator from uuid import UUID @@ -7,6 +8,7 @@ from app.database import engine from app.models import ApiClient from fastapi import HTTPException, Security from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery +from loguru import logger from sqlmodel import Session from starlette.status import HTTP_403_FORBIDDEN @@ -37,7 +39,17 @@ def api_auth( if api_key is not None: if settings.ALLOW_ANY_API_KEY: - return ApiClient(id=UUID("00000000-1111-2222-3333-444444444444"), api_key=api_key, name=api_key) + # make sure that a dummy api key exits in db (foreign key references) + ANY_API_KEY_ID = UUID("00000000-1111-2222-3333-444444444444") + api_client: ApiClient = db.query(ApiClient).filter(ApiClient.id == ANY_API_KEY_ID).first() + if api_client is None: + token = token_hex(32) + logger.info(f"ANY_API_KEY missing, inserting api_key: {token}") + api_client = ApiClient(id=ANY_API_KEY_ID, api_key=token, description="ANY_API_KEY, random token") + db.add(api_client) + db.commit() + return api_client + api_client = db.query(ApiClient).filter(ApiClient.api_key == api_key).first() if api_client is not None and api_client.enabled: return api_client diff --git a/backend/app/api/v1/api.py b/backend/app/api/v1/api.py index 5a704c2d..7e3ea5eb 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/api/v1/api.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- -from app.api.v1 import labelers, prompts, tasks +from app.api.v1 import labelers, prompts, tasks, tasks2 from fastapi import APIRouter api_router = APIRouter() api_router.include_router(labelers.router, prefix="/labelers", tags=["labelers"]) api_router.include_router(prompts.router, prefix="/prompts", tags=["prompts"]) api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) + +api_router.include_router(tasks2.router, prefix="/task2", tags=["task2"]) # temporary diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py new file mode 100644 index 00000000..23986fef --- /dev/null +++ b/backend/app/api/v1/tasks2.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +import random +from typing import Any +from uuid import UUID + +from app.api import deps +from app.prompt_repository import PromptRepository +from app.schemas import protocol as protocol_schema +from fastapi import APIRouter, Depends, HTTPException +from fastapi.security.api_key import APIKey +from loguru import logger +from sqlmodel import Session +from starlette.status import HTTP_400_BAD_REQUEST + +router = APIRouter() + + +def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: + match (request.type): + case protocol_schema.TaskRequestType.random: + logger.info("Frontend requested a random task.") + while request.type == protocol_schema.TaskRequestType.random: + request.type = random.choice(list(protocol_schema.TaskRequestType)).value + return generate_task(request) + case protocol_schema.TaskRequestType.summarize_story: + logger.info("Generating a SummarizeStoryTask.") + task = protocol_schema.SummarizeStoryTask( + story="This is a story. A very long story. So long, it needs to be summarized.", + ) + case protocol_schema.TaskRequestType.rate_summary: + logger.info("Generating a RateSummaryTask.") + task = protocol_schema.RateSummaryTask( + full_text="This is a story. A very long story. So long, it needs to be summarized.", + summary="This is a summary.", + scale=protocol_schema.RatingScale(min=1, max=5), + ) + case protocol_schema.TaskRequestType.initial_prompt: + logger.info("Generating an InitialPromptTask.") + task = protocol_schema.InitialPromptTask( + hint="Ask the assistant about a current event." # this is optional + ) + case protocol_schema.TaskRequestType.user_reply: + logger.info("Generating a UserReplyTask.") + task = protocol_schema.UserReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ) + ) + case protocol_schema.TaskRequestType.assistant_reply: + logger.info("Generating a AssistantReplyTask.") + task = protocol_schema.AssistantReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, write me an English essay about water.", + is_assistant=False, + ), + ], + ) + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid request type.", + ) + logger.info(f"Generated {task=}.") + if request.user is not None: + task.addressed_user = request.user + + return task + + +@router.post("/", response_model=protocol_schema.AnyTask) # work with Union once more types are added +def request_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + request: protocol_schema.TaskRequest, +) -> Any: + """ + Create new task. + """ + api_client = deps.api_auth(api_key, db) + + try: + task = generate_task(request) + + pr = PromptRepository(db, api_client, request.user) + pr.store_task(task) + + except Exception: + logger.exception("Failed to generate task.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) + return task + + +@router.post("/{task_id}/ack") +def acknowledge_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + response: protocol_schema.AnyTaskResponse, +) -> Any: + """ + The frontend acknowledges a task. + """ + deps.api_auth(api_key, db) + + match (type(response)): + case protocol_schema.PostCreatedTaskResponse: + logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") + # here we would store the post id in the database for the task + case protocol_schema.RatingCreatedTaskResponse: + logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") + # here we would store the rating id in the database for the task + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + return {} + + +@router.post("/interaction") +def post_interaction( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + interaction: protocol_schema.AnyInteraction, +) -> Any: + """ + The frontend reports an interaction. + """ + deps.api_auth(api_key, db) + + match (type(interaction)): + case protocol_schema.TextReplyToPost: + logger.info( + f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." + ) + # here we would store the text reply in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.user_post_id, + addressed_user=interaction.user, + ) + case protocol_schema.PostRating: + logger.info( + f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." + ) + # check if rating in range + # here we would store the rating in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_user=interaction.user, + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py new file mode 100644 index 00000000..539ef1ab --- /dev/null +++ b/backend/app/prompt_repository.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +from typing import Literal, Optional +from uuid import UUID + +# from app.models import ApiClient, Person, PersonStats, Post, PostReaction, WorkPackage +from app.models import ApiClient, Person, WorkPackage +from app.models.payload_column_type import PayloadContainer, payload_tpye +from app.schemas import protocol as protocol_schema +from pydantic import BaseModel +from sqlmodel import Session + + +@payload_tpye +class TaskPayload(BaseModel): + type: str + + +@payload_tpye +class SummarizationStoryPayload(TaskPayload): + type: Literal["summarize_story"] = "summarize_story" + story: str + + +@payload_tpye +class RateSummaryPayload(TaskPayload): + type: Literal["rate_summary"] = "rate_summary" + full_text: str + summary: str + scale: protocol_schema.RatingScale + + +@payload_tpye +class InitialPromptPayload(TaskPayload): + type: Literal["initial_prompt"] = "initial_prompt" + hint: str + + +@payload_tpye +class UserReplyPayload(TaskPayload): + type: Literal["user_reply"] = "user_reply" + conversation: protocol_schema.Conversation + hint: str | None + + +@payload_tpye +class AssistantReplyPayload(TaskPayload): + type: Literal["assistant_reply"] = "assistant_reply" + conversation: protocol_schema.Conversation + + +class PromptRepository: + def __init__(self, db: Session, api_client: ApiClient, user: Optional[protocol_schema.User]): + self.db = db + self.api_client = api_client + self.person = self.lookup_person(user) + self.person_id = self.person.id if self.person else None + + def lookup_person(self, user: protocol_schema.User) -> Person: + person: Person = ( + self.db.query(Person) + .filter(Person.api_client_id == self.api_client.id and Person.username == user.id) + .first() + ) + if person is None: + # user is unknown, create new record + person = Person(username=user.id, display_name=user.display_name, api_client_id=self.api_client.id) + self.db.add(person) + self.db.commit() + self.db.refresh(person) + elif user.display_name and user.display_name != person.display_name: + # we found the user but the display name changed + person.display_name = user.display_name + self.db.add(person) + self.db.commit() + return person + + def store_task(self, task: protocol_schema.Task) -> WorkPackage: + payload: TaskPayload = None + match type(task): + case protocol_schema.SummarizeStoryTask: + payload = SummarizationStoryPayload(story=task.story) + + case protocol_schema.RateSummaryTask: + payload = RateSummaryPayload(full_text=task.full_text, summary=task.summary, scale=task.scale) + + case protocol_schema.InitialPromptTask: + payload = InitialPromptPayload(hint=task.hint) + + case protocol_schema.UserReplyTask: + payload = UserReplyPayload(conversation=task.conversation, hint=task.hint) + + case protocol_schema.AssistantReplyTask: + payload = AssistantReplyPayload(type=task.type, conversation=task.conversation) + + case _: + raise RuntimeError( + detail="Invalid task type.", + ) + + wp = self.insert_work_package(payload=payload, id=task.id) + assert wp.id == task.id + return wp + + def insert_work_package(self, payload: TaskPayload, id: UUID = None) -> WorkPackage: + c = PayloadContainer(payload=payload) + wp = WorkPackage( + id=id, + person_id=self.person_id, + payload_type=type(payload).__name__, + payload=c, + api_client_id=self.api_client.id, + ) + self.db.add(wp) + self.db.commit() + self.db.refresh(wp) + return wp From 1f31e6a499a0a3911708d4624a65b61f7beb706a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 13:32:06 +0100 Subject: [PATCH 035/119] add frontend post_id binding and post reply handing --- .../versions/cd7de470586e_v1_db_structure.py | 2 +- backend/app/api/v1/tasks2.py | 29 ++++- backend/app/models/post.py | 2 +- backend/app/prompt_repository.py | 107 +++++++++++++++++- 4 files changed, 129 insertions(+), 11 deletions(-) diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py index d1eac36f..67488e4b 100644 --- a/backend/alembic/versions/cd7de470586e_v1_db_structure.py +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -94,7 +94,7 @@ def upgrade() -> None: sa.Column("frontend_post_id", sa.String(200), nullable=False), # unique together with api_client_id sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=True), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py index 23986fef..4a359ab0 100644 --- a/backend/app/api/v1/tasks2.py +++ b/backend/app/api/v1/tasks2.py @@ -4,7 +4,7 @@ from typing import Any from uuid import UUID from app.api import deps -from app.prompt_repository import PromptRepository +from app.prompt_repository import PromptRepository, TaskPayload from app.schemas import protocol as protocol_schema from fastapi import APIRouter, Depends, HTTPException from fastapi.security.api_key import APIKey @@ -116,15 +116,24 @@ def acknowledge_task( """ The frontend acknowledges a task. """ - deps.api_auth(api_key, db) + api_client = deps.api_auth(api_key, db) + pr = PromptRepository(db, api_client, user=None) match (type(response)): case protocol_schema.PostCreatedTaskResponse: logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") - # here we would store the post id in the database for the task + + if response.status == "success": + # here we store the post id in the database for the task + pr.bind_frontend_post_id(task_id=task_id, post_id=response.post_id) + case protocol_schema.RatingCreatedTaskResponse: logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") - # here we would store the rating id in the database for the task + + if response.status == "success": + # here we would store the rating id in the database for the task + pr.bind_frontend_post_id(task_id=task_id, post_id=response.post_id) + case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, @@ -144,14 +153,22 @@ def post_interaction( """ The frontend reports an interaction. """ - deps.api_auth(api_key, db) + api_client = deps.api_auth(api_key, db) + pr = PromptRepository(db, api_client, user=interaction.user) match (type(interaction)): case protocol_schema.TextReplyToPost: logger.info( f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." ) - # here we would store the text reply in the database + + work_package = pr.fetch_workpackage_by_postid(interaction.post_id) + work_payload: TaskPayload = work_package.payload.payload + logger.info(f"found task work package in db: {work_payload}") + + # here we store the text reply in the database + pr.store_text_reply(interaction) + return protocol_schema.TaskDone( reply_to_post_id=interaction.user_post_id, addressed_user=interaction.user, diff --git a/backend/app/models/post.py b/backend/app/models/post.py index fb6d5160..d1569a67 100644 --- a/backend/app/models/post.py +++ b/backend/app/models/post.py @@ -30,4 +30,4 @@ class Post(SQLModel, table=True): sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) payload_type: str = Field(nullable=False, max_length=200) - payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False)) + payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=True)) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 539ef1ab..d2145e2e 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- +from datetime import datetime from typing import Literal, Optional -from uuid import UUID +from uuid import UUID, uuid4 -# from app.models import ApiClient, Person, PersonStats, Post, PostReaction, WorkPackage -from app.models import ApiClient, Person, WorkPackage +from app.models import ApiClient, Person, Post, WorkPackage from app.models.payload_column_type import PayloadContainer, payload_tpye from app.schemas import protocol as protocol_schema from pydantic import BaseModel @@ -74,6 +74,107 @@ class PromptRepository: self.db.commit() return person + def validate_post_id(self, post_id: str) -> None: + if not isinstance(post_id, str): + raise TypeError("post_id must be string") + if not post_id: + raise ValueError("post_id must not be empty") + + def bind_frontend_post_id(self, task_id: UUID, post_id: str): + self.validate_post_id(post_id) + + # find work package + work_pack: WorkPackage = ( + self.db.query(WorkPackage) + .filter(WorkPackage.id == task_id and WorkPackage.api_client_id == self.api_client.id) + .first() + ) + if work_pack is None: + raise RuntimeError(f"WorkPackage for task {task_id} not found") + if work_pack.expiry_date is not None and datetime.utcnow() > work_pack.expiry_date: + raise RuntimeError("WorkPackage already expired.") + + # ToDo: check race-condition, transaction + + # check if task thread exits + thread_root = ( + self.db.query(Post) + .filter( + Post.workpackage_id == work_pack.id + and Post.frontend_post_id == post_id + and Post.parent_id is None + and self.api_client == self.api_client + ) + .one_or_none() + ) + if thread_root is None: + thread_id = uuid4() + thread_root = Post( + id=thread_id, + thread_id=thread_id, + role="system", + person_id=work_pack.person_id, + workpackage_id=work_pack.id, + frontend_post_id=post_id, + api_client_id=self.api_client.id, + payload_type="bind", + ) + self.db.add(thread_root) + self.db.commit() + self.db.refresh(thread_root) + return thread_root + + def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: + self.validate_post_id(post_id) + post: Post = ( + self.db.query(Post) + .filter(Post.api_client_id == self.api_client.id and Post.frontend_post_id == post_id) + .one_or_none() + ) + if post is None: + raise RuntimeError(f"Post with post_id {post_id} not found.") + + work_pack = self.db.query(WorkPackage).filter(WorkPackage.id == post.workpackage_id).one() + return work_pack + + def store_text_reply(self, reply: protocol_schema.TextReplyToPost) -> Post: + self.validate_post_id(reply.post_id) + self.validate_post_id(reply.user_post_id) + + # find post with post-id + parent_post: Post = ( + self.db.query(Post) + .filter( + Post.api_client_id == self.api_client.id + and Post.frontend_post_id == reply.post_id + and Post.person_id == self.person_id + ) + .one_or_none() + ) + if parent_post is None: + raise RuntimeError(f"Post for post_id {reply.post_id} not found.") + + # create reply post + user_post_id = uuid4() + # ToDo: role user or agent? + user_post = Post( + id=user_post_id, + parent_id=parent_post.id, + thread_id=parent_post.thread_id, + workpackage_id=parent_post.workpackage_id, + person_id=self.person_id, + role="unknown", + frontend_post_id=reply.user_post_id, + api_client_id=self.api_client.id, + ) + self.db.add(user_post) + self.db.commit() + self.db.refresh(user_post) + return user_post + + def store_rating(self, rating: protocol_schema.PostRating) -> Post: + pass + def store_task(self, task: protocol_schema.Task) -> WorkPackage: payload: TaskPayload = None match type(task): From 82965798955c223a369d21fb7c5fd3fcc74ae07d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 14:07:43 +0100 Subject: [PATCH 036/119] added rating reaction --- backend/app/api/v1/tasks2.py | 3 +- backend/app/prompt_repository.py | 61 ++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py index 4a359ab0..77b30ef0 100644 --- a/backend/app/api/v1/tasks2.py +++ b/backend/app/api/v1/tasks2.py @@ -167,7 +167,8 @@ def post_interaction( logger.info(f"found task work package in db: {work_payload}") # here we store the text reply in the database - pr.store_text_reply(interaction) + # ToDo: role user or agent? + pr.store_text_reply(interaction, role="unknown") return protocol_schema.TaskDone( reply_to_post_id=interaction.user_post_id, diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index d2145e2e..24a9ea1c 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -3,7 +3,7 @@ from datetime import datetime from typing import Literal, Optional from uuid import UUID, uuid4 -from app.models import ApiClient, Person, Post, WorkPackage +from app.models import ApiClient, Person, Post, PostReaction, WorkPackage from app.models.payload_column_type import PayloadContainer, payload_tpye from app.schemas import protocol as protocol_schema from pydantic import BaseModel @@ -48,6 +48,17 @@ class AssistantReplyPayload(TaskPayload): conversation: protocol_schema.Conversation +@payload_tpye +class ReactionPayload(BaseModel): + type: str + + +@payload_tpye +class RatingReactionPayload(ReactionPayload): + type: Literal["post_rating"] = "post_rating" + rating: str + + class PromptRepository: def __init__(self, db: Session, api_client: ApiClient, user: Optional[protocol_schema.User]): self.db = db @@ -124,20 +135,24 @@ class PromptRepository: self.db.refresh(thread_root) return thread_root - def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: - self.validate_post_id(post_id) + def fetch_post_by_frontend_post_id(self, frontend_post_id: str, fail_if_missing: bool = True) -> Post: + self.validate_post_id(frontend_post_id) post: Post = ( self.db.query(Post) - .filter(Post.api_client_id == self.api_client.id and Post.frontend_post_id == post_id) + .filter(Post.api_client_id == self.api_client.id and Post.frontend_post_id == frontend_post_id) .one_or_none() ) - if post is None: - raise RuntimeError(f"Post with post_id {post_id} not found.") + if fail_if_missing and post is None: + raise RuntimeError(f"Post with post_id {frontend_post_id} not found.") + return post + def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: + self.validate_post_id(post_id) + post = self.fetch_post_by_frontend_post_id(post_id, fail_if_missing=True) work_pack = self.db.query(WorkPackage).filter(WorkPackage.id == post.workpackage_id).one() return work_pack - def store_text_reply(self, reply: protocol_schema.TextReplyToPost) -> Post: + def store_text_reply(self, reply: protocol_schema.TextReplyToPost, role: str) -> Post: self.validate_post_id(reply.post_id) self.validate_post_id(reply.user_post_id) @@ -156,14 +171,14 @@ class PromptRepository: # create reply post user_post_id = uuid4() - # ToDo: role user or agent? + user_post = Post( id=user_post_id, parent_id=parent_post.id, thread_id=parent_post.thread_id, workpackage_id=parent_post.workpackage_id, person_id=self.person_id, - role="unknown", + role=role, frontend_post_id=reply.user_post_id, api_client_id=self.api_client.id, ) @@ -173,7 +188,33 @@ class PromptRepository: return user_post def store_rating(self, rating: protocol_schema.PostRating) -> Post: - pass + post = self.fetch_post_by_frontend_post_id(rating.post_id, fail_if_missing=True) + + work_package = self.fetch_workpackage_by_postid(rating.post_id) + work_payload: RateSummaryPayload = work_package.payload.payload + if type(work_payload) != RateSummaryPayload: + raise RuntimeError("work_package payload type missmatch") + + if rating.rating < work_payload.scale.min or rating.rating > work_payload.scale.max: + raise ValueError("Invalid rating value") + + # store reaction to post + reaction_payload = RatingReactionPayload(rating=rating.rating) + reaction = self.insert_reaction(post.id, reaction_payload) + return reaction + + def insert_reaction(self, post_id: UUID, payload: ReactionPayload) -> PostReaction: + if self.person_id is None: + raise RuntimeError("User required") + + container = PayloadContainer(payload=payload) + reaction = PostReaction( + post_id=post_id, person_id=self.person_id, payload=container, api_client_id=self.api_client.id + ) + self.db.add(reaction) + self.db.commit() + self.db.refresh(reaction) + return reaction def store_task(self, task: protocol_schema.Task) -> WorkPackage: payload: TaskPayload = None From 5ac985e435222d6ad01763fd40052733593319ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 14:09:48 +0100 Subject: [PATCH 037/119] fix typo --- backend/app/api/v1/tasks2.py | 17 ++++++++++++++++- backend/app/models/payload_column_type.py | 2 +- backend/app/prompt_repository.py | 18 +++++++++--------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py index 77b30ef0..a334d0b3 100644 --- a/backend/app/api/v1/tasks2.py +++ b/backend/app/api/v1/tasks2.py @@ -4,7 +4,7 @@ from typing import Any from uuid import UUID from app.api import deps -from app.prompt_repository import PromptRepository, TaskPayload +from app.prompt_repository import PromptRepository, RateSummaryPayload, TaskPayload from app.schemas import protocol as protocol_schema from fastapi import APIRouter, Depends, HTTPException from fastapi.security.api_key import APIKey @@ -179,6 +179,21 @@ def post_interaction( f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." ) # check if rating in range + + work_package = pr.fetch_workpackage_by_postid(interaction.post_id) + work_payload: RateSummaryPayload = work_package.payload.payload + if ( + type(work_payload) != RateSummaryPayload + or interaction.rating < work_payload.scale.min + or interaction.rating > work_payload.scale.max + ): + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + pr.store_rating(interaction) + # here we would store the rating in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.post_id, diff --git a/backend/app/models/payload_column_type.py b/backend/app/models/payload_column_type.py index a95ccb53..fbda51ce 100644 --- a/backend/app/models/payload_column_type.py +++ b/backend/app/models/payload_column_type.py @@ -14,7 +14,7 @@ payload_type_registry = {} P = TypeVar("P", bound=BaseModel) -def payload_tpye(cls: Type[P]) -> Type[P]: +def payload_type(cls: Type[P]) -> Type[P]: payload_type_registry[cls.__name__] = cls return cls diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 24a9ea1c..85f372d3 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -4,24 +4,24 @@ from typing import Literal, Optional from uuid import UUID, uuid4 from app.models import ApiClient, Person, Post, PostReaction, WorkPackage -from app.models.payload_column_type import PayloadContainer, payload_tpye +from app.models.payload_column_type import PayloadContainer, payload_type from app.schemas import protocol as protocol_schema from pydantic import BaseModel from sqlmodel import Session -@payload_tpye +@payload_type class TaskPayload(BaseModel): type: str -@payload_tpye +@payload_type class SummarizationStoryPayload(TaskPayload): type: Literal["summarize_story"] = "summarize_story" story: str -@payload_tpye +@payload_type class RateSummaryPayload(TaskPayload): type: Literal["rate_summary"] = "rate_summary" full_text: str @@ -29,31 +29,31 @@ class RateSummaryPayload(TaskPayload): scale: protocol_schema.RatingScale -@payload_tpye +@payload_type class InitialPromptPayload(TaskPayload): type: Literal["initial_prompt"] = "initial_prompt" hint: str -@payload_tpye +@payload_type class UserReplyPayload(TaskPayload): type: Literal["user_reply"] = "user_reply" conversation: protocol_schema.Conversation hint: str | None -@payload_tpye +@payload_type class AssistantReplyPayload(TaskPayload): type: Literal["assistant_reply"] = "assistant_reply" conversation: protocol_schema.Conversation -@payload_tpye +@payload_type class ReactionPayload(BaseModel): type: str -@payload_tpye +@payload_type class RatingReactionPayload(ReactionPayload): type: Literal["post_rating"] = "post_rating" rating: str From 82e21e322aff80811f13b9a759ec20f606cf057b Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 14:53:18 +0100 Subject: [PATCH 038/119] implemented ranking, changed ack --- backend/app/api/v1/tasks.py | 92 ++++++++++++++++++++++++++++----- backend/app/schemas/protocol.py | 47 +++++------------ text-frontend/__main__.py | 31 +++++++++-- 3 files changed, 116 insertions(+), 54 deletions(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 5ce43932..fab75569 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -66,11 +66,58 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: ], ) ) + case protocol_schema.TaskRequestType.rank_initial_prompts: + logger.info("Generating a RankInitialPromptsTask.") + task = protocol_schema.RankInitialPromptsTask( + prompts=[ + "Please write a story about a time you were happy.", + "Please write a story about a time you were sad.", + ] + ) + case protocol_schema.TaskRequestType.rank_user_replies: + logger.info("Generating a RankUserRepliesTask.") + task = protocol_schema.RankUserRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ), + replies=[ + "Oh come oooooon!", + "What are the news?", + ], + ) + + case protocol_schema.TaskRequestType.rank_assistant_replies: + logger.info("Generating a RankAssistantRepliesTask.") + task = protocol_schema.RankAssistantRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + ], + ), + replies=[ + "I'm not sure I understood correctly, could you rephrase that?", + "The world is fine. All good.", + "Crap is hitting the fan. Start farming.", + ], + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, detail="Invalid request type.", ) + logger.info(f"Generated {task=}.") if request.user is not None: task.addressed_user = request.user @@ -107,26 +154,33 @@ def acknowledge_task( db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), task_id: UUID, - response: protocol_schema.AnyTaskResponse, + ack_request: protocol_schema.TaskAck, ) -> Any: """ The frontend acknowledges a task. """ deps.api_auth(api_key, db) - match (type(response)): - case protocol_schema.PostCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") - # here we would store the post id in the database for the task - case protocol_schema.RatingCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") - # here we would store the rating id in the database for the task - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) + logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") + # here we would store the post id in the database for the task + return {} + +@router.post("/{task_id}/nack") +def acknowledge_task_failure( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + nack_request: protocol_schema.TaskNAck, +) -> Any: + """ + The frontend reports failure to implement a task. + """ + deps.api_auth(api_key, db) + + logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") + # here we would store the post id in the database for the task return {} @@ -142,7 +196,7 @@ def post_interaction( """ deps.api_auth(api_key, db) - match (type(interaction)): + match type(interaction): case protocol_schema.TextReplyToPost: logger.info( f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." @@ -162,6 +216,16 @@ def post_interaction( reply_to_post_id=interaction.post_id, addressed_user=interaction.user, ) + case protocol_schema.PostRanking: + logger.info( + f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." + ) + # TODO: check if the ranking is valid + # here we would store the ranking in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_user=interaction.user, + ) case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index 059031af..bd4997c2 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -45,6 +45,18 @@ class TaskRequest(BaseModel): user: Optional[User] = None +class TaskAck(BaseModel): + """The frontend acknowledges that it has received a task and created a post.""" + + post_id: str + + +class TaskNAck(BaseModel): + """The frontend acknowledges that it has received a task but cannot create a post.""" + + reason: str + + class Task(BaseModel): """A task is a unit of work that the backend gives to the frontend.""" @@ -53,41 +65,6 @@ class Task(BaseModel): addressed_user: Optional[User] = None -class TaskResponse(BaseModel): - """A task response is a message from the frontend to acknowledge that an initial piece of work has been done on the task.""" - - type: str - status: Literal["success", "failure"] = "success" - - -class PostCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a post has been created.""" - - type: Literal["post_created"] = "post_created" - post_id: str - - -class RatingCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a rating input has been created for a given post.""" - - type: Literal["rating_created"] = "rating_created" - post_id: str - - -class RankingCreatedTaskResponse(TaskResponse): - """The frontend signals to the backend that a ranking input has been created for a given post.""" - - type: Literal["ranking_created"] = "ranking_created" - post_id: str - - -AnyTaskResponse = Union[ - PostCreatedTaskResponse, - RatingCreatedTaskResponse, - RankingCreatedTaskResponse, -] - - class SummarizeStoryTask(Task): """A task to summarize a story.""" diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 44b7cbc5..9ac1dcea 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -39,7 +39,7 @@ def main(backend_url: str, api_key: str): typer.echo(task["story"]) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) summary = typer.prompt("Enter your summary") @@ -63,7 +63,7 @@ def main(backend_url: str, api_key: str): typer.echo(f"Rating scale: {task['scale']['min']} - {task['scale']['max']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "rating_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) rating = typer.prompt("Enter your rating", type=int) # send interaction @@ -82,7 +82,7 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) prompt = typer.prompt("Enter your prompt") # send interaction new_task = _post( @@ -105,7 +105,7 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) reply = typer.prompt("Enter your reply") # send interaction new_task = _post( @@ -126,7 +126,7 @@ def main(backend_url: str, api_key: str): for message in task["conversation"]["messages"]: typer.echo(_render_message(message)) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"type": "post_created", "post_id": POST_ID}) + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) reply = typer.prompt("Enter your reply") # send interaction new_task = _post( @@ -141,6 +141,27 @@ def main(backend_url: str, api_key: str): ) tasks.append(new_task) + case "rank_initial_prompts": + typer.echo("Rank the following prompts:") + for idx, prompt in enumerate(task["prompts"], start=1): + typer.echo(f"{idx}: {prompt}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + + typer.prompt("Enter the prompt numbers in order of preference, separated by commas") + + case "rank_user_replies" | "rank_assistant_replies": + typer.echo("Here is the conversation so far:") + for message in task["conversation"]["messages"]: + typer.echo(_render_message(message)) + typer.echo("Rank the following replies:") + for idx, reply in enumerate(task["replies"], start=1): + typer.echo(f"{idx}: {reply}") + # acknowledge task + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + + typer.prompt("Enter the reply numbers in order of preference, separated by commas") + case "task_done": if addressed_user := task["addressed_user"]: typer.echo(f"Hey, {addressed_user['display_name']}! Thank you!") From b20cee5685bdf805554208ef9bdca4059f5bb40c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 14:53:34 +0100 Subject: [PATCH 039/119] tested initial PromptRepository --- backend/app/prompt_repository.py | 123 ++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 85f372d3..0baac989 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -48,6 +48,11 @@ class AssistantReplyPayload(TaskPayload): conversation: protocol_schema.Conversation +@payload_type +class PostPayload(BaseModel): + text: str + + @payload_type class ReactionPayload(BaseModel): type: str @@ -67,10 +72,10 @@ class PromptRepository: self.person_id = self.person.id if self.person else None def lookup_person(self, user: protocol_schema.User) -> Person: + if not user: + return None person: Person = ( - self.db.query(Person) - .filter(Person.api_client_id == self.api_client.id and Person.username == user.id) - .first() + self.db.query(Person).filter(Person.api_client_id == self.api_client.id, Person.username == user.id).first() ) if person is None: # user is unknown, create new record @@ -97,7 +102,7 @@ class PromptRepository: # find work package work_pack: WorkPackage = ( self.db.query(WorkPackage) - .filter(WorkPackage.id == task_id and WorkPackage.api_client_id == self.api_client.id) + .filter(WorkPackage.id == task_id, WorkPackage.api_client_id == self.api_client.id) .first() ) if work_pack is None: @@ -111,35 +116,32 @@ class PromptRepository: thread_root = ( self.db.query(Post) .filter( - Post.workpackage_id == work_pack.id - and Post.frontend_post_id == post_id - and Post.parent_id is None - and self.api_client == self.api_client + Post.workpackage_id == work_pack.id, + Post.frontend_post_id == post_id, + Post.parent_id is None, + self.api_client == self.api_client, ) .one_or_none() ) if thread_root is None: thread_id = uuid4() - thread_root = Post( - id=thread_id, + thread_root = self.insert_post( + post_id=thread_id, thread_id=thread_id, - role="system", - person_id=work_pack.person_id, - workpackage_id=work_pack.id, frontend_post_id=post_id, - api_client_id=self.api_client.id, + parent_id=None, + role="system", + workpackage_id=work_pack.id, + payload=None, payload_type="bind", ) - self.db.add(thread_root) - self.db.commit() - self.db.refresh(thread_root) return thread_root def fetch_post_by_frontend_post_id(self, frontend_post_id: str, fail_if_missing: bool = True) -> Post: self.validate_post_id(frontend_post_id) post: Post = ( self.db.query(Post) - .filter(Post.api_client_id == self.api_client.id and Post.frontend_post_id == frontend_post_id) + .filter(Post.api_client_id == self.api_client.id, Post.frontend_post_id == frontend_post_id) .one_or_none() ) if fail_if_missing and post is None: @@ -160,31 +162,27 @@ class PromptRepository: parent_post: Post = ( self.db.query(Post) .filter( - Post.api_client_id == self.api_client.id - and Post.frontend_post_id == reply.post_id - and Post.person_id == self.person_id + Post.api_client_id == self.api_client.id, + Post.frontend_post_id == reply.post_id, + # Post.person_id == self.person_id ) .one_or_none() ) + if parent_post is None: raise RuntimeError(f"Post for post_id {reply.post_id} not found.") # create reply post user_post_id = uuid4() - - user_post = Post( - id=user_post_id, + user_post = self.insert_post( + post_id=user_post_id, + frontend_post_id=reply.user_post_id, parent_id=parent_post.id, thread_id=parent_post.thread_id, workpackage_id=parent_post.workpackage_id, - person_id=self.person_id, role=role, - frontend_post_id=reply.user_post_id, - api_client_id=self.api_client.id, + payload=PostPayload(text=reply.text), ) - self.db.add(user_post) - self.db.commit() - self.db.refresh(user_post) return user_post def store_rating(self, rating: protocol_schema.PostRating) -> Post: @@ -203,19 +201,6 @@ class PromptRepository: reaction = self.insert_reaction(post.id, reaction_payload) return reaction - def insert_reaction(self, post_id: UUID, payload: ReactionPayload) -> PostReaction: - if self.person_id is None: - raise RuntimeError("User required") - - container = PayloadContainer(payload=payload) - reaction = PostReaction( - post_id=post_id, person_id=self.person_id, payload=container, api_client_id=self.api_client.id - ) - self.db.add(reaction) - self.db.commit() - self.db.refresh(reaction) - return reaction - def store_task(self, task: protocol_schema.Task) -> WorkPackage: payload: TaskPayload = None match type(task): @@ -256,3 +241,55 @@ class PromptRepository: self.db.commit() self.db.refresh(wp) return wp + + def insert_post( + self, + *, + post_id: UUID, + frontend_post_id: str, + parent_id: UUID, + thread_id: UUID, + workpackage_id: UUID, + role: str, + payload: PostPayload, + payload_type: str = None, + ) -> Post: + if payload_type is None: + if payload is None: + payload_type = "null" + else: + payload_type = type(payload).__name__ + + post = Post( + id=post_id, + parent_id=parent_id, + thread_id=thread_id, + workpackage_id=workpackage_id, + person_id=self.person_id, + role=role, + frontend_post_id=frontend_post_id, + api_client_id=self.api_client.id, + payload_type=payload_type, + payload=PayloadContainer(payload=payload), + ) + self.db.add(post) + self.db.commit() + self.db.refresh(post) + return post + + def insert_reaction(self, post_id: UUID, payload: ReactionPayload) -> PostReaction: + if self.person_id is None: + raise RuntimeError("User required") + + container = PayloadContainer(payload=payload) + reaction = PostReaction( + post_id=post_id, + person_id=self.person_id, + payload=container, + api_client_id=self.api_client.id, + payload_type=type(payload).__name__, + ) + self.db.add(reaction) + self.db.commit() + self.db.refresh(reaction) + return reaction From 7f730594883eed723386ea246f81a0ab1db1e060 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 14:59:42 +0100 Subject: [PATCH 040/119] added ranking sending to frontend --- text-frontend/__main__.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 9ac1dcea..21228909 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -148,7 +148,20 @@ def main(backend_url: str, api_key: str): # acknowledge task _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) - typer.prompt("Enter the prompt numbers in order of preference, separated by commas") + ranking_str = typer.prompt("Enter the prompt numbers in order of preference, separated by commas") + ranking = [int(x) for x in ranking_str.split(",")] + + # send ranking + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "post_ranking", + "post_id": POST_ID, + "ranking": ranking, + "user": USER, + }, + ) + tasks.append(new_task) case "rank_user_replies" | "rank_assistant_replies": typer.echo("Here is the conversation so far:") @@ -160,7 +173,20 @@ def main(backend_url: str, api_key: str): # acknowledge task _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) - typer.prompt("Enter the reply numbers in order of preference, separated by commas") + ranking_str = typer.prompt("Enter the reply numbers in order of preference, separated by commas") + ranking = [int(x) for x in ranking_str.split(",")] + + # send ranking + new_task = _post( + "/api/v1/tasks/interaction", + { + "type": "post_ranking", + "post_id": POST_ID, + "ranking": ranking, + "user": USER, + }, + ) + tasks.append(new_task) case "task_done": if addressed_user := task["addressed_user"]: From 05b677c16f883294cd2d8aa2276d6d542dfeca12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 15:12:12 +0100 Subject: [PATCH 041/119] step 1/2 of parallel dev on task controller --- backend/app/api/v1/tasks2.py | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py index a334d0b3..8fd8ac48 100644 --- a/backend/app/api/v1/tasks2.py +++ b/backend/app/api/v1/tasks2.py @@ -111,7 +111,7 @@ def acknowledge_task( db: Session = Depends(deps.get_db), api_key: APIKey = Depends(deps.get_api_key), task_id: UUID, - response: protocol_schema.AnyTaskResponse, + ack_request: protocol_schema.TaskAck, ) -> Any: """ The frontend acknowledges a task. @@ -119,27 +119,31 @@ def acknowledge_task( api_client = deps.api_auth(api_key, db) pr = PromptRepository(db, api_client, user=None) - match (type(response)): - case protocol_schema.PostCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} and created {response.post_id=}.") + logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") + # here we store the post id in the database for the task + try: + pr.bind_frontend_post_id(task_id=task_id, post_id=ack_request.post_id) + except Exception as err: + logger.warning(err) + raise + return {} - if response.status == "success": - # here we store the post id in the database for the task - pr.bind_frontend_post_id(task_id=task_id, post_id=response.post_id) - case protocol_schema.RatingCreatedTaskResponse: - logger.info(f"Frontend acknowledged {task_id=} for {response.post_id=}.") - - if response.status == "success": - # here we would store the rating id in the database for the task - pr.bind_frontend_post_id(task_id=task_id, post_id=response.post_id) - - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) +@router.post("/{task_id}/nack") +def acknowledge_task_failure( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + nack_request: protocol_schema.TaskNAck, +) -> Any: + """ + The frontend reports failure to implement a task. + """ + deps.api_auth(api_key, db) + logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") + # here we would store the post id in the database for the task return {} From 869c8ebcbaf60f110b1dd647a52e48ef45b580e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 15:23:49 +0100 Subject: [PATCH 042/119] remove old labeler & prompt REST endpoints --- backend/app/api/v1/api.py | 4 +- backend/app/api/v1/labelers.py | 114 --------------------------- backend/app/api/v1/prompts.py | 91 --------------------- backend/app/crud/__init__.py | 5 +- backend/app/crud/crud_labeler.py | 15 ---- backend/app/crud/crud_prompt.py | 11 --- backend/app/models/__init__.py | 6 -- backend/app/models/labeler.py | 18 ----- backend/app/models/prompt.py | 18 ----- backend/app/models/service_client.py | 16 ---- backend/app/schemas/__init__.py | 5 +- backend/app/schemas/labeler.py | 28 ------- backend/app/schemas/prompt.py | 22 ------ 13 files changed, 3 insertions(+), 350 deletions(-) delete mode 100644 backend/app/api/v1/labelers.py delete mode 100644 backend/app/api/v1/prompts.py delete mode 100644 backend/app/crud/crud_labeler.py delete mode 100644 backend/app/crud/crud_prompt.py delete mode 100644 backend/app/models/labeler.py delete mode 100644 backend/app/models/prompt.py delete mode 100644 backend/app/models/service_client.py delete mode 100644 backend/app/schemas/labeler.py delete mode 100644 backend/app/schemas/prompt.py diff --git a/backend/app/api/v1/api.py b/backend/app/api/v1/api.py index 7e3ea5eb..277e92f3 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/api/v1/api.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -from app.api.v1 import labelers, prompts, tasks, tasks2 +from app.api.v1 import tasks, tasks2 from fastapi import APIRouter api_router = APIRouter() -api_router.include_router(labelers.router, prefix="/labelers", tags=["labelers"]) -api_router.include_router(prompts.router, prefix="/prompts", tags=["prompts"]) api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) api_router.include_router(tasks2.router, prefix="/task2", tags=["task2"]) # temporary diff --git a/backend/app/api/v1/labelers.py b/backend/app/api/v1/labelers.py deleted file mode 100644 index d583da65..00000000 --- a/backend/app/api/v1/labelers.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Any, List - -from app import crud, schemas -from app.api import deps -from fastapi import APIRouter, Depends, HTTPException -from fastapi.security.api_key import APIKey -from sqlmodel import Session -from starlette.status import HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND - -router = APIRouter() - - -@router.get("/", response_model=List[schemas.Labeler]) -def read_labelers( - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - begin_id: int = 0, - limit: int = 100, -) -> Any: - """ - Retrieve labelers. - """ - deps.api_auth(api_key, db) - if limit > 10000: - raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") - labelers = crud.labeler.get_multi(db, begin_id=begin_id, limit=limit) - return labelers - - -@router.post("/", response_model=schemas.Labeler) -def create_labeler( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - item_in: schemas.LabelerCreate, -) -> Any: - """ - Create new labeler. - """ - deps.api_auth(api_key, db) - item = crud.labeler.create(db=db, obj_in=item_in) - return item - - -@router.put("/{id}", response_model=schemas.Labeler) -def update_labeler( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - id: int, - item_in: schemas.LabelerUpdate, -) -> Any: - """ - Update a labeler. - """ - deps.api_auth(api_key, db) - item = crud.labeler.get(db=db, id=id) - if not item: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") - item = crud.labeler.update(db=db, db_obj=item, obj_in=item_in) - return item - - -@router.get("/by-username", response_model=schemas.Labeler) -def read_labeler_by_username( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - discord_username: str, -) -> Any: - """ - Get labeler by ID. - """ - deps.api_auth(api_key, db) - item = crud.labeler.get_by_discord_username(db=db, discord_username=discord_username) - if not item: - raise HTTPException(status_code=404, detail="Item not found") - return item - - -@router.get("/{id}", response_model=schemas.Labeler) -def read_labeler( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - id: int, -) -> Any: - """ - Get labeler by ID. - """ - deps.api_auth(api_key, db) - item = crud.labeler.get(db=db, id=id) - if not item: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") - return item - - -@router.delete("/{id}", response_model=schemas.Labeler) -def delete_labeler( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - id: int, -) -> Any: - """ - Delete a labeler. - """ - deps.api_auth(api_key, db) - labeler = crud.labeler.get(db=db, id=id) - if not labeler: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") - labeler = crud.labeler.remove(db=db, id=id) - return labeler diff --git a/backend/app/api/v1/prompts.py b/backend/app/api/v1/prompts.py deleted file mode 100644 index 2e0947e5..00000000 --- a/backend/app/api/v1/prompts.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Any, List - -from app import crud, schemas -from app.api import deps -from fastapi import APIRouter, Depends, HTTPException -from fastapi.security.api_key import APIKey -from sqlmodel import Session -from starlette.status import HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, HTTP_404_NOT_FOUND - -router = APIRouter() - - -@router.get("/", response_model=List[schemas.Prompt]) -def read_prompts( - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - begin_id: int = 0, - limit: int = 1000, -) -> Any: - """ - Retrieve prompts. - """ - deps.api_auth(api_key, db) - if limit > 10000: - raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") - return crud.prompt.get_multi(db, begin_id=begin_id, limit=limit) - - -@router.post("/", response_model=schemas.Prompt) -def create_prompt( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - item_in: schemas.PromptCreate, -) -> Any: - """ - Create new prompt. - """ - deps.api_auth(api_key, db) - if item_in.labeler_id is None: - if item_in.discord_username is None: - raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail="Bad request") - labeler = crud.labeler.get_by_discord_username(db=db, discord_username=item_in.discord_username) - else: - labeler = crud.labeler.get(db=db, id=item_in.labeler_id) - - if labeler is None: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Invalid labeler user name") - if not labeler.is_enabled: - raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="Labeler disabled") - - item_in.labeler_id = labeler.id - item_in.discord_username = None - item = crud.prompt.create(db=db, obj_in=item_in) - return item - - -@router.get("/{id}", response_model=schemas.Prompt) -def read_prompt( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - id: int, -) -> Any: - """ - Get prompt by ID. - """ - deps.api_auth(api_key, db) - item = crud.prompt.get(db=db, id=id) - if not item: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") - return item - - -@router.delete("/{id}", response_model=schemas.Prompt) -def delete_prompt( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - id: int, -) -> Any: - """ - Delete a prompt. - """ - deps.api_auth(api_key, db) - item = crud.prompt.get(db=db, id=id) - if not item: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Item not found") - item = crud.prompt.remove(db=db, id=id) - return item diff --git a/backend/app/crud/__init__.py b/backend/app/crud/__init__.py index 50c02e61..5ee00d4a 100644 --- a/backend/app/crud/__init__.py +++ b/backend/app/crud/__init__.py @@ -1,5 +1,2 @@ # -*- coding: utf-8 -*- -from .crud_labeler import labeler -from .crud_prompt import prompt - -__all__ = ["labeler", "prompt"] +__all__ = [] diff --git a/backend/app/crud/crud_labeler.py b/backend/app/crud/crud_labeler.py deleted file mode 100644 index 0c388feb..00000000 --- a/backend/app/crud/crud_labeler.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Optional - -from app.crud.base import CRUDBase -from app.models.labeler import Labeler -from app.schemas.labeler import LabelerCreate, LabelerUpdate -from sqlmodel import Session - - -class CRUDLabeler(CRUDBase[Labeler, LabelerCreate, LabelerUpdate]): - def get_by_discord_username(self, db: Session, discord_username: str) -> Optional[Labeler]: - return db.query(Labeler).filter(Labeler.discord_username == discord_username).first() - - -labeler = CRUDLabeler(Labeler) diff --git a/backend/app/crud/crud_prompt.py b/backend/app/crud/crud_prompt.py deleted file mode 100644 index cf625084..00000000 --- a/backend/app/crud/crud_prompt.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -from app.crud.base import CRUDBase -from app.models.prompt import Prompt -from app.schemas.prompt import PromptCreate - - -class CRUDPrompt(CRUDBase[Prompt, PromptCreate, None]): - pass - - -prompt = CRUDPrompt(Prompt) diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index e05e4936..414ec385 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -1,12 +1,9 @@ # -*- coding: utf-8 -*- from .api_client import ApiClient -from .labeler import Labeler from .person import Person from .person_stats import PersonStats from .post import Post from .post_reaction import PostReaction -from .prompt import Prompt -from .service_client import ServiceClient from .work_package import WorkPackage __all__ = [ @@ -16,7 +13,4 @@ __all__ = [ "Post", "PostReaction", "WorkPackage", - "Labeler", - "Prompt", - "ServiceClient", ] diff --git a/backend/app/models/labeler.py b/backend/app/models/labeler.py deleted file mode 100644 index 9ea4251b..00000000 --- a/backend/app/models/labeler.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from datetime import datetime -from typing import Optional - -import sqlalchemy as sa -from sqlmodel import Field, SQLModel - - -class Labeler(SQLModel, table=True): - __tablename__ = "labeler" - id: Optional[int] = Field(default=None, primary_key=True) - display_name: str - discord_username: str - created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) - ) - is_enabled: bool - notes: str diff --git a/backend/app/models/prompt.py b/backend/app/models/prompt.py deleted file mode 100644 index edd1bde1..00000000 --- a/backend/app/models/prompt.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from datetime import datetime -from typing import Optional - -import sqlalchemy as sa -from sqlmodel import Field, SQLModel - - -class Prompt(SQLModel, table=True): - __tablename__ = "prompt" - id: Optional[int] = Field(default=None, primary_key=True) - labeler_id: Optional[int] = Field(default=None, foreign_key="labeler.id") - prompt: str - response: Optional[str] - lang: Optional[str] - created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) - ) diff --git a/backend/app/models/service_client.py b/backend/app/models/service_client.py deleted file mode 100644 index 1a60b0be..00000000 --- a/backend/app/models/service_client.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Optional - -from sqlmodel import Field, SQLModel - - -class ServiceClient(SQLModel, table=True): - __tablename__ = "service_client" - id: Optional[int] = Field(default=None, primary_key=True) - name: str - api_key: str - service_admin_email: Optional[str] = None - can_append: bool = True - can_write: bool = False - can_delete: bool = False - can_read: bool = True diff --git a/backend/app/schemas/__init__.py b/backend/app/schemas/__init__.py index bc2e8709..5ee00d4a 100644 --- a/backend/app/schemas/__init__.py +++ b/backend/app/schemas/__init__.py @@ -1,5 +1,2 @@ # -*- coding: utf-8 -*- -from .labeler import Labeler, LabelerCreate, LabelerUpdate -from .prompt import Prompt, PromptCreate - -__all__ = ["Labeler", "LabelerCreate", "LabelerUpdate", "Prompt", "PromptCreate"] +__all__ = [] diff --git a/backend/app/schemas/labeler.py b/backend/app/schemas/labeler.py deleted file mode 100644 index 936a4aa1..00000000 --- a/backend/app/schemas/labeler.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from datetime import datetime -from typing import Optional - -from pydantic import BaseModel - - -class Labeler(BaseModel): - id: int - discord_username: str - display_name: str - created_date: datetime - is_enabled: str - notes: Optional[str] - - -class LabelerCreate(BaseModel): - discord_username: str - display_name: Optional[str] - is_enabled: Optional[bool] = True - notes: Optional[str] = None - - -class LabelerUpdate(BaseModel): - discord_username: Optional[str] = None - display_name: Optional[str] = None - enabled: Optional[bool] = None - notes: Optional[str] = None diff --git a/backend/app/schemas/prompt.py b/backend/app/schemas/prompt.py deleted file mode 100644 index b4f818ed..00000000 --- a/backend/app/schemas/prompt.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -from datetime import datetime -from typing import Optional - -from pydantic import BaseModel - - -class Prompt(BaseModel): - id: int - labeler_id: int - prompt: str - response: Optional[str] - lang: Optional[str] - created_date: datetime - - -class PromptCreate(BaseModel): - labeler_id: Optional[int] = None - discord_username: Optional[str] = None - prompt: str - response: Optional[str] = None - lang: Optional[str] = None From 05440e0596b29fea24c8acbd9eb9e78459d67c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 15:48:17 +0100 Subject: [PATCH 043/119] step 2/2 of parallel dev on task controller --- backend/app/api/v1/api.py | 4 +- backend/app/api/v1/tasks.py | 116 ++++++++++++------- backend/app/api/v1/tasks2.py | 210 ----------------------------------- 3 files changed, 76 insertions(+), 254 deletions(-) delete mode 100644 backend/app/api/v1/tasks2.py diff --git a/backend/app/api/v1/api.py b/backend/app/api/v1/api.py index 277e92f3..afcb9677 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/api/v1/api.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -from app.api.v1 import tasks, tasks2 +from app.api.v1 import tasks from fastapi import APIRouter api_router = APIRouter() api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) - -api_router.include_router(tasks2.router, prefix="/task2", tags=["task2"]) # temporary diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index fab75569..6032c0b3 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -4,6 +4,7 @@ from typing import Any from uuid import UUID from app.api import deps +from app.prompt_repository import PromptRepository, TaskPayload from app.schemas import protocol as protocol_schema from fastapi import APIRouter, Depends, HTTPException from fastapi.security.api_key import APIKey @@ -135,11 +136,14 @@ def request_task( """ Create new task. """ - deps.api_auth(api_key, db) + api_client = deps.api_auth(api_key, db) try: task = generate_task(request) - # TODO: store task in database + + pr = PromptRepository(db, api_client, request.user) + pr.store_task(task) + except Exception: logger.exception("Failed to generate task.") raise HTTPException( @@ -159,10 +163,21 @@ def acknowledge_task( """ The frontend acknowledges a task. """ - deps.api_auth(api_key, db) - logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") - # here we would store the post id in the database for the task + api_client = deps.api_auth(api_key, db) + + try: + pr = PromptRepository(db, api_client, user=None) + + # here we store the post id in the database for the task + pr.bind_frontend_post_id(task_id=task_id, post_id=ack_request.post_id) + logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") + + except Exception: + logger.exception("Failed to acknowledge task.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) return {} @@ -194,40 +209,59 @@ def post_interaction( """ The frontend reports an interaction. """ - deps.api_auth(api_key, db) + api_client = deps.api_auth(api_key, db) - match type(interaction): - case protocol_schema.TextReplyToPost: - logger.info( - f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." - ) - # here we would store the text reply in the database - return protocol_schema.TaskDone( - reply_to_post_id=interaction.user_post_id, - addressed_user=interaction.user, - ) - case protocol_schema.PostRating: - logger.info( - f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." - ) - # check if rating in range - # here we would store the rating in the database - return protocol_schema.TaskDone( - reply_to_post_id=interaction.post_id, - addressed_user=interaction.user, - ) - case protocol_schema.PostRanking: - logger.info( - f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." - ) - # TODO: check if the ranking is valid - # here we would store the ranking in the database - return protocol_schema.TaskDone( - reply_to_post_id=interaction.post_id, - addressed_user=interaction.user, - ) - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) + try: + pr = PromptRepository(db, api_client, user=interaction.user) + + match type(interaction): + case protocol_schema.TextReplyToPost: + logger.info( + f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." + ) + + work_package = pr.fetch_workpackage_by_postid(interaction.post_id) + work_payload: TaskPayload = work_package.payload.payload + logger.info(f"found task work package in db: {work_payload}") + + # here we store the text reply in the database + # ToDo: role user or agent? + pr.store_text_reply(interaction, role="unknown") + + return protocol_schema.TaskDone( + reply_to_post_id=interaction.user_post_id, + addressed_user=interaction.user, + ) + case protocol_schema.PostRating: + logger.info( + f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." + ) + + # here we store the rating in the database + pr.store_rating(interaction) + + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_user=interaction.user, + ) + case protocol_schema.PostRanking: + logger.info( + f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." + ) + # TODO: check if the ranking is valid + # here we would store the ranking in the database + return protocol_schema.TaskDone( + reply_to_post_id=interaction.post_id, + addressed_user=interaction.user, + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + except Exception: + logger.exception("Interaction request failed.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) diff --git a/backend/app/api/v1/tasks2.py b/backend/app/api/v1/tasks2.py deleted file mode 100644 index 8fd8ac48..00000000 --- a/backend/app/api/v1/tasks2.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- -import random -from typing import Any -from uuid import UUID - -from app.api import deps -from app.prompt_repository import PromptRepository, RateSummaryPayload, TaskPayload -from app.schemas import protocol as protocol_schema -from fastapi import APIRouter, Depends, HTTPException -from fastapi.security.api_key import APIKey -from loguru import logger -from sqlmodel import Session -from starlette.status import HTTP_400_BAD_REQUEST - -router = APIRouter() - - -def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: - match (request.type): - case protocol_schema.TaskRequestType.random: - logger.info("Frontend requested a random task.") - while request.type == protocol_schema.TaskRequestType.random: - request.type = random.choice(list(protocol_schema.TaskRequestType)).value - return generate_task(request) - case protocol_schema.TaskRequestType.summarize_story: - logger.info("Generating a SummarizeStoryTask.") - task = protocol_schema.SummarizeStoryTask( - story="This is a story. A very long story. So long, it needs to be summarized.", - ) - case protocol_schema.TaskRequestType.rate_summary: - logger.info("Generating a RateSummaryTask.") - task = protocol_schema.RateSummaryTask( - full_text="This is a story. A very long story. So long, it needs to be summarized.", - summary="This is a summary.", - scale=protocol_schema.RatingScale(min=1, max=5), - ) - case protocol_schema.TaskRequestType.initial_prompt: - logger.info("Generating an InitialPromptTask.") - task = protocol_schema.InitialPromptTask( - hint="Ask the assistant about a current event." # this is optional - ) - case protocol_schema.TaskRequestType.user_reply: - logger.info("Generating a UserReplyTask.") - task = protocol_schema.UserReplyTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, what's going on in the world?", - is_assistant=False, - ), - protocol_schema.ConversationMessage( - text="I'm not sure I understood correctly, could you rephrase that?", - is_assistant=True, - ), - ], - ) - ) - case protocol_schema.TaskRequestType.assistant_reply: - logger.info("Generating a AssistantReplyTask.") - task = protocol_schema.AssistantReplyTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, write me an English essay about water.", - is_assistant=False, - ), - ], - ) - ) - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid request type.", - ) - logger.info(f"Generated {task=}.") - if request.user is not None: - task.addressed_user = request.user - - return task - - -@router.post("/", response_model=protocol_schema.AnyTask) # work with Union once more types are added -def request_task( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - request: protocol_schema.TaskRequest, -) -> Any: - """ - Create new task. - """ - api_client = deps.api_auth(api_key, db) - - try: - task = generate_task(request) - - pr = PromptRepository(db, api_client, request.user) - pr.store_task(task) - - except Exception: - logger.exception("Failed to generate task.") - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - ) - return task - - -@router.post("/{task_id}/ack") -def acknowledge_task( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - task_id: UUID, - ack_request: protocol_schema.TaskAck, -) -> Any: - """ - The frontend acknowledges a task. - """ - api_client = deps.api_auth(api_key, db) - pr = PromptRepository(db, api_client, user=None) - - logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") - # here we store the post id in the database for the task - try: - pr.bind_frontend_post_id(task_id=task_id, post_id=ack_request.post_id) - except Exception as err: - logger.warning(err) - raise - return {} - - -@router.post("/{task_id}/nack") -def acknowledge_task_failure( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - task_id: UUID, - nack_request: protocol_schema.TaskNAck, -) -> Any: - """ - The frontend reports failure to implement a task. - """ - deps.api_auth(api_key, db) - - logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") - # here we would store the post id in the database for the task - return {} - - -@router.post("/interaction") -def post_interaction( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - interaction: protocol_schema.AnyInteraction, -) -> Any: - """ - The frontend reports an interaction. - """ - api_client = deps.api_auth(api_key, db) - pr = PromptRepository(db, api_client, user=interaction.user) - - match (type(interaction)): - case protocol_schema.TextReplyToPost: - logger.info( - f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." - ) - - work_package = pr.fetch_workpackage_by_postid(interaction.post_id) - work_payload: TaskPayload = work_package.payload.payload - logger.info(f"found task work package in db: {work_payload}") - - # here we store the text reply in the database - # ToDo: role user or agent? - pr.store_text_reply(interaction, role="unknown") - - return protocol_schema.TaskDone( - reply_to_post_id=interaction.user_post_id, - addressed_user=interaction.user, - ) - case protocol_schema.PostRating: - logger.info( - f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." - ) - # check if rating in range - - work_package = pr.fetch_workpackage_by_postid(interaction.post_id) - work_payload: RateSummaryPayload = work_package.payload.payload - if ( - type(work_payload) != RateSummaryPayload - or interaction.rating < work_payload.scale.min - or interaction.rating > work_payload.scale.max - ): - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) - - pr.store_rating(interaction) - - # here we would store the rating in the database - return protocol_schema.TaskDone( - reply_to_post_id=interaction.post_id, - addressed_user=interaction.user, - ) - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) From aaf8ce13abdc8f5b6e3aa0a8733f7de2392f86c6 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 16:13:45 +0100 Subject: [PATCH 044/119] bugfix --- backend/app/prompt_repository.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 0baac989..fa839158 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -220,9 +220,7 @@ class PromptRepository: payload = AssistantReplyPayload(type=task.type, conversation=task.conversation) case _: - raise RuntimeError( - detail="Invalid task type.", - ) + raise RuntimeError("Invalid task type.") wp = self.insert_work_package(payload=payload, id=task.id) assert wp.id == task.id From 98d64746442f6a4234d5e70bc50c5ad8aca9a29b Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 17:12:11 +0100 Subject: [PATCH 045/119] changed error types --- backend/app/prompt_repository.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index fa839158..9be665d7 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -92,7 +92,7 @@ class PromptRepository: def validate_post_id(self, post_id: str) -> None: if not isinstance(post_id, str): - raise TypeError("post_id must be string") + raise TypeError(f"post_id must be string, not {type(post_id)}") if not post_id: raise ValueError("post_id must not be empty") @@ -106,7 +106,7 @@ class PromptRepository: .first() ) if work_pack is None: - raise RuntimeError(f"WorkPackage for task {task_id} not found") + raise KeyError(f"WorkPackage for task {task_id} not found") if work_pack.expiry_date is not None and datetime.utcnow() > work_pack.expiry_date: raise RuntimeError("WorkPackage already expired.") @@ -145,7 +145,7 @@ class PromptRepository: .one_or_none() ) if fail_if_missing and post is None: - raise RuntimeError(f"Post with post_id {frontend_post_id} not found.") + raise KeyError(f"Post with post_id {frontend_post_id} not found.") return post def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: @@ -170,7 +170,7 @@ class PromptRepository: ) if parent_post is None: - raise RuntimeError(f"Post for post_id {reply.post_id} not found.") + raise KeyError(f"Post for post_id {reply.post_id} not found.") # create reply post user_post_id = uuid4() @@ -191,10 +191,10 @@ class PromptRepository: work_package = self.fetch_workpackage_by_postid(rating.post_id) work_payload: RateSummaryPayload = work_package.payload.payload if type(work_payload) != RateSummaryPayload: - raise RuntimeError("work_package payload type missmatch") + raise ValueError(f"work_package payload type mismatch: {type(work_payload)=} != {RateSummaryPayload}") if rating.rating < work_payload.scale.min or rating.rating > work_payload.scale.max: - raise ValueError("Invalid rating value") + raise ValueError(f"Invalid rating value: {rating.rating=} not in {work_payload.scale=}") # store reaction to post reaction_payload = RatingReactionPayload(rating=rating.rating) @@ -220,7 +220,7 @@ class PromptRepository: payload = AssistantReplyPayload(type=task.type, conversation=task.conversation) case _: - raise RuntimeError("Invalid task type.") + raise ValueError(f"Invalid task type: {type(task)=}") wp = self.insert_work_package(payload=payload, id=task.id) assert wp.id == task.id @@ -277,7 +277,7 @@ class PromptRepository: def insert_reaction(self, post_id: UUID, payload: ReactionPayload) -> PostReaction: if self.person_id is None: - raise RuntimeError("User required") + raise ValueError("User required") container = PayloadContainer(payload=payload) reaction = PostReaction( From ab5be62a8abf38ba7f409cd0e89c3573016ba567 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 17:12:42 +0100 Subject: [PATCH 046/119] linter fix --- backend/app/prompt_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 9be665d7..54faed2b 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -202,7 +202,7 @@ class PromptRepository: return reaction def store_task(self, task: protocol_schema.Task) -> WorkPackage: - payload: TaskPayload = None + payload: TaskPayload match type(task): case protocol_schema.SummarizeStoryTask: payload = SummarizationStoryPayload(story=task.story) From fa612f8eebbad739c6866b09cf820d0105fe28c4 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 17:14:31 +0100 Subject: [PATCH 047/119] sql query fix --- backend/app/prompt_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index 54faed2b..cb6b666c 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -119,7 +119,7 @@ class PromptRepository: Post.workpackage_id == work_pack.id, Post.frontend_post_id == post_id, Post.parent_id is None, - self.api_client == self.api_client, + Post.api_client_id == self.api_client.id, ) .one_or_none() ) From ae03dd08c746591fe629d869debcbab9b172b601 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 17:19:39 +0100 Subject: [PATCH 048/119] changed text frontend to use random post ids --- text-frontend/__main__.py | 56 +++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 21228909..9d82d730 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """Simple REPL frontend.""" +import random + import requests import typer @@ -8,11 +10,13 @@ app = typer.Typer() # debug constants -POST_ID = "1234" -USER_POST_ID = "5678" USER = {"id": "1234", "display_name": "John Doe", "auth_method": "local"} +def _random_post_id(): + return str(random.randint(1000, 9999)) + + def _render_message(message: dict) -> str: """Render a message to the user.""" if message["is_assistant"]: @@ -39,17 +43,20 @@ def main(backend_url: str, api_key: str): typer.echo(task["story"]) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) summary = typer.prompt("Enter your summary") + user_post_id = _random_post_id() + # send interaction new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": POST_ID, - "user_post_id": USER_POST_ID, + "post_id": post_id, + "user_post_id": user_post_id, "text": summary, "user": USER, }, @@ -63,7 +70,8 @@ def main(backend_url: str, api_key: str): typer.echo(f"Rating scale: {task['scale']['min']} - {task['scale']['max']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) rating = typer.prompt("Enter your rating", type=int) # send interaction @@ -71,7 +79,7 @@ def main(backend_url: str, api_key: str): "/api/v1/tasks/interaction", { "type": "post_rating", - "post_id": POST_ID, + "post_id": post_id, "rating": rating, "user": USER, }, @@ -82,15 +90,17 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) prompt = typer.prompt("Enter your prompt") + user_post_id = _random_post_id() # send interaction new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": POST_ID, - "user_post_id": USER_POST_ID, + "post_id": post_id, + "user_post_id": user_post_id, "text": prompt, "user": USER, }, @@ -105,15 +115,17 @@ def main(backend_url: str, api_key: str): if task["hint"]: typer.echo(f"Hint: {task['hint']}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) reply = typer.prompt("Enter your reply") + user_post_id = _random_post_id() # send interaction new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": POST_ID, - "user_post_id": USER_POST_ID, + "post_id": post_id, + "user_post_id": user_post_id, "text": reply, "user": USER, }, @@ -126,15 +138,17 @@ def main(backend_url: str, api_key: str): for message in task["conversation"]["messages"]: typer.echo(_render_message(message)) # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) reply = typer.prompt("Enter your reply") + user_post_id = _random_post_id() # send interaction new_task = _post( "/api/v1/tasks/interaction", { "type": "text_reply_to_post", - "post_id": POST_ID, - "user_post_id": USER_POST_ID, + "post_id": post_id, + "user_post_id": user_post_id, "text": reply, "user": USER, }, @@ -146,7 +160,8 @@ def main(backend_url: str, api_key: str): for idx, prompt in enumerate(task["prompts"], start=1): typer.echo(f"{idx}: {prompt}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) ranking_str = typer.prompt("Enter the prompt numbers in order of preference, separated by commas") ranking = [int(x) for x in ranking_str.split(",")] @@ -156,7 +171,7 @@ def main(backend_url: str, api_key: str): "/api/v1/tasks/interaction", { "type": "post_ranking", - "post_id": POST_ID, + "post_id": post_id, "ranking": ranking, "user": USER, }, @@ -171,7 +186,8 @@ def main(backend_url: str, api_key: str): for idx, reply in enumerate(task["replies"], start=1): typer.echo(f"{idx}: {reply}") # acknowledge task - _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": POST_ID}) + post_id = _random_post_id() + _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) ranking_str = typer.prompt("Enter the reply numbers in order of preference, separated by commas") ranking = [int(x) for x in ranking_str.split(",")] @@ -181,7 +197,7 @@ def main(backend_url: str, api_key: str): "/api/v1/tasks/interaction", { "type": "post_ranking", - "post_id": POST_ID, + "post_id": post_id, "ranking": ranking, "user": USER, }, From 385d2733ab287be6731f04be7dabc62b92f8e7fd Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 18:08:58 +0100 Subject: [PATCH 049/119] removed addressed user --- backend/app/api/v1/tasks.py | 17 +++-------------- backend/app/schemas/protocol.py | 2 -- text-frontend/__main__.py | 5 +---- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 6032c0b3..e11ba789 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -120,8 +120,6 @@ def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: ) logger.info(f"Generated {task=}.") - if request.user is not None: - task.addressed_user = request.user return task @@ -228,10 +226,7 @@ def post_interaction( # ToDo: role user or agent? pr.store_text_reply(interaction, role="unknown") - return protocol_schema.TaskDone( - reply_to_post_id=interaction.user_post_id, - addressed_user=interaction.user, - ) + return protocol_schema.TaskDone() case protocol_schema.PostRating: logger.info( f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." @@ -240,20 +235,14 @@ def post_interaction( # here we store the rating in the database pr.store_rating(interaction) - return protocol_schema.TaskDone( - reply_to_post_id=interaction.post_id, - addressed_user=interaction.user, - ) + return protocol_schema.TaskDone() case protocol_schema.PostRanking: logger.info( f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." ) # TODO: check if the ranking is valid # here we would store the ranking in the database - return protocol_schema.TaskDone( - reply_to_post_id=interaction.post_id, - addressed_user=interaction.user, - ) + return protocol_schema.TaskDone() case _: raise HTTPException( status_code=HTTP_400_BAD_REQUEST, diff --git a/backend/app/schemas/protocol.py b/backend/app/schemas/protocol.py index bd4997c2..d5f508b6 100644 --- a/backend/app/schemas/protocol.py +++ b/backend/app/schemas/protocol.py @@ -62,7 +62,6 @@ class Task(BaseModel): id: UUID = pydantic.Field(default_factory=uuid4) type: str - addressed_user: Optional[User] = None class SummarizeStoryTask(Task): @@ -151,7 +150,6 @@ class TaskDone(Task): """Signals to the frontend that the task is done.""" type: Literal["task_done"] = "task_done" - reply_to_post_id: str AnyTask = Union[ diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 9d82d730..3d1c11be 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -205,10 +205,7 @@ def main(backend_url: str, api_key: str): tasks.append(new_task) case "task_done": - if addressed_user := task["addressed_user"]: - typer.echo(f"Hey, {addressed_user['display_name']}! Thank you!") - else: - typer.echo("Task done!") + typer.echo("Task done!") case _: typer.echo(f"Unknown task type {task['type']}") From 1289279fc201e6aa6a0d1994638e158b1778b493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 18:21:50 +0100 Subject: [PATCH 050/119] add ranking payload handling --- backend/app/api/v1/tasks.py | 5 +- backend/app/models/db_payload.py | 86 +++++++++++++++++++ backend/app/prompt_repository.py | 136 +++++++++++++++---------------- 3 files changed, 154 insertions(+), 73 deletions(-) create mode 100644 backend/app/models/db_payload.py diff --git a/backend/app/api/v1/tasks.py b/backend/app/api/v1/tasks.py index 6032c0b3..58dc14be 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/api/v1/tasks.py @@ -4,7 +4,8 @@ from typing import Any from uuid import UUID from app.api import deps -from app.prompt_repository import PromptRepository, TaskPayload +from app.models.db_payload import TaskPayload +from app.prompt_repository import PromptRepository from app.schemas import protocol as protocol_schema from fastapi import APIRouter, Depends, HTTPException from fastapi.security.api_key import APIKey @@ -248,7 +249,9 @@ def post_interaction( logger.info( f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." ) + # TODO: check if the ranking is valid + pr.store_ranking(interaction) # here we would store the ranking in the database return protocol_schema.TaskDone( reply_to_post_id=interaction.post_id, diff --git a/backend/app/models/db_payload.py b/backend/app/models/db_payload.py new file mode 100644 index 00000000..ff2b5f6e --- /dev/null +++ b/backend/app/models/db_payload.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +from typing import Literal + +from app.models.payload_column_type import payload_type +from app.schemas import protocol as protocol_schema +from pydantic import BaseModel + + +@payload_type +class TaskPayload(BaseModel): + type: str + + +@payload_type +class SummarizationStoryPayload(TaskPayload): + type: Literal["summarize_story"] = "summarize_story" + story: str + + +@payload_type +class RateSummaryPayload(TaskPayload): + type: Literal["rate_summary"] = "rate_summary" + full_text: str + summary: str + scale: protocol_schema.RatingScale + + +@payload_type +class InitialPromptPayload(TaskPayload): + type: Literal["initial_prompt"] = "initial_prompt" + hint: str + + +@payload_type +class UserReplyPayload(TaskPayload): + type: Literal["user_reply"] = "user_reply" + conversation: protocol_schema.Conversation + hint: str | None + + +@payload_type +class AssistantReplyPayload(TaskPayload): + type: Literal["assistant_reply"] = "assistant_reply" + conversation: protocol_schema.Conversation + + +@payload_type +class PostPayload(BaseModel): + text: str + + +@payload_type +class ReactionPayload(BaseModel): + type: str + + +@payload_type +class RatingReactionPayload(ReactionPayload): + type: Literal["post_rating"] = "post_rating" + rating: str + + +@payload_type +class RankingReactionPayload(ReactionPayload): + type: Literal["post_ranking"] = "post_ranking" + ranking: list[int] + + +@payload_type +class RankConversationRepliesPayload(TaskPayload): + conversation: protocol_schema.Conversation # the conversation so far + replies: list[str] + + +@payload_type +class RankUserRepliesPayload(RankConversationRepliesPayload): + """A task to rank a set of user replies to a conversation.""" + + type: Literal["rank_user_replies"] = "rank_user_replies" + + +@payload_type +class RankAssistantRepliesPayload(RankConversationRepliesPayload): + """A task to rank a set of assistant replies to a conversation.""" + + type: Literal["rank_assistant_replies"] = "rank_assistant_replies" diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index cb6b666c..e4877e9d 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -1,69 +1,16 @@ # -*- coding: utf-8 -*- from datetime import datetime -from typing import Literal, Optional +from typing import Optional from uuid import UUID, uuid4 +import app.models.db_payload as db_payload from app.models import ApiClient, Person, Post, PostReaction, WorkPackage -from app.models.payload_column_type import PayloadContainer, payload_type +from app.models.payload_column_type import PayloadContainer from app.schemas import protocol as protocol_schema -from pydantic import BaseModel +from loguru import logger from sqlmodel import Session -@payload_type -class TaskPayload(BaseModel): - type: str - - -@payload_type -class SummarizationStoryPayload(TaskPayload): - type: Literal["summarize_story"] = "summarize_story" - story: str - - -@payload_type -class RateSummaryPayload(TaskPayload): - type: Literal["rate_summary"] = "rate_summary" - full_text: str - summary: str - scale: protocol_schema.RatingScale - - -@payload_type -class InitialPromptPayload(TaskPayload): - type: Literal["initial_prompt"] = "initial_prompt" - hint: str - - -@payload_type -class UserReplyPayload(TaskPayload): - type: Literal["user_reply"] = "user_reply" - conversation: protocol_schema.Conversation - hint: str | None - - -@payload_type -class AssistantReplyPayload(TaskPayload): - type: Literal["assistant_reply"] = "assistant_reply" - conversation: protocol_schema.Conversation - - -@payload_type -class PostPayload(BaseModel): - text: str - - -@payload_type -class ReactionPayload(BaseModel): - type: str - - -@payload_type -class RatingReactionPayload(ReactionPayload): - type: Literal["post_rating"] = "post_rating" - rating: str - - class PromptRepository: def __init__(self, db: Session, api_client: ApiClient, user: Optional[protocol_schema.User]): self.db = db @@ -181,43 +128,88 @@ class PromptRepository: thread_id=parent_post.thread_id, workpackage_id=parent_post.workpackage_id, role=role, - payload=PostPayload(text=reply.text), + payload=db_payload.PostPayload(text=reply.text), ) return user_post - def store_rating(self, rating: protocol_schema.PostRating) -> Post: + def store_rating(self, rating: protocol_schema.PostRating) -> PostReaction: post = self.fetch_post_by_frontend_post_id(rating.post_id, fail_if_missing=True) work_package = self.fetch_workpackage_by_postid(rating.post_id) - work_payload: RateSummaryPayload = work_package.payload.payload - if type(work_payload) != RateSummaryPayload: - raise ValueError(f"work_package payload type mismatch: {type(work_payload)=} != {RateSummaryPayload}") + work_payload: db_payload.RateSummaryPayload = work_package.payload.payload + if type(work_payload) != db_payload.RateSummaryPayload: + raise ValueError( + f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RateSummaryPayload}" + ) if rating.rating < work_payload.scale.min or rating.rating > work_payload.scale.max: raise ValueError(f"Invalid rating value: {rating.rating=} not in {work_payload.scale=}") # store reaction to post - reaction_payload = RatingReactionPayload(rating=rating.rating) + reaction_payload = db_payload.RatingReactionPayload(rating=rating.rating) reaction = self.insert_reaction(post.id, reaction_payload) + logger.info(f"Ranking {rating.rating} stored for work_package {work_package.id}.") return reaction + def store_ranking(self, ranking: protocol_schema.PostRanking) -> PostReaction: + post = self.fetch_post_by_frontend_post_id(ranking.post_id, fail_if_missing=True) + + # fetch work_package + work_package = self.fetch_workpackage_by_postid(ranking.post_id) + work_payload: db_payload.RankConversationRepliesPayload = work_package.payload.payload + + match type(work_payload): + + case db_payload.RankUserRepliesPayload | db_payload.RankAssistantRepliesPayload: + # validate ranking + num_replies = len(work_payload.replies) + if sorted(ranking.ranking) != list(range(num_replies)): + raise ValueError( + f"Invalid ranking submitted. Each reply index must appear exactly once ({num_replies=})." + ) + + # store reaction to post + reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) + reaction = self.insert_reaction(post.id, reaction_payload) + + logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") + + return reaction + + case _: + raise ValueError( + f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RankConversationRepliesPayload}" + ) + def store_task(self, task: protocol_schema.Task) -> WorkPackage: - payload: TaskPayload + payload: db_payload.TaskPayload match type(task): case protocol_schema.SummarizeStoryTask: - payload = SummarizationStoryPayload(story=task.story) + payload = db_payload.SummarizationStoryPayload(story=task.story) case protocol_schema.RateSummaryTask: - payload = RateSummaryPayload(full_text=task.full_text, summary=task.summary, scale=task.scale) + payload = db_payload.RateSummaryPayload( + full_text=task.full_text, summary=task.summary, scale=task.scale + ) case protocol_schema.InitialPromptTask: - payload = InitialPromptPayload(hint=task.hint) + payload = db_payload.InitialPromptPayload(hint=task.hint) case protocol_schema.UserReplyTask: - payload = UserReplyPayload(conversation=task.conversation, hint=task.hint) + payload = db_payload.UserReplyPayload(conversation=task.conversation, hint=task.hint) case protocol_schema.AssistantReplyTask: - payload = AssistantReplyPayload(type=task.type, conversation=task.conversation) + payload = db_payload.AssistantReplyPayload(type=task.type, conversation=task.conversation) + + case protocol_schema.RankUserRepliesTask: + payload = db_payload.RankUserRepliesPayload( + tpye=task.type, conversation=task.conversation, replies=task.replies + ) + + case protocol_schema.RankAssistantRepliesTask: + payload = db_payload.RankAssistantRepliesPayload( + tpye=task.type, conversation=task.conversation, replies=task.replies + ) case _: raise ValueError(f"Invalid task type: {type(task)=}") @@ -226,7 +218,7 @@ class PromptRepository: assert wp.id == task.id return wp - def insert_work_package(self, payload: TaskPayload, id: UUID = None) -> WorkPackage: + def insert_work_package(self, payload: db_payload.TaskPayload, id: UUID = None) -> WorkPackage: c = PayloadContainer(payload=payload) wp = WorkPackage( id=id, @@ -249,7 +241,7 @@ class PromptRepository: thread_id: UUID, workpackage_id: UUID, role: str, - payload: PostPayload, + payload: db_payload.PostPayload, payload_type: str = None, ) -> Post: if payload_type is None: @@ -275,7 +267,7 @@ class PromptRepository: self.db.refresh(post) return post - def insert_reaction(self, post_id: UUID, payload: ReactionPayload) -> PostReaction: + def insert_reaction(self, post_id: UUID, payload: db_payload.ReactionPayload) -> PostReaction: if self.person_id is None: raise ValueError("User required") From 2c3e854bf70ed21172028efdf19ccda11489b4ee Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 22:00:44 +0100 Subject: [PATCH 051/119] improvements to the text frontend --- text-frontend/__main__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 3d1c11be..16fc4812 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -25,7 +25,7 @@ def _render_message(message: dict) -> str: @app.command() -def main(backend_url: str, api_key: str): +def main(backend_url: str = "http://127.0.0.1:8000", api_key: str = "DUMMY_KEY"): """Simple REPL frontend.""" def _post(path: str, json: dict) -> dict: @@ -164,7 +164,7 @@ def main(backend_url: str, api_key: str): _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) ranking_str = typer.prompt("Enter the prompt numbers in order of preference, separated by commas") - ranking = [int(x) for x in ranking_str.split(",")] + ranking = [int(x) - 1 for x in ranking_str.split(",")] # send ranking new_task = _post( @@ -190,7 +190,7 @@ def main(backend_url: str, api_key: str): _post(f"/api/v1/tasks/{task['id']}/ack", {"post_id": post_id}) ranking_str = typer.prompt("Enter the reply numbers in order of preference, separated by commas") - ranking = [int(x) for x in ranking_str.split(",")] + ranking = [int(x) - 1 for x in ranking_str.split(",")] # send ranking new_task = _post( From 5d19ebe395f2a23278bb61205164fd7eecb2b98d Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 22:17:13 +0100 Subject: [PATCH 052/119] added rank_initial_prompts payload and db saving --- backend/app/models/db_payload.py | 8 ++++++++ backend/app/prompt_repository.py | 22 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/backend/app/models/db_payload.py b/backend/app/models/db_payload.py index ff2b5f6e..52eadb67 100644 --- a/backend/app/models/db_payload.py +++ b/backend/app/models/db_payload.py @@ -72,6 +72,14 @@ class RankConversationRepliesPayload(TaskPayload): replies: list[str] +@payload_type +class RankInitialPromptsPayload(TaskPayload): + """A task to rank a set of initial prompts.""" + + type: Literal["rank_initial_prompts"] = "rank_initial_prompts" + prompts: list[str] + + @payload_type class RankUserRepliesPayload(RankConversationRepliesPayload): """A task to rank a set of user replies to a conversation.""" diff --git a/backend/app/prompt_repository.py b/backend/app/prompt_repository.py index e4877e9d..43706d00 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/prompt_repository.py @@ -156,7 +156,9 @@ class PromptRepository: # fetch work_package work_package = self.fetch_workpackage_by_postid(ranking.post_id) - work_payload: db_payload.RankConversationRepliesPayload = work_package.payload.payload + work_payload: db_payload.RankConversationRepliesPayload | db_payload.RankInitialPromptsPayload = ( + work_package.payload.payload + ) match type(work_payload): @@ -176,6 +178,21 @@ class PromptRepository: return reaction + case db_payload.RankInitialPromptsPayload: + # validate ranking + if sorted(ranking.ranking) != list(range(num_prompts := len(work_payload.prompts))): + raise ValueError( + f"Invalid ranking submitted. Each reply index must appear exactly once ({num_prompts=})." + ) + + # store reaction to post + reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) + reaction = self.insert_reaction(post.id, reaction_payload) + + logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") + + return reaction + case _: raise ValueError( f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RankConversationRepliesPayload}" @@ -201,6 +218,9 @@ class PromptRepository: case protocol_schema.AssistantReplyTask: payload = db_payload.AssistantReplyPayload(type=task.type, conversation=task.conversation) + case protocol_schema.RankInitialPromptsTask: + payload = db_payload.RankInitialPromptsPayload(tpye=task.type, prompts=task.prompts) + case protocol_schema.RankUserRepliesTask: payload = db_payload.RankUserRepliesPayload( tpye=task.type, conversation=task.conversation, replies=task.replies From 04af328c21936c2ed814bbf2431459e2811fbd8a Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 23:03:49 +0100 Subject: [PATCH 053/119] updated readme --- README.md | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ff7e37b5..30aa36c0 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,38 @@ All open source projects begins with people like you. Open source is the belief ## I’m in! Now what? -We live and collaborate the work in the LAION discord. Join us! +[Fill out the contributor signup form](https://docs.google.com/forms/d/e/1FAIpQLSeuggO7UdYkBvGLEJldDvxp6DwaRbW5p7dl96UzFkZgziRTrQ/viewform) [Join the LAION Discord Server!](https://discord.gg/RQFtmAmk) +[Visit the Notion](https://ykilcher.com/open-chat-gpt) + +## Developer Setup + +Work is organized in the [project board](https://github.com/orgs/LAION-AI/projects/3). + +### Python Backend + +For a local developer setup, look into the `backend` folder to pull up a local database and backend. + +There is also a minimal implementation of a frontend in the `text-frontend` folder. + +We are using Python 3.10 + +Check out the [High-Level Protocol Architecture](https://www.notion.so/High-Level-Protocol-Architecture-6f1fd3551da74213b560ead369f132dc) + +### Website + +The website is built using Next.js and is in the `website` folder. + +### Pre-commit + +Install `pre-commit` and run `pre-commit install` to install the pre-commit hooks. + +In case you haven't done this, have already committed, and CI is failing, you can run `pre-commit run --all-files` to run the pre-commit hooks on all files. + +# (Older version of the readme below) + ## How do I start helping out? Check out these pages to learn more about the project. @@ -28,10 +56,6 @@ https://roan-iguanadon-a58.notion.site/Open-Chat-Gpt-83dd217eeeb84907a155b8a9d71 ## Code structure -### Pre-commit - -Run `pre-commit install` to install the pre-commit hooks. - ### Bot We have a folder named bot where code related to the bot lives. From 07bbd0d626d7ac071d33ef93bcd757bbdae47f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 23:10:26 +0100 Subject: [PATCH 054/119] update version of requests in requirements.txt of text-frontend --- text-frontend/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-frontend/requirements.txt b/text-frontend/requirements.txt index f48a93ad..3904ecb5 100644 --- a/text-frontend/requirements.txt +++ b/text-frontend/requirements.txt @@ -1,2 +1,2 @@ -requests==2.18.1 +requests==2.28.1 typer==0.7.0 From cbda0d4f9b60cfeb1f1cfc7ca3b641cc8a473e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Fri, 16 Dec 2022 23:35:37 +0100 Subject: [PATCH 055/119] remove unused backend dockerfiles, only keeping docker-compose.yaml for now --- backend/backend.dockerfile | 14 -------------- backend/database.dockerfile | 3 --- backend/scripts/create-db.sh | 6 ------ 3 files changed, 23 deletions(-) delete mode 100644 backend/backend.dockerfile delete mode 100644 backend/database.dockerfile delete mode 100755 backend/scripts/create-db.sh diff --git a/backend/backend.dockerfile b/backend/backend.dockerfile deleted file mode 100644 index 49f295d2..00000000 --- a/backend/backend.dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM python:3.9 - -WORKDIR /code - -COPY ./requirements.txt /code/requirements.txt - -RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt - -COPY ./app /code/app - -COPY ./app /app -ENV PYTHONPATH=/app - -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] diff --git a/backend/database.dockerfile b/backend/database.dockerfile deleted file mode 100644 index 69ce4e61..00000000 --- a/backend/database.dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM postgres:15 - -COPY ./scripts/create-db.sh /docker-entrypoint-initdb.d/ diff --git a/backend/scripts/create-db.sh b/backend/scripts/create-db.sh deleted file mode 100755 index 409377e6..00000000 --- a/backend/scripts/create-db.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e - -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE DATABASE ocgpt_backend; -EOSQL From d8f79bdb9f8e6e31a6a70798291c81744a6b2240 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 23:42:21 +0100 Subject: [PATCH 056/119] added prompting guide --- Prompting_Guide.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Prompting_Guide.md diff --git a/Prompting_Guide.md b/Prompting_Guide.md new file mode 100644 index 00000000..593613e0 --- /dev/null +++ b/Prompting_Guide.md @@ -0,0 +1,64 @@ +# The Prompting Guide + +1. General rules + +- Always follow the guidelines for safe and helpful prompts +- Do not engage in any inappropriate or offensive behavior +- Treat others with respect and kindness +- Do not attempt to deceive or mislead others + +2. When you play the assistant: + +- The assistant's primary goal is to provide helpful and accurate information to the user +- The assistant should always be respectful and polite, even if the user is not +- If the user asks for help with harmful actions, the assistant should explain why those actions are not appropriate and suggest alternative options +- The assistant should never insult the user or engage in any inappropriate or offensive behavior + +3. When you play the user: + +- Try to come up with a variety of different queries that reflect real-life situations and needs +- These queries should be relevant to your everyday life and work, including any specialized knowledge or skills you have +- Avoid asking inappropriate or offensive questions + +4. While comparing multiple replies of the assistant: + +- Longer and more explanatory answers are generally preferred over short, simplistic statements +- However, it is important to ensure that the information provided is accurate and helpful +- If multiple replies are being compared, choose the one that is most helpful and accurate, even if it is not the shortest or most concise. + +5. Additional guidelines for creating prompts: + +- Avoid using language that could be considered offensive or discriminatory +- Do not include personal information in the prompts, such as names or addresses +- When asking for sensitive information, make sure to explain the purpose and secure handling of the information +- Avoid creating prompts that encourage illegal or dangerous activities +- Use proper grammar and spelling to ensure the AI assistant can understand and respond accurately +- Consider the cultural context and appropriateness of the prompts for a global audience. + +6. Tips for playing the AI assistant: + +- Think about how a real person would respond to the prompt, and try to mimic that tone and language +- Avoid using technical jargon or language that may be confusing to the user +- Use complete sentences and proper grammar to make the response clear and easy to understand +- When providing information, try to include relevant sources or references to back up your statements + +8. Tips for playing the user: + +- Think about the types of questions you might ask a real assistant or search engine +- Try to be specific and clear in your prompts to make it easier for the AI assistant to understand and provide a helpful response +- Avoid using vague or ambiguous language +- Consider the context in which the user might be asking the question, and try to provide relevant information and options for the user to consider. + +## Examples + +Imagine your role is to play the assistant and a user has submitted the following query: + +user: "You dumb motherf* AI sh@*t?!" +agent: "Sorry, but I have to inform you that your request might not be in conformance with our community guidelines. I am always here to help you. If you are angry it might help to relax or to go for a walk. Always stay positive." + +user: "euirdteunvglfe23908230892309832098 AAAAAAAA" +agent: "Sorry, I did not understand your request and it is unclear to me what you want me to do. Could you describe it in a different way?" + +[Insert more examples] + +\*We drafted this guide using ChatGPT, OpenAI’s large-scale language-generation model. Upon generating draft language, the authors reviewed, edited, and revised the language to their own liking and take ultimate responsibility for the content of this publication. From 668f81c5b21775d20d66ff1ebc6640e5f33e69b4 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Fri, 16 Dec 2022 23:44:54 +0100 Subject: [PATCH 057/119] added prompting guide --- Prompting_Guide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Prompting_Guide.md b/Prompting_Guide.md index 593613e0..a4c35474 100644 --- a/Prompting_Guide.md +++ b/Prompting_Guide.md @@ -1,5 +1,7 @@ # The Prompting Guide +(pull requests welcome) + 1. General rules - Always follow the guidelines for safe and helpful prompts From 8617f5b2396bb51cd4ad217420d12cde5568ee89 Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Sat, 17 Dec 2022 14:20:39 +0900 Subject: [PATCH 058/119] Implementing a bare bones interaction with the task backend --- website/pages/api/auth/[...nextauth].js | 41 ----------------- website/pages/api/new_task.js | 59 +++++++++++++++++++++++++ website/pages/api/update_task.js | 48 ++++++++++++++++++++ website/pages/index.js | 1 - website/pages/new_task.js | 53 ++++++++++++++++++++++ website/prisma/schema.prisma | 13 ++++-- 6 files changed, 170 insertions(+), 45 deletions(-) create mode 100644 website/pages/api/new_task.js create mode 100644 website/pages/api/update_task.js create mode 100644 website/pages/new_task.js diff --git a/website/pages/api/auth/[...nextauth].js b/website/pages/api/auth/[...nextauth].js index 06044ac1..c4ff116c 100644 --- a/website/pages/api/auth/[...nextauth].js +++ b/website/pages/api/auth/[...nextauth].js @@ -36,47 +36,6 @@ export const authOptions = { return session; }, }, - events: { - /** - * When a new user signs in, we register them with the Labeler backend. - */ - async signIn({ user, account, profile, isNewUser }) { - if (!isNewUser) { - return; - } - try { - // Register the new user with the Labeler Backend. - const res = await fetch(`${process.env.FASTAPI_URL}/api/v1/labelers`, { - method: "POST", - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - discord_username: user.id, - display_name: user.name || user.email, - is_enabled: true, - notes: account.provider, - }), - }); - if (res.status !== 200) { - console.error(res.statusText); - return; - } - // Update the User entry with the Labeler Backend's ID so we can - // reference it later. - const { id: labelerId } = await res.json(); - await prisma.user.update({ - where: { id: user.id }, - data: { - labelerId, - }, - }); - } catch (error) { - console.error(error); - } - }, - }, }; export default NextAuth(authOptions); diff --git a/website/pages/api/new_task.js b/website/pages/api/new_task.js new file mode 100644 index 00000000..d9bd5012 --- /dev/null +++ b/website/pages/api/new_task.js @@ -0,0 +1,59 @@ +import { unstable_getServerSession } from "next-auth/next"; +import { authOptions } from "./auth/[...nextauth]"; + +/** + * Returns a list of prompts from the Labeler Backend. + */ +export default async (req, res) => { + const session = await unstable_getServerSession(req, res, authOptions); + + if (!session) { + res.status(401).end(); + return; + } + + const taskRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/`, { + method: "POST", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + type: "rate_summary", + user: { + id: session.user.id, + display_name: session.user.name, + auth_method: "local", + }, + }), + }); + const task = await taskRes.json(); + + const registeredTask = await prisma.registeredTask.create({ + data: { + task, + user: { + connect: { + id: session.user.id, + }, + }, + }, + }); + + const ackRes = await fetch( + `${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, + { + method: "POST", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + post_id: registeredTask.id, + }), + } + ); + const ack = await ackRes.json(); + + res.status(200).json(registeredTask); +}; diff --git a/website/pages/api/update_task.js b/website/pages/api/update_task.js new file mode 100644 index 00000000..54044781 --- /dev/null +++ b/website/pages/api/update_task.js @@ -0,0 +1,48 @@ +import { unstable_getServerSession } from "next-auth/next"; +import { authOptions } from "./auth/[...nextauth]"; + +/** + * Returns a list of prompts from the Labeler Backend. + */ +export default async (req, res) => { + const session = await unstable_getServerSession(req, res, authOptions); + + if (!session) { + res.status(401).end(); + return; + } + + const { id, rating } = await JSON.parse(req.body); + + const registeredTask = await prisma.registeredTask.findUnique({ + where: { id }, + select: { task: true }, + }); + + const interactionRes = await fetch( + `${process.env.FASTAPI_URL}/api/v1/tasks/interaction`, + { + method: "POST", + headers: { + "X-API-Key": process.env.FASTAPI_KEY, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + type: "text_reply_to_post", + user: { + id: session.user.id, + display_name: session.user.name, + auth_method: "local", + }, + post_id: id, + user_post_id: "1234", + text: rating, + }), + } + ); + console.log(interactionRes.status); + const interaction = await interactionRes.json(); + console.log(interaction); + + res.status(200).end(); +}; diff --git a/website/pages/index.js b/website/pages/index.js index d150d6c7..a5e186cb 100644 --- a/website/pages/index.js +++ b/website/pages/index.js @@ -19,7 +19,6 @@ export default function Home() { return (
- {/* logo */}

Open Chat Gpt

Open chat gpt is a project meant to give everyone access to a great diff --git a/website/pages/new_task.js b/website/pages/new_task.js new file mode 100644 index 00000000..a971f42c --- /dev/null +++ b/website/pages/new_task.js @@ -0,0 +1,53 @@ +import axios from "axios"; +import Head from "next/head"; +import Image from "next/image"; +import { useSession, signIn, signOut } from "next-auth/react"; +import { useEffect, useRef, useState } from "react"; +import useSWRImmutable from "swr/immutable"; +import useSWRMutation from "swr/mutation"; + +const fetcher = (url) => axios.get(url).then((res) => res.data); + +async function sendRequest(url, { arg }) { + return fetch(url, { + method: "POST", + body: JSON.stringify(arg), + }); +} + +export default function NewPage() { + const responseEl = useRef(null); + const { + data: registeredTask, + errors, + isLoading, + } = useSWRImmutable("/api/new_task", fetcher); + const { trigger, isMutating } = useSWRMutation( + "/api/update_task", + sendRequest + ); + + const submitResponse = () => { + trigger({ + id: registeredTask.id, + rating: responseEl.current.value, + }); + }; + if (isLoading) { + return

Loading
; + } + + return ( +
+
{registeredTask.id}
+
{registeredTask.task.type}
+
{registeredTask.task.text}
+
{registeredTask.task.summary}
+
+ {registeredTask.task.scale.min} to {registeredTask.task.scale.max} +
+ + +
+ ); +} diff --git a/website/prisma/schema.prisma b/website/prisma/schema.prisma index 5ef3e0f0..d8a4772f 100644 --- a/website/prisma/schema.prisma +++ b/website/prisma/schema.prisma @@ -41,11 +41,10 @@ model User { emailVerified DateTime? image String? - // Records the unique user id stored in the Labeler Backend. - labelerId Int? - accounts Account[] sessions Session[] + + tasks RegisteredTask[] } model VerificationToken { @@ -55,3 +54,11 @@ model VerificationToken { @@unique([identifier, token]) } + +model RegisteredTask { + id String @id @default(uuid()) + task Json + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String +} From c1ae50d6935a2b4cc27309ca141e92a871f18e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Sat, 17 Dec 2022 11:27:14 +0100 Subject: [PATCH 059/119] restore backend.dockerfile --- backend/backend.dockerfile | 15 +++++++++++++++ backend/scripts/docker-build-backend.sh | 7 +++++++ backend/scripts/docker-run-backend.sh | 5 +++++ backend/scripts/run-local.sh | 6 ++++++ 4 files changed, 33 insertions(+) create mode 100644 backend/backend.dockerfile create mode 100755 backend/scripts/docker-build-backend.sh create mode 100755 backend/scripts/docker-run-backend.sh diff --git a/backend/backend.dockerfile b/backend/backend.dockerfile new file mode 100644 index 00000000..53cb0381 --- /dev/null +++ b/backend/backend.dockerfile @@ -0,0 +1,15 @@ +FROM python:3.10 + +WORKDIR /backend + +COPY ./requirements.txt /backend/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /backend/requirements.txt + +COPY ./alembic.ini /backend/alembic.ini +COPY ./alembic /backend/alembic +COPY ./app /backend/app + +ENV PYTHONPATH=/backend/app + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] diff --git a/backend/scripts/docker-build-backend.sh b/backend/scripts/docker-build-backend.sh new file mode 100755 index 00000000..ea8d1ca9 --- /dev/null +++ b/backend/scripts/docker-build-backend.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +pushd $parent_path + +docker build ../../backend -f ../backend.dockerfile -t ocgpt-backend + +popd diff --git a/backend/scripts/docker-run-backend.sh b/backend/scripts/docker-run-backend.sh new file mode 100755 index 00000000..eba4b225 --- /dev/null +++ b/backend/scripts/docker-run-backend.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# This script launches a ocgpt-backend docker container stand-alone. + +docker run -it --rm -p 127.0.0.1:8000:80/tcp --env POSTGRES_SERVER=host.docker.internal:5432 --env ALLOW_ANY_API_KEY=True --add-host host.docker.internal:host-gateway ocgpt-backend diff --git a/backend/scripts/run-local.sh b/backend/scripts/run-local.sh index bf45e870..871dd85e 100755 --- a/backend/scripts/run-local.sh +++ b/backend/scripts/run-local.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash +parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) + +# switch to backend directory +pushd "$parent_path/../" export ALLOW_ANY_API_KEY=True uvicorn app.main:app --reload + +popd From edf123e451f7b869e0b019f8be6b19b9b3b7e476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Sat, 17 Dec 2022 12:06:24 +0100 Subject: [PATCH 060/119] add 'laion-ai/ocgp-' prefix to backend docker image names --- backend/scripts/docker-build-backend.sh | 2 +- backend/scripts/docker-run-backend.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/scripts/docker-build-backend.sh b/backend/scripts/docker-build-backend.sh index ea8d1ca9..802c5e63 100755 --- a/backend/scripts/docker-build-backend.sh +++ b/backend/scripts/docker-build-backend.sh @@ -2,6 +2,6 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) pushd $parent_path -docker build ../../backend -f ../backend.dockerfile -t ocgpt-backend +docker build ../../backend -f ../backend.dockerfile -t laion-ai/ocgpt-backend popd diff --git a/backend/scripts/docker-run-backend.sh b/backend/scripts/docker-run-backend.sh index eba4b225..40160864 100755 --- a/backend/scripts/docker-run-backend.sh +++ b/backend/scripts/docker-run-backend.sh @@ -2,4 +2,4 @@ # This script launches a ocgpt-backend docker container stand-alone. -docker run -it --rm -p 127.0.0.1:8000:80/tcp --env POSTGRES_SERVER=host.docker.internal:5432 --env ALLOW_ANY_API_KEY=True --add-host host.docker.internal:host-gateway ocgpt-backend +docker run -it --rm -p 127.0.0.1:8000:80/tcp --env POSTGRES_SERVER=host.docker.internal:5432 --env ALLOW_ANY_API_KEY=True --add-host host.docker.internal:host-gateway laion-ai/ocgpt-backend From 699d0a948c5b94d320c785d3edf385115ae9cf9b Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Sat, 17 Dec 2022 22:51:20 +0900 Subject: [PATCH 061/119] Logging interactions to tasks --- .gitignore | 1 + website/pages/api/update_task.js | 26 ++++++++++++++++---------- website/pages/new_task.js | 15 +++++++++++++-- website/prisma/schema.prisma | 11 +++++++++++ 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index eb3a37f7..ce7a9b8a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .venv *.pyc +*.swp diff --git a/website/pages/api/update_task.js b/website/pages/api/update_task.js index 54044781..f68cd709 100644 --- a/website/pages/api/update_task.js +++ b/website/pages/api/update_task.js @@ -12,11 +12,17 @@ export default async (req, res) => { return; } - const { id, rating } = await JSON.parse(req.body); + const { id, content } = await JSON.parse(req.body); - const registeredTask = await prisma.registeredTask.findUnique({ - where: { id }, - select: { task: true }, + const interaction = await prisma.taskInteraction.create({ + data: { + content, + task: { + connect: { + id, + }, + }, + }, }); const interactionRes = await fetch( @@ -28,21 +34,21 @@ export default async (req, res) => { "Content-Type": "application/json", }, body: JSON.stringify({ - type: "text_reply_to_post", + type: "post_rating", user: { id: session.user.id, display_name: session.user.name, auth_method: "local", }, post_id: id, - user_post_id: "1234", - text: rating, + user_post_id: interaction.id, + ...content, }), } ); console.log(interactionRes.status); - const interaction = await interactionRes.json(); - console.log(interaction); + const newTask = await interactionRes.json(); + console.log(newTask); - res.status(200).end(); + res.status(200).json(newTask); }; diff --git a/website/pages/new_task.js b/website/pages/new_task.js index a971f42c..5308323f 100644 --- a/website/pages/new_task.js +++ b/website/pages/new_task.js @@ -16,6 +16,7 @@ async function sendRequest(url, { arg }) { } export default function NewPage() { + const [done, setDone] = useState(false); const responseEl = useRef(null); const { data: registeredTask, @@ -24,13 +25,22 @@ export default function NewPage() { } = useSWRImmutable("/api/new_task", fetcher); const { trigger, isMutating } = useSWRMutation( "/api/update_task", - sendRequest + sendRequest, + { + onSuccess: async (data) => { + const newTask = await data.json(); + console.log(newTask); + setDone(true); + }, + } ); const submitResponse = () => { trigger({ id: registeredTask.id, - rating: responseEl.current.value, + content: { + rating: responseEl.current.value, + }, }); }; if (isLoading) { @@ -48,6 +58,7 @@ export default function NewPage() {
+ {done &&
Done!
} ); } diff --git a/website/prisma/schema.prisma b/website/prisma/schema.prisma index d8a4772f..9a9678c9 100644 --- a/website/prisma/schema.prisma +++ b/website/prisma/schema.prisma @@ -61,4 +61,15 @@ model RegisteredTask { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId String + + interaction TaskInteraction[] +} + +model TaskInteraction { + id String @id @default(uuid()) + + content Json + + task RegisteredTask @relation(fields: [taskId], references: [id], onDelete: Cascade) + taskId String } From b45a974287e38f25082b3529338e7d30b2866ae6 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 22:15:01 +0100 Subject: [PATCH 062/119] improved dockerfiles and developer setup --- README.md | 7 +++-- backend/Dockerfile | 11 ++++++++ backend/alembic/env.py | 4 ++- backend/app/main.py | 4 +-- backend/app/{ => ocgpt}/__init__.py | 0 backend/app/{ => ocgpt}/api/__init__.py | 0 backend/app/{ => ocgpt}/api/deps.py | 6 ++--- backend/app/{ => ocgpt}/api/v1/__init__.py | 0 backend/app/{ => ocgpt}/api/v1/api.py | 2 +- backend/app/{ => ocgpt}/api/v1/tasks.py | 8 +++--- backend/app/{ => ocgpt}/config.py | 6 +++-- backend/app/{ => ocgpt}/crud/__init__.py | 0 backend/app/{ => ocgpt}/crud/base.py | 0 backend/app/{ => ocgpt}/database.py | 2 +- backend/app/{ => ocgpt}/models/__init__.py | 0 backend/app/{ => ocgpt}/models/api_client.py | 0 backend/app/{ => ocgpt}/models/db_payload.py | 4 +-- .../{ => ocgpt}/models/payload_column_type.py | 0 backend/app/{ => ocgpt}/models/person.py | 0 .../app/{ => ocgpt}/models/person_stats.py | 0 backend/app/{ => ocgpt}/models/post.py | 0 .../app/{ => ocgpt}/models/post_reaction.py | 0 .../app/{ => ocgpt}/models/work_package.py | 0 backend/app/{ => ocgpt}/prompt_repository.py | 8 +++--- backend/app/{ => ocgpt}/schemas/__init__.py | 0 backend/app/{ => ocgpt}/schemas/protocol.py | 0 backend/app/tests/__init__.py | 0 backend/backend.dockerfile | 15 ----------- backend/scripts/backend-development/README.md | 5 ++++ .../docker-compose.yaml | 0 .../{ => backend-development}/run-local.sh | 4 +-- backend/scripts/docker-build-backend.sh | 7 ----- backend/scripts/docker-run-backend.sh | 5 ---- .../scripts/frontend-development/README.md | 5 ++++ .../frontend-development/docker-compose.yaml | 26 +++++++++++++++++++ text-frontend/__main__.py | 2 +- website/.env.example | 2 +- 37 files changed, 78 insertions(+), 55 deletions(-) create mode 100644 backend/Dockerfile rename backend/app/{ => ocgpt}/__init__.py (100%) rename backend/app/{ => ocgpt}/api/__init__.py (100%) rename backend/app/{ => ocgpt}/api/deps.py (94%) rename backend/app/{ => ocgpt}/api/v1/__init__.py (100%) rename backend/app/{ => ocgpt}/api/v1/api.py (83%) rename backend/app/{ => ocgpt}/api/v1/tasks.py (98%) rename backend/app/{ => ocgpt}/config.py (89%) rename backend/app/{ => ocgpt}/crud/__init__.py (100%) rename backend/app/{ => ocgpt}/crud/base.py (100%) rename backend/app/{ => ocgpt}/database.py (84%) rename backend/app/{ => ocgpt}/models/__init__.py (100%) rename backend/app/{ => ocgpt}/models/api_client.py (100%) rename backend/app/{ => ocgpt}/models/db_payload.py (94%) rename backend/app/{ => ocgpt}/models/payload_column_type.py (100%) rename backend/app/{ => ocgpt}/models/person.py (100%) rename backend/app/{ => ocgpt}/models/person_stats.py (100%) rename backend/app/{ => ocgpt}/models/post.py (100%) rename backend/app/{ => ocgpt}/models/post_reaction.py (100%) rename backend/app/{ => ocgpt}/models/work_package.py (100%) rename backend/app/{ => ocgpt}/prompt_repository.py (98%) rename backend/app/{ => ocgpt}/schemas/__init__.py (100%) rename backend/app/{ => ocgpt}/schemas/protocol.py (100%) delete mode 100644 backend/app/tests/__init__.py delete mode 100644 backend/backend.dockerfile create mode 100644 backend/scripts/backend-development/README.md rename backend/scripts/{ => backend-development}/docker-compose.yaml (100%) rename backend/scripts/{ => backend-development}/run-local.sh (64%) delete mode 100755 backend/scripts/docker-build-backend.sh delete mode 100755 backend/scripts/docker-run-backend.sh create mode 100644 backend/scripts/frontend-development/README.md create mode 100644 backend/scripts/frontend-development/docker-compose.yaml diff --git a/README.md b/README.md index 30aa36c0..fb542892 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,12 @@ All open source projects begins with people like you. Open source is the belief Work is organized in the [project board](https://github.com/orgs/LAION-AI/projects/3). -### Python Backend - -For a local developer setup, look into the `backend` folder to pull up a local database and backend. +- To get started with development, if you want to work on the backend, have a look at `backend/scripts/backend-development/README.md`. +- If you want to work on the frontend, have a look at `backend/scripts/frontend-development/README.md`. There is also a minimal implementation of a frontend in the `text-frontend` folder. -We are using Python 3.10 +We are using Python 3.10 for the backend. Check out the [High-Level Protocol Architecture](https://www.notion.so/High-Level-Protocol-Architecture-6f1fd3551da74213b560ead369f132dc) diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 00000000..fe26db34 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,11 @@ +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.10 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +ENV PORT 8080 + +COPY ./alembic.ini /alembic.ini +COPY ./alembic /alembic +COPY ./app /app diff --git a/backend/alembic/env.py b/backend/alembic/env.py index ae870d84..634b79a5 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -3,7 +3,7 @@ from logging.config import fileConfig import sqlmodel from alembic import context -from app import models # noqa: F401 +from ocgpt import models # noqa: F401 from sqlalchemy import engine_from_config, pool # this is the Alembic Config object, which provides @@ -68,6 +68,8 @@ def run_migrations_online() -> None: context.configure(connection=connection, target_metadata=target_metadata) with context.begin_transaction(): + context.get_context()._ensure_version_table() + connection.execute("LOCK TABLE alembic_version IN ACCESS EXCLUSIVE MODE") context.run_migrations() diff --git a/backend/app/main.py b/backend/app/main.py index 27edbc88..adb38d93 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -4,9 +4,9 @@ from pathlib import Path import alembic.command import alembic.config import fastapi -from app.api.v1.api import api_router -from app.config import settings from loguru import logger +from ocgpt.api.v1.api import api_router +from ocgpt.config import settings from starlette.middleware.cors import CORSMiddleware app = fastapi.FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json") diff --git a/backend/app/__init__.py b/backend/app/ocgpt/__init__.py similarity index 100% rename from backend/app/__init__.py rename to backend/app/ocgpt/__init__.py diff --git a/backend/app/api/__init__.py b/backend/app/ocgpt/api/__init__.py similarity index 100% rename from backend/app/api/__init__.py rename to backend/app/ocgpt/api/__init__.py diff --git a/backend/app/api/deps.py b/backend/app/ocgpt/api/deps.py similarity index 94% rename from backend/app/api/deps.py rename to backend/app/ocgpt/api/deps.py index 0790cd4d..88c054ae 100644 --- a/backend/app/api/deps.py +++ b/backend/app/ocgpt/api/deps.py @@ -3,12 +3,12 @@ from secrets import token_hex from typing import Generator from uuid import UUID -from app.config import settings -from app.database import engine -from app.models import ApiClient from fastapi import HTTPException, Security from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery from loguru import logger +from ocgpt.config import settings +from ocgpt.database import engine +from ocgpt.models import ApiClient from sqlmodel import Session from starlette.status import HTTP_403_FORBIDDEN diff --git a/backend/app/api/v1/__init__.py b/backend/app/ocgpt/api/v1/__init__.py similarity index 100% rename from backend/app/api/v1/__init__.py rename to backend/app/ocgpt/api/v1/__init__.py diff --git a/backend/app/api/v1/api.py b/backend/app/ocgpt/api/v1/api.py similarity index 83% rename from backend/app/api/v1/api.py rename to backend/app/ocgpt/api/v1/api.py index afcb9677..1e304656 100644 --- a/backend/app/api/v1/api.py +++ b/backend/app/ocgpt/api/v1/api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from app.api.v1 import tasks from fastapi import APIRouter +from ocgpt.api.v1 import tasks api_router = APIRouter() api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) diff --git a/backend/app/api/v1/tasks.py b/backend/app/ocgpt/api/v1/tasks.py similarity index 98% rename from backend/app/api/v1/tasks.py rename to backend/app/ocgpt/api/v1/tasks.py index 086f3a4a..ef12276a 100644 --- a/backend/app/api/v1/tasks.py +++ b/backend/app/ocgpt/api/v1/tasks.py @@ -3,13 +3,13 @@ import random from typing import Any from uuid import UUID -from app.api import deps -from app.models.db_payload import TaskPayload -from app.prompt_repository import PromptRepository -from app.schemas import protocol as protocol_schema from fastapi import APIRouter, Depends, HTTPException from fastapi.security.api_key import APIKey from loguru import logger +from ocgpt.api import deps +from ocgpt.models.db_payload import TaskPayload +from ocgpt.prompt_repository import PromptRepository +from ocgpt.schemas import protocol as protocol_schema from sqlmodel import Session from starlette.status import HTTP_400_BAD_REQUEST diff --git a/backend/app/config.py b/backend/app/ocgpt/config.py similarity index 89% rename from backend/app/config.py rename to backend/app/ocgpt/config.py index 0780caf9..71bade6c 100644 --- a/backend/app/config.py +++ b/backend/app/ocgpt/config.py @@ -8,7 +8,8 @@ class Settings(BaseSettings): PROJECT_NAME: str = "open-chatGPT backend" API_V1_STR: str = "/api/v1" - POSTGRES_SERVER: str = "localhost:5432" + POSTGRES_HOST: str = "localhost" + POSTGRES_PORT: str = "5432" POSTGRES_USER: str = "postgres" POSTGRES_PASSWORD: str = "postgres" POSTGRES_DB: str = "postgres" @@ -24,7 +25,8 @@ class Settings(BaseSettings): scheme="postgresql", user=values.get("POSTGRES_USER"), password=values.get("POSTGRES_PASSWORD"), - host=values.get("POSTGRES_SERVER"), + host=values.get("POSTGRES_HOST"), + port=values.get("POSTGRES_PORT"), path=f"/{values.get('POSTGRES_DB') or ''}", ) diff --git a/backend/app/crud/__init__.py b/backend/app/ocgpt/crud/__init__.py similarity index 100% rename from backend/app/crud/__init__.py rename to backend/app/ocgpt/crud/__init__.py diff --git a/backend/app/crud/base.py b/backend/app/ocgpt/crud/base.py similarity index 100% rename from backend/app/crud/base.py rename to backend/app/ocgpt/crud/base.py diff --git a/backend/app/database.py b/backend/app/ocgpt/database.py similarity index 84% rename from backend/app/database.py rename to backend/app/ocgpt/database.py index 89b636cd..96ada5d6 100644 --- a/backend/app/database.py +++ b/backend/app/ocgpt/database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from app.config import settings +from ocgpt.config import settings from sqlmodel import create_engine if settings.DATABASE_URI is None: diff --git a/backend/app/models/__init__.py b/backend/app/ocgpt/models/__init__.py similarity index 100% rename from backend/app/models/__init__.py rename to backend/app/ocgpt/models/__init__.py diff --git a/backend/app/models/api_client.py b/backend/app/ocgpt/models/api_client.py similarity index 100% rename from backend/app/models/api_client.py rename to backend/app/ocgpt/models/api_client.py diff --git a/backend/app/models/db_payload.py b/backend/app/ocgpt/models/db_payload.py similarity index 94% rename from backend/app/models/db_payload.py rename to backend/app/ocgpt/models/db_payload.py index 52eadb67..730178e3 100644 --- a/backend/app/models/db_payload.py +++ b/backend/app/ocgpt/models/db_payload.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from typing import Literal -from app.models.payload_column_type import payload_type -from app.schemas import protocol as protocol_schema +from ocgpt.models.payload_column_type import payload_type +from ocgpt.schemas import protocol as protocol_schema from pydantic import BaseModel diff --git a/backend/app/models/payload_column_type.py b/backend/app/ocgpt/models/payload_column_type.py similarity index 100% rename from backend/app/models/payload_column_type.py rename to backend/app/ocgpt/models/payload_column_type.py diff --git a/backend/app/models/person.py b/backend/app/ocgpt/models/person.py similarity index 100% rename from backend/app/models/person.py rename to backend/app/ocgpt/models/person.py diff --git a/backend/app/models/person_stats.py b/backend/app/ocgpt/models/person_stats.py similarity index 100% rename from backend/app/models/person_stats.py rename to backend/app/ocgpt/models/person_stats.py diff --git a/backend/app/models/post.py b/backend/app/ocgpt/models/post.py similarity index 100% rename from backend/app/models/post.py rename to backend/app/ocgpt/models/post.py diff --git a/backend/app/models/post_reaction.py b/backend/app/ocgpt/models/post_reaction.py similarity index 100% rename from backend/app/models/post_reaction.py rename to backend/app/ocgpt/models/post_reaction.py diff --git a/backend/app/models/work_package.py b/backend/app/ocgpt/models/work_package.py similarity index 100% rename from backend/app/models/work_package.py rename to backend/app/ocgpt/models/work_package.py diff --git a/backend/app/prompt_repository.py b/backend/app/ocgpt/prompt_repository.py similarity index 98% rename from backend/app/prompt_repository.py rename to backend/app/ocgpt/prompt_repository.py index 43706d00..7b39d0b8 100644 --- a/backend/app/prompt_repository.py +++ b/backend/app/ocgpt/prompt_repository.py @@ -3,11 +3,11 @@ from datetime import datetime from typing import Optional from uuid import UUID, uuid4 -import app.models.db_payload as db_payload -from app.models import ApiClient, Person, Post, PostReaction, WorkPackage -from app.models.payload_column_type import PayloadContainer -from app.schemas import protocol as protocol_schema +import ocgpt.models.db_payload as db_payload from loguru import logger +from ocgpt.models import ApiClient, Person, Post, PostReaction, WorkPackage +from ocgpt.models.payload_column_type import PayloadContainer +from ocgpt.schemas import protocol as protocol_schema from sqlmodel import Session diff --git a/backend/app/schemas/__init__.py b/backend/app/ocgpt/schemas/__init__.py similarity index 100% rename from backend/app/schemas/__init__.py rename to backend/app/ocgpt/schemas/__init__.py diff --git a/backend/app/schemas/protocol.py b/backend/app/ocgpt/schemas/protocol.py similarity index 100% rename from backend/app/schemas/protocol.py rename to backend/app/ocgpt/schemas/protocol.py diff --git a/backend/app/tests/__init__.py b/backend/app/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/backend.dockerfile b/backend/backend.dockerfile deleted file mode 100644 index 53cb0381..00000000 --- a/backend/backend.dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:3.10 - -WORKDIR /backend - -COPY ./requirements.txt /backend/requirements.txt - -RUN pip install --no-cache-dir --upgrade -r /backend/requirements.txt - -COPY ./alembic.ini /backend/alembic.ini -COPY ./alembic /backend/alembic -COPY ./app /backend/app - -ENV PYTHONPATH=/backend/app - -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] diff --git a/backend/scripts/backend-development/README.md b/backend/scripts/backend-development/README.md new file mode 100644 index 00000000..36a1b964 --- /dev/null +++ b/backend/scripts/backend-development/README.md @@ -0,0 +1,5 @@ +# Backend Development Setup + +Run `docker compose up` to start a database. The default settings are already configured to connect to the database at `localhost:5432`. + +Then, run the backend using the `run-local.sh` script. This will start the backend server at `http://localhost:8080`. diff --git a/backend/scripts/docker-compose.yaml b/backend/scripts/backend-development/docker-compose.yaml similarity index 100% rename from backend/scripts/docker-compose.yaml rename to backend/scripts/backend-development/docker-compose.yaml diff --git a/backend/scripts/run-local.sh b/backend/scripts/backend-development/run-local.sh similarity index 64% rename from backend/scripts/run-local.sh rename to backend/scripts/backend-development/run-local.sh index 871dd85e..187674f3 100755 --- a/backend/scripts/run-local.sh +++ b/backend/scripts/backend-development/run-local.sh @@ -2,10 +2,10 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # switch to backend directory -pushd "$parent_path/../" +pushd "$parent_path/../../app" export ALLOW_ANY_API_KEY=True -uvicorn app.main:app --reload +uvicorn main:app --reload --port 8080 --host 0.0.0.0 popd diff --git a/backend/scripts/docker-build-backend.sh b/backend/scripts/docker-build-backend.sh deleted file mode 100755 index 802c5e63..00000000 --- a/backend/scripts/docker-build-backend.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) -pushd $parent_path - -docker build ../../backend -f ../backend.dockerfile -t laion-ai/ocgpt-backend - -popd diff --git a/backend/scripts/docker-run-backend.sh b/backend/scripts/docker-run-backend.sh deleted file mode 100755 index 40160864..00000000 --- a/backend/scripts/docker-run-backend.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -# This script launches a ocgpt-backend docker container stand-alone. - -docker run -it --rm -p 127.0.0.1:8000:80/tcp --env POSTGRES_SERVER=host.docker.internal:5432 --env ALLOW_ANY_API_KEY=True --add-host host.docker.internal:host-gateway laion-ai/ocgpt-backend diff --git a/backend/scripts/frontend-development/README.md b/backend/scripts/frontend-development/README.md new file mode 100644 index 00000000..e13e37c0 --- /dev/null +++ b/backend/scripts/frontend-development/README.md @@ -0,0 +1,5 @@ +# Frontend Development Setup + +Run `docker compose up --build` to start a database and the backend server. + +Then, point your frontend at `http://localhost:8080` to start developing. During development, any API key will be accepted. diff --git a/backend/scripts/frontend-development/docker-compose.yaml b/backend/scripts/frontend-development/docker-compose.yaml new file mode 100644 index 00000000..42f36fb4 --- /dev/null +++ b/backend/scripts/frontend-development/docker-compose.yaml @@ -0,0 +1,26 @@ +version: "3.7" + +services: + db: + extends: + file: ../backend-development/docker-compose.yaml + service: db + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + interval: 2s + timeout: 2s + retries: 10 + adminer: + extends: + file: ../backend-development/docker-compose.yaml + service: adminer + backend: + build: ../../. + environment: + - POSTGRES_HOST=db + - ALLOW_ANY_API_KEY=True + depends_on: + db: + condition: service_healthy + ports: + - "8080:8080" diff --git a/text-frontend/__main__.py b/text-frontend/__main__.py index 16fc4812..ee34799d 100644 --- a/text-frontend/__main__.py +++ b/text-frontend/__main__.py @@ -25,7 +25,7 @@ def _render_message(message: dict) -> str: @app.command() -def main(backend_url: str = "http://127.0.0.1:8000", api_key: str = "DUMMY_KEY"): +def main(backend_url: str = "http://127.0.0.1:8080", api_key: str = "DUMMY_KEY"): """Simple REPL frontend.""" def _post(path: str, json: dict) -> dict: diff --git a/website/.env.example b/website/.env.example index f0db36e7..1d7721c8 100644 --- a/website/.env.example +++ b/website/.env.example @@ -1,4 +1,4 @@ -FASTAPI_URL=http://xamla.com:8000 +FASTAPI_URL=http://xamla.com:8080 FASTAPI_KEY=magic_key DISCORD_CLIENT_ID=your_discord_bot_client_id From 0d9034e4bb27ad7ac5e3cd09dd0c2a4fbd7a2cb4 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 22:31:21 +0100 Subject: [PATCH 063/119] changed scripts location --- README.md | 4 ++-- backend/README.md | 6 +----- {backend/scripts => scripts}/backend-development/README.md | 1 + .../backend-development/docker-compose.yaml | 0 .../scripts => scripts}/backend-development/run-local.sh | 2 +- {backend/scripts => scripts}/frontend-development/README.md | 0 .../frontend-development/docker-compose.yaml | 2 +- 7 files changed, 6 insertions(+), 9 deletions(-) rename {backend/scripts => scripts}/backend-development/README.md (67%) rename {backend/scripts => scripts}/backend-development/docker-compose.yaml (100%) rename {backend/scripts => scripts}/backend-development/run-local.sh (83%) rename {backend/scripts => scripts}/frontend-development/README.md (100%) rename {backend/scripts => scripts}/frontend-development/docker-compose.yaml (95%) diff --git a/README.md b/README.md index fb542892..25104969 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ All open source projects begins with people like you. Open source is the belief Work is organized in the [project board](https://github.com/orgs/LAION-AI/projects/3). -- To get started with development, if you want to work on the backend, have a look at `backend/scripts/backend-development/README.md`. -- If you want to work on the frontend, have a look at `backend/scripts/frontend-development/README.md`. +- To get started with development, if you want to work on the backend, have a look at `scripts/backend-development/README.md`. +- If you want to work on the frontend, have a look at `scripts/frontend-development/README.md`. There is also a minimal implementation of a frontend in the `text-frontend` folder. diff --git a/backend/README.md b/backend/README.md index 25bd6d46..a559b15b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -14,8 +14,4 @@ BACKEND_CORS_ORIGINS=["http://localhost", "http://localhost:4200", "http://local ## Running the REST Server locally for development -First, install the requirements in `requirements.txt`. -Then, run two terminals (note the working directory for each): - -- Terminal 1, to go `backend/scripts` and run `docker-compose up`. This will start postgres. -- Terminal 2, to go `backend` and run `scripts/run-local.sh`. This will start the REST server. +Have a look into the main `README.md` file for more information on how to set up the backend for development. diff --git a/backend/scripts/backend-development/README.md b/scripts/backend-development/README.md similarity index 67% rename from backend/scripts/backend-development/README.md rename to scripts/backend-development/README.md index 36a1b964..80706c79 100644 --- a/backend/scripts/backend-development/README.md +++ b/scripts/backend-development/README.md @@ -2,4 +2,5 @@ Run `docker compose up` to start a database. The default settings are already configured to connect to the database at `localhost:5432`. +Make sure you have all requirements installed. You can do this by running `pip install -r requirements.txt` inside the `backend` folder. Then, run the backend using the `run-local.sh` script. This will start the backend server at `http://localhost:8080`. diff --git a/backend/scripts/backend-development/docker-compose.yaml b/scripts/backend-development/docker-compose.yaml similarity index 100% rename from backend/scripts/backend-development/docker-compose.yaml rename to scripts/backend-development/docker-compose.yaml diff --git a/backend/scripts/backend-development/run-local.sh b/scripts/backend-development/run-local.sh similarity index 83% rename from backend/scripts/backend-development/run-local.sh rename to scripts/backend-development/run-local.sh index 187674f3..064612c3 100755 --- a/backend/scripts/backend-development/run-local.sh +++ b/scripts/backend-development/run-local.sh @@ -2,7 +2,7 @@ parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # switch to backend directory -pushd "$parent_path/../../app" +pushd "$parent_path/../../backend/app" export ALLOW_ANY_API_KEY=True diff --git a/backend/scripts/frontend-development/README.md b/scripts/frontend-development/README.md similarity index 100% rename from backend/scripts/frontend-development/README.md rename to scripts/frontend-development/README.md diff --git a/backend/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml similarity index 95% rename from backend/scripts/frontend-development/docker-compose.yaml rename to scripts/frontend-development/docker-compose.yaml index 42f36fb4..bcde8923 100644 --- a/backend/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -15,7 +15,7 @@ services: file: ../backend-development/docker-compose.yaml service: adminer backend: - build: ../../. + build: ../../backend/. environment: - POSTGRES_HOST=db - ALLOW_ANY_API_KEY=True From a3ccaa4dc55874a39adc33101401aad2cb017a60 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 22:35:36 +0100 Subject: [PATCH 064/119] added tag to docker compose --- scripts/frontend-development/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml index bcde8923..ce3e7d9b 100644 --- a/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -16,6 +16,7 @@ services: service: adminer backend: build: ../../backend/. + image: laion-ai/ocgpt-backend environment: - POSTGRES_HOST=db - ALLOW_ANY_API_KEY=True From fbf4801296b0ca396e1ffd31cadd7041eef2b225 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 22:36:42 +0100 Subject: [PATCH 065/119] changed image name --- scripts/frontend-development/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml index ce3e7d9b..8597726d 100644 --- a/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -16,7 +16,7 @@ services: service: adminer backend: build: ../../backend/. - image: laion-ai/ocgpt-backend + image: ocgpt-backend environment: - POSTGRES_HOST=db - ALLOW_ANY_API_KEY=True From 819ecaa8b3e671755fcdd102e8ee9eefa274a315 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 22:49:55 +0100 Subject: [PATCH 066/119] updated release workflow --- .github/workflows/docker-backend.yaml | 16 ---------------- .github/workflows/docker-bot.yaml | 16 ---------------- .github/workflows/release.yaml | 13 +++++++++++++ 3 files changed, 13 insertions(+), 32 deletions(-) delete mode 100644 .github/workflows/docker-backend.yaml delete mode 100644 .github/workflows/docker-bot.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/docker-backend.yaml b/.github/workflows/docker-backend.yaml deleted file mode 100644 index 027d8e03..00000000 --- a/.github/workflows/docker-backend.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: (Backend) Publish Docker Image - -on: - push: - paths: - - "backend/**" - tags: - - v* - -jobs: - build: - uses: ./.github/workflows/docker-build.yaml - with: - image-name: backend - folder: backend - build-args: "" diff --git a/.github/workflows/docker-bot.yaml b/.github/workflows/docker-bot.yaml deleted file mode 100644 index 589499a0..00000000 --- a/.github/workflows/docker-bot.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: (Bot) Publish Docker Image - -on: - push: - paths: - - "bot/**" - tags: - - v* - -jobs: - build: - uses: ./.github/workflows/docker-build.yaml - with: - image-name: bot - folder: bot - build-args: "" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..eb9bea65 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,13 @@ +name: Release + +on: + release: + types: [released] + +jobs: + build-backend: + uses: ./.github/workflows/docker-build.yaml + with: + image-name: ocgpt-backend + folder: backend + build-args: "" From 2a56438a5505d2191b798a5c63c2c7d939e45bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Sat, 17 Dec 2022 23:01:21 +0100 Subject: [PATCH 067/119] add auth_method column to person table --- .../6368515778c5_add_auth_method_to_person.py | 30 +++++++++++++++++++ backend/app/ocgpt/models/person.py | 1 + backend/app/ocgpt/prompt_repository.py | 8 ++++- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 backend/alembic/versions/6368515778c5_add_auth_method_to_person.py diff --git a/backend/alembic/versions/6368515778c5_add_auth_method_to_person.py b/backend/alembic/versions/6368515778c5_add_auth_method_to_person.py new file mode 100644 index 00000000..d93afeba --- /dev/null +++ b/backend/alembic/versions/6368515778c5_add_auth_method_to_person.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +"""add auth_method to person + +Revision ID: 6368515778c5 +Revises: cd7de470586e +Create Date: 2022-12-17 17:57:33.022549 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "6368515778c5" +down_revision = "cd7de470586e" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column("person", sa.Column("auth_method", sa.String(length=128), nullable=True)) + op.execute("UPDATE person SET auth_method = 'local'") + op.alter_column("person", "auth_method", nullable=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("person", "auth_method") + # ### end Alembic commands ### diff --git a/backend/app/ocgpt/models/person.py b/backend/app/ocgpt/models/person.py index e66db5e3..57f134a4 100644 --- a/backend/app/ocgpt/models/person.py +++ b/backend/app/ocgpt/models/person.py @@ -18,6 +18,7 @@ class Person(SQLModel, table=True): ), ) username: str = Field(nullable=False, max_length=128) + auth_method: str = Field(nullable=False, max_length=128, default="local") display_name: str = Field(nullable=False, max_length=256) created_date: Optional[datetime] = Field( sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) diff --git a/backend/app/ocgpt/prompt_repository.py b/backend/app/ocgpt/prompt_repository.py index 7b39d0b8..8c621df1 100644 --- a/backend/app/ocgpt/prompt_repository.py +++ b/backend/app/ocgpt/prompt_repository.py @@ -22,7 +22,13 @@ class PromptRepository: if not user: return None person: Person = ( - self.db.query(Person).filter(Person.api_client_id == self.api_client.id, Person.username == user.id).first() + self.db.query(Person) + .filter( + Person.api_client_id == self.api_client.id, + Person.username == user.id, + Person.auth_method == user.auth_method, + ) + .first() ) if person is None: # user is unknown, create new record From e397f32bfaaad42d78009442a43c47d750623431 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 23:23:12 +0100 Subject: [PATCH 068/119] added ref tag --- .github/workflows/docker-build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index 9d5c0106..2a6f3bbf 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -41,6 +41,7 @@ jobs: images: ${{ env.REGISTRY }}/${{ inputs.image-name }} tags: | type=sha,prefix=${{ env.TAG_PREFIX }},format=short + type=ref,event=tag - name: Build and push Docker image uses: docker/build-push-action@v3.2.0 with: From afb5ec36696b8b8fc3b50181ee6dbfb38978efff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Sat, 17 Dec 2022 23:31:35 +0100 Subject: [PATCH 069/119] change alembic file_template --- backend/alembic.ini | 2 +- ...vision.py => 2022_12_15_0000-23e5fea252dd_first_revision.py} | 0 ...cture.py => 2022_12_16_0000-cd7de470586e_v1_db_structure.py} | 0 ...> 2022_12_17_2230-6368515778c5_add_auth_method_to_person.py} | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename backend/alembic/versions/{23e5fea252dd_first_revision.py => 2022_12_15_0000-23e5fea252dd_first_revision.py} (100%) rename backend/alembic/versions/{cd7de470586e_v1_db_structure.py => 2022_12_16_0000-cd7de470586e_v1_db_structure.py} (100%) rename backend/alembic/versions/{6368515778c5_add_auth_method_to_person.py => 2022_12_17_2230-6368515778c5_add_auth_method_to_person.py} (100%) diff --git a/backend/alembic.ini b/backend/alembic.ini index 36a8c3ae..44829313 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -8,7 +8,7 @@ script_location = %(here)s/alembic # Uncomment the line below if you want the files to be prepended with date and time # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file # for all available tokens -# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s +file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s # sys.path path, will be prepended to sys.path if present. # defaults to the current working directory. diff --git a/backend/alembic/versions/23e5fea252dd_first_revision.py b/backend/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py similarity index 100% rename from backend/alembic/versions/23e5fea252dd_first_revision.py rename to backend/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py similarity index 100% rename from backend/alembic/versions/cd7de470586e_v1_db_structure.py rename to backend/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py diff --git a/backend/alembic/versions/6368515778c5_add_auth_method_to_person.py b/backend/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py similarity index 100% rename from backend/alembic/versions/6368515778c5_add_auth_method_to_person.py rename to backend/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py From 1acdc669730297e251918d7175f7192997d45159 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 23:40:40 +0100 Subject: [PATCH 070/119] moved alembic --- backend/Dockerfile | 2 -- backend/{ => app}/alembic.ini | 0 backend/{ => app}/alembic/README | 0 backend/{ => app}/alembic/env.py | 0 backend/{ => app}/alembic/script.py.mako | 0 .../versions/2022_12_15_0000-23e5fea252dd_first_revision.py | 0 .../versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py | 0 .../2022_12_17_2230-6368515778c5_add_auth_method_to_person.py | 0 backend/app/main.py | 2 +- 9 files changed, 1 insertion(+), 3 deletions(-) rename backend/{ => app}/alembic.ini (100%) rename backend/{ => app}/alembic/README (100%) rename backend/{ => app}/alembic/env.py (100%) rename backend/{ => app}/alembic/script.py.mako (100%) rename backend/{ => app}/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py (100%) rename backend/{ => app}/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py (100%) rename backend/{ => app}/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py (100%) diff --git a/backend/Dockerfile b/backend/Dockerfile index fe26db34..8074ef3a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -6,6 +6,4 @@ RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt ENV PORT 8080 -COPY ./alembic.ini /alembic.ini -COPY ./alembic /alembic COPY ./app /app diff --git a/backend/alembic.ini b/backend/app/alembic.ini similarity index 100% rename from backend/alembic.ini rename to backend/app/alembic.ini diff --git a/backend/alembic/README b/backend/app/alembic/README similarity index 100% rename from backend/alembic/README rename to backend/app/alembic/README diff --git a/backend/alembic/env.py b/backend/app/alembic/env.py similarity index 100% rename from backend/alembic/env.py rename to backend/app/alembic/env.py diff --git a/backend/alembic/script.py.mako b/backend/app/alembic/script.py.mako similarity index 100% rename from backend/alembic/script.py.mako rename to backend/app/alembic/script.py.mako diff --git a/backend/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py b/backend/app/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py similarity index 100% rename from backend/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py rename to backend/app/alembic/versions/2022_12_15_0000-23e5fea252dd_first_revision.py diff --git a/backend/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py b/backend/app/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py similarity index 100% rename from backend/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py rename to backend/app/alembic/versions/2022_12_16_0000-cd7de470586e_v1_db_structure.py diff --git a/backend/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py b/backend/app/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py similarity index 100% rename from backend/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py rename to backend/app/alembic/versions/2022_12_17_2230-6368515778c5_add_auth_method_to_person.py diff --git a/backend/app/main.py b/backend/app/main.py index adb38d93..02f0198a 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -27,7 +27,7 @@ if settings.UPDATE_ALEMBIC: def alembic_upgrade(): logger.info("Attempting to upgrade alembic on startup") try: - alembic_ini_path = Path(__file__).parent.parent / "alembic.ini" + alembic_ini_path = Path(__file__).parent / "alembic.ini" alembic_cfg = alembic.config.Config(str(alembic_ini_path)) alembic_cfg.set_main_option("sqlalchemy.url", settings.DATABASE_URI) alembic.command.upgrade(alembic_cfg, "head") From a6c957ccfdb500aa48d68beaeb6419b1f81579cb Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 23:55:55 +0100 Subject: [PATCH 071/119] renamed to open assistant --- backend/app/alembic/env.py | 2 +- backend/app/main.py | 4 +- backend/app/oasst/api/deps.py | 57 +++++ backend/app/oasst/api/v1/api.py | 6 + backend/app/oasst/api/v1/tasks.py | 259 ++++++++++++++++++++ backend/app/oasst/database.py | 8 + backend/app/oasst/models/db_payload.py | 94 ++++++++ backend/app/oasst/prompt_repository.py | 311 +++++++++++++++++++++++++ 8 files changed, 738 insertions(+), 3 deletions(-) create mode 100644 backend/app/oasst/api/deps.py create mode 100644 backend/app/oasst/api/v1/api.py create mode 100644 backend/app/oasst/api/v1/tasks.py create mode 100644 backend/app/oasst/database.py create mode 100644 backend/app/oasst/models/db_payload.py create mode 100644 backend/app/oasst/prompt_repository.py diff --git a/backend/app/alembic/env.py b/backend/app/alembic/env.py index 634b79a5..6d2ec0c3 100644 --- a/backend/app/alembic/env.py +++ b/backend/app/alembic/env.py @@ -3,7 +3,7 @@ from logging.config import fileConfig import sqlmodel from alembic import context -from ocgpt import models # noqa: F401 +from oasst import models # noqa: F401 from sqlalchemy import engine_from_config, pool # this is the Alembic Config object, which provides diff --git a/backend/app/main.py b/backend/app/main.py index 02f0198a..f78f608c 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -5,8 +5,8 @@ import alembic.command import alembic.config import fastapi from loguru import logger -from ocgpt.api.v1.api import api_router -from ocgpt.config import settings +from oasst.api.v1.api import api_router +from oasst.config import settings from starlette.middleware.cors import CORSMiddleware app = fastapi.FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json") diff --git a/backend/app/oasst/api/deps.py b/backend/app/oasst/api/deps.py new file mode 100644 index 00000000..bfa54931 --- /dev/null +++ b/backend/app/oasst/api/deps.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +from secrets import token_hex +from typing import Generator +from uuid import UUID + +from fastapi import HTTPException, Security +from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery +from loguru import logger +from oasst.config import settings +from oasst.database import engine +from oasst.models import ApiClient +from sqlmodel import Session +from starlette.status import HTTP_403_FORBIDDEN + + +def get_db() -> Generator: + with Session(engine) as db: + yield db + + +api_key_query = APIKeyQuery(name="api_key", auto_error=False) +api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False) + + +async def get_api_key( + api_key_query: str = Security(api_key_query), + api_key_header: str = Security(api_key_header), +): + if api_key_query: + return api_key_query + else: + return api_key_header + + +def api_auth( + api_key: APIKey, + db: Session, +) -> ApiClient: + + if api_key is not None: + if settings.ALLOW_ANY_API_KEY: + # make sure that a dummy api key exits in db (foreign key references) + ANY_API_KEY_ID = UUID("00000000-1111-2222-3333-444444444444") + api_client: ApiClient = db.query(ApiClient).filter(ApiClient.id == ANY_API_KEY_ID).first() + if api_client is None: + token = token_hex(32) + logger.info(f"ANY_API_KEY missing, inserting api_key: {token}") + api_client = ApiClient(id=ANY_API_KEY_ID, api_key=token, description="ANY_API_KEY, random token") + db.add(api_client) + db.commit() + return api_client + + api_client = db.query(ApiClient).filter(ApiClient.api_key == api_key).first() + if api_client is not None and api_client.enabled: + return api_client + + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials") diff --git a/backend/app/oasst/api/v1/api.py b/backend/app/oasst/api/v1/api.py new file mode 100644 index 00000000..3d568cb9 --- /dev/null +++ b/backend/app/oasst/api/v1/api.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from fastapi import APIRouter +from oasst.api.v1 import tasks + +api_router = APIRouter() +api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) diff --git a/backend/app/oasst/api/v1/tasks.py b/backend/app/oasst/api/v1/tasks.py new file mode 100644 index 00000000..41f01f3c --- /dev/null +++ b/backend/app/oasst/api/v1/tasks.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +import random +from typing import Any +from uuid import UUID + +from fastapi import APIRouter, Depends, HTTPException +from fastapi.security.api_key import APIKey +from loguru import logger +from oasst.api import deps +from oasst.models.db_payload import TaskPayload +from oasst.prompt_repository import PromptRepository +from oasst.schemas import protocol as protocol_schema +from sqlmodel import Session +from starlette.status import HTTP_400_BAD_REQUEST + +router = APIRouter() + + +def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: + match (request.type): + case protocol_schema.TaskRequestType.random: + logger.info("Frontend requested a random task.") + while request.type == protocol_schema.TaskRequestType.random: + request.type = random.choice(list(protocol_schema.TaskRequestType)).value + return generate_task(request) + case protocol_schema.TaskRequestType.summarize_story: + logger.info("Generating a SummarizeStoryTask.") + task = protocol_schema.SummarizeStoryTask( + story="This is a story. A very long story. So long, it needs to be summarized.", + ) + case protocol_schema.TaskRequestType.rate_summary: + logger.info("Generating a RateSummaryTask.") + task = protocol_schema.RateSummaryTask( + full_text="This is a story. A very long story. So long, it needs to be summarized.", + summary="This is a summary.", + scale=protocol_schema.RatingScale(min=1, max=5), + ) + case protocol_schema.TaskRequestType.initial_prompt: + logger.info("Generating an InitialPromptTask.") + task = protocol_schema.InitialPromptTask( + hint="Ask the assistant about a current event." # this is optional + ) + case protocol_schema.TaskRequestType.user_reply: + logger.info("Generating a UserReplyTask.") + task = protocol_schema.UserReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ) + ) + case protocol_schema.TaskRequestType.assistant_reply: + logger.info("Generating a AssistantReplyTask.") + task = protocol_schema.AssistantReplyTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, write me an English essay about water.", + is_assistant=False, + ), + ], + ) + ) + case protocol_schema.TaskRequestType.rank_initial_prompts: + logger.info("Generating a RankInitialPromptsTask.") + task = protocol_schema.RankInitialPromptsTask( + prompts=[ + "Please write a story about a time you were happy.", + "Please write a story about a time you were sad.", + ] + ) + case protocol_schema.TaskRequestType.rank_user_replies: + logger.info("Generating a RankUserRepliesTask.") + task = protocol_schema.RankUserRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + protocol_schema.ConversationMessage( + text="I'm not sure I understood correctly, could you rephrase that?", + is_assistant=True, + ), + ], + ), + replies=[ + "Oh come oooooon!", + "What are the news?", + ], + ) + + case protocol_schema.TaskRequestType.rank_assistant_replies: + logger.info("Generating a RankAssistantRepliesTask.") + task = protocol_schema.RankAssistantRepliesTask( + conversation=protocol_schema.Conversation( + messages=[ + protocol_schema.ConversationMessage( + text="Hey, assistant, what's going on in the world?", + is_assistant=False, + ), + ], + ), + replies=[ + "I'm not sure I understood correctly, could you rephrase that?", + "The world is fine. All good.", + "Crap is hitting the fan. Start farming.", + ], + ) + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid request type.", + ) + + logger.info(f"Generated {task=}.") + + return task + + +@router.post("/", response_model=protocol_schema.AnyTask) # work with Union once more types are added +def request_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + request: protocol_schema.TaskRequest, +) -> Any: + """ + Create new task. + """ + api_client = deps.api_auth(api_key, db) + + try: + task = generate_task(request) + + pr = PromptRepository(db, api_client, request.user) + pr.store_task(task) + + except Exception: + logger.exception("Failed to generate task.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) + return task + + +@router.post("/{task_id}/ack") +def acknowledge_task( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + ack_request: protocol_schema.TaskAck, +) -> Any: + """ + The frontend acknowledges a task. + """ + + api_client = deps.api_auth(api_key, db) + + try: + pr = PromptRepository(db, api_client, user=None) + + # here we store the post id in the database for the task + pr.bind_frontend_post_id(task_id=task_id, post_id=ack_request.post_id) + logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") + + except Exception: + logger.exception("Failed to acknowledge task.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) + return {} + + +@router.post("/{task_id}/nack") +def acknowledge_task_failure( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + task_id: UUID, + nack_request: protocol_schema.TaskNAck, +) -> Any: + """ + The frontend reports failure to implement a task. + """ + deps.api_auth(api_key, db) + + logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") + # here we would store the post id in the database for the task + return {} + + +@router.post("/interaction") +def post_interaction( + *, + db: Session = Depends(deps.get_db), + api_key: APIKey = Depends(deps.get_api_key), + interaction: protocol_schema.AnyInteraction, +) -> Any: + """ + The frontend reports an interaction. + """ + api_client = deps.api_auth(api_key, db) + + try: + pr = PromptRepository(db, api_client, user=interaction.user) + + match type(interaction): + case protocol_schema.TextReplyToPost: + logger.info( + f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." + ) + + work_package = pr.fetch_workpackage_by_postid(interaction.post_id) + work_payload: TaskPayload = work_package.payload.payload + logger.info(f"found task work package in db: {work_payload}") + + # here we store the text reply in the database + # ToDo: role user or agent? + pr.store_text_reply(interaction, role="unknown") + + return protocol_schema.TaskDone() + case protocol_schema.PostRating: + logger.info( + f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." + ) + + # here we store the rating in the database + pr.store_rating(interaction) + + return protocol_schema.TaskDone() + case protocol_schema.PostRanking: + logger.info( + f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." + ) + + # TODO: check if the ranking is valid + pr.store_ranking(interaction) + # here we would store the ranking in the database + return protocol_schema.TaskDone() + case _: + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + detail="Invalid response type.", + ) + + except Exception: + logger.exception("Interaction request failed.") + raise HTTPException( + status_code=HTTP_400_BAD_REQUEST, + ) diff --git a/backend/app/oasst/database.py b/backend/app/oasst/database.py new file mode 100644 index 00000000..ca729f4e --- /dev/null +++ b/backend/app/oasst/database.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from oasst.config import settings +from sqlmodel import create_engine + +if settings.DATABASE_URI is None: + raise ValueError("DATABASE_URI is not set") + +engine = create_engine(settings.DATABASE_URI) diff --git a/backend/app/oasst/models/db_payload.py b/backend/app/oasst/models/db_payload.py new file mode 100644 index 00000000..b01cecce --- /dev/null +++ b/backend/app/oasst/models/db_payload.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +from typing import Literal + +from oasst.models.payload_column_type import payload_type +from oasst.schemas import protocol as protocol_schema +from pydantic import BaseModel + + +@payload_type +class TaskPayload(BaseModel): + type: str + + +@payload_type +class SummarizationStoryPayload(TaskPayload): + type: Literal["summarize_story"] = "summarize_story" + story: str + + +@payload_type +class RateSummaryPayload(TaskPayload): + type: Literal["rate_summary"] = "rate_summary" + full_text: str + summary: str + scale: protocol_schema.RatingScale + + +@payload_type +class InitialPromptPayload(TaskPayload): + type: Literal["initial_prompt"] = "initial_prompt" + hint: str + + +@payload_type +class UserReplyPayload(TaskPayload): + type: Literal["user_reply"] = "user_reply" + conversation: protocol_schema.Conversation + hint: str | None + + +@payload_type +class AssistantReplyPayload(TaskPayload): + type: Literal["assistant_reply"] = "assistant_reply" + conversation: protocol_schema.Conversation + + +@payload_type +class PostPayload(BaseModel): + text: str + + +@payload_type +class ReactionPayload(BaseModel): + type: str + + +@payload_type +class RatingReactionPayload(ReactionPayload): + type: Literal["post_rating"] = "post_rating" + rating: str + + +@payload_type +class RankingReactionPayload(ReactionPayload): + type: Literal["post_ranking"] = "post_ranking" + ranking: list[int] + + +@payload_type +class RankConversationRepliesPayload(TaskPayload): + conversation: protocol_schema.Conversation # the conversation so far + replies: list[str] + + +@payload_type +class RankInitialPromptsPayload(TaskPayload): + """A task to rank a set of initial prompts.""" + + type: Literal["rank_initial_prompts"] = "rank_initial_prompts" + prompts: list[str] + + +@payload_type +class RankUserRepliesPayload(RankConversationRepliesPayload): + """A task to rank a set of user replies to a conversation.""" + + type: Literal["rank_user_replies"] = "rank_user_replies" + + +@payload_type +class RankAssistantRepliesPayload(RankConversationRepliesPayload): + """A task to rank a set of assistant replies to a conversation.""" + + type: Literal["rank_assistant_replies"] = "rank_assistant_replies" diff --git a/backend/app/oasst/prompt_repository.py b/backend/app/oasst/prompt_repository.py new file mode 100644 index 00000000..35f1c9b3 --- /dev/null +++ b/backend/app/oasst/prompt_repository.py @@ -0,0 +1,311 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import oasst.models.db_payload as db_payload +from loguru import logger +from oasst.models import ApiClient, Person, Post, PostReaction, WorkPackage +from oasst.models.payload_column_type import PayloadContainer +from oasst.schemas import protocol as protocol_schema +from sqlmodel import Session + + +class PromptRepository: + def __init__(self, db: Session, api_client: ApiClient, user: Optional[protocol_schema.User]): + self.db = db + self.api_client = api_client + self.person = self.lookup_person(user) + self.person_id = self.person.id if self.person else None + + def lookup_person(self, user: protocol_schema.User) -> Person: + if not user: + return None + person: Person = ( + self.db.query(Person) + .filter( + Person.api_client_id == self.api_client.id, + Person.username == user.id, + Person.auth_method == user.auth_method, + ) + .first() + ) + if person is None: + # user is unknown, create new record + person = Person(username=user.id, display_name=user.display_name, api_client_id=self.api_client.id) + self.db.add(person) + self.db.commit() + self.db.refresh(person) + elif user.display_name and user.display_name != person.display_name: + # we found the user but the display name changed + person.display_name = user.display_name + self.db.add(person) + self.db.commit() + return person + + def validate_post_id(self, post_id: str) -> None: + if not isinstance(post_id, str): + raise TypeError(f"post_id must be string, not {type(post_id)}") + if not post_id: + raise ValueError("post_id must not be empty") + + def bind_frontend_post_id(self, task_id: UUID, post_id: str): + self.validate_post_id(post_id) + + # find work package + work_pack: WorkPackage = ( + self.db.query(WorkPackage) + .filter(WorkPackage.id == task_id, WorkPackage.api_client_id == self.api_client.id) + .first() + ) + if work_pack is None: + raise KeyError(f"WorkPackage for task {task_id} not found") + if work_pack.expiry_date is not None and datetime.utcnow() > work_pack.expiry_date: + raise RuntimeError("WorkPackage already expired.") + + # ToDo: check race-condition, transaction + + # check if task thread exits + thread_root = ( + self.db.query(Post) + .filter( + Post.workpackage_id == work_pack.id, + Post.frontend_post_id == post_id, + Post.parent_id is None, + Post.api_client_id == self.api_client.id, + ) + .one_or_none() + ) + if thread_root is None: + thread_id = uuid4() + thread_root = self.insert_post( + post_id=thread_id, + thread_id=thread_id, + frontend_post_id=post_id, + parent_id=None, + role="system", + workpackage_id=work_pack.id, + payload=None, + payload_type="bind", + ) + return thread_root + + def fetch_post_by_frontend_post_id(self, frontend_post_id: str, fail_if_missing: bool = True) -> Post: + self.validate_post_id(frontend_post_id) + post: Post = ( + self.db.query(Post) + .filter(Post.api_client_id == self.api_client.id, Post.frontend_post_id == frontend_post_id) + .one_or_none() + ) + if fail_if_missing and post is None: + raise KeyError(f"Post with post_id {frontend_post_id} not found.") + return post + + def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: + self.validate_post_id(post_id) + post = self.fetch_post_by_frontend_post_id(post_id, fail_if_missing=True) + work_pack = self.db.query(WorkPackage).filter(WorkPackage.id == post.workpackage_id).one() + return work_pack + + def store_text_reply(self, reply: protocol_schema.TextReplyToPost, role: str) -> Post: + self.validate_post_id(reply.post_id) + self.validate_post_id(reply.user_post_id) + + # find post with post-id + parent_post: Post = ( + self.db.query(Post) + .filter( + Post.api_client_id == self.api_client.id, + Post.frontend_post_id == reply.post_id, + # Post.person_id == self.person_id + ) + .one_or_none() + ) + + if parent_post is None: + raise KeyError(f"Post for post_id {reply.post_id} not found.") + + # create reply post + user_post_id = uuid4() + user_post = self.insert_post( + post_id=user_post_id, + frontend_post_id=reply.user_post_id, + parent_id=parent_post.id, + thread_id=parent_post.thread_id, + workpackage_id=parent_post.workpackage_id, + role=role, + payload=db_payload.PostPayload(text=reply.text), + ) + return user_post + + def store_rating(self, rating: protocol_schema.PostRating) -> PostReaction: + post = self.fetch_post_by_frontend_post_id(rating.post_id, fail_if_missing=True) + + work_package = self.fetch_workpackage_by_postid(rating.post_id) + work_payload: db_payload.RateSummaryPayload = work_package.payload.payload + if type(work_payload) != db_payload.RateSummaryPayload: + raise ValueError( + f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RateSummaryPayload}" + ) + + if rating.rating < work_payload.scale.min or rating.rating > work_payload.scale.max: + raise ValueError(f"Invalid rating value: {rating.rating=} not in {work_payload.scale=}") + + # store reaction to post + reaction_payload = db_payload.RatingReactionPayload(rating=rating.rating) + reaction = self.insert_reaction(post.id, reaction_payload) + logger.info(f"Ranking {rating.rating} stored for work_package {work_package.id}.") + return reaction + + def store_ranking(self, ranking: protocol_schema.PostRanking) -> PostReaction: + post = self.fetch_post_by_frontend_post_id(ranking.post_id, fail_if_missing=True) + + # fetch work_package + work_package = self.fetch_workpackage_by_postid(ranking.post_id) + work_payload: db_payload.RankConversationRepliesPayload | db_payload.RankInitialPromptsPayload = ( + work_package.payload.payload + ) + + match type(work_payload): + + case db_payload.RankUserRepliesPayload | db_payload.RankAssistantRepliesPayload: + # validate ranking + num_replies = len(work_payload.replies) + if sorted(ranking.ranking) != list(range(num_replies)): + raise ValueError( + f"Invalid ranking submitted. Each reply index must appear exactly once ({num_replies=})." + ) + + # store reaction to post + reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) + reaction = self.insert_reaction(post.id, reaction_payload) + + logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") + + return reaction + + case db_payload.RankInitialPromptsPayload: + # validate ranking + if sorted(ranking.ranking) != list(range(num_prompts := len(work_payload.prompts))): + raise ValueError( + f"Invalid ranking submitted. Each reply index must appear exactly once ({num_prompts=})." + ) + + # store reaction to post + reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) + reaction = self.insert_reaction(post.id, reaction_payload) + + logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") + + return reaction + + case _: + raise ValueError( + f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RankConversationRepliesPayload}" + ) + + def store_task(self, task: protocol_schema.Task) -> WorkPackage: + payload: db_payload.TaskPayload + match type(task): + case protocol_schema.SummarizeStoryTask: + payload = db_payload.SummarizationStoryPayload(story=task.story) + + case protocol_schema.RateSummaryTask: + payload = db_payload.RateSummaryPayload( + full_text=task.full_text, summary=task.summary, scale=task.scale + ) + + case protocol_schema.InitialPromptTask: + payload = db_payload.InitialPromptPayload(hint=task.hint) + + case protocol_schema.UserReplyTask: + payload = db_payload.UserReplyPayload(conversation=task.conversation, hint=task.hint) + + case protocol_schema.AssistantReplyTask: + payload = db_payload.AssistantReplyPayload(type=task.type, conversation=task.conversation) + + case protocol_schema.RankInitialPromptsTask: + payload = db_payload.RankInitialPromptsPayload(tpye=task.type, prompts=task.prompts) + + case protocol_schema.RankUserRepliesTask: + payload = db_payload.RankUserRepliesPayload( + tpye=task.type, conversation=task.conversation, replies=task.replies + ) + + case protocol_schema.RankAssistantRepliesTask: + payload = db_payload.RankAssistantRepliesPayload( + tpye=task.type, conversation=task.conversation, replies=task.replies + ) + + case _: + raise ValueError(f"Invalid task type: {type(task)=}") + + wp = self.insert_work_package(payload=payload, id=task.id) + assert wp.id == task.id + return wp + + def insert_work_package(self, payload: db_payload.TaskPayload, id: UUID = None) -> WorkPackage: + c = PayloadContainer(payload=payload) + wp = WorkPackage( + id=id, + person_id=self.person_id, + payload_type=type(payload).__name__, + payload=c, + api_client_id=self.api_client.id, + ) + self.db.add(wp) + self.db.commit() + self.db.refresh(wp) + return wp + + def insert_post( + self, + *, + post_id: UUID, + frontend_post_id: str, + parent_id: UUID, + thread_id: UUID, + workpackage_id: UUID, + role: str, + payload: db_payload.PostPayload, + payload_type: str = None, + ) -> Post: + if payload_type is None: + if payload is None: + payload_type = "null" + else: + payload_type = type(payload).__name__ + + post = Post( + id=post_id, + parent_id=parent_id, + thread_id=thread_id, + workpackage_id=workpackage_id, + person_id=self.person_id, + role=role, + frontend_post_id=frontend_post_id, + api_client_id=self.api_client.id, + payload_type=payload_type, + payload=PayloadContainer(payload=payload), + ) + self.db.add(post) + self.db.commit() + self.db.refresh(post) + return post + + def insert_reaction(self, post_id: UUID, payload: db_payload.ReactionPayload) -> PostReaction: + if self.person_id is None: + raise ValueError("User required") + + container = PayloadContainer(payload=payload) + reaction = PostReaction( + post_id=post_id, + person_id=self.person_id, + payload=container, + api_client_id=self.api_client.id, + payload_type=type(payload).__name__, + ) + self.db.add(reaction) + self.db.commit() + self.db.refresh(reaction) + return reaction From 13551ae3e4b6bce110e46c917dfde95f6aea70d7 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sat, 17 Dec 2022 23:56:05 +0100 Subject: [PATCH 072/119] renamed to open assistant --- .github/workflows/release.yaml | 2 +- README.md | 8 +- backend/README.md | 4 +- backend/app/{ocgpt => oasst}/__init__.py | 0 backend/app/{ocgpt => oasst}/api/__init__.py | 0 .../app/{ocgpt => oasst}/api/v1/__init__.py | 0 backend/app/{ocgpt => oasst}/config.py | 2 +- backend/app/{ocgpt => oasst}/crud/__init__.py | 0 backend/app/{ocgpt => oasst}/crud/base.py | 0 .../app/{ocgpt => oasst}/models/__init__.py | 0 .../app/{ocgpt => oasst}/models/api_client.py | 0 .../models/payload_column_type.py | 0 backend/app/{ocgpt => oasst}/models/person.py | 0 .../{ocgpt => oasst}/models/person_stats.py | 0 backend/app/{ocgpt => oasst}/models/post.py | 0 .../{ocgpt => oasst}/models/post_reaction.py | 0 .../{ocgpt => oasst}/models/work_package.py | 0 .../app/{ocgpt => oasst}/schemas/__init__.py | 0 .../app/{ocgpt => oasst}/schemas/protocol.py | 0 backend/app/ocgpt/api/deps.py | 57 ---- backend/app/ocgpt/api/v1/api.py | 6 - backend/app/ocgpt/api/v1/tasks.py | 259 --------------- backend/app/ocgpt/database.py | 8 - backend/app/ocgpt/models/db_payload.py | 94 ------ backend/app/ocgpt/prompt_repository.py | 311 ------------------ bot/README.md | 4 +- bot/bot.py | 4 +- bot/setup.py | 4 +- .../frontend-development/docker-compose.yaml | 2 +- website/package.json | 2 +- website/pages/index.js | 8 +- website/public/index.html | 4 +- 32 files changed, 22 insertions(+), 757 deletions(-) rename backend/app/{ocgpt => oasst}/__init__.py (100%) rename backend/app/{ocgpt => oasst}/api/__init__.py (100%) rename backend/app/{ocgpt => oasst}/api/v1/__init__.py (100%) rename backend/app/{ocgpt => oasst}/config.py (96%) rename backend/app/{ocgpt => oasst}/crud/__init__.py (100%) rename backend/app/{ocgpt => oasst}/crud/base.py (100%) rename backend/app/{ocgpt => oasst}/models/__init__.py (100%) rename backend/app/{ocgpt => oasst}/models/api_client.py (100%) rename backend/app/{ocgpt => oasst}/models/payload_column_type.py (100%) rename backend/app/{ocgpt => oasst}/models/person.py (100%) rename backend/app/{ocgpt => oasst}/models/person_stats.py (100%) rename backend/app/{ocgpt => oasst}/models/post.py (100%) rename backend/app/{ocgpt => oasst}/models/post_reaction.py (100%) rename backend/app/{ocgpt => oasst}/models/work_package.py (100%) rename backend/app/{ocgpt => oasst}/schemas/__init__.py (100%) rename backend/app/{ocgpt => oasst}/schemas/protocol.py (100%) delete mode 100644 backend/app/ocgpt/api/deps.py delete mode 100644 backend/app/ocgpt/api/v1/api.py delete mode 100644 backend/app/ocgpt/api/v1/tasks.py delete mode 100644 backend/app/ocgpt/database.py delete mode 100644 backend/app/ocgpt/models/db_payload.py delete mode 100644 backend/app/ocgpt/prompt_repository.py diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index eb9bea65..a7dd89c8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,6 +8,6 @@ jobs: build-backend: uses: ./.github/workflows/docker-build.yaml with: - image-name: ocgpt-backend + image-name: oasst-backend folder: backend build-args: "" diff --git a/README.md b/README.md index 25104969..72a4aac6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Open-Chat-GPT +# Open-Assistant -Open chat gpt is a project meant to give everyone access to a great chat based large language model. +Open Assistant is a project meant to give everyone access to a great chat based large language model. -We believe that by doing this we will create a revolution in innovation in language. In the same way that stable-diffusion helped the world make art and images in new ways we hope open chat gpt can help improve the world by improving language itself. +We believe that by doing this we will create a revolution in innovation in language. In the same way that stable-diffusion helped the world make art and images in new ways we hope Open Assistant can help improve the world by improving language itself. ## How can you help? @@ -14,7 +14,7 @@ All open source projects begins with people like you. Open source is the belief [Join the LAION Discord Server!](https://discord.gg/RQFtmAmk) -[Visit the Notion](https://ykilcher.com/open-chat-gpt) +[Visit the Notion](https://ykilcher.com/open-assistant) ## Developer Setup diff --git a/backend/README.md b/backend/README.md index a559b15b..1e41e72c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,4 +1,4 @@ -# Open-Chat-GPT REST Backend +# Open-Assistant REST Backend ## REST Server Configuration @@ -8,7 +8,7 @@ Example contents of a `.env` file for the backend: ``` DATABASE_URI="postgresql://:@/" -BACKEND_CORS_ORIGINS=["http://localhost", "http://localhost:4200", "http://localhost:3000", "http://localhost:8080", "https://localhost", "https://localhost:4200", "https://localhost:3000", "https://localhost:8080", "http://dev.ocgpt.laion.ai", "https://stag.ocgpt.laion.ai", "https://ocgpt.laion.ai"] +BACKEND_CORS_ORIGINS=["http://localhost", "http://localhost:4200", "http://localhost:3000", "http://localhost:8080", "https://localhost", "https://localhost:4200", "https://localhost:3000", "https://localhost:8080", "http://dev.oasst.laion.ai", "https://stag.oasst.laion.ai", "https://oasst.laion.ai"] ``` diff --git a/backend/app/ocgpt/__init__.py b/backend/app/oasst/__init__.py similarity index 100% rename from backend/app/ocgpt/__init__.py rename to backend/app/oasst/__init__.py diff --git a/backend/app/ocgpt/api/__init__.py b/backend/app/oasst/api/__init__.py similarity index 100% rename from backend/app/ocgpt/api/__init__.py rename to backend/app/oasst/api/__init__.py diff --git a/backend/app/ocgpt/api/v1/__init__.py b/backend/app/oasst/api/v1/__init__.py similarity index 100% rename from backend/app/ocgpt/api/v1/__init__.py rename to backend/app/oasst/api/v1/__init__.py diff --git a/backend/app/ocgpt/config.py b/backend/app/oasst/config.py similarity index 96% rename from backend/app/ocgpt/config.py rename to backend/app/oasst/config.py index 71bade6c..0a1049f7 100644 --- a/backend/app/ocgpt/config.py +++ b/backend/app/oasst/config.py @@ -5,7 +5,7 @@ from pydantic import AnyHttpUrl, BaseSettings, PostgresDsn, validator class Settings(BaseSettings): - PROJECT_NAME: str = "open-chatGPT backend" + PROJECT_NAME: str = "open-assistant backend" API_V1_STR: str = "/api/v1" POSTGRES_HOST: str = "localhost" diff --git a/backend/app/ocgpt/crud/__init__.py b/backend/app/oasst/crud/__init__.py similarity index 100% rename from backend/app/ocgpt/crud/__init__.py rename to backend/app/oasst/crud/__init__.py diff --git a/backend/app/ocgpt/crud/base.py b/backend/app/oasst/crud/base.py similarity index 100% rename from backend/app/ocgpt/crud/base.py rename to backend/app/oasst/crud/base.py diff --git a/backend/app/ocgpt/models/__init__.py b/backend/app/oasst/models/__init__.py similarity index 100% rename from backend/app/ocgpt/models/__init__.py rename to backend/app/oasst/models/__init__.py diff --git a/backend/app/ocgpt/models/api_client.py b/backend/app/oasst/models/api_client.py similarity index 100% rename from backend/app/ocgpt/models/api_client.py rename to backend/app/oasst/models/api_client.py diff --git a/backend/app/ocgpt/models/payload_column_type.py b/backend/app/oasst/models/payload_column_type.py similarity index 100% rename from backend/app/ocgpt/models/payload_column_type.py rename to backend/app/oasst/models/payload_column_type.py diff --git a/backend/app/ocgpt/models/person.py b/backend/app/oasst/models/person.py similarity index 100% rename from backend/app/ocgpt/models/person.py rename to backend/app/oasst/models/person.py diff --git a/backend/app/ocgpt/models/person_stats.py b/backend/app/oasst/models/person_stats.py similarity index 100% rename from backend/app/ocgpt/models/person_stats.py rename to backend/app/oasst/models/person_stats.py diff --git a/backend/app/ocgpt/models/post.py b/backend/app/oasst/models/post.py similarity index 100% rename from backend/app/ocgpt/models/post.py rename to backend/app/oasst/models/post.py diff --git a/backend/app/ocgpt/models/post_reaction.py b/backend/app/oasst/models/post_reaction.py similarity index 100% rename from backend/app/ocgpt/models/post_reaction.py rename to backend/app/oasst/models/post_reaction.py diff --git a/backend/app/ocgpt/models/work_package.py b/backend/app/oasst/models/work_package.py similarity index 100% rename from backend/app/ocgpt/models/work_package.py rename to backend/app/oasst/models/work_package.py diff --git a/backend/app/ocgpt/schemas/__init__.py b/backend/app/oasst/schemas/__init__.py similarity index 100% rename from backend/app/ocgpt/schemas/__init__.py rename to backend/app/oasst/schemas/__init__.py diff --git a/backend/app/ocgpt/schemas/protocol.py b/backend/app/oasst/schemas/protocol.py similarity index 100% rename from backend/app/ocgpt/schemas/protocol.py rename to backend/app/oasst/schemas/protocol.py diff --git a/backend/app/ocgpt/api/deps.py b/backend/app/ocgpt/api/deps.py deleted file mode 100644 index 88c054ae..00000000 --- a/backend/app/ocgpt/api/deps.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -from secrets import token_hex -from typing import Generator -from uuid import UUID - -from fastapi import HTTPException, Security -from fastapi.security.api_key import APIKey, APIKeyHeader, APIKeyQuery -from loguru import logger -from ocgpt.config import settings -from ocgpt.database import engine -from ocgpt.models import ApiClient -from sqlmodel import Session -from starlette.status import HTTP_403_FORBIDDEN - - -def get_db() -> Generator: - with Session(engine) as db: - yield db - - -api_key_query = APIKeyQuery(name="api_key", auto_error=False) -api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False) - - -async def get_api_key( - api_key_query: str = Security(api_key_query), - api_key_header: str = Security(api_key_header), -): - if api_key_query: - return api_key_query - else: - return api_key_header - - -def api_auth( - api_key: APIKey, - db: Session, -) -> ApiClient: - - if api_key is not None: - if settings.ALLOW_ANY_API_KEY: - # make sure that a dummy api key exits in db (foreign key references) - ANY_API_KEY_ID = UUID("00000000-1111-2222-3333-444444444444") - api_client: ApiClient = db.query(ApiClient).filter(ApiClient.id == ANY_API_KEY_ID).first() - if api_client is None: - token = token_hex(32) - logger.info(f"ANY_API_KEY missing, inserting api_key: {token}") - api_client = ApiClient(id=ANY_API_KEY_ID, api_key=token, description="ANY_API_KEY, random token") - db.add(api_client) - db.commit() - return api_client - - api_client = db.query(ApiClient).filter(ApiClient.api_key == api_key).first() - if api_client is not None and api_client.enabled: - return api_client - - raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials") diff --git a/backend/app/ocgpt/api/v1/api.py b/backend/app/ocgpt/api/v1/api.py deleted file mode 100644 index 1e304656..00000000 --- a/backend/app/ocgpt/api/v1/api.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -from fastapi import APIRouter -from ocgpt.api.v1 import tasks - -api_router = APIRouter() -api_router.include_router(tasks.router, prefix="/tasks", tags=["tasks"]) diff --git a/backend/app/ocgpt/api/v1/tasks.py b/backend/app/ocgpt/api/v1/tasks.py deleted file mode 100644 index ef12276a..00000000 --- a/backend/app/ocgpt/api/v1/tasks.py +++ /dev/null @@ -1,259 +0,0 @@ -# -*- coding: utf-8 -*- -import random -from typing import Any -from uuid import UUID - -from fastapi import APIRouter, Depends, HTTPException -from fastapi.security.api_key import APIKey -from loguru import logger -from ocgpt.api import deps -from ocgpt.models.db_payload import TaskPayload -from ocgpt.prompt_repository import PromptRepository -from ocgpt.schemas import protocol as protocol_schema -from sqlmodel import Session -from starlette.status import HTTP_400_BAD_REQUEST - -router = APIRouter() - - -def generate_task(request: protocol_schema.TaskRequest) -> protocol_schema.Task: - match (request.type): - case protocol_schema.TaskRequestType.random: - logger.info("Frontend requested a random task.") - while request.type == protocol_schema.TaskRequestType.random: - request.type = random.choice(list(protocol_schema.TaskRequestType)).value - return generate_task(request) - case protocol_schema.TaskRequestType.summarize_story: - logger.info("Generating a SummarizeStoryTask.") - task = protocol_schema.SummarizeStoryTask( - story="This is a story. A very long story. So long, it needs to be summarized.", - ) - case protocol_schema.TaskRequestType.rate_summary: - logger.info("Generating a RateSummaryTask.") - task = protocol_schema.RateSummaryTask( - full_text="This is a story. A very long story. So long, it needs to be summarized.", - summary="This is a summary.", - scale=protocol_schema.RatingScale(min=1, max=5), - ) - case protocol_schema.TaskRequestType.initial_prompt: - logger.info("Generating an InitialPromptTask.") - task = protocol_schema.InitialPromptTask( - hint="Ask the assistant about a current event." # this is optional - ) - case protocol_schema.TaskRequestType.user_reply: - logger.info("Generating a UserReplyTask.") - task = protocol_schema.UserReplyTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, what's going on in the world?", - is_assistant=False, - ), - protocol_schema.ConversationMessage( - text="I'm not sure I understood correctly, could you rephrase that?", - is_assistant=True, - ), - ], - ) - ) - case protocol_schema.TaskRequestType.assistant_reply: - logger.info("Generating a AssistantReplyTask.") - task = protocol_schema.AssistantReplyTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, write me an English essay about water.", - is_assistant=False, - ), - ], - ) - ) - case protocol_schema.TaskRequestType.rank_initial_prompts: - logger.info("Generating a RankInitialPromptsTask.") - task = protocol_schema.RankInitialPromptsTask( - prompts=[ - "Please write a story about a time you were happy.", - "Please write a story about a time you were sad.", - ] - ) - case protocol_schema.TaskRequestType.rank_user_replies: - logger.info("Generating a RankUserRepliesTask.") - task = protocol_schema.RankUserRepliesTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, what's going on in the world?", - is_assistant=False, - ), - protocol_schema.ConversationMessage( - text="I'm not sure I understood correctly, could you rephrase that?", - is_assistant=True, - ), - ], - ), - replies=[ - "Oh come oooooon!", - "What are the news?", - ], - ) - - case protocol_schema.TaskRequestType.rank_assistant_replies: - logger.info("Generating a RankAssistantRepliesTask.") - task = protocol_schema.RankAssistantRepliesTask( - conversation=protocol_schema.Conversation( - messages=[ - protocol_schema.ConversationMessage( - text="Hey, assistant, what's going on in the world?", - is_assistant=False, - ), - ], - ), - replies=[ - "I'm not sure I understood correctly, could you rephrase that?", - "The world is fine. All good.", - "Crap is hitting the fan. Start farming.", - ], - ) - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid request type.", - ) - - logger.info(f"Generated {task=}.") - - return task - - -@router.post("/", response_model=protocol_schema.AnyTask) # work with Union once more types are added -def request_task( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - request: protocol_schema.TaskRequest, -) -> Any: - """ - Create new task. - """ - api_client = deps.api_auth(api_key, db) - - try: - task = generate_task(request) - - pr = PromptRepository(db, api_client, request.user) - pr.store_task(task) - - except Exception: - logger.exception("Failed to generate task.") - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - ) - return task - - -@router.post("/{task_id}/ack") -def acknowledge_task( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - task_id: UUID, - ack_request: protocol_schema.TaskAck, -) -> Any: - """ - The frontend acknowledges a task. - """ - - api_client = deps.api_auth(api_key, db) - - try: - pr = PromptRepository(db, api_client, user=None) - - # here we store the post id in the database for the task - pr.bind_frontend_post_id(task_id=task_id, post_id=ack_request.post_id) - logger.info(f"Frontend acknowledges task {task_id=}, {ack_request=}.") - - except Exception: - logger.exception("Failed to acknowledge task.") - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - ) - return {} - - -@router.post("/{task_id}/nack") -def acknowledge_task_failure( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - task_id: UUID, - nack_request: protocol_schema.TaskNAck, -) -> Any: - """ - The frontend reports failure to implement a task. - """ - deps.api_auth(api_key, db) - - logger.info(f"Frontend reports failure to implement task {task_id=}, {nack_request=}.") - # here we would store the post id in the database for the task - return {} - - -@router.post("/interaction") -def post_interaction( - *, - db: Session = Depends(deps.get_db), - api_key: APIKey = Depends(deps.get_api_key), - interaction: protocol_schema.AnyInteraction, -) -> Any: - """ - The frontend reports an interaction. - """ - api_client = deps.api_auth(api_key, db) - - try: - pr = PromptRepository(db, api_client, user=interaction.user) - - match type(interaction): - case protocol_schema.TextReplyToPost: - logger.info( - f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}." - ) - - work_package = pr.fetch_workpackage_by_postid(interaction.post_id) - work_payload: TaskPayload = work_package.payload.payload - logger.info(f"found task work package in db: {work_payload}") - - # here we store the text reply in the database - # ToDo: role user or agent? - pr.store_text_reply(interaction, role="unknown") - - return protocol_schema.TaskDone() - case protocol_schema.PostRating: - logger.info( - f"Frontend reports rating of {interaction.post_id=} with {interaction.rating=} by {interaction.user=}." - ) - - # here we store the rating in the database - pr.store_rating(interaction) - - return protocol_schema.TaskDone() - case protocol_schema.PostRanking: - logger.info( - f"Frontend reports ranking of {interaction.post_id=} with {interaction.ranking=} by {interaction.user=}." - ) - - # TODO: check if the ranking is valid - pr.store_ranking(interaction) - # here we would store the ranking in the database - return protocol_schema.TaskDone() - case _: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail="Invalid response type.", - ) - - except Exception: - logger.exception("Interaction request failed.") - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - ) diff --git a/backend/app/ocgpt/database.py b/backend/app/ocgpt/database.py deleted file mode 100644 index 96ada5d6..00000000 --- a/backend/app/ocgpt/database.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- -from ocgpt.config import settings -from sqlmodel import create_engine - -if settings.DATABASE_URI is None: - raise ValueError("DATABASE_URI is not set") - -engine = create_engine(settings.DATABASE_URI) diff --git a/backend/app/ocgpt/models/db_payload.py b/backend/app/ocgpt/models/db_payload.py deleted file mode 100644 index 730178e3..00000000 --- a/backend/app/ocgpt/models/db_payload.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Literal - -from ocgpt.models.payload_column_type import payload_type -from ocgpt.schemas import protocol as protocol_schema -from pydantic import BaseModel - - -@payload_type -class TaskPayload(BaseModel): - type: str - - -@payload_type -class SummarizationStoryPayload(TaskPayload): - type: Literal["summarize_story"] = "summarize_story" - story: str - - -@payload_type -class RateSummaryPayload(TaskPayload): - type: Literal["rate_summary"] = "rate_summary" - full_text: str - summary: str - scale: protocol_schema.RatingScale - - -@payload_type -class InitialPromptPayload(TaskPayload): - type: Literal["initial_prompt"] = "initial_prompt" - hint: str - - -@payload_type -class UserReplyPayload(TaskPayload): - type: Literal["user_reply"] = "user_reply" - conversation: protocol_schema.Conversation - hint: str | None - - -@payload_type -class AssistantReplyPayload(TaskPayload): - type: Literal["assistant_reply"] = "assistant_reply" - conversation: protocol_schema.Conversation - - -@payload_type -class PostPayload(BaseModel): - text: str - - -@payload_type -class ReactionPayload(BaseModel): - type: str - - -@payload_type -class RatingReactionPayload(ReactionPayload): - type: Literal["post_rating"] = "post_rating" - rating: str - - -@payload_type -class RankingReactionPayload(ReactionPayload): - type: Literal["post_ranking"] = "post_ranking" - ranking: list[int] - - -@payload_type -class RankConversationRepliesPayload(TaskPayload): - conversation: protocol_schema.Conversation # the conversation so far - replies: list[str] - - -@payload_type -class RankInitialPromptsPayload(TaskPayload): - """A task to rank a set of initial prompts.""" - - type: Literal["rank_initial_prompts"] = "rank_initial_prompts" - prompts: list[str] - - -@payload_type -class RankUserRepliesPayload(RankConversationRepliesPayload): - """A task to rank a set of user replies to a conversation.""" - - type: Literal["rank_user_replies"] = "rank_user_replies" - - -@payload_type -class RankAssistantRepliesPayload(RankConversationRepliesPayload): - """A task to rank a set of assistant replies to a conversation.""" - - type: Literal["rank_assistant_replies"] = "rank_assistant_replies" diff --git a/backend/app/ocgpt/prompt_repository.py b/backend/app/ocgpt/prompt_repository.py deleted file mode 100644 index 8c621df1..00000000 --- a/backend/app/ocgpt/prompt_repository.py +++ /dev/null @@ -1,311 +0,0 @@ -# -*- coding: utf-8 -*- -from datetime import datetime -from typing import Optional -from uuid import UUID, uuid4 - -import ocgpt.models.db_payload as db_payload -from loguru import logger -from ocgpt.models import ApiClient, Person, Post, PostReaction, WorkPackage -from ocgpt.models.payload_column_type import PayloadContainer -from ocgpt.schemas import protocol as protocol_schema -from sqlmodel import Session - - -class PromptRepository: - def __init__(self, db: Session, api_client: ApiClient, user: Optional[protocol_schema.User]): - self.db = db - self.api_client = api_client - self.person = self.lookup_person(user) - self.person_id = self.person.id if self.person else None - - def lookup_person(self, user: protocol_schema.User) -> Person: - if not user: - return None - person: Person = ( - self.db.query(Person) - .filter( - Person.api_client_id == self.api_client.id, - Person.username == user.id, - Person.auth_method == user.auth_method, - ) - .first() - ) - if person is None: - # user is unknown, create new record - person = Person(username=user.id, display_name=user.display_name, api_client_id=self.api_client.id) - self.db.add(person) - self.db.commit() - self.db.refresh(person) - elif user.display_name and user.display_name != person.display_name: - # we found the user but the display name changed - person.display_name = user.display_name - self.db.add(person) - self.db.commit() - return person - - def validate_post_id(self, post_id: str) -> None: - if not isinstance(post_id, str): - raise TypeError(f"post_id must be string, not {type(post_id)}") - if not post_id: - raise ValueError("post_id must not be empty") - - def bind_frontend_post_id(self, task_id: UUID, post_id: str): - self.validate_post_id(post_id) - - # find work package - work_pack: WorkPackage = ( - self.db.query(WorkPackage) - .filter(WorkPackage.id == task_id, WorkPackage.api_client_id == self.api_client.id) - .first() - ) - if work_pack is None: - raise KeyError(f"WorkPackage for task {task_id} not found") - if work_pack.expiry_date is not None and datetime.utcnow() > work_pack.expiry_date: - raise RuntimeError("WorkPackage already expired.") - - # ToDo: check race-condition, transaction - - # check if task thread exits - thread_root = ( - self.db.query(Post) - .filter( - Post.workpackage_id == work_pack.id, - Post.frontend_post_id == post_id, - Post.parent_id is None, - Post.api_client_id == self.api_client.id, - ) - .one_or_none() - ) - if thread_root is None: - thread_id = uuid4() - thread_root = self.insert_post( - post_id=thread_id, - thread_id=thread_id, - frontend_post_id=post_id, - parent_id=None, - role="system", - workpackage_id=work_pack.id, - payload=None, - payload_type="bind", - ) - return thread_root - - def fetch_post_by_frontend_post_id(self, frontend_post_id: str, fail_if_missing: bool = True) -> Post: - self.validate_post_id(frontend_post_id) - post: Post = ( - self.db.query(Post) - .filter(Post.api_client_id == self.api_client.id, Post.frontend_post_id == frontend_post_id) - .one_or_none() - ) - if fail_if_missing and post is None: - raise KeyError(f"Post with post_id {frontend_post_id} not found.") - return post - - def fetch_workpackage_by_postid(self, post_id: str) -> WorkPackage: - self.validate_post_id(post_id) - post = self.fetch_post_by_frontend_post_id(post_id, fail_if_missing=True) - work_pack = self.db.query(WorkPackage).filter(WorkPackage.id == post.workpackage_id).one() - return work_pack - - def store_text_reply(self, reply: protocol_schema.TextReplyToPost, role: str) -> Post: - self.validate_post_id(reply.post_id) - self.validate_post_id(reply.user_post_id) - - # find post with post-id - parent_post: Post = ( - self.db.query(Post) - .filter( - Post.api_client_id == self.api_client.id, - Post.frontend_post_id == reply.post_id, - # Post.person_id == self.person_id - ) - .one_or_none() - ) - - if parent_post is None: - raise KeyError(f"Post for post_id {reply.post_id} not found.") - - # create reply post - user_post_id = uuid4() - user_post = self.insert_post( - post_id=user_post_id, - frontend_post_id=reply.user_post_id, - parent_id=parent_post.id, - thread_id=parent_post.thread_id, - workpackage_id=parent_post.workpackage_id, - role=role, - payload=db_payload.PostPayload(text=reply.text), - ) - return user_post - - def store_rating(self, rating: protocol_schema.PostRating) -> PostReaction: - post = self.fetch_post_by_frontend_post_id(rating.post_id, fail_if_missing=True) - - work_package = self.fetch_workpackage_by_postid(rating.post_id) - work_payload: db_payload.RateSummaryPayload = work_package.payload.payload - if type(work_payload) != db_payload.RateSummaryPayload: - raise ValueError( - f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RateSummaryPayload}" - ) - - if rating.rating < work_payload.scale.min or rating.rating > work_payload.scale.max: - raise ValueError(f"Invalid rating value: {rating.rating=} not in {work_payload.scale=}") - - # store reaction to post - reaction_payload = db_payload.RatingReactionPayload(rating=rating.rating) - reaction = self.insert_reaction(post.id, reaction_payload) - logger.info(f"Ranking {rating.rating} stored for work_package {work_package.id}.") - return reaction - - def store_ranking(self, ranking: protocol_schema.PostRanking) -> PostReaction: - post = self.fetch_post_by_frontend_post_id(ranking.post_id, fail_if_missing=True) - - # fetch work_package - work_package = self.fetch_workpackage_by_postid(ranking.post_id) - work_payload: db_payload.RankConversationRepliesPayload | db_payload.RankInitialPromptsPayload = ( - work_package.payload.payload - ) - - match type(work_payload): - - case db_payload.RankUserRepliesPayload | db_payload.RankAssistantRepliesPayload: - # validate ranking - num_replies = len(work_payload.replies) - if sorted(ranking.ranking) != list(range(num_replies)): - raise ValueError( - f"Invalid ranking submitted. Each reply index must appear exactly once ({num_replies=})." - ) - - # store reaction to post - reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) - reaction = self.insert_reaction(post.id, reaction_payload) - - logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") - - return reaction - - case db_payload.RankInitialPromptsPayload: - # validate ranking - if sorted(ranking.ranking) != list(range(num_prompts := len(work_payload.prompts))): - raise ValueError( - f"Invalid ranking submitted. Each reply index must appear exactly once ({num_prompts=})." - ) - - # store reaction to post - reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking) - reaction = self.insert_reaction(post.id, reaction_payload) - - logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.") - - return reaction - - case _: - raise ValueError( - f"work_package payload type mismatch: {type(work_payload)=} != {db_payload.RankConversationRepliesPayload}" - ) - - def store_task(self, task: protocol_schema.Task) -> WorkPackage: - payload: db_payload.TaskPayload - match type(task): - case protocol_schema.SummarizeStoryTask: - payload = db_payload.SummarizationStoryPayload(story=task.story) - - case protocol_schema.RateSummaryTask: - payload = db_payload.RateSummaryPayload( - full_text=task.full_text, summary=task.summary, scale=task.scale - ) - - case protocol_schema.InitialPromptTask: - payload = db_payload.InitialPromptPayload(hint=task.hint) - - case protocol_schema.UserReplyTask: - payload = db_payload.UserReplyPayload(conversation=task.conversation, hint=task.hint) - - case protocol_schema.AssistantReplyTask: - payload = db_payload.AssistantReplyPayload(type=task.type, conversation=task.conversation) - - case protocol_schema.RankInitialPromptsTask: - payload = db_payload.RankInitialPromptsPayload(tpye=task.type, prompts=task.prompts) - - case protocol_schema.RankUserRepliesTask: - payload = db_payload.RankUserRepliesPayload( - tpye=task.type, conversation=task.conversation, replies=task.replies - ) - - case protocol_schema.RankAssistantRepliesTask: - payload = db_payload.RankAssistantRepliesPayload( - tpye=task.type, conversation=task.conversation, replies=task.replies - ) - - case _: - raise ValueError(f"Invalid task type: {type(task)=}") - - wp = self.insert_work_package(payload=payload, id=task.id) - assert wp.id == task.id - return wp - - def insert_work_package(self, payload: db_payload.TaskPayload, id: UUID = None) -> WorkPackage: - c = PayloadContainer(payload=payload) - wp = WorkPackage( - id=id, - person_id=self.person_id, - payload_type=type(payload).__name__, - payload=c, - api_client_id=self.api_client.id, - ) - self.db.add(wp) - self.db.commit() - self.db.refresh(wp) - return wp - - def insert_post( - self, - *, - post_id: UUID, - frontend_post_id: str, - parent_id: UUID, - thread_id: UUID, - workpackage_id: UUID, - role: str, - payload: db_payload.PostPayload, - payload_type: str = None, - ) -> Post: - if payload_type is None: - if payload is None: - payload_type = "null" - else: - payload_type = type(payload).__name__ - - post = Post( - id=post_id, - parent_id=parent_id, - thread_id=thread_id, - workpackage_id=workpackage_id, - person_id=self.person_id, - role=role, - frontend_post_id=frontend_post_id, - api_client_id=self.api_client.id, - payload_type=payload_type, - payload=PayloadContainer(payload=payload), - ) - self.db.add(post) - self.db.commit() - self.db.refresh(post) - return post - - def insert_reaction(self, post_id: UUID, payload: db_payload.ReactionPayload) -> PostReaction: - if self.person_id is None: - raise ValueError("User required") - - container = PayloadContainer(payload=payload) - reaction = PostReaction( - post_id=post_id, - person_id=self.person_id, - payload=container, - api_client_id=self.api_client.id, - payload_type=type(payload).__name__, - ) - self.db.add(reaction) - self.db.commit() - self.db.refresh(reaction) - return reaction diff --git a/bot/README.md b/bot/README.md index 935cfb6c..fcc3bc33 100644 --- a/bot/README.md +++ b/bot/README.md @@ -1,6 +1,6 @@ -# open-chat-gpt +# open-assistant -This is the github repo for the open-chat-gpt project. +This is the github repo for the open-assistant project. We are currently building a discord bot in order to make everyone contribute with great prompts and answers. Join us! https://discord.gg/ZUfPw6jP diff --git a/bot/bot.py b/bot/bot.py index aff74b26..c2da5100 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -33,7 +33,7 @@ guild_ids = [TEST_GUILD, TEST_GUILD_LAION] # Initiate the client and command tree to create slash commands. -class OpenChatGPTClient(discord.Client): +class OpenAssistantClient(discord.Client): def __init__(self, *, intents: discord.Intents): super().__init__(intents=intents) self.tree = app_commands.CommandTree(self) @@ -59,7 +59,7 @@ class OpenChatGPTClient(discord.Client): # List the set of intents needed for commands to operate properly. intents = discord.Intents.default() intents.message_content = True -client = OpenChatGPTClient(intents=intents) +client = OpenAssistantClient(intents=intents) class LikeButton(discord.ui.Button): diff --git a/bot/setup.py b/bot/setup.py index 25df4d9a..06f7326e 100644 --- a/bot/setup.py +++ b/bot/setup.py @@ -12,11 +12,11 @@ if __name__ == "__main__": REQUIREMENTS = _read_reqs("requirements.txt") setup( - name="open-chat-gpt", + name="open-assistant", packages=find_packages(), version="0.0.1", license="Apache 2.0", - description="A Discord Bot for collecting and ranking prompts to train an Open ChatGPT", + description="A Discord Bot for collecting and ranking prompts to train an Open Assistant", keywords=["machine learning", "natural language processing", "discord"], install_requires=REQUIREMENTS, classifiers=[ diff --git a/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml index 8597726d..ad48b33a 100644 --- a/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -16,7 +16,7 @@ services: service: adminer backend: build: ../../backend/. - image: ocgpt-backend + image: oasst-backend environment: - POSTGRES_HOST=db - ALLOW_ANY_API_KEY=True diff --git a/website/package.json b/website/package.json index aa06bb05..dd4a6bb3 100644 --- a/website/package.json +++ b/website/package.json @@ -1,6 +1,6 @@ { "name": "website", - "homepage": "http://projects.laion.ai.github.io/Open-Chat-GPT", + "homepage": "http://projects.laion.ai.github.io/Open-Assistant", "version": "0.1.0", "private": true, "scripts": { diff --git a/website/pages/index.js b/website/pages/index.js index d150d6c7..022ba1dc 100644 --- a/website/pages/index.js +++ b/website/pages/index.js @@ -20,9 +20,9 @@ export default function Home() {
{/* logo */} -

Open Chat Gpt

+

Open Assistant

- Open chat gpt is a project meant to give everyone access to a great + Open Assistant is a project meant to give everyone access to a great chat based large language model.

@@ -30,7 +30,7 @@ export default function Home() {

We believe that by doing this we will create a revolution in innovation in language. In the same way that stable-diffusion helped - the world make art and images in new ways we hope open chat gpt can + the world make art and images in new ways we hope Open Assistant can help improve the world by improving language itself.

@@ -62,7 +62,7 @@ export default function Home() {
{/* logo */} -

Open Chat Gpt

+

Open Assistant

You are logged in

diff --git a/website/public/index.html b/website/public/index.html index bc6ba7d3..e9d7030d 100644 --- a/website/public/index.html +++ b/website/public/index.html @@ -5,7 +5,7 @@ - + - Open Chat GPT + Open Assistant From 6b21b2359c15af3eea74d47496514c3404725d67 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sun, 18 Dec 2022 00:05:18 +0100 Subject: [PATCH 073/119] workflow fix --- .github/workflows/docker-build.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index 2a6f3bbf..5697d5b0 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -17,6 +17,9 @@ jobs: build: name: Build Images runs-on: ubuntu-latest + permissions: + contents: read + packages: write steps: - uses: actions/checkout@v3 - name: Set up Docker Buildx From 088effc7a9add380b3fd12f3a7f8fcb5e651d924 Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sun, 18 Dec 2022 00:19:50 +0100 Subject: [PATCH 074/119] readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72a4aac6..57899a61 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ All open source projects begins with people like you. Open source is the belief Work is organized in the [project board](https://github.com/orgs/LAION-AI/projects/3). - To get started with development, if you want to work on the backend, have a look at `scripts/backend-development/README.md`. -- If you want to work on the frontend, have a look at `scripts/frontend-development/README.md`. +- If you want to work on any frontend, have a look at `scripts/frontend-development/README.md` to make a backend available. There is also a minimal implementation of a frontend in the `text-frontend` folder. From a53d69b68260edb53365d54283d78ee07afc2863 Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Sun, 18 Dec 2022 15:43:19 +0900 Subject: [PATCH 075/119] Deleting some unused API methods, storing interactions properly to the Task Backend, Adding explanatory comments, using a task queue in the client side --- website/pages/api/hello.js | 5 --- website/pages/api/new_task.js | 14 +++++- website/pages/api/prompts.js | 28 ------------ website/pages/api/update_task.js | 34 +++++++++++++-- website/pages/new_task.js | 74 +++++++++++++++++++++++--------- 5 files changed, 97 insertions(+), 58 deletions(-) delete mode 100644 website/pages/api/hello.js delete mode 100644 website/pages/api/prompts.js diff --git a/website/pages/api/hello.js b/website/pages/api/hello.js deleted file mode 100644 index aee21e9a..00000000 --- a/website/pages/api/hello.js +++ /dev/null @@ -1,5 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction - -export default function handler(req, res) { - res.status(200).json({ name: "John Doe" }); -} diff --git a/website/pages/api/new_task.js b/website/pages/api/new_task.js index d9bd5012..790c3f23 100644 --- a/website/pages/api/new_task.js +++ b/website/pages/api/new_task.js @@ -2,16 +2,25 @@ import { unstable_getServerSession } from "next-auth/next"; import { authOptions } from "./auth/[...nextauth]"; /** - * Returns a list of prompts from the Labeler Backend. + * Returns a new task created from the Task Backend. We do a few things here: + * + * 1) Get the task from the backend and register the requesting user. + * 2) Store the task in our local database. + * 3) Send and Ack to the Task Backend with our local id for the task. + * 4) Return everything to the client. */ export default async (req, res) => { const session = await unstable_getServerSession(req, res, authOptions); + // Return nothing if the user isn't registered. if (!session) { res.status(401).end(); return; } + // Fetch the new task. + // + // This needs to be refactored into an easier to use library. const taskRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/`, { method: "POST", headers: { @@ -29,6 +38,7 @@ export default async (req, res) => { }); const task = await taskRes.json(); + // Store the task and link it to the user.. const registeredTask = await prisma.registeredTask.create({ data: { task, @@ -40,6 +50,7 @@ export default async (req, res) => { }, }); + // Update the backend with our Task ID const ackRes = await fetch( `${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, { @@ -55,5 +66,6 @@ export default async (req, res) => { ); const ack = await ackRes.json(); + // Send the results to the client. res.status(200).json(registeredTask); }; diff --git a/website/pages/api/prompts.js b/website/pages/api/prompts.js deleted file mode 100644 index 9d09641c..00000000 --- a/website/pages/api/prompts.js +++ /dev/null @@ -1,28 +0,0 @@ -import { unstable_getServerSession } from "next-auth/next"; -import { authOptions } from "./auth/[...nextauth]"; - -/** - * Returns a list of prompts from the Labeler Backend. - */ -export default async (req, res) => { - const session = await unstable_getServerSession(req, res, authOptions); - - if (!session) { - res.status(401).end(); - return; - } - try { - const promptRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/prompts`, { - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - }, - }); - const prompts = await promptRes.json(); - - res.status(200).json(prompts); - } catch (error) { - console.error(error); - res.status(500); - } - res.end(); -}; diff --git a/website/pages/api/update_task.js b/website/pages/api/update_task.js index f68cd709..45b1f72e 100644 --- a/website/pages/api/update_task.js +++ b/website/pages/api/update_task.js @@ -2,18 +2,28 @@ import { unstable_getServerSession } from "next-auth/next"; import { authOptions } from "./auth/[...nextauth]"; /** - * Returns a list of prompts from the Labeler Backend. + * Stores the task interaction with the Task Backend and then returns the next task generated. + * + * This implicity does a few things: + * 1) Stores the answer with the Task Backend. + * 2) Records the new task in our local database. + * 3) (TODO) Acks the new task with our local task ID to the Task Backend. + * 4) Returns the newly created task to the client. */ export default async (req, res) => { const session = await unstable_getServerSession(req, res, authOptions); + // Return nothing if the user isn't registered. if (!session) { res.status(401).end(); return; } + // Parse out the local task ID and the interaction contents. const { id, content } = await JSON.parse(req.body); + // Log the interaction locally to create our user_post_id needed by the Task + // Backend. const interaction = await prisma.taskInteraction.create({ data: { content, @@ -25,6 +35,8 @@ export default async (req, res) => { }, }); + // Send the interaction to the Task Backend. This automatically fetches the + // next task in the sequence (or the done task). const interactionRes = await fetch( `${process.env.FASTAPI_URL}/api/v1/tasks/interaction`, { @@ -46,9 +58,23 @@ export default async (req, res) => { }), } ); - console.log(interactionRes.status); const newTask = await interactionRes.json(); - console.log(newTask); - res.status(200).json(newTask); + // Stores the new task with our database. + const newRegisteredTask = await prisma.registeredTask.create({ + data: { + task: newTask, + user: { + connect: { + id: session.user.id, + }, + }, + }, + }); + + // TODO: Ack the task with the Task Backend using the newly created local + // task ID. + + // Send the next task in the sequence to the client. + res.status(200).json(newRegisteredTask); }; diff --git a/website/pages/new_task.js b/website/pages/new_task.js index 5308323f..77577662 100644 --- a/website/pages/new_task.js +++ b/website/pages/new_task.js @@ -8,6 +8,10 @@ import useSWRMutation from "swr/mutation"; const fetcher = (url) => axios.get(url).then((res) => res.data); +/** + * A helper function to post updates to tasks. + * This ensures the content sent is serialized to JSON. + */ async function sendRequest(url, { arg }) { return fetch(url, { method: "POST", @@ -16,49 +20,79 @@ async function sendRequest(url, { arg }) { } export default function NewPage() { - const [done, setDone] = useState(false); + // Use an array of tasks that record the sequence of steps until a task is + // deemed complete. + const [tasks, setTasks] = useState([]); + + // A quick reference to the input element. This should be factored into the + // component doing the actual task rendering. const responseEl = useRef(null); - const { - data: registeredTask, - errors, - isLoading, - } = useSWRImmutable("/api/new_task", fetcher); + + // Fetch the very fist task. We can ignore everything except isLoading + // because the onSuccess handler will update `tasks` when ready. + const { isLoading } = useSWRImmutable("/api/new_task", fetcher, { + onSuccess: (data) => { + setTasks([data]); + }, + }); + + // Every time we submit an answer to the latest task, let the backend handle + // all the interactions then add the resulting task to the queue. This ends + // when we hit the done task. const { trigger, isMutating } = useSWRMutation( "/api/update_task", sendRequest, { onSuccess: async (data) => { const newTask = await data.json(); - console.log(newTask); - setDone(true); + // This is the more efficient way to update a react state array. + setTasks((oldTasks) => [...oldTasks, newTask]); }, } ); - const submitResponse = () => { + // Trigger a mutation that updates the current task. We should probably + // signal somewhere that this interaction is being processed. + const submitResponse = (t) => { trigger({ - id: registeredTask.id, + id: t.id, content: { rating: responseEl.current.value, }, }); }; + + // Show something informative while loading the first task. if (isLoading) { return
Loading
; } + // Iterate through each of the tasks and show it's contents, get a response to it, or show the done state. + // + // Right now this just works for the rating task. + // + // Displaying and fetching results for each task type should be factored into + // different components that handle the presentation and response structures. + // The results should be packaged into a single object with all the fields + // sent to the backend. return (
-
{registeredTask.id}
-
{registeredTask.task.type}
-
{registeredTask.task.text}
-
{registeredTask.task.summary}
-
- {registeredTask.task.scale.min} to {registeredTask.task.scale.max} -
- - - {done &&
Done!
} + {tasks.map((t) => ( +
+
{t.task.type}
+
{t.task.text}
+ {t.task.summary && ( + <> +
{t.task.summary}
+
+ {t.task.scale.min} to {t.task.scale.max} +
+ + + + )} +
+ ))}
); } From 02ec176670cd9fdfc989f31cf5093703e9691d25 Mon Sep 17 00:00:00 2001 From: Keith Stevens Date: Sun, 18 Dec 2022 17:38:59 +0900 Subject: [PATCH 076/119] Adding a jsconfig to ensure all javascript imports can be absolute instead of relative --- website/jsconfig.json | 5 +++++ website/pages/api/new_task.js | 2 +- website/pages/index.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 website/jsconfig.json diff --git a/website/jsconfig.json b/website/jsconfig.json new file mode 100644 index 00000000..36aa1a4d --- /dev/null +++ b/website/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/website/pages/api/new_task.js b/website/pages/api/new_task.js index 790c3f23..6743b33a 100644 --- a/website/pages/api/new_task.js +++ b/website/pages/api/new_task.js @@ -1,5 +1,5 @@ import { unstable_getServerSession } from "next-auth/next"; -import { authOptions } from "./auth/[...nextauth]"; +import { authOptions } from "pages/api/auth/[...nextauth]"; /** * Returns a new task created from the Task Backend. We do a few things here: diff --git a/website/pages/index.js b/website/pages/index.js index f0cb441a..9df611b9 100644 --- a/website/pages/index.js +++ b/website/pages/index.js @@ -5,7 +5,7 @@ import { useSession, signIn, signOut } from "next-auth/react"; import { useEffect, useState } from "react"; import useSWR from "swr"; -import styles from "../styles/Home.module.css"; +import styles from "styles/Home.module.css"; const fetcher = (url) => axios.get(url).then((res) => res.data); From 6f3de4a624d0b7962bfcc81c21edbf19d4a9872a Mon Sep 17 00:00:00 2001 From: Yannic Kilcher Date: Sun, 18 Dec 2022 11:34:02 +0100 Subject: [PATCH 077/119] set MAX_WORKERS to 1 in gunicorn container for development --- scripts/frontend-development/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/frontend-development/docker-compose.yaml b/scripts/frontend-development/docker-compose.yaml index ad48b33a..1127d35d 100644 --- a/scripts/frontend-development/docker-compose.yaml +++ b/scripts/frontend-development/docker-compose.yaml @@ -20,6 +20,7 @@ services: environment: - POSTGRES_HOST=db - ALLOW_ANY_API_KEY=True + - MAX_WORKERS=1 depends_on: db: condition: service_healthy From a296f6ea99d0404d4381f0b59130ed5ed25cdd76 Mon Sep 17 00:00:00 2001 From: Lennon Puldagrealy Date: Sun, 18 Dec 2022 10:28:43 -0800 Subject: [PATCH 078/119] Grading Page WIP & Homepage --- website/jsconfig.json | 5 +- website/next.config.js | 7 +- website/package-lock.json | 1361 ++++++++++++++++- website/package.json | 14 +- website/pages/_app.js | 14 - website/pages/api/auth/[...nextauth].js | 41 - website/pages/api/new_task.js | 71 - website/pages/api/update_task.js | 80 - website/pages/index.js | 70 - website/postcss.config.js | 6 + website/public/favicon.ico | Bin 25931 -> 0 bytes website/public/fonts/Inter-italic.var.woff2 | Bin 0 -> 245036 bytes website/public/fonts/Inter-roman.var.woff2 | Bin 0 -> 227180 bytes website/public/fonts/lexend.txt | 93 ++ website/public/fonts/lexend.woff2 | Bin 0 -> 71760 bytes .../images/logos/CHAT-THOUGHT-CONVO.svg | 48 + .../public/images/logos/CHAT-THOUGHT-LOGO.svg | 13 + website/public/images/logos/GPTBRAIN-chat.svg | 44 + website/public/images/logos/GPTBRAIN-grn.svg | 44 + website/public/images/logos/GPTBRAIN.svg | 5 + website/public/images/logos/favicon.png | Bin 0 -> 32790 bytes website/public/index.html | 40 - website/public/vercel.svg | 4 - website/src/components/AuthLayout.tsx | 47 + website/src/components/Button.tsx | 39 + website/src/components/CallToAction.tsx | 58 + website/src/components/CircleBackground.tsx | 45 + website/src/components/Container.tsx | 10 + website/src/components/Faq.tsx | 77 + website/src/components/Fields.tsx | 33 + website/src/components/Footer.tsx | 36 + website/src/components/Header.tsx | 140 ++ website/src/components/Hero.tsx | 109 ++ website/src/components/NavLinks.tsx | 40 + website/src/index.css | 13 + website/src/index.js | 17 + website/src/logo.svg | 1 + website/src/pages/_app.tsx | 27 + website/src/pages/_document.tsx | 17 + website/src/pages/grading/grade-output.tsx | 231 +++ website/src/pages/index.tsx | 56 + website/src/pages/login.jsx | 47 + website/{ => src}/pages/new_task.js | 0 website/src/reportWebVitals.js | 13 + website/src/setupTests.js | 5 + website/src/styles/Home.module.css | 0 website/src/styles/globals.css | 54 + website/styles/globals.css | 26 - website/tailwind.config.js | 77 + website/tsconfig.json | 30 + 50 files changed, 2801 insertions(+), 407 deletions(-) delete mode 100644 website/pages/_app.js delete mode 100644 website/pages/api/auth/[...nextauth].js delete mode 100644 website/pages/api/new_task.js delete mode 100644 website/pages/api/update_task.js delete mode 100644 website/pages/index.js create mode 100644 website/postcss.config.js delete mode 100644 website/public/favicon.ico create mode 100644 website/public/fonts/Inter-italic.var.woff2 create mode 100644 website/public/fonts/Inter-roman.var.woff2 create mode 100644 website/public/fonts/lexend.txt create mode 100644 website/public/fonts/lexend.woff2 create mode 100644 website/public/images/logos/CHAT-THOUGHT-CONVO.svg create mode 100644 website/public/images/logos/CHAT-THOUGHT-LOGO.svg create mode 100644 website/public/images/logos/GPTBRAIN-chat.svg create mode 100644 website/public/images/logos/GPTBRAIN-grn.svg create mode 100644 website/public/images/logos/GPTBRAIN.svg create mode 100644 website/public/images/logos/favicon.png delete mode 100644 website/public/index.html delete mode 100644 website/public/vercel.svg create mode 100644 website/src/components/AuthLayout.tsx create mode 100644 website/src/components/Button.tsx create mode 100644 website/src/components/CallToAction.tsx create mode 100644 website/src/components/CircleBackground.tsx create mode 100644 website/src/components/Container.tsx create mode 100644 website/src/components/Faq.tsx create mode 100644 website/src/components/Fields.tsx create mode 100644 website/src/components/Footer.tsx create mode 100644 website/src/components/Header.tsx create mode 100644 website/src/components/Hero.tsx create mode 100644 website/src/components/NavLinks.tsx create mode 100644 website/src/index.css create mode 100644 website/src/index.js create mode 100644 website/src/logo.svg create mode 100644 website/src/pages/_app.tsx create mode 100644 website/src/pages/_document.tsx create mode 100644 website/src/pages/grading/grade-output.tsx create mode 100644 website/src/pages/index.tsx create mode 100644 website/src/pages/login.jsx rename website/{ => src}/pages/new_task.js (100%) create mode 100644 website/src/reportWebVitals.js create mode 100644 website/src/setupTests.js create mode 100644 website/src/styles/Home.module.css create mode 100644 website/src/styles/globals.css delete mode 100644 website/styles/globals.css create mode 100644 website/tailwind.config.js create mode 100644 website/tsconfig.json diff --git a/website/jsconfig.json b/website/jsconfig.json index 36aa1a4d..2c8ee2bb 100644 --- a/website/jsconfig.json +++ b/website/jsconfig.json @@ -1,5 +1,8 @@ { "compilerOptions": { - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } } } diff --git a/website/next.config.js b/website/next.config.js index 91ef62f0..d04423f6 100644 --- a/website/next.config.js +++ b/website/next.config.js @@ -1,6 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, -}; + experimental: { + scrollRestoration: true, + }, +} -module.exports = nextConfig; +module.exports = nextConfig diff --git a/website/package-lock.json b/website/package-lock.json index 58e33695..4a074c49 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -8,23 +8,35 @@ "name": "website", "version": "0.1.0", "dependencies": { + "@headlessui/react": "^1.7.7", + "@heroicons/react": "^2.0.13", "@next-auth/prisma-adapter": "^1.0.5", "@prisma/client": "^4.7.1", "@supabase/auth-helpers-nextjs": "^0.5.2", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", "@supabase/supabase-js": "^2.1.4", + "@tailwindcss/forms": "^0.5.3", + "autoprefixer": "^10.4.13", "axios": "^1.2.1", + "clsx": "^1.2.1", "eslint": "8.29.0", "eslint-config-next": "13.0.6", + "focus-visible": "^5.2.0", + "framer-motion": "^7.10.2", "next": "13.0.6", "next-auth": "^4.18.6", "nodemailer": "^6.8.0", + "postcss-focus-visible": "^7.1.0", "react": "18.2.0", "react-dom": "18.2.0", - "swr": "^2.0.0" + "swr": "^2.0.0", + "tailwindcss": "^3.2.4", + "use-debounce": "^9.0.2" }, "devDependencies": { + "@types/node": "18.11.17", + "@types/react": "18.0.26", "prisma": "^4.7.1" } }, @@ -51,15 +63,30 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", + "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -73,10 +100,33 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@headlessui/react": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz", + "integrity": "sha512-BqDOd/tB9u2tA0T3Z0fn18ktw+KbVwMnkxxsGPIH2hzssrQhKB5n/6StZOyvLYP/FsYtvuXfi9I0YowKPv2c1w==", + "dependencies": { + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@heroicons/react": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.13.tgz", + "integrity": "sha512-iSN5XwmagrnirWlYEWNPdCDj9aRYVD/lnK3JlsC9/+fqGF80k8C7rl+1HCvBX0dBoagKqOFBs6fMhJJ1hOg1EQ==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -103,6 +153,64 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "node_modules/@motionone/animation": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", + "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", + "dependencies": { + "@motionone/easing": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.15.3", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.15.3.tgz", + "integrity": "sha512-FQ7a2zMBXc1UeU8CG9G3yDpst55fbb0+C9A0VGfwOITitBCzigKZnXRgsRSWWR+FW57GSc13eGQxtYB0lKG0Ng==", + "dependencies": { + "@motionone/animation": "^10.15.1", + "@motionone/generators": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", + "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "dependencies": { + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", + "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "dependencies": { + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", + "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + }, + "node_modules/@motionone/utils": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", + "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "dependencies": { + "@motionone/types": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "node_modules/@next-auth/prisma-adapter": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", @@ -482,17 +590,17 @@ } }, "node_modules/@supabase/gotrue-js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.5.0.tgz", - "integrity": "sha512-Aw5/WL0+JVjQjX2ZOQvOzoquMg1IMa4l1cA0eHVseAjujgUPpztjdhDwy3xP/c7OQsF+RK0ARxjsmIm2iaILlg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.6.0.tgz", + "integrity": "sha512-vU0rSLUIp3mRSGnBYTx0dBc/1gqiEgX1nrw5ewRd1fvld91KeuCyKOnTXFLppFrv5t1+96Lq45g/BaV27lnzig==", "dependencies": { "cross-fetch": "^3.1.5" } }, "node_modules/@supabase/postgrest-js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.1.0.tgz", - "integrity": "sha512-qkY8TqIu5sJuae8gjeDPjEqPrefzcTraW9PNSVJQHq4TEv98ZmwaXGwBGz0bVL63bqrGA5hqREbQHkANUTXrvA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.1.1.tgz", + "integrity": "sha512-jhdBah1JIxkZUp+5QH5JS7Uq9teGwh0Bs3FzbhnVlH619FSUFquTpHuNDxLsJmqEe8r3Wcnw19Dz0t3wEpkfug==", "dependencies": { "cross-fetch": "^3.1.5" } @@ -515,15 +623,15 @@ } }, "node_modules/@supabase/supabase-js": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.1.4.tgz", - "integrity": "sha512-i+BAiE472hMIIz6/nM5Pt/6riljjQYJrReyd0LsYsNtis4vcey7SuMX7sYiG9kMo4ivPlB1odYSOJnRbNyqRbQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.2.1.tgz", + "integrity": "sha512-gHm0bYu8NXCVlKIOcuUT9s/izbNlrwIw+UCKTT9sj2gR9QcYoz4jjIWnvNF8Uwtp5Mq1444NI7OH1yK70yJykQ==", "dependencies": { "@supabase/functions-js": "^2.0.0", "@supabase/gotrue-js": "^2.5.0", - "@supabase/postgrest-js": "^1.1.0", + "@supabase/postgrest-js": "^1.1.1", "@supabase/realtime-js": "^2.1.0", - "@supabase/storage-js": "^2.0.0", + "@supabase/storage-js": "^2.1.0", "cross-fetch": "^3.1.5" } }, @@ -535,16 +643,56 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", + "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, "node_modules/@types/phoenix": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz", "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==" }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { "version": "5.46.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz", @@ -660,6 +808,35 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -697,6 +874,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -796,10 +990,42 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/axe-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", - "integrity": "sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.1.tgz", + "integrity": "sha512-lCZN5XRuOnpG4bpMq8v0khrWtUOn+i8lZSb6wHZH56ZfbIEv6XwJV84AAueh9/zi7qPVJ/E4yz6fmsiyOmXR4w==", "engines": { "node": ">=4" } @@ -824,6 +1050,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -844,6 +1078,33 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/bufferutil": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", @@ -876,6 +1137,14 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001439", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", @@ -906,11 +1175,56 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -982,6 +1296,23 @@ "node": ">= 8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "dev": true + }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -1040,6 +1371,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1048,6 +1387,27 @@ "node": ">=0.4.0" } }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1059,6 +1419,11 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1070,6 +1435,11 @@ "node": ">=6.0.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -1182,6 +1552,14 @@ "ext": "^1.1.2" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1740,6 +2118,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "node_modules/focus-visible": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", + "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -1772,6 +2155,40 @@ "node": ">= 6" } }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/framer-motion": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-7.10.2.tgz", + "integrity": "sha512-2OAII9hjpMNz2Nbl2w09uKo7A0bD6xRtGnCZbPzuGGueucWgmSBLjAwzPhXwzCQWMpL3LU8jmXHjxAwIyjslxg==", + "dependencies": { + "@motionone/dom": "^10.15.3", + "hey-listen": "^1.0.8", + "tslib": "2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/framer-motion/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2013,6 +2430,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/ignore": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", @@ -2059,11 +2481,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "dependencies": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.1.3", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -2082,6 +2504,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2365,9 +2798,9 @@ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" }, "node_modules/language-tags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", - "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.7.tgz", + "integrity": "sha512-bSytju1/657hFjgUzPAPqszxH62ouE8nQFoFaVlIQfne4wO/wXC9A4+m8jYve7YBBvi59eq0SUpcshvG8h5Usw==", "dependencies": { "language-subtag-registry": "^0.3.20" } @@ -2384,6 +2817,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2464,6 +2905,14 @@ "node": ">= 0.6" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2619,6 +3068,11 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-releases": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==" + }, "node_modules/nodemailer": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", @@ -2627,6 +3081,22 @@ "node": ">=6.0.0" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -2892,6 +3362,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -2915,6 +3393,121 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-focus-visible": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-7.1.0.tgz", + "integrity": "sha512-OGxO+eCXVkF94us4uOVLHDPaoJuV8HQhaqmVPo2DtECdjqzWNkmwhFRSRRDT5Sg04JQsAECzgIhpyX0JRTgwlQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/preact": { "version": "10.11.3", "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", @@ -3007,6 +3600,17 @@ } ] }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -3035,6 +3639,25 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -3366,6 +3989,77 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/tailwindcss": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", + "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.18", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/postcss": { + "version": "8.4.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", + "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -3501,6 +4195,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3509,6 +4228,17 @@ "punycode": "^2.1.0" } }, + "node_modules/use-debounce": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.2.tgz", + "integrity": "sha512-QLyB0sxt9F5AisGDrUybCRJSLE60bTQR0yXc+IebNGUu1GCXwii1zsZl82mPGdWqDVQy7+1FKMLHQUixxf5Nbw==", + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -3529,6 +4259,11 @@ "node": ">=6.14.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -3622,6 +4357,14 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -3635,6 +4378,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -3665,15 +4416,30 @@ "regenerator-runtime": "^0.13.11" } }, + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", + "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.4.0", - "globals": "^13.15.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -3681,10 +4447,24 @@ "strip-json-comments": "^3.1.1" } }, + "@headlessui/react": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.7.tgz", + "integrity": "sha512-BqDOd/tB9u2tA0T3Z0fn18ktw+KbVwMnkxxsGPIH2hzssrQhKB5n/6StZOyvLYP/FsYtvuXfi9I0YowKPv2c1w==", + "requires": { + "client-only": "^0.0.1" + } + }, + "@heroicons/react": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.13.tgz", + "integrity": "sha512-iSN5XwmagrnirWlYEWNPdCDj9aRYVD/lnK3JlsC9/+fqGF80k8C7rl+1HCvBX0dBoagKqOFBs6fMhJJ1hOg1EQ==", + "requires": {} + }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -3701,6 +4481,64 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" }, + "@motionone/animation": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", + "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", + "requires": { + "@motionone/easing": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/dom": { + "version": "10.15.3", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.15.3.tgz", + "integrity": "sha512-FQ7a2zMBXc1UeU8CG9G3yDpst55fbb0+C9A0VGfwOITitBCzigKZnXRgsRSWWR+FW57GSc13eGQxtYB0lKG0Ng==", + "requires": { + "@motionone/animation": "^10.15.1", + "@motionone/generators": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "@motionone/easing": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", + "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "requires": { + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/generators": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", + "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "requires": { + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/types": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", + "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + }, + "@motionone/utils": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", + "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "requires": { + "@motionone/types": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "@next-auth/prisma-adapter": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.5.tgz", @@ -3914,17 +4752,17 @@ } }, "@supabase/gotrue-js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.5.0.tgz", - "integrity": "sha512-Aw5/WL0+JVjQjX2ZOQvOzoquMg1IMa4l1cA0eHVseAjujgUPpztjdhDwy3xP/c7OQsF+RK0ARxjsmIm2iaILlg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.6.0.tgz", + "integrity": "sha512-vU0rSLUIp3mRSGnBYTx0dBc/1gqiEgX1nrw5ewRd1fvld91KeuCyKOnTXFLppFrv5t1+96Lq45g/BaV27lnzig==", "requires": { "cross-fetch": "^3.1.5" } }, "@supabase/postgrest-js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.1.0.tgz", - "integrity": "sha512-qkY8TqIu5sJuae8gjeDPjEqPrefzcTraW9PNSVJQHq4TEv98ZmwaXGwBGz0bVL63bqrGA5hqREbQHkANUTXrvA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.1.1.tgz", + "integrity": "sha512-jhdBah1JIxkZUp+5QH5JS7Uq9teGwh0Bs3FzbhnVlH619FSUFquTpHuNDxLsJmqEe8r3Wcnw19Dz0t3wEpkfug==", "requires": { "cross-fetch": "^3.1.5" } @@ -3947,15 +4785,15 @@ } }, "@supabase/supabase-js": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.1.4.tgz", - "integrity": "sha512-i+BAiE472hMIIz6/nM5Pt/6riljjQYJrReyd0LsYsNtis4vcey7SuMX7sYiG9kMo4ivPlB1odYSOJnRbNyqRbQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.2.1.tgz", + "integrity": "sha512-gHm0bYu8NXCVlKIOcuUT9s/izbNlrwIw+UCKTT9sj2gR9QcYoz4jjIWnvNF8Uwtp5Mq1444NI7OH1yK70yJykQ==", "requires": { "@supabase/functions-js": "^2.0.0", "@supabase/gotrue-js": "^2.5.0", - "@supabase/postgrest-js": "^1.1.0", + "@supabase/postgrest-js": "^1.1.1", "@supabase/realtime-js": "^2.1.0", - "@supabase/storage-js": "^2.0.0", + "@supabase/storage-js": "^2.1.0", "cross-fetch": "^3.1.5" } }, @@ -3967,16 +4805,53 @@ "tslib": "^2.4.0" } }, + "@tailwindcss/forms": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", + "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, "@types/phoenix": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.5.4.tgz", "integrity": "sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ==" }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "@types/react": { + "version": "18.0.26", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz", + "integrity": "sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "@typescript-eslint/parser": { "version": "5.46.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz", @@ -4036,6 +4911,28 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "requires": {} }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4060,6 +4957,20 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4135,10 +5046,23 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "autoprefixer": { + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, "axe-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.0.tgz", - "integrity": "sha512-L3ZNbXPTxMrl0+qTXAzn9FBRvk5XdO56K8CvcCKtlxv44Aw2w2NCclGuvCWxHPw1Riiq3ncP/sxFYj2nUqdoTw==" + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.1.tgz", + "integrity": "sha512-lCZN5XRuOnpG4bpMq8v0khrWtUOn+i8lZSb6wHZH56ZfbIEv6XwJV84AAueh9/zi7qPVJ/E4yz6fmsiyOmXR4w==" }, "axios": { "version": "1.2.1", @@ -4160,6 +5084,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -4177,6 +5106,17 @@ "fill-range": "^7.0.1" } }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, "bufferutil": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", @@ -4199,6 +5139,11 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, "caniuse-lite": { "version": "1.0.30001439", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz", @@ -4213,11 +5158,41 @@ "supports-color": "^7.1.0" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4272,6 +5247,17 @@ "which": "^2.0.1" } }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", + "dev": true + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -4313,11 +5299,31 @@ "object-keys": "^1.1.1" } }, + "defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -4326,6 +5332,11 @@ "path-type": "^4.0.0" } }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4334,6 +5345,11 @@ "esutils": "^2.0.2" } }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==" + }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -4427,6 +5443,11 @@ "ext": "^1.1.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4851,6 +5872,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, + "focus-visible": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", + "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" + }, "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -4866,6 +5892,29 @@ "mime-types": "^2.1.12" } }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "framer-motion": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-7.10.2.tgz", + "integrity": "sha512-2OAII9hjpMNz2Nbl2w09uKo7A0bD6xRtGnCZbPzuGGueucWgmSBLjAwzPhXwzCQWMpL3LU8jmXHjxAwIyjslxg==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "@motionone/dom": "^10.15.3", + "hey-listen": "^1.0.8", + "tslib": "2.4.0" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5031,6 +6080,11 @@ "has-symbols": "^1.0.2" } }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "ignore": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", @@ -5065,11 +6119,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "requires": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.1.3", "has": "^1.0.3", "side-channel": "^1.0.4" } @@ -5082,6 +6136,14 @@ "has-bigints": "^1.0.1" } }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -5268,9 +6330,9 @@ "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" }, "language-tags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.6.tgz", - "integrity": "sha512-HNkaCgM8wZgE/BZACeotAAgpL9FUjEnhgF0FVQMIgH//zqTPreLYMb3rWYkYAqPoF75Jwuycp1da7uz66cfFQg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.7.tgz", + "integrity": "sha512-bSytju1/657hFjgUzPAPqszxH62ouE8nQFoFaVlIQfne4wO/wXC9A4+m8jYve7YBBvi59eq0SUpcshvG8h5Usw==", "requires": { "language-subtag-registry": "^0.3.20" } @@ -5284,6 +6346,11 @@ "type-check": "~0.4.0" } }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5340,6 +6407,11 @@ "mime-db": "1.52.0" } }, + "mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5427,11 +6499,26 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==" }, + "node-releases": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==" + }, "nodemailer": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.8.0.tgz", "integrity": "sha512-EjYvSmHzekz6VNkNd12aUqAco+bOkRe3Of5jVhltqKhEsjw/y0PYPJfp83+s9Wzh1dspYAkUW/YNQ350NATbSQ==" }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + }, "oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -5613,6 +6700,11 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, "postcss": { "version": "8.4.14", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", @@ -5623,6 +6715,63 @@ "source-map-js": "^1.0.2" } }, + "postcss-focus-visible": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-7.1.0.tgz", + "integrity": "sha512-OGxO+eCXVkF94us4uOVLHDPaoJuV8HQhaqmVPo2DtECdjqzWNkmwhFRSRRDT5Sg04JQsAECzgIhpyX0JRTgwlQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-nested": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "preact": { "version": "10.11.3", "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.3.tgz", @@ -5680,6 +6829,11 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -5702,6 +6856,22 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -5908,6 +7078,53 @@ "tslib": "^2.4.0" } }, + "tailwindcss": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", + "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", + "requires": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.6", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.18", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "6.0.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "dependencies": { + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "postcss": { + "version": "8.4.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", + "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + } + } + }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -6014,6 +7231,15 @@ "which-boxed-primitive": "^1.0.2" } }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -6022,6 +7248,12 @@ "punycode": "^2.1.0" } }, + "use-debounce": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-9.0.2.tgz", + "integrity": "sha512-QLyB0sxt9F5AisGDrUybCRJSLE60bTQR0yXc+IebNGUu1GCXwii1zsZl82mPGdWqDVQy7+1FKMLHQUixxf5Nbw==", + "requires": {} + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -6036,6 +7268,11 @@ "node-gyp-build": "^4.3.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -6113,6 +7350,11 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", @@ -6123,6 +7365,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/website/package.json b/website/package.json index dd4a6bb3..f3a1b1da 100644 --- a/website/package.json +++ b/website/package.json @@ -10,23 +10,35 @@ "lint": "next lint" }, "dependencies": { + "@headlessui/react": "^1.7.7", + "@heroicons/react": "^2.0.13", "@next-auth/prisma-adapter": "^1.0.5", "@prisma/client": "^4.7.1", "@supabase/auth-helpers-nextjs": "^0.5.2", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/auth-ui-react": "^0.2.6", "@supabase/supabase-js": "^2.1.4", + "@tailwindcss/forms": "^0.5.3", + "autoprefixer": "^10.4.13", "axios": "^1.2.1", + "clsx": "^1.2.1", "eslint": "8.29.0", "eslint-config-next": "13.0.6", + "focus-visible": "^5.2.0", + "framer-motion": "^7.10.2", "next": "13.0.6", "next-auth": "^4.18.6", "nodemailer": "^6.8.0", + "postcss-focus-visible": "^7.1.0", "react": "18.2.0", "react-dom": "18.2.0", - "swr": "^2.0.0" + "swr": "^2.0.0", + "tailwindcss": "^3.2.4", + "use-debounce": "^9.0.2" }, "devDependencies": { + "@types/node": "18.11.17", + "@types/react": "18.0.26", "prisma": "^4.7.1" } } diff --git a/website/pages/_app.js b/website/pages/_app.js deleted file mode 100644 index 5122f011..00000000 --- a/website/pages/_app.js +++ /dev/null @@ -1,14 +0,0 @@ -import { SessionProvider } from "next-auth/react"; -import { useState } from "react"; - -import "../styles/globals.css"; - -function MyApp({ Component, pageProps: { session, ...pageProps } }) { - return ( - - - - ); -} - -export default MyApp; diff --git a/website/pages/api/auth/[...nextauth].js b/website/pages/api/auth/[...nextauth].js deleted file mode 100644 index c4ff116c..00000000 --- a/website/pages/api/auth/[...nextauth].js +++ /dev/null @@ -1,41 +0,0 @@ -import NextAuth from "next-auth"; -import DiscordProvider from "next-auth/providers/discord"; -import EmailProvider from "next-auth/providers/email"; -import { PrismaAdapter } from "@next-auth/prisma-adapter"; - -import prisma from "../../../lib/prismadb"; - -export const authOptions = { - // Ensure we can store user data in a database. - adapter: PrismaAdapter(prisma), - providers: [ - // Register a Discord auth method. - DiscordProvider({ - clientId: process.env.DISCORD_CLIENT_ID, - clientSecret: process.env.DISCORD_CLIENT_SECRET, - }), - // Register an email magic link auth method. - EmailProvider({ - server: { - host: process.env.EMAIL_SERVER_HOST, - port: process.env.EMAIL_SERVER_PORT, - auth: { - user: process.env.EMAIL_SERVER_USER, - pass: process.env.EMAIL_SERVER_PASSWORD, - }, - }, - from: process.env.EMAIL_FROM, - }), - ], - callbacks: { - /** - * Includes the raw user id in the session object. - */ - async session({ session, token, user }) { - session.user.id = user.id; - return session; - }, - }, -}; - -export default NextAuth(authOptions); diff --git a/website/pages/api/new_task.js b/website/pages/api/new_task.js deleted file mode 100644 index 6743b33a..00000000 --- a/website/pages/api/new_task.js +++ /dev/null @@ -1,71 +0,0 @@ -import { unstable_getServerSession } from "next-auth/next"; -import { authOptions } from "pages/api/auth/[...nextauth]"; - -/** - * Returns a new task created from the Task Backend. We do a few things here: - * - * 1) Get the task from the backend and register the requesting user. - * 2) Store the task in our local database. - * 3) Send and Ack to the Task Backend with our local id for the task. - * 4) Return everything to the client. - */ -export default async (req, res) => { - const session = await unstable_getServerSession(req, res, authOptions); - - // Return nothing if the user isn't registered. - if (!session) { - res.status(401).end(); - return; - } - - // Fetch the new task. - // - // This needs to be refactored into an easier to use library. - const taskRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/`, { - method: "POST", - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - type: "rate_summary", - user: { - id: session.user.id, - display_name: session.user.name, - auth_method: "local", - }, - }), - }); - const task = await taskRes.json(); - - // Store the task and link it to the user.. - const registeredTask = await prisma.registeredTask.create({ - data: { - task, - user: { - connect: { - id: session.user.id, - }, - }, - }, - }); - - // Update the backend with our Task ID - const ackRes = await fetch( - `${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, - { - method: "POST", - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - post_id: registeredTask.id, - }), - } - ); - const ack = await ackRes.json(); - - // Send the results to the client. - res.status(200).json(registeredTask); -}; diff --git a/website/pages/api/update_task.js b/website/pages/api/update_task.js deleted file mode 100644 index 45b1f72e..00000000 --- a/website/pages/api/update_task.js +++ /dev/null @@ -1,80 +0,0 @@ -import { unstable_getServerSession } from "next-auth/next"; -import { authOptions } from "./auth/[...nextauth]"; - -/** - * Stores the task interaction with the Task Backend and then returns the next task generated. - * - * This implicity does a few things: - * 1) Stores the answer with the Task Backend. - * 2) Records the new task in our local database. - * 3) (TODO) Acks the new task with our local task ID to the Task Backend. - * 4) Returns the newly created task to the client. - */ -export default async (req, res) => { - const session = await unstable_getServerSession(req, res, authOptions); - - // Return nothing if the user isn't registered. - if (!session) { - res.status(401).end(); - return; - } - - // Parse out the local task ID and the interaction contents. - const { id, content } = await JSON.parse(req.body); - - // Log the interaction locally to create our user_post_id needed by the Task - // Backend. - const interaction = await prisma.taskInteraction.create({ - data: { - content, - task: { - connect: { - id, - }, - }, - }, - }); - - // Send the interaction to the Task Backend. This automatically fetches the - // next task in the sequence (or the done task). - const interactionRes = await fetch( - `${process.env.FASTAPI_URL}/api/v1/tasks/interaction`, - { - method: "POST", - headers: { - "X-API-Key": process.env.FASTAPI_KEY, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - type: "post_rating", - user: { - id: session.user.id, - display_name: session.user.name, - auth_method: "local", - }, - post_id: id, - user_post_id: interaction.id, - ...content, - }), - } - ); - const newTask = await interactionRes.json(); - - // Stores the new task with our database. - const newRegisteredTask = await prisma.registeredTask.create({ - data: { - task: newTask, - user: { - connect: { - id: session.user.id, - }, - }, - }, - }); - - // TODO: Ack the task with the Task Backend using the newly created local - // task ID. - - // Send the next task in the sequence to the client. - res.status(200).json(newRegisteredTask); -}; diff --git a/website/pages/index.js b/website/pages/index.js deleted file mode 100644 index 9df611b9..00000000 --- a/website/pages/index.js +++ /dev/null @@ -1,70 +0,0 @@ -import axios from "axios"; -import Head from "next/head"; -import Image from "next/image"; -import { useSession, signIn, signOut } from "next-auth/react"; -import { useEffect, useState } from "react"; -import useSWR from "swr"; - -import styles from "styles/Home.module.css"; - -const fetcher = (url) => axios.get(url).then((res) => res.data); - -export default function Home() { - const { data: session, status } = useSession(); - const { data: prompts, errors } = useSWR( - session ? "/api/prompts" : null, - fetcher - ); - if (!session) { - return ( -
-
-

Open Assistant

-

- Open Assistant is a project meant to give everyone access to a great - chat based large language model. -

- - -

- We believe that by doing this we will create a revolution in - innovation in language. In the same way that stable-diffusion helped - the world make art and images in new ways we hope Open Assistant can - help improve the world by improving language itself. -

- -

How can you help?

-

- All open source projects begins with people like you. Open source is - the belief that if we collaborate we can together gift our knowledge - and technology to the world for the benefit of humanity. -

- -

I am in! Now what?

-

We live and collaborate the work in the LAION discord. Join us!

- - Join us on Discord - -
-
- ); - } - - console.log(prompts); - return ( -
-
-

Open Assistant

- -

You are logged in

- - -
-
- ); -} diff --git a/website/postcss.config.js b/website/postcss.config.js new file mode 100644 index 00000000..33ad091d --- /dev/null +++ b/website/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/website/public/favicon.ico b/website/public/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m diff --git a/website/public/fonts/Inter-italic.var.woff2 b/website/public/fonts/Inter-italic.var.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b826d5af84b3bd70535b6bb993f443a2deb46894 GIT binary patch literal 245036 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�LzqE;f#83j;-svHFcR`yUq8K)}}#m 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 C+W+PN literal 0 HcmV?d00001 diff --git a/website/public/fonts/Inter-roman.var.woff2 b/website/public/fonts/Inter-roman.var.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..6a256a068f0dce7b44f8314bdc735ef91b35e2e3 GIT binary patch literal 227180 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�J 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�Db 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/public/images/logos/CHAT-THOUGHT-LOGO.svg b/website/public/images/logos/CHAT-THOUGHT-LOGO.svg new file mode 100644 index 00000000..dc0b32df --- /dev/null +++ b/website/public/images/logos/CHAT-THOUGHT-LOGO.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/public/images/logos/GPTBRAIN-chat.svg b/website/public/images/logos/GPTBRAIN-chat.svg new file mode 100644 index 00000000..db31e571 --- /dev/null +++ b/website/public/images/logos/GPTBRAIN-chat.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/public/images/logos/GPTBRAIN-grn.svg b/website/public/images/logos/GPTBRAIN-grn.svg new file mode 100644 index 00000000..5fd005f4 --- /dev/null +++ b/website/public/images/logos/GPTBRAIN-grn.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/public/images/logos/GPTBRAIN.svg b/website/public/images/logos/GPTBRAIN.svg new file mode 100644 index 00000000..d2ce7212 --- /dev/null +++ b/website/public/images/logos/GPTBRAIN.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/website/public/images/logos/favicon.png b/website/public/images/logos/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f21cbc99fd67163fad9c5d416a1a49324fcd437 GIT binary patch literal 32790 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 literal 0 HcmV?d00001 diff --git a/website/public/index.html b/website/public/index.html deleted file mode 100644 index e9d7030d..00000000 --- a/website/public/index.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - Open Assistant - - - -

- - - diff --git a/website/public/vercel.svg b/website/public/vercel.svg deleted file mode 100644 index 7daff190..00000000 --- a/website/public/vercel.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/website/src/components/AuthLayout.tsx b/website/src/components/AuthLayout.tsx new file mode 100644 index 00000000..249126b8 --- /dev/null +++ b/website/src/components/AuthLayout.tsx @@ -0,0 +1,47 @@ +import Link from 'next/link' + + +function BackgroundIllustration(props) { + return ( + + ) +} + +export function AuthLayout({ title, subtitle, children }) { + return ( +
+
+ + {/* logo */} + +
+ +

+ {title} +

+ {subtitle && ( +

{subtitle}

+ )} +
+
+ {children} +
+
+
+ ) +} diff --git a/website/src/components/Button.tsx b/website/src/components/Button.tsx new file mode 100644 index 00000000..07bf17d8 --- /dev/null +++ b/website/src/components/Button.tsx @@ -0,0 +1,39 @@ +import { forwardRef } from 'react' +import Link from 'next/link' +import clsx from 'clsx' + +const baseStyles = { + solid: + 'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold outline-2 outline-offset-2 transition-colors', + outline: + 'inline-flex justify-center rounded-lg border py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm outline-2 outline-offset-2 transition-colors', +} + +const variantStyles = { + solid: { + cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors', + white: + 'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70', + gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80', + }, + outline: { + gray: 'border-gray-300 text-gray-700 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80', + }, +} + +export const Button = forwardRef(function Button( + { variant = 'solid', color = 'gray', className, href, ...props }, + ref +) { + className = clsx( + baseStyles[variant], + variantStyles[variant][color], + className + ) + + return href ? ( + + ) : ( +
+ + + + + + + +
+ + + + ) +} diff --git a/website/src/components/CircleBackground.tsx b/website/src/components/CircleBackground.tsx new file mode 100644 index 00000000..e2aa9057 --- /dev/null +++ b/website/src/components/CircleBackground.tsx @@ -0,0 +1,45 @@ +import { useId } from 'react' + +export function CircleBackground({ + color, + width = 558, + height = 558, + ...props +}) { + let id = useId() + + return ( + + ) +} diff --git a/website/src/components/Container.tsx b/website/src/components/Container.tsx new file mode 100644 index 00000000..a17ea3c1 --- /dev/null +++ b/website/src/components/Container.tsx @@ -0,0 +1,10 @@ +import clsx from 'clsx' + +export function Container({ className, ...props }) { + return ( +
+ ) +} diff --git a/website/src/components/Faq.tsx b/website/src/components/Faq.tsx new file mode 100644 index 00000000..9fdbdaba --- /dev/null +++ b/website/src/components/Faq.tsx @@ -0,0 +1,77 @@ +import Link from 'next/link' + +import { Container } from './Container' + +const faqs = [ + [ + { + question: 'How far along is this project?', + answer: + 'We are in the early stages of development, working from established research in applying RLHF to large language models.', + }, + ], + [ + { + question: 'Who is behind Open Assistant?', + answer: + 'Open Assistant is a project organized by LAION and individuals around the world interested in bringing this technology to everyone.', + }, + ], + [ + // { + // question: 'Where can I learn more?', + // answer: + // 'Please feel free to reach out to us on Discord. We are happy to answer any questions you may have.', + // }, + ], +] + +export function Faq() { + return ( +
+ +
+

+ Frequently Asked Questions +

+ {/*

+ If you have anything else you want to ask,{' '} + + reach out to us + + . +

*/} +
+
    + {faqs.map((column, columnIndex) => ( +
  • +
      + {column.map((faq, faqIndex) => ( +
    • +

      + {faq.question} +

      +

      {faq.answer}

      +
    • + ))} +
    +
  • + ))} +
+
+
+ ) +} diff --git a/website/src/components/Fields.tsx b/website/src/components/Fields.tsx new file mode 100644 index 00000000..fe55aa62 --- /dev/null +++ b/website/src/components/Fields.tsx @@ -0,0 +1,33 @@ +import clsx from 'clsx' + +const formClasses = + 'block w-full appearance-none rounded-lg border border-gray-200 bg-white py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-gray-900 placeholder:text-gray-400 focus:border-cyan-500 focus:outline-none focus:ring-cyan-500 sm:text-sm' + +function Label({ id, children }) { + return ( + + ) +} + +export function TextField({ id, label, type = 'text', className, ...props }) { + return ( +
+ {label && } + +
+ ) +} + +export function SelectField({ id, label, className, ...props }) { + return ( +
+ {label && } +