Merge branch 'main' of github.com:LAION-AI/Open-Chat-GPT

This commit is contained in:
Yannic Kilcher
2023-01-03 08:40:10 +01:00
50 changed files with 5753 additions and 557 deletions
+38
View File
@@ -0,0 +1,38 @@
# Train using supervised examples
Requirements
```
wandb
evaluate
datasets
transformers
torch
```
Start training reward model
```bash
python trainer.py --configs defaults galactica-125
```
## Dataset
For now we only support webgpt and summary dataset from OpenAI. Once
open-asisstant dataset are available it will be added here.
## Model
TBD
## Results
Experimental results in wandb
[here](https://wandb.ai/sanagnos/supervised-finetuning?workspace=user-sanagnos).
## TODOS
- decide on a model
- add special token to declare prompt and reply. Do nto freeze the weights for
these
- Merge utils etc with reward model
@@ -0,0 +1,37 @@
defaults:
learning_rate: 1e-5
gradient_checkpointing: false
gradient_accumulation_steps: 32
per_device_train_batch_size: 2
per_device_eval_batch_size: 2
weight_decay: 0.00
warmup_steps: 600
eval_steps: 200
save_steps: 500
max_length: 512
num_train_epochs: 3
logging_steps: 10
max_grad_norm: 2.0
save_total_limit: 4
eval_accumulation_steps:
freeze_layer:
datasets:
- webgpt
cache_dir: ~/.cache
loss_fn: CrossEntropyLoss
eval_size:
log_dir: "base"
galactica-125:
learning_rate: 5e-5
model_name: facebook/galactica-125m
weight_decay: 0.01
warmup_steps: 600
gradient_checkpointing: false
gradient_accumulation_steps: 2
per_device_train_batch_size: 4
per_device_eval_batch_size: 4
debug:
eval_steps: 20
eval_size: 100
@@ -0,0 +1,67 @@
from datasets import load_dataset
from sklearn.model_selection import train_test_split
from torch.utils.data import Dataset, Subset
class SquadV2Dataset(Dataset):
def __init__(self, cache_dir, split):
self.dataset = load_dataset("squad_v2", cache_dir=cache_dir, split=split)
def __len__(self):
return len(self.dataset)
def __getitem__(self, idx):
data = self.dataset[idx]
# dummy return first answer
return "".join([data["title"], ". ", data["context"], " " + data["question"]]), data["answers"]["text"][0]
class WebGPT(Dataset):
def __init__(self) -> None:
super().__init__()
dataset = load_dataset("openai/webgpt_comparisons")
questions = {}
# using prompt as our index will allows us
# to add additional generated prompt later
self.index2question = {}
for row in dataset["train"]:
question = row["question"]["full_text"]
if question not in self.index2question:
self.index2question[len(self.index2question)] = question
# only keep the best answer
questions[question] = row["answer_0" if row["score_0"] > row["score_1"] else "answer_1"]
self.questions = questions
def __len__(self):
return len(self.index2question)
def __getitem__(self, index):
question = self.index2question[index]
answer = self.questions[question]
return [question, answer]
def train_val_dataset(dataset, val_split=0.2):
train_idx, val_idx = train_test_split(
list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True
)
return Subset(dataset, train_idx), Subset(dataset, val_idx)
def get_one_dataset(conf, dataset_name):
dataset_name = dataset_name.lower()
if dataset_name == "squadv2":
raise ValueError("SquadV2 is not diverse enough for generation .. ")
train = SquadV2Dataset(conf.cache_dir, "train")
eval = SquadV2Dataset(conf.cache_dir, "validation")
elif dataset_name == "webgpt":
dataset = WebGPT()
train, eval = train_val_dataset(dataset, val_split=0.2)
else:
raise ValueError(f"Unknown dataset {dataset_name}")
return train, eval
@@ -0,0 +1,85 @@
from dataclasses import dataclass
from typing import Optional, Union
import numpy as np
import torch
from torch.nn import functional as F
from transformers.tokenization_utils_base import PaddingStrategy, PreTrainedTokenizerBase
@dataclass
class DialogueDataCollator:
"""
Expects a list of texts corresponding to a sequence of [question, answer, question, answer, ...] pairs.
"""
tokenizer: PreTrainedTokenizerBase
padding: Union[bool, str, PaddingStrategy] = True
max_length: Optional[int] = None
pad_to_multiple_of: Optional[int] = None
def __call__(self, features):
# TODO add special tokens for question and answer here
# additional_special_tokens = ['<question>', '<answer>']
prompt_tokens = ["Question: ", "Answer: "]
flatten_messages = []
label_masks = []
for messages in features:
assert len(messages) % 2 == 0, "Number of messages must be even"
messages = [
(prompt_tokens[0] if i % 2 == 0 else "") + x + ((" " + prompt_tokens[1]) if i % 2 == 0 else "")
for i, x in enumerate(messages)
]
# Add a way for the model to terminate generation, reinitialize prompter
messages.append(prompt_tokens[0])
flatten_messages.append(
self.tokenizer(
"".join(messages),
truncation=True,
max_length=self.max_length,
return_offsets_mapping=True,
)
)
message_change_indices = np.cumsum([len(x) for x in messages[:-1]])
# for each token an integer indicating the index of the message it belongs to. Just to create the label mask.
# TEXT: Question: Hello, how are you? Answer: I am fine. Question: What is your name? Answer: My name is John.
# MESSAGE_INDICES: 0 0 0 0 0 0 1 1 1 2 2 2 2 2 2 3 3 3 3
# If no result in next, we are predicting the last termination token(s)
message_indices = list(
map(
lambda x: next((i for i, val in enumerate(message_change_indices) if val >= x), -2),
list(map(lambda x: x[1], flatten_messages[-1]["offset_mapping"])),
)
)
label_mask = np.roll(list(map(lambda x: x % 2 == 1, message_indices)), -1, -1)
try:
label_mask[[i for i in range(len(message_indices)) if message_indices[i] == -2][0] - 1] = True
except IndexError:
# an aftermath of padding
pass
label_masks.append(label_mask)
flatten_messages[-1].pop("offset_mapping")
batch = self.tokenizer.pad(
flatten_messages,
padding=self.padding,
max_length=self.max_length,
pad_to_multiple_of=self.pad_to_multiple_of,
return_tensors="pt",
)
dim = batch["input_ids"].shape[-1]
batch["label_masks"] = torch.stack([F.pad(torch.tensor(x), (0, dim - len(x))) for x in label_masks])
for k in list(batch.keys()):
if k not in ["input_ids", "attention_mask", "label_masks"]:
batch.pop(k)
return batch
+15
View File
@@ -0,0 +1,15 @@
from torch import nn
class CrossEntropyLoss(nn.CrossEntropyLoss):
def __init__(self, weight=None, size_average=None, ignore_index=-100, reduce=None, reduction="mean"):
super(CrossEntropyLoss, self).__init__(weight, size_average, ignore_index, reduce, reduction)
def forward(self, input, target, mask=None):
if mask is not None:
mask = mask.view(-1)
input = input.view(-1, input.size(-1))
target = target.view(-1)
input = input[mask]
target = target[mask]
return super(CrossEntropyLoss, self).forward(input, target)
+200
View File
@@ -0,0 +1,200 @@
import argparse
import os
from dataclasses import dataclass
from distutils.util import strtobool
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import torch
from torch import nn
from torch.utils.data import Dataset
from transformers import (
DataCollator,
EvalPrediction,
PreTrainedModel,
PreTrainedTokenizerBase,
Trainer,
TrainerCallback,
TrainingArguments,
get_cosine_schedule_with_warmup,
)
from utils import get_dataset, get_loss, get_model, get_tokenizer, read_yamls
os.environ["WANDB_PROJECT"] = "supervised-finetuning"
@dataclass
class CustomTrainingArguments(TrainingArguments):
loss_function: str = "CrossEntropyLoss"
def compute_metrics(eval_pred):
pred_ids = eval_pred.predictions
labels = eval_pred.label_ids
return {"accuracy": (pred_ids[labels > 0] == labels[labels > 0]).mean()}
def preprocess_logits_for_metrics(logits, labels):
pred_ids = torch.argmax(logits, dim=-1)
return pred_ids
class SFTTrainer(Trainer):
def __init__(
self,
model: Union[PreTrainedModel, nn.Module] = None,
args: TrainingArguments = None,
data_collator: Optional[DataCollator] = None,
train_dataset: Optional[Dataset] = None,
eval_dataset: Optional[Dataset] = None,
tokenizer: Optional[PreTrainedTokenizerBase] = None,
model_init: Callable[[], PreTrainedModel] = None,
compute_metrics: Optional[Callable[[EvalPrediction], Dict]] = None,
callbacks: Optional[List[TrainerCallback]] = None,
optimizers: Tuple[torch.optim.Optimizer, torch.optim.lr_scheduler.LambdaLR] = (None, None),
preprocess_logits_for_metrics: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = None,
):
super().__init__(
model,
args,
data_collator,
train_dataset,
eval_dataset,
tokenizer,
model_init,
compute_metrics,
callbacks,
optimizers,
preprocess_logits_for_metrics,
)
self.loss_fct = get_loss(args.loss_function)
def fetch_scheduler(self):
return get_cosine_schedule_with_warmup(
self.optimizer,
num_warmup_steps=self.args.warmup_steps,
num_training_steps=self.num_train_steps,
num_cycles=1,
last_epoch=-1,
)
def compute_loss(self, model, inputs, return_outputs=False):
labels_mask = inputs.pop("label_masks")
outputs = model(**inputs)
loss = self.loss_fct(outputs.get("logits"), torch.roll(inputs["input_ids"], -1, -1), mask=labels_mask)
return (loss, outputs) if return_outputs else loss
def _compute_loss(self, model, inputs):
labels_mask = inputs.pop("label_masks")
inputs = self._prepare_inputs(inputs)
outputs = model(**inputs)
logits = outputs.get("logits")
targets = torch.roll(inputs["input_ids"], -1, -1)
loss = self.loss_fct(outputs.get("logits"), targets, mask=labels_mask)
return loss, logits, targets, labels_mask
def prediction_step(
self,
model: nn.Module,
inputs: Dict[str, Union[torch.Tensor, Any]],
prediction_loss_only: bool,
ignore_keys: Optional[List[str]] = None,
) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor], Optional[torch.Tensor]]:
with torch.no_grad():
loss, logits, labels, labels_mask = self._compute_loss(model, inputs)
labels[~labels_mask] = -1
loss = loss.mean().detach()
if self.args.prediction_loss_only:
return (loss, None, None)
return (loss, logits, labels)
def _strtobool(x):
return bool(strtobool(x))
def argument_parsing(notebook=False, notebook_args=None):
parser = argparse.ArgumentParser()
parser.add_argument("--configs", nargs="+", required=True)
if notebook:
args, remaining = parser.parse_known_args(notebook_args)
else:
args, remaining = parser.parse_known_args()
# Config from YAML
conf = {}
configs = read_yamls("./configs")
for name in args.configs:
if "," in name:
for n in name.split(","):
conf.update(configs[n])
else:
conf.update(configs[name])
# Override config from command-line
parser = argparse.ArgumentParser()
for key, value in conf.items():
type_ = type(value) if value is not None else str
if type_ == bool:
type_ = _strtobool
parser.add_argument(f"--{key}", type=type_, default=value)
return parser.parse_args(remaining)
if __name__ == "__main__":
training_conf = argument_parsing()
model = get_model(training_conf)
tokenizer = get_tokenizer(training_conf)
train, evals, collate_fn = get_dataset(training_conf, tokenizer)
args = CustomTrainingArguments(
output_dir=f"{training_conf.model_name}-{training_conf.log_dir}-finetuned",
num_train_epochs=training_conf.num_train_epochs,
warmup_steps=training_conf.warmup_steps,
loss_function=training_conf.loss_fn,
learning_rate=float(training_conf.learning_rate),
fp16=True,
gradient_checkpointing=training_conf.gradient_checkpointing,
gradient_accumulation_steps=training_conf.gradient_accumulation_steps,
per_device_train_batch_size=training_conf.per_device_train_batch_size,
per_device_eval_batch_size=training_conf.per_device_eval_batch_size,
weight_decay=training_conf.weight_decay,
max_grad_norm=training_conf.max_grad_norm,
logging_steps=training_conf.logging_steps,
save_total_limit=training_conf.save_total_limit,
evaluation_strategy="steps",
eval_steps=training_conf.eval_steps,
save_steps=training_conf.save_steps,
eval_accumulation_steps=training_conf.eval_accumulation_steps,
report_to="wandb",
)
assert len(evals) > 0
trainer = SFTTrainer(
model,
args,
train_dataset=train,
eval_dataset=evals,
data_collator=collate_fn,
tokenizer=tokenizer,
compute_metrics=compute_metrics,
preprocess_logits_for_metrics=preprocess_logits_for_metrics,
)
trainer.train()
+111
View File
@@ -0,0 +1,111 @@
from pathlib import Path
import yaml
from custom_datasets import get_one_dataset
from custom_datasets.dialogue_collator import DialogueDataCollator
from losses import CrossEntropyLoss
from sklearn.model_selection import train_test_split
from torch.utils.data import ConcatDataset, Subset
from transformers import AutoModelForCausalLM, AutoTokenizer
SUPPORTED_MODELS = ["galactica"]
def get_tokenizer(conf):
tokenizer = AutoTokenizer.from_pretrained(conf.model_name, cache_dir=conf.cache_dir)
if "galactica" in conf.model_name:
tokenizer.add_special_tokens({"pad_token": "<pad>", "eos_token": "</s>"})
return tokenizer
def get_model(conf):
if not any([x in conf.model_name for x in SUPPORTED_MODELS]):
raise ValueError(
f"Model {conf.model_name} not supported. Supported models: {SUPPORTED_MODELS}. "
"To include more make sure the masking is dne correctly... (decoder only supported for now)"
)
model = AutoModelForCausalLM.from_pretrained(conf.model_name, cache_dir=conf.cache_dir)
if conf.freeze_layer:
model = freeze_top_n_layers(model, conf.freeze_layer)
model_parameters = filter(lambda p: p.requires_grad, model.parameters())
params = sum([p.numel() for p in model_parameters])
print("Number of trainable parameters: {}M".format(int(params / 1e6)))
return model
def get_dataset(conf, tokenizer):
train_datasets, evals = [], {}
for dataset_name in conf.datasets:
train, val = get_one_dataset(conf, dataset_name)
train_datasets.append(train)
evals[dataset_name] = Subset(val, list(range(min(len(val), conf.eval_size)))) if conf.eval_size else val
train = ConcatDataset(train_datasets)
collate_fn = DialogueDataCollator(tokenizer, max_length=conf.max_length)
return train, evals, collate_fn
def get_loss(loss):
if loss == "CrossEntropyLoss":
return CrossEntropyLoss()
else:
raise ValueError(f"Loss {loss} not supported")
def read_yamls(dir):
conf = {}
no_conf = True
for config_file in Path(dir).glob("**/*.yaml"):
no_conf = False
with config_file.open("r") as f:
conf.update(yaml.safe_load(f))
if no_conf:
print(f"WARNING: No yaml files found in {dir}")
return conf
def train_val_dataset(dataset, val_split=0.2):
train_idx, val_idx = train_test_split(
list(range(len(dataset))), test_size=val_split, random_state=666, shuffle=True
)
return Subset(dataset, train_idx), Subset(dataset, val_idx)
def freeze_top_n_layers(model, target_layers):
# its possible we can simply detect which module is a ModuleList
# and simply freeze the module without doing string parsing
for name, param in model.named_parameters():
if "embed" in name:
param.requires_grad = False
elif ".layer" in name or ".h." in name:
tokens = name.split(".")
layer_ = None
for token in tokens:
if token.isdigit():
layer_ = int(token)
break
if layer_ is not None and layer_ < target_layers:
# print('freeze ', layer_, name)
param.requires_grad = False
return model
if __name__ == "__main__":
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bigscience/bloomz-560m")
freeze_top_n_layers(model, 10)
print(model.state_dict().keys())
+4 -1
View File
@@ -3,7 +3,10 @@ const nextConfig = {
output: "standalone",
reactStrictMode: true,
experimental: {
scrollRestoration: true,
/* Disabling this for now only because it causes a warning in the console that cannot be silenced for eslint
If this can be resolved, we should re-enable this.
*/
// scrollRestoration: true,
},
};
+4444 -30
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -22,6 +22,7 @@
"@dnd-kit/core": "^6.0.6",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.1",
"@dnd-kit/utilities": "^3.2.1",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@headlessui/react": "^1.7.7",
@@ -39,9 +40,11 @@
"eslint-plugin-simple-import-sort": "^8.0.0",
"focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"install": "^0.13.0",
"next": "13.0.6",
"next-auth": "^4.18.6",
"nodemailer": "^6.8.0",
"npm": "^9.2.0",
"postcss-focus-visible": "^7.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
+2 -2
View File
@@ -1,11 +1,11 @@
export function AuthLayout({ children }) {
return (
<main className="flex items-center justify-center sm:py-4 subpixel-antialiased">
<div className="flex items-center justify-center sm:py-4 subpixel-antialiased">
<div className="flex items-center w-full max-w-2xl flex-col px-4 sm:px-6">
<div className="flex-auto items-center justify-center w-full py-10 px-4 sm:mx-0 sm:flex-none sm:rounded-2xl sm:p-4">
{children}
</div>
</div>
</main>
</div>
);
}
+38 -6
View File
@@ -1,16 +1,48 @@
import { CircleBackground } from "./CircleBackground";
import { useColorMode } from "@chakra-ui/react";
import { useId } from "react";
import { Container } from "./Container";
export function CallToAction() {
function CircleBackground({ width = 558, height = 558, ...props }) {
const id = useId();
const { colorMode } = useColorMode();
const baseRingColor = colorMode === "light" ? "#777" : "#000";
const gradStopColor = colorMode === "light" ? "#fff" : "#000";
return (
<section id="join-us" className="relative overflow-hidden bg-gray-900 py-20 sm:py-28">
<svg viewBox="0 0 558 558" width={width} height={height} fill="none" aria-hidden="true" {...props}>
<defs>
<linearGradient id={id} x1="79" y1="16" x2="105" y2="237" gradientUnits="userSpaceOnUse">
<stop stopColor={gradStopColor} />
<stop offset="1" stopColor={baseRingColor} stopOpacity="0" />
</linearGradient>
</defs>
<path
opacity=".2"
d="M1 279C1 125.465 125.465 1 279 1s278 124.465 278 278-124.465 278-278 278S1 432.535 1 279Z"
stroke={baseRingColor}
/>
<path d="M1 279C1 125.465 125.465 1 279 1" stroke={`url(#${id})`} strokeLinecap="round" />
</svg>
);
}
export function CallToAction() {
const { colorMode } = useColorMode();
const bgColorClass = colorMode === "light" ? "bg-gray-900" : "bg-gray-50";
const headingColorClass = colorMode === "light" ? "text-white" : "text-black";
const textColorClass = colorMode === "light" ? "text-gray-300" : "text-black";
return (
<section id="join-us" className={`relative overflow-hidden py-20 sm:py-28 ${bgColorClass} ${textColorClass}`}>
<div className="absolute top-1/2 left-20 -translate-y-1/2 sm:left-1/2 sm:-translate-x-1/2">
<CircleBackground color="#fff" className="animate-spin-slower" />
<CircleBackground className="animate-spin-slower" />
</div>
<Container className="relative">
<div className="mx-auto max-w-md sm:text-center">
<h2 className="text-3xl font-medium tracking-tight text-white sm:text-4xl">Join Us</h2>
<p className="mt-4 text-lg text-gray-300">
<h2 className={`text-3xl font-medium tracking-tight sm:text-4xl ${headingColorClass}`}>Join Us</h2>
<p className="mt-4 text-lg">
All open source projects begin with people like you. Open source is the belief that if we collaborate we can
together gift our knowledge and technology to the world for the benefit of humanity. Are you in? Find us
here:
@@ -1,22 +0,0 @@
import { useId } from "react";
export function CircleBackground({ color, width = 558, height = 558, ...props }) {
const id = useId();
return (
<svg viewBox="0 0 558 558" width={width} height={height} fill="none" aria-hidden="true" {...props}>
<defs>
<linearGradient id={id} x1="79" y1="16" x2="105" y2="237" gradientUnits="userSpaceOnUse">
<stop stopColor={color} />
<stop offset="1" stopColor={color} stopOpacity="0" />
</linearGradient>
</defs>
<path
opacity=".2"
d="M1 279C1 125.465 125.465 1 279 1s278 124.465 278 278-124.465 278-278 278S1 432.535 1 279Z"
stroke={color}
/>
<path d="M1 279C1 125.465 125.465 1 279 1" stroke={`url(#${id})`} strokeLinecap="round" />
</svg>
);
}
+10 -3
View File
@@ -1,3 +1,5 @@
import { useColorMode } from "@chakra-ui/react";
import { Container } from "./Container";
const faqs = [
@@ -25,11 +27,16 @@ const faqs = [
];
export function Faq() {
const { colorMode } = useColorMode();
const headingColorClass = colorMode === "light" ? "text-gray-900" : "text-white";
const textColorClass = colorMode === "light" ? "text-gray-700" : "text-gray-100";
return (
<section id="faq" aria-labelledby="faqs-title" className="border-t border-gray-200 py-20 sm:py-32">
<Container className="">
<div className="mx-auto max-w-2xl lg:mx-0">
<h2 id="faqs-title" className="text-3xl font-medium tracking-tight text-gray-900">
<h2 id="faqs-title" className={`text-3xl font-medium tracking-tight ${headingColorClass}`}>
Frequently Asked Questions
</h2>
{/* <p className="mt-2 text-lg text-gray-600">
@@ -52,8 +59,8 @@ export function Faq() {
<ul role="list" className="space-y-10">
{column.map((faq, faqIndex) => (
<li key={faqIndex}>
<h3 className="text-lg font-semibold leading-6 text-gray-900">{faq.question}</h3>
<p className="mt-4 text-sm text-gray-700">{faq.answer}</p>
<h3 className={`text-lg font-semibold leading-6 ${headingColorClass}`}>{faq.question}</h3>
<p className={`mt-4 text-sm ${textColorClass}`}>{faq.answer}</p>
</li>
))}
</ul>
+59 -70
View File
@@ -2,6 +2,7 @@ import {
Button,
Checkbox,
Flex,
Grid,
Popover,
PopoverAnchor,
PopoverArrow,
@@ -15,6 +16,7 @@ import {
SliderTrack,
Spacer,
useBoolean,
useId,
} from "@chakra-ui/react";
import { FlagIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
import { useState } from "react";
@@ -65,58 +67,47 @@ export const FlaggableElement = (props) => {
isLazy
lazyBehavior="keepMounted"
>
<div className="inline-block float-left">
<Grid templateColumns="1fr min-content" gap={2}>
<PopoverAnchor>{props.children}</PopoverAnchor>
<PopoverTrigger>
<Button color="transparent">
<FlagIcon
className="h-5 w-5 ml-3 align-center text-gray-400 group-hover:text-gray-500"
aria-hidden="true"
/>
<Button h="full">
<FlagIcon className="w-4 text-gray-400 group-hover:text-gray-500" aria-hidden="true" />
</Button>
</PopoverTrigger>
</div>
</Grid>
<PopoverContent width="fit-content">
<PopoverArrow />
<PopoverCloseButton />
<div className="flex mt-3 ">
<PopoverBody>
<ul>
{TEXT_LABEL_FLAGS.map((option, i) => {
return (
<FlagCheckboxLi
option={option}
key={i}
idx={i}
checkboxValues={checkboxValues}
sliderValues={sliderValues}
checkboxHandler={handleCheckboxState}
sliderHandler={handleSliderState}
></FlagCheckboxLi>
);
})}
</ul>
<div className="flex justify-center ml-auto">
<Button
isDisabled={
!checkboxValues.reduce((all, current) => {
return all | current;
}, false)
}
onClick={() => submitResponse()}
className="bg-indigo-600 text-black hover:bg-indigo-700"
>
Report
</Button>
</div>
</PopoverBody>
<div className="relative h-4">
<PopoverCloseButton />
</div>
<PopoverBody>
{TEXT_LABEL_FLAGS.map((option, i) => (
<FlagCheckbox
option={option}
key={i}
idx={i}
checkboxValues={checkboxValues}
sliderValues={sliderValues}
checkboxHandler={handleCheckboxState}
sliderHandler={handleSliderState}
/>
))}
<Flex justify="center">
<Button
isDisabled={!checkboxValues.some(Boolean)}
onClick={submitResponse}
className="bg-indigo-600 text-black hover:bg-indigo-700"
>
Report
</Button>
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
};
function FlagCheckboxLi(props: {
function FlagCheckbox(props: {
option: textFlagLabels;
idx: number;
checkboxValues: boolean[];
@@ -136,37 +127,35 @@ function FlagCheckboxLi(props: {
);
}
const id = useId();
return (
<li>
<Flex>
<Checkbox
onChange={(e) => {
props.checkboxHandler(e.target.checked, props.idx);
}}
/>
<label
className=" ml-1 mr-1 text-sm form-check-label hover:cursor-pointer"
htmlFor={props.option.attributeName}
>
<span className="text-gray-800 hover:text-blue-700 float-left">{props.option.labelText}</span>
{AdditionalExplanation}
</label>
<Spacer />
<Slider
width="100px"
isDisabled={!props.checkboxValues[props.idx]}
defaultValue={100}
onChangeEnd={(val) => {
props.sliderHandler(val / 100, props.idx);
}}
>
<SliderTrack>
<SliderFilledTrack />
<SliderThumb />
</SliderTrack>
</Slider>
</Flex>
</li>
<Flex gap={1}>
<Checkbox
id={id}
onChange={(e) => {
props.checkboxHandler(e.target.checked, props.idx);
}}
/>
<label className="text-sm form-check-label" htmlFor={id}>
<span className="text-gray-800 hover:text-blue-700 float-left">{props.option.labelText}</span>
{AdditionalExplanation}
</label>
<Spacer />
<Slider
width="100px"
isDisabled={!props.checkboxValues[props.idx]}
defaultValue={100}
onChangeEnd={(val) => {
props.sliderHandler(val / 100, props.idx);
}}
>
<SliderTrack>
<SliderFilledTrack />
<SliderThumb />
</SliderTrack>
</Slider>
</Flex>
);
}
interface textFlagLabels {
+70 -60
View File
@@ -1,71 +1,81 @@
import { useColorMode } from "@chakra-ui/react";
import Image from "next/image";
import Link from "next/link";
import { Container } from "./Container";
export function Footer() {
return (
<footer className="border-t border-gray-200 bg-white">
<main>
<Container className="">
<div className="flex flex-wrap justify-between gap-y-12 py-10 lg:items-center lg:py-16">
<div className="flex items-center text-black pr-8">
<Link href="/" aria-label="Home" className="flex items-center">
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="52" height="52" alt="logo" />
</Link>
const { colorMode } = useColorMode();
const bgColorClass = colorMode === "light" ? "bg-transparent" : "bg-gray-800";
const borderClass = colorMode === "light" ? "border-slate-200" : "border-transparent";
<div className="ml-2">
<p className="text-base font-bold">Open Assistant</p>
<p className="text-sm">Conversational AI for everyone.</p>
return (
<footer className={bgColorClass}>
<div className={`flex mx-auto max-w-7xl justify-between py-10 px-10 border-t ${borderClass}`}>
<div className="flex items-center pr-8">
<Link href="/" aria-label="Home" className="flex items-center">
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="52" height="52" alt="logo" />
</Link>
<div className="ml-2">
<p className="text-base font-bold">Open Assistant</p>
<p className="text-sm">Conversational AI for everyone.</p>
</div>
</div>
<nav className="flex justify-center gap-20">
<div className="flex flex-col text-sm leading-7">
<b>Information</b>
<div className="flex flex-col leading-5">
<Link href="#" aria-label="Our Team" className="hover:underline underline-offset-2">
Our Team
</Link>
<Link href="/#join-us" aria-label="Join Us" className="hover:underline underline-offset-2">
Join Us
</Link>
</div>
</div>
<nav className="flex justify-center gap-20">
<div className="flex flex-col text-sm leading-7">
<b>Legal</b>
<div className="flex flex-col leading-5">
<Link href="/privacy-policy" aria-label="Privacy Policy" className="hover:underline underline-offset-2">
Privacy Policy
</Link>
<Link
href="/terms-of-service"
aria-label="Terms of Service"
className="hover:underline underline-offset-2"
>
Terms of Service
</Link>
</div>
</div>
<nav className="flex justify-center gap-20">
<div className="flex flex-col text-sm leading-7">
<b>Legal</b>
<div className="flex flex-col leading-5">
<Link
href="/privacy-policy"
aria-label="Privacy Policy"
className="hover:underline underline-offset-2"
>
Privacy Policy
</Link>
<Link
href="/terms-of-service"
aria-label="Terms of Service"
className="hover:underline underline-offset-2"
>
Terms of Service
</Link>
</div>
<div className="flex flex-col text-sm leading-7">
<b>Connect</b>
<div className="flex flex-col leading-5">
<Link
href="https://github.com/LAION-AI/Open-Assistant"
rel="noopener noreferrer nofollow"
target="_blank"
aria-label="Privacy Policy"
className="hover:underline underline-offset-2"
>
Github
</Link>
<Link
href="https://discord.gg/pXtnYk9c"
rel="noopener noreferrer nofollow"
target="_blank"
aria-label="Terms of Service"
className="hover:underline underline-offset-2"
>
Discord
</Link>
</div>
<div className="flex flex-col text-sm leading-7">
<b>Connect</b>
<div className="flex flex-col leading-5">
<Link
href="https://github.com/LAION-AI/Open-Assistant"
rel="noopener noreferrer nofollow"
target="_blank"
aria-label="Privacy Policy"
className="hover:underline underline-offset-2"
>
Github
</Link>
<Link
href="https://discord.gg/pXtnYk9c"
rel="noopener noreferrer nofollow"
target="_blank"
aria-label="Terms of Service"
className="hover:underline underline-offset-2"
>
Discord
</Link>
</div>
</div>
</nav>
</div>
</Container>
</main>
</div>
</nav>
{/* </div> */}
</nav>
</div>
</footer>
);
}
@@ -22,4 +22,15 @@ const Template = (args) => {
};
export const Default = Template.bind({});
Default.args = { session: { data: { user: { name: "StoryBook user" } }, status: "authenticated" }, transparent: false };
Default.args = {
session: {
data: {
user: {
name: "StoryBook user",
},
},
status: "authenticated",
},
transparent: false,
borderClass: undefined,
};
+70 -59
View File
@@ -1,13 +1,12 @@
import { Button } from "@chakra-ui/react";
import { Box, Button, useColorMode } from "@chakra-ui/react";
import { Popover } from "@headlessui/react";
import clsx from "clsx";
import { AnimatePresence, motion } from "framer-motion";
import Image from "next/image";
import Link from "next/link";
import { useSession } from "next-auth/react";
import { FaUser } from "react-icons/fa";
import { Container } from "src/components/Container";
import { ColorModeIconToggle } from "../UI/ColorModeIconToggle";
import { NavLinks } from "./NavLinks";
import { UserMenu } from "./UserMenu";
@@ -55,63 +54,75 @@ function AccountButton() {
}
export function Header(props) {
const transparent = props.transparent ?? false;
const { colorMode } = useColorMode();
const borderClass = props.transparent
? ""
: colorMode === "light"
? "border-b border-gray-400"
: "border-b border-zinc-800";
return (
<header className={clsx(!transparent && "bg-white")}>
<nav>
<Container className="relative z-10 flex justify-between py-8">
<div className="relative z-10 flex items-center gap-16">
<Link href="/" aria-label="Home" className="flex items-center">
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
<span className="text-2xl font-bold ml-3">Open Assistant</span>
</Link>
<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">
<Image src="/images/logos/logo.svg" className="mx-auto object-fill" width="50" height="50" alt="logo" />
<span className="text-2xl font-bold ml-3">Open Assistant</span>
</Link>
<div className="hidden lg:flex lg:gap-10">
<NavLinks />
</div>
<div 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="mt-8 flex flex-col gap-4"></div>
</Popover.Panel>
</>
)}
</AnimatePresence>
</>
)}
</Popover>
<AccountButton />
<UserMenu />
</div>
</Container>
</nav>
</header>
</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>
);
}
+8 -2
View File
@@ -1,9 +1,15 @@
import { useColorMode } from "@chakra-ui/react";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { useState } from "react";
export function NavLinks(): JSX.Element {
const [hoveredIndex, setHoveredIndex] = useState(null);
const { colorMode } = useColorMode();
const linkColor = colorMode === "light" ? "text-gray-700 hover:text-gray-900" : "text-gray-50 hover:text-white";
const hoverBgColor = colorMode === "light" ? "bg-gray-100" : "bg-gray-800";
return (
<>
@@ -14,14 +20,14 @@ export function NavLinks(): JSX.Element {
<Link
key={label}
href={href}
className="relative -my-2 -mx-3 rounded-lg px-3 py-2 text-sm text-gray-700 transition-colors delay-150 hover:text-gray-900 hover:delay-[0ms]"
className={`${linkColor} relative -my-2 -mx-3 rounded-lg px-3 py-2 text-sm transition-colors delay-150 hover:delay-[0ms]`}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
>
<AnimatePresence>
{hoveredIndex === index && (
<motion.span
className="absolute inset-0 rounded-lg bg-gray-100"
className={`${hoverBgColor} absolute inset-0 rounded-lg`}
layoutId="hoverBackground"
initial={{ opacity: 0 }}
animate={{ opacity: 1, transition: { duration: 0.15 } }}
+8 -6
View File
@@ -1,3 +1,4 @@
import { Box, useColorModeValue } from "@chakra-ui/react";
import { Popover } from "@headlessui/react";
import { AnimatePresence, motion } from "framer-motion";
import Image from "next/image";
@@ -7,6 +8,7 @@ import { FaCog, FaSignOutAlt } from "react-icons/fa";
export function UserMenu() {
const { data: session } = useSession();
const backgroundColor = useColorModeValue("#FFFFFF", "#000000");
if (!session) {
return <></>;
@@ -26,7 +28,7 @@ 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 bg-white border border-slate-300/70 hover:bg-gray-200/50 transition-colors duration-300">
<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">
<Image
src="/images/temp-avatars/av1.jpg"
alt="Profile Picture"
@@ -41,7 +43,7 @@ export function UserMenu() {
</Popover.Button>
<AnimatePresence initial={false}>
{open && (
<>
<Box backgroundColor={backgroundColor}>
<Popover.Panel
static
as={motion.div}
@@ -52,9 +54,9 @@ export function UserMenu() {
y: -10,
transition: { duration: 0.2 },
}}
className="absolute right-0 mt-3 w-screen max-w-xs p-4 rounded-md bg-white border border-slate-300/70"
className="absolute right-0 mt-3 w-screen bg-inherit max-w-xs p-4 rounded-md border border-slate-300/70"
>
<div className="flex flex-col gap-1">
<Box className="flex flex-col gap-1">
{accountOptions.map((item) => (
<a
key={item.name}
@@ -81,9 +83,9 @@ export function UserMenu() {
<p>Sign Out</p>
</div>
</a>
</div>
</Box>
</Popover.Panel>
</>
</Box>
)}
</AnimatePresence>
</>
+22 -10
View File
@@ -1,3 +1,4 @@
import { useColorMode } from "@chakra-ui/react";
import Image from "next/image";
import { useId } from "react";
@@ -6,6 +7,10 @@ import { Container } from "./Container";
function BackgroundIllustration(props) {
const id = useId();
const { colorMode } = useColorMode();
const baseRingColor = colorMode === "light" ? "#d4d4d4" : "#005a69";
const gradStopColor = colorMode === "light" ? "#06b6d4" : "#00f2ff";
return (
<div {...props}>
<svg
@@ -16,14 +21,14 @@ function BackgroundIllustration(props) {
>
<path
d="M1025 513c0 282.77-229.23 512-512 512S1 795.77 1 513 230.23 1 513 1s512 229.23 512 512Z"
stroke="#D4D4D4"
stroke={baseRingColor}
strokeOpacity="0.7"
/>
<path d="M513 1025C230.23 1025 1 795.77 1 513" stroke={`url(#${id}-gradient-1)`} strokeLinecap="round" />
<defs>
<linearGradient id={`${id}-gradient-1`} x1="1" y1="513" x2="1" y2="1025" gradientUnits="userSpaceOnUse">
<stop stopColor="#06b6d4" />
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
<stop stopColor={gradStopColor} />
<stop offset="1" stopColor={gradStopColor} stopOpacity="0" />
</linearGradient>
</defs>
</svg>
@@ -35,14 +40,14 @@ function BackgroundIllustration(props) {
>
<path
d="M913 513c0 220.914-179.086 400-400 400S113 733.914 113 513s179.086-400 400-400 400 179.086 400 400Z"
stroke="#D4D4D4"
stroke={baseRingColor}
strokeOpacity="0.7"
/>
<path d="M913 513c0 220.914-179.086 400-400 400" stroke={`url(#${id}-gradient-2)`} strokeLinecap="round" />
<defs>
<linearGradient id={`${id}-gradient-2`} x1="913" y1="513" x2="913" y2="913" gradientUnits="userSpaceOnUse">
<stop stopColor="#06b6d4" />
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
<stop stopColor={gradStopColor} />
<stop offset="1" stopColor={gradStopColor} stopOpacity="0" />
</linearGradient>
</defs>
</svg>
@@ -51,17 +56,24 @@ function BackgroundIllustration(props) {
}
export function Hero() {
const { colorMode } = useColorMode();
const pTextColor = colorMode === "light" ? "text-gray-600" : "text-white";
const fancyTextGradientClasses =
colorMode === "light" ? "from-blue-600 via-sky-400 to-blue-700" : "from-blue-500 via-sky-300 to-blue-400";
return (
<div className="overflow-hidden py-20 sm:py-32 lg:pb-32 xl:pb-36">
<Container className="">
<div className="lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20">
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
<h1 className="text-5xl mb-6 font-bold tracking-tight text-gray-900">Open Assistant</h1>
<p className="mt-8 text-3xl inline bg-gradient-to-r from-indigo-600 via-sky-400 to-indigo-700 bg-clip-text font-display tracking-tight text-transparent">
<h1 className="text-5xl mb-6 font-bold tracking-tight">Open Assistant</h1>
<p
className={`bg-gradient-to-r ${fancyTextGradientClasses} mt-8 text-3xl inline bg-clip-text font-display tracking-tight text-transparent`}
>
<b>Conversational AI for everyone.</b>
</p>
<p className="mt-6 text-lg text-gray-600">We believe we can create a revolution.</p>
<p className="mt-6 text-lg text-gray-600">
<p className={`mt-6 text-lg ${pTextColor}`}>We believe we can create a revolution.</p>
<p className={`mt-6 text-lg ${pTextColor}`}>
In the same way that Stable Diffusion helped the world make art and images in new ways, we want to improve
the world by providing amazing conversational AI.
</p>
@@ -1,12 +1,18 @@
import { Progress } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
export const LoadingScreen = ({ text }) => (
<div className="bg-slate-100">
<Progress size="xs" isIndeterminate />
{text && (
<div className="flex h-full">
<div className="text-xl font-bold text-gray-800 mx-auto my-auto">{text}</div>
</div>
)}
</div>
);
export const LoadingScreen = ({ text }) => {
const { colorMode } = useColorMode();
const mainClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
return (
<div className={`h-full ${mainClasses}`}>
<Progress size="sm" isIndeterminate />
{text && (
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">{text}</div>
</div>
)}
</div>
);
};
+21 -12
View File
@@ -1,3 +1,6 @@
import { Grid } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { FlaggableElement } from "./FlaggableElement";
export interface Message {
@@ -5,23 +8,29 @@ export interface Message {
is_assistant: boolean;
}
const getColor = (isAssistant: boolean) => (isAssistant ? "bg-slate-800" : "bg-sky-900");
const getBgColor = (isAssistant: boolean, colorMode: "light" | "dark") => {
if (colorMode === "light") {
return isAssistant ? "bg-slate-800" : "bg-sky-900";
} else {
return isAssistant ? "bg-black" : "bg-sky-900";
}
};
export const Messages = ({ messages, post_id }: { messages: Message[]; post_id: string }) => {
const { colorMode } = useColorMode();
const items = messages.map(({ text, is_assistant }: Message, i: number) => {
return (
<div className="flex" key={i + text}>
<FlaggableElement text={text} post_id={post_id}>
<div
key={i + text}
className={`${getColor(is_assistant)} p-4 my-1 rounded-xl text-white whitespace-pre-wrap float-left mr-3`}
>
{text}
</div>
</FlaggableElement>
</div>
<FlaggableElement text={text} post_id={post_id} key={i + text}>
<div
key={i + text}
className={`${getBgColor(is_assistant, colorMode)} p-4 rounded-md text-white whitespace-pre-wrap`}
>
{text}
</div>
</FlaggableElement>
);
});
// Maybe also show a legend of the colors?
return <>{items}</>;
return <Grid gap={2}>{items}</Grid>;
};
+9 -6
View File
@@ -2,9 +2,9 @@ import { Flex } from "@chakra-ui/react";
import {
closestCenter,
DndContext,
KeyboardSensor,
PointerSensor,
TouchSensor,
KeyboardSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
@@ -23,6 +23,7 @@ import { SortableItem } from "./SortableItem";
export interface SortableProps {
items: ReactNode[];
onChange: (newSortedIndices: number[]) => void;
className?: string;
}
interface SortableItems {
@@ -31,18 +32,18 @@ interface SortableItems {
item: ReactNode;
}
export const Sortable = ({ items, onChange }: SortableProps) => {
export const Sortable = (props: SortableProps) => {
const [itemsWithIds, setItemsWithIds] = useState<SortableItems[]>([]);
useEffect(() => {
setItemsWithIds(
items.map((item, idx) => ({
props.items.map((item, idx) => ({
item,
id: idx + 1, // +1 because dndtoolkit has problem with "falsy" ids
originalIndex: idx,
}))
);
}, [items]);
}, [props.items]);
const sensors = useSensors(
useSensor(PointerSensor),
@@ -50,6 +51,8 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
);
const extraClasses = props.className || "";
return (
<DndContext
sensors={sensors}
@@ -58,7 +61,7 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
modifiers={[restrictToVerticalAxis]}
>
<SortableContext items={itemsWithIds} strategy={verticalListSortingStrategy}>
<Flex direction="column" gap={2}>
<Flex direction="column" gap={2} className={extraClasses}>
{itemsWithIds.map(({ id, item }) => (
<SortableItem key={id} id={id}>
{item}
@@ -78,7 +81,7 @@ export const Sortable = ({ items, onChange }: SortableProps) => {
const oldIndex = items.findIndex((x) => x.id === active.id);
const newIndex = items.findIndex((x) => x.id === over.id);
const newArray = arrayMove(items, oldIndex, newIndex);
onChange(newArray.map((item) => item.originalIndex));
props.onChange(newArray.map((item) => item.originalIndex));
return newArray;
});
}
@@ -1,8 +1,9 @@
import { Button } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { RxDragHandleDots2 } from "react-icons/rx";
import { PropsWithChildren } from "react";
import { RxDragHandleDots2 } from "react-icons/rx";
export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>) => {
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
@@ -13,9 +14,15 @@ export const SortableItem = ({ children, id }: PropsWithChildren<{ id: number }>
touchAction: "none",
};
const { colorMode } = useColorMode();
const themedClasses =
colorMode === "light"
? "bg-slate-600 hover:bg-slate-500 text-white"
: "bg-black hover:bg-slate-900 text-white ring-1 ring-white/30 ring-inset hover:ring-slate-200/50";
return (
<li
className="grid grid-cols-[min-content_1fr] items-center rounded-lg shadow-md gap-x-2 p-2 bg-white hover:bg-slate-50"
className={`grid grid-cols-[min-content_1fr] items-center rounded-lg shadow-md gap-x-2 p-2 ${themedClasses}`}
ref={setNodeRef}
style={style}
>
@@ -0,0 +1,20 @@
import { useColorMode } from "@chakra-ui/react";
interface SurveyCardProps {
className?: string;
children: React.ReactNode;
}
export const SurveyCard = (props: SurveyCardProps) => {
const extraClases = props.className || "";
const { colorMode } = useColorMode();
const baseCardClasses = "rounded-lg h-full block p-6";
const cardClases =
colorMode === "light"
? `${baseCardClasses} bg-slate-50 text-gray-800 shadow-lg ${extraClases}`
: // `${baseCardClasses} bg-slate-800 text-white shadow-xl${extraClases}`;
`${baseCardClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`;
return <div className={cardClases}>{props.children}</div>;
};
@@ -0,0 +1,40 @@
import { useColorMode } from "@chakra-ui/react";
import { Flex } from "@chakra-ui/react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
interface TaskControlsProps {
// we need a task type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tasks: any[];
className?: string;
onSubmitResponse: (task: { id: string }) => void;
onSkip: () => void;
}
export const TaskControls = (props: TaskControlsProps) => {
const extraClases = props.className || "";
const { colorMode } = useColorMode();
const baseClasses = "flex flex-row justify-items-stretch mb-8 p-4 rounded-lg max-w-7xl mx-auto";
const taskControlClases =
colorMode === "light"
? `${baseClasses} bg-white text-gray-800 shadow-lg ${extraClases}`
: `${baseClasses} bg-slate-800 text-slate-400 shadow-xl ring-1 ring-white/10 ring-inset ${extraClases}`;
const endTask = props.tasks[props.tasks.length - 1];
return (
<section className={taskControlClases}>
<TaskInfo id={props.tasks[0].id} output="Submit your answer" />
<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 onClick={props.onSkip}>Next Task</SubmitButton>
)}
</Flex>
</section>
);
};
@@ -0,0 +1,16 @@
import { SurveyCard } from "src/components/Survey/SurveyCard";
export const TwoColumnsWithCards = ({ children }: { children: React.ReactNode[] }) => {
if (!Array.isArray(children) || children.length !== 2) {
throw new Error("TwoColumns expects 2 children");
}
const [first, second] = children;
return (
<div className="mb-8 mx-auto max-w-7xl lt-lg:mb-12 grid lg:gap-x-12 lg:grid-cols-2">
<SurveyCard>{first}</SurveyCard>
<SurveyCard className="lg:mt-0 lt-lg:mt-6">{second}</SurveyCard>
</div>
);
};
+1 -1
View File
@@ -1,6 +1,6 @@
export const TaskInfo = ({ id, output }: { id: string; output: string }) => {
return (
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
<div className="grid grid-cols-[min-content_auto] gap-x-2 ">
<b>Prompt</b>
<span data-cy="task-id">{id}</span>
<b>Output</b>
@@ -1,12 +1,24 @@
import { Flex } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import React from "react";
import { TaskOption } from "./TaskOption";
import { TaskOptions } from "./TaskOptions";
export const TaskSelection = () => {
const { colorMode } = useColorMode();
const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-800" : "bg-slate-900 text-white";
return (
<Flex gap={10} wrap="wrap" justifyContent="space-evenly" width="full" height="full" alignItems={"center"}>
<Flex
gap={10}
wrap="wrap"
justifyContent="space-evenly"
width="full"
height="full"
alignItems={"center"}
className={mainBgClasses}
>
<TaskOptions key="create" title="Create">
{/* <TaskOption
alt="Summarize Stories"
-14
View File
@@ -1,14 +0,0 @@
export const TwoColumns = ({ children }: { children: React.ReactNode[] }) => {
if (!Array.isArray(children) || children.length !== 2) {
throw new Error("TwoColumns expects 2 children");
}
const [first, second] = children;
return (
<section className="mb-8 lt-lg:mb-12 grid lg:gap-x-12 lg:grid-cols-2">
<div className="rounded-lg shadow-lg h-full block bg-white p-6">{first}</div>
<div className="rounded-lg shadow-lg h-full block bg-white p-6 mt-6 lg:mt-0">{second}</div>
</section>
);
};
@@ -0,0 +1,23 @@
import { useColorMode } from "@chakra-ui/react";
import { CiDark } from "react-icons/ci";
import { CiLight } from "react-icons/ci";
export function ColorModeIconToggle(props) {
const { colorMode, toggleColorMode } = useColorMode();
const propsClassName = props.className ?? "";
return (
<button
type="button"
className={`flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5 ${propsClassName}`}
aria-label="Toggle dark mode"
onClick={toggleColorMode}
>
{colorMode === "light" ? (
<CiDark className="h-5 w-5 stroke-zinc-900 dark:hidden" />
) : (
<CiLight className="h-5 w-5 stroke-white" />
)}
</button>
);
}
@@ -0,0 +1,16 @@
import { Switch, useColorMode } from "@chakra-ui/react";
import React from "react";
const ColorModeSwitch = () => {
const { colorMode, toggleColorMode } = useColorMode();
return (
<Switch
onChange={toggleColorMode}
defaultChecked={colorMode === "light"}
checked={colorMode === "light"}
size="lg"
/>
);
};
export default ColorModeSwitch;
+5 -28
View File
@@ -1,48 +1,25 @@
import "../styles/globals.css";
import "focus-visible";
import { ChakraProvider } from "@chakra-ui/react";
import { extendTheme } from "@chakra-ui/react";
import { Inter } from "@next/font/google";
import type { AppProps } from "next/app";
import { SessionProvider } from "next-auth/react";
import { getDefaultLayout, NextPageWithLayout } from "src/components/Layout";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
const theme = extendTheme({
styles: {
global: {
body: {
bg: "white",
},
main: {
fontFamily: "Inter",
},
header: {
fontFamily: "Inter",
},
},
},
});
import { Chakra, getServerSideProps } from "../styles/Chakra";
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppPropsWithLayout) {
function MyApp({ Component, pageProps: { session, cookies, ...pageProps } }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? getDefaultLayout;
const page = getLayout(<Component {...pageProps} />);
return (
<ChakraProvider theme={theme}>
<Chakra cookies={cookies}>
<SessionProvider session={session}>{page}</SessionProvider>
</ChakraProvider>
</Chakra>
);
}
export { getServerSideProps };
export default MyApp;
+2 -16
View File
@@ -2,24 +2,10 @@ import { Button } from "@chakra-ui/react";
import Head from "next/head";
import Link from "next/link";
import { useSession } from "next-auth/react";
import React, { useState } from "react";
import React from "react";
export default function Account() {
const { data: session } = useSession();
const [username, setUsername] = useState("null");
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleUpdate = async () => {
const response = await fetch("../api/update", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ username }),
});
const { name } = await response.json();
setUsername(name);
};
if (!session) {
return;
@@ -34,7 +20,7 @@ export default function Account() {
/>
</Head>
<main className="h-3/4 z-0 bg-white flex flex-col items-center justify-center">
<p>{username}</p>
<p>{session.user.name || "No username"}</p>
<Button>
<Link href="/account/edit">Edit Username</Link>
</Button>
@@ -63,7 +63,8 @@ const handler = async (req, res) => {
message_id: registeredTask.id,
}),
});
await ackRes.json();
ackRes; // calling this only to get rid of the unused variable warning... not sure if anything is intended to be done with ackRes
// Send the results to the client.
res.status(200).json(registeredTask);
+30 -12
View File
@@ -1,13 +1,16 @@
import { Button, Input, Stack } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import Link from "next/link";
import { getCsrfToken, getProviders, signIn } from "next-auth/react";
import React, { useRef } from "react";
import { FaBug, FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa";
import { AuthLayout } from "src/components/AuthLayout";
import { Footer } from "src/components/Footer";
import { Header } from "src/components/Header";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function Signin({ csrfToken, providers }) {
function Signin({ csrfToken, providers }) {
const { discord, email, github, credentials } = providers;
const emailEl = useRef(null);
const signinWithEmail = (ev: React.FormEvent) => {
@@ -21,8 +24,14 @@ export default function Signin({ csrfToken, providers }) {
signIn(credentials.id, { callbackUrl: "/", username: debugUsernameEl.current.value });
}
const { colorMode } = useColorMode();
const bgColorClass = colorMode === "light" ? "bg-gray-50" : "bg-chakra-gray-900";
const buttonBgColor = colorMode === "light" ? "#2563eb" : "#2563eb";
const buttonColorScheme = colorMode === "light" ? "blue" : "dark-blue-btn";
return (
<>
<div className={bgColorClass}>
<Head>
<title>Sign Up - Open Assistant</title>
<meta name="Sign Up" content="Sign up to access Open Assistant" />
@@ -30,11 +39,11 @@ export default function Signin({ csrfToken, providers }) {
<AuthLayout>
<Stack spacing="2">
{credentials && (
<form onSubmit={signinWithDebugCredentials} className="border-2 border-orange-200 rounded-md p-4 relative">
<span className="text-orange-600 absolute -top-3 left-5 bg-white px-1">For Debugging Only</span>
<form onSubmit={signinWithDebugCredentials} className="border-2 border-orange-600 rounded-md p-4 relative">
<span className={`text-orange-600 absolute -top-3 left-5 ${bgColorClass} px-1`}>For Debugging Only</span>
<Stack>
<Input variant="outline" size="lg" placeholder="Username" ref={debugUsernameEl} />
<Button size={"lg"} leftIcon={<FaBug />} colorScheme="gray" type="submit">
<Button size={"lg"} leftIcon={<FaBug />} colorScheme={buttonColorScheme} color="white" type="submit">
Continue with Debug User
</Button>
</Stack>
@@ -43,13 +52,13 @@ export default function Signin({ csrfToken, providers }) {
{email && (
<form onSubmit={signinWithEmail}>
<Stack>
<Input data-cy="email-address" variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
<Button
data-cy="signin-email-button"
size={"lg"}
leftIcon={<FaEnvelope />}
colorScheme="gray"
type="submit"
colorScheme={buttonColorScheme}
color="white"
>
Continue with Email
</Button>
@@ -58,7 +67,7 @@ export default function Signin({ csrfToken, providers }) {
)}
{discord && (
<Button
bg="#5865F2"
bg={buttonBgColor}
_hover={{ bg: "#4A57E3" }}
_active={{
bg: "#454FBF",
@@ -107,12 +116,21 @@ export default function Signin({ csrfToken, providers }) {
</Link>
</div>
</AuthLayout>
</>
</div>
);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export async function getServerSideProps(context) {
Signin.getLayout = (page) => (
<div className="grid grid-rows-[min-content_1fr_min-content] h-full justify-items-stretch">
<Header transparent={true} />
{page}
<Footer />
</div>
);
export default Signin;
export async function getServerSideProps() {
const csrfToken = await getCsrfToken();
const providers = await getProviders();
return {
+14 -27
View File
@@ -1,11 +1,10 @@
import { Flex, Textarea } from "@chakra-ui/react";
import { Container, Textarea } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { useRef, useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Messages } from "src/components/Messages";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { TwoColumns } from "src/components/TwoColumns";
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";
@@ -45,43 +44,31 @@ const AssistantReply = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
return <Container className="p-6 text-center text-gray-800">No tasks found...</Container>;
}
const task = tasks[0].task;
const endTask = tasks[tasks.length - 1];
return (
<div className="p-6 bg-slate-100 text-gray-800">
<TwoColumns>
<div className={`p-12 ${mainBgClasses}`}>
<TwoColumnsWithCards>
<>
<h5 className="text-lg font-semibold">Reply as the assistant</h5>
<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" data-cy="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumns>
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumnsWithCards>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])}>
Submit
</SubmitButton>
) : (
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
Next Task
</SubmitButton>
)}
</Flex>
</section>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</div>
);
};
+18 -23
View File
@@ -1,11 +1,9 @@
import { Flex, Textarea } from "@chakra-ui/react";
import Head from "next/head";
import { Textarea } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { useRef, useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { TwoColumns } from "src/components/TwoColumns";
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";
@@ -20,7 +18,7 @@ const SummarizeStory = () => {
// Fetch the very fist task. We can ignore everything except isLoading
// because the onSuccess handler will update `tasks` when ready.
const { isLoading } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
const { isLoading, mutate } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
onSuccess: (data) => {
setTasks([data]);
},
@@ -50,6 +48,14 @@ const SummarizeStory = () => {
});
};
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..." />;
}
@@ -59,31 +65,20 @@ const SummarizeStory = () => {
}
return (
<>
<Head>
<title>Summarize A Story</title>
<meta name="description" content="Summarize a story to train our model." />
</Head>
<div className={`p-12 ${mainBgClasses}`}>
<main className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
<TwoColumns>
<TwoColumnsWithCards>
<>
<h5 className="text-lg font-semibold">Instruction</h5>
<p className="text-lg py-1">Summarize the following story</p>
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.story}</div>
</>
<Textarea name="summary" placeholder="Summary" ref={inputRef} />
</TwoColumns>
</TwoColumnsWithCards>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
<SubmitButton onClick={() => submitResponse(tasks[0])}>Submit</SubmitButton>
</Flex>
</section>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</main>
</>
</div>
);
};
+20 -26
View File
@@ -1,11 +1,10 @@
import { Flex, Textarea } from "@chakra-ui/react";
import { Textarea } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { useRef, useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Messages } from "src/components/Messages";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { TwoColumns } from "src/components/TwoColumns";
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";
@@ -45,43 +44,38 @@ const UserReply = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
return (
<div className={`p-12 ${mainBgClasses}`}>
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
</div>
</div>
);
}
const task = tasks[0].task;
const endTask = tasks[tasks.length - 1];
return (
<div className="p-6 bg-slate-100 text-gray-800">
<TwoColumns>
<div className={`p-12 ${mainBgClasses}`}>
<TwoColumnsWithCards>
<>
<h5 className="text-lg font-semibold">Reply as a user</h5>
<p className="text-lg py-1">Given the following conversation, provide an adequate reply</p>
<Messages messages={task.conversation.messages} post_id={task.id} />
{task.hint && <p className="text-lg py-1">Hint: {task.hint}</p>}
</>
<Textarea name="reply" data-cy="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumns>
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumnsWithCards>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])}>
Submit
</SubmitButton>
) : (
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
Next Task
</SubmitButton>
)}
</Flex>
</section>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</div>
);
};
@@ -1,11 +1,10 @@
import { Flex } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Sortable } from "src/components/Sortable/Sortable";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { SurveyCard } from "src/components/Survey/SurveyCard";
import { TaskControls } from "src/components/Survey/TaskControls";
import fetcher from "src/lib/fetcher";
import poster from "src/lib/poster";
import useSWRImmutable from "swr/immutable";
@@ -47,48 +46,42 @@ const RankAssistantReplies = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
return (
<div className={`p-12 ${mainBgClasses}`}>
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
</div>
</div>
);
}
const replies = tasks[0].task.replies as string[];
const endTask = tasks[tasks.length - 1];
return (
<>
<Head>
<title>Rank Assistant Replies</title>
<meta name="description" content="Rank Assistant Replies." />
</Head>
<main className="p-6 bg-slate-100 text-gray-800">
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
<div className={`p-12 ${mainBgClasses}`}>
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
<p className="text-lg py-1">
Given the following replies, sort them from best to worst, best being first, worst being last.
</p>
<Sortable items={replies} onChange={setRanking} />
</div>
<Sortable items={replies} onChange={setRanking} className="my-8" />
</SurveyCard>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
Submit
</SubmitButton>
) : (
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
Next Task
</SubmitButton>
)}
</Flex>
</section>
</main>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</div>
</>
);
};
@@ -1,11 +1,10 @@
import { Flex } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Sortable } from "src/components/Sortable/Sortable";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { SurveyCard } from "src/components/Survey/SurveyCard";
import { TaskControls } from "src/components/Survey/TaskControls";
import fetcher from "src/lib/fetcher";
import poster from "src/lib/poster";
import useSWRImmutable from "swr/immutable";
@@ -18,6 +17,7 @@ const RankInitialPrompts = () => {
* The best prompt will have index 0, and the worst is the last.
*/
const [ranking, setRanking] = useState<number[]>([]);
// const bg = useColorModeValue("gray.100", "gray.800");
const { isLoading, mutate } = useSWRImmutable("/api/new_task/rank_initial_prompts", fetcher, {
onSuccess: (data) => {
@@ -47,47 +47,40 @@ const RankInitialPrompts = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
return (
<div className={`p-12 ${mainBgClasses}`}>
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
</div>
</div>
);
}
const endTask = tasks[tasks.length - 1];
return (
<>
<Head>
<title>Rank Initial Prompts</title>
<meta name="description" content="Rank initial prompts." />
</Head>
<main className="p-6 bg-slate-100 text-gray-800">
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
<div className={`p-12 ${mainBgClasses}`}>
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
<p className="text-lg py-1">
Given the following prompts, sort them from best to worst, best being first, worst being last.
</p>
<Sortable items={tasks[0].task.prompts} onChange={setRanking} />
</div>
<Sortable items={tasks[0].task.prompts} onChange={setRanking} className="my-8" />
</SurveyCard>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
Submit
</SubmitButton>
) : (
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
Next Task
</SubmitButton>
)}
</Flex>
</section>
</main>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</div>
</>
);
};
@@ -1,11 +1,10 @@
import { Flex } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import Head from "next/head";
import { useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import { Sortable } from "src/components/Sortable/Sortable";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { SurveyCard } from "src/components/Survey/SurveyCard";
import { TaskControls } from "src/components/Survey/TaskControls";
import fetcher from "src/lib/fetcher";
import poster from "src/lib/poster";
import useSWRImmutable from "swr/immutable";
@@ -47,48 +46,41 @@ const RankUserReplies = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">Loading...</div>;
return (
<div className={`p-12 ${mainBgClasses}`}>
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
</div>
</div>
);
}
const replies = tasks[0].task.replies as string[];
const endTask = tasks[tasks.length - 1];
return (
<>
<Head>
<title>Rank User Replies</title>
<meta name="description" content="Rank User Replies." />
</Head>
<main className="p-6 bg-slate-100 text-gray-800">
<div className="rounded-lg shadow-lg block bg-white p-6 mb-8">
<div className={`p-12 ${mainBgClasses}`}>
<SurveyCard className="max-w-7xl mx-auto h-fit mb-24">
<h5 className="text-lg font-semibold mb-4">Instructions</h5>
<p className="text-lg py-1">
Given the following replies, sort them from best to worst, best being first, worst being last.
</p>
<Sortable items={replies} onChange={setRanking} />
</div>
<Sortable items={replies} onChange={setRanking} className="my-8" />
</SurveyCard>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton data-cy="submit" onClick={() => submitResponse(tasks[0])} disabled={ranking.length === 0}>
Submit
</SubmitButton>
) : (
<SubmitButton data-cy="next-task" onClick={fetchNextTask}>
Next Task
</SubmitButton>
)}
</Flex>
</section>
</main>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</div>
</>
);
};
+21 -21
View File
@@ -1,13 +1,12 @@
import { Flex, Textarea } from "@chakra-ui/react";
import { Textarea } from "@chakra-ui/react";
import { useColorMode } from "@chakra-ui/react";
import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
import Head from "next/head";
import { useState } from "react";
import { SkipButton } from "src/components/Buttons/Skip";
import { SubmitButton } from "src/components/Buttons/Submit";
import { LoadingScreen } from "src/components/Loading/LoadingScreen";
import RatingRadioGroup from "src/components/RatingRadioGroup";
import { TaskInfo } from "src/components/TaskInfo/TaskInfo";
import { TwoColumns } from "src/components/TwoColumns";
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";
@@ -50,15 +49,27 @@ const RateSummary = () => {
});
};
const fetchNextTask = () => {
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 <div className="p-6 bg-slate-100 text-gray-800">No tasks found...</div>;
return (
<div className={`p-12 ${mainBgClasses}`}>
<div className="flex h-full">
<div className="text-xl font-bold mx-auto my-auto">No tasks found...</div>
</div>
</div>
);
}
const endTask = tasks[tasks.length - 1];
return (
<>
<Head>
@@ -66,7 +77,7 @@ const RateSummary = () => {
<meta name="description" content="Rate a proposed story summary." />
</Head>
<main className="p-6 bg-slate-100 text-gray-800">
<TwoColumns>
<TwoColumnsWithCards>
<>
<h5 className="text-lg font-semibold mb-4">Instruction</h5>
<div className="bg-slate-800 p-6 rounded-xl text-white whitespace-pre-wrap">{tasks[0].task.full_text}</div>
@@ -89,20 +100,9 @@ const RateSummary = () => {
</ul>
<Textarea name="notes" placeholder="Optional notes" />
</section>
</TwoColumns>
</TwoColumnsWithCards>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<TaskInfo id={tasks[0].id} output="Submit your answer" />
<Flex justify="center" ml="auto" gap={2}>
<SkipButton>Skip</SkipButton>
{endTask.task.type !== "task_done" ? (
<SubmitButton onClick={() => submitResponse(tasks[0])}>Submit</SubmitButton>
) : (
<SubmitButton onClick={mutate}>Next Task</SubmitButton>
)}
</Flex>
</section>
<TaskControls tasks={tasks} onSubmitResponse={submitResponse} onSkip={fetchNextTask} />
</main>
</>
);
+2 -4
View File
@@ -20,11 +20,9 @@ const Home = () => {
/>
</Head>
{session ? (
<main className="my-4">
<TaskSelection />
</main>
<TaskSelection />
) : (
<main>
<main className="oa-basic-theme">
<Hero />
<CallToAction />
<Faq />
+24
View File
@@ -0,0 +1,24 @@
import { ChakraProvider, cookieStorageManagerSSR, localStorageManager } from "@chakra-ui/react";
import { theme } from "./Theme";
export function Chakra({ cookies, children }) {
const colorModeManager = typeof cookies === "string" ? cookieStorageManagerSSR(cookies) : localStorageManager;
return (
<ChakraProvider theme={theme} colorModeManager={colorModeManager}>
{children}
</ChakraProvider>
);
}
// also export a reusable function getServerSideProps
export function getServerSideProps({ req }) {
return {
props: {
// first time users will not have any cookies and you may not return
// undefined here, hence ?? is necessary
cookies: req.headers.cookie ?? "",
},
};
}
+14
View File
@@ -0,0 +1,14 @@
export const colors = {
light: {
bg: "rgb(250,250,250)",
text: "black",
},
dark: {
bg: "gray.900",
text: "white",
},
"dark-blue-btn": {
200: "rgb(29,78,216)",
300: "blue",
},
};
@@ -0,0 +1,14 @@
import { defineStyleConfig } from "@chakra-ui/styled-system";
const baseStyle = {};
const variants = {
"no-padding": {
padding: 0,
},
};
export const containerTheme = defineStyleConfig({
baseStyle,
variants,
});
+37
View File
@@ -0,0 +1,37 @@
import { type ThemeConfig, extendTheme } from "@chakra-ui/react";
import { Styles } from "@chakra-ui/theme-tools";
import { colors } from "./colors";
import { containerTheme } from "./components/Container";
const config: ThemeConfig = {
initialColorMode: "light",
useSystemColorMode: true,
disableTransitionOnChange: false,
};
const components = {
Container: containerTheme,
};
const styles: Styles = {
global: (props) => ({
"*": {
transition: "background-color 200ms cubic-bezier(0.4, 0, 1, 1)",
// bg: props.colorMode === "light" ? colors.light.bg : colors.dark.bg,
// color: props.colorMode === "light" ? colors.light.text : colors.dark.text,
},
".oa-basic-theme": {
bg: props.colorMode === "light" ? colors.light.bg : colors.dark.bg,
color: props.colorMode === "light" ? colors.light.text : colors.dark.text,
},
main: {
fontFamily: "Inter",
},
header: {
fontFamily: "Inter",
},
}),
};
export const theme = extendTheme({ colors, config, styles, components });
+4
View File
@@ -71,6 +71,10 @@ module.exports = {
maxWidth: {
"2xl": "40rem",
},
colors: {
"chakra-gray-900": "#171923",
},
},
},
plugins: [require("@tailwindcss/forms")],