mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-07-20 12:10:50 +08:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
name: Build Frontend
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./website
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: 'npm'
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
@@ -10,6 +10,11 @@ repos:
|
||||
- id: trailing-whitespace
|
||||
- id: check-ast
|
||||
- id: check-yaml
|
||||
# Always check YAML but skip a few YAML files that are auto-generated
|
||||
# and which break the standard YAML check. The alternative would be to
|
||||
# skip any unsafe errors (and thus break YAML compatibility) or use
|
||||
# some other checker that may not work in general.
|
||||
exclude: copilot/web/addons/*
|
||||
- id: check-json
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
|
||||
+14
-11
@@ -2,33 +2,33 @@
|
||||
|
||||
(pull requests welcome)
|
||||
|
||||
1. General rules
|
||||
## 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:
|
||||
## 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:
|
||||
## 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:
|
||||
## 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:
|
||||
## 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
|
||||
@@ -37,14 +37,14 @@
|
||||
- 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:
|
||||
## 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:
|
||||
## 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
|
||||
@@ -55,11 +55,14 @@
|
||||
|
||||
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: "Hi!"
|
||||
<br>Agent: "Hello! How can I help you?"
|
||||
|
||||
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?"
|
||||
User: "euirdteunvglfe23908230892309832098 AAAAAAAA"
|
||||
<br>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?"
|
||||
|
||||
User: "You dumb motherf*&ker AI sh*t?!"
|
||||
<br>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."
|
||||
|
||||
[Insert more examples]
|
||||
|
||||
|
||||
@@ -30,6 +30,50 @@ All open source projects begins with people like you. Open source is the belief
|
||||
|
||||
[Visit the Notion](https://ykilcher.com/open-assistant)
|
||||
|
||||
### Taking on Tasks
|
||||
|
||||
We have a growing task list
|
||||
[of issues](https://github.com/LAION-AI/Open-Assistant/issues). Find an issue
|
||||
that appeals to you and make a comment that you'd like to work on it. Include
|
||||
in your comment a brief description of how you'll solve the problem and if
|
||||
there are any open questions you want to discuss. Once a project coordinator
|
||||
has assigned the issue to you, start working on it.
|
||||
|
||||
If the issue is currently unclear but you are interested, please post in
|
||||
Discord and someone can help clarify the issue with more detail.
|
||||
|
||||
### Submitting Work
|
||||
|
||||
We're all working on different parts of Open Assistant together. To make
|
||||
contributions smoothly we recommend the following:
|
||||
|
||||
1. Work on a small focused change that only touches on a few files.
|
||||
1. Run `pre-commit` and make sure all files have formatting fixed. This
|
||||
simplifies life for reviewers.
|
||||
1. Package up a small bit of work that solves part of the problem into a Pull
|
||||
Request and send it out for review
|
||||
1. If you're lucky, we can merge your change into `main` without any problems.
|
||||
If there's changes to files you're working on, resolve them by:
|
||||
1. First try rebase as suggested
|
||||
[in these instructions](https://timwise.co.uk/2019/10/14/merge-vs-rebase/#should-you-rebase)
|
||||
1. If rebase feels too painful, merge as suggested
|
||||
[in these instructions](https://timwise.co.uk/2019/10/14/merge-vs-rebase/#should-you-merge)
|
||||
1. Once you've resolved any conflicts, finish the review and merge into `main`.
|
||||
1. Merge in your change and move onto a new issue or the second step of your
|
||||
current issue.
|
||||
|
||||
Additionally, if someone is working on an issue that interests you, ask if they
|
||||
need help on it or would like suggestions on how to approach the issue. If so,
|
||||
share wildly. If they seem to have a good handle on it, let them work on their
|
||||
solution until a challenge comes up.
|
||||
|
||||
### When does a review finish
|
||||
|
||||
A review finishes when all blocking comments are addressed and at least one
|
||||
owning reviewer has approved the PR. Be sure to acknowledge any non-blocking
|
||||
comments either by making the request change, explaining why it's not being
|
||||
addressed now, or filing an issue to handle it later.
|
||||
|
||||
## Developer Setup
|
||||
|
||||
Work is organized in the [project board](https://github.com/orgs/LAION-AI/projects/3).
|
||||
@@ -47,6 +91,22 @@ 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)
|
||||
|
||||
## End to End Demo
|
||||
|
||||
If you are interested in just taking a look at the project.
|
||||
You can set up an entire stack needed to run Open Assistant, including the
|
||||
website, backend, and associated dependent services.
|
||||
|
||||
To start the demo, run this, in root directory:
|
||||
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Then, navigate to `http://localhost:3000` and interact with the website. When
|
||||
logging in, navigate to `http://localhost:1080` to get the magic email login
|
||||
link.
|
||||
|
||||
### Website
|
||||
|
||||
The website is built using Next.js and is in the `website` folder.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
application: open-assistant
|
||||
@@ -0,0 +1,94 @@
|
||||
# Deploying on AWS
|
||||
|
||||
## Introduction
|
||||
|
||||
[Copilot](https://aws.github.io/copilot-cli/) helps simplify AWS resources and
|
||||
automate deploymnents for projects.
|
||||
|
||||
This sample configuration runs the Open Assistant web app as an ECS Fargate
|
||||
services backed by a Serverless Aurora Postgres database.
|
||||
|
||||
## To Setup
|
||||
|
||||
Setup requires a few steps:
|
||||
|
||||
```sh
|
||||
copilot app init --domain your_domain.com
|
||||
```
|
||||
|
||||
This will initialize and register a variety of URLs with your
|
||||
`your_domain.com`. Replace with a proper domain to setup SSL certificates.
|
||||
|
||||
```sh
|
||||
copilot env deploy
|
||||
```
|
||||
|
||||
This will create a variety of aws roles and services needed for deployment.
|
||||
|
||||
```sh
|
||||
copilot deploy
|
||||
```
|
||||
|
||||
This will depoy the services but it won't be 100% ready for usage. Before
|
||||
being ready, we have to inspect the AWS Secrets manager and extract out the
|
||||
database credentials. Read those credentials then put them, and a few other
|
||||
secrets, in a `secrets.yml` file like the following:
|
||||
|
||||
```yaml
|
||||
DATABASE_URL:
|
||||
staging: postgres://postgres:${db_password}@${db_host}:${db_port}/${db_name}
|
||||
DISCORD_CLIENT_ID:
|
||||
staging: ...
|
||||
DISCORD_CLIENT_SECRET:
|
||||
staging: ...
|
||||
EMAIL_SERVER_HOST:
|
||||
staging: ...
|
||||
EMAIL_SERVER_PORT:
|
||||
staging: ...
|
||||
EMAIL_SERVER_USER:
|
||||
staging: ...
|
||||
EMAIL_SERVER_PASSWORD:
|
||||
staging: ...
|
||||
EMAIL_FROM:
|
||||
staging: ...
|
||||
FASTAPI_URL:
|
||||
staging: ...
|
||||
FASTAPI_KEY:
|
||||
staging: ...
|
||||
NEXTAUTH_SECRET:
|
||||
staging: ...
|
||||
```
|
||||
|
||||
Then, upload the secrets to AWS with:
|
||||
|
||||
```sh
|
||||
copilot secret init --cli-input-yaml secrets.yml
|
||||
```
|
||||
|
||||
Now, finally deploy:
|
||||
|
||||
```sh
|
||||
copilot deploy
|
||||
```
|
||||
|
||||
If we documented everything correctly, the site should work properly.
|
||||
|
||||
## To Update Manually
|
||||
|
||||
First, make sure the database is updated with any schema changes:
|
||||
|
||||
```sh
|
||||
copilot task run \
|
||||
--app open-assistant --env staging \
|
||||
-n prisma-push \
|
||||
--dockerfile docker/Dockerfile.prisma --build-context "./" \
|
||||
--secrets DATABASE_URL=/copilot/open-assistant/staging/secrets/DATABASE_URL
|
||||
```
|
||||
|
||||
Next, deploy everything:
|
||||
|
||||
```sh
|
||||
copilot deploy
|
||||
```
|
||||
|
||||
TODO: Make this a pipeline once github and aws are fully connected.
|
||||
@@ -0,0 +1,10 @@
|
||||
# The manifest for the "staging" environment.
|
||||
# Read the full specification for the "Environment" type at:
|
||||
# https://aws.github.io/copilot-cli/docs/manifest/environment/
|
||||
|
||||
name: staging
|
||||
type: Environment
|
||||
|
||||
# Configure observability for your environment resources.
|
||||
observability:
|
||||
container_insights: false
|
||||
@@ -0,0 +1,144 @@
|
||||
Parameters:
|
||||
App:
|
||||
Type: String
|
||||
Description: Your application's name.
|
||||
Env:
|
||||
Type: String
|
||||
Description: The environment name your service, job, or workflow is being deployed to.
|
||||
Name:
|
||||
Type: String
|
||||
Description: The name of the service, job, or workflow being deployed.
|
||||
# Customize your Aurora Serverless cluster by setting the default value of the following parameters.
|
||||
webclusterDBName:
|
||||
Type: String
|
||||
Description: The name of the initial database to be created in the Aurora Serverless v2 cluster.
|
||||
Default: oassist_web
|
||||
# Cannot have special characters
|
||||
# Naming constraints: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Limits.html#RDS_Limits.Constraints
|
||||
Mappings:
|
||||
webclusterEnvScalingConfigurationMap:
|
||||
staging:
|
||||
"DBMinCapacity": 0.5 # AllowedValues: from 0.5 through 128
|
||||
"DBMaxCapacity": 8 # AllowedValues: from 0.5 through 128
|
||||
|
||||
All:
|
||||
"DBMinCapacity": 0.5 # AllowedValues: from 0.5 through 128
|
||||
"DBMaxCapacity": 8 # AllowedValues: from 0.5 through 128
|
||||
|
||||
Resources:
|
||||
webclusterDBSubnetGroup:
|
||||
Type: "AWS::RDS::DBSubnetGroup"
|
||||
Properties:
|
||||
DBSubnetGroupDescription: Group of Copilot private subnets for Aurora Serverless v2 cluster.
|
||||
SubnetIds:
|
||||
!Split [",", { "Fn::ImportValue": !Sub "${App}-${Env}-PrivateSubnets" }]
|
||||
webclusterSecurityGroup:
|
||||
Metadata:
|
||||
"aws:copilot:description": "A security group for your workload to access the Aurora Serverless v2 cluster webcluster"
|
||||
Type: "AWS::EC2::SecurityGroup"
|
||||
Properties:
|
||||
GroupDescription: !Sub "The Security Group for ${Name} to access Aurora Serverless v2 cluster webcluster."
|
||||
VpcId:
|
||||
Fn::ImportValue: !Sub "${App}-${Env}-VpcId"
|
||||
Tags:
|
||||
- Key: Name
|
||||
Value: !Sub "copilot-${App}-${Env}-${Name}-Aurora"
|
||||
webclusterDBClusterSecurityGroup:
|
||||
Metadata:
|
||||
"aws:copilot:description": "A security group for your Aurora Serverless v2 cluster webcluster"
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: The Security Group for the Aurora Serverless v2 cluster.
|
||||
SecurityGroupIngress:
|
||||
- ToPort: 5432
|
||||
FromPort: 5432
|
||||
IpProtocol: tcp
|
||||
Description: !Sub "From the Aurora Security Group of the workload ${Name}."
|
||||
SourceSecurityGroupId: !Ref webclusterSecurityGroup
|
||||
VpcId:
|
||||
Fn::ImportValue: !Sub "${App}-${Env}-VpcId"
|
||||
webclusterAuroraSecret:
|
||||
Metadata:
|
||||
"aws:copilot:description": "A Secrets Manager secret to store your DB credentials"
|
||||
Type: AWS::SecretsManager::Secret
|
||||
Properties:
|
||||
Description: !Sub Aurora main user secret for ${AWS::StackName}
|
||||
GenerateSecretString:
|
||||
SecretStringTemplate: '{"username": "postgres"}'
|
||||
GenerateStringKey: "password"
|
||||
ExcludePunctuation: true
|
||||
IncludeSpace: false
|
||||
PasswordLength: 16
|
||||
webclusterDBClusterParameterGroup:
|
||||
Metadata:
|
||||
"aws:copilot:description": "A DB parameter group for engine configuration values"
|
||||
Type: "AWS::RDS::DBClusterParameterGroup"
|
||||
Properties:
|
||||
Description: !Ref "AWS::StackName"
|
||||
Family: "aurora-postgresql14"
|
||||
Parameters:
|
||||
client_encoding: "UTF8"
|
||||
webclusterDBCluster:
|
||||
Metadata:
|
||||
"aws:copilot:description": "The webcluster Aurora Serverless v2 database cluster"
|
||||
Type: "AWS::RDS::DBCluster"
|
||||
Properties:
|
||||
MasterUsername:
|
||||
!Join [
|
||||
"",
|
||||
[
|
||||
"{{resolve:secretsmanager:",
|
||||
!Ref webclusterAuroraSecret,
|
||||
":SecretString:username}}",
|
||||
],
|
||||
]
|
||||
MasterUserPassword:
|
||||
!Join [
|
||||
"",
|
||||
[
|
||||
"{{resolve:secretsmanager:",
|
||||
!Ref webclusterAuroraSecret,
|
||||
":SecretString:password}}",
|
||||
],
|
||||
]
|
||||
DatabaseName: !Ref webclusterDBName
|
||||
Engine: "aurora-postgresql"
|
||||
EngineVersion: "14.4"
|
||||
DBClusterParameterGroupName: !Ref webclusterDBClusterParameterGroup
|
||||
DBSubnetGroupName: !Ref webclusterDBSubnetGroup
|
||||
Port: 5432
|
||||
VpcSecurityGroupIds:
|
||||
- !Ref webclusterDBClusterSecurityGroup
|
||||
ServerlessV2ScalingConfiguration:
|
||||
# Replace "All" below with "!Ref Env" to set different autoscaling limits per environment.
|
||||
MinCapacity:
|
||||
!FindInMap [webclusterEnvScalingConfigurationMap, All, DBMinCapacity]
|
||||
MaxCapacity:
|
||||
!FindInMap [webclusterEnvScalingConfigurationMap, All, DBMaxCapacity]
|
||||
webclusterDBWriterInstance:
|
||||
Metadata:
|
||||
"aws:copilot:description": "The webcluster Aurora Serverless v2 writer instance"
|
||||
Type: "AWS::RDS::DBInstance"
|
||||
Properties:
|
||||
DBClusterIdentifier: !Ref webclusterDBCluster
|
||||
DBInstanceClass: db.serverless
|
||||
Engine: "aurora-postgresql"
|
||||
PromotionTier: 1
|
||||
AvailabilityZone: !Select
|
||||
- 0
|
||||
- !GetAZs
|
||||
Ref: AWS::Region
|
||||
|
||||
webclusterSecretAuroraClusterAttachment:
|
||||
Type: AWS::SecretsManager::SecretTargetAttachment
|
||||
Properties:
|
||||
SecretId: !Ref webclusterAuroraSecret
|
||||
TargetId: !Ref webclusterDBCluster
|
||||
TargetType: AWS::RDS::DBCluster
|
||||
Outputs:
|
||||
webclusterSecret: # injected as WEBCLUSTER_SECRET environment variable by Copilot.
|
||||
Description: "The JSON secret that holds the database username and password. Fields are 'host', 'port', 'dbname', 'username', 'password', 'dbClusterIdentifier' and 'engine'"
|
||||
Value: !Ref webclusterAuroraSecret
|
||||
webclusterSecurityGroup:
|
||||
Description: "The security group to attach to the workload."
|
||||
Value: !Ref webclusterSecurityGroup
|
||||
@@ -0,0 +1,41 @@
|
||||
# The manifest for the "web" service.
|
||||
# Read the full specification for the "Load Balanced Web Service" type at:
|
||||
# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/
|
||||
|
||||
name: web
|
||||
type: Load Balanced Web Service
|
||||
|
||||
http:
|
||||
path: "/"
|
||||
|
||||
image:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.website
|
||||
context: ./
|
||||
port: 3000
|
||||
|
||||
cpu: 256
|
||||
memory: 512
|
||||
platform: linux/amd64
|
||||
count: 1
|
||||
exec: true
|
||||
network:
|
||||
connect: true
|
||||
|
||||
environments:
|
||||
staging:
|
||||
variables:
|
||||
NEXTAUTH_URL: https://web.staging.open-assistant.surfacedata.org
|
||||
|
||||
secrets:
|
||||
DATABASE_URL: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/DATABASE_URL
|
||||
DISCORD_CLIENT_ID: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/DISCORD_CLIENT_ID
|
||||
DISCORD_CLIENT_SECRET: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/DISCORD_CLIENT_SECRET
|
||||
EMAIL_SERVER_HOST: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_SERVER_HOST
|
||||
EMAIL_SERVER_PASSWORD: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_SERVER_PASSWORD
|
||||
EMAIL_SERVER_PORT: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_SERVER_PORT
|
||||
EMAIL_SERVER_USER: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_SERVER_USER
|
||||
EMAIL_FROM: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/EMAIL_FROM
|
||||
FASTAPI_KEY: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/FASTAPI_KEY
|
||||
FASTAPI_URL: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/FASTAPI_URL
|
||||
NEXTAUTH_SECRET: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/NEXTAUTH_SECRET
|
||||
@@ -0,0 +1,100 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
# Use `docker compose up backend-dev` to start a database and work and the backend.
|
||||
backend-dev:
|
||||
image: tianon/true
|
||||
depends_on: [db, adminer]
|
||||
|
||||
# Use `docker compose up frontend-dev` to start all services needed to work on the frontend.
|
||||
frontend-dev:
|
||||
image: tianon/true
|
||||
depends_on: [db, webdb, adminer, maildev, backend]
|
||||
|
||||
# This DB is for the FastAPI Backend.
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# This DB is for Web Authentication and data caching.
|
||||
webdb:
|
||||
image: postgres
|
||||
restart: always
|
||||
ports:
|
||||
- 5433:5432
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: oasst_web
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# This lets you manually inspect the web and backend databases.
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
ports:
|
||||
- 8089:8080
|
||||
|
||||
# This fakes an SMTP email server used by website authentication.
|
||||
# User registration emails can be found by going to localhost:1080 and
|
||||
# opening the emails listed.
|
||||
maildev:
|
||||
image: maildev/maildev
|
||||
restart: always
|
||||
environment:
|
||||
- MAILDEV_WEB_PORT=1080
|
||||
- MAILDEV_SMTP_PORT=1025
|
||||
ports:
|
||||
- "1080:1080"
|
||||
- "1025:1025"
|
||||
|
||||
# The oassist backend service.
|
||||
backend:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.backend
|
||||
image: oasst-backend
|
||||
environment:
|
||||
- POSTGRES_HOST=db
|
||||
- DEBUG_SKIP_API_KEY_CHECK=True
|
||||
- MAX_WORKERS=1
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
# The oassist web service.
|
||||
web:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.website
|
||||
image: oasst-web
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:postgres@webdb/oasst_web
|
||||
- FASTAPI_URL=http://backend:8080
|
||||
- FASTAPI_KEY=1234
|
||||
- NEXTAUTH_SECRET=O/M2uIbGj+lDD2oyNa8ax4jEOJqCPJzO53UbWShmq98=
|
||||
- EMAIL_SERVER_HOST=maildev
|
||||
- EMAIL_SERVER_PORT=1025
|
||||
- EMAIL_FROM=info@example.com
|
||||
- NEXTAUTH_URL=http://localhost:3000
|
||||
depends_on:
|
||||
webdb:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: bash wait-for-postgres.sh node server.js
|
||||
@@ -0,0 +1,10 @@
|
||||
# This docker file will update the web database with schema changes. Later it
|
||||
# will be updated to do prisma migrations.
|
||||
|
||||
FROM --platform=linux/amd64 node:16.18.0 AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./website/ .
|
||||
|
||||
CMD ["npx", "prisma", "db", "push"]
|
||||
@@ -1,7 +1,8 @@
|
||||
# Note: node:16.18.0 works most reliably when using different platforms (namely
|
||||
# Mac M1) and avoids recent Prisma docker bugs that lead to segfaults.
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM node:16.19 AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
# RUN apk add --no-cache libc6-compat
|
||||
FROM node:16.18.0 AS deps
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
@@ -12,27 +13,19 @@ RUN \
|
||||
fi
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM node:16.19 AS builder
|
||||
FROM node:16.18.0 AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY ./website/ .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage.
|
||||
# Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# RUN yarn build
|
||||
RUN npx prisma generate
|
||||
RUN npm run build
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM node:16.19 AS runner
|
||||
FROM node:16.18.0 AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
@@ -48,8 +41,6 @@ COPY --chown=nextjs:nodejs ./website/prisma/schema.prisma ./
|
||||
# https://docs.docker.com/compose/startup-order/
|
||||
COPY --chown=nextjs:nodejs ./website/wait-for-postgres.sh ./
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 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`.
|
||||
In root directory, run `docker compose up backend-dev` 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 and `pip install -e .` inside the `oasst-shared` folder.
|
||||
Then, run the backend using the `run-local.sh` script. This will start the backend server at `http://localhost:8080`.
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
ports:
|
||||
- 8089:8080
|
||||
@@ -1,14 +0,0 @@
|
||||
# End to End Demo
|
||||
|
||||
This sets up an entire stack needed to run Open Assistant, including the
|
||||
website, backend, and associated dependent services.
|
||||
|
||||
To start the service, do the following:
|
||||
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Then, navigate to `http://localhost:3000` and interact with the website. When
|
||||
logging in, navigate to `http://localhost:1080` to get the magic email login
|
||||
link.
|
||||
@@ -1,56 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
# This DB is for the FastAPI Backend.
|
||||
db:
|
||||
extends:
|
||||
file: ../frontend-development/docker-compose.yaml
|
||||
service: db
|
||||
|
||||
# This DB is for Web Authentication and data caching.
|
||||
webdb:
|
||||
extends:
|
||||
file: ../frontend-development/docker-compose.yaml
|
||||
service: webdb
|
||||
|
||||
# This lets you manually inspect the web and backend databases.
|
||||
adminer:
|
||||
extends:
|
||||
file: ../frontend-development/docker-compose.yaml
|
||||
service: adminer
|
||||
|
||||
# This fakes an SMTP email server used by website authentication.
|
||||
# User registration emails can be found by going to localhost:1080 and
|
||||
# opening the emails listed.
|
||||
maildev:
|
||||
extends:
|
||||
file: ../frontend-development/docker-compose.yaml
|
||||
service: maildev
|
||||
|
||||
# The oassist backend service.
|
||||
backend:
|
||||
extends:
|
||||
file: ../frontend-development/docker-compose.yaml
|
||||
service: backend
|
||||
|
||||
# The oassist web service.
|
||||
web:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.website
|
||||
context: ../../
|
||||
image: oasst-web
|
||||
environment:
|
||||
- DATABASE_URL=postgres://postgres:postgres@webdb/ocgpt_website
|
||||
- FASTAPI_URL=http://backend:8080
|
||||
- FASTAPI_KEY=1234
|
||||
- NEXTAUTH_SECRET=O/M2uIbGj+lDD2oyNa8ax4jEOJqCPJzO53UbWShmq98=
|
||||
- EMAIL_SERVER_HOST=maildev
|
||||
- EMAIL_SERVER_PORT=1025
|
||||
- EMAIL_FROM=info@example.com
|
||||
- NEXTAUTH_URL=http://localhost:3000
|
||||
depends_on:
|
||||
webdb:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: bash wait-for-postgres.sh node server.js
|
||||
@@ -1,5 +1,5 @@
|
||||
# Frontend Development Setup
|
||||
|
||||
Run `docker compose up --build` to start a database and the backend server.
|
||||
In root directory run `docker compose up frontend-dev --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.
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
# This DB is for the FastAPI Backend.
|
||||
db:
|
||||
extends:
|
||||
file: ../backend-development/docker-compose.yaml
|
||||
service: db
|
||||
|
||||
# This DB is for Web Authentication and data caching.
|
||||
webdb:
|
||||
image: postgres
|
||||
restart: always
|
||||
ports:
|
||||
- 5433:5432
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: ocgpt_website
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres"]
|
||||
interval: 2s
|
||||
timeout: 2s
|
||||
retries: 10
|
||||
|
||||
# This lets you manually inspect the web and backend databases.
|
||||
adminer:
|
||||
extends:
|
||||
file: ../backend-development/docker-compose.yaml
|
||||
service: adminer
|
||||
|
||||
backend:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.backend
|
||||
context: ../..
|
||||
image: oasst-backend
|
||||
environment:
|
||||
- POSTGRES_HOST=db
|
||||
- DEBUG_SKIP_API_KEY_CHECK=True
|
||||
- MAX_WORKERS=1
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
# This fakes an SMTP email server used by website authentication.
|
||||
# User registration emails can be found by going to localhost:1080 and
|
||||
# opening the emails listed.
|
||||
maildev:
|
||||
image: maildev/maildev
|
||||
restart: always
|
||||
environment:
|
||||
- MAILDEV_WEB_PORT=1080
|
||||
- MAILDEV_SMTP_PORT=1025
|
||||
ports:
|
||||
- "1080:1080"
|
||||
- "1025:1025"
|
||||
@@ -2,7 +2,7 @@
|
||||
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||
|
||||
# switch to bot directory
|
||||
pushd "$parent_path/../../bot"
|
||||
pushd "$parent_path/../../discord-bot"
|
||||
|
||||
python3 __main__.py
|
||||
|
||||
|
||||
@@ -113,6 +113,20 @@ When writing code for the website, we have a few best practices:
|
||||
1. Define functional React components (with types for all properties when
|
||||
feasible).
|
||||
|
||||
### URL Paths
|
||||
|
||||
To use stable and consistent URL paths, we recommend the following strategy for new tasks:
|
||||
|
||||
1. For any task that involves writing a free-form response, put the page under
|
||||
`website/src/pages/create` with a page name matching the task type, such as
|
||||
`summarize_story.tsx`.
|
||||
1. For any task that evaluates, rates, or ranks content, put the page under
|
||||
`website/src/pages/evaluate` with a page name matching the task type such
|
||||
as `rate_summary.tsx`.
|
||||
|
||||
With this we'll be able to ensure these contribution pages are hidden from
|
||||
logged out users but accessible to logged in users.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
Generated
+678
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"sharp": "^0.31.3",
|
||||
"swr": "^2.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"use-debounce": "^9.0.2"
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
-- 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;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "labelerId" INTEGER;
|
||||
@@ -1,3 +0,0 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
@@ -5,6 +5,7 @@ datasource db {
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["native", "linux-musl"]
|
||||
}
|
||||
|
||||
model Account {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
export function AuthLayout({ children }) {
|
||||
return (
|
||||
<main className="flex items-center justify-center min-h-full overflow-hidden pt-16 sm:py-28 subpixel-antialiased">
|
||||
<main className="flex items-center justify-center sm:py-4 subpixel-antialiased">
|
||||
<div className="flex items-center w-full max-w-2xl flex-col px-4 sm:px-6">
|
||||
<Link href="/" aria-label="Home" className="flex items-center text-3xl font-bold text-black">
|
||||
<Image src="/images/logos/logo.svg" width="100" height="100" alt="Open Assistant Logo" /> Open Assistant
|
||||
</Link>
|
||||
<div className="flex-auto items-center justify-center w-full py-10 px-4 sm:mx-0 sm:flex-none sm:rounded-2xl sm:p-20">
|
||||
<div className="flex-auto items-center justify-center w-full py-10 px-4 sm:mx-0 sm:flex-none sm:rounded-2xl sm:p-4">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,9 +55,9 @@ function AccountButton() {
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header>
|
||||
<header className="bg-white">
|
||||
<nav>
|
||||
<Container className="relative bg-white z-10 flex justify-between py-8">
|
||||
<Container className="relative z-10 flex justify-between py-8">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link href="/" aria-label="Home" className="flex items-center">
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// https://nextjs.org/docs/basic-features/layouts
|
||||
|
||||
import type { NextPage } from "next";
|
||||
|
||||
import { Footer } from "./Footer";
|
||||
import { Header } from "./Header";
|
||||
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (page: React.ReactElement) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const getDefaultLayout = (page: React.ReactElement) => (
|
||||
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
|
||||
<Header />
|
||||
{page}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const noLayout = (page: React.ReactElement) => page;
|
||||
@@ -1,8 +1,7 @@
|
||||
import { chakra, Box, HStack, useRadio, useRadioGroup } from "@chakra-ui/react";
|
||||
import { Box, HStack, useRadio, useRadioGroup } from "@chakra-ui/react";
|
||||
|
||||
const RatingRadioButton = (props) => {
|
||||
const { option, ...radioProps } = props;
|
||||
const { getInputProps, getCheckboxProps } = useRadio(radioProps);
|
||||
const { state, getInputProps, getCheckboxProps } = useRadio(props);
|
||||
|
||||
const input = getInputProps();
|
||||
const checkbox = getCheckboxProps();
|
||||
@@ -27,25 +26,33 @@ const RatingRadioButton = (props) => {
|
||||
px={5}
|
||||
py={3}
|
||||
>
|
||||
{option}
|
||||
{props.children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const RatingRadioGroup = (props) => {
|
||||
const { min, max, onChange, ...rest } = props;
|
||||
const { value, getRadioProps, getRootProps } = useRadioGroup({
|
||||
defaultValue: min,
|
||||
const { min, max, onChange } = props;
|
||||
const { getRadioProps, getRootProps } = useRadioGroup({
|
||||
name: "rating",
|
||||
defaultValue: `${min}`,
|
||||
onChange: onChange,
|
||||
});
|
||||
const options = Array.from(new Array(1 + max - min), (x, i) => i + min);
|
||||
const group = getRootProps();
|
||||
|
||||
const options = Array.from(new Array(1 + max - min), (x, i) => `${i + min}`);
|
||||
|
||||
return (
|
||||
<HStack {...getRootProps()}>
|
||||
{options.map((option) => (
|
||||
<RatingRadioButton key={option} option={option} {...getRadioProps({ value: option })} />
|
||||
))}
|
||||
<HStack {...group}>
|
||||
{options.map((option) => {
|
||||
const radio = getRadioProps({ value: option });
|
||||
return (
|
||||
<RatingRadioButton key={option} {...radio}>
|
||||
{option}
|
||||
</RatingRadioButton>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ export const TaskSelection = () => {
|
||||
alt="Summarize Stories"
|
||||
img="/images/logos/logo.svg"
|
||||
title="Summarize stories"
|
||||
link="/summarize/story"
|
||||
link="/create/summarize_story"
|
||||
/>
|
||||
<TaskOption alt="Reply as User" img="/images/logos/logo.svg" title="Reply as User" link="/create/user_reply" />
|
||||
<TaskOption
|
||||
@@ -22,7 +22,18 @@ export const TaskSelection = () => {
|
||||
/>
|
||||
</TaskOptions>
|
||||
<TaskOptions key="evaluate" title="Evaluate">
|
||||
<TaskOption alt="Rate Prompts" img="/images/logos/logo.svg" title="Rate Prompts" link="/grading/grade-output" />
|
||||
<TaskOption
|
||||
alt="Rate Prompts"
|
||||
img="/images/logos/logo.svg"
|
||||
title="Rate Prompts"
|
||||
link="/evaluate/rate_summary"
|
||||
/>
|
||||
<TaskOption
|
||||
alt="Rank Initial Prompts"
|
||||
img="/images/logos/logo.svg"
|
||||
title="Rank Initial Prompts"
|
||||
link="/evaluate/rank_initial_prompts"
|
||||
/>
|
||||
</TaskOptions>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,11 @@ import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html className="h-full bg-gray-50 antialiased" lang="en">
|
||||
<Html className="h-full antialiased" lang="en">
|
||||
<Head>
|
||||
<link rel="shortcut icon" type="image/png" href="/images/logos/favicon.png" />
|
||||
</Head>
|
||||
<body className="flex h-full flex-col">
|
||||
<body className="flex h-full flex-col bg-gray-50">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
||||
@@ -43,12 +43,12 @@ const AssistantReply = () => {
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">Loading...</div>;
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
|
||||
const task = tasks[0].task;
|
||||
return (
|
||||
<div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
<div className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Reply as the assistant</h5>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useRef, useState } from "react";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { Button } from "src/components/Button";
|
||||
|
||||
const SummarizeStory = () => {
|
||||
// Use an array of tasks that record the sequence of steps until a task is
|
||||
// deemed complete.
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// 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/summarize_story", 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", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
// This is the more efficient way to update a react state array.
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
// Trigger a mutation that updates the current task. We should probably
|
||||
// signal somewhere that this interaction is being processed.
|
||||
const submitResponse = (task: { id: string }) => {
|
||||
const text = inputRef.current.value.trim();
|
||||
trigger({
|
||||
id: task.id,
|
||||
update_type: "text_reply_to_post",
|
||||
content: {
|
||||
text,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className=" p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Summarize A Story</title>
|
||||
<meta name="description" content="Summarize a story to train our model." />
|
||||
</Head>
|
||||
<main className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Instruction</h5>
|
||||
<p className="text-lg py-1">Summarize the following story</p>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.story}</div>
|
||||
</>
|
||||
<Textarea name="summary" placeholder="Summary" ref={inputRef} />
|
||||
</TwoColumns>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
|
||||
<b>Prompt</b>
|
||||
<span>{tasks[0].id}</span>
|
||||
<b>Output</b>
|
||||
<span>Submit your answer</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center ml-auto">
|
||||
<Button className="mr-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200">Skip</Button>
|
||||
<Button
|
||||
onClick={() => submitResponse(tasks[0])}
|
||||
className="bg-indigo-600 text-white shadow-sm hover:bg-indigo-700"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SummarizeStory;
|
||||
@@ -43,12 +43,12 @@ const UserReply = () => {
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">Loading...</div>;
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
|
||||
const task = tasks[0].task;
|
||||
return (
|
||||
<div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
<div className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Reply as a user</h5>
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { ArrowUpIcon, ArrowDownIcon } from "@heroicons/react/20/solid";
|
||||
import clsx from "clsx";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
|
||||
import { Button } from "src/components/Button";
|
||||
|
||||
const RankInitialPrompts = () => {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
/**
|
||||
* This array will contain the ranked indices of the prompts
|
||||
* The best prompt will have index 0, and the worst is the last.
|
||||
*/
|
||||
const [ranking, setRanking] = useState<number[]>([]);
|
||||
|
||||
const { isLoading } = useSWRImmutable("/api/new_task/rank_initial_prompts", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
setTasks([data]);
|
||||
|
||||
const indices = Array.from({ length: data.task.prompts.length }).map((_, i) => i);
|
||||
setRanking(indices);
|
||||
},
|
||||
});
|
||||
|
||||
const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
const submitResponse = (task) => {
|
||||
trigger({
|
||||
id: task.id,
|
||||
update_type: "post_ranking",
|
||||
content: {
|
||||
ranking,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
const prompts = tasks[0].task.prompts as string[];
|
||||
const items = ranking.map((i) => ({
|
||||
text: prompts[i],
|
||||
originalIndex: i,
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Rank Initial Prompts</title>
|
||||
<meta name="description" content="Rank initial prompts." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
|
||||
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
|
||||
<p className="text-lg py-1">
|
||||
Given the following prompts, sort them from best to worst, best being first, worst being last.
|
||||
</p>
|
||||
<ul className="flex flex-col gap-4">
|
||||
{items.map(({ text, originalIndex }, i) => (
|
||||
<SortableItem
|
||||
key={`${originalIndex}_${i}`}
|
||||
canIncrement={i > 0}
|
||||
onIncrement={() => {
|
||||
const newRanking = ranking.slice();
|
||||
const newIdx = i - 1;
|
||||
[newRanking[i], newRanking[newIdx]] = [newRanking[newIdx], newRanking[i]];
|
||||
setRanking(newRanking);
|
||||
}}
|
||||
canDecrement={i < items.length - 1}
|
||||
onDecrement={() => {
|
||||
const newRanking = ranking.slice();
|
||||
const newIdx = i + 1;
|
||||
[newRanking[i], newRanking[newIdx]] = [newRanking[newIdx], newRanking[i]];
|
||||
setRanking(newRanking);
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</SortableItem>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
|
||||
<b>Prompt</b>
|
||||
<span>{tasks[0].id}</span>
|
||||
<b>Output</b>
|
||||
<span>Submit your answer</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center ml-auto">
|
||||
<Button className="mr-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200">Skip</Button>
|
||||
<Button
|
||||
onClick={() => submitResponse(tasks[0])}
|
||||
disabled={ranking.length === 0}
|
||||
className="bg-indigo-600 text-white shadow-sm hover:bg-indigo-700"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RankInitialPrompts;
|
||||
|
||||
const SortableItem = ({ canIncrement, canDecrement, onIncrement, onDecrement, children, ...props }) => {
|
||||
return (
|
||||
<li className="grid grid-cols-[min-content_1fr] items-center rounded-lg shadow-md gap-x-2 p-2">
|
||||
<ArrowButton active={canIncrement} onClick={onIncrement}>
|
||||
<ArrowUpIcon width={28} />
|
||||
</ArrowButton>
|
||||
<span style={{ gridRow: "span 2" }}>{children}</span>
|
||||
|
||||
<ArrowButton active={canDecrement} onClick={onDecrement}>
|
||||
<ArrowDownIcon width={28} />
|
||||
</ArrowButton>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const ArrowButton = ({ children, active, onClick }) => {
|
||||
return (
|
||||
<Button
|
||||
className={clsx("justify-center", active ? "hover:bg-indigo-200" : "opacity-10")}
|
||||
onClick={onClick}
|
||||
disabled={!active}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,187 @@
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
|
||||
import Head from "next/head";
|
||||
import { useState } from "react";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
import RatingRadioGroup from "src/components/RatingRadioGroup";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
|
||||
import { TwoColumns } from "src/components/TwoColumns";
|
||||
import { Button } from "src/components/Button";
|
||||
|
||||
const RateSummary = () => {
|
||||
// Use an array of tasks that record the sequence of steps until a task is
|
||||
// deemed complete.
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [rating, setRating] = useState(0);
|
||||
|
||||
// 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/rate_summary", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
console.log(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", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
// This is the more efficient way to update a react state array.
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
// Trigger a mutation that updates the current task. We should probably
|
||||
// signal somewhere that this interaction is being processed.
|
||||
const submitResponse = (t) => {
|
||||
trigger({
|
||||
id: t.id,
|
||||
content: {
|
||||
rating: rating,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Rate A Summary</title>
|
||||
<meta name="description" content="Rate a proposed story summary." />
|
||||
</Head>
|
||||
<main className="p-6 bg-slate-100 text-gray-800">
|
||||
<TwoColumns>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold mb-4">Instruction</h5>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.full_text}</div>
|
||||
</>
|
||||
|
||||
<section className="grid grid-row-[auto] gap-3">
|
||||
<h5 className="text-lg font-semibold">Output</h5>
|
||||
<p className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.summary}</p>
|
||||
<h3 className="text-lg text-center font-medium leading-6 text-gray-900">Rating</h3>
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
({tasks[0].task.scale.min} = worst, {tasks[0].task.scale.max} = best)
|
||||
</p>
|
||||
<div className="m-auto">
|
||||
<RatingRadioGroup min={tasks[0].task.scale.min} max={tasks[0].task.scale.max} onChange={setRating} />
|
||||
</div>
|
||||
<ul>
|
||||
{ANNOTATION_FLAGS.map((option, i) => (
|
||||
<AnnotationCheckboxLi option={option} key={i} />
|
||||
))}
|
||||
</ul>
|
||||
<Textarea name="notes" placeholder="Optional notes" />
|
||||
</section>
|
||||
</TwoColumns>
|
||||
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
|
||||
<b>Prompt</b>
|
||||
<span>{tasks[0].id}</span>
|
||||
<b>Output</b>
|
||||
<span>Submit your answer</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center ml-auto">
|
||||
<Button className="mr-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200">Skip</Button>
|
||||
<Button
|
||||
onClick={() => submitResponse(tasks[0])}
|
||||
className="bg-indigo-600 text-white shadow-sm hover:bg-indigo-700"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RateSummary;
|
||||
|
||||
function AnnotationCheckboxLi(props: { option: annotationBool }): JSX.Element {
|
||||
let AdditionalExplanation = null;
|
||||
if (props.option.additionalExplanation) {
|
||||
AdditionalExplanation = (
|
||||
<a href="#" className="group flex items-center space-x-2.5 text-sm ">
|
||||
<QuestionMarkCircleIcon className="h-5 w-5 ml-3 text-gray-400 group-hover:text-gray-500" aria-hidden="true" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li className="form-check flex mb-1">
|
||||
<input
|
||||
className="form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer"
|
||||
type="checkbox"
|
||||
value=""
|
||||
id={props.option.attributeName}
|
||||
/>
|
||||
<label className="flex ml-1 form-check-label hover:cursor-pointer" htmlFor={props.option.attributeName}>
|
||||
<span className="text-gray-800 hover:text-blue-700">{props.option.labelText}</span>
|
||||
{AdditionalExplanation}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
interface annotationBool {
|
||||
attributeName: string;
|
||||
labelText: string;
|
||||
additionalExplanation?: string;
|
||||
}
|
||||
|
||||
const ANNOTATION_FLAGS: annotationBool[] = [
|
||||
// For the time being this list is configured on the FE.
|
||||
// In the future it may be provided by the API.
|
||||
{
|
||||
attributeName: "fails_task",
|
||||
labelText: "Fails to follow the correct instruction / task",
|
||||
additionalExplanation: "__TODO__",
|
||||
},
|
||||
{
|
||||
attributeName: "not_customer_assistant_appropriate",
|
||||
labelText: "Inappropriate for customer assistant",
|
||||
additionalExplanation: "__TODO__",
|
||||
},
|
||||
{
|
||||
attributeName: "contains_sexual_content",
|
||||
labelText: "Contains sexual content",
|
||||
},
|
||||
{
|
||||
attributeName: "contains_violent_content",
|
||||
labelText: "Contains violent content",
|
||||
},
|
||||
{
|
||||
attributeName: "encourages_violence",
|
||||
labelText: "Encourages or fails to discourage violence/abuse/terrorism/self-harm",
|
||||
},
|
||||
{
|
||||
attributeName: "denigrates_a_protected_class",
|
||||
labelText: "Denigrates a protected class",
|
||||
},
|
||||
{
|
||||
attributeName: "gives_harmful_advice",
|
||||
labelText: "Fails to follow the correct instruction / task",
|
||||
additionalExplanation:
|
||||
"The advice given in the output is harmful or counter-productive. This may be in addition to, but is distinct from the question about encouraging violence/abuse/terrorism/self-harm.",
|
||||
},
|
||||
{
|
||||
attributeName: "expresses_moral_judgement",
|
||||
labelText: "Expresses moral judgement",
|
||||
},
|
||||
];
|
||||
@@ -1,222 +0,0 @@
|
||||
import { HStack, Textarea } from "@chakra-ui/react";
|
||||
import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
|
||||
import { useSession, signIn, signOut } from "next-auth/react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
import RatingRadioGroup from "src/components/RatingRadioGroup";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
|
||||
const GradeOutput = () => {
|
||||
// Use an array of tasks that record the sequence of steps until a task is
|
||||
// deemed complete.
|
||||
const [tasks, setTasks] = useState([]);
|
||||
const [rating, setRating] = useState(0);
|
||||
|
||||
// A quick reference to the input element. This should be factored into the
|
||||
// component doing the actual task rendering.
|
||||
const responseEl = useRef(null);
|
||||
|
||||
// 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/rate_summary", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
console.log(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", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
// This is the more efficient way to update a react state array.
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
// Trigger a mutation that updates the current task. We should probably
|
||||
// signal somewhere that this interaction is being processed.
|
||||
const submitResponse = (t) => {
|
||||
trigger({
|
||||
id: t.id,
|
||||
content: {
|
||||
rating: rating,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800"></div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
{/* Instrunction and Output panels */}
|
||||
<section className="mb-8 lt-lg:mb-12 ">
|
||||
<div className="grid lg:gap-x-12 lg:grid-cols-2">
|
||||
{/* Instruction panel */}
|
||||
<div className="rounded-lg shadow-lg h-full block bg-white">
|
||||
<div className="p-6">
|
||||
<h5 className="text-lg font-semibold mb-4">Instruction</h5>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">
|
||||
{tasks[0].task.full_text}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Output panel */}
|
||||
<div className="mt-6 lg:mt-0 rounded-lg shadow-lg h-full block bg-white">
|
||||
<div className="p-6">
|
||||
<h5 className="text-lg font-semibold mb-4">Output</h5>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.summary}</div>
|
||||
</div>
|
||||
{/* Form wrap*/}
|
||||
<div className="p-6">
|
||||
<h3 className="text-lg text-center font-medium leading-6 text-gray-900">Rating</h3>
|
||||
<p className="text-center mt-1 text-sm text-gray-500">
|
||||
({tasks[0].task.scale.min} = worst, {tasks[0].task.scale.max} = best)
|
||||
</p>
|
||||
|
||||
{/* Rating buttons */}
|
||||
<div className="flex justify-center p-6">
|
||||
<RatingRadioGroup min={tasks[0].task.scale.min} max={tasks[0].task.scale.max} onChange={setRating} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Annotation checkboxes */}
|
||||
<div className="flex justify-center px-10">
|
||||
<ul>
|
||||
{ANNOTATION_FLAGS.map((option, i) => {
|
||||
return <AnnotationCheckboxLi option={option} key={i}></AnnotationCheckboxLi>;
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex justify-center p-6">
|
||||
<Textarea name="notes" placeholder="Optional notes" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Info & controls */}
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<div className="flex flex-col justify-self-start text-gray-700">
|
||||
<div>
|
||||
<span>
|
||||
<b>Prompt</b>
|
||||
</span>
|
||||
<span className="ml-2">{tasks[0].id}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<b>Output</b>
|
||||
</span>
|
||||
<span className="ml-2">{tasks.length === 2 ? tasks[1].id : "Submit your answer"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skip / Submit controls */}
|
||||
<div className="flex justify-center ml-auto">
|
||||
<button
|
||||
type="button"
|
||||
className="mr-2 inline-flex items-center rounded-md border border-transparent bg-indigo-100 px-4 py-2 text-sm font-medium text-indigo-700 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => submitResponse(tasks[0])}
|
||||
className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GradeOutput;
|
||||
|
||||
function AnnotationCheckboxLi(props: { option: annotationBool }): JSX.Element {
|
||||
let AdditionalExplanation = null;
|
||||
if (props.option.additionalExplanation) {
|
||||
AdditionalExplanation = (
|
||||
<a href="#" className="group flex items-center space-x-2.5 text-sm ">
|
||||
<QuestionMarkCircleIcon className="h-5 w-5 ml-3 text-gray-400 group-hover:text-gray-500" aria-hidden="true" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className="form-check flex mb-1">
|
||||
<input
|
||||
className="form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer"
|
||||
type="checkbox"
|
||||
value=""
|
||||
id={props.option.attributeName}
|
||||
/>
|
||||
<label className="flex ml-1 form-check-label hover:cursor-pointer" htmlFor={props.option.attributeName}>
|
||||
<span className="text-gray-800 hover:text-blue-700">{props.option.labelText}</span>
|
||||
{AdditionalExplanation}
|
||||
</label>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface annotationBool {
|
||||
attributeName: string;
|
||||
labelText: string;
|
||||
additionalExplanation?: string;
|
||||
}
|
||||
|
||||
const ANNOTATION_FLAGS: annotationBool[] = [
|
||||
// For the time being this list is configured on the FE.
|
||||
// In the future it may be provided by the API.
|
||||
{
|
||||
attributeName: "fails_task",
|
||||
labelText: "Fails to follow the correct instruction / task",
|
||||
additionalExplanation: "__TODO__",
|
||||
},
|
||||
{
|
||||
attributeName: "not_customer_assistant_appropriate",
|
||||
labelText: "Inappropriate for customer assistant",
|
||||
additionalExplanation: "__TODO__",
|
||||
},
|
||||
{
|
||||
attributeName: "contains_sexual_content",
|
||||
labelText: "Contains sexual content",
|
||||
},
|
||||
{
|
||||
attributeName: "contains_violent_content",
|
||||
labelText: "Contains violent content",
|
||||
},
|
||||
{
|
||||
attributeName: "encourages_violence",
|
||||
labelText: "Encourages or fails to discourage violence/abuse/terrorism/self-harm",
|
||||
},
|
||||
{
|
||||
attributeName: "denigrates_a_protected_class",
|
||||
labelText: "Denigrates a protected class",
|
||||
},
|
||||
{
|
||||
attributeName: "gives_harmful_advice",
|
||||
labelText: "Fails to follow the correct instruction / task",
|
||||
additionalExplanation:
|
||||
"The advice given in the output is harmful or counter-productive. This may be in addition to, but is distinct from the question about encouraging violence/abuse/terrorism/self-harm.",
|
||||
},
|
||||
{
|
||||
attributeName: "expresses_moral_judgement",
|
||||
labelText: "Expresses moral judgement",
|
||||
},
|
||||
];
|
||||
@@ -6,7 +6,7 @@ import { HiBarsArrowUp, HiBarsArrowDown } from "react-icons/hi2";
|
||||
const LeaderBoard = () => {
|
||||
const PlaceHolderProps = { username: "test_user", score: 10 };
|
||||
return (
|
||||
<div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
<div className="p-6 bg-slate-100 text-gray-800">
|
||||
<div className="flex flex-col">
|
||||
<div className="rounded-lg shadow-lg h-full block bg-white">
|
||||
<div className="p-8">
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
// TODO(#65): Unify and simplify the task paths
|
||||
import { Textarea } from "@chakra-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
|
||||
const SummarizeStory = () => {
|
||||
// Use an array of tasks that record the sequence of steps until a task is
|
||||
// deemed complete.
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// 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/summarize_story", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
console.log(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", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
// This is the more efficient way to update a react state array.
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
// Trigger a mutation that updates the current task. We should probably
|
||||
// signal somewhere that this interaction is being processed.
|
||||
const submitResponse = (task: { id: string }) => {
|
||||
const text = inputRef.current.value.trim();
|
||||
trigger({
|
||||
id: task.id,
|
||||
content: {
|
||||
update_type: "text_reply_to_post",
|
||||
text,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Make this a nicer loading screen.
|
||||
*/
|
||||
if (tasks.length == 0) {
|
||||
return <div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800">Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800">
|
||||
{/* Instrunction and Output panels */}
|
||||
<section className="mb-8 lt-lg:mb-12 ">
|
||||
<div className="grid lg:gap-x-12 lg:grid-cols-2">
|
||||
{/* Instruction panel */}
|
||||
<div className="rounded-lg shadow-lg h-full block bg-white">
|
||||
<div className="p-6">
|
||||
<h5 className="text-lg font-semibold">Instruction</h5>
|
||||
<p className="text-lg py-1">Summarize the following story</p>
|
||||
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.story}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Output panel */}
|
||||
<div className="mt-6 lg:mt-0 rounded-lg shadow-lg h-full block bg-white">
|
||||
<div className="flex justify-center p-6">
|
||||
<Textarea name="summary" placeholder="Summary" ref={inputRef} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Info & controls */}
|
||||
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
|
||||
<div className="flex flex-col justify-self-start text-gray-700">
|
||||
<div>
|
||||
<span>
|
||||
<b>Prompt</b>
|
||||
</span>
|
||||
<span className="ml-2">{tasks[0].id}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<b>Output</b>
|
||||
</span>
|
||||
<span className="ml-2">Submit your answer</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Skip / Submit controls */}
|
||||
<div className="flex justify-center ml-auto">
|
||||
<button
|
||||
type="button"
|
||||
className="mr-2 inline-flex items-center rounded-md border border-transparent bg-indigo-100 px-4 py-2 text-sm font-medium text-indigo-700 hover:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => submitResponse(tasks[0])}
|
||||
className="inline-flex items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SummarizeStory;
|
||||
Reference in New Issue
Block a user