diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 54943c2a..271ea4b8 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -25,6 +25,7 @@ jobs: | sed s/{CODA_INCOMING_TOKEN}/${{ secrets.CODA_INCOMING_TOKEN }}/ \ | sed s/{CODA_WRITES_TOKEN}/${{ secrets.CODA_WRITES_TOKEN }}/ \ | sed s/{GOOGLE_ANALYTICS_ID}/${{ secrets.GOOGLE_ANALYTICS_ID }}/ \ + | sed s/{DISCORD_LOGGING_URL}/${{ secrets.DISCORD_LOGGING_URL }}/ \ > wrangler.toml npm ci npm run deploy diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dfd10a8b..108ff512 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,6 +24,7 @@ jobs: | sed s/{CODA_INCOMING_TOKEN}/${{ secrets.CODA_INCOMING_TOKEN }}/ \ | sed s/{CODA_WRITES_TOKEN}/${{ secrets.CODA_WRITES_TOKEN }}/ \ | sed s/{GOOGLE_ANALYTICS_ID}/${{ secrets.GOOGLE_ANALYTICS_ID }}/ \ + | sed s/{DISCORD_LOGGING_URL}/${{ secrets.DISCORD_LOGGING_URL }}/ \ > wrangler.toml npm ci npm run deploy diff --git a/app/components/Chatbot/ChatEntry.tsx b/app/components/Chatbot/ChatEntry.tsx index 1df99738..e284dcea 100644 --- a/app/components/Chatbot/ChatEntry.tsx +++ b/app/components/Chatbot/ChatEntry.tsx @@ -3,7 +3,7 @@ import {Link} from '@remix-run/react' import MarkdownIt from 'markdown-it' import QuestionMarkIcon from '~/components/icons-generated/QuestionMark' import Contents from '~/components/Article/Contents' -import Feedback from '~/components/Feedback' +import Feedback, {logFeedback} from '~/components/Feedback' import useGlossary from '~/hooks/useGlossary' import './chat_entry.css' import type {Entry, AssistantEntry, StampyEntry, Citation, ErrorMessage} from '~/hooks/useChat' @@ -136,7 +136,7 @@ const Reference = (citation: Citation) => { ) } -const ChatbotReply = ({phase, content, citationsMap}: AssistantEntry) => { +const ChatbotReply = ({question, phase, content, citationsMap}: AssistantEntry) => { const citations = [] as Citation[] citationsMap?.forEach((v) => { citations.push(v) @@ -193,6 +193,9 @@ const ChatbotReply = ({phase, content, citationsMap}: AssistantEntry) => { pageid="chatbot" upHint="This response was helpful" downHint="This response was unhelpful" + onSubmit={async (message: string, option?: string) => + logFeedback({message, option, type: 'bot', question, answer: content, citations}) + } options={[ 'Making things up', 'Wrong subject', @@ -227,6 +230,9 @@ const StampyArticle = ({pageid, content, title}: StampyEntry) => { pageid={pageid} upHint="This response was helpful" downHint="This response was unhelpful" + onSubmit={async (message: string, option?: string) => + logFeedback({message, option, type: 'human', question: title, answer: content, pageid}) + } options={[ 'Making things up', 'Wrong subject', diff --git a/app/components/Chatbot/index.tsx b/app/components/Chatbot/index.tsx index 12900acc..2e0f7118 100644 --- a/app/components/Chatbot/index.tsx +++ b/app/components/Chatbot/index.tsx @@ -13,46 +13,35 @@ import Input from '~/components/Input' // to be replaced with actual pool questions const poolQuestions = [ + {title: 'Do people seriously worry about existential risk from AI?', pageid: '6953'}, + {title: 'Is AI safety about systems becoming malevolent or conscious?', pageid: '6194'}, + {title: 'When do experts think human-level AI will be created?', pageid: '5633'}, + {title: 'Why is AI alignment a hard problem?', pageid: '8163'}, { - title: 'What is AI Safety? - from pool', - pageid: '8486', + title: 'Why can’t we just “put the AI in a box” so that it can’t influence the outside world?', + pageid: '6176', }, { - title: 'How would the AI even get out in the world? -- from pool', - pageid: '7638', + title: 'What are the differences between AGI, transformative AI, and superintelligence?', + pageid: '5864', }, + {title: 'What are large language models?', pageid: '5864'}, + {title: "Why can't we just turn the AI off if it starts to misbehave?", pageid: '3119'}, + {title: 'What is instrumental convergence?', pageid: '897I'}, + {title: "What is Goodhart's law?", pageid: '8185'}, + {title: 'What is the orthogonality thesis?', pageid: '6568'}, + {title: 'How powerful would a superintelligence become?', pageid: '7755'}, + {title: 'Will AI be able to think faster than humans?', pageid: '8E41'}, + {title: "Isn't the real concern misuse?", pageid: '9B85'}, + {title: 'Are AIs conscious?', pageid: '8V5J'}, { - title: 'What is the AI alignment problem? -- from pool', - pageid: '8EL9', - }, - { - title: 'What are existential risks (x-risks)? -- from pool', - pageid: '89LL', - }, - { - title: "Isn't the real concern misuse? -- from pool", - pageid: '9B85', - }, - { - title: "Aren't there easy solutions to AI alignment? -- from pool", - pageid: '6172', - }, - { - title: 'Will we ever build superintelligence? -- from pool', - pageid: '7565', - }, - { - title: 'Will the first AGI be an LLM? -- from pool', - pageid: '85E2', - }, - { - title: 'Why not just raise AI like kids? -- from pool', - pageid: '93R9', - }, - { - title: 'Why is AI alignment a hard problem? -- from pool', - pageid: '8163', + title: + 'What are the differences between a singularity, an intelligence explosion, and a hard takeoff?', + pageid: '8IHO', }, + {title: 'What is an intelligence explosion?', pageid: '6306'}, + {title: 'How might AGI kill people?', pageid: '5943'}, + {title: 'What is a "warning shot"?', pageid: '7748'}, ] const MIN_SIMILARITY = 0.85 @@ -105,7 +94,7 @@ const QuestionInput = ({initial, onChange, onAsk}: QuestionInputProps) => { ) } -export const WidgetStampy = () => { +export const WidgetStampy = ({className}: {className?: string}) => { const [question, setQuestion] = useState('') const navigate = useNavigate() const questions = [ @@ -116,7 +105,7 @@ export const WidgetStampy = () => { const stampyUrl = (question: string) => `/chat/?question=${question.trim()}` return ( -
+

Questions?

Ask Stampy, our chatbot, any question about AI safety

@@ -257,6 +246,7 @@ export const Chatbot = ({question, questions, settings}: ChatbotProps) => { // Add a new history entry, replacing the previous one if it was canceled const message = {content: question, role: 'user'} as Entry + const answer = {role: 'assistant', question} as AssistantEntry setHistory((current) => { const last = current[current.length - 1] if ( @@ -265,15 +255,11 @@ export const Chatbot = ({question, questions, settings}: ChatbotProps) => { (last?.role === 'stampy' && last?.content) || ['error'].includes(last?.role) ) { - return [...current, message, {role: 'assistant'} as AssistantEntry] + return [...current, message, answer] } else if (last?.role === 'user' && last?.content === question) { - return [...current.slice(0, current.length - 1), {role: 'assistant'} as AssistantEntry] + return [...current.slice(0, current.length - 1), answer] } - return [ - ...current.slice(0, current.length - 2), - message, - {role: 'assistant'} as AssistantEntry, - ] + return [...current.slice(0, current.length - 2), message, answer] }) setFollowups(undefined) diff --git a/app/components/Feedback/Form.tsx b/app/components/Feedback/Form.tsx index c2054599..c3febdcb 100644 --- a/app/components/Feedback/Form.tsx +++ b/app/components/Feedback/Form.tsx @@ -4,11 +4,13 @@ import useOutsideOnClick from '~/hooks/useOnOutsideClick' import './feedback.css' export type FeedbackFormProps = { + onSubmit?: (msg: string, option?: string) => Promise onClose?: () => void options?: string[] } -const FeedbackForm = ({onClose, options}: FeedbackFormProps) => { +const FeedbackForm = ({onSubmit, onClose, options}: FeedbackFormProps) => { const [selected, setSelected] = useState() + const [message, setMessage] = useState('') const [enabledSubmit, setEnabledSubmit] = useState(!options) const [numClicks, setNumClicks] = useState(0) const clickCheckerRef = useOutsideOnClick(onClose) @@ -28,7 +30,8 @@ const FeedbackForm = ({onClose, options}: FeedbackFormProps) => { setEnabledSubmit(true) } - const handleSubmit = () => { + const handleSubmit = async () => { + onSubmit && (await onSubmit(message, selected)) onClose && onClose() } @@ -56,6 +59,7 @@ const FeedbackForm = ({onClose, options}: FeedbackFormProps) => { name="feedback-text" className={['feedback-text bordered', !options ? 'no-options' : ''].join(' ')} placeholder="Leave a comment (optional)" + onChange={(e) => setMessage(e.target.value)} />