Merge branch 'main' into discord-credentials

This commit is contained in:
Karthik Raju
2023-01-04 17:40:27 +05:30
committed by GitHub
55 changed files with 2711 additions and 657 deletions
+93
View File
@@ -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">
+8 -3
View File
@@ -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"]
+2
View File
@@ -0,0 +1,2 @@
# ensure pre-commit is installed
pre-commit install
+6 -9
View File
@@ -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/.*/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:
+9
View File
@@ -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
+1 -1
View File
@@ -1,7 +1,7 @@
BOT_TOKEN=<discord bot token>
DECLARE_GLOBAL_COMMANDS=<testing guild id>
OWNER_IDS=[<your user id>, <other user ids>]
PREFIX="/" # Don't change, this allows for slash commands in DMs
PREFIX="/" # DO NOT LEAVE EMPTY, slash command prefix in DMs
OASST_API_URL="http://localhost:8080" # No trailing '/'
OASST_API_KEY=""
+13 -6
View File
@@ -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
+2 -3
View File
@@ -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()
+173 -177
View File
@@ -1,14 +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.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
@@ -16,8 +29,10 @@ from oasst_shared.schemas.protocol import TaskRequestType
plugin = lightbulb.Plugin("WorkPlugin")
MAX_TASK_TIME = 60 * 60 # 1 hour
MAX_TASK_ACCEPT_TIME = 60 # 1 minute
MAX_TASK_TIME = 60 * 60 # seconds
MAX_TASK_ACCEPT_TIME = 60 * 10 # seconds
settings = Settings()
@plugin.command
@@ -33,25 +48,50 @@ MAX_TASK_ACCEPT_TIME = 60 # 1 minute
@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.Context, task_type: TaskRequestType) -> None:
@@ -71,38 +111,79 @@ async def _handle_task(ctx: lightbulb.Context, task_type: TaskRequestType) -> No
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 below:"))
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
@@ -110,7 +191,7 @@ async def _handle_task(ctx: lightbulb.Context, task_type: TaskRequestType) -> No
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:
@@ -127,33 +208,20 @@ async def _handle_task(ctx: lightbulb.Context, task_type: TaskRequestType) -> No
for id in log_channel_ids
]
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}")
)
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(EMPTY, embed=done_embed) for ch in channels if isinstance(ch, hikari.TextableChannel))
)
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
@@ -166,10 +234,12 @@ async def _select_task(
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:
@@ -181,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.Context, 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.
@@ -208,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
@@ -250,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 (
@@ -279,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
@@ -318,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.
@@ -344,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):
+208
View File
@@ -0,0 +1,208 @@
"""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")}
{_ordered_list(task.prompts)}
{_ranking_prompt("Reply with the numbers of best to worst prompts separated by commas (example: '4,1,3,2')")}
"""
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")}
{_conversation(task.conversation)}
{_user(None)}
{_ordered_list(task.replies)}
{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")}
"""
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")}
{_conversation(task.conversation)}
{_assistant(None)}
{_ordered_list(task.replies)}
{_ranking_prompt("Reply with the numbers of best to worst replies separated by commas (example: '4,1,3,2')")}
"""
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")}
{_conversation(task.conversation)}
{_hint(task.hint)}
{_response_prompt("Please provide a reply to the assistant.")}
"""
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")}
{_conversation(task.conversation)}
{_response_prompt("Please provide a reply to the assistant.")}
"""
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)
-7
View File
@@ -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"],
+3
View File
@@ -10,6 +10,9 @@ Also, the schemas are leaning heavily on the
[OpenAssistant Data Structures](https://docs.google.com/presentation/d/1iaX_nxasVWlvPiSNs0cllR9L_1neZq0RJxd6MFEalUY/edit?usp=sharing)
presentation.
_Note on conformity: be pragmatic and decide what makes sense 🙂 , it's more
important that we move forward than cramming everything into a uniform thing._
## Data Schemas
### Main structure: conversation trees
+79
View File
@@ -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 todays electric and hybrid cars, and which will drive \n",
"tomorrows 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 worlds \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 todays electric and hybrid cars, and which will drive \n",
"tomorrows 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 worlds \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)"
]
},
{
@@ -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 {};
+7 -7
View File
@@ -39,7 +39,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
"sharp": "^0.31.3",
"sharp": "0.31.2",
"swr": "^2.0.0",
"tailwindcss": "^3.2.4",
"use-debounce": "^9.0.2"
@@ -27907,9 +27907,9 @@
}
},
"node_modules/sharp": {
"version": "0.31.3",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.3.tgz",
"integrity": "sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==",
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz",
"integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==",
"hasInstallScript": true,
"dependencies": {
"color": "^4.2.3",
@@ -52121,9 +52121,9 @@
}
},
"sharp": {
"version": "0.31.3",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.3.tgz",
"integrity": "sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==",
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.31.2.tgz",
"integrity": "sha512-DUdNVEXgS5A97cTagSLIIp8dUZ/lZtk78iNVZgHdHbx1qnQR7JAHY0BnXnwwH39Iw+VKhO08CTYhIg0p98vQ5Q==",
"requires": {
"color": "^4.2.3",
"detect-libc": "^2.0.1",
+1 -1
View File
@@ -49,7 +49,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
"sharp": "^0.31.3",
"sharp": "0.31.2",
"swr": "^2.0.0",
"tailwindcss": "^3.2.4",
"use-debounce": "^9.0.2"
@@ -0,0 +1,28 @@
import { Button, useDisclosure } from "@chakra-ui/react";
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton } from "@chakra-ui/react";
import React from "react";
export const CollapsableText = ({ text, maxLength = 220 }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
if (typeof text != "string" || text.length <= maxLength) {
return text;
} else {
return (
<>
{text.substring(0, maxLength - 3)}
<Button style={{ display: "contents" }} onClick={onOpen}>
...
</Button>
<Modal isOpen={isOpen} onClose={onClose} size="xl" scrollBehavior={"inside"}>
<ModalOverlay style={{ width: "100%", height: "100%" }}>
<ModalContent maxH="400">
<ModalHeader>Full Text</ModalHeader>
<ModalCloseButton />
<ModalBody>{text}</ModalBody>
</ModalContent>
</ModalOverlay>
</Modal>
</>
);
}
};
+1 -1
View File
@@ -1,5 +1,5 @@
import clsx from "clsx";
export function Container({ className, ...props }) {
return <div className={clsx("mx-auto max-w-7xl px-4 sm:px-6 lg:px-8", className)} {...props} />;
return <div className={clsx("mx-auto max-w-7xl px-4", className)} {...props} />;
}
@@ -0,0 +1,17 @@
import { Box } from "@chakra-ui/react";
import { Message } from "./Messages";
export const ContextMessages = ({ messages }: { messages: Message[] }) => {
return (
<Box className="flex flex-col gap-1">
{messages.map((message, i) => {
return (
<Box key={i}>
<span>{message.is_assistant ? "Assistant: " : "User: "}</span>
<span>{message.text}</span>
</Box>
);
})}
</Box>
);
};
@@ -0,0 +1,94 @@
import { Badge, Box, Image, Link, Stack, StackDivider, Text, useColorModeValue } from "@chakra-ui/react";
export function LeaderboardTable() {
const backgroundColor = useColorModeValue("white", "gray.700");
const accentColor = useColorModeValue("gray.200", "gray.900");
//need to add streak info to chart
const leaderInfo = [
{
name: "fozziethebeat#6690",
image: "/images/temp-avatars/av1.jpg",
score: "5,208",
arrowDir: "increase",
streak: false,
streakCount: "5-Day Streak",
},
{
name: "k_nearest_neighbor#8579",
image: "/images/temp-avatars/av2.jpg",
score: "5,164",
arrowDir: "decrease",
streak: false,
streakCount: "",
},
{
name: "andreaskoepf#2266",
image: "/images/temp-avatars/av3.jpg",
score: "5,120",
arrowDir: "",
streak: false,
streakCount: "2-Day Streak",
},
{
name: "AbdBarho#1684",
image: "/images/temp-avatars/av4.jpg",
score: "4,260",
arrowDir: "",
streak: false,
streakCount: "",
},
{
name: "zu#9016",
image: "/images/temp-avatars/av5.jpg",
score: "3,608",
arrowDir: "",
streak: false,
streakCount: "",
},
];
return (
<main className="h-fit col-span-3">
<div className="flex flex-col gap-4">
<div className="flex items-end justify-between">
<Text className="text-2xl font-bold">Top 5 Contributors</Text>
<Link href="#" _hover={{ textDecoration: "none" }}>
<Text color="blue.400" className="text-sm font-bold">
View All -&gt;
</Text>
</Link>
</div>
<Box
backgroundColor={backgroundColor}
boxShadow="base"
dropShadow={accentColor}
borderRadius="xl"
className="p-6 shadow-sm"
>
<Stack divider={<StackDivider />} spacing="4">
<div className="grid grid-cols-4 items-center font-bold">
<p>Name</p>
<div className="col-start-4 flex justify-center">
<p>Score</p>
</div>
</div>
{leaderInfo.map((item, itemIndex) => (
<div key={itemIndex} className="grid grid-cols-4 items-center">
<div className="flex items-center gap-3">
<Image alt="Profile Picture" src={item.image} boxSize="7" borderRadius="full"></Image>
<p>{item.name}</p>
<Badge colorScheme="purple">{item.streakCount}</Badge>
</div>
<Box bg={backgroundColor} className="col-start-4 flex justify-center">
<p>{item.score}</p>
</Box>
</div>
))}
</Stack>
</Box>
</div>
</main>
);
}
@@ -0,0 +1,83 @@
import { Box, Button, Link, Text, Tooltip, useColorMode } from "@chakra-ui/react";
import { useRouter } from "next/router";
import { FiLayout, FiSun } from "react-icons/fi";
import { colors } from "styles/Theme/colors";
export function SideMenu() {
const router = useRouter();
const { colorMode, toggleColorMode } = useColorMode();
const buttonOptions = [
{
label: "Dashboard",
pathname: "/dashboard",
desc: "Dashboard Home",
icon: FiLayout,
},
// {
// label: "Leaderboard",
// pathname: "#",
// desc: "Public Leaderboard",
// icon: FiAward,
// },
// {
// label: "Stats",
// pathname: "#",
// desc: "User Statistics",
// icon: FiBarChart2,
// },
];
return (
<main className="sticky top-0 sm:h-full">
<Box
width={["100%", "100%", "100px", "280px"]}
backgroundColor={colorMode === "light" ? colors.light.div : colors.dark.div}
boxShadow="base"
borderRadius="xl"
className="grid grid-cols-4 gap-2 sm:flex sm:flex-col sm:justify-between p-4 h-full"
>
<nav className="grid grid-cols-3 col-span-3 sm:flex sm:flex-col gap-2">
{buttonOptions.map((item, itemIndex) => (
<Tooltip
key={itemIndex}
fontFamily="inter"
label={item.label}
placement="right"
className="hidden lg:hidden sm:block"
>
<Link key={`${item.label}-${itemIndex}`} href={item.pathname} style={{ textDecoration: "none" }}>
<Button
justifyContent={["center", "center", "center", "left"]}
gap="3"
size="lg"
width="full"
bg={router.pathname === item.pathname ? "blue.500" : null}
_hover={router.pathname === item.pathname ? { bg: "blue.600" } : null}
>
<item.icon className={router.pathname === item.pathname ? "text-blue-200" : null} />
<Text
fontWeight="normal"
color={router.pathname === item.pathname ? "white" : null}
className="hidden lg:block"
>
{item.label}
</Text>
</Button>
</Link>
</Tooltip>
))}
</nav>
<div>
<Tooltip fontFamily="inter" label="Toggle Dark Mode" placement="right" className="hidden lg:hidden sm:block">
<Button size="lg" width="full" justifyContent="center" onClick={toggleColorMode} gap="2">
<FiSun />
<Text fontWeight="normal" className="hidden lg:block">
{colorMode === "light" ? "Dark Mode" : "Light Mode"}
</Text>
</Button>
</Tooltip>
</div>
</Box>
</main>
);
}
@@ -0,0 +1,120 @@
import { Box, Flex, GridItem, Heading, SimpleGrid, Text, useColorModeValue } from "@chakra-ui/react";
import Link from "next/link";
const crTasks = [
{
label: "Reply as User",
desc: "Chat with Open Assistant and help improve its responses as you interact with it.",
type: "create",
pathname: "/create/assistant_reply",
},
{
label: "Reply as Assistant",
desc: "Help Open Assistant improve its responses to conversations with other users.",
type: "create",
pathname: "/create/assistant_reply",
},
];
const evTasks = [
{
label: "Rank User Replies",
type: "eval",
desc: "Help Open Assistant improve its responses to conversations with other users.",
pathname: "/evaluate/rank_user_replies",
},
{
label: "Rank Assistant Replies",
desc: "Score prompts given by Open Assistant based on their accuracy and readability.",
type: "eval",
pathname: "/evaluate/rank_assistant_replies",
},
{
label: "Rank Initial Prompts",
desc: "Score prompts given by Open Assistant based on their accuracy and readability.",
type: "eval;",
pathname: "/evaluate/rank_initial_prompts",
},
];
export const TaskOption = () => {
const backgroundColor = useColorModeValue("white", "gray.700");
return (
<Box className="flex flex-col gap-14" fontFamily="inter">
<div>
<Text className="text-2xl font-bold pb-4">Create</Text>
<SimpleGrid columns={[1, 2, 2, 3, 4]} gap={4}>
{crTasks.map((item, itemIndex) => (
<Link key={itemIndex} href={item.pathname}>
<GridItem
bg={backgroundColor}
borderRadius="xl"
boxShadow="base"
className="flex flex-col justify-between h-full"
>
<Box className="p-6 pb-10">
<Flex flexDir="column" gap="3">
<Heading size="md" fontFamily="inter">
{item.label}
</Heading>
<Text size="sm" opacity="80%">
{item.desc}
</Text>
</Flex>
</Box>
<Box
bg="blue.500"
borderBottomRadius="xl"
className="px-6 py-2 transition-colors duration-300"
_hover={{ backgroundColor: "blue.600" }}
>
<Text fontWeight="bold" color="white">
Go
</Text>
</Box>
</GridItem>
</Link>
))}
</SimpleGrid>
</div>
<div>
<Text className="text-2xl font-bold pb-4">Evaluate</Text>
<SimpleGrid columns={[1, 2, 2, 3, 4]} gap={4}>
{evTasks.map((item, itemIndex) => (
<Link key={itemIndex} href={item.pathname}>
<GridItem
bg={backgroundColor}
borderRadius="xl"
boxShadow="base"
className="flex flex-col justify-between h-full"
>
<Box className="p-6 pb-10">
<Flex flexDir="column" gap="3">
<Heading size="md" fontFamily="inter">
{item.label}
</Heading>
<Text size="sm" opacity="80%">
{item.desc}
</Text>
</Flex>
</Box>
<Box
bg="blue.500"
borderBottomRadius="xl"
className="px-6 py-2 transition-colors duration-300"
_hover={{ backgroundColor: "blue.600" }}
>
<Text fontWeight="bold" color="white">
Go
</Text>
</Box>
</GridItem>
</Link>
))}
</SimpleGrid>
</div>
</Box>
);
};
@@ -0,0 +1,3 @@
export { LeaderboardTable } from "./LeaderboardTable";
export { SideMenu } from "./SideMenu";
export { TaskOption } from "./TaskOption";
+10 -83
View File
@@ -1,43 +1,11 @@
import { Box, Button, useColorMode } from "@chakra-ui/react";
import { Popover } from "@headlessui/react";
import { AnimatePresence, motion } from "framer-motion";
import { Box, Button, Text, useColorMode } from "@chakra-ui/react";
import Image from "next/image";
import Link from "next/link";
import { useSession } from "next-auth/react";
import { FaUser } from "react-icons/fa";
import { ColorModeIconToggle } from "../UI/ColorModeIconToggle";
import { UserMenu } from "./UserMenu";
function MenuIcon(props) {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" {...props}>
<path d="M5 6h14M5 18h14M5 12h14" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
function ChevronUpIcon(props) {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" {...props}>
<path d="M17 14l-5-5-5 5" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
function MobileNavLink({ children, ...props }) {
return (
<Popover.Button
as={Link}
href={props.href}
className="block text-base leading-7 tracking-tight text-gray-700"
{...props}
>
{children}
</Popover.Button>
);
}
function AccountButton() {
const { data: session } = useSession();
if (session) {
@@ -53,70 +21,29 @@ function AccountButton() {
}
export function Header(props) {
const { data: session } = useSession();
const homeURL = session ? "/dashboard" : "/";
const { colorMode } = useColorMode();
const borderClass = props.transparent
? ""
: colorMode === "light"
? "border-b border-gray-400"
: "border-b border-zinc-800";
return (
<nav className={`oa-basic-theme ${borderClass}`}>
<Box className="flex mx-auto max-w-7xl justify-between py-8 px-10">
<div className="relative z-10 flex items-center gap-16">
<Link href="/" aria-label="Home" className="flex items-center">
<Box className="relative z-10 flex justify-between px-4 py-4">
<div className="relative z-10 flex items-center gap-10">
<Link href={homeURL} aria-label="Home" className="flex items-center">
<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>
<Text fontFamily="inter" fontSize="2xl" fontWeight="bold" className="ml-3">
Open Assistant
</Text>
</Link>
</div>
<div className="flex items-center gap-4">
<Popover className="lg:hidden">
{({ open }) => (
<>
<Popover.Button
className="relative z-10 inline-flex items-center rounded-lg stroke-gray-900 p-2 hover:bg-gray-200/50 hover:stroke-gray-600 active:stroke-gray-900 [&:not(:focus-visible)]:focus:outline-none"
aria-label="Toggle site navigation"
>
{({ open }) => (open ? <ChevronUpIcon className="h-6 w-6" /> : <MenuIcon className="h-6 w-6" />)}
</Popover.Button>
<AnimatePresence initial={false}>
{open && (
<>
<Popover.Overlay
static
as={motion.div}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-1 bg-gray-300/60 backdrop-blur"
/>
<Popover.Panel
static
as={motion.div}
initial={{ opacity: 0, y: -32 }}
animate={{ opacity: 1, y: 0 }}
exit={{
opacity: 0,
y: -32,
transition: { duration: 0.2 },
}}
className="absolute inset-x-0 top-0 z-0 origin-top rounded-b-2xl bg-white px-6 pb-6 pt-32 shadow-2xl shadow-gray-900/20"
>
<div className="space-y-4">
<MobileNavLink href="/#join-us">Join Us</MobileNavLink>
<MobileNavLink href="/#faqs">FAQs</MobileNavLink>
</div>
<div className="mt-8 flex flex-col gap-4"></div>
</Popover.Panel>
</>
)}
</AnimatePresence>
</>
)}
</Popover>
<AccountButton />
<UserMenu />
<ColorModeIconToggle className="ml-5" />
</div>
</Box>
</nav>
+6 -3
View File
@@ -1,7 +1,8 @@
import { useColorMode } from "@chakra-ui/react";
import { Text, useColorMode } from "@chakra-ui/react";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { useState } from "react";
import { colors } from "styles/Theme/colors";
export function NavLinks(): JSX.Element {
const [hoveredIndex, setHoveredIndex] = useState(null);
@@ -14,8 +15,8 @@ export function NavLinks(): JSX.Element {
return (
<>
{[
["Join Us", "/#join-us"],
["FAQ", "/#faq"],
["Join Us", "/#join-us"],
].map(([label, href], index) => (
<Link
key={label}
@@ -38,7 +39,9 @@ export function NavLinks(): JSX.Element {
/>
)}
</AnimatePresence>
<span className="relative z-10">{label}</span>
<Text color={colorMode === "light" ? colors.light.text : colors.dark.text} className="relative z-10">
{label}
</Text>
</Link>
))}
</>
+53 -31
View File
@@ -1,25 +1,33 @@
import { Box, useColorModeValue } from "@chakra-ui/react";
import { Box, Link, Text, useColorModeValue } from "@chakra-ui/react";
import { Popover } from "@headlessui/react";
import { AnimatePresence, motion } from "framer-motion";
import Image from "next/image";
import { signOut, useSession } from "next-auth/react";
import React from "react";
import { FaCog, FaSignOutAlt } from "react-icons/fa";
import { FiLayout, FiLogOut, FiSettings } from "react-icons/fi";
export function UserMenu() {
const { data: session } = useSession();
const backgroundColor = useColorModeValue("#FFFFFF", "#000000");
const backgroundColor = useColorModeValue("white", "gray.700");
const accentColor = useColorModeValue("gray.300", "gray.600");
if (!session) {
return <></>;
}
if (session && session.user) {
const accountOptions = [
{
name: "Dashboard",
href: "/dashboard",
desc: "Dashboard",
icon: FiLayout,
//For future use
},
{
name: "Account Settings",
href: "/account",
desc: "Account Settings",
icon: FaCog,
icon: FiSettings,
//For future use
},
];
@@ -28,18 +36,22 @@ export function UserMenu() {
{({ open }) => (
<>
<Popover.Button aria-label="Toggle Account Options" className="flex">
<div className="flex items-center gap-4 p-1 lg:pr-6 rounded-full border border-slate-300/70 hover:bg-gray-200/50 transition-colors duration-300">
<Box
borderWidth="1px"
borderColor={accentColor}
className="flex items-center gap-4 p-1 lg:pr-6 rounded-full transition-colors duration-300"
>
<Image
src={session.user.image || "/images/temp-avatars/av1.jpg"}
alt="Profile Picture"
width="40"
height="40"
width="36"
height="36"
className="rounded-full"
></Image>
<p data-cy="username" className="hidden lg:flex">
{session.user.name || session.user.email}
</p>
</div>
</Box>
</Popover.Button>
<AnimatePresence initial={false}>
{open && (
@@ -54,35 +66,45 @@ export function UserMenu() {
y: -10,
transition: { duration: 0.2 },
}}
className="absolute right-0 mt-3 w-screen bg-inherit max-w-xs p-4 rounded-md border border-slate-300/70"
>
<Box className="flex flex-col gap-1">
{accountOptions.map((item) => (
<a
key={item.name}
href={item.href}
aria-label={item.desc}
className="flex items-center rounded-md hover:bg-gray-200/50"
<Box
bg={backgroundColor}
borderWidth="1px"
borderColor={accentColor}
borderRadius="xl"
className="absolute right-0 mt-3 w-screen max-w-xs p-4"
>
<Box className="flex flex-col gap-1">
{accountOptions.map((item) => (
<Link
key={item.name}
href={item.href}
aria-label={item.desc}
className="flex items-center"
bg={backgroundColor}
_hover={{ textDecoration: "none" }}
>
<div className="p-4">
<item.icon className="text-blue-500" aria-hidden="true" />
</div>
<div>
<Text fontFamily="inter">{item.name}</Text>
</div>
</Link>
))}
<Link
className="flex items-center rounded-md cursor-pointer"
_hover={{ textDecoration: "none" }}
onClick={() => signOut({ callbackUrl: "/" })}
>
<div className="p-4">
<item.icon aria-hidden="true" />
<FiLogOut className="text-blue-500" />
</div>
<div>
<p>{item.name}</p>
<Text fontFamily="inter">Sign Out</Text>
</div>
</a>
))}
<a
className="flex items-center rounded-md hover:bg-gray-100 cursor-pointer"
onClick={() => signOut({ callbackUrl: "/" })}
>
<div className="p-4">
<FaSignOutAlt />
</div>
<div>
<p>Sign Out</p>
</div>
</a>
</Link>
</Box>
</Box>
</Popover.Panel>
</Box>
+8
View File
@@ -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;
+78
View File
@@ -0,0 +1,78 @@
import { Container } from "./Container";
const Roadmap = () => {
return (
<Container className="">
<div className="py-32">
<h2 className="text-4xl mb-16">Our Roadmap</h2>
<div className="flex flex-col items-center space-y-8 md:space-y-0 md:items-start md:flex-row md:justify-between">
<div className="flex flex-col items-center space-y-4">
<div className="h-[5rem] w-[5rem] border-4 border-[#a72a1e] rounded-full flex items-center justify-center">
<p className="font-bold text-[#a72a1e] text-center">ASAP</p>
</div>
<h4 className="font-bold text-xl text-[#a72a1e] text-center max-w-[10rem]">Minimum Viable Prototype</h4>
<ul className="ml-6 md:ml-8 lg:ml-6 space-y-4 text-[#a72a1e] list-disc">
<li>Data Collection Pipeline</li>
<li>RL on Human Feedback</li>
<li>Assistant v1 usable</li>
<li>Out January 2023!</li>
</ul>
</div>
<div>
<span className="w-[4vw] h-[4px] mt-8 bg-[#a72a1e] rounded-full hidden md:block" />
</div>
<span className="w-[4px] h-16 bg-[#a72a1e] rounded-full block md:hidden" />
<div className="flex flex-col items-center space-y-4">
<div className="h-[5rem] w-[5rem] border-4 border-[#858585] rounded-full flex items-center justify-center">
<p className="font-bold text-[#858585] text-center">
Q1
<br />
2023
</p>
</div>
<h4 className="font-bold text-xl text-[#858585] text-center max-w-[10rem]">Growing Up</h4>
<ul className="ml-6 md:ml-8 lg:ml-6 space-y-4 text-[#858585] list-disc">
<li>Retrieval Augmentation</li>
<li>Rapid Personalization</li>
<li>Using External Tools</li>
</ul>
</div>
<div>
<span className="w-[4vw] h-[4px] mt-8 bg-[#858585] rounded-full hidden md:block" />
</div>
<span className="w-[4px] h-16 bg-[#858585] rounded-full block md:hidden" />
<div className="flex flex-col items-center space-y-4">
<div className="h-[5rem] w-[5rem] border-4 border-[#858585] rounded-full flex items-center justify-center">
<p className="font-bold text-[#858585] text-center">
Q2
<br />
2023
</p>
</div>
<h4 className="font-bold text-xl text-[#858585] text-center max-w-[10rem]">Growing Up</h4>
<ul className="ml-6 md:ml-8 lg:ml-6 space-y-4 text-[#858585] list-disc">
<li>Third-Party Extentions</li>
<li>Device Control</li>
<li>Multi-Modality</li>
</ul>
</div>
<div>
<span className="w-[4vw] h-[4px] mt-8 bg-[#858585] rounded-full hidden md:block" />
</div>
<span className="w-[4px] h-16 bg-[#858585] rounded-full block md:hidden" />
<div className="flex flex-col items-center space-y-4">
<div className="h-[5rem] w-[5rem] border-4 border-[#858585] rounded-full flex items-center justify-center">
<p className="font-bold text-[#858585] text-center">...</p>
</div>
<h4 className="font-bold text-xl text-[#858585] text-center max-w-[10rem]">Growing Up</h4>
<ul className="ml-6 md:ml-8 lg:ml-6 space-y-4 text-[#858585] list-disc">
<li>What do you need?</li>
</ul>
</div>
</div>
</div>
</Container>
);
};
export default Roadmap;
+50
View File
@@ -0,0 +1,50 @@
import { Container } from "./Container";
const Services = () => {
return (
<div className="bg-white py-32 border-t-[1px] border-gray-150">
<Container className="">
<div className="grid grid-cols-1 lg:grid-cols-10 lg:grid-rows-2">
<div className="grid col-span-3 row-span-2">
<div className="min-h-[17.5rem] px-12 sm:px-16 row-span-1 flex flex-col justify-center bg-[#307bf3] rounded-tl-[45px] lg:rounded-tl-none lg:rounded-bl-[45px] rounded-br-[45px] lg:rounded-br-none lg:rounded-tr-[45px]">
<h4 className="font-bold text-white text-xl mb-4">Your Conversational Assistant</h4>
<span className="w-8 h-[2px] bg-white mb-4 block lg:hidden" />
<p className="text-white">State-of-the-Art chat assistant that can be personalized to your needs</p>
</div>
<div className="min-h-[17.5rem] px-12 sm:px-16 row-span-1 flex flex-col justify-center bg-[#275ddf] rounded-tl-[45px] rounded-br-[45px]">
<h4 className="font-bold text-white text-xl mb-4">Interface w/ external systems</h4>
<span className="w-8 h-[2px] bg-white mb-4 block lg:hidden" />
<p className="text-white">
Usage of APIs and third-party applications, described via language & demonstrations.
</p>
</div>
</div>
<div className="grid grid-rows-2 col-span-3 row-span-2">
<div className="min-h-[17.5rem] px-12 sm:px-16 row-span-1 flex flex-col justify-center bg-[#307bf3] lg:bg-[#275ddf] rounded-tl-[45px] rounded-br-[45px]">
<h4 className="font-bold text-white text-xl mb-4">Retrieval via Search Engines</h4>
<span className="w-8 h-[2px] bg-white mb-4 block lg:hidden" />
<p className="text-white">External, upgradeable knowledge: No need for billions of parameters.</p>
</div>
<div className="min-h-[17.5rem] px-12 sm:px-16 row-span-1 flex flex-col justify-center bg-[#275ddf] lg:bg-[#307bf3] rounded-tl-[45px] lg:rounded-tl-none lg:rounded-bl-[45px] rounded-br-[45px] lg:rounded-br-none lg:rounded-tr-[45px]">
<h4 className="font-bold text-white text-xl mb-4">A building block for developers</h4>
<span className="w-8 h-[2px] bg-white mb-4 block lg:hidden" />
<p className="text-white">Integrate OpenAssistant into your application.</p>
</div>
</div>
<div className="px-12 sm:px-16 py-20 lg:p-20 col-span-4 row-span-2 bg-[#1a44a1] lg:flex lg:flex-col lg:justify-center rounded-tl-[45px] rounded-br-[45px] lg:rounded-tr-[80px] lg:rounded-tl-none lg:rounded-br-none">
<h4 className="font-bold text-white text-xl mb-4">OpenAssistant unifies all knowledge work in one place</h4>
<span className="w-8 h-[2px] bg-white mb-4 block lg:hidden" />
<ul className="ml-4 sm:ml-12 mt-8 space-y-4 list-disc text-white">
<li>Uses modern deep learning</li>
<li>Runs on consumer hardware</li>
<li>Trains on human feedback</li>
<li>Free and open</li>
</ul>
</div>
</div>
</Container>
</div>
);
};
export default Services;
+2 -1
View File
@@ -18,6 +18,7 @@ import {
} from "@dnd-kit/sortable";
import { ReactNode, useEffect, useState } from "react";
import { CollapsableText } from "../CollapsableText";
import { SortableItem } from "./SortableItem";
export interface SortableProps {
@@ -64,7 +65,7 @@ export const Sortable = (props: SortableProps) => {
<Flex direction="column" gap={2} className={extraClasses}>
{itemsWithIds.map(({ id, item }) => (
<SortableItem key={id} id={id}>
{item}
<CollapsableText text={item} />
</SortableItem>
))}
</Flex>
@@ -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"
+25
View File
@@ -0,0 +1,25 @@
import { Container } from "./Container";
import Image from "next/image";
const Vision = () => {
return (
<div className="bg-gray-900 py-20">
<Container className="">
<div className="grid gap-16 items-center py-20 md:py-32 lg:grid-cols-2">
<div>
<h2 className="text-4xl text-white mb-6">Our Vision</h2>
<p className="text-2xl text-gray-400">
We want OpenAssistant to be the single, unifying platform that all other systems use to interface with
humans.
</p>
</div>
<div className="m-auto rounded-tl-[45px] rounded-br-[45px] overflow-hidden">
<Image src="/images/temp-avatars/av2.jpg" width={450} height={450} alt="temp-image" />
</div>
</div>
</Container>
</div>
);
};
export default Vision;
+1 -1
View File
@@ -4,5 +4,5 @@ export { default } from "next-auth/middleware";
* Guards all pages under `/grading` and redirects them to the sign in page.
*/
export const config = {
matcher: ["/create/:path*", "/evaluate/:path*", "/account/:path*"],
matcher: ["/create/:path*", "/evaluate/:path*", "/account/:path*", "/dashboard"],
};
+39
View File
@@ -0,0 +1,39 @@
import { Container } from "src/components/Container";
import Services from "src/components/Services";
import Vision from "src/components/Vision";
import Roadmap from "src/components/Roadmap";
import { CallToAction } from "src/components/CallToAction";
import Image from "next/image";
const AboutPage = () => {
return (
<div>
<Container className="">
<div className="grid gap-16 items-center py-20 md:py-40 lg:grid-cols-2">
<div className="m-auto order-2 lg:order-1">
<Image src="/images/logos/logo.png" width={450} height={450} alt="temp-image" />
</div>
<div className="space-y-8 order-1 lg:order-2">
<div>
<h1 className="text-4xl mb-6">What is OpenAssistant?</h1>
<p className="text-2xl">
OpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems,
and retrieve information dynamically to do so.
</p>
</div>
<p className="text-2xl">
It can be extended and personalized easily and is developed as free, open-source software.
</p>
</div>
</div>
</Container>
<Services />
<Vision />
<Roadmap />
<CallToAction />
</div>
);
};
export default AboutPage;
+2 -1
View File
@@ -7,6 +7,7 @@ import React, { useState } from "react";
export default function Account() {
const { data: session } = useSession();
const [username, setUsername] = useState("");
const updateUser = async (e: React.SyntheticEvent) => {
e.preventDefault();
try {
@@ -16,6 +17,7 @@ export default function Account() {
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
session.user.name = username;
await Router.push("/account");
} catch (error) {
console.error(error);
@@ -49,7 +51,6 @@ export default function Account() {
</Button>
</InputGroup>
</form>
<p>{session.user.email}</p>
</main>
</>
);
+1
View File
@@ -1,4 +1,5 @@
import { getSession } from "next-auth/react";
import prisma from "../../lib/prismadb";
// POST /api/post
// Required fields in body: title
+6 -4
View File
@@ -15,13 +15,13 @@ function Signin({ csrfToken, providers }) {
const emailEl = useRef(null);
const signinWithEmail = (ev: React.FormEvent) => {
ev.preventDefault();
signIn(email.id, { callbackUrl: "/", email: emailEl.current.value });
signIn(email.id, { callbackUrl: "/dashboard", email: emailEl.current.value });
};
const debugUsernameEl = useRef(null);
function signinWithDebugCredentials(ev: React.FormEvent) {
ev.preventDefault();
signIn(credentials.id, { callbackUrl: "/", username: debugUsernameEl.current.value });
signIn(credentials.id, { callbackUrl: "/dashboard", username: debugUsernameEl.current.value });
}
const { colorMode } = useColorMode();
@@ -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"
@@ -97,7 +98,8 @@ function Signin({ csrfToken, providers }) {
</Button>
)}
</Stack>
<div className="pt-10 text-center">
<hr className="mt-14 mb-4 h-px bg-gray-200 border-0" />
<div className="text-center">
By signing up you agree to our <br></br>
<Link href="/terms-of-service" aria-label="Terms of Service" className="hover:underline underline-offset-4">
<b>Terms of Service</b>
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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} />
+37
View File
@@ -0,0 +1,37 @@
import { Box, useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { Header } from "src/components/Header";
import { LeaderboardTable, SideMenu, TaskOption } from "src/components/Dashboard";
import { colors } from "styles/Theme/colors";
const Dashboard = () => {
const { colorMode } = useColorMode();
return (
<>
<Head>
<title>Dashboard - Open Assistant</title>
<meta name="description" content="Chat with Open Assistant and provide feedback." />
</Head>
<Box backgroundColor={colorMode === "light" ? colors.light.bg : colors.dark.bg} className="sm:overflow-hidden">
<Box className="sm:flex h-full gap-6">
<Box className="p-6 sm:pr-0">
<SideMenu />
</Box>
<Box className="flex flex-col overflow-auto p-6 sm:pl-0 gap-14">
<TaskOption />
<LeaderboardTable />
</Box>
</Box>
</Box>
</>
);
};
Dashboard.getLayout = (page) => (
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
<Header transparent={true} />
{page}
</div>
);
export default Dashboard;
@@ -1,7 +1,9 @@
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { useState } from "react";
import { ContextMessages } from "src/components/ContextMessages";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Message } from "src/components/Messages";
import { Sortable } from "src/components/Sortable/Sortable";
import { SurveyCard } from "src/components/Survey/SurveyCard";
import { TaskControls } from "src/components/Survey/TaskControls";
@@ -64,6 +66,7 @@ const RankAssistantReplies = () => {
}
const replies = tasks[0].task.replies as string[];
const messages = tasks[0].task.conversation.messages as Message[];
return (
<>
@@ -77,6 +80,7 @@ const RankAssistantReplies = () => {
<p className="text-lg py-1">
Given the following replies, sort them from best to worst, best being first, worst being last.
</p>
<ContextMessages messages={messages} />
<Sortable items={replies} onChange={setRanking} className="my-8" />
</SurveyCard>
@@ -1,7 +1,9 @@
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { useState } from "react";
import { ContextMessages } from "src/components/ContextMessages";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Message } from "src/components/Messages";
import { Sortable } from "src/components/Sortable/Sortable";
import { SurveyCard } from "src/components/Survey/SurveyCard";
import { TaskControls } from "src/components/Survey/TaskControls";
@@ -63,6 +65,7 @@ const RankUserReplies = () => {
);
}
const replies = tasks[0].task.replies as string[];
const messages = tasks[0].task.conversation.messages as Message[];
return (
<>
@@ -76,6 +79,7 @@ const RankUserReplies = () => {
<p className="text-lg py-1">
Given the following replies, sort them from best to worst, best being first, worst being last.
</p>
<ContextMessages messages={messages} />
<Sortable items={replies} onChange={setRanking} className="my-8" />
</SurveyCard>
+7 -22
View File
@@ -1,15 +1,10 @@
import Head from "next/head";
import { useSession } from "next-auth/react";
import { CallToAction } from "src/components/CallToAction";
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 { TaskSelection } from "src/components/TaskSelection";
import { getTransparentHeaderLayout } from "src/components/Layout";
const Home = () => {
const { data: session } = useSession();
return (
<>
<Head>
@@ -19,25 +14,15 @@ const Home = () => {
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>
{session ? (
<TaskSelection />
) : (
<main className="oa-basic-theme">
<Hero />
<CallToAction />
<Faq />
</main>
)}
<main className="oa-basic-theme">
<Hero />
<CallToAction />
<Faq />
</main>
</>
);
};
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;
+409
View File
@@ -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>
&apos;Personal data&apos; means any information relating to an identified or identifiable natural person
(hereinafter the &apos;data subject&apos;). 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>
&apos;Restriction of processing&apos; 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>
&apos;Profiling&apos; 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&apos;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>
&apos;Pseudonymization&apos; 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>
&apos;Filing system&apos; 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>
&apos;Controller&apos; 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>
&apos;Processor&apos; 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>
&apos;Recipient&apos; 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 &apos;third party&apos; 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 users 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 subjects 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 subjects rights and freedoms and legitimate
interests, or is based on the data subjects explicit consent. The controller shall implement suitable
measures to safeguard the data subjects 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;
+154
View File
@@ -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:
&quot;LAION&quot;) 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&apos;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;
+9 -1
View File
@@ -14,6 +14,14 @@ const components = {
Container: containerTheme,
};
const breakpoints = {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
};
const styles: Styles = {
global: (props) => ({
"*": {
@@ -34,4 +42,4 @@ const styles: Styles = {
}),
};
export const theme = extendTheme({ colors, config, styles, components });
export const theme = extendTheme({ colors, config, styles, components, breakpoints });
@@ -0,0 +1,25 @@
import {
color,
defineStyle,
defineStyleConfig,
// transition,
} from "@chakra-ui/styled-system";
import { colors } from "../colors";
const baseStyle = defineStyle(({ colorMode }) => ({
minWidth: "100%",
bg: colorMode === "light" ? colors.light.bg : colors.dark.bg,
// transition: "background-color 300ms cubic-bezier(0.4, 0, 1, 1)",
color: colorMode === "light" ? colors.light.text : colors.dark.text,
}));
const variants = {
"no-padding": {
padding: 0,
},
};
export const containerTheme = defineStyleConfig({
baseStyle,
variants,
});
+14
View File
@@ -0,0 +1,14 @@
export const colors = {
light: {
bg: "gray.100",
btn: "gray.50",
div: "white",
text: "black",
},
dark: {
bg: "gray.900",
btn: "gray.600",
div: "gray.700",
text: "gray.200",
},
};
+68
View File
@@ -0,0 +1,68 @@
import {
type ThemeConfig,
extendTheme,
usePrefersReducedMotion,
} from "@chakra-ui/react";
import { containerTheme } from "./Components/Container";
import { StyleFunctionProps, Styles } from "@chakra-ui/theme-tools";
const config: ThemeConfig = {
initialColorMode: "system",
useSystemColorMode: false,
disableTransitionOnChange: true,
};
const components = {
Container: containerTheme,
Box: (props: StyleFunctionProps) => ({
backgroundColor: props.colorMode === "light" ? "white" : "gray.800",
}),
Button: {
baseStyle: {
fontWeight: "normal",
},
sizes: {
lg: {
fontSize: "md",
paddingY: "7",
},
},
variants: {
solid: (props: StyleFunctionProps) => ({
bg: props.colorMode === "light" ? "gray.100" : "gray.600",
_hover: {
bg: props.colorMode === "light" ? "gray.200" : "#3D4A60",
},
_active: {
bg: props.colorMode === "light" ? "gray.300" : "#374254",
},
borderRadius: "lg",
}),
// gradient: (props: StyleFunctionProps) => ({
// bg: `linear-gradient(${white}, ${bgColor}) padding-box,
// linear-gradient(135deg, ${lgFrom}, ${lgTo}) border-box`,
// }),
},
},
};
const breakpoints = {
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
};
const styles = {
global: (props) => ({
main: {
fontFamily: "Inter",
},
header: {
fontFamily: "Inter",
},
}),
};
export const theme = extendTheme({ config, styles, components, breakpoints });