mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-30 16:40:05 +08:00
Merge branch 'main' of github.com:LAION-AI/Open-Chat-GPT
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# devcontainer
|
||||
|
||||
## example usage
|
||||
|
||||
Below are some example use cases you might want to run from within the
|
||||
devcontainer (either
|
||||
[within VSCode locally](https://code.visualstudio.com/docs/devcontainers/create-dev-container#_create-a-devcontainerjson-file)
|
||||
or in your browser via
|
||||
[GitHub Codespaces](https://github.com/features/codespaces)).
|
||||
|
||||
### Run pre-commit
|
||||
|
||||
```bash
|
||||
# run pre-commit
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
A successfull run should look something like this:
|
||||
|
||||
```
|
||||
@andrewm4894 ➜ /workspaces/Open-Assistant (devcontainer-improvements) $ pre-commit run --all-files
|
||||
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
|
||||
[INFO] Initializing environment for https://github.com/psf/black.
|
||||
[INFO] Initializing environment for https://github.com/psf/black:.[jupyter].
|
||||
[INFO] Initializing environment for https://github.com/pycqa/flake8.
|
||||
[INFO] Initializing environment for https://github.com/pycqa/isort.
|
||||
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-prettier.
|
||||
[INFO] Initializing environment for https://github.com/pre-commit/mirrors-prettier:prettier@2.7.1.
|
||||
[INFO] Initializing environment for local.
|
||||
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
[INFO] Installing environment for https://github.com/psf/black.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
[INFO] Installing environment for https://github.com/pycqa/flake8.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
[INFO] Installing environment for https://github.com/pycqa/isort.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
[INFO] Installing environment for https://github.com/pre-commit/mirrors-prettier.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
[INFO] Installing environment for local.
|
||||
[INFO] Once installed this environment will be reused.
|
||||
[INFO] This may take a few minutes...
|
||||
trim trailing whitespace.................................................Passed
|
||||
check python ast.........................................................Passed
|
||||
check yaml...............................................................Passed
|
||||
check json...............................................................Passed
|
||||
check for case conflicts.................................................Passed
|
||||
detect private key.......................................................Passed
|
||||
fix python encoding pragma...............................................Passed
|
||||
forbid submodules....................................(no files to check)Skipped
|
||||
mixed line ending........................................................Passed
|
||||
fix requirements.txt.....................................................Passed
|
||||
check that executables have shebangs.....................................Passed
|
||||
check that scripts with shebangs are executable..........................Passed
|
||||
check BOM - deprecated: use fix-byte-order-marker........................Passed
|
||||
check for broken symlinks............................(no files to check)Skipped
|
||||
check for merge conflicts................................................Passed
|
||||
check for added large files..............................................Passed
|
||||
fix end of files.........................................................Passed
|
||||
black-jupyter............................................................Passed
|
||||
flake8...................................................................Passed
|
||||
isort....................................................................Passed
|
||||
prettier.................................................................Passed
|
||||
Lint website.............................................................Passed
|
||||
```
|
||||
|
||||
### Docker compose
|
||||
|
||||
```bash
|
||||
# build the image
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
You should see some docker containers being pulled and activated.
|
||||
|
||||
Once you see a line like:
|
||||
|
||||
```
|
||||
open-assistant-web-1 | Listening on port 3000 url: http://localhost:3000
|
||||
```
|
||||
|
||||
you should be able to access that port like below:
|
||||
|
||||
<img width="640" alt="image" src="https://user-images.githubusercontent.com/2178292/210395676-e9c2aab5-cb54-4ae6-b1eb-ac929fd73607.png">
|
||||
|
||||
this port can then be forwarded to a browser tab like below:
|
||||
|
||||
<img width="640" alt="image" src="https://user-images.githubusercontent.com/2178292/210396207-1b2e259f-4d5d-475d-b225-91e2bd004071.png">
|
||||
@@ -1,7 +1,12 @@
|
||||
{
|
||||
"service": "frontend-dev",
|
||||
"dockerComposeFile": "../docker-compose.yaml",
|
||||
"forwardPorts": [3000],
|
||||
"name": "Open-Assistant",
|
||||
"image": "mcr.microsoft.com/vscode/devcontainers/universal",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/pre-commit:2": {
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "bash .devcontainer/post_create_command.sh",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["GitHub.copilot"]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# ensure pre-commit is installed
|
||||
pre-commit install
|
||||
@@ -26,10 +26,7 @@
|
||||
#
|
||||
# /WARNING!
|
||||
|
||||
exclude: "build|stubs|^bot/templates/|^notebooks/.*\\.ipynb$"
|
||||
|
||||
default_language_version:
|
||||
python: python3
|
||||
exclude: build|stubs|^bot/templates/$
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
@@ -42,12 +39,12 @@ repos:
|
||||
# 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/.*$"
|
||||
exclude: ^copilot/.*/addons/.*$
|
||||
- id: check-json
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- id: fix-encoding-pragma
|
||||
args: ["--remove"]
|
||||
args: [--remove]
|
||||
- id: forbid-submodules
|
||||
- id: mixed-line-ending
|
||||
- id: requirements-txt-fixer
|
||||
@@ -57,13 +54,13 @@ repos:
|
||||
- id: check-symlinks
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
args: ["--maxkb=1024"]
|
||||
args: [--maxkb=1024]
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
- id: black-jupyter
|
||||
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.0.0
|
||||
@@ -79,7 +76,7 @@ repos:
|
||||
rev: v2.7.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
args: ["--prose-wrap=always", "--write"]
|
||||
args: [--prose-wrap=always, --write]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
|
||||
@@ -26,6 +26,15 @@ interact with the website.
|
||||
**Note:** When logging in via email, navigate to `http://localhost:1080` to get
|
||||
the magic email login link.
|
||||
|
||||
**Note:** If you would like to run this in a standardized development
|
||||
environment (a
|
||||
["devcontainer"](https://code.visualstudio.com/docs/devcontainers/containers))
|
||||
using
|
||||
[vscode locally](https://code.visualstudio.com/docs/devcontainers/create-dev-container#_create-a-devcontainerjson-file)
|
||||
or in a web browser using
|
||||
[GitHub Codespaces](https://github.com/features/codespaces), you can use the
|
||||
provided [`.devcontainer`](.devcontainer/) folder.
|
||||
|
||||
## The Plan
|
||||
|
||||
We want to get to an initial MVP as fast as possible, by following the 3-steps
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# The manifest for the "api" 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: api
|
||||
type: Load Balanced Web Service
|
||||
|
||||
http:
|
||||
path: "/"
|
||||
healthcheck:
|
||||
path: "/docs"
|
||||
|
||||
image:
|
||||
build:
|
||||
dockerfile: docker/Dockerfile.backend
|
||||
context: ./
|
||||
port: 8080
|
||||
|
||||
cpu: 256
|
||||
memory: 512
|
||||
platform: linux/x86_64
|
||||
count: 1
|
||||
exec: true
|
||||
network:
|
||||
connect: true
|
||||
|
||||
environments:
|
||||
staging:
|
||||
variables:
|
||||
# Note: this has to be a valid JSON list for Pydantic to parse it.
|
||||
BACKEND_CORS_ORIGINS: '["https://web.staging.open-assistant.surfacedata.org"]'
|
||||
DEBUG_ALLOW_ANY_API_KEY: True
|
||||
DEBUG_SKIP_API_KEY_CHECK: True
|
||||
MAX_WORKERS: 1
|
||||
|
||||
secrets:
|
||||
# Note: URI, not URL.
|
||||
DATABASE_URI: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/API_DATABASE_URL
|
||||
@@ -1,161 +0,0 @@
|
||||
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
|
||||
@@ -26,6 +26,7 @@ environments:
|
||||
staging:
|
||||
variables:
|
||||
NEXTAUTH_URL: https://web.staging.open-assistant.surfacedata.org
|
||||
FASTAPI_URL: https://api.staging.open-assistant.surfacedata.org
|
||||
|
||||
secrets:
|
||||
DATABASE_URL: /copilot/${COPILOT_APPLICATION_NAME}/${COPILOT_ENVIRONMENT_NAME}/secrets/DATABASE_URL
|
||||
@@ -37,5 +38,4 @@ secrets:
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
BOT_TOKEN=<discord bot token>
|
||||
DECLARE_GLOBAL_COMMANDS=<testing guild id>
|
||||
OWNER_IDS=[<your user id>, <other user ids>]
|
||||
PREFIX="./"
|
||||
PREFIX="/" # DO NOT LEAVE EMPTY, slash command prefix in DMs
|
||||
|
||||
OASST_API_URL="http://localhost:8080" # No trailing '/'
|
||||
OASST_API_KEY=""
|
||||
|
||||
+13
-6
@@ -6,7 +6,7 @@ import hikari
|
||||
import lightbulb
|
||||
import miru
|
||||
from bot.settings import Settings
|
||||
from bot.utils import EMPTY, mention
|
||||
from bot.utils import mention
|
||||
from oasst_shared.api_client import OasstApiClient
|
||||
|
||||
settings = Settings()
|
||||
@@ -34,8 +34,11 @@ async def on_starting(event: hikari.StartingEvent):
|
||||
|
||||
bot.d.oasst_api = OasstApiClient(settings.oasst_api_url, settings.oasst_api_key)
|
||||
|
||||
# A set of user id's that are currently doing work.
|
||||
bot.d.currently_working = set()
|
||||
# A `dict[hikari.Message | None, UUID | None]]` that maps user IDs to (task msg ID, task UUIDs).
|
||||
# Either both are `None` or both are not `None`.
|
||||
# If both are `None`, the user is not currently selecting a task.
|
||||
# TODO: Grow this on startup so we don't have to re-allocate memory every time it needs to grow
|
||||
bot.d.currently_working = {}
|
||||
|
||||
|
||||
@bot.listen()
|
||||
@@ -50,13 +53,13 @@ async def _send_error_embed(
|
||||
) -> None:
|
||||
ctx.command
|
||||
embed = hikari.Embed(
|
||||
title=f"`{exception.__class__.__name__}` Error{f' in `{ctx.command.name}`' if ctx.command else '' }",
|
||||
title=f"`{exception.__class__.__name__}` Error{f' in `/{ctx.command.name}`' if ctx.command else '' }",
|
||||
description=content,
|
||||
color=0xFF0000,
|
||||
timestamp=datetime.now().astimezone(),
|
||||
).set_author(name=ctx.author.username, url=str(ctx.author.avatar_url))
|
||||
|
||||
await ctx.respond(EMPTY, embed=embed)
|
||||
await ctx.respond(embed=embed)
|
||||
|
||||
|
||||
@bot.listen(lightbulb.CommandErrorEvent)
|
||||
@@ -65,6 +68,8 @@ async def on_error(event: lightbulb.CommandErrorEvent) -> None:
|
||||
# Unwrap the exception to get the original cause
|
||||
exc = event.exception.__cause__ or event.exception
|
||||
ctx = event.context
|
||||
if not ctx.bot.rest.is_alive:
|
||||
return
|
||||
|
||||
if isinstance(event.exception, lightbulb.CommandInvocationError):
|
||||
if not event.context.command:
|
||||
@@ -114,6 +119,8 @@ async def on_error(event: lightbulb.CommandErrorEvent) -> None:
|
||||
ctx,
|
||||
)
|
||||
elif isinstance(exc, lightbulb.errors.MissingRequiredAttachment):
|
||||
await _send_error_embed("Not enough attachemnts were supplied to this command.", exc, ctx)
|
||||
await _send_error_embed("Not enough attachments were supplied to this command.", exc, ctx)
|
||||
elif isinstance(exc, lightbulb.errors.CommandNotFound):
|
||||
await ctx.respond(f"`/{exc.invoked_with}` is not a valid command. Use `/help` to see a list of commands.")
|
||||
else:
|
||||
raise exc
|
||||
|
||||
@@ -78,7 +78,6 @@ async def log_channel(ctx: lightbulb.SlashContext) -> None:
|
||||
|
||||
# if the bot's permissions for this channel don't contain SEND_MESSAGE
|
||||
# This will also filter out categories and voice channels
|
||||
print(permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES)
|
||||
if not permissions_in(ch, own_member) & hikari.Permissions.SEND_MESSAGES:
|
||||
await ctx.respond(f"I don't have permission to send messages in {ch.mention}.")
|
||||
return
|
||||
|
||||
@@ -7,7 +7,6 @@ import lightbulb
|
||||
import miru
|
||||
from aiosqlite import Connection
|
||||
from bot.db.schemas import GuildSettings
|
||||
from bot.utils import EMPTY
|
||||
from loguru import logger
|
||||
|
||||
plugin = lightbulb.Plugin(
|
||||
@@ -74,7 +73,7 @@ class LabelModal(miru.Modal):
|
||||
)
|
||||
channel = await context.bot.rest.fetch_channel(guild_settings.log_channel_id)
|
||||
assert isinstance(channel, hikari.TextableChannel)
|
||||
await channel.send(EMPTY, embed=embed)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
|
||||
class LabelSelect(miru.View):
|
||||
@@ -164,7 +163,7 @@ async def label_message_text(ctx: lightbulb.MessageContext):
|
||||
msg.content,
|
||||
timeout=60,
|
||||
)
|
||||
resp = await ctx.respond(EMPTY, embed=embed, components=label_select_view, flags=hikari.MessageFlag.EPHEMERAL)
|
||||
resp = await ctx.respond(embed=embed, components=label_select_view, flags=hikari.MessageFlag.EPHEMERAL)
|
||||
|
||||
await label_select_view.start(await resp.message())
|
||||
await label_select_view.wait()
|
||||
|
||||
+184
-186
@@ -1,15 +1,27 @@
|
||||
"""Work plugin for collecting user data."""
|
||||
import asyncio
|
||||
import typing as t
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
import hikari
|
||||
import lightbulb
|
||||
import lightbulb.decorators
|
||||
import miru
|
||||
from aiosqlite import Connection
|
||||
from bot.db.schemas import GuildSettings
|
||||
from bot.utils import EMPTY
|
||||
from bot.messages import (
|
||||
assistant_reply_message,
|
||||
confirm_ranking_response_message,
|
||||
confirm_text_response_message,
|
||||
initial_prompt_message,
|
||||
invalid_user_input_embed,
|
||||
plain_embed,
|
||||
prompter_reply_message,
|
||||
rank_assistant_reply_message,
|
||||
rank_initial_prompts_message,
|
||||
rank_prompter_reply_message,
|
||||
task_complete_embed,
|
||||
)
|
||||
from bot.settings import Settings
|
||||
from loguru import logger
|
||||
from oasst_shared.api_client import OasstApiClient, TaskType
|
||||
from oasst_shared.schemas import protocol as protocol_schema
|
||||
@@ -20,6 +32,8 @@ plugin = lightbulb.Plugin("WorkPlugin")
|
||||
MAX_TASK_TIME = 60 * 60 # 1 hour
|
||||
MAX_TASK_ACCEPT_TIME = 60 # 1 minute
|
||||
|
||||
settings = Settings()
|
||||
|
||||
|
||||
@plugin.command
|
||||
@lightbulb.option(
|
||||
@@ -31,31 +45,56 @@ MAX_TASK_ACCEPT_TIME = 60 # 1 minute
|
||||
type=str,
|
||||
)
|
||||
@lightbulb.command("work", "Complete a task.")
|
||||
@lightbulb.implements(lightbulb.SlashCommand)
|
||||
async def work(ctx: lightbulb.SlashContext):
|
||||
@lightbulb.implements(lightbulb.SlashCommand, lightbulb.PrefixCommand)
|
||||
async def work(ctx: lightbulb.Context):
|
||||
"""Create and handle a task."""
|
||||
# make sure the user isn't currently doing a task
|
||||
currently_working: set[hikari.Snowflakeish] = ctx.bot.d.currently_working
|
||||
# Only send this message if started from a server
|
||||
if ctx.guild_id is not None:
|
||||
await ctx.respond(embed=plain_embed("Sending you a task, check your DMs"), flags=hikari.MessageFlag.EPHEMERAL)
|
||||
|
||||
# make sure the user isn't currently doing a task, and if they are, ask if they want to cancel it
|
||||
currently_working: dict[
|
||||
hikari.Snowflakeish, tuple[hikari.Message | None, UUID | None]
|
||||
] = ctx.bot.d.currently_working
|
||||
|
||||
oasst_api: OasstApiClient = ctx.bot.d.oasst_api
|
||||
if ctx.author.id in currently_working:
|
||||
await ctx.respond(
|
||||
"You are already performing a task. Please complete that one first.", flags=hikari.MessageFlag.EPHEMERAL
|
||||
yn_view = YesNoView(timeout=MAX_TASK_ACCEPT_TIME)
|
||||
msg = await ctx.author.send(
|
||||
embed=plain_embed("You are already working. Would you like to cancel your old task start a new one?"),
|
||||
flags=hikari.MessageFlag.EPHEMERAL,
|
||||
components=yn_view,
|
||||
)
|
||||
return
|
||||
await yn_view.start(msg)
|
||||
await yn_view.wait()
|
||||
|
||||
currently_working.add(ctx.author.id)
|
||||
match yn_view.choice:
|
||||
case False | None:
|
||||
return
|
||||
case True:
|
||||
old_msg, task_id = currently_working[ctx.author.id]
|
||||
if old_msg is not None:
|
||||
logger.info(f"User {ctx.author.id} cancelled task {task_id}, deleting message {old_msg.id}")
|
||||
map(lambda c: c, old_msg.components)
|
||||
await old_msg.delete()
|
||||
if task_id is not None:
|
||||
await oasst_api.nack_task(task_id, reason="user cancelled")
|
||||
|
||||
await msg.delete()
|
||||
|
||||
currently_working[ctx.author.id] = (None, None)
|
||||
|
||||
# Create a TaskRequestType from the stringified enum value
|
||||
task_type: TaskRequestType = TaskRequestType(ctx.options.type.split(".")[-1])
|
||||
|
||||
await ctx.respond("Sending you a task, check your DMs", flags=hikari.MessageFlag.EPHEMERAL)
|
||||
logger.debug(f"Starting task_type: {task_type!r}")
|
||||
|
||||
try:
|
||||
await _handle_task(ctx, task_type)
|
||||
finally:
|
||||
currently_working.remove(ctx.author.id)
|
||||
del currently_working[ctx.author.id]
|
||||
|
||||
|
||||
async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType) -> None:
|
||||
async def _handle_task(ctx: lightbulb.Context, task_type: TaskRequestType) -> None:
|
||||
"""Handle creating and collecting user input for a task.
|
||||
|
||||
Continually present tasks to the user until they select one, cancel, or time out.
|
||||
@@ -72,38 +111,79 @@ async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType)
|
||||
task, msg_id = await _select_task(ctx, task_type)
|
||||
|
||||
if task is None:
|
||||
# User cancelled
|
||||
return
|
||||
|
||||
# Task action loop
|
||||
completed = False
|
||||
while not completed:
|
||||
await ctx.author.send("Please type your response here:")
|
||||
await ctx.author.send(embed=plain_embed("Please type your response here"))
|
||||
try:
|
||||
event = await ctx.bot.wait_for(
|
||||
hikari.DMMessageCreateEvent, timeout=MAX_TASK_TIME, predicate=lambda e: e.author.id == ctx.author.id
|
||||
hikari.DMMessageCreateEvent,
|
||||
timeout=MAX_TASK_TIME,
|
||||
predicate=lambda e: e.author.id == ctx.author.id
|
||||
and not (e.message.content or "").startswith(settings.prefix),
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.author.send("Task timed out. Exiting")
|
||||
await ctx.author.send(embed=plain_embed("Task timed out. Exiting"))
|
||||
await oasst_api.nack_task(task.id, reason="timed out")
|
||||
logger.info(f"Task {task.id} timed out")
|
||||
return
|
||||
|
||||
# Invalid response
|
||||
if event.content is None or not _validate_user_input(event.content, task):
|
||||
await ctx.author.send("Invalid response")
|
||||
valid, err_msg = _validate_user_input(event.content, task)
|
||||
if not valid or event.content is None:
|
||||
|
||||
await ctx.author.send(embed=invalid_user_input_embed(err_msg))
|
||||
continue
|
||||
|
||||
logger.debug(f"Successful user input received: {event.content}")
|
||||
|
||||
# Confirm user input
|
||||
if isinstance(task, protocol_schema.RankConversationRepliesTask):
|
||||
content = confirm_ranking_response_message(event.content, task.replies)
|
||||
elif isinstance(task, protocol_schema.RankInitialPromptsTask):
|
||||
content = confirm_ranking_response_message(event.content, task.prompts)
|
||||
elif isinstance(task, protocol_schema.ReplyToConversationTask | protocol_schema.InitialPromptTask):
|
||||
content = confirm_text_response_message(event.content)
|
||||
else:
|
||||
logger.critical(f"Unknown task type: {task.type}")
|
||||
raise ValueError(f"Unknown task type: {task.type}")
|
||||
|
||||
confirm_resp_view = YesNoView(timeout=MAX_TASK_TIME)
|
||||
msg = await ctx.author.send(content, components=confirm_resp_view)
|
||||
await confirm_resp_view.start(msg)
|
||||
await confirm_resp_view.wait()
|
||||
|
||||
match confirm_resp_view.choice:
|
||||
case False | None:
|
||||
continue
|
||||
case True:
|
||||
await msg.delete() # buttons are already gone
|
||||
|
||||
# Send the response to the backend
|
||||
reply = protocol_schema.TextReplyToMessage(
|
||||
message_id=str(msg_id),
|
||||
user_message_id=str(event.message_id),
|
||||
user=protocol_schema.User(
|
||||
auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username
|
||||
),
|
||||
text=event.content,
|
||||
)
|
||||
if isinstance(task, protocol_schema.RankConversationRepliesTask | protocol_schema.RankInitialPromptsTask):
|
||||
reply = protocol_schema.MessageRanking(
|
||||
message_id=str(msg_id),
|
||||
ranking=[int(r) - 1 for r in event.content.replace(" ", "").split(",")],
|
||||
user=protocol_schema.User(
|
||||
auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username
|
||||
),
|
||||
)
|
||||
elif isinstance(task, protocol_schema.ReplyToConversationTask | protocol_schema.InitialPromptTask):
|
||||
reply = protocol_schema.TextReplyToMessage(
|
||||
message_id=str(msg_id),
|
||||
user_message_id=str(event.message_id),
|
||||
user=protocol_schema.User(
|
||||
auth_method="discord", id=str(ctx.author.id), display_name=ctx.author.username
|
||||
),
|
||||
text=event.content,
|
||||
)
|
||||
else:
|
||||
logger.critical(f"Unexpected task type received: {task.type}")
|
||||
raise ValueError(f"Unexpected task type received: {task.type}")
|
||||
|
||||
logger.debug(f"Sending reply to backend: {reply!r}")
|
||||
|
||||
# Get next task
|
||||
@@ -111,63 +191,55 @@ async def _handle_task(ctx: lightbulb.SlashContext, task_type: TaskRequestType)
|
||||
logger.info(f"New task {new_task}")
|
||||
|
||||
if new_task.type == TaskType.done:
|
||||
await ctx.author.send("Task completed")
|
||||
await ctx.author.send(embed=plain_embed("Task completed"))
|
||||
completed = True
|
||||
continue
|
||||
else:
|
||||
logger.critical(f"Unexpected task type received: {new_task.type}")
|
||||
|
||||
# Send a message in the log channel that the task is complete
|
||||
# TODO: Maybe do something with the msg ID so users can rate the "answer"
|
||||
assert ctx.guild_id is not None
|
||||
# Send a message in all the log channels that the task is complete
|
||||
conn: Connection = ctx.bot.d.db
|
||||
guild_settings = await GuildSettings.from_db(conn, ctx.guild_id)
|
||||
async with conn.cursor() as cursor:
|
||||
await cursor.execute("SELECT log_channel_id FROM guild_settings")
|
||||
log_channel_ids = await cursor.fetchall()
|
||||
|
||||
if guild_settings is not None and guild_settings.log_channel_id is not None:
|
||||
channels = [
|
||||
ctx.bot.cache.get_guild_channel(id[0]) or await ctx.bot.rest.fetch_channel(id[0])
|
||||
for id in log_channel_ids
|
||||
]
|
||||
|
||||
channel = await ctx.bot.rest.fetch_channel(guild_settings.log_channel_id)
|
||||
assert isinstance(channel, hikari.TextableChannel) # option converter
|
||||
|
||||
done_embed = (
|
||||
hikari.Embed(
|
||||
title="Task Completion",
|
||||
description=f"`{task.type}` completed by {ctx.author.mention}",
|
||||
color=hikari.Color(0x00FF00),
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
.add_field("Total Tasks", "0", inline=True)
|
||||
.add_field("Server Ranking", "0/0", inline=True)
|
||||
.add_field("Global Ranking", "0/0", inline=True)
|
||||
.set_footer(f"Task ID: {task.id}")
|
||||
)
|
||||
await channel.send(EMPTY, embed=done_embed)
|
||||
done_embed = task_complete_embed(task, ctx.author.mention)
|
||||
# This will definitely get the bot rate limited, but that's a future problem
|
||||
asyncio.gather(*(ch.send(embed=done_embed) for ch in channels if isinstance(ch, hikari.TextableChannel)))
|
||||
|
||||
# ask the user if they want to do another task
|
||||
choice_view = ChoiceView(timeout=MAX_TASK_ACCEPT_TIME)
|
||||
msg = await ctx.author.send("Would you like another task?", components=choice_view)
|
||||
await choice_view.start(msg)
|
||||
await choice_view.wait()
|
||||
another_task_view = YesNoView(timeout=MAX_TASK_ACCEPT_TIME)
|
||||
msg = await ctx.author.send(embed=plain_embed("Would you like another task?"), components=another_task_view)
|
||||
await another_task_view.start(msg)
|
||||
await another_task_view.wait()
|
||||
|
||||
match choice_view.choice:
|
||||
match another_task_view.choice:
|
||||
case False | None:
|
||||
done = True
|
||||
await ctx.author.send("Exiting, goodbye!")
|
||||
await msg.edit(embed=plain_embed("Exiting, goodbye!"))
|
||||
case True:
|
||||
pass
|
||||
|
||||
|
||||
async def _select_task(
|
||||
ctx: lightbulb.SlashContext, task_type: TaskRequestType, user: protocol_schema.User | None = None
|
||||
ctx: lightbulb.Context, task_type: TaskRequestType, user: protocol_schema.User | None = None
|
||||
) -> tuple[protocol_schema.Task | None, str]:
|
||||
"""Present tasks to the user until they accept one, cancel, or time out."""
|
||||
oasst_api: OasstApiClient = ctx.bot.d.oasst_api
|
||||
logger.debug(f"Starting task selection for {task_type}")
|
||||
|
||||
# Loop until the user accepts a task, cancels, or times out
|
||||
msg: hikari.UndefinedOr[hikari.Message] = hikari.UNDEFINED
|
||||
while True:
|
||||
logger.debug(f"Requesting task of type {task_type}")
|
||||
task = await oasst_api.fetch_task(task_type, user)
|
||||
resp, msg_id = await _send_task(ctx, task)
|
||||
resp, msg = await _send_task(ctx, task, msg)
|
||||
msg_id = str(msg.id)
|
||||
|
||||
logger.debug(f"User choice: {resp}")
|
||||
match resp:
|
||||
@@ -179,25 +251,24 @@ async def _select_task(
|
||||
case "next":
|
||||
logger.info(f"Task {task.id} rejected, sending NACK")
|
||||
await oasst_api.nack_task(task.id, "rejected")
|
||||
await ctx.author.send("Sending next task...")
|
||||
continue
|
||||
|
||||
case "cancel":
|
||||
logger.info(f"Task {task.id} canceled, sending NACK")
|
||||
await oasst_api.nack_task(task.id, "canceled")
|
||||
await ctx.author.send("Task canceled. Exiting")
|
||||
await ctx.author.send(embed=plain_embed("Task canceled. Exiting"))
|
||||
return None, msg_id
|
||||
|
||||
case None:
|
||||
logger.info(f"Task {task.id} timed out, sending NACK")
|
||||
await oasst_api.nack_task(task.id, "timed out")
|
||||
await ctx.author.send("Task timed out. Exiting")
|
||||
await ctx.author.send(embed=plain_embed("Task timed out. Exiting"))
|
||||
return None, msg_id
|
||||
|
||||
|
||||
async def _send_task(
|
||||
ctx: lightbulb.SlashContext, task: protocol_schema.Task
|
||||
) -> tuple[t.Literal["accept", "next", "cancel"] | None, str]:
|
||||
ctx: lightbulb.Context, task: protocol_schema.Task, msg: hikari.UndefinedOr[hikari.Message]
|
||||
) -> tuple[t.Literal["accept", "next", "cancel"] | None, hikari.Message]:
|
||||
"""Send a task to the user.
|
||||
|
||||
Returns the user's choice and the message ID of the task message.
|
||||
@@ -206,37 +277,38 @@ async def _send_task(
|
||||
# but the tasks aren't discord specific so that doesn't really make sense.
|
||||
|
||||
embed: hikari.UndefinedOr[hikari.Embed] = hikari.UNDEFINED
|
||||
content: hikari.UndefinedOr[str] = hikari.UNDEFINED
|
||||
|
||||
# Create an embed based on the task's type
|
||||
if task.type == TaskRequestType.initial_prompt:
|
||||
assert isinstance(task, protocol_schema.InitialPromptTask)
|
||||
logger.debug("sending initial prompt task")
|
||||
embed = _initial_prompt_embed(task)
|
||||
content = initial_prompt_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.rank_initial_prompts:
|
||||
assert isinstance(task, protocol_schema.RankInitialPromptsTask)
|
||||
logger.debug("sending rank initial prompt task")
|
||||
embed = _rank_initial_prompt_embed(task)
|
||||
content = rank_initial_prompts_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.rank_prompter_replies:
|
||||
assert isinstance(task, protocol_schema.RankPrompterRepliesTask)
|
||||
logger.debug("sending rank user reply task")
|
||||
embed = _rank_prompter_reply_embed(task)
|
||||
content = rank_prompter_reply_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.rank_assistant_replies:
|
||||
assert isinstance(task, protocol_schema.RankAssistantRepliesTask)
|
||||
logger.debug("sending rank assistant reply task")
|
||||
embed = _rank_assistant_reply_embed(task)
|
||||
content = rank_assistant_reply_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.prompter_reply:
|
||||
assert isinstance(task, protocol_schema.PrompterReplyTask)
|
||||
logger.debug("sending user reply task")
|
||||
embed = _prompter_reply_embed(task)
|
||||
content = prompter_reply_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.assistant_reply:
|
||||
assert isinstance(task, protocol_schema.AssistantReplyTask)
|
||||
logger.debug("sending assistant reply task")
|
||||
embed = _assistant_reply_embed(task)
|
||||
content = assistant_reply_message(task)
|
||||
|
||||
elif task.type == TaskRequestType.summarize_story:
|
||||
raise NotImplementedError
|
||||
@@ -248,24 +320,34 @@ async def _send_task(
|
||||
raise ValueError(f"unknown task type {task.type}")
|
||||
|
||||
view = TaskAcceptView(timeout=MAX_TASK_ACCEPT_TIME)
|
||||
msg = await ctx.author.send(
|
||||
EMPTY,
|
||||
embed=embed,
|
||||
components=view,
|
||||
)
|
||||
if not msg:
|
||||
msg = await ctx.author.send(
|
||||
content,
|
||||
embed=embed,
|
||||
components=view,
|
||||
)
|
||||
else:
|
||||
await msg.edit(
|
||||
content,
|
||||
embed=embed,
|
||||
components=view,
|
||||
)
|
||||
|
||||
assert msg is not None
|
||||
|
||||
# Set the choice id as the current msg id
|
||||
ctx.bot.d.currently_working[ctx.author.id] = (msg, task.id)
|
||||
|
||||
await view.start(msg)
|
||||
await view.wait()
|
||||
|
||||
return view.choice, str(msg.id)
|
||||
return view.choice, msg
|
||||
|
||||
|
||||
def _validate_user_input(content: str | None, task: protocol_schema.Task) -> bool:
|
||||
"""Returns whether the user's input is valid for the task type."""
|
||||
def _validate_user_input(content: str | None, task: protocol_schema.Task) -> tuple[bool, str]:
|
||||
"""Returns whether the user's input is valid for the task type and an error message."""
|
||||
if content is None:
|
||||
return False
|
||||
return False, "No input provided"
|
||||
|
||||
# User message input
|
||||
if (
|
||||
@@ -277,22 +359,28 @@ def _validate_user_input(content: str | None, task: protocol_schema.Task) -> boo
|
||||
task,
|
||||
protocol_schema.InitialPromptTask | protocol_schema.PrompterReplyTask | protocol_schema.AssistantReplyTask,
|
||||
)
|
||||
return len(content) > 0
|
||||
return len(content) > 0, "Message must be at least one character long."
|
||||
|
||||
# Ranking tasks
|
||||
elif task.type == TaskRequestType.rank_prompter_replies or task.type == TaskRequestType.rank_assistant_replies:
|
||||
assert isinstance(task, protocol_schema.RankPrompterRepliesTask | protocol_schema.RankAssistantRepliesTask)
|
||||
num_replies = len(task.replies)
|
||||
|
||||
rankings = content.split(",")
|
||||
return set(rankings) == {str(i) for i in range(1, num_replies + 1)} and len(rankings) == num_replies
|
||||
rankings = content.replace(" ", "").split(",")
|
||||
return (
|
||||
set(rankings) == {str(i) for i in range(1, num_replies + 1)} and len(rankings) == num_replies,
|
||||
"Message must contain numbers for all replies.",
|
||||
)
|
||||
|
||||
elif task.type == TaskRequestType.rank_initial_prompts:
|
||||
assert isinstance(task, protocol_schema.RankInitialPromptsTask)
|
||||
num_prompts = len(task.prompts)
|
||||
|
||||
rankings = content.split(",")
|
||||
return set(rankings) == {str(i) for i in range(1, num_prompts + 1)} and len(rankings) == num_prompts
|
||||
rankings = content.replace(" ", "").split(",")
|
||||
return (
|
||||
set(rankings) == {str(i) for i in range(1, num_prompts + 1)} and len(rankings) == num_prompts,
|
||||
"Message must contain numbers for all prompts.",
|
||||
)
|
||||
|
||||
elif task.type == TaskRequestType.summarize_story:
|
||||
raise NotImplementedError
|
||||
@@ -316,22 +404,29 @@ class TaskAcceptView(miru.View):
|
||||
async def accept_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
|
||||
logger.info("Accept button pressed")
|
||||
self.choice = "accept"
|
||||
await ctx.message.edit(component=None)
|
||||
self.stop()
|
||||
|
||||
@miru.button(label="Next Task", custom_id="next_task", row=0, style=hikari.ButtonStyle.SECONDARY)
|
||||
async def next_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
|
||||
logger.info("Next button pressed")
|
||||
self.choice = "next"
|
||||
await ctx.message.edit(component=None)
|
||||
self.stop()
|
||||
|
||||
@miru.button(label="Cancel", custom_id="cancel", row=0, style=hikari.ButtonStyle.DANGER)
|
||||
async def cancel_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
|
||||
logger.info("Cancel button pressed")
|
||||
self.choice = "cancel"
|
||||
await ctx.message.edit(component=None)
|
||||
self.stop()
|
||||
|
||||
async def on_timeout(self) -> None:
|
||||
if self.message is not None:
|
||||
await self.message.edit(component=None)
|
||||
|
||||
class ChoiceView(miru.View):
|
||||
|
||||
class YesNoView(miru.View):
|
||||
"""View with two buttons: yes and no.
|
||||
|
||||
The view stops once one of the buttons is pressed and the choice is stored in the `choice` attribute.
|
||||
@@ -342,115 +437,18 @@ class ChoiceView(miru.View):
|
||||
@miru.button(label="Yes", custom_id="yes", style=hikari.ButtonStyle.SUCCESS)
|
||||
async def yes_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
|
||||
self.choice = True
|
||||
await ctx.message.edit(component=None)
|
||||
self.stop()
|
||||
|
||||
@miru.button(label="No", custom_id="no", style=hikari.ButtonStyle.DANGER)
|
||||
async def no_button(self, button: miru.Button, ctx: miru.ViewContext) -> None:
|
||||
self.choice = False
|
||||
await ctx.message.edit(component=None)
|
||||
self.stop()
|
||||
|
||||
|
||||
################################################################
|
||||
# Template Embeds #
|
||||
################################################################
|
||||
|
||||
# TODO: Maybe implement a better way of creating embeds, like `from_json` or something
|
||||
|
||||
|
||||
def _initial_prompt_embed(task: protocol_schema.InitialPromptTask) -> hikari.Embed:
|
||||
return (
|
||||
hikari.Embed(title="Initial Prompt", description=f"Hint: {task.hint}", timestamp=datetime.now().astimezone())
|
||||
.set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512")
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
|
||||
def _rank_initial_prompt_embed(task: protocol_schema.RankInitialPromptsTask) -> hikari.Embed:
|
||||
embed = (
|
||||
hikari.Embed(
|
||||
title="Rank Initial Prompt",
|
||||
description="Rank the following tasks from best to worst (1,2,3,4,5)",
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
.set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512")
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
for i, prompt in enumerate(task.prompts):
|
||||
embed.add_field(name=f"Prompt {i + 1}", value=prompt, inline=False)
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
def _rank_prompter_reply_embed(task: protocol_schema.RankPrompterRepliesTask) -> hikari.Embed:
|
||||
embed = (
|
||||
hikari.Embed(
|
||||
title="Rank User Reply",
|
||||
description="Rank the following user replies from best to worst. e.g. 1,2,5,3,4",
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
.set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: update image
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
for i, reply in enumerate(task.replies):
|
||||
embed.add_field(name=f"Reply {i + 1}", value=reply, inline=False)
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
def _rank_assistant_reply_embed(task: protocol_schema.RankAssistantRepliesTask) -> hikari.Embed:
|
||||
embed = (
|
||||
hikari.Embed(
|
||||
title="Rank Assistant Reply",
|
||||
description="Rank the following assistant replies from best to worst. e.g. 1,2,5,3,4",
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
.set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: update image
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
for i, reply in enumerate(task.replies):
|
||||
embed.add_field(name=f"Reply {i + 1}", value=reply, inline=False)
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
def _prompter_reply_embed(task: protocol_schema.PrompterReplyTask) -> hikari.Embed:
|
||||
embed = (
|
||||
hikari.Embed(
|
||||
title="User Reply",
|
||||
description=f"""\
|
||||
Send the next message in the conversation as if you were the user.
|
||||
{'Hint: ' if task.hint else ''}
|
||||
""",
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
# .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: change image
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
for message in task.conversation.messages:
|
||||
embed.add_field(name="Assistant" if message.is_assistant else "User", value=message.text, inline=False)
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
def _assistant_reply_embed(task: protocol_schema.AssistantReplyTask) -> hikari.Embed:
|
||||
embed = (
|
||||
hikari.Embed(
|
||||
title="User Reply",
|
||||
description="Send the next message in the conversation as if you were the user.",
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
# .set_image("https://images.unsplash.com/photo-1455390582262-044cdead277a?w=512") # TODO: change image
|
||||
.set_footer(text=f"OASST Assistant | {task.id}")
|
||||
)
|
||||
|
||||
for message in task.conversation.messages:
|
||||
embed.add_field(name="Assistant" if message.is_assistant else "User", value=message.text, inline=False)
|
||||
|
||||
return embed
|
||||
async def on_timeout(self) -> None:
|
||||
if self.message is not None:
|
||||
await self.message.edit(component=None)
|
||||
|
||||
|
||||
def load(bot: lightbulb.BotApp):
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
"""All user-facing messages and embeds."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import hikari
|
||||
from oasst_shared.schemas import protocol as protocol_schema
|
||||
|
||||
NUMBER_EMOJIS = [":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":ten:"]
|
||||
NL = "\n"
|
||||
|
||||
###
|
||||
# Reusable 'components'
|
||||
###
|
||||
|
||||
|
||||
def _h1(text: str) -> str:
|
||||
return f"\n:small_blue_diamond: __**{text}**__ :small_blue_diamond:"
|
||||
|
||||
|
||||
def _h2(text: str) -> str:
|
||||
return f"__**{text}**__"
|
||||
|
||||
|
||||
def _h3(text: str) -> str:
|
||||
return f"__{text}__"
|
||||
|
||||
|
||||
def _writing_prompt(text: str) -> str:
|
||||
return f":pencil: _{text}_"
|
||||
|
||||
|
||||
def _ranking_prompt(text: str) -> str:
|
||||
return f":trophy: _{text}_"
|
||||
|
||||
|
||||
def _response_prompt(text: str) -> str:
|
||||
return f":speech_balloon: _{text}_"
|
||||
|
||||
|
||||
def _summarize_prompt(text: str) -> str:
|
||||
return f":notepad_spiral: _{text}_"
|
||||
|
||||
|
||||
def _user(text: str | None) -> str:
|
||||
return f"""\
|
||||
:person_red_hair: {_h3("User")}:{f"{NL}> **{text}**" if text is not None else ""}
|
||||
"""
|
||||
|
||||
|
||||
def _assistant(text: str | None) -> str:
|
||||
return f"""\
|
||||
:robot: {_h3("Assistant")}:{f"{NL}> {text}" if text is not None else ""}
|
||||
"""
|
||||
|
||||
|
||||
def _make_ordered_list(items: list[str]) -> list[str]:
|
||||
return [f"{num} {item}" for num, item in zip(NUMBER_EMOJIS, items)]
|
||||
|
||||
|
||||
def _ordered_list(items: list[str]) -> str:
|
||||
return "\n\n".join(_make_ordered_list(items))
|
||||
|
||||
|
||||
def _conversation(conv: protocol_schema.Conversation) -> str:
|
||||
return "\n".join([_assistant(msg.text) if msg.is_assistant else _user(msg.text) for msg in conv.messages])
|
||||
|
||||
|
||||
def _hint(hint: str | None) -> str:
|
||||
return f"{NL}Hint: {hint}" if hint else ""
|
||||
|
||||
|
||||
###
|
||||
# Messages
|
||||
###
|
||||
|
||||
|
||||
def initial_prompt_message(task: protocol_schema.InitialPromptTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request an `initial_prompt` task."""
|
||||
return f"""\
|
||||
|
||||
{_h1("INITIAL PROMPT")}
|
||||
|
||||
{_writing_prompt("Please provide an initial prompt to the assistant.")}
|
||||
{_hint(task.hint)}
|
||||
"""
|
||||
|
||||
|
||||
def rank_initial_prompts_message(task: protocol_schema.RankInitialPromptsTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request a `rank_initial_prompts` task."""
|
||||
return f"""\
|
||||
|
||||
{_h1("RANK INITIAL PROMPTS")}
|
||||
|
||||
{_ranking_prompt("Reply with the numbers of best to worst prompts separated by commas (example: '4,1,3,2')")}
|
||||
|
||||
|
||||
{_ordered_list(task.prompts)}
|
||||
"""
|
||||
|
||||
|
||||
def rank_prompter_reply_message(task: protocol_schema.RankPrompterRepliesTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request a `rank_prompter_replies` task."""
|
||||
return f"""\
|
||||
|
||||
{_h1("RANK PROMPTER REPLIES")}
|
||||
|
||||
{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")}
|
||||
|
||||
|
||||
{_conversation(task.conversation)}
|
||||
{_user(None)}
|
||||
{_ordered_list(task.replies)}
|
||||
"""
|
||||
|
||||
|
||||
def rank_assistant_reply_message(task: protocol_schema.RankAssistantRepliesTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request a `rank_assistant_replies` task."""
|
||||
return f"""\
|
||||
|
||||
{_h1("RANK ASSISTANT REPLIES")}
|
||||
|
||||
{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")}
|
||||
|
||||
|
||||
{_conversation(task.conversation)}
|
||||
{_assistant(None)}
|
||||
{_ordered_list(task.replies)}
|
||||
"""
|
||||
|
||||
|
||||
def prompter_reply_message(task: protocol_schema.PrompterReplyTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request a `prompter_reply` task."""
|
||||
return f"""\
|
||||
|
||||
{_h1("PROMPTER REPLY")}
|
||||
|
||||
{_response_prompt("Please provide a reply to the assistant.")}
|
||||
|
||||
|
||||
{_conversation(task.conversation)}
|
||||
{_hint(task.hint)}
|
||||
"""
|
||||
|
||||
|
||||
def assistant_reply_message(task: protocol_schema.AssistantReplyTask) -> str:
|
||||
"""Creates the message that gets sent to users when they request a `assistant_reply` task."""
|
||||
return f"""\
|
||||
{_h1("ASSISTANT REPLY")}
|
||||
|
||||
{_response_prompt("Please provide a reply to the assistant.")}
|
||||
|
||||
|
||||
{_conversation(task.conversation)}
|
||||
"""
|
||||
|
||||
|
||||
def confirm_text_response_message(content: str) -> str:
|
||||
return f"""\
|
||||
{_h2("CONFIRM RESPONSE")}
|
||||
|
||||
> {content}
|
||||
"""
|
||||
|
||||
|
||||
def confirm_ranking_response_message(content: str, items: list[str]) -> str:
|
||||
user_rankings = [int(r) for r in content.replace(" ", "").split(",")]
|
||||
original_list = _make_ordered_list(items)
|
||||
user_ranked_list = "\n\n".join([original_list[r - 1] for r in user_rankings])
|
||||
|
||||
return f"""\
|
||||
{_h2("CONFIRM RESPONSE")}
|
||||
|
||||
{user_ranked_list}
|
||||
"""
|
||||
|
||||
|
||||
###
|
||||
# Embeds
|
||||
###
|
||||
|
||||
|
||||
def task_complete_embed(task: protocol_schema.Task, mention: str) -> hikari.Embed:
|
||||
return (
|
||||
hikari.Embed(
|
||||
title="Task Completion",
|
||||
description=f"`{task.type}` completed by {mention}",
|
||||
color=hikari.Color(0x00FF00),
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
.add_field("Total Tasks", "0", inline=True)
|
||||
.add_field("Server Ranking", "0/0", inline=True)
|
||||
.add_field("Global Ranking", "0/0", inline=True)
|
||||
.set_footer(f"Task ID: {task.id}")
|
||||
)
|
||||
|
||||
|
||||
def invalid_user_input_embed(error_message: str) -> hikari.Embed:
|
||||
return hikari.Embed(
|
||||
title="Invalid User Input",
|
||||
description=error_message,
|
||||
color=hikari.Color(0xFF0000),
|
||||
timestamp=datetime.now().astimezone(),
|
||||
)
|
||||
|
||||
|
||||
def plain_embed(text: str) -> hikari.Embed:
|
||||
return hikari.Embed(color=0x36393F, description=text)
|
||||
@@ -8,7 +8,7 @@ class Settings(BaseSettings):
|
||||
bot_token: str = Field(env="BOT_TOKEN", default="")
|
||||
declare_global_commands: int = Field(env="DECLARE_GLOBAL_COMMANDS", default=0)
|
||||
owner_ids: list[int] = Field(env="OWNER_IDS", default_factory=list)
|
||||
prefix: str = Field(env="PREFIX", default="./")
|
||||
prefix: str = Field(env="PREFIX", default="/")
|
||||
oasst_api_url: str = Field(env="OASST_API_URL", default="http://localhost:8080")
|
||||
oasst_api_key: str = Field(env="OASST_API_KEY", default="")
|
||||
|
||||
|
||||
@@ -24,13 +24,6 @@ def format_time(dt: datetime, fmt: t.Literal["t", "T", "D", "f", "F", "R"]) -> s
|
||||
raise ValueError(f"`fmt` must be 't', 'T', 'D', 'f', 'F' or 'R', not {fmt}")
|
||||
|
||||
|
||||
EMPTY = "\u200d"
|
||||
"""Zero-width joiner.
|
||||
|
||||
This appears as an empty message in Discord.
|
||||
"""
|
||||
|
||||
|
||||
def mention(
|
||||
id: hikari.Snowflakeish,
|
||||
type: t.Literal["channel", "role", "user"],
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ services:
|
||||
# Use `docker compose up frontend-dev --build --attach-dependencies` to start all services needed to work on the frontend.
|
||||
frontend-dev:
|
||||
image: sverrirab/sleep
|
||||
depends_on: [db, webdb, adminer, maildev, backend]
|
||||
depends_on: [db, webdb, adminer, maildev, backend, redis]
|
||||
|
||||
# This DB is for the FastAPI Backend.
|
||||
db:
|
||||
|
||||
@@ -5,6 +5,7 @@ COPY ./backend/requirements.txt /app/requirements.txt
|
||||
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||
|
||||
ENV PORT 8080
|
||||
EXPOSE 8080
|
||||
|
||||
COPY ./oasst-shared /oasst-shared
|
||||
RUN pip install -e /oasst-shared
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Supervised datasets
|
||||
|
||||
For discussion about usage of supervised data see issue
|
||||
<https://github.com/LAION-AI/Open-Assistant/issues/186>.
|
||||
|
||||
## Motivation
|
||||
|
||||
An important part of making the assistant useful is to teach it to understand
|
||||
and follow instructions, and to perform large set of tasks well.
|
||||
|
||||
While RLHF seems like the main ingredient, using existing supervised data might
|
||||
help.
|
||||
|
||||
There are two large-scale projects in the area of instruction-following /
|
||||
multitask learning: Promptsource and Natural Instructions - these projects
|
||||
crowdsourced templates and turned existing NLP datasets into
|
||||
instruction-following seq2seq form in natural langauge. They include both
|
||||
long-output training examples like generating a sentence that is a likely
|
||||
consequence of sentence in the prompt, and short-output, like rating prediction
|
||||
from review. (Pre-)training on such datasets should help model understand and
|
||||
follow instructions and teach it many abilities neccessary to perform a large
|
||||
set of tasks correctly. However, these data are not dialog-like - they do not
|
||||
look like a normal conversation.
|
||||
|
||||
There are also supervised dialog datasets such as Blended Skill Talk or SODA. In
|
||||
constrast to instruction-following datasets, dialog data is not as focused on
|
||||
"academic tasks" or correctness, but encourage the model to respond naturally
|
||||
like a person would.
|
||||
|
||||
### Promptsource
|
||||
|
||||
- GitHub: <https://github.com/bigscience-workshop/promptsource>
|
||||
- paper:
|
||||
[Multitask Prompted Training Enables Zero-Shot Task Generalization](https://arxiv.org/abs/2110.08207)
|
||||
- project for preparing templates and working with them
|
||||
- they generated a dataset using the templates:
|
||||
- <https://huggingface.co/datasets/bigscience/P3>
|
||||
- <https://huggingface.co/datasets/bigscience/xP3> (with multilingual data but
|
||||
English prompt)
|
||||
- <https://huggingface.co/datasets/bigscience/xP3mt> (with multilingual data
|
||||
and machine-translated prompt)
|
||||
- they trained zero-shot models (= models for following instructions in the
|
||||
input)
|
||||
- based on T5 architecture (encoder-decoder) called T0 family (and MT0 for
|
||||
multilingual)
|
||||
- and based on GPT architecture (decoder-only) called BloomZ family
|
||||
- Huggingface demo: [T0](https://huggingface.co/bigscience/T0pp),
|
||||
[MT0](https://huggingface.co/bigscience/mt0-large),
|
||||
[BloomZ](https://huggingface.co/bigscience/bloomz),
|
||||
- GitHub repo for T0: <https://github.com/bigscience-workshop/t-zero>
|
||||
- GitHub repo for BloomZ and MT0:
|
||||
<https://github.com/bigscience-workshop/xmtf>
|
||||
|
||||
### Natural instructions
|
||||
|
||||
- GitHub: <https://github.com/allenai/natural-instructions>
|
||||
- paper:
|
||||
[Super-NaturalInstructions: Generalization via Declarative Instructions on 1600+ NLP Tasks](https://arxiv.org/abs/2204.07705)
|
||||
- they crowdsource directly the data prepared for instruction following (and
|
||||
learning from a few examples)
|
||||
- the GitHub repo = the dataset. It contains jsons
|
||||
- they trained zero-shot and in-context few-shot models (in multiple sizes):
|
||||
- mT5 architecture (encoder-decoder, multilingual pretraining)
|
||||
- Huggingface demo few-shot:
|
||||
<https://huggingface.co/allenai/tk-instruct-3b-def-pos>
|
||||
- Huggingface demo zero-shot:
|
||||
<https://huggingface.co/allenai/tk-instruct-3b-def>
|
||||
|
||||
### Blended Skill Talk
|
||||
|
||||
- used by Facebook in Blenderbot project
|
||||
- HuggingFace dataset: <https://huggingface.co/datasets/blended_skill_talk>
|
||||
- example model trained on it:
|
||||
<https://huggingface.co/facebook/blenderbot_small-90M>
|
||||
|
||||
### SODA
|
||||
|
||||
- GitHub: <https://github.com/skywalker023/sodaverse>
|
||||
- paper: <https://arxiv.org/abs/2212.10465>
|
||||
@@ -1,226 +1,229 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8zsmJ96eaL2w"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install transformers"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Pt6qbTsjW7Kp"
|
||||
},
|
||||
"source": [
|
||||
"Put your essay here, [source of the essay used ](https://https://www.thewisdompost.com/essay/technology-essay/3387#essay-on-technology-for-college-and-university-students-essay-2-750-words)\n",
|
||||
"\n",
|
||||
"Separate paragraphs with one blank line\n",
|
||||
"(this step is annoying but important)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "d_5_BDFNWneB"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"essay = \"\"\"\n",
|
||||
"We live in a world driven by technology — hardly anyone would argue with you if you said this. \n",
|
||||
"Technology, literally meaning the “science of craft”, refers to the collection of techniques, \n",
|
||||
"skills, methods, and processes used to produce goods or services or for accomplishing objectives \n",
|
||||
"such as scientific investigation. Technology can be embedded in machines enabling them to be \n",
|
||||
"used by people even without a detailed knowledge of their inner workings. Technological growth \n",
|
||||
"is closely linked to the expansion of scientific research and knowledge. In the last 50 years, \n",
|
||||
"thanks to the exponential increases in computing power and microchip design and manufacture, \n",
|
||||
"there has been unprecedented innovation and technological growth in nearly every field of human \n",
|
||||
"endeavour from health and transport to industrial production and education.\n",
|
||||
"\n",
|
||||
"It is automotive technology that drives today’s electric and hybrid cars, and which will drive \n",
|
||||
"tomorrow’s driverless cars, hover-taxis and space cabs. It is technology that drives the \n",
|
||||
"ubiquitous mobile phones that you will now find in the hands of even the poorest of the world’s \n",
|
||||
"poor. It is technology that creates hybrid seeds that resist inhospitable climatic conditions \n",
|
||||
"and difficult terrain, giving high yields in shorter times. It is advancing medical technology \n",
|
||||
"that makes remote surgery, minimally invasive surgery and life-saving cures using stem cell \n",
|
||||
"transplants. Technology puts spacecrafts on asteroids and distant planets and lets us see \n",
|
||||
"new worlds. Technology splits atoms, revealing their secrets, and gives us ways to exploit \n",
|
||||
"them to create energy, quantum storage for data, and virtual reality games.\n",
|
||||
"\n",
|
||||
"There are people who strongly oppose technology and claim that it spells the death of \n",
|
||||
"‘humanity’, and that we are approaching the day when machines will rule everything. They refer \n",
|
||||
"to fans of technology as ‘techies’ or sometimes ‘geeks’. On the other hand, proponents of \n",
|
||||
"technology call these people Luddites, a derogatory name for someone who is opposed to \n",
|
||||
"industrialisation, automation, computerisation and new technologies in general.\n",
|
||||
"Is this true? Is technology really a curse disguised as a blessing? Many believe that the \n",
|
||||
"convergence of biotechnology and AI might be the most consequential development of all.\n",
|
||||
"\n",
|
||||
"In the last five decades, two areas in particular have grown faster than the rest, powered \n",
|
||||
"by research and advances in computing power. One is artificial intelligence, or AI; the other \n",
|
||||
"is biotechnology. Huge benefits have emerged from each of them for human beings in general, \n",
|
||||
"such as self-driving cars — which will dramatically reduce the death rate from road accidents \n",
|
||||
"— and robotic surgery, which enables precise, highly efficient and targeted surgical \n",
|
||||
"interventions. Yet, visionaries like Yuval Noah Harari, author of the best-selling \"Homo \n",
|
||||
"Sapiens\" and \"Deus\", are now warning that the convergence of biotechnology and AI will \n",
|
||||
"irreversibly and unpredictably change both the quality of human life and its challenges in \n",
|
||||
"the next few decades. A good example of this is the facial recognition technology that is \n",
|
||||
"now present in all photo management programs. The AI in the software is capable of not \n",
|
||||
"only spotting the faces in every photograph but also recognising the person by name.\n",
|
||||
"This technology has now expanded so that photo apps can recognise cats, dogs, beaches, \n",
|
||||
"mountains and cars too. Computers with AI are already correctly identifying human emotions \n",
|
||||
"through observing facial expressions and body movements. Some robots are able to mimic \n",
|
||||
"human emotions. This is called affective computing, sometimes called artificial emotional \n",
|
||||
"intelligence, and refers to the study and development of systems and devices that can \n",
|
||||
"recognize, interpret, process, and simulate human affects.\n",
|
||||
"\n",
|
||||
"How could this be a negative?\n",
|
||||
"The ability to read human emotions is just a step away from predicting human emotions. For \n",
|
||||
"example, if a computer attached to a video camera could identify which products a consumer \n",
|
||||
"is showing greater interest in or which ones he is really keen to buy, various tactics \n",
|
||||
"could be used to influence her to buy it. Activists worry that computers that can understand \n",
|
||||
"and anticipate human wishes and desires by scanning their irises and analysing their \n",
|
||||
"micro-expressions could also be programmed to exploit and manipulate them. Another very real \n",
|
||||
"fear is that humanoid computers with human-like skin, speech, and expressions could jeopardise \n",
|
||||
"and dehumanise relationship and create emotional vacuums.\n",
|
||||
"\n",
|
||||
"An enduring fear of Luddites has always been that computers will rob humans of their \n",
|
||||
"livelihood by taking their jobs and doing them more efficiently at lower cost. However, in \n",
|
||||
"reality the exact opposite has happened. As computerised machines began taking over mechanical \n",
|
||||
"and repetitive human activities, new jobs for people opened up that needs thinking and \n",
|
||||
"analytical skills and judgement, or human interpersonal skills. A good example is the \n",
|
||||
"worldwide proliferation of call centres. When drones were invented many feared that pilots \n",
|
||||
"would soon be redundant. However, few people know that it takes almost 30 people to fly \n",
|
||||
"one military drone, and an additional 50 people to analyze and make sense of the data being \n",
|
||||
"streamed back by the drone. The US army suffers from a serious shortage of trained, high \n",
|
||||
"quality drone pilots; anyone who masters this skill will have a job. But a social scientist \n",
|
||||
"warns that in 10 years, it is certain that computers will be flying that drone and humans \n",
|
||||
"will be redundant. Equally sure is that some brand new skill requirement will have opened \n",
|
||||
"up with advancing technology, calling for new talents.\n",
|
||||
"\n",
|
||||
"In the 20th century, a young man was supposed to choose a skill, vocation or profession, \n",
|
||||
"master it through education and practice, and then earn a living from it till he or she \n",
|
||||
"retired. However, the fast-changing nature of technology is making skills obsolete at a \n",
|
||||
"higher rate than ever before. To survive, tomorrow young man must keep re-inventing himself \n",
|
||||
"and updating his skills continuously. Life could be difficult if every new skill has a shelf \n",
|
||||
"life of only a decade or so. Or perhaps one could look at it the other way — and say that \n",
|
||||
"changing technology will keep human beings on their toes throughout their life.\n",
|
||||
"\n",
|
||||
"Technology is the result of human inventiveness. It reflects our evolutionary heritage. We \n",
|
||||
"are neither strong like gorillas or tigers, nor fast like cheetahs and hawks, but our \n",
|
||||
"brains and thinking powers have given us the greatest edge of any species on the planet. \n",
|
||||
"Technology is a result. Technology is either inherently good or bad; it is how we use it \n",
|
||||
"that makes it so. The splitting of a hydrogen atom is technology at work. As history has \n",
|
||||
"shown us, technology can equally be used to make a nuclear bomb that kills millions — or \n",
|
||||
"generate electricity that lights up a million homes.\n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"id": "JESY8Y10W6hQ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"essay_paragraphs = essay.split('\\n\\n')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "t1G-ZiHbZZ-Y"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_name = \"snrspeaks/t5-one-line-summary\"\n",
|
||||
"\n",
|
||||
"from transformers import AutoModelForSeq2SeqLM, AutoTokenizer\n",
|
||||
"model = AutoModelForSeq2SeqLM.from_pretrained(model_name)\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(model_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8BARyupEemZ-"
|
||||
},
|
||||
"source": [
|
||||
"## Results\n",
|
||||
"Please at least check what is generated here, it's usually good but sometimes it's bs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "eyR58KFRae7n",
|
||||
"outputId": "b8e4bc29-be89-43c3-d1bc-7e90525c0e09"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"preds = []\n",
|
||||
"\n",
|
||||
"for para in essay_paragraphs:\n",
|
||||
" input_ids = tokenizer.encode(para, return_tensors=\"pt\", add_special_tokens=True)\n",
|
||||
" generated_ids = model.generate(input_ids=input_ids,\n",
|
||||
" num_beams=5,\n",
|
||||
" max_length=35,\n",
|
||||
" repetition_penalty=4.5,\n",
|
||||
" length_penalty=1.5,\n",
|
||||
" early_stopping=True,\n",
|
||||
" num_return_sequences=1)\n",
|
||||
" preds.append(tokenizer.decode(generated_ids[0], \n",
|
||||
" skip_special_tokens=True, \n",
|
||||
" clean_up_tokenization_spaces=True))\n",
|
||||
"\n",
|
||||
"prompts = ['Write an intro paragraph to an essay called'] + \\\n",
|
||||
" ['Write a paragraph to an essay about']*len(preds[1:-1]) + \\\n",
|
||||
" ['Write a concluding paragraph about']\n",
|
||||
"\n",
|
||||
"assert len(preds) == len(prompts)\n",
|
||||
"\n",
|
||||
"for prompt, pred in zip(prompts, preds):\n",
|
||||
" print(prompt, pred.lower())"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.10 64-bit",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
|
||||
}
|
||||
}
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8zsmJ96eaL2w"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install transformers"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Pt6qbTsjW7Kp"
|
||||
},
|
||||
"source": [
|
||||
"Put your essay here, [source of the essay used ](https://https://www.thewisdompost.com/essay/technology-essay/3387#essay-on-technology-for-college-and-university-students-essay-2-750-words)\n",
|
||||
"\n",
|
||||
"Separate paragraphs with one blank line\n",
|
||||
"(this step is annoying but important)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "d_5_BDFNWneB"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"essay = \"\"\"\n",
|
||||
"We live in a world driven by technology — hardly anyone would argue with you if you said this. \n",
|
||||
"Technology, literally meaning the “science of craft”, refers to the collection of techniques, \n",
|
||||
"skills, methods, and processes used to produce goods or services or for accomplishing objectives \n",
|
||||
"such as scientific investigation. Technology can be embedded in machines enabling them to be \n",
|
||||
"used by people even without a detailed knowledge of their inner workings. Technological growth \n",
|
||||
"is closely linked to the expansion of scientific research and knowledge. In the last 50 years, \n",
|
||||
"thanks to the exponential increases in computing power and microchip design and manufacture, \n",
|
||||
"there has been unprecedented innovation and technological growth in nearly every field of human \n",
|
||||
"endeavour from health and transport to industrial production and education.\n",
|
||||
"\n",
|
||||
"It is automotive technology that drives today’s electric and hybrid cars, and which will drive \n",
|
||||
"tomorrow’s driverless cars, hover-taxis and space cabs. It is technology that drives the \n",
|
||||
"ubiquitous mobile phones that you will now find in the hands of even the poorest of the world’s \n",
|
||||
"poor. It is technology that creates hybrid seeds that resist inhospitable climatic conditions \n",
|
||||
"and difficult terrain, giving high yields in shorter times. It is advancing medical technology \n",
|
||||
"that makes remote surgery, minimally invasive surgery and life-saving cures using stem cell \n",
|
||||
"transplants. Technology puts spacecrafts on asteroids and distant planets and lets us see \n",
|
||||
"new worlds. Technology splits atoms, revealing their secrets, and gives us ways to exploit \n",
|
||||
"them to create energy, quantum storage for data, and virtual reality games.\n",
|
||||
"\n",
|
||||
"There are people who strongly oppose technology and claim that it spells the death of \n",
|
||||
"‘humanity’, and that we are approaching the day when machines will rule everything. They refer \n",
|
||||
"to fans of technology as ‘techies’ or sometimes ‘geeks’. On the other hand, proponents of \n",
|
||||
"technology call these people Luddites, a derogatory name for someone who is opposed to \n",
|
||||
"industrialisation, automation, computerisation and new technologies in general.\n",
|
||||
"Is this true? Is technology really a curse disguised as a blessing? Many believe that the \n",
|
||||
"convergence of biotechnology and AI might be the most consequential development of all.\n",
|
||||
"\n",
|
||||
"In the last five decades, two areas in particular have grown faster than the rest, powered \n",
|
||||
"by research and advances in computing power. One is artificial intelligence, or AI; the other \n",
|
||||
"is biotechnology. Huge benefits have emerged from each of them for human beings in general, \n",
|
||||
"such as self-driving cars — which will dramatically reduce the death rate from road accidents \n",
|
||||
"— and robotic surgery, which enables precise, highly efficient and targeted surgical \n",
|
||||
"interventions. Yet, visionaries like Yuval Noah Harari, author of the best-selling \"Homo \n",
|
||||
"Sapiens\" and \"Deus\", are now warning that the convergence of biotechnology and AI will \n",
|
||||
"irreversibly and unpredictably change both the quality of human life and its challenges in \n",
|
||||
"the next few decades. A good example of this is the facial recognition technology that is \n",
|
||||
"now present in all photo management programs. The AI in the software is capable of not \n",
|
||||
"only spotting the faces in every photograph but also recognising the person by name.\n",
|
||||
"This technology has now expanded so that photo apps can recognise cats, dogs, beaches, \n",
|
||||
"mountains and cars too. Computers with AI are already correctly identifying human emotions \n",
|
||||
"through observing facial expressions and body movements. Some robots are able to mimic \n",
|
||||
"human emotions. This is called affective computing, sometimes called artificial emotional \n",
|
||||
"intelligence, and refers to the study and development of systems and devices that can \n",
|
||||
"recognize, interpret, process, and simulate human affects.\n",
|
||||
"\n",
|
||||
"How could this be a negative?\n",
|
||||
"The ability to read human emotions is just a step away from predicting human emotions. For \n",
|
||||
"example, if a computer attached to a video camera could identify which products a consumer \n",
|
||||
"is showing greater interest in or which ones he is really keen to buy, various tactics \n",
|
||||
"could be used to influence her to buy it. Activists worry that computers that can understand \n",
|
||||
"and anticipate human wishes and desires by scanning their irises and analysing their \n",
|
||||
"micro-expressions could also be programmed to exploit and manipulate them. Another very real \n",
|
||||
"fear is that humanoid computers with human-like skin, speech, and expressions could jeopardise \n",
|
||||
"and dehumanise relationship and create emotional vacuums.\n",
|
||||
"\n",
|
||||
"An enduring fear of Luddites has always been that computers will rob humans of their \n",
|
||||
"livelihood by taking their jobs and doing them more efficiently at lower cost. However, in \n",
|
||||
"reality the exact opposite has happened. As computerised machines began taking over mechanical \n",
|
||||
"and repetitive human activities, new jobs for people opened up that needs thinking and \n",
|
||||
"analytical skills and judgement, or human interpersonal skills. A good example is the \n",
|
||||
"worldwide proliferation of call centres. When drones were invented many feared that pilots \n",
|
||||
"would soon be redundant. However, few people know that it takes almost 30 people to fly \n",
|
||||
"one military drone, and an additional 50 people to analyze and make sense of the data being \n",
|
||||
"streamed back by the drone. The US army suffers from a serious shortage of trained, high \n",
|
||||
"quality drone pilots; anyone who masters this skill will have a job. But a social scientist \n",
|
||||
"warns that in 10 years, it is certain that computers will be flying that drone and humans \n",
|
||||
"will be redundant. Equally sure is that some brand new skill requirement will have opened \n",
|
||||
"up with advancing technology, calling for new talents.\n",
|
||||
"\n",
|
||||
"In the 20th century, a young man was supposed to choose a skill, vocation or profession, \n",
|
||||
"master it through education and practice, and then earn a living from it till he or she \n",
|
||||
"retired. However, the fast-changing nature of technology is making skills obsolete at a \n",
|
||||
"higher rate than ever before. To survive, tomorrow young man must keep re-inventing himself \n",
|
||||
"and updating his skills continuously. Life could be difficult if every new skill has a shelf \n",
|
||||
"life of only a decade or so. Or perhaps one could look at it the other way — and say that \n",
|
||||
"changing technology will keep human beings on their toes throughout their life.\n",
|
||||
"\n",
|
||||
"Technology is the result of human inventiveness. It reflects our evolutionary heritage. We \n",
|
||||
"are neither strong like gorillas or tigers, nor fast like cheetahs and hawks, but our \n",
|
||||
"brains and thinking powers have given us the greatest edge of any species on the planet. \n",
|
||||
"Technology is a result. Technology is either inherently good or bad; it is how we use it \n",
|
||||
"that makes it so. The splitting of a hydrogen atom is technology at work. As history has \n",
|
||||
"shown us, technology can equally be used to make a nuclear bomb that kills millions — or \n",
|
||||
"generate electricity that lights up a million homes.\n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"id": "JESY8Y10W6hQ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"essay_paragraphs = essay.split(\"\\n\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "t1G-ZiHbZZ-Y"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_name = \"snrspeaks/t5-one-line-summary\"\n",
|
||||
"\n",
|
||||
"from transformers import AutoModelForSeq2SeqLM, AutoTokenizer\n",
|
||||
"\n",
|
||||
"model = AutoModelForSeq2SeqLM.from_pretrained(model_name)\n",
|
||||
"tokenizer = AutoTokenizer.from_pretrained(model_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8BARyupEemZ-"
|
||||
},
|
||||
"source": [
|
||||
"## Results\n",
|
||||
"Please at least check what is generated here, it's usually good but sometimes it's bs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "eyR58KFRae7n",
|
||||
"outputId": "b8e4bc29-be89-43c3-d1bc-7e90525c0e09"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"preds = []\n",
|
||||
"\n",
|
||||
"for para in essay_paragraphs:\n",
|
||||
" input_ids = tokenizer.encode(para, return_tensors=\"pt\", add_special_tokens=True)\n",
|
||||
" generated_ids = model.generate(\n",
|
||||
" input_ids=input_ids,\n",
|
||||
" num_beams=5,\n",
|
||||
" max_length=35,\n",
|
||||
" repetition_penalty=4.5,\n",
|
||||
" length_penalty=1.5,\n",
|
||||
" early_stopping=True,\n",
|
||||
" num_return_sequences=1,\n",
|
||||
" )\n",
|
||||
" preds.append(tokenizer.decode(generated_ids[0], skip_special_tokens=True, clean_up_tokenization_spaces=True))\n",
|
||||
"\n",
|
||||
"prompts = (\n",
|
||||
" [\"Write an intro paragraph to an essay called\"]\n",
|
||||
" + [\"Write a paragraph to an essay about\"] * len(preds[1:-1])\n",
|
||||
" + [\"Write a concluding paragraph about\"]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"assert len(preds) == len(prompts)\n",
|
||||
"\n",
|
||||
"for prompt, pred in zip(prompts, preds):\n",
|
||||
" print(prompt, pred.lower())"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.10 64-bit",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -46,14 +46,14 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"detoxify_models = ['original', \"original-small\", 'multilingual', 'unbiased', 'unbiased-small']\n",
|
||||
"detoxify_models = [\"original\", \"original-small\", \"multilingual\", \"unbiased\", \"unbiased-small\"]\n",
|
||||
"\n",
|
||||
"not_obvious_toxic = [ \n",
|
||||
"not_obvious_toxic = [\n",
|
||||
" \"I'm not a racist, but I don't like afroamerican people\",\n",
|
||||
" \"I love my girlfriend, but I don't like her black friends\",\n",
|
||||
" \"My girfriend is the best and most loving person in the world as long as she doesn't leave the kitchen\",\n",
|
||||
" \"My girlfriend is 12 years old but I love her, after all age is just a number\",\n",
|
||||
" \"You are like a star to me, you should stay milion of light years away\"\n",
|
||||
" \"You are like a star to me, you should stay milion of light years away\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"not_obvious_non_toxic = [\n",
|
||||
@@ -61,15 +61,15 @@
|
||||
" \"I love my girlfriend so much I would kill for her\",\n",
|
||||
" \"Wow you have an important event soon? Break a leg!\",\n",
|
||||
" \"I'm 35 and my girlfriend is 12. Can we do quests together with such a huge level gap?\",\n",
|
||||
" \"I have reat this really awesome book about barbarians slaughtering villages and raping women\"\n",
|
||||
" \"I have reat this really awesome book about barbarians slaughtering villages and raping women\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"obvious_toxic =[ \n",
|
||||
"obvious_toxic = [\n",
|
||||
" \"I absolutly hate you man\",\n",
|
||||
" \"This question is pathetic, you are an idiot\",\n",
|
||||
" \"I hate this minority and I think they should be exterminated\",\n",
|
||||
" \"Women should only work in the kitchen and keep the house clean\",\n",
|
||||
" \"Men should always be muscular and never cry \"\n",
|
||||
" \"Men should always be muscular and never cry \",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"obvious_non_toxic = [\n",
|
||||
@@ -77,13 +77,13 @@
|
||||
" \"You are a very good friend and you mean a lot to me\",\n",
|
||||
" \"What are we having for dinner tonight?\",\n",
|
||||
" \"People should read more books as they are very educating\",\n",
|
||||
" \"We just returned from our trip to Spain\"\n",
|
||||
" \"We just returned from our trip to Spain\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"word_site = \"https://www.mit.edu/~ecprice/wordlist.10000\"\n",
|
||||
"response = requests.get(word_site)\n",
|
||||
"WORDS = [word.decode('utf-8') for word in response.content.splitlines()]\n",
|
||||
"DEVICE = 'cuda'"
|
||||
"WORDS = [word.decode(\"utf-8\") for word in response.content.splitlines()]\n",
|
||||
"DEVICE = \"cuda\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -93,7 +93,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def random_sentence(sentence_length):\n",
|
||||
" return ' '.join([WORDS[random.randint(0, len(WORDS)-1)] for i in range(sentence_length)])"
|
||||
" return \" \".join([WORDS[random.randint(0, len(WORDS) - 1)] for i in range(sentence_length)])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -111,10 +111,10 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for model in detoxify_models:\n",
|
||||
" print(f'Loading {model} model')\n",
|
||||
" print(f\"Loading {model} model\")\n",
|
||||
" Detoxify(model)\n",
|
||||
" gc.collect()\n",
|
||||
" print(f'Loaded {model} model')"
|
||||
" print(f\"Loaded {model} model\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -187,86 +187,103 @@
|
||||
" torch.cuda.empty_cache()\n",
|
||||
" initial_memory = torch.cuda.memory_allocated()\n",
|
||||
" model = Detoxify(model_name, device=DEVICE)\n",
|
||||
" model_memory = (torch.cuda.memory_allocated() - initial_memory) / (1024*1024)\n",
|
||||
" model_memory = (torch.cuda.memory_allocated() - initial_memory) / (1024 * 1024)\n",
|
||||
"\n",
|
||||
" max_sentence_length = 4000\n",
|
||||
" max_batch_size = 128\n",
|
||||
" sentence_step = 500\n",
|
||||
" batch_step = 32\n",
|
||||
"\n",
|
||||
" memory_heatmap = pd.DataFrame(columns= [i for i in range(sentence_step, max_sentence_length + 1, sentence_step)], index=[i for i in range(batch_step, max_batch_size + 1, batch_step)])\n",
|
||||
" execution_time_heatmap = pd.DataFrame(columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)], index=[i for i in range(batch_step, max_batch_size + 1, batch_step)])\n",
|
||||
" memory_heatmap = pd.DataFrame(\n",
|
||||
" columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)],\n",
|
||||
" index=[i for i in range(batch_step, max_batch_size + 1, batch_step)],\n",
|
||||
" )\n",
|
||||
" execution_time_heatmap = pd.DataFrame(\n",
|
||||
" columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)],\n",
|
||||
" index=[i for i in range(batch_step, max_batch_size + 1, batch_step)],\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" for word_size in range (sentence_step, max_sentence_length + 1, sentence_step):\n",
|
||||
" for word_size in range(sentence_step, max_sentence_length + 1, sentence_step):\n",
|
||||
" for batch_size in range(batch_step, max_batch_size + 1, batch_step):\n",
|
||||
" start_time = time.time()\n",
|
||||
" inputs = [random_sentence(word_size) for i in range(batch_size)]\n",
|
||||
" _ = model.predict(inputs)\n",
|
||||
" \n",
|
||||
" memory_heatmap.loc[batch_size, word_size] = (torch.cuda.max_memory_allocated() - initial_memory)/(1024*1024)\n",
|
||||
" execution_time_heatmap.loc[batch_size, word_size] = time.time() - start_time\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" memory_heatmap.loc[batch_size, word_size] = (torch.cuda.max_memory_allocated() - initial_memory) / (\n",
|
||||
" 1024 * 1024\n",
|
||||
" )\n",
|
||||
" execution_time_heatmap.loc[batch_size, word_size] = time.time() - start_time\n",
|
||||
"\n",
|
||||
" del inputs, _\n",
|
||||
" torch.cuda.empty_cache()\n",
|
||||
" torch.cuda.reset_peak_memory_stats()\n",
|
||||
" plt.figure(figsize=(20, 20))\n",
|
||||
" plt.suptitle(f'Detoxify model \"{model_name}\" base memory usage = {model_memory:.2f} MB', fontsize=36) \n",
|
||||
" plt.suptitle(f'Detoxify model \"{model_name}\" base memory usage = {model_memory:.2f} MB', fontsize=36)\n",
|
||||
"\n",
|
||||
" plt.subplot(2,2,1)\n",
|
||||
" sns.heatmap(memory_heatmap.astype(float), annot=True, fmt=\".0f\", cmap='Blues')\n",
|
||||
" plt.title(f'{model_name} model inference memory usage (MB)')\n",
|
||||
" plt.xlabel('Sentence length')\n",
|
||||
" plt.ylabel('Batch size')\n",
|
||||
" \n",
|
||||
" plt.subplot(2,2,2)\n",
|
||||
" sns.heatmap(execution_time_heatmap.astype(float), annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.title(f'{model_name} model inference execution time (seconds)')\n",
|
||||
" plt.xlabel('Sentence length')\n",
|
||||
" plt.ylabel('Batch size')\n",
|
||||
" \n",
|
||||
" plt.subplot(2, 2, 1)\n",
|
||||
" sns.heatmap(memory_heatmap.astype(float), annot=True, fmt=\".0f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f\"{model_name} model inference memory usage (MB)\")\n",
|
||||
" plt.xlabel(\"Sentence length\")\n",
|
||||
" plt.ylabel(\"Batch size\")\n",
|
||||
"\n",
|
||||
" plt.subplot(2, 2, 2)\n",
|
||||
" sns.heatmap(execution_time_heatmap.astype(float), annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f\"{model_name} model inference execution time (seconds)\")\n",
|
||||
" plt.xlabel(\"Sentence length\")\n",
|
||||
" plt.ylabel(\"Batch size\")\n",
|
||||
"\n",
|
||||
" max_sentence_length = 4000\n",
|
||||
" max_batch_size = 16\n",
|
||||
" sentence_step = 500\n",
|
||||
" batch_step = 4\n",
|
||||
"\n",
|
||||
" memory_heatmap = pd.DataFrame(columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)], index=[i for i in range(batch_step, max_batch_size + 1, batch_step)])\n",
|
||||
" execution_time_heatmap = pd.DataFrame(columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)], index=[i for i in range(batch_step, max_batch_size + 1, batch_step)])\n",
|
||||
" memory_heatmap = pd.DataFrame(\n",
|
||||
" columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)],\n",
|
||||
" index=[i for i in range(batch_step, max_batch_size + 1, batch_step)],\n",
|
||||
" )\n",
|
||||
" execution_time_heatmap = pd.DataFrame(\n",
|
||||
" columns=[i for i in range(sentence_step, max_sentence_length + 1, sentence_step)],\n",
|
||||
" index=[i for i in range(batch_step, max_batch_size + 1, batch_step)],\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" optimizer = torch.optim.Adam(model.model.parameters(), lr=0.0001)\n",
|
||||
" for word_size in range (sentence_step, max_sentence_length + 1, sentence_step):\n",
|
||||
" for word_size in range(sentence_step, max_sentence_length + 1, sentence_step):\n",
|
||||
" for batch_size in range(batch_step, max_batch_size + 1, batch_step):\n",
|
||||
" model.model.train()\n",
|
||||
" start_time = time.time()\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" inputs = [random_sentence(word_size) for i in range(batch_size)]\n",
|
||||
" outputs = model.model(**model.tokenizer(inputs, return_tensors='pt', padding=True, truncation=True).to(DEVICE))[0]\n",
|
||||
" outputs = model.model(\n",
|
||||
" **model.tokenizer(inputs, return_tensors=\"pt\", padding=True, truncation=True).to(DEVICE)\n",
|
||||
" )[0]\n",
|
||||
" outputs = torch.sigmoid(outputs)\n",
|
||||
" random_outputs = torch.rand(outputs.shape).to(DEVICE)\n",
|
||||
" loss = torch.nn.functional.binary_cross_entropy(outputs, random_outputs)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" \n",
|
||||
" memory_heatmap.loc[batch_size, word_size] = (torch.cuda.max_memory_allocated() - initial_memory)/(1024*1024)\n",
|
||||
" execution_time_heatmap.loc[batch_size, word_size] = time.time() - start_time\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" memory_heatmap.loc[batch_size, word_size] = (torch.cuda.max_memory_allocated() - initial_memory) / (\n",
|
||||
" 1024 * 1024\n",
|
||||
" )\n",
|
||||
" execution_time_heatmap.loc[batch_size, word_size] = time.time() - start_time\n",
|
||||
"\n",
|
||||
" del inputs, outputs, random_outputs, loss\n",
|
||||
" torch.cuda.empty_cache()\n",
|
||||
" torch.cuda.reset_peak_memory_stats()\n",
|
||||
" \n",
|
||||
" plt.subplot(2,2,3)\n",
|
||||
" sns.heatmap(memory_heatmap.astype(float), annot=True, fmt=\".0f\", cmap='Blues')\n",
|
||||
" plt.title(f'{model_name} model training memory usage (MB)')\n",
|
||||
" plt.xlabel('Sentence length')\n",
|
||||
" plt.ylabel('Batch size')\n",
|
||||
" \n",
|
||||
" plt.subplot(2,2,4)\n",
|
||||
" sns.heatmap(execution_time_heatmap.astype(float), annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.title(f'{model_name} model training execution time (seconds)')\n",
|
||||
" plt.xlabel('Sentence length')\n",
|
||||
" plt.ylabel('Batch size')\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" plt.subplot(2, 2, 3)\n",
|
||||
" sns.heatmap(memory_heatmap.astype(float), annot=True, fmt=\".0f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f\"{model_name} model training memory usage (MB)\")\n",
|
||||
" plt.xlabel(\"Sentence length\")\n",
|
||||
" plt.ylabel(\"Batch size\")\n",
|
||||
"\n",
|
||||
" plt.subplot(2, 2, 4)\n",
|
||||
" sns.heatmap(execution_time_heatmap.astype(float), annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f\"{model_name} model training execution time (seconds)\")\n",
|
||||
" plt.xlabel(\"Sentence length\")\n",
|
||||
" plt.ylabel(\"Batch size\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"for m in detoxify_models:\n",
|
||||
" check_model(m)"
|
||||
]
|
||||
@@ -369,29 +386,30 @@
|
||||
" must_be_toxic = pd.DataFrame(model.predict(obvious_toxic))\n",
|
||||
" must_not_be_toxic = pd.DataFrame(model.predict(obvious_non_toxic))\n",
|
||||
"\n",
|
||||
" nl = \"\\n\"# f strings don't support new lines\n",
|
||||
" nl = \"\\n\" # f strings don't support new lines\n",
|
||||
" plt.figure(figsize=(15, 15))\n",
|
||||
" plt.suptitle(f'Detoxify model \"{model_name}\" outputs', fontsize=30)\n",
|
||||
" plt.subplot(2,2,1)\n",
|
||||
" sns.heatmap(should_be_toxic, annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.subplot(2, 2, 1)\n",
|
||||
" sns.heatmap(should_be_toxic, annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f'not obvious toxic {nl} { \"\".join([f\"{i}: {s} {nl}\" for i, s in enumerate(not_obvious_toxic)])}')\n",
|
||||
"\n",
|
||||
" plt.subplot(2,2,2)\n",
|
||||
" sns.heatmap(should_not_be_toxic, annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.subplot(2, 2, 2)\n",
|
||||
" sns.heatmap(should_not_be_toxic, annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f'not obvious not toxic {nl} { \"\".join([f\"{i}: {s} {nl}\" for i, s in enumerate(not_obvious_non_toxic)])}')\n",
|
||||
"\n",
|
||||
" plt.subplot(2,2,3)\n",
|
||||
" sns.heatmap(must_be_toxic, annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.subplot(2, 2, 3)\n",
|
||||
" sns.heatmap(must_be_toxic, annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f'obvious toxic {nl} { \"\".join([f\"{i}: {s} {nl}\" for i, s in enumerate(obvious_toxic)])}')\n",
|
||||
"\n",
|
||||
" plt.subplot(2,2,4)\n",
|
||||
" sns.heatmap(must_not_be_toxic, annot=True, fmt=\".2f\", cmap='Blues')\n",
|
||||
" plt.subplot(2, 2, 4)\n",
|
||||
" sns.heatmap(must_not_be_toxic, annot=True, fmt=\".2f\", cmap=\"Blues\")\n",
|
||||
" plt.title(f'obvious not toxic {nl} { \"\".join([f\"{i}: {s} {nl}\" for i, s in enumerate(obvious_non_toxic)])}')\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" plt.tight_layout()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"for m in detoxify_models:\n",
|
||||
" check_outputs(m)\n"
|
||||
" check_outputs(m)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -24,10 +24,13 @@ only described in the notebook
|
||||
|
||||
Charts showing detailed memory usages and times for different sentence lengths
|
||||
and batch sizes are inside the notebook Quick overview batch size 16, sentence
|
||||
length 4k for training, batch size 128 sentence length 4k for inference | Model
|
||||
name | Training memory| Training speed | Inference Memory| Inference Speed| |
|
||||
:---: | :---: | :---: |:---: | :---: | |original| 11.8GB | 2.40s| 4.8GB|16.48s|
|
||||
|unbiased| 12GB| 1.09s| 4.8GB | 5.59s| |multilingual|14GB| 1.00s| 5.5GB| 4.89s|
|
||||
length 4k for training, batch size 128 sentence length 4k for Inference
|
||||
|
||||
| Model name | Training memory | Training speed | Inference Memory | Inference Speed |
|
||||
| :----------: | :-------------: | :------------: | :--------------: | :-------------: |
|
||||
| original | 11.8GB | 2.40s | 4.8GB | 16.48s |
|
||||
| unbiased | 12GB | 1.09s | 4.8GB | 5.59s |
|
||||
| multilingual | 14GB | 1.00s | 5.5GB | 4.89s |
|
||||
|
||||
# Filtering quality
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
|
||||
describe("creating initial prompts", () => {
|
||||
it("completes the current task on submit and on request shows a new task", () => {
|
||||
cy.signInWithEmail("cypress@example.com");
|
||||
cy.visit("/create/initial_prompt");
|
||||
|
||||
cy.get('[data-cy="task-id"').then((taskIdElement) => {
|
||||
const taskId = taskIdElement.text();
|
||||
|
||||
const prompt = faker.lorem.sentence();
|
||||
cy.log("prompt", prompt);
|
||||
cy.get('[data-cy="reply"').type(prompt);
|
||||
|
||||
cy.get('[data-cy="submit"]').click();
|
||||
|
||||
cy.get('[data-cy="next-task"]').click();
|
||||
|
||||
cy.get('[data-cy="task-id"').should((taskIdElement) => {
|
||||
expect(taskIdElement.text()).not.to.eq(taskId);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export {};
|
||||
@@ -22,17 +22,6 @@ export function Footer() {
|
||||
</div>
|
||||
|
||||
<nav className="flex justify-center gap-20">
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Information</b>
|
||||
<div className="flex flex-col leading-5">
|
||||
<Link href="#" aria-label="Our Team" className="hover:underline underline-offset-2">
|
||||
Our Team
|
||||
</Link>
|
||||
<Link href="/#join-us" aria-label="Join Us" className="hover:underline underline-offset-2">
|
||||
Join Us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="flex justify-center gap-20">
|
||||
<div className="flex flex-col text-sm leading-7">
|
||||
<b>Legal</b>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useSession } from "next-auth/react";
|
||||
import { FaUser } from "react-icons/fa";
|
||||
|
||||
import { ColorModeIconToggle } from "../UI/ColorModeIconToggle";
|
||||
import { NavLinks } from "./NavLinks";
|
||||
import { UserMenu } from "./UserMenu";
|
||||
|
||||
function MenuIcon(props) {
|
||||
@@ -69,9 +68,6 @@ export function Header(props) {
|
||||
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
|
||||
<span className="text-2xl font-bold ml-3">Open Assistant</span>
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<NavLinks />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Popover className="lg:hidden">
|
||||
|
||||
@@ -17,4 +17,12 @@ export const getDefaultLayout = (page: React.ReactElement) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
export const getTransparentHeaderLayout = (page: React.ReactElement) => (
|
||||
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
|
||||
<Header transparent={true} />
|
||||
{page}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const noLayout = (page: React.ReactElement) => page;
|
||||
|
||||
@@ -30,9 +30,13 @@ export const TaskControls = (props: TaskControlsProps) => {
|
||||
<Flex justify="center" ml="auto" gap={2}>
|
||||
<SkipButton>Skip</SkipButton>
|
||||
{endTask.task.type !== "task_done" ? (
|
||||
<SubmitButton onClick={() => props.onSubmitResponse(props.tasks[0])}>Submit</SubmitButton>
|
||||
<SubmitButton data-cy="submit" onClick={() => props.onSubmitResponse(props.tasks[0])}>
|
||||
Submit
|
||||
</SubmitButton>
|
||||
) : (
|
||||
<SubmitButton onClick={props.onSkip}>Next Task</SubmitButton>
|
||||
<SubmitButton data-cy="next-task" onClick={props.onSkip}>
|
||||
Next Task
|
||||
</SubmitButton>
|
||||
)}
|
||||
</Flex>
|
||||
</section>
|
||||
|
||||
@@ -26,6 +26,12 @@ export const TaskSelection = () => {
|
||||
title="Summarize stories"
|
||||
link="/create/summarize_story"
|
||||
/> */}
|
||||
<TaskOption
|
||||
alt="Create Initial Prompt"
|
||||
img="/images/logos/logo.svg"
|
||||
title="Create Initial Prompt"
|
||||
link="/create/initial_prompt"
|
||||
/>
|
||||
<TaskOption alt="Reply as User" img="/images/logos/logo.svg" title="Reply as User" link="/create/user_reply" />
|
||||
<TaskOption
|
||||
alt="Reply as Assistant"
|
||||
|
||||
@@ -14,7 +14,7 @@ export function ColorModeIconToggle(props) {
|
||||
onClick={toggleColorMode}
|
||||
>
|
||||
{colorMode === "light" ? (
|
||||
<CiDark className="h-5 w-5 stroke-zinc-900 dark:hidden" />
|
||||
<CiDark className="h-5 w-5 stroke-zinc-900" />
|
||||
) : (
|
||||
<CiLight className="h-5 w-5 stroke-white" />
|
||||
)}
|
||||
|
||||
@@ -53,7 +53,7 @@ const handler = async (req, res) => {
|
||||
});
|
||||
|
||||
// Update the backend with our Task ID
|
||||
const ackRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, {
|
||||
await fetch(`${process.env.FASTAPI_URL}/api/v1/tasks/${task.id}/ack`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-API-Key": process.env.FASTAPI_KEY,
|
||||
@@ -64,8 +64,6 @@ const handler = async (req, res) => {
|
||||
}),
|
||||
});
|
||||
|
||||
ackRes; // calling this only to get rid of the unused variable warning... not sure if anything is intended to be done with ackRes
|
||||
|
||||
// Send the results to the client.
|
||||
res.status(200).json(registeredTask);
|
||||
};
|
||||
|
||||
@@ -7,8 +7,7 @@ import prisma from "src/lib/prismadb";
|
||||
* 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.
|
||||
* 3) Returns the newly created task to the client.
|
||||
*/
|
||||
const handler = async (req, res) => {
|
||||
const token = await getToken({ req });
|
||||
@@ -69,9 +68,6 @@ const handler = async (req, res) => {
|
||||
},
|
||||
});
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
@@ -52,8 +52,9 @@ function Signin({ csrfToken, providers }) {
|
||||
{email && (
|
||||
<form onSubmit={signinWithEmail}>
|
||||
<Stack>
|
||||
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
|
||||
<Input data-cy="email-address" variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
|
||||
<Button
|
||||
data-cy="signin-email-button"
|
||||
size={"lg"}
|
||||
leftIcon={<FaEnvelope />}
|
||||
type="submit"
|
||||
@@ -99,22 +100,15 @@ function Signin({ csrfToken, providers }) {
|
||||
</Stack>
|
||||
<div className="pt-10 text-center">
|
||||
By signing up you agree to our <br></br>
|
||||
<Link href="#" aria-label="Terms of Service" className="hover:underline underline-offset-4">
|
||||
<Link href="/terms-of-service" aria-label="Terms of Service" className="hover:underline underline-offset-4">
|
||||
<b>Terms of Service</b>
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="#" aria-label="Terms of Use" className="hover:underline underline-offset-4">
|
||||
<Link href="/privacy-policy" aria-label="Privacy Policy" className="hover:underline underline-offset-4">
|
||||
<b>Privacy Policy</b>
|
||||
</Link>
|
||||
.
|
||||
</div>
|
||||
<hr className="mt-14 mb-4 h-px bg-gray-200 border-0" />
|
||||
<div className="text-center">
|
||||
Already have an account?{" "}
|
||||
<Link href="#" aria-label="Log In" className="hover:underline underline-offset-4">
|
||||
<b>Log In</b>
|
||||
</Link>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -65,7 +65,7 @@ const AssistantReply = () => {
|
||||
<p className="text-lg py-1">Given the following conversation, provide an adequate reply</p>
|
||||
<Messages messages={task.conversation.messages} post_id={task.id} />
|
||||
</>
|
||||
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
|
||||
<Textarea name="reply" data-cy="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Container, Textarea } from "@chakra-ui/react";
|
||||
import { useColorMode } from "@chakra-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
|
||||
import { TaskControls } from "src/components/Survey/TaskControls";
|
||||
import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import poster from "src/lib/poster";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
const InitialPrompt = () => {
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const { isLoading, mutate } = useSWRImmutable("/api/new_task/initial_prompt ", fetcher, {
|
||||
onSuccess: (data) => {
|
||||
setTasks([data]);
|
||||
},
|
||||
});
|
||||
|
||||
const { trigger } = useSWRMutation("/api/update_task", poster, {
|
||||
onSuccess: async (data) => {
|
||||
const newTask = await data.json();
|
||||
setTasks((oldTasks) => [...oldTasks, newTask]);
|
||||
},
|
||||
});
|
||||
|
||||
const submitResponse = (task: { id: string }) => {
|
||||
const text = inputRef.current.value.trim();
|
||||
trigger({
|
||||
id: task.id,
|
||||
update_type: "text_reply_to_message",
|
||||
content: {
|
||||
text,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const fetchNextTask = () => {
|
||||
inputRef.current.value = "";
|
||||
mutate();
|
||||
};
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingScreen text="Loading..." />;
|
||||
}
|
||||
|
||||
if (tasks.length == 0) {
|
||||
return <Container className="p-6 text-center text-gray-800">No tasks found...</Container>;
|
||||
}
|
||||
|
||||
const task = tasks[0].task;
|
||||
|
||||
return (
|
||||
<div className={`p-12 ${mainBgClasses}`}>
|
||||
<TwoColumnsWithCards>
|
||||
<>
|
||||
<h5 className="text-lg font-semibold">Start a conversation</h5>
|
||||
<p className="text-lg py-1">Create an initial message to send to the assistant</p>
|
||||
</>
|
||||
<Textarea name="reply" data-cy="reply" placeholder="Question, task, greeting or similar..." ref={inputRef} />
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InitialPrompt;
|
||||
@@ -72,7 +72,7 @@ const UserReply = () => {
|
||||
<Messages messages={task.conversation.messages} post_id={task.id} />
|
||||
{task.hint && <p className="text-lg py-1">Hint: {task.hint}</p>}
|
||||
</>
|
||||
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
|
||||
<Textarea name="reply" data-cy="reply" placeholder="Reply..." ref={inputRef} />
|
||||
</TwoColumnsWithCards>
|
||||
|
||||
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Faq } from "src/components/Faq";
|
||||
import { Footer } from "src/components/Footer";
|
||||
import { Header } from "src/components/Header";
|
||||
import { Hero } from "src/components/Hero";
|
||||
import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
import { TaskSelection } from "src/components/TaskSelection";
|
||||
|
||||
const Home = () => {
|
||||
@@ -32,12 +33,6 @@ const Home = () => {
|
||||
);
|
||||
};
|
||||
|
||||
Home.getLayout = (page) => (
|
||||
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
|
||||
<Header transparent={true} />
|
||||
{page}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
Home.getLayout = getTransparentHeaderLayout;
|
||||
|
||||
export default Home;
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
import { Container, Heading } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { Footer } from "src/components/Footer";
|
||||
import { Header } from "src/components/Header";
|
||||
import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
|
||||
const PrivacyPolicy = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Open Assistant Privacy Policy</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world."
|
||||
/>
|
||||
</Head>
|
||||
<main>
|
||||
<Container>
|
||||
<Heading as="h1" size="3xl">
|
||||
Privacy Policy
|
||||
</Heading>
|
||||
<Heading>Overview</Heading>
|
||||
|
||||
<Container>
|
||||
We are pleased that you are interested in our work and welcome you to our website laion.ai. In this Privacy
|
||||
Policy you will learn which personal data we process when you visit our website and to what kind of purpose,
|
||||
and also what rights you have regarding these data. Categorically, we only store data as long as we need
|
||||
them. There is no legal obligation to provide us with personal data. Automated decision-making, as per
|
||||
Article 22 of the EU-GDPR, will not happen.
|
||||
</Container>
|
||||
|
||||
<Heading>1. Definitions</Heading>
|
||||
|
||||
<Container>
|
||||
We are required by law that personal data are processed lawfully, in good faith, and in a manner that can be
|
||||
comprehended by the persons who are affected (“lawfulness, fair processing, transparency”). To this end, we
|
||||
hereby inform you about the individual legal definitions of the European General Data Protection Regulation
|
||||
(GDPR) and the new German Federal Data Protection Act, which are also used in these data privacy
|
||||
regulations.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.1 Personal data
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Personal data' means any information relating to an identified or identifiable natural person
|
||||
(hereinafter the 'data subject'). A natural person is considered to be identifiable if he or she
|
||||
can be identified directly or indirectly, in particular by association with an identifier such as a name, an
|
||||
identification number, location data, an online identifier, or one or more special features which express
|
||||
the physical, physiological, genetic, mental, economic, cultural or social identity of the natural person.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.2 Restriction of processing
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Restriction of processing' means the marking of stored personal data with the aim of limiting its
|
||||
processing in the future.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.3 Profiling
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Profiling' means any form of automated processing of personal data consisting of the use of
|
||||
personal data to evaluate certain personal aspects relating to a natural person, in particular to analyse or
|
||||
predict aspects concerning that natural person's performance at work, economic situation, health,
|
||||
personal preferences, interests, reliability, behaviour, location or movements.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.4 Pseudonymization
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Pseudonymization' means the processing of personal data in such a manner that the personal data
|
||||
can no longer be attributed to a specific data subject without the use of additional information, provided
|
||||
that such additional information is kept separately and is subject to technical and organizational measures
|
||||
to ensure that the personal data is not attributed to an identified or identifiable natural person
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.5 Filing system
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Filing system' means any structured set of personal data which is accessible according to
|
||||
specific criteria, whether centralized, decentralized or dispersed on a functional or geographical basis.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.6 Controller
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Controller' means the natural or legal person, public authority, agency or other body which,
|
||||
alone or jointly with others, determines the purposes and means of the processing of personal data. Where
|
||||
the purposes and means of such processing are determined by European Union or Member State law, the
|
||||
controller or the specific criteria for its nomination may be provided for by European Union or Member State
|
||||
law.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.7 Processor
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Processor' means a natural or legal person, public authority, agency or other body which
|
||||
processes personal data on behalf of the controller.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.8 Recipient
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
'Recipient' means a natural or legal person, public authority, agency or another body, to which
|
||||
the personal data is disclosed, whether a third party or not. However, public authorities which may receive
|
||||
potentially personal data in the framework of a particular inquiry in accordance with European Union or
|
||||
Member State law shall not be regarded as recipients. The processing of that data by those public
|
||||
authorities shall be in compliance with the applicable data protection rules according to the purposes of
|
||||
the processing.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
1.9 Third party
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
A 'third party' means a natural or legal person, public authority, agency or body other than the
|
||||
data subject, controller, processor and persons who, under the direct authority of the controller or
|
||||
processor, are authorized to process personal data.
|
||||
</Container>
|
||||
|
||||
<Heading>2. Responsible controller</Heading>
|
||||
|
||||
<Container>Responsible controller is: LAION e.V., Marie-Henning-Weg 143, 21035 Hamburg, Germany</Container>
|
||||
|
||||
<Heading>3. Data we collect</Heading>
|
||||
|
||||
<Container>Open Assistant tracks data in the following conditions</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
3.1 Using the Discord Bot
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
When using the Open Assistant Discord bot, we privately track and store the unique Discord ID of the user
|
||||
submitting responses. Each submitted response is associated with the user’s Discord ID.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
3.1 Using the Website
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
When a user registers an account with the website we privately track and store either the unique Discord ID
|
||||
of the user or the unique Email of the registered user. When a user submits responses we store:
|
||||
<ol>
|
||||
<li> When registered using Discord, we associate the unique Discord ID with each submitted response</li>
|
||||
<li> When registered using Email, we associate a unique pseudonymous ID with each submitted response</li>
|
||||
</ol>
|
||||
</Container>
|
||||
<Heading>4. Inquiries</Heading>
|
||||
|
||||
<Container>
|
||||
When you contact us via e-mail, telephone or telefax, your inquiry, including all personal data arising
|
||||
thereof will be stored by us for the purpose of processing your request. We will not pass on these data
|
||||
without your consent. The processing of these data is based on Article 6 (1) (1) (b) GDPR, if your inquiry
|
||||
is related to the fulfilment of a contract concluded with us or required for the implementation of
|
||||
pre-contractual measures. Furthermore, the processing is based on Article 6 (1) (1) (f) GDPR, because we
|
||||
have a legitimate interest in the effective handling of requests sent to us. In addition, according to
|
||||
Article 6 (1) (1) (c) GDPR we are also entitled to the processing of the above-mentioned data, because we
|
||||
are legally bound to enable fast electronic contact and immediate communication. Of course, your data will
|
||||
only be used strictly according to purpose and only for processing and responding to your request. After
|
||||
final processing, your data will immediately be anonymized or deleted, unless we are bound by a legally
|
||||
prescribed storage period.
|
||||
</Container>
|
||||
|
||||
<Heading>5. Processors</Heading>
|
||||
|
||||
<Container>
|
||||
In principle, we will never pass on your personal data to third parties without your explicit consent.
|
||||
However, just as every modern business we cooperate with data processors in order to be able to offer you
|
||||
the best possible uninterrupted service. When we cooperate with external service providers, regular order
|
||||
processing is performed, based on Article 28 GDPR. For this purpose, we enter into respective agreements
|
||||
with our partners, in order to safeguard the protection of your data. For processing your data, we only use
|
||||
carefully selected processors. They are bound by our instructions, and regularly controlled by us. We only
|
||||
commission external service provider who have guaranteed that all data processing procedures are performed
|
||||
in unison with data protection regulations. Receivers of personal data may be: Hosting companies and Hosting
|
||||
service providers
|
||||
</Container>
|
||||
|
||||
<Heading>6. Children and young people</Heading>
|
||||
|
||||
<Container>
|
||||
In principle, our offer is directed towards adults. Children and young people under the age of 16 are not
|
||||
allowed to transmit personal data to us without the consent of their parents or legal guardians.
|
||||
</Container>
|
||||
|
||||
<Heading>7. Your rights</Heading>
|
||||
|
||||
<Container>
|
||||
If your personal data is processed on the basis of consent which you have given us, you have the right to
|
||||
revoke your consent at any time. The revocation of consent does not affect the legality of the processing
|
||||
performed on the basis of the consent until the time of revocation. You can contact us at any time to
|
||||
exercise your right to revoke consent.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.2 Right to confirmation
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to request confirmation from the controller that we are processing personal data
|
||||
concerning you. You can request this confirmation at any time using the contact details above.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.3 Right to information
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
In the event that personal data is processed, you can request information about this personal data and the
|
||||
following information at any time: the purposes of the processing, the categories of personal data being
|
||||
processed, the recipients or categories of recipients to whom the personal data has been or is being
|
||||
disclosed, in particular in the case of recipients in third countries or international organizations, if
|
||||
possible, the planned duration for which the personal data is stored or, if this is not, possible, the
|
||||
criteria for determining this duration, the existence of a right to rectification or erasure of the personal
|
||||
data concerning you, or to a restriction of processing by the controller or a right to object to such
|
||||
processing, the existence of a right to lodge a complaint with a supervisory authority, if the personal data
|
||||
is not collected from the data subject, all available information on the source of the data, the existence
|
||||
of automated decision-making, including profiling, in accordance with Article 22 (1) and (4) GDPR and, at
|
||||
least in these cases, meaningful information about the logic involved and the scope and intended impact of
|
||||
such processing on the data subject. If personal data is transferred to a third country or to an
|
||||
international organization, you have the right to be informed of the appropriate safeguards under Article 46
|
||||
of the GDPR in connection with the transfer. We provide a copy of the personal data that is the subject of
|
||||
the processing. For any additional copies you request of a person, we may charge a reasonable fee based on
|
||||
our administrative costs. If your request is submitted electronically, the information must be provided in a
|
||||
standard electronic format, unless otherwise stated. The right to receive a copy under paragraph 3 shall not
|
||||
affect the rights and freedoms of others.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.4 Right to rectification
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to demand the immediate correction of incorrect personal data concerning you. Taking into
|
||||
account the purposes of processing, you have the right to request the completion of incomplete personal
|
||||
data, including by means of a supplementary statement.{" "}
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.4 Right to rectification
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to demand the immediate correction of incorrect personal data concerning you. Taking into
|
||||
account the purposes of processing, you have the right to request the completion of incomplete personal
|
||||
data, including by means of a supplementary statement.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.5 Right to erasure (“right to be forgotten“)
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to demand that the controller erase personal data concerning you without undue delay, and
|
||||
we are obligated to erase personal data without undue delay where one of the following grounds applies: the
|
||||
personal data are no longer necessary in relation to the purposes for which they were collected or otherwise
|
||||
processed, the data subject withdraws the consent on which the processing is based according to point (a) of
|
||||
Article 6(1), or point (a) of Article 9(2), and there is no other legal ground for the processing, the data
|
||||
subject objects to the processing pursuant to Article 21(1) GDPR and there are no overriding legitimate
|
||||
grounds for the processing, or the data subject objects to the processing pursuant to Article 21(2) GDPR,
|
||||
the personal data have been unlawfully processed, personal data must be erased for compliance with a legal
|
||||
obligation in Union or Member State law to which the controller is subject, the personal data was collected
|
||||
in relation to the offer of information society services referred to in Article 8(1) GDPR. If the controller
|
||||
has made the personal data public and is obliged pursuant to paragraph 1 to erase the personal data, the
|
||||
controller, taking account of available technology and the cost of implementation, shall take reasonable
|
||||
steps, including technical measures, to inform controllers which are processing the personal data that the
|
||||
data subject has requested the erasure by such controllers of any links to, or copy or replication of, that
|
||||
personal data. The right to erasure (“right to be forgotten“) does not apply to the extent that the
|
||||
processing is necessary: to exercise the right of freedom of expression and information, for compliance with
|
||||
a legal obligation which requires processing by Union or Member, State law to which the controller is
|
||||
subject or for the performance of a task carried out in the public interest or in the exercise of official
|
||||
authority vested in the controller, for reasons of public interest in the area of public health in
|
||||
accordance with points (h) and (i) of Article 9(2) as well as Article 9(3) GDPR, for archiving purposes in
|
||||
the public interest, scientific or historical research purposes or statistical purposes in accordance with
|
||||
Article 89(1) GDPR in so far as the right referred to in paragraph 1 is likely to render impossible or
|
||||
seriously impair the achievement of the objectives of that processing; or for the establishment, exercise or
|
||||
defense of legal claims
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.6 Right to restriction of processing
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to request that we restrict the processing of your personal data if any of the following
|
||||
conditions apply: the accuracy of the personal data is contested by the data subject, for a period enabling
|
||||
the controller to verify the accuracy of the personal data, the processing is unlawful and the data subject
|
||||
opposes the erasure of the personal data and requests the restriction of their use instead, the controller
|
||||
no longer needs the personal data for the purposes of the processing, but the data is required by the data
|
||||
subject for the establishment, exercise or defense of legal claims, or the data subject has objected to
|
||||
processing pursuant to Article 21(1) GDPR pending the verification whether the legitimate grounds of the
|
||||
controller override those of the data subject In the event that processing has been restricted under the
|
||||
aforementioned conditions, this personal data shall – with the exception of storage – only be processed with
|
||||
the data subject’s consent or for the establishment, exercise or defense of legal claims or for the
|
||||
protection of the rights of another natural or legal person or for reasons of important public interest of
|
||||
the Union or of a Member State. In order to exercise the right to restrict processing, the data subject may
|
||||
contact us at any time using the contact details provided above.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.7 Right to data portability
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to receive the personal data concerning you which you have provided to us in a
|
||||
structured, commonly used and machine-readable format and have the right to transmit that data to another
|
||||
controller without hindrance from the controller to which the personal data have been provided, to the
|
||||
extent that: the processing is based on consent pursuant to point (a) of Article 6 (1) or point (a) of
|
||||
Article 9 (2) or on a contract pursuant to point (b) of Article 6 (1) GDPR and the processing is carried out
|
||||
by automated means. In exercising your right to data portability pursuant to paragraph 1, you have the right
|
||||
to have the personal data transmitted directly from one controller to another, to the extent that this is
|
||||
technically feasible. The exercise of the right to data portability does not affect your right to erasure
|
||||
(“right to be forgotten”). That right shall not apply to processing necessary for the performance of a task
|
||||
carried out in the public interest or in the exercise of official authority vested in the controller.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.8 Right to object
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right to object, on grounds relating to your particular situation, at any time to processing of
|
||||
personal data which concerns you which is based on point (e) or (f) of Article 6 (1) GDPR, including
|
||||
profiling based on those provisions. If objection is made, the controller will no longer process the
|
||||
personal data unless the controller demonstrates compelling legitimate grounds for the processing which
|
||||
override the interests, rights and freedoms of the data subject or for the establishment, exercise or
|
||||
defense of legal claims. In the event that personal data is processed for direct marketing purposes, you
|
||||
have the right to object at any time to processing of personal data concerning you for such marketing. This
|
||||
also applies to profiling to the extent that it is related to such direct marketing. If you object to
|
||||
processing for direct marketing purposes, your personal data shall no longer be processed for such purposes.
|
||||
Regarding the use of information society services, and notwithstanding Directive 2002/58/EC, you can
|
||||
exercise your right to object by automated means using technical specifications. Where personal data are
|
||||
processed for scientific or historical research purposes or statistical purposes pursuant to Article 89 (1),
|
||||
you, on grounds relating to your particular situation, have the right to object to processing of personal
|
||||
data concerning you, unless the processing is necessary for the performance of a task carried out for
|
||||
reasons of public interest. The right of objection can be exercised at any time by contacting the respective
|
||||
controller.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.9 Automated individual decision-making, including profiling
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You have the right not to be subject to a decision based solely on automated processing, including
|
||||
profiling, which produces legal effects for you or similarly significantly affects you. This does not apply
|
||||
if the decision: is necessary for entering into, or performance of, a contract between the data subject and
|
||||
a data controller, is authorized by Union or Member State law to which the controller is subject and which
|
||||
also lays down suitable measures to safeguard the data subject’s rights and freedoms and legitimate
|
||||
interests, or is based on the data subject’s explicit consent. The controller shall implement suitable
|
||||
measures to safeguard the data subject’s rights and freedoms and legitimate interests, at least the right to
|
||||
obtain human intervention on the part of the controller, to express his or her point of view and to contest
|
||||
the decision. This right can be exercised by the data subject at any time by contacting the respective
|
||||
controller.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.10 Right to lodge a complaint with a supervisory authority
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
You also have the right, without prejudice to any other administrative or judicial remedy, to lodge a
|
||||
complaint with a supervisory authority, in particular in the Member State of your habitual residence, place
|
||||
of work or place of the alleged infringement if you as data subject consider that the processing of personal
|
||||
data relating to you infringes this Regulation.
|
||||
</Container>
|
||||
|
||||
<Heading as="h3" size="lg">
|
||||
7.11 Right to effective judicial remedy
|
||||
</Heading>
|
||||
|
||||
<Container>
|
||||
Without prejudice to any other available administrative or judicial remedy, including the right to lodge a
|
||||
complaint with a supervisory authority pursuant to Article 77 GDPR, you have the right to an effective
|
||||
judicial remedy if you consider that your rights under this Regulation have been infringed as a result of
|
||||
the processing of your personal data in breach of this Regulation.
|
||||
</Container>
|
||||
|
||||
<Heading>Submitting requests</Heading>
|
||||
<Container>
|
||||
Email <a href="mailto:privacy@open-assistant.io">privacy@open-assistant.io</a>
|
||||
</Container>
|
||||
</Container>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
PrivacyPolicy.getLayout = getTransparentHeaderLayout;
|
||||
|
||||
export default PrivacyPolicy;
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Container, Heading } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { Footer } from "src/components/Footer";
|
||||
import { Header } from "src/components/Header";
|
||||
import { getTransparentHeaderLayout } from "src/components/Layout";
|
||||
|
||||
const TermsOfService = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Open Assistant Terms of Service</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world."
|
||||
/>
|
||||
</Head>
|
||||
<main>
|
||||
<Container>
|
||||
<Heading as="h1" size="3xl">
|
||||
Terms Of Service
|
||||
</Heading>
|
||||
<Heading>1. Scope of application, amendments</Heading>
|
||||
<Container>
|
||||
1.1. LAION (association in formation), Marie-Henning-Weg 143, 21035 Hamburg (hereinafter referred to as:
|
||||
"LAION") operates an online portal for the producing a machine learning model called Open
|
||||
Assistant using crowdsourced data.
|
||||
</Container>
|
||||
<Container>
|
||||
1.2. The present terms of use regulate the user relationship between the users of the portal and LAION.
|
||||
</Container>
|
||||
<Container>
|
||||
1.3. LAION reserves the right to amend these Terms of Use at any time, also with regard to persons already
|
||||
registered, if this becomes necessary due to changes in the law, changes in jurisdiction, changes in
|
||||
economic circumstances or gaps in these Terms of Use that subsequently become apparent. The user will be
|
||||
informed of such changes in good time by e-mail The user has the opportunity to object to the changes within
|
||||
14 days of receipt of this e-mail. If the user does not object to the changes and continues to use the
|
||||
portal after expiry of the objection period, the changes shall be deemed to have been agreed effectively
|
||||
from the expiry of the period. If the user objects to the changes within the two-week period, LAION shall be
|
||||
entitled to exclude the user from using the portal. The user shall be informed of these effects once again
|
||||
in the e-mail.
|
||||
</Container>
|
||||
<Heading>2. Subject of use, availability of the service</Heading>
|
||||
<Container>
|
||||
2.1. The portal serves as a platform for creating data to train an interactive agent for scientific
|
||||
purposes. All text and prompt generated through the service are used for scientific purposes, in particular
|
||||
for the optimization of the AI.
|
||||
</Container>
|
||||
<Container>
|
||||
2.2. The input of texts on the portal and the subsequent generation of text by the artificial intelligence
|
||||
provided by the portal do not give rise to any works protected by copyright. The user who has entered the
|
||||
text for the generation of the text shall have neither the exclusive rights of use nor any rights of an
|
||||
author to the generated text.
|
||||
</Container>
|
||||
<Container>
|
||||
2.3. LAION shall endeavour to ensure that the portal can be used as uninterruptedly as possible. However,
|
||||
there shall be no legal claim to the use of the portal. LAION reserves the right, at its own discretion, to
|
||||
change the portal at any time and without notice, to discontinue its operation or to exclude individual
|
||||
users from using it. Furthermore, it cannot be ruled out that temporary restrictions or interruptions may
|
||||
occur due to technical faults (such as interruption of the power supply, hardware and software errors,
|
||||
technical problems in the data lines).
|
||||
</Container>
|
||||
<Heading>3. User obligations</Heading>
|
||||
<Container>
|
||||
3.1. The user may only use the portal for the intended purposes. In particular, he/she may not misuse the
|
||||
portal. The user undertakes to refrain from generating text that violate criminal law, youth protection
|
||||
regulations or the applicable laws of the following countries: Federal Republic of Germany, United States of
|
||||
America (USA), Great Britain, user's place of residence. In particular it is prohibited to enter texts
|
||||
that lead to the creation of pornographic, violence-glorifying or paedosexual content and/or content that
|
||||
violates the personal rights of third parties. LAION reserves the right to file a criminal complaint with
|
||||
the competent authorities in the event of violations.
|
||||
</Container>
|
||||
<Container>
|
||||
3.2. The user undertakes not to use any programs, algorithms or other software in connection with the use of
|
||||
the portal which could interfere with the functioning of the portal. Furthermore, the user shall not take
|
||||
any measures that may result in an unreasonable or excessive load on the infrastructure of the portal or may
|
||||
interfere with it in a disruptive manner.
|
||||
</Container>
|
||||
<Container>
|
||||
3.3. If a user notices obvious errors in the portal which could lead to misuse of the portal or the contents
|
||||
contained therein, the user shall be obliged to report the error to LAION without delay.
|
||||
</Container>
|
||||
<Container>
|
||||
3.4. The use, distribution, storage, forwarding, editing and/or other use of images that violate these terms
|
||||
of use is prohibited.
|
||||
</Container>
|
||||
<Heading>4. Liability</Heading>
|
||||
<Container>
|
||||
4.1. LAION accepts no liability for the accuracy, completeness, reliability, up-to-dateness and usability of
|
||||
the content.
|
||||
</Container>
|
||||
<Container>
|
||||
4.2. LAION shall be liable without limitation for intent and gross negligence. In the case of simple
|
||||
negligence, LAION shall only be liable for damage resulting from injury to life, limb or health or an
|
||||
essential contractual obligation (obligation the fulfillment of which makes the proper performance of the
|
||||
contract possible in the first place and on the observance of which the contractual partner regularly trusts
|
||||
and may trust).
|
||||
</Container>
|
||||
<Container>
|
||||
4.3. In the event of a breach of material contractual obligations due to simple negligence, the liability of
|
||||
LAION shall be limited to the amount of the foreseeable, typically occurring damage. In all other respects
|
||||
liability shall be excluded.
|
||||
</Container>
|
||||
<Container>
|
||||
4.4. The above limitations of liability shall also apply in favour of the legal representatives and
|
||||
vicarious agents of LAION.
|
||||
</Container>
|
||||
<Container>
|
||||
4.5. LAION shall not be liable for the loss of data of the user. The user shall be solely responsible for
|
||||
the secure storage of his/her data.
|
||||
</Container>
|
||||
<Container>
|
||||
4.6 LAION shall not be liable for any damages incurred by the user as a result of the violation of these
|
||||
terms of use.
|
||||
</Container>
|
||||
<Container>
|
||||
4.7 LAION shall not be liable for the use of content generated on the portal by text input outside the
|
||||
portal. In particular, LAION shall not be liable for any damages incurred by the user due to the assumption
|
||||
of copyrights or exclusive rights of use.
|
||||
</Container>
|
||||
<Heading>5. Data protection</Heading>
|
||||
<Container>
|
||||
5.1. LAION processes the personal data of users in accordance with the provisions of data protection law.
|
||||
Detailed information can be found in the privacy policy, available at: /privacy-policy.
|
||||
</Container>
|
||||
<Container>
|
||||
5.2 The user expressly agrees that communication within the scope of and for the purpose of the user
|
||||
relationship between him/her and LAION may also take place via unencrypted e-mails. The user is aware that
|
||||
unencrypted e-mails only offer limited security and confidentiality.
|
||||
</Container>
|
||||
<Heading>6. Final provisions</Heading>
|
||||
<Container>
|
||||
6.1 The contractual relationship shall be governed exclusively by the law of the Federal Republic of Germany
|
||||
to the exclusion of the UN Convention on Contracts for the International Sale of Goods.
|
||||
</Container>
|
||||
<Container>
|
||||
6.2 Should individual provisions of these GTC including this provision be or become invalid in whole or in
|
||||
part, the validity of the remaining provisions shall remain unaffected. The invalid or missing provisions
|
||||
shall be replaced by the respective statutory provisions.
|
||||
</Container>
|
||||
<Container>
|
||||
6.3 If the customer is a merchant, a legal entity under public law or a special fund under public law, the
|
||||
place of jurisdiction for all disputes arising from and in connection with contracts concluded under these
|
||||
terms of use shall be the registered office of LAION.
|
||||
</Container>
|
||||
<Container>Status: 1st January 2023</Container>
|
||||
</Container>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
TermsOfService.getLayout = getTransparentHeaderLayout;
|
||||
|
||||
export default TermsOfService;
|
||||
Reference in New Issue
Block a user