diff --git a/website/src/components/FlaggableElement.tsx b/website/src/components/FlaggableElement.tsx new file mode 100644 index 00000000..cae24484 --- /dev/null +++ b/website/src/components/FlaggableElement.tsx @@ -0,0 +1,216 @@ +import { + Button, + Checkbox, + Flex, + Popover, + PopoverAnchor, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverTrigger, + Slider, + SliderFilledTrack, + SliderThumb, + SliderTrack, + Spacer, + useBoolean, +} from "@chakra-ui/react"; +import { FlagIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; +import { useState } from "react"; +import poster from "src/lib/poster"; +import useSWRMutation from "swr/mutation"; + +export const FlaggableElement = (props) => { + const [isEditing, setIsEditing] = useBoolean(); + const { trigger } = useSWRMutation("/api/v1/text_labels", poster, { + onSuccess: () => { + setIsEditing.off; + }, + }); + + const submitResponse = () => { + const label_map: Map = new Map(); + TEXT_LABEL_FLAGS.forEach((flag, i) => { + if (checkboxValues[i]) { + label_map.set(flag.attributeName, sliderValues[i]); + } + }); + trigger({ post_id: props.post_id, label_map: Object.fromEntries(label_map), text: props.text }); + }; + const [checkboxValues, setCheckboxValues] = useState(new Array(TEXT_LABEL_FLAGS.length).fill(false)); + const [sliderValues, setSliderValues] = useState(new Array(TEXT_LABEL_FLAGS.length).fill(1)); + + const handleCheckboxState = (isChecked, idx) => { + setCheckboxValues( + checkboxValues.map((val, i) => { + return i == idx ? isChecked : val; + }) + ); + }; + const handleSliderState = (newVal, idx) => { + setSliderValues( + sliderValues.map((val, i) => { + return i == idx ? newVal : val; + }) + ); + }; + + return ( + +
+ {props.children} + + + +
+ + + + +
+ +
    + {TEXT_LABEL_FLAGS.map((option, i) => { + return ( + + ); + })} +
+
+ +
+
+
+
+
+ ); +}; +function FlagCheckboxLi(props: { + option: textFlagLabels; + idx: number; + checkboxValues: boolean[]; + sliderValues: number[]; + checkboxHandler: (newVal: boolean, idx: number) => void; + sliderHandler: (newVal: number, idx: number) => void; +}): JSX.Element { + let AdditionalExplanation = null; + if (props.option.additionalExplanation) { + AdditionalExplanation = ( + + + ); + } + + return ( +
  • + + { + props.checkboxHandler(e.target.checked, props.idx); + }} + /> + + + { + props.sliderHandler(val / 100, props.idx); + }} + > + + + + + + +
  • + ); +} +interface textFlagLabels { + attributeName: string; + labelText: string; + additionalExplanation?: string; +} +const TEXT_LABEL_FLAGS: textFlagLabels[] = [ + // 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/components/Messages.tsx b/website/src/components/Messages.tsx index 4bc747d5..cc94fcc0 100644 --- a/website/src/components/Messages.tsx +++ b/website/src/components/Messages.tsx @@ -1,3 +1,5 @@ +import { FlaggableElement } from "./FlaggableElement"; + export interface Message { text: string; is_assistant: boolean; @@ -5,11 +7,18 @@ export interface Message { const getColor = (isAssistant: boolean) => (isAssistant ? "bg-slate-800" : "bg-sky-900"); -export const Messages = ({ messages }: { messages: Message[] }) => { +export const Messages = ({ messages, post_id }: { messages: Message[]; post_id: string }) => { const items = messages.map(({ text, is_assistant }: Message, i: number) => { return ( -
    - {text} +
    + +
    + {text} +
    +
    ); }); diff --git a/website/src/pages/create/assistant_reply.tsx b/website/src/pages/create/assistant_reply.tsx index 54badd71..4350fe16 100644 --- a/website/src/pages/create/assistant_reply.tsx +++ b/website/src/pages/create/assistant_reply.tsx @@ -61,7 +61,7 @@ const AssistantReply = () => { <>
    Reply as the assistant

    Given the following conversation, provide an adequate reply

    - +