From 0522be9879672ed036b4116df0d677779a094528 Mon Sep 17 00:00:00 2001 From: Daniel O'Connell Date: Tue, 30 Apr 2024 15:13:39 +0200 Subject: [PATCH] Chat settings --- app/components/Chatbot/ChatEntry.tsx | 13 +++++++-- app/components/Chatbot/index.tsx | 12 ++++++-- app/components/Chatbot/widgit.css | 13 +++++++++ app/hooks/useChat.ts | 23 +++++++++++---- app/root.css | 4 +++ app/routes/chat.tsx | 43 +++++++++++++++++++++++++++- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/app/components/Chatbot/ChatEntry.tsx b/app/components/Chatbot/ChatEntry.tsx index c28a2d9d..bebefc51 100644 --- a/app/components/Chatbot/ChatEntry.tsx +++ b/app/components/Chatbot/ChatEntry.tsx @@ -3,6 +3,7 @@ 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 LinkIcon from '~/components/icons-generated/Link' import PersonIcon from '~/components/icons-generated/Person' import StampyIcon from '~/components/icons-generated/Stampy' import Contents from '~/components/Article/Contents' @@ -104,8 +105,9 @@ const Reference = ({id, title, authors, source, url, index}: Citation) => {
{' ยท '} - - {referenceSources[source as keyof typeof referenceSources] || new URL(url).host} + + {referenceSources[source as keyof typeof referenceSources] || new URL(url).host}{' '} +
@@ -158,7 +160,12 @@ const ChatbotReply = ({phase, content, citationsMap}: AssistantEntry) => { } })} - {citations?.slice(0, MAX_REFERENCES).map(Reference)} + {citations && citations.length > 0 && ( + <> +
+
{citations?.slice(0, MAX_REFERENCES).map(Reference)}
+ + )} {phase === 'followups' ?

Checking for followups...

: undefined} ) diff --git a/app/components/Chatbot/index.tsx b/app/components/Chatbot/index.tsx index 68682070..9412a470 100644 --- a/app/components/Chatbot/index.tsx +++ b/app/components/Chatbot/index.tsx @@ -3,7 +3,7 @@ import {Link, useFetcher} from '@remix-run/react' import StampyIcon from '~/components/icons-generated/Stampy' import SendIcon from '~/components/icons-generated/PlaneSend' import Button from '~/components/Button' -import {queryLLM, Entry, AssistantEntry, StampyEntry, Followup} from '~/hooks/useChat' +import {queryLLM, Entry, AssistantEntry, StampyEntry, Followup, ChatSettings} from '~/hooks/useChat' import ChatEntry from './ChatEntry' import './widgit.css' import {questionUrl} from '~/routesMapper' @@ -138,7 +138,12 @@ const SplashScreen = ({ ) -export const Chatbot = ({question, questions}: {question?: string; questions?: string[]}) => { +type ChatbotProps = { + question?: string + questions?: string[] + settings?: ChatSettings +} +export const Chatbot = ({question, questions, settings}: ChatbotProps) => { const [followups, setFollowups] = useState() // FIXME: Generate session id @@ -231,7 +236,8 @@ export const Chatbot = ({question, questions}: {question?: string; questions?: s [...history, message], updateReply, sessionId, - newController + newController, + settings ) if (!newController.signal.aborted) { updateReply(result) diff --git a/app/components/Chatbot/widgit.css b/app/components/Chatbot/widgit.css index 1aaea416..a54b8512 100644 --- a/app/components/Chatbot/widgit.css +++ b/app/components/Chatbot/widgit.css @@ -20,3 +20,16 @@ width: 100%; } } + +.settings-container { + position: absolute; + bottom: var(--spacing-16); + left: var(--spacing-16); +} + +.settings { + padding: var(--spacing-32); + margin-bottom: var(--spacing-24); + flex-direction: column; + gap: var(--spacing-24); +} diff --git a/app/hooks/useChat.ts b/app/hooks/useChat.ts index a8563ad6..aaad81ec 100644 --- a/app/hooks/useChat.ts +++ b/app/hooks/useChat.ts @@ -61,7 +61,18 @@ export type SearchResult = { result: Entry } -export type Mode = 'rookie' | 'concise' | 'default' | 'discord' +type Model = + | 'gpt-3.5-turbo' + | 'gpt-4' + | 'gpt-4-turbo-preview' + | 'claude-3-opus-20240229' + | 'claude-3-sonnet-20240229' + | 'claude-3-haiku-20240307' +export type Mode = 'rookie' | 'concise' | 'default' +export type ChatSettings = { + mode?: Mode + completions?: Model +} const DATA_HEADER = 'data: ' const EVENT_END_HEADER = 'event: close' @@ -229,7 +240,8 @@ export const extractAnswer = async ( const fetchLLM = async ( sessionId: string | undefined, history: HistoryEntry[], - controller: AbortController + controller: AbortController, + settings?: ChatSettings ): Promise => fetch(CHATBOT_URL, { signal: controller.signal, @@ -240,18 +252,19 @@ const fetchLLM = async ( 'Content-Type': 'application/json', Accept: 'text/event-stream', }, - body: JSON.stringify({sessionId, history, settings: {mode: 'default'}}), + body: JSON.stringify({sessionId, history, settings: settings || {mode: 'default'}}), }).catch(ignoreAbort) export const queryLLM = async ( history: HistoryEntry[], setCurrent: (e: AssistantEntry) => void, sessionId: string | undefined, - controller: AbortController + controller: AbortController, + settings?: ChatSettings ): Promise => { setCurrent({...makeEntry(), phase: 'started'}) // do SSE on a POST request. - const res = await fetchLLM(sessionId, history, controller) + const res = await fetchLLM(sessionId, history, controller, settings) if (!res) { return {result: {role: 'error', content: 'No response from server'}} diff --git a/app/root.css b/app/root.css index bce98dcc..0de35ba7 100644 --- a/app/root.css +++ b/app/root.css @@ -392,6 +392,10 @@ svg { cursor: pointer; } +.full-height { + height: 100%; +} + /* for troubleshooting */ .pink { diff --git a/app/routes/chat.tsx b/app/routes/chat.tsx index 1adef68e..64b514dc 100644 --- a/app/routes/chat.tsx +++ b/app/routes/chat.tsx @@ -1,16 +1,40 @@ +import {useState} from 'react' import {ShouldRevalidateFunction, useSearchParams} from '@remix-run/react' +import SettingsIcon from '~/components/icons-generated/Settings' import Page from '~/components/Page' import Chatbot from '~/components/Chatbot' +import {ChatSettings, Mode} from '~/hooks/useChat' +import Button from '~/components/Button' export const shouldRevalidate: ShouldRevalidateFunction = () => false export default function App() { const [params] = useSearchParams() + const [showSettings, setShowSettings] = useState(false) + const [chatSettings, setChatSettings] = useState({ + mode: 'default', + completions: 'gpt-3.5-turbo', + } as ChatSettings) const question = params.get('question') || undefined + const ModeButton = ({name, mode}: {name: string; mode: Mode}) => ( + + ) + + const stopBubbling = (e: any) => { + e.preventDefault() + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + } + return ( -
+
+
+ {showSettings && ( +
+
Answer detail
+ + + +
+ )} + setShowSettings((current) => !current)} + /> +
)