Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use human answers when available #601

Merged
merged 1 commit into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 52 additions & 22 deletions app/components/Chatbot/ChatEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import {ComponentType} from 'react'
import {Link} from '@remix-run/react'
import MarkdownIt from 'markdown-it'
import QuestionMarkIcon from '~/components/icons-generated/QuestionMark'
import BotIcon from '~/components/icons-generated/Bot'
import PersonIcon from '~/components/icons-generated/Person'
import StampyIcon from '~/components/icons-generated/Stampy'
import Contents from '~/components/Article/Contents'
import useGlossary from '~/hooks/useGlossary'
import './chat_entry.css'
import type {Entry, AssistantEntry, StampyEntry, Citation} from '~/hooks/useChat'
import type {Entry, AssistantEntry, StampyEntry, Citation, ErrorMessage} from '~/hooks/useChat'

const MAX_REFERENCES = 10
const hints = {
bot: 'bla bla bla something bot',
human: 'bla bla bla by humans',
error: null,
}

const AnswerInfo = ({answerType}: {answerType?: 'human' | 'bot'}) => {
if (!answerType) return null
const AnswerInfo = ({answerType}: {answerType?: 'human' | 'bot' | 'error'}) => {
if (!answerType || !hints[answerType]) return null
return (
<span className="info">
{answerType === 'human' ? <PersonIcon /> : <BotIcon />}
Expand All @@ -31,7 +34,7 @@ const AnswerInfo = ({answerType}: {answerType?: 'human' | 'bot'}) => {
type TitleProps = {
title: string
Icon: ComponentType
answerType?: 'human' | 'bot'
answerType?: 'human' | 'bot' | 'error'
}
const Title = ({title, Icon, answerType}: TitleProps) => (
<div className="flex-container title">
Expand All @@ -48,14 +51,29 @@ const UserQuery = ({content}: Entry) => (
</div>
)

// FIXME: this id should be unique across the page - I doubt it will be now
const ReferenceLink = ({id, reference}: {id: string; reference: string}) => (
<Link id={`#${id}-ref`} to={`#${id}`} className="reference-link">
{reference}
</Link>
)
const md = new MarkdownIt({html: true})
const ReferenceLink = ({id, index, text}: Citation) => {
if (!index || index > MAX_REFERENCES) return ''

const Reference = ({id, title, authors, source, url, reference}: Citation) => {
const parsed = text?.match(/^###.*?###\s+"""(.*?)"""$/ms)
return (
<>
<Link id={`${id}-ref`} to={`#${id}`} className={`reference-link ref-${index}`}>
<span>{index}</span>
</Link>
{parsed && (
<div
className="reference-contents rounded"
dangerouslySetInnerHTML={{
__html: md.render(parsed[1]),
}}
/>
)}
</>
)
}

const Reference = ({id, title, authors, source, url, index}: Citation) => {
const referenceSources = {
arxiv: 'Scientific paper',
blogs: 'Blogpost',
Expand All @@ -65,6 +83,7 @@ const Reference = ({id, title, authors, source, url, reference}: Citation) => {
arbital: 'Arbital',
distill: 'Distill',
'aisafety.info': 'AISafety.info',
youtube: 'YouTube',
}

const Authors = ({authors}: {authors?: string[]}) => {
Expand All @@ -78,8 +97,8 @@ const Reference = ({id, title, authors, source, url, reference}: Citation) => {
}

return (
<div key={id} id={`#${id}`} className="reference padding-bottom-32">
<div className="reference-num small">{reference}</div>
<div key={id} id={id} className="reference padding-bottom-32">
<div className={`reference-num small ref-${index}`}>{index}</div>
<div>
<div className="title">{title}</div>
<div>
Expand All @@ -99,9 +118,7 @@ const ChatbotReply = ({phase, content, citationsMap}: AssistantEntry) => {
citationsMap?.forEach((v) => {
citations.push(v)
})

const references = citations.map(({reference}) => reference).join('')
const referencesRegex = new RegExp(`(\\[[${references}]\\])`)
citations.sort((a, b) => a.index - b.index)

const PhaseState = () => {
switch (phase) {
Expand All @@ -128,17 +145,20 @@ const ChatbotReply = ({phase, content, citationsMap}: AssistantEntry) => {
<div>
<Title title="Stampy" Icon={StampyIcon} answerType="bot" />
<PhaseState />
<div>
{content?.split(referencesRegex).map((chunk, i) => {
if (chunk.match(referencesRegex)) {
const ref = citationsMap?.get(chunk[1])
return <ReferenceLink key={i} id={ref?.id || chunk[i]} reference={chunk[1]} />
<div className="padding-bottom-24">
{content?.split(/(\[\d+\])|(\n)/).map((chunk, i) => {
if (chunk?.match(/(\[\d+\])/)) {
const refId = chunk.slice(1, chunk.length - 1)
const ref = citationsMap?.get(refId)
return ref && <ReferenceLink key={i} {...ref} />
} else if (chunk === '\n') {
return <br key={i} />
} else {
return <span key={i}>{chunk}</span>
}
})}
</div>
{citations?.map(Reference)}
{citations?.slice(0, MAX_REFERENCES).map(Reference)}
{phase === 'followups' ? <p>Checking for followups...</p> : undefined}
</div>
)
Expand All @@ -157,11 +177,21 @@ const StampyArticle = ({pageid, content}: StampyEntry) => {
)
}

const ErrorReply = ({content}: ErrorMessage) => {
return (
<div>
<Title title="Error" Icon={StampyIcon} answerType="error" />
<div>{content}</div>
</div>
)
}

const ChatEntry = (props: Entry) => {
const roles = {
user: UserQuery,
stampy: StampyArticle,
assistant: ChatbotReply,
error: ErrorReply,
} as {[k: string]: ComponentType<Entry>}
const Role = roles[props.role] as ComponentType<Entry>
if (!Role) return null
Expand Down
57 changes: 0 additions & 57 deletions app/components/Chatbot/Widgit.tsx

This file was deleted.

81 changes: 77 additions & 4 deletions app/components/Chatbot/chat_entry.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,66 @@ article.stampy {
}

.chat-entry .reference-link {
background: red;
width: var(--spacing-16);
height: var(--spacing-16);
font-size: smaller;
vertical-align: super;
padding: 2px 4px;
margin-left: 2px;
border-radius: var(--border-radius);
}
.chat-entry .reference-link span {
min-width: 10px;
display: inline-block;
text-align: center;
}

.ref-1 {
background: rgb(211, 255, 253);
color: rgb(24, 185, 71);
}

.ref-2 {
background: rgb(255, 221, 244);
color: rgb(251, 0, 158);
}

.ref-3 {
background: rgb(217, 253, 254);
color: rgb(23, 184, 197);
}

.ref-4 {
background: rgb(254, 230, 202);
color: rgb(230, 107, 9);
}

.ref-5 {
background: rgb(244, 223, 255);
color: rgb(164, 3, 254);
}

.ref-6 {
background: rgb(231, 255, 178);
color: rgb(99, 159, 4);
}

.ref-7 {
background: rgb(231, 232, 255);
color: rgb(77, 75, 254);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why you use rgb instead of hex

}

.ref-8 {
background: rgb(255, 254, 156);
color: rgb(144, 140, 5);
}

.ref-9 {
background: rgb(254, 226, 226);
color: rgb(200, 0, 5);
}

.ref-10 {
background: rgb(214, 240, 255);
color: rgb(18, 144, 254);
}

.reference {
Expand All @@ -43,11 +99,28 @@ article.stampy {
.reference .reference-num {
width: var(--spacing-32);
height: var(--spacing-32);
background: red;
border-radius: 6px;
text-align: center;
}

.reference .source-link {
color: var(--colors-teal-500);
}

.reference-contents {
visibility: hidden;
transition: visibility 0.2s;
max-width: 600px;
word-wrap: break-word;
background-color: var(--colors-cool-grey-300);
padding: var(--spacing-16) var(--spacing-24);
position: absolute;
transform: translateX(50%);
text-decoration: unset;
}

.reference-contents:hover,
.reference-link:hover + .reference-contents {
visibility: visible;
transition-delay: 0s;
}
Loading
Loading