provide a way to generate an assistant message if deleted or errored

This commit is contained in:
Ethan Garber 2025-06-25 20:27:23 -04:00
parent 4e2b28c687
commit 9018f6deae

View File

@ -5,6 +5,7 @@ import debounce from 'lodash.debounce'
import cloneDeep from 'lodash.clonedeep' import cloneDeep from 'lodash.clonedeep'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { ArrowDown } from 'lucide-react' import { ArrowDown } from 'lucide-react'
import { Play } from 'lucide-react'
import HeaderPage from '@/containers/HeaderPage' import HeaderPage from '@/containers/HeaderPage'
import { useThreads } from '@/hooks/useThreads' import { useThreads } from '@/hooks/useThreads'
@ -22,6 +23,7 @@ import { useAppearance } from '@/hooks/useAppearance'
import { useOutOfContextPromiseModal } from '@/containers/dialogs/OutOfContextDialog' import { useOutOfContextPromiseModal } from '@/containers/dialogs/OutOfContextDialog'
import { ContentType, ThreadMessage } from '@janhq/core' import { ContentType, ThreadMessage } from '@janhq/core'
import { useTranslation } from '@/i18n/react-i18next-compat' import { useTranslation } from '@/i18n/react-i18next-compat'
import { useChat } from '@/hooks/useChat'
// as route.threadsDetail // as route.threadsDetail
export const Route = createFileRoute('/threads/$threadId')({ export const Route = createFileRoute('/threads/$threadId')({
@ -40,6 +42,7 @@ function ThreadDetail() {
const { setMessages } = useMessages() const { setMessages } = useMessages()
const { streamingContent } = useAppState() const { streamingContent } = useAppState()
const { appMainViewBgColor, chatWidth } = useAppearance() const { appMainViewBgColor, chatWidth } = useAppearance()
const { sendMessage } = useChat()
const { messages } = useMessages( const { messages } = useMessages(
useShallow((state) => ({ useShallow((state) => ({
@ -216,12 +219,24 @@ function ThreadDetail() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
// used when there is a sent/added user message and no assistant message (error or manual deletion)
const generateAIResponse = () => {
const latestUserMessage = messages[messages.length - 1]
if (latestUserMessage?.content?.[0]?.text?.value) {
sendMessage(latestUserMessage.content[0].text.value, undefined, false)
}
}
const threadModel = useMemo(() => thread?.model, [thread]) const threadModel = useMemo(() => thread?.model, [thread])
if (!messages || !threadModel) return null if (!messages || !threadModel) return null
const contextOverflowModalComponent = <OutOfContextModal /> const contextOverflowModalComponent = <OutOfContextModal />
const showScrollToBottomBtn = !isAtBottom && hasScrollbar
const showGenerateAIResponseBtn =
messages[messages.length - 1]?.role === 'user' && !streamingContent
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
<HeaderPage> <HeaderPage>
@ -289,19 +304,31 @@ function ThreadDetail() {
appMainViewBgColor.a === 1 appMainViewBgColor.a === 1
? 'from-main-view/20 bg-gradient-to-b to-main-view backdrop-blur' ? 'from-main-view/20 bg-gradient-to-b to-main-view backdrop-blur'
: 'bg-transparent', : 'bg-transparent',
!isAtBottom && hasScrollbar && 'visibility-visible opacity-100' (showScrollToBottomBtn || showGenerateAIResponseBtn) &&
'visibility-visible opacity-100'
)} )}
> >
<div {showScrollToBottomBtn && (
className="bg-main-view-fg/10 px-4 border border-main-view-fg/5 flex items-center justify-center rounded-xl gap-x-2 cursor-pointer pointer-events-auto" <div
onClick={() => { className="bg-main-view-fg/10 px-4 border border-main-view-fg/5 flex items-center justify-center rounded-xl gap-x-2 cursor-pointer pointer-events-auto"
scrollToBottom(true) onClick={() => {
setIsUserScrolling(false) scrollToBottom(true)
}} setIsUserScrolling(false)
> }}
<p className="text-xs">{t('scrollToBottom')}</p> >
<ArrowDown size={12} /> <p className="text-xs">{t('scrollToBottom')}</p>
</div> <ArrowDown size={12} />
</div>
)}
{showGenerateAIResponseBtn && (
<div
className="bg-main-view-fg/10 px-4 border border-main-view-fg/5 flex items-center justify-center rounded-xl gap-x-2 cursor-pointer pointer-events-auto"
onClick={generateAIResponse}
>
<p className="text-xs">{t('Generate AI Response')}</p>
<Play size={12} />
</div>
)}
</div> </div>
<ChatInput model={threadModel} /> <ChatInput model={threadModel} />
</div> </div>