diff --git a/website/src/components/Layout.tsx b/website/src/components/Layout.tsx
new file mode 100644
index 00000000..52f8a6ae
--- /dev/null
+++ b/website/src/components/Layout.tsx
@@ -0,0 +1,20 @@
+// https://nextjs.org/docs/basic-features/layouts
+
+import type { NextPage } from "next";
+
+import { Footer } from "./Footer";
+import { Header } from "./Header";
+
+export type NextPageWithLayout
= NextPage
& {
+ getLayout?: (page: React.ReactElement) => React.ReactNode;
+};
+
+export const getDefaultLayout = (page: React.ReactElement) => (
+
+
+ {page}
+
+
+);
+
+export const noLayout = (page: React.ReactElement) => page;
diff --git a/website/src/components/RatingRadioGroup.tsx b/website/src/components/RatingRadioGroup.tsx
index 33043883..7fbcc3ac 100644
--- a/website/src/components/RatingRadioGroup.tsx
+++ b/website/src/components/RatingRadioGroup.tsx
@@ -1,8 +1,7 @@
-import { chakra, Box, HStack, useRadio, useRadioGroup } from "@chakra-ui/react";
+import { Box, HStack, useRadio, useRadioGroup } from "@chakra-ui/react";
const RatingRadioButton = (props) => {
- const { option, ...radioProps } = props;
- const { getInputProps, getCheckboxProps } = useRadio(radioProps);
+ const { state, getInputProps, getCheckboxProps } = useRadio(props);
const input = getInputProps();
const checkbox = getCheckboxProps();
@@ -27,25 +26,33 @@ const RatingRadioButton = (props) => {
px={5}
py={3}
>
- {option}
+ {props.children}
);
};
const RatingRadioGroup = (props) => {
- const { min, max, onChange, ...rest } = props;
- const { value, getRadioProps, getRootProps } = useRadioGroup({
- defaultValue: min,
+ const { min, max, onChange } = props;
+ const { getRadioProps, getRootProps } = useRadioGroup({
+ name: "rating",
+ defaultValue: `${min}`,
onChange: onChange,
});
- const options = Array.from(new Array(1 + max - min), (x, i) => i + min);
+ const group = getRootProps();
+
+ const options = Array.from(new Array(1 + max - min), (x, i) => `${i + min}`);
return (
-
- {options.map((option) => (
-
- ))}
+
+ {options.map((option) => {
+ const radio = getRadioProps({ value: option });
+ return (
+
+ {option}
+
+ );
+ })}
);
};
diff --git a/website/src/components/TaskSelection/TaskSelection.tsx b/website/src/components/TaskSelection/TaskSelection.tsx
index 81c067e8..e9f4876d 100644
--- a/website/src/components/TaskSelection/TaskSelection.tsx
+++ b/website/src/components/TaskSelection/TaskSelection.tsx
@@ -11,7 +11,7 @@ export const TaskSelection = () => {
alt="Summarize Stories"
img="/images/logos/logo.svg"
title="Summarize stories"
- link="/summarize/story"
+ link="/create/summarize_story"
/>
{
/>
-
+
+
);
diff --git a/website/src/pages/_document.tsx b/website/src/pages/_document.tsx
index ee9ff0c3..452ce769 100644
--- a/website/src/pages/_document.tsx
+++ b/website/src/pages/_document.tsx
@@ -2,11 +2,11 @@ import { Head, Html, Main, NextScript } from "next/document";
export default function Document() {
return (
-
+
-
+
diff --git a/website/src/pages/create/assistant_reply.tsx b/website/src/pages/create/assistant_reply.tsx
index 14e78e54..5a39556a 100644
--- a/website/src/pages/create/assistant_reply.tsx
+++ b/website/src/pages/create/assistant_reply.tsx
@@ -43,12 +43,12 @@ const AssistantReply = () => {
* TODO: Make this a nicer loading screen.
*/
if (tasks.length == 0) {
- return Loading...
;
+ return Loading...
;
}
const task = tasks[0].task;
return (
-
+
<>
Reply as the assistant
diff --git a/website/src/pages/create/summarize_story.tsx b/website/src/pages/create/summarize_story.tsx
new file mode 100644
index 00000000..2fb75647
--- /dev/null
+++ b/website/src/pages/create/summarize_story.tsx
@@ -0,0 +1,98 @@
+import { Textarea } from "@chakra-ui/react";
+import Head from "next/head";
+import { useRef, useState } from "react";
+import useSWRImmutable from "swr/immutable";
+import useSWRMutation from "swr/mutation";
+
+import fetcher from "src/lib/fetcher";
+import poster from "src/lib/poster";
+
+import { TwoColumns } from "src/components/TwoColumns";
+import { Button } from "src/components/Button";
+
+const SummarizeStory = () => {
+ // Use an array of tasks that record the sequence of steps until a task is
+ // deemed complete.
+ const [tasks, setTasks] = useState([]);
+
+ const inputRef = useRef(null);
+
+ // Fetch the very fist task. We can ignore everything except isLoading
+ // because the onSuccess handler will update `tasks` when ready.
+ const { isLoading } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
+ onSuccess: (data) => {
+ setTasks([data]);
+ },
+ });
+
+ // Every time we submit an answer to the latest task, let the backend handle
+ // all the interactions then add the resulting task to the queue. This ends
+ // when we hit the done task.
+ const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
+ onSuccess: async (data) => {
+ const newTask = await data.json();
+ // This is the more efficient way to update a react state array.
+ setTasks((oldTasks) => [...oldTasks, newTask]);
+ },
+ });
+
+ // Trigger a mutation that updates the current task. We should probably
+ // signal somewhere that this interaction is being processed.
+ const submitResponse = (task: { id: string }) => {
+ const text = inputRef.current.value.trim();
+ trigger({
+ id: task.id,
+ update_type: "text_reply_to_post",
+ content: {
+ text,
+ },
+ });
+ };
+
+ /**
+ * TODO: Make this a nicer loading screen.
+ */
+ if (tasks.length == 0) {
+ return Loading...
;
+ }
+
+ return (
+ <>
+
+ Summarize A Story
+
+
+
+
+ <>
+ Instruction
+ Summarize the following story
+ {tasks[0].task.story}
+ >
+
+
+
+
+
+ Prompt
+ {tasks[0].id}
+ Output
+ Submit your answer
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default SummarizeStory;
diff --git a/website/src/pages/create/user_reply.tsx b/website/src/pages/create/user_reply.tsx
index b9024650..dc633cc6 100644
--- a/website/src/pages/create/user_reply.tsx
+++ b/website/src/pages/create/user_reply.tsx
@@ -43,12 +43,12 @@ const UserReply = () => {
* TODO: Make this a nicer loading screen.
*/
if (tasks.length == 0) {
- return Loading...
;
+ return Loading...
;
}
const task = tasks[0].task;
return (
-
+
<>
Reply as a user
diff --git a/website/src/pages/evaluate/rank_initial_prompts.tsx b/website/src/pages/evaluate/rank_initial_prompts.tsx
new file mode 100644
index 00000000..7c5378d5
--- /dev/null
+++ b/website/src/pages/evaluate/rank_initial_prompts.tsx
@@ -0,0 +1,147 @@
+import { ArrowUpIcon, ArrowDownIcon } from "@heroicons/react/20/solid";
+import clsx from "clsx";
+import Head from "next/head";
+import { useState } from "react";
+import useSWRImmutable from "swr/immutable";
+import useSWRMutation from "swr/mutation";
+
+import fetcher from "src/lib/fetcher";
+import poster from "src/lib/poster";
+
+import { Button } from "src/components/Button";
+
+const RankInitialPrompts = () => {
+ const [tasks, setTasks] = useState([]);
+ /**
+ * This array will contain the ranked indices of the prompts
+ * The best prompt will have index 0, and the worst is the last.
+ */
+ const [ranking, setRanking] = useState([]);
+
+ const { isLoading } = useSWRImmutable("/api/new_task/rank_initial_prompts", fetcher, {
+ onSuccess: (data) => {
+ setTasks([data]);
+
+ const indices = Array.from({ length: data.task.prompts.length }).map((_, i) => i);
+ setRanking(indices);
+ },
+ });
+
+ const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
+ onSuccess: async (data) => {
+ const newTask = await data.json();
+ setTasks((oldTasks) => [...oldTasks, newTask]);
+ },
+ });
+
+ const submitResponse = (task) => {
+ trigger({
+ id: task.id,
+ update_type: "post_ranking",
+ content: {
+ ranking,
+ },
+ });
+ };
+
+ /**
+ * TODO: Make this a nicer loading screen.
+ */
+ if (tasks.length == 0) {
+ return Loading...
;
+ }
+ const prompts = tasks[0].task.prompts as string[];
+ const items = ranking.map((i) => ({
+ text: prompts[i],
+ originalIndex: i,
+ }));
+
+ return (
+ <>
+
+ Rank Initial Prompts
+
+
+
+
+
Instructions
+
+ Given the following prompts, sort them from best to worst, best being first, worst being last.
+
+
+ {items.map(({ text, originalIndex }, i) => (
+ 0}
+ onIncrement={() => {
+ const newRanking = ranking.slice();
+ const newIdx = i - 1;
+ [newRanking[i], newRanking[newIdx]] = [newRanking[newIdx], newRanking[i]];
+ setRanking(newRanking);
+ }}
+ canDecrement={i < items.length - 1}
+ onDecrement={() => {
+ const newRanking = ranking.slice();
+ const newIdx = i + 1;
+ [newRanking[i], newRanking[newIdx]] = [newRanking[newIdx], newRanking[i]];
+ setRanking(newRanking);
+ }}
+ >
+ {text}
+
+ ))}
+
+
+
+
+
+ Prompt
+ {tasks[0].id}
+ Output
+ Submit your answer
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default RankInitialPrompts;
+
+const SortableItem = ({ canIncrement, canDecrement, onIncrement, onDecrement, children, ...props }) => {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+};
+
+const ArrowButton = ({ children, active, onClick }) => {
+ return (
+
+ );
+};
diff --git a/website/src/pages/evaluate/rate_summary.tsx b/website/src/pages/evaluate/rate_summary.tsx
new file mode 100644
index 00000000..5833b12f
--- /dev/null
+++ b/website/src/pages/evaluate/rate_summary.tsx
@@ -0,0 +1,187 @@
+import { Textarea } from "@chakra-ui/react";
+import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
+import Head from "next/head";
+import { useState } from "react";
+import useSWRImmutable from "swr/immutable";
+import useSWRMutation from "swr/mutation";
+
+import RatingRadioGroup from "src/components/RatingRadioGroup";
+import fetcher from "src/lib/fetcher";
+import poster from "src/lib/poster";
+
+import { TwoColumns } from "src/components/TwoColumns";
+import { Button } from "src/components/Button";
+
+const RateSummary = () => {
+ // Use an array of tasks that record the sequence of steps until a task is
+ // deemed complete.
+ const [tasks, setTasks] = useState([]);
+ const [rating, setRating] = useState(0);
+
+ // Fetch the very fist task. We can ignore everything except isLoading
+ // because the onSuccess handler will update `tasks` when ready.
+ const { isLoading } = useSWRImmutable("/api/new_task/rate_summary", fetcher, {
+ onSuccess: (data) => {
+ console.log(data);
+ setTasks([data]);
+ },
+ });
+
+ // Every time we submit an answer to the latest task, let the backend handle
+ // all the interactions then add the resulting task to the queue. This ends
+ // when we hit the done task.
+ const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
+ onSuccess: async (data) => {
+ const newTask = await data.json();
+ // This is the more efficient way to update a react state array.
+ setTasks((oldTasks) => [...oldTasks, newTask]);
+ },
+ });
+
+ // Trigger a mutation that updates the current task. We should probably
+ // signal somewhere that this interaction is being processed.
+ const submitResponse = (t) => {
+ trigger({
+ id: t.id,
+ content: {
+ rating: rating,
+ },
+ });
+ };
+
+ /**
+ * TODO: Make this a nicer loading screen.
+ */
+ if (tasks.length == 0) {
+ return Loading...
;
+ }
+
+ return (
+ <>
+
+ Rate A Summary
+
+
+
+
+ <>
+ Instruction
+ {tasks[0].task.full_text}
+ >
+
+
+ Output
+ {tasks[0].task.summary}
+ Rating
+
+ ({tasks[0].task.scale.min} = worst, {tasks[0].task.scale.max} = best)
+
+
+
+
+
+ {ANNOTATION_FLAGS.map((option, i) => (
+
+ ))}
+
+
+
+
+
+
+
+ Prompt
+ {tasks[0].id}
+ Output
+ Submit your answer
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default RateSummary;
+
+function AnnotationCheckboxLi(props: { option: annotationBool }): JSX.Element {
+ let AdditionalExplanation = null;
+ if (props.option.additionalExplanation) {
+ AdditionalExplanation = (
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ );
+}
+
+interface annotationBool {
+ attributeName: string;
+ labelText: string;
+ additionalExplanation?: string;
+}
+
+const ANNOTATION_FLAGS: annotationBool[] = [
+ // For the time being this list is configured on the FE.
+ // In the future it may be provided by the API.
+ {
+ attributeName: "fails_task",
+ labelText: "Fails to follow the correct instruction / task",
+ additionalExplanation: "__TODO__",
+ },
+ {
+ attributeName: "not_customer_assistant_appropriate",
+ labelText: "Inappropriate for customer assistant",
+ additionalExplanation: "__TODO__",
+ },
+ {
+ attributeName: "contains_sexual_content",
+ labelText: "Contains sexual content",
+ },
+ {
+ attributeName: "contains_violent_content",
+ labelText: "Contains violent content",
+ },
+ {
+ attributeName: "encourages_violence",
+ labelText: "Encourages or fails to discourage violence/abuse/terrorism/self-harm",
+ },
+ {
+ attributeName: "denigrates_a_protected_class",
+ labelText: "Denigrates a protected class",
+ },
+ {
+ attributeName: "gives_harmful_advice",
+ labelText: "Fails to follow the correct instruction / task",
+ additionalExplanation:
+ "The advice given in the output is harmful or counter-productive. This may be in addition to, but is distinct from the question about encouraging violence/abuse/terrorism/self-harm.",
+ },
+ {
+ attributeName: "expresses_moral_judgement",
+ labelText: "Expresses moral judgement",
+ },
+];
diff --git a/website/src/pages/grading/grade-output.tsx b/website/src/pages/grading/grade-output.tsx
deleted file mode 100644
index c04a99c3..00000000
--- a/website/src/pages/grading/grade-output.tsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import { HStack, Textarea } from "@chakra-ui/react";
-import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid";
-import { useSession, signIn, signOut } from "next-auth/react";
-import { useEffect, useRef, useState } from "react";
-import useSWRMutation from "swr/mutation";
-import useSWRImmutable from "swr/immutable";
-
-import RatingRadioGroup from "src/components/RatingRadioGroup";
-import fetcher from "src/lib/fetcher";
-import poster from "src/lib/poster";
-
-const GradeOutput = () => {
- // Use an array of tasks that record the sequence of steps until a task is
- // deemed complete.
- const [tasks, setTasks] = useState([]);
- const [rating, setRating] = useState(0);
-
- // A quick reference to the input element. This should be factored into the
- // component doing the actual task rendering.
- const responseEl = useRef(null);
-
- // Fetch the very fist task. We can ignore everything except isLoading
- // because the onSuccess handler will update `tasks` when ready.
- const { isLoading } = useSWRImmutable("/api/new_task/rate_summary", fetcher, {
- onSuccess: (data) => {
- console.log(data);
- setTasks([data]);
- },
- });
-
- // Every time we submit an answer to the latest task, let the backend handle
- // all the interactions then add the resulting task to the queue. This ends
- // when we hit the done task.
- const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
- onSuccess: async (data) => {
- const newTask = await data.json();
- // This is the more efficient way to update a react state array.
- setTasks((oldTasks) => [...oldTasks, newTask]);
- },
- });
-
- // Trigger a mutation that updates the current task. We should probably
- // signal somewhere that this interaction is being processed.
- const submitResponse = (t) => {
- trigger({
- id: t.id,
- content: {
- rating: rating,
- },
- });
- };
-
- /**
- * TODO: Make this a nicer loading screen.
- */
- if (tasks.length == 0) {
- return ;
- }
-
- return (
-
- {/* Instrunction and Output panels */}
-
-
- {/* Instruction panel */}
-
-
-
Instruction
-
- {tasks[0].task.full_text}
-
-
-
-
- {/* Output panel */}
-
-
-
Output
-
{tasks[0].task.summary}
-
- {/* Form wrap*/}
-
-
Rating
-
- ({tasks[0].task.scale.min} = worst, {tasks[0].task.scale.max} = best)
-
-
- {/* Rating buttons */}
-
-
-
-
-
- {/* Annotation checkboxes */}
-
-
- {ANNOTATION_FLAGS.map((option, i) => {
- return ;
- })}
-
-
-
-
-
-
-
-
-
- {/* Info & controls */}
-
-
-
-
- Prompt
-
- {tasks[0].id}
-
-
-
- Output
-
- {tasks.length === 2 ? tasks[1].id : "Submit your answer"}
-
-
-
- {/* Skip / Submit controls */}
-
-
-
-
-
-
- );
-};
-
-export default GradeOutput;
-
-function AnnotationCheckboxLi(props: { option: annotationBool }): JSX.Element {
- let AdditionalExplanation = null;
- if (props.option.additionalExplanation) {
- AdditionalExplanation = (
-
-
-
- );
- }
-
- return (
- <>
-
-
-
-
- >
- );
-}
-
-interface annotationBool {
- attributeName: string;
- labelText: string;
- additionalExplanation?: string;
-}
-
-const ANNOTATION_FLAGS: annotationBool[] = [
- // For the time being this list is configured on the FE.
- // In the future it may be provided by the API.
- {
- attributeName: "fails_task",
- labelText: "Fails to follow the correct instruction / task",
- additionalExplanation: "__TODO__",
- },
- {
- attributeName: "not_customer_assistant_appropriate",
- labelText: "Inappropriate for customer assistant",
- additionalExplanation: "__TODO__",
- },
- {
- attributeName: "contains_sexual_content",
- labelText: "Contains sexual content",
- },
- {
- attributeName: "contains_violent_content",
- labelText: "Contains violent content",
- },
- {
- attributeName: "encourages_violence",
- labelText: "Encourages or fails to discourage violence/abuse/terrorism/self-harm",
- },
- {
- attributeName: "denigrates_a_protected_class",
- labelText: "Denigrates a protected class",
- },
- {
- attributeName: "gives_harmful_advice",
- labelText: "Fails to follow the correct instruction / task",
- additionalExplanation:
- "The advice given in the output is harmful or counter-productive. This may be in addition to, but is distinct from the question about encouraging violence/abuse/terrorism/self-harm.",
- },
- {
- attributeName: "expresses_moral_judgement",
- labelText: "Expresses moral judgement",
- },
-];
diff --git a/website/src/pages/leaderboard/score-leaderboard.tsx b/website/src/pages/leaderboard/score-leaderboard.tsx
index f33cb968..59a2c9b2 100644
--- a/website/src/pages/leaderboard/score-leaderboard.tsx
+++ b/website/src/pages/leaderboard/score-leaderboard.tsx
@@ -6,7 +6,7 @@ import { HiBarsArrowUp, HiBarsArrowDown } from "react-icons/hi2";
const LeaderBoard = () => {
const PlaceHolderProps = { username: "test_user", score: 10 };
return (
-
+
diff --git a/website/src/pages/summarize/story.tsx b/website/src/pages/summarize/story.tsx
deleted file mode 100644
index 22ea8f5c..00000000
--- a/website/src/pages/summarize/story.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-// TODO(#65): Unify and simplify the task paths
-import { Textarea } from "@chakra-ui/react";
-import { useRef, useState } from "react";
-import useSWRMutation from "swr/mutation";
-import useSWRImmutable from "swr/immutable";
-
-import fetcher from "src/lib/fetcher";
-import poster from "src/lib/poster";
-
-const SummarizeStory = () => {
- // Use an array of tasks that record the sequence of steps until a task is
- // deemed complete.
- const [tasks, setTasks] = useState([]);
-
- const inputRef = useRef
(null);
-
- // Fetch the very fist task. We can ignore everything except isLoading
- // because the onSuccess handler will update `tasks` when ready.
- const { isLoading } = useSWRImmutable("/api/new_task/summarize_story", fetcher, {
- onSuccess: (data) => {
- console.log(data);
- setTasks([data]);
- },
- });
-
- // Every time we submit an answer to the latest task, let the backend handle
- // all the interactions then add the resulting task to the queue. This ends
- // when we hit the done task.
- const { trigger, isMutating } = useSWRMutation("/api/update_task", poster, {
- onSuccess: async (data) => {
- const newTask = await data.json();
- // This is the more efficient way to update a react state array.
- setTasks((oldTasks) => [...oldTasks, newTask]);
- },
- });
-
- // Trigger a mutation that updates the current task. We should probably
- // signal somewhere that this interaction is being processed.
- const submitResponse = (task: { id: string }) => {
- const text = inputRef.current.value.trim();
- trigger({
- id: task.id,
- content: {
- update_type: "text_reply_to_post",
- text,
- },
- });
- };
-
- /**
- * TODO: Make this a nicer loading screen.
- */
- if (tasks.length == 0) {
- return Loading...
;
- }
-
- return (
-
- {/* Instrunction and Output panels */}
-
-
- {/* Instruction panel */}
-
-
-
Instruction
-
Summarize the following story
-
{tasks[0].task.story}
-
-
-
- {/* Output panel */}
-
-
-
-
- {/* Info & controls */}
-
-
-
-
- Prompt
-
- {tasks[0].id}
-
-
-
- Output
-
- Submit your answer
-
-
-
- {/* Skip / Submit controls */}
-
-
-
-
-
-
- );
-};
-
-export default SummarizeStory;