Merge branch 'main' into 65-consistent-paths-plus-ui

This commit is contained in:
Keith Stevens
2022-12-28 07:56:42 +09:00
committed by GitHub
27 changed files with 718 additions and 33 deletions
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
"""add_journal_table
Revision ID: 3358eb6834e6
Revises: 067c4002f2d9
Create Date: 2022-12-27 14:44:59.483868
"""
import sqlalchemy as sa
import sqlmodel
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "3358eb6834e6"
down_revision = "067c4002f2d9"
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"journal",
sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False),
sa.Column(
"created_date", sa.DateTime(timezone=True), server_default=sa.text("CURRENT_TIMESTAMP"), nullable=False
),
sa.Column(
"event_payload",
postgresql.JSONB(astext_type=sa.Text()),
nullable=False,
),
sa.Column("person_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
sa.Column("post_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
sa.Column("api_client_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
sa.Column("event_type", sqlmodel.sql.sqltypes.AutoString(length=200), nullable=False),
sa.ForeignKeyConstraint(
["api_client_id"],
["api_client.id"],
),
sa.ForeignKeyConstraint(
["person_id"],
["person.id"],
),
sa.ForeignKeyConstraint(
["post_id"],
["post.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_journal_person_id"), "journal", ["person_id"], unique=False)
op.create_table(
"journal_integration",
sa.Column("id", postgresql.UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), nullable=False),
sa.Column("last_run", sa.DateTime(), nullable=True),
sa.Column("description", sqlmodel.sql.sqltypes.AutoString(length=512), nullable=False),
sa.Column("last_journal_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
sa.Column("last_error", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
sa.Column("next_run", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["last_journal_id"],
["journal.id"],
),
sa.PrimaryKeyConstraint("id", "description"),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table("journal_integration")
op.drop_index(op.f("ix_journal_person_id"), table_name="journal")
op.drop_table("journal")
# ### end Alembic commands ###
-5
View File
@@ -7,7 +7,6 @@ from fastapi import APIRouter, Depends, HTTPException
from fastapi.security.api_key import APIKey
from loguru import logger
from oasst_backend.api import deps
from oasst_backend.models.db_payload import TaskPayload
from oasst_backend.prompt_repository import PromptRepository
from oasst_shared.schemas import protocol as protocol_schema
from sqlmodel import Session
@@ -219,10 +218,6 @@ def post_interaction(
f"Frontend reports text reply to {interaction.post_id=} with {interaction.text=} by {interaction.user=}."
)
work_package = pr.fetch_workpackage_by_postid(interaction.post_id)
work_payload: TaskPayload = work_package.payload.payload
logger.info(f"found task work package in db: {work_payload}")
# here we store the text reply in the database
# ToDo: role user or agent?
pr.store_text_reply(interaction, role="unknown")
+122
View File
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
import enum
from typing import Literal, Optional
from uuid import UUID
from oasst_backend.models import ApiClient, Journal, Person, WorkPackage
from oasst_backend.models.payload_column_type import PayloadContainer, payload_type
from oasst_shared.utils import utcnow
from pydantic import BaseModel
from sqlmodel import Session
class JournalEventType(str, enum.Enum):
"""A label for a piece of text."""
user_created = "user_created"
text_reply_to_post = "text_reply_to_post"
post_rating = "post_rating"
post_ranking = "post_ranking"
@payload_type
class JournalEvent(BaseModel):
type: str
person_id: Optional[UUID]
post_id: Optional[UUID]
workpackage_id: Optional[UUID]
task_type: Optional[str]
@payload_type
class TextReplyEvent(JournalEvent):
type: Literal[JournalEventType.text_reply_to_post] = JournalEventType.text_reply_to_post
length: int
role: str
@payload_type
class RatingEvent(JournalEvent):
type: Literal[JournalEventType.post_rating] = JournalEventType.post_rating
rating: int
@payload_type
class RankingEvent(JournalEvent):
type: Literal[JournalEventType.post_ranking] = JournalEventType.post_ranking
ranking: list[int]
class JournalWriter:
def __init__(self, db: Session, api_client: ApiClient, person: Person):
self.db = db
self.api_client = api_client
self.person = person
self.person_id = self.person.id if self.person else None
def log_text_reply(self, work_package: WorkPackage, post_id: UUID, role: str, length: int) -> Journal:
return self.log(
task_type=work_package.payload_type,
event_type=JournalEventType.text_reply_to_post,
payload=TextReplyEvent(role=role, length=length),
workpackage_id=work_package.id,
post_id=post_id,
)
def log_rating(self, work_package: WorkPackage, post_id: UUID, rating: int) -> Journal:
return self.log(
task_type=work_package.payload_type,
event_type=JournalEventType.post_rating,
payload=RatingEvent(rating=rating),
workpackage_id=work_package.id,
post_id=post_id,
)
def log_ranking(self, work_package: WorkPackage, post_id: UUID, ranking: list[int]) -> Journal:
return self.log(
task_type=work_package.payload_type,
event_type=JournalEventType.post_ranking,
payload=RankingEvent(ranking=ranking),
workpackage_id=work_package.id,
post_id=post_id,
)
def log(
self,
*,
payload: JournalEvent,
task_type: str,
event_type: str = None,
workpackage_id: Optional[UUID] = None,
post_id: Optional[UUID] = None,
commit: bool = True,
) -> Journal:
if event_type is None:
if payload is None:
event_type = "null"
else:
event_type = type(payload).__name__
if payload.person_id is None:
payload.person_id = self.person_id
if payload.post_id is None:
payload.post_id = post_id
if payload.workpackage_id is None:
payload.workpackage_id = workpackage_id
if payload.task_type is None:
payload.task_type = task_type
entry = Journal(
person_id=self.person_id,
api_client_id=self.api_client.id,
created_date=utcnow(),
event_type=event_type,
event_payload=PayloadContainer(payload=payload),
post_id=post_id,
)
self.db.add(entry)
if commit:
self.db.commit()
return entry
+3
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from .api_client import ApiClient
from .journal import Journal, JournalIntegration
from .person import Person
from .person_stats import PersonStats
from .post import Post
@@ -15,4 +16,6 @@ __all__ = [
"PostReaction",
"WorkPackage",
"TextLabels",
"Journal",
"JournalIntegration",
]
+56
View File
@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from typing import Optional
from uuid import UUID, uuid1, uuid4
import sqlalchemy as sa
import sqlalchemy.dialects.postgresql as pg
from sqlmodel import Field, SQLModel
from .payload_column_type import PayloadContainer, payload_column_type
def generate_time_uuid(node=None, clock_seq=None):
"""Create a lexicographically sortable time ordered custom (non-standard) UUID by reordering the timestamp fields of a version 1 UUID."""
(time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node) = uuid1(node, clock_seq).fields
# reconstruct 60 bit timestamp, see version 1 uuid: https://www.rfc-editor.org/rfc/rfc4122
timestamp = (time_hi_version & 0xFFF) << 48 | (time_mid << 32) | time_low
version = time_hi_version >> 12
assert version == 1
a = timestamp >> 28 # bits 28-59
b = (timestamp >> 12) & 0xFFFF # bits 12-27
c = timestamp & 0xFFF # bits 0-11 (clear version bits)
clock_seq_hi_variant &= 0xF # (clear variant bits)
return UUID(fields=(a, b, c, clock_seq_hi_variant, clock_seq_low, node), version=None)
class Journal(SQLModel, table=True):
__tablename__ = "journal"
id: Optional[UUID] = Field(
sa_column=sa.Column(pg.UUID(as_uuid=True), primary_key=True, default=generate_time_uuid),
)
created_date: Optional[datetime] = Field(
sa_column=sa.Column(sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp())
)
person_id: UUID = Field(nullable=True, foreign_key="person.id", index=True)
post_id: Optional[UUID] = Field(foreign_key="post.id", nullable=True)
api_client_id: UUID = Field(foreign_key="api_client.id")
event_type: str = Field(nullable=False, max_length=200)
event_payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False))
class JournalIntegration(SQLModel, table=True):
__tablename__ = "journal_integration"
id: Optional[UUID] = Field(
sa_column=sa.Column(
pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()")
),
)
description: str = Field(max_length=512, primary_key=True)
last_journal_id: UUID = Field(foreign_key="journal.id", nullable=True)
last_run: datetime = Field(sa_column=sa.Column(sa.DateTime(), nullable=True))
last_error: str = Field(nullable=True)
next_run: datetime = Field(nullable=True)
@@ -5,6 +5,7 @@ from uuid import UUID, uuid4
import oasst_backend.models.db_payload as db_payload
from loguru import logger
from oasst_backend.journal_writer import JournalWriter
from oasst_backend.models import ApiClient, Person, Post, PostReaction, TextLabels, WorkPackage
from oasst_backend.models.payload_column_type import PayloadContainer
from oasst_shared.schemas import protocol as protocol_schema
@@ -17,6 +18,7 @@ class PromptRepository:
self.api_client = api_client
self.person = self.lookup_person(user)
self.person_id = self.person.id if self.person else None
self.journal = JournalWriter(db, api_client, self.person)
def lookup_person(self, user: protocol_schema.User) -> Person:
if not user:
@@ -116,6 +118,10 @@ class PromptRepository:
self.validate_post_id(reply.post_id)
self.validate_post_id(reply.user_post_id)
work_package = self.fetch_workpackage_by_postid(reply.post_id)
work_payload: db_payload.TaskPayload = work_package.payload.payload
logger.info(f"found task work package in db: {work_payload}")
# find post with post-id
parent_post: Post = (
self.db.query(Post)
@@ -141,6 +147,7 @@ class PromptRepository:
role=role,
payload=db_payload.PostPayload(text=reply.text),
)
self.journal.log_text_reply(work_package=work_package, post_id=user_post_id, role=role, length=len(reply.text))
return user_post
def store_rating(self, rating: protocol_schema.PostRating) -> PostReaction:
@@ -159,6 +166,7 @@ class PromptRepository:
# store reaction to post
reaction_payload = db_payload.RatingReactionPayload(rating=rating.rating)
reaction = self.insert_reaction(post.id, reaction_payload)
self.journal.log_rating(work_package, post_id=post.id, rating=rating.rating)
logger.info(f"Ranking {rating.rating} stored for work_package {work_package.id}.")
return reaction
@@ -184,6 +192,7 @@ class PromptRepository:
# store reaction to post
reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking)
reaction = self.insert_reaction(post.id, reaction_payload)
self.journal.log_ranking(work_package, post_id=post.id, ranking=ranking.ranking)
logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.")
@@ -199,6 +208,7 @@ class PromptRepository:
# store reaction to post
reaction_payload = db_payload.RankingReactionPayload(ranking=ranking.ranking)
reaction = self.insert_reaction(post.id, reaction_payload)
self.journal.log_ranking(work_package, post_id=post.id, ranking=ranking.ranking)
logger.info(f"Ranking {ranking.ranking} stored for work_package {work_package.id}.")
+7
View File
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timezone
def utcnow() -> datetime:
"""Return the current utc date and time with tzinfo set to UTC."""
return datetime.now(timezone.utc)
@@ -9,6 +9,7 @@ services:
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 2s
@@ -16,6 +16,7 @@ services:
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: ocgpt_website
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 2s
+2 -2
View File
@@ -68,7 +68,7 @@ def get_winner(pairs):
def get_ranking(pairs):
"""
Abuses concordance property to get a (not necessarily unqiue) ranking.
The lack of uniqueness is due to the potential existance of multiple
The lack of uniqueness is due to the potential existence of multiple
equally ranked winners. We have to pick one, which is where
the non-uniqueness comes from
"""
@@ -99,7 +99,7 @@ def ranked_pairs(ranks: List[List[int]]):
tallies = tallies - tallies.T
# print(tallies)
# note: the resulting tally matrix should be skew-symmetric
# order by strenght of victory (using tideman's original method, don't think it would make a difference for us)
# order by strength of victory (using tideman's original method, don't think it would make a difference for us)
sorted_majorities = []
for i in range(len(ranks[0])):
for j in range(len(ranks[i])):
+1 -1
View File
@@ -5,7 +5,7 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5433/ocgpt_website
FASTAPI_URL=http://localhost:8080
FASTAPI_KEY=1234
# A dev Auth Secret. Can be exposed if we never use this publically.
# A dev Auth Secret. Can be exposed if we never use this publicly.
NEXTAUTH_SECRET=O/M2uIbGj+lDD2oyNa8ax4jEOJqCPJzO53UbWShmq98=
# The SMTP host and port found by running the jobs in /scripts/frontend-development/docker-compose.yaml
+8
View File
@@ -63,6 +63,14 @@ If you're doing active development we suggest the following workflow:
navigate to `http://localhost:1080`. Check the email listed and click the
log in link. You're now logged in and authenticated.
### Using debug user credentials
Whenever the website runs in development mode, you can use the debug credentials provider to log in without fancy emails or OAuth.
1. Development mode is automatically active when you start the website with `npm run dev`.
1. Use the `Login` button in the top right to go to the login page.
1. You should see a section for debug credentials. Enter any username you wish, you will be logged in as that user.
## Code Layout
### React Code
+2 -2
View File
@@ -12,7 +12,7 @@ export function Avatar() {
return <></>;
}
if (session && session.user) {
const email = session.user.email;
const displayName = session.user.name || session.user.email;
const accountOptions = [
{
name: "Account Settings",
@@ -35,7 +35,7 @@ export function Avatar() {
height="40"
className="rounded-full"
></Image>
<p className="hidden lg:flex">{email}</p>
<p className="hidden lg:flex">{displayName}</p>
{/* Will be changed to username once it is implemented */}
</div>
</Popover.Button>
+19
View File
@@ -0,0 +1,19 @@
import clsx from "clsx";
export const Button = (
props: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
) => {
const { className, children, ...rest } = props;
return (
<button
type="button"
className={clsx(
"inline-flex items-center rounded-md border border-transparent px-4 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2",
className
)}
{...rest}
>
{children}
</button>
);
};
+18
View File
@@ -0,0 +1,18 @@
export interface Message {
text: string;
is_assistant: boolean;
}
const getColor = (isAssistant: boolean) => (isAssistant ? "bg-slate-800" : "bg-sky-900");
export const Messages = ({ messages }: { messages: Message[] }) => {
const items = messages.map(({ text, is_assistant }: Message, i: number) => {
return (
<div key={i + text} className={`${getColor(is_assistant)} p-4 my-1 rounded-xl text-white whitespace-pre-wrap`}>
{text}
</div>
);
});
// Maybe also show a legend of the colors?
return <>{items}</>;
};
+12
View File
@@ -0,0 +1,12 @@
const RankItem = ({ username, score }) => {
return (
<div className="flex flex-row justify-between p-6 border-2 border-slate-100 text-left font-semibold hover:bg-sky-50">
<div>1</div>
<div>@username</div>
<div>20.5</div>
<div>gold</div>
</div>
);
};
export default RankItem;
@@ -0,0 +1,39 @@
import { Card, CardBody, Flex, Heading } from "@chakra-ui/react";
import Image from "next/image";
import Link from "next/link";
export type OptionProps = {
img: string;
alt: string;
title: string;
link: string;
};
export const TaskOption = (props: OptionProps) => {
const { alt, img, title, link } = props;
return (
<Link href={link}>
<Card
maxW="300"
minW="300"
minH="300"
maxH="300"
className="transition ease-in-out duration-500 sm:grayscale hover:grayscale-0"
>
<CardBody width="full" height="full">
<Flex direction="column" alignItems="center" justifyContent="center">
<Image src={img} alt={alt} width={200} height={200} />
<Heading
mt={-10}
className="bg-gradient-to-r from-indigo-600 via-sky-400 to-indigo-700 bg-clip-text tracking-tight text-transparent"
textAlign="center"
fontSize="3xl"
>
{title}
</Heading>
</Flex>
</CardBody>
</Card>
</Link>
);
};
@@ -0,0 +1,23 @@
import { Divider, Flex, Heading } from "@chakra-ui/react";
import React from "react";
export type TaskOptionsProps = {
title: string;
children: JSX.Element | JSX.Element[];
};
export const TaskOptions = (props: TaskOptionsProps) => {
const { title, children } = props;
return (
<Flex gap={10} wrap="wrap" justifyContent="center">
<Heading
className="bg-gradient-to-r from-indigo-600 via-sky-400 to-indigo-700 bg-clip-text tracking-tight text-transparent"
fontSize="5xl"
>
{title}
</Heading>
<Divider mt={-8} />
{children}
</Flex>
);
};
@@ -0,0 +1,29 @@
import React from "react";
import { TaskOptions } from "./TaskOptions";
import { Flex } from "@chakra-ui/react";
import { TaskOption } from "./TaskOption";
export const TaskSelection = () => {
return (
<Flex gap={10} wrap="wrap" justifyContent="space-evenly" width="full" height="full" alignItems={"center"}>
<TaskOptions key="create" title="Create">
<TaskOption
alt="Summarize Stories"
img="/images/logos/logo.svg"
title="Summarize stories"
link="/summarize/story"
/>
<TaskOption alt="Reply as User" img="/images/logos/logo.svg" title="Reply as User" link="/create/user_reply" />
<TaskOption
alt="Reply as Assistant"
img="/images/logos/logo.svg"
title="Reply as Assistant"
link="/create/assistant_reply"
/>
</TaskOptions>
<TaskOptions key="evaluate" title="Evaluate">
<TaskOption alt="Rate Prompts" img="/images/logos/logo.svg" title="Rate Prompts" link="/grading/grade-output" />
</TaskOptions>
</Flex>
);
};
@@ -0,0 +1,3 @@
export { TaskSelection } from "./TaskSelection";
export { TaskOptions } from "./TaskOptions";
export { TaskOption } from "./TaskOption";
+14
View File
@@ -0,0 +1,14 @@
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>
);
};
@@ -2,6 +2,7 @@ import type { AuthOptions } from "next-auth";
import NextAuth from "next-auth";
import DiscordProvider from "next-auth/providers/discord";
import EmailProvider from "next-auth/providers/email";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import prisma from "src/lib/prismadb";
@@ -32,6 +33,23 @@ if (process.env.DISCORD_CLIENT_ID) {
);
}
if (process.env.NODE_ENV === "development") {
providers.push(
CredentialsProvider({
name: "Debug Credentials",
credentials: {
username: { label: "Username", type: "text" },
},
async authorize(credentials) {
return {
id: credentials.username,
name: credentials.username,
};
},
})
);
}
export const authOptions: AuthOptions = {
// Ensure we can store user data in a database.
adapter: PrismaAdapter(prisma),
+31 -16
View File
@@ -1,6 +1,6 @@
import { Button, Input, Stack } from "@chakra-ui/react";
import Head from "next/head";
import { FaDiscord, FaEnvelope, FaGithub } from "react-icons/fa";
import { FaDiscord, FaEnvelope, FaBug, FaGithub } from "react-icons/fa";
import { getCsrfToken, getProviders, signIn } from "next-auth/react";
import { useRef } from "react";
import Link from "next/link";
@@ -8,12 +8,20 @@ import Link from "next/link";
import { AuthLayout } from "src/components/AuthLayout";
export default function Signin({ csrfToken, providers }) {
const { discord, email, github } = providers;
const { discord, email, github, credentials } = providers;
const emailEl = useRef(null);
const signinWithEmail = () => {
const debugUsernameEl = useRef(null);
const signinWithEmail = (ev: React.FormEvent) => {
ev.preventDefault();
signIn(email.id, { callbackUrl: "/", email: emailEl.current.value });
};
function signinWithDebugCredentials(ev: React.FormEvent) {
ev.preventDefault();
signIn(credentials.id, { callbackUrl: "/", username: debugUsernameEl.current.value });
}
return (
<>
<Head>
@@ -21,20 +29,27 @@ export default function Signin({ csrfToken, providers }) {
<meta name="Sign Up" content="Sign up to access Open Assistant" />
</Head>
<AuthLayout>
<Stack spacing="2">
<Stack spacing="6">
{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>
<Stack>
<Input variant="outline" size="lg" placeholder="Username" ref={debugUsernameEl} />
<Button size={"lg"} leftIcon={<FaBug />} colorScheme="gray" type="submit">
Continue with Debug User
</Button>
</Stack>
</form>
)}
{email && (
<Stack>
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
<Button
size={"lg"}
leftIcon={<FaEnvelope />}
colorScheme="gray"
onClick={signinWithEmail}
// isDisabled="false"
>
Continue with Email
</Button>
</Stack>
<form onSubmit={signinWithEmail}>
<Stack>
<Input variant="outline" size="lg" placeholder="Email Address" ref={emailEl} />
<Button size={"lg"} leftIcon={<FaEnvelope />} colorScheme="gray">
Continue with Email
</Button>
</Stack>
</form>
)}
{discord && (
<Button
@@ -0,0 +1,83 @@
import { Textarea } from "@chakra-ui/react";
import { useRef, useState } from "react";
import useSWRMutation from "swr/mutation";
import useSWRImmutable from "swr/immutable";
import fetcher from "src/lib/fetcher";
import poster from "src/lib/poster";
import { Messages } from "src/components/Messages";
import { TwoColumns } from "src/components/TwoColumns";
import { Button } from "src/components/Button";
const AssistantReply = () => {
const [tasks, setTasks] = useState([]);
const inputRef = useRef<HTMLTextAreaElement>(null);
const { isLoading } = useSWRImmutable("/api/new_task/assistant_reply ", fetcher, {
onSuccess: (data) => {
console.log(data);
setTasks([data]);
},
});
const { trigger, isMutating } = 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_post",
content: {
text,
},
});
};
/**
* TODO: Make this a nicer loading screen.
*/
if (tasks.length == 0) {
return <div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">Loading...</div>;
}
const task = tasks[0].task;
return (
<div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
<TwoColumns>
<>
<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} />
</>
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumns>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
<b>Prompt</b>
<span>{tasks[0].id}</span>
<b>Output</b>
<span>Submit your answer</span>
</div>
<div className="flex justify-center ml-auto">
<Button className="mr-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200">Skip</Button>
<Button
onClick={() => submitResponse(tasks[0])}
className="bg-indigo-600 text-white shadow-sm hover:bg-indigo-700"
>
Submit
</Button>
</div>
</section>
</div>
);
};
export default AssistantReply;
+84
View File
@@ -0,0 +1,84 @@
import { Textarea } from "@chakra-ui/react";
import { useRef, useState } from "react";
import useSWRMutation from "swr/mutation";
import useSWRImmutable from "swr/immutable";
import fetcher from "src/lib/fetcher";
import poster from "src/lib/poster";
import { Messages } from "src/components/Messages";
import { TwoColumns } from "src/components/TwoColumns";
import { Button } from "src/components/Button";
const UserReply = () => {
const [tasks, setTasks] = useState([]);
const inputRef = useRef<HTMLTextAreaElement>(null);
const { isLoading } = useSWRImmutable("/api/new_task/user_reply", fetcher, {
onSuccess: (data) => {
console.log(data);
setTasks([data]);
},
});
const { trigger, isMutating } = 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_post",
content: {
text,
},
});
};
/**
* TODO: Make this a nicer loading screen.
*/
if (tasks.length == 0) {
return <div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">Loading...</div>;
}
const task = tasks[0].task;
return (
<div className="p-6 h-full mx-auto bg-slate-100 text-gray-800">
<TwoColumns>
<>
<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} />
{task.hint && <p className="text-lg py-1">Hint: {task.hint}</p>}
</>
<Textarea name="reply" placeholder="Reply..." ref={inputRef} />
</TwoColumns>
<section className="mb-8 p-4 rounded-lg shadow-lg bg-white flex flex-row justify-items-stretch ">
<div className="grid grid-cols-[min-content_auto] gap-x-2 text-gray-700">
<b>Prompt</b>
<span>{tasks[0].id}</span>
<b>Output</b>
<span>Submit your answer</span>
</div>
<div className="flex justify-center ml-auto">
<Button className="mr-2 bg-indigo-100 text-indigo-700 hover:bg-indigo-200">Skip</Button>
<Button
onClick={() => submitResponse(tasks[0])}
className="bg-indigo-600 text-white shadow-sm hover:bg-indigo-700"
>
Submit
</Button>
</div>
</section>
</div>
);
};
export default UserReply;
+3 -7
View File
@@ -8,6 +8,7 @@ import { Faq } from "src/components/Faq";
import { Footer } from "src/components/Footer";
import { Header } from "src/components/Header";
import { Hero } from "src/components/Hero";
import { TaskSelection } from "src/components/TaskSelection";
const Home = () => {
const { data: session } = useSession();
@@ -43,13 +44,8 @@ const Home = () => {
/>
</Head>
<Header />
<main className="h-3/4 z-0 bg-white flex flex-col items-center justify-center gap-2">
<Button size="lg" colorScheme="blue" className="drop-shadow">
<Link href="/evaluate/rate_summary">Rate a Summary</Link>
</Button>
<Button size="lg" colorScheme="blue" className="drop-shadow">
<Link href="/create/summarize_story">Summarize a story</Link>
</Button>
<main className="h-3/4 m-20 z-0 bg-white flex flex-col items-center justify-center gap-2">
<TaskSelection />
</main>
<Footer />
</>
@@ -0,0 +1,54 @@
import RankItem from "@/components/RankItem";
import { BarsArrowUpIcon, BarsArrowDownIcon } from "@heroicons/react/24/solid";
import Image from "next/image";
import { HiBarsArrowUp, HiBarsArrowDown } from "react-icons/hi2";
const LeaderBoard = () => {
const PlaceHolderProps = { username: "test_user", score: 10 };
return (
<div className=" p-6 h-full mx-auto bg-slate-100 text-gray-800">
<div className="flex flex-col">
<div className="rounded-lg shadow-lg h-full block bg-white">
<div className="p-8">
<h5 className="text-2xl font-bold">LeaderBoard</h5>
</div>
<div className="flex flex-row justify-between px-6 py-3 font-semibold text-md">
<div className="flex flex-row items-center justify-center space-x-2">
<div>
<p>Rank</p>
</div>
<div className="mt-2 text-slate-400 hover:text-sky-400 hover:cursor-pointer">
<HiBarsArrowDown className="w-6 h-6 text-inherit"></HiBarsArrowDown>
</div>
</div>
<div className="flex flex-row items-center justify-center space-x-2">
<div>
<p>User</p>
</div>
<div className="mt-2 text-slate-400 hover:text-sky-400 hover:cursor-pointer">
<HiBarsArrowDown className="w-6 h-6 text-inherit"></HiBarsArrowDown>
</div>
</div>
<div className="flex flex-row items-center justify-center space-x-2">
<div>
<p>Score</p>
</div>
<div className="mt-2 text-slate-400 hover:text-sky-400 hover:cursor-pointer">
<HiBarsArrowDown className="w-6 h-6 text-inherit"></HiBarsArrowDown>
</div>
</div>
<div className="flex flex-row items-center justify-center space-x-2">
<div>
<p>Medal</p>
</div>
</div>
</div>
{/* leaderboard items */}
<RankItem {...PlaceHolderProps}></RankItem>
</div>
</div>
</div>
);
};
export default LeaderBoard;