fix: refresh should not create new thread (#3314)
This commit is contained in:
parent
a4f5fda104
commit
624d07703c
@ -1,8 +1,7 @@
|
|||||||
import React, { ReactNode, forwardRef } from 'react'
|
import React, { forwardRef } from 'react'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
import './styles.scss'
|
import './styles.scss'
|
||||||
import { ScrollArea } from '../ScrollArea'
|
|
||||||
|
|
||||||
export interface TextAreaProps
|
export interface TextAreaProps
|
||||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||||
|
|||||||
@ -2,18 +2,27 @@
|
|||||||
|
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
|
import { useAtomValue } from 'jotai'
|
||||||
|
|
||||||
import useAssistantCreate, { janAssistant } from '@/hooks/useAssistantCreate'
|
import useAssistantCreate, { janAssistant } from '@/hooks/useAssistantCreate'
|
||||||
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||||
import useEngineQuery from '@/hooks/useEngineQuery'
|
import useEngineQuery from '@/hooks/useEngineQuery'
|
||||||
import { useLoadTheme } from '@/hooks/useLoadTheme'
|
import { useLoadTheme } from '@/hooks/useLoadTheme'
|
||||||
import useModelHub from '@/hooks/useModelHub'
|
import useModelHub from '@/hooks/useModelHub'
|
||||||
import useModels from '@/hooks/useModels'
|
import useModelQuery from '@/hooks/useModelQuery'
|
||||||
import useThreads from '@/hooks/useThreads'
|
import useThreadCreateMutation from '@/hooks/useThreadCreateMutation'
|
||||||
|
import useThreadQuery from '@/hooks/useThreadQuery'
|
||||||
|
|
||||||
|
import { getSelectedModelAtom } from '@/helpers/atoms/Model.atom'
|
||||||
|
import { threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
const DataLoader: React.FC = () => {
|
const DataLoader: React.FC = () => {
|
||||||
const { getThreadList } = useThreads()
|
const selectedModel = useAtomValue(getSelectedModelAtom)
|
||||||
const { getModels } = useModels()
|
const allThreads = useAtomValue(threadsAtom)
|
||||||
const { data: assistants } = useAssistantQuery()
|
const { data: assistants } = useAssistantQuery()
|
||||||
|
const { data: models } = useModelQuery()
|
||||||
|
const { data: threads, isLoading: isFetchingThread } = useThreadQuery()
|
||||||
|
const createThreadMutation = useThreadCreateMutation()
|
||||||
const assistantCreateMutation = useAssistantCreate()
|
const assistantCreateMutation = useAssistantCreate()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -25,16 +34,36 @@ const DataLoader: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [assistants, assistantCreateMutation])
|
}, [assistants, assistantCreateMutation])
|
||||||
|
|
||||||
|
// automatically create new thread if thread list is empty
|
||||||
|
useEffect(() => {
|
||||||
|
if (isFetchingThread) return
|
||||||
|
if (allThreads.length > 0) return
|
||||||
|
if (!assistants || assistants.length === 0) return
|
||||||
|
if (!models || models.length === 0) return
|
||||||
|
if (allThreads.length === 0 && !createThreadMutation.isPending) {
|
||||||
|
const model = selectedModel ?? models[0]
|
||||||
|
const assistant = assistants[0]
|
||||||
|
|
||||||
|
console.log('Create new thread because user have no thread')
|
||||||
|
createThreadMutation.mutate({
|
||||||
|
modelId: model.id,
|
||||||
|
assistant: assistant,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
assistants,
|
||||||
|
models,
|
||||||
|
isFetchingThread,
|
||||||
|
threads,
|
||||||
|
createThreadMutation,
|
||||||
|
allThreads,
|
||||||
|
selectedModel,
|
||||||
|
])
|
||||||
|
|
||||||
useModelHub()
|
useModelHub()
|
||||||
useLoadTheme()
|
useLoadTheme()
|
||||||
useEngineQuery()
|
useEngineQuery()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getThreadList()
|
|
||||||
getModels()
|
|
||||||
}, [getThreadList, getModels])
|
|
||||||
|
|
||||||
console.debug('Load Data...')
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { useAtomValue, useSetAtom } from 'jotai'
|
|||||||
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||||
import useThreads from '@/hooks/useThreads'
|
import useThreads from '@/hooks/useThreads'
|
||||||
|
|
||||||
import { copyOverInstructionEnabledAtom } from '@/screens/Settings/Advanced/components/CopyOverInstruction'
|
import { copyOverInstructionEnabledAtom } from '@/screens/Thread/ThreadRightPanel/AssistantSettingContainer/components/CopyOverInstruction'
|
||||||
|
|
||||||
import { toaster } from '../Toast'
|
import { toaster } from '../Toast'
|
||||||
|
|
||||||
|
|||||||
@ -70,17 +70,14 @@ export const isGeneratingResponseAtom = atom<boolean>(false)
|
|||||||
*/
|
*/
|
||||||
export const threadsAtom = atom<Thread[]>([])
|
export const threadsAtom = atom<Thread[]>([])
|
||||||
|
|
||||||
export const deleteThreadAtom = atom(null, (_get, set, threadId: string) => {
|
export const deleteThreadAtom = atom(null, (get, set, threadId: string) => {
|
||||||
set(threadsAtom, (threads) => {
|
const allThreads = get(threadsAtom)
|
||||||
// set active thread to the latest
|
const filteredThreads = allThreads.filter((t) => t.id !== threadId)
|
||||||
const allThreads = threads.filter((c) => c.id !== threadId)
|
if (filteredThreads.length > 0) {
|
||||||
if (allThreads.length > 0) {
|
|
||||||
const latestThread = allThreads[0]
|
const latestThread = allThreads[0]
|
||||||
set(activeThreadIdAtom, latestThread.id)
|
set(activeThreadIdAtom, latestThread.id)
|
||||||
}
|
}
|
||||||
|
set(threadsAtom, filteredThreads)
|
||||||
return allThreads
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const activeThreadAtom = atom<Thread | undefined>((get) =>
|
export const activeThreadAtom = atom<Thread | undefined>((get) =>
|
||||||
|
|||||||
26
web/hooks/useModelQuery.ts
Normal file
26
web/hooks/useModelQuery.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import { useSetAtom } from 'jotai'
|
||||||
|
|
||||||
|
import useCortex from './useCortex'
|
||||||
|
|
||||||
|
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
|
||||||
|
|
||||||
|
export const modelQueryKey = ['getModels']
|
||||||
|
|
||||||
|
const useModelQuery = () => {
|
||||||
|
const { fetchModels } = useCortex()
|
||||||
|
const setDownloadedModels = useSetAtom(downloadedModelsAtom)
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: modelQueryKey,
|
||||||
|
queryFn: async () => {
|
||||||
|
const models = await fetchModels()
|
||||||
|
setDownloadedModels(models)
|
||||||
|
return models
|
||||||
|
},
|
||||||
|
staleTime: 30 * 1000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useModelQuery
|
||||||
58
web/hooks/useThreadCreateMutation.ts
Normal file
58
web/hooks/useThreadCreateMutation.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Assistant } from '@janhq/core'
|
||||||
|
import { useMutation } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import { useSetAtom } from 'jotai'
|
||||||
|
|
||||||
|
import { toaster } from '@/containers/Toast'
|
||||||
|
|
||||||
|
import useCortex from './useCortex'
|
||||||
|
|
||||||
|
import { setThreadMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
|
||||||
|
import { setActiveThreadIdAtom, threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
|
export type ThreadCreateMutationVariables = {
|
||||||
|
modelId: string
|
||||||
|
assistant: Assistant
|
||||||
|
instructions?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const useThreadCreateMutation = () => {
|
||||||
|
const { createThread } = useCortex()
|
||||||
|
const setThreads = useSetAtom(threadsAtom)
|
||||||
|
const setActiveThreadId = useSetAtom(setActiveThreadIdAtom)
|
||||||
|
const setThreadMessage = useSetAtom(setThreadMessagesAtom)
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (variables: ThreadCreateMutationVariables) => {
|
||||||
|
const { assistant, modelId, instructions } = variables
|
||||||
|
if (instructions) {
|
||||||
|
assistant.instructions = instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
return createThread({
|
||||||
|
...assistant,
|
||||||
|
model: modelId,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onSuccess: (thread, variables, context) => {
|
||||||
|
console.log('New thread created', thread, variables, context)
|
||||||
|
setThreads((threads) => [thread, ...threads])
|
||||||
|
setActiveThreadId(thread.id)
|
||||||
|
setThreadMessage(thread.id, [])
|
||||||
|
},
|
||||||
|
|
||||||
|
onError: (error, variables) => {
|
||||||
|
console.error(
|
||||||
|
`Failed to create new thread: ${JSON.stringify(variables)}, error: ${error}`
|
||||||
|
)
|
||||||
|
toaster({
|
||||||
|
title: 'Failed to create thread',
|
||||||
|
description: `Unexpected error while creating thread. Please try again!`,
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useThreadCreateMutation
|
||||||
26
web/hooks/useThreadQuery.ts
Normal file
26
web/hooks/useThreadQuery.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import { useSetAtom } from 'jotai'
|
||||||
|
|
||||||
|
import useCortex from './useCortex'
|
||||||
|
|
||||||
|
import { threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
|
export const threadQueryKey = ['getThreads']
|
||||||
|
|
||||||
|
const useThreadQuery = () => {
|
||||||
|
const { fetchThreads } = useCortex()
|
||||||
|
const setThreads = useSetAtom(threadsAtom)
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: threadQueryKey,
|
||||||
|
queryFn: async () => {
|
||||||
|
const threads = await fetchThreads()
|
||||||
|
setThreads(threads)
|
||||||
|
return threads
|
||||||
|
},
|
||||||
|
staleTime: 30 * 1000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useThreadQuery
|
||||||
@ -27,18 +27,12 @@ const useThreads = () => {
|
|||||||
const deleteThreadState = useSetAtom(deleteThreadAtom)
|
const deleteThreadState = useSetAtom(deleteThreadAtom)
|
||||||
const cleanMessages = useSetAtom(cleanChatMessageAtom)
|
const cleanMessages = useSetAtom(cleanChatMessageAtom)
|
||||||
const {
|
const {
|
||||||
fetchThreads,
|
|
||||||
createThread,
|
createThread,
|
||||||
fetchMessages,
|
fetchMessages,
|
||||||
deleteThread: deleteCortexThread,
|
deleteThread: deleteCortexThread,
|
||||||
cleanThread: cleanCortexThread,
|
cleanThread: cleanCortexThread,
|
||||||
} = useCortex()
|
} = useCortex()
|
||||||
|
|
||||||
const getThreadList = useCallback(async () => {
|
|
||||||
const threads = await fetchThreads()
|
|
||||||
setThreads(threads)
|
|
||||||
}, [setThreads, fetchThreads])
|
|
||||||
|
|
||||||
const setActiveThread = useCallback(
|
const setActiveThread = useCallback(
|
||||||
async (threadId: string) => {
|
async (threadId: string) => {
|
||||||
const messages = await fetchMessages(threadId)
|
const messages = await fetchMessages(threadId)
|
||||||
@ -85,7 +79,6 @@ const useThreads = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getThreadList,
|
|
||||||
createThread: createNewThread,
|
createThread: createNewThread,
|
||||||
setActiveThread,
|
setActiveThread,
|
||||||
deleteThread,
|
deleteThread,
|
||||||
|
|||||||
@ -13,7 +13,8 @@ import useAssistantQuery from '@/hooks/useAssistantQuery'
|
|||||||
import { downloadStateListAtom } from '@/hooks/useDownloadState'
|
import { downloadStateListAtom } from '@/hooks/useDownloadState'
|
||||||
import useModelDownloadMutation from '@/hooks/useModelDownloadMutation'
|
import useModelDownloadMutation from '@/hooks/useModelDownloadMutation'
|
||||||
import { QuickStartModel } from '@/hooks/useModelHub'
|
import { QuickStartModel } from '@/hooks/useModelHub'
|
||||||
import useThreads from '@/hooks/useThreads'
|
|
||||||
|
import useThreadCreateMutation from '@/hooks/useThreadCreateMutation'
|
||||||
|
|
||||||
import { formatDownloadPercentage, toGibibytes } from '@/utils/converter'
|
import { formatDownloadPercentage, toGibibytes } from '@/utils/converter'
|
||||||
import { downloadProgress } from '@/utils/download'
|
import { downloadProgress } from '@/utils/download'
|
||||||
@ -75,8 +76,8 @@ const DownloadContainer: React.FC<DownloadContainerProps> = ({
|
|||||||
fileName,
|
fileName,
|
||||||
}) => {
|
}) => {
|
||||||
const downloadModelMutation = useModelDownloadMutation()
|
const downloadModelMutation = useModelDownloadMutation()
|
||||||
|
const createThreadMutation = useThreadCreateMutation()
|
||||||
const setMainViewState = useSetAtom(mainViewStateAtom)
|
const setMainViewState = useSetAtom(mainViewStateAtom)
|
||||||
const { createThread } = useThreads()
|
|
||||||
const { data: assistants } = useAssistantQuery()
|
const { data: assistants } = useAssistantQuery()
|
||||||
|
|
||||||
const { abortDownload } = useAbortDownload()
|
const { abortDownload } = useAbortDownload()
|
||||||
@ -116,16 +117,19 @@ const DownloadContainer: React.FC<DownloadContainerProps> = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await createThread(persistModelId, {
|
await createThreadMutation.mutateAsync({
|
||||||
|
modelId: persistModelId,
|
||||||
|
assistant: {
|
||||||
...assistants[0],
|
...assistants[0],
|
||||||
model: persistModelId,
|
model: persistModelId,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
setDownloadLocalModelModalStage('NONE', undefined)
|
setDownloadLocalModelModalStage('NONE', undefined)
|
||||||
setMainViewState(MainViewState.Thread)
|
setMainViewState(MainViewState.Thread)
|
||||||
}, [
|
}, [
|
||||||
setDownloadLocalModelModalStage,
|
setDownloadLocalModelModalStage,
|
||||||
setMainViewState,
|
setMainViewState,
|
||||||
createThread,
|
createThreadMutation,
|
||||||
persistModelId,
|
persistModelId,
|
||||||
assistants,
|
assistants,
|
||||||
])
|
])
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
import { ChangeEvent, useCallback } from 'react'
|
|
||||||
|
|
||||||
import { Switch } from '@janhq/joi'
|
|
||||||
import { useAtom } from 'jotai'
|
|
||||||
import { atomWithStorage } from 'jotai/utils'
|
|
||||||
|
|
||||||
const COPY_OVER_INSTRUCTION_ENABLED = 'copy_over_instruction_enabled'
|
|
||||||
export const copyOverInstructionEnabledAtom = atomWithStorage(
|
|
||||||
COPY_OVER_INSTRUCTION_ENABLED,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
const CopyOverInstructionItem: React.FC = () => {
|
|
||||||
const [copyOverInstructionEnabled, setCopyOverInstructionEnabled] = useAtom(
|
|
||||||
copyOverInstructionEnabledAtom
|
|
||||||
)
|
|
||||||
|
|
||||||
const onSwitchToggled = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setCopyOverInstructionEnabled(e.target.checked)
|
|
||||||
},
|
|
||||||
[setCopyOverInstructionEnabled]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex w-full flex-col items-start justify-between gap-4 border-b border-[hsla(var(--app-border))] py-4 first:pt-0 last:border-none sm:flex-row">
|
|
||||||
<div className="flex-shrink-0 space-y-1">
|
|
||||||
<div className="flex gap-x-2">
|
|
||||||
<h6 className="font-semibold capitalize">Copy Over Instruction</h6>
|
|
||||||
</div>
|
|
||||||
<p className="font-medium leading-relaxed text-[hsla(var(--text-secondary))]">
|
|
||||||
Enable instruction to be copied to new thread
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{/**/}
|
|
||||||
<Switch checked={copyOverInstructionEnabled} onChange={onSwitchToggled} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CopyOverInstructionItem
|
|
||||||
@ -14,7 +14,6 @@ import useModelStop from '@/hooks/useModelStop'
|
|||||||
import { useSettings } from '@/hooks/useSettings'
|
import { useSettings } from '@/hooks/useSettings'
|
||||||
|
|
||||||
import DataFolder from './DataFolder'
|
import DataFolder from './DataFolder'
|
||||||
import CopyOverInstructionItem from './components/CopyOverInstruction'
|
|
||||||
|
|
||||||
import DataMigration from './components/DataMigration'
|
import DataMigration from './components/DataMigration'
|
||||||
|
|
||||||
@ -459,7 +458,6 @@ const Advanced = () => {
|
|||||||
|
|
||||||
{/* Factory Reset */}
|
{/* Factory Reset */}
|
||||||
{/* <FactoryReset /> */}
|
{/* <FactoryReset /> */}
|
||||||
{experimentalEnabled && <CopyOverInstructionItem />}
|
|
||||||
{experimentalEnabled && <DataMigration />}
|
{experimentalEnabled && <DataMigration />}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@ -32,9 +32,7 @@ const ChatTextInput: React.FC<Props> = ({
|
|||||||
|
|
||||||
const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom)
|
const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom)
|
||||||
|
|
||||||
const disabled = useMemo(() => {
|
const disabled = useMemo(() => !activeThreadId, [activeThreadId])
|
||||||
return !activeThreadId
|
|
||||||
}, [activeThreadId])
|
|
||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
@ -44,10 +42,8 @@ const ChatTextInput: React.FC<Props> = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (textareaRef.current) {
|
textareaRef.current?.focus()
|
||||||
textareaRef.current.focus()
|
})
|
||||||
}
|
|
||||||
}, [activeThreadId])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (textareaRef.current?.clientHeight) {
|
if (textareaRef.current?.clientHeight) {
|
||||||
|
|||||||
@ -23,12 +23,16 @@ const ModalDeleteThread: React.FC<Props> = ({
|
|||||||
const { deleteThread } = useThreads()
|
const { deleteThread } = useThreads()
|
||||||
|
|
||||||
const onDeleteThreadClick = useCallback(async () => {
|
const onDeleteThreadClick = useCallback(async () => {
|
||||||
|
try {
|
||||||
await deleteThread(id)
|
await deleteThread(id)
|
||||||
toaster({
|
toaster({
|
||||||
title: 'Thread successfully deleted.',
|
title: 'Thread successfully deleted.',
|
||||||
description: `Thread ${title} has been successfully deleted.`,
|
description: `Thread ${title} has been successfully deleted.`,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
}, [deleteThread, id, title])
|
}, [deleteThread, id, title])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
|
|
||||||
import { Button } from '@janhq/joi'
|
import { Button } from '@janhq/joi'
|
||||||
import { AnimatePresence } from 'framer-motion'
|
import { AnimatePresence } from 'framer-motion'
|
||||||
@ -13,48 +13,31 @@ import { toaster } from '@/containers/Toast'
|
|||||||
|
|
||||||
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
import useAssistantQuery from '@/hooks/useAssistantQuery'
|
||||||
|
|
||||||
|
import useThreadCreateMutation from '@/hooks/useThreadCreateMutation'
|
||||||
|
|
||||||
import useThreads from '@/hooks/useThreads'
|
import useThreads from '@/hooks/useThreads'
|
||||||
|
|
||||||
import { copyOverInstructionEnabledAtom } from '@/screens/Settings/Advanced/components/CopyOverInstruction'
|
import { copyOverInstructionEnabledAtom } from '../ThreadRightPanel/AssistantSettingContainer/components/CopyOverInstruction'
|
||||||
|
|
||||||
import ThreadItem from './ThreadItem'
|
import ThreadItem from './ThreadItem'
|
||||||
|
|
||||||
import {
|
import { getSelectedModelAtom } from '@/helpers/atoms/Model.atom'
|
||||||
downloadedModelsAtom,
|
|
||||||
getSelectedModelAtom,
|
|
||||||
} from '@/helpers/atoms/Model.atom'
|
|
||||||
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
|
||||||
import { activeThreadAtom, threadsAtom } from '@/helpers/atoms/Thread.atom'
|
import { activeThreadAtom, threadsAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
const ThreadLeftPanel: React.FC = () => {
|
const ThreadLeftPanel: React.FC = () => {
|
||||||
const { createThread, setActiveThread } = useThreads()
|
const { setActiveThread } = useThreads()
|
||||||
|
const createThreadMutation = useThreadCreateMutation()
|
||||||
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
const reduceTransparent = useAtomValue(reduceTransparentAtom)
|
||||||
const downloadedModels = useAtomValue(downloadedModelsAtom)
|
|
||||||
const selectedModel = useAtomValue(getSelectedModelAtom)
|
const selectedModel = useAtomValue(getSelectedModelAtom)
|
||||||
const threads = useAtomValue(threadsAtom)
|
const threads = useAtomValue(threadsAtom)
|
||||||
|
|
||||||
const activeThread = useAtomValue(activeThreadAtom)
|
const activeThread = useAtomValue(activeThreadAtom)
|
||||||
const { data: assistants } = useAssistantQuery()
|
const { data: assistants } = useAssistantQuery()
|
||||||
const isCreatingThread = useRef(false)
|
|
||||||
const copyOverInstructionEnabled = useAtomValue(
|
const copyOverInstructionEnabled = useAtomValue(
|
||||||
copyOverInstructionEnabledAtom
|
copyOverInstructionEnabledAtom
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// if user does not have any threads, we should create one
|
|
||||||
const createThreadIfEmpty = async () => {
|
|
||||||
if (!assistants || assistants.length === 0) return
|
|
||||||
if (downloadedModels.length === 0) return
|
|
||||||
if (threads.length > 0) return
|
|
||||||
if (isCreatingThread.current) return
|
|
||||||
isCreatingThread.current = true
|
|
||||||
// user have models but does not have any thread. Let's create one
|
|
||||||
await createThread(downloadedModels[0].model, assistants[0])
|
|
||||||
isCreatingThread.current = false
|
|
||||||
}
|
|
||||||
createThreadIfEmpty()
|
|
||||||
}, [threads, assistants, downloadedModels, createThread])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeThread?.id) return
|
if (activeThread?.id) return
|
||||||
if (threads.length === 0) return
|
if (threads.length === 0) return
|
||||||
@ -75,9 +58,13 @@ const ThreadLeftPanel: React.FC = () => {
|
|||||||
if (copyOverInstructionEnabled) {
|
if (copyOverInstructionEnabled) {
|
||||||
instructions = activeThread?.assistants[0]?.instructions ?? undefined
|
instructions = activeThread?.assistants[0]?.instructions ?? undefined
|
||||||
}
|
}
|
||||||
createThread(selectedModel.model, assistants[0], instructions)
|
await createThreadMutation.mutateAsync({
|
||||||
|
modelId: selectedModel.model,
|
||||||
|
assistant: assistants[0],
|
||||||
|
instructions,
|
||||||
|
})
|
||||||
}, [
|
}, [
|
||||||
createThread,
|
createThreadMutation,
|
||||||
selectedModel,
|
selectedModel,
|
||||||
assistants,
|
assistants,
|
||||||
activeThread,
|
activeThread,
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { ChangeEvent, useCallback } from 'react'
|
||||||
|
|
||||||
|
import { Switch } from '@janhq/joi'
|
||||||
|
import { useAtom } from 'jotai'
|
||||||
|
import { atomWithStorage } from 'jotai/utils'
|
||||||
|
|
||||||
|
const COPY_OVER_INSTRUCTION_ENABLED = 'copy_over_instruction_enabled'
|
||||||
|
|
||||||
|
export const copyOverInstructionEnabledAtom = atomWithStorage(
|
||||||
|
COPY_OVER_INSTRUCTION_ENABLED,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
const CopyOverInstruction: React.FC = () => {
|
||||||
|
const [copyOverInstructionEnabled, setCopyOverInstructionEnabled] = useAtom(
|
||||||
|
copyOverInstructionEnabledAtom
|
||||||
|
)
|
||||||
|
|
||||||
|
const onSwitchToggled = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setCopyOverInstructionEnabled(e.target.checked)
|
||||||
|
},
|
||||||
|
[setCopyOverInstructionEnabled]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-row items-center justify-center gap-x-2">
|
||||||
|
<h6 className="flex-1 font-medium">Save instructions for all threads</h6>
|
||||||
|
<Switch
|
||||||
|
checked={copyOverInstructionEnabled}
|
||||||
|
onChange={onSwitchToggled}
|
||||||
|
className="flex-shrink-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CopyOverInstruction
|
||||||
@ -7,9 +7,14 @@ import { useDebouncedCallback } from 'use-debounce'
|
|||||||
|
|
||||||
import useUpdateInstruction from '@/hooks/useUpdateInstruction'
|
import useUpdateInstruction from '@/hooks/useUpdateInstruction'
|
||||||
|
|
||||||
|
import CopyOverInstruction from './components/CopyOverInstruction'
|
||||||
|
|
||||||
|
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
|
||||||
|
|
||||||
import { activeThreadAtom } from '@/helpers/atoms/Thread.atom'
|
import { activeThreadAtom } from '@/helpers/atoms/Thread.atom'
|
||||||
|
|
||||||
const AssistantSettingContainer: React.FC = () => {
|
const AssistantSettingContainer: React.FC = () => {
|
||||||
|
const experimentalEnabled = useAtomValue(experimentalFeatureEnabledAtom)
|
||||||
const activeThread = useAtomValue(activeThreadAtom)
|
const activeThread = useAtomValue(activeThreadAtom)
|
||||||
const [instructions, setInstructions] = useState(
|
const [instructions, setInstructions] = useState(
|
||||||
activeThread?.assistants[0]?.instructions || ''
|
activeThread?.assistants[0]?.instructions || ''
|
||||||
@ -43,11 +48,13 @@ const AssistantSettingContainer: React.FC = () => {
|
|||||||
Instructions
|
Instructions
|
||||||
</label>
|
</label>
|
||||||
<TextArea
|
<TextArea
|
||||||
|
rows={5}
|
||||||
id="assistant-instructions"
|
id="assistant-instructions"
|
||||||
placeholder="Eg. You are a helpful assistant."
|
placeholder="Eg. You are a helpful assistant."
|
||||||
value={instructions}
|
value={instructions}
|
||||||
onChange={onInstructionChanged}
|
onChange={onInstructionChanged}
|
||||||
/>
|
/>
|
||||||
|
{experimentalEnabled && <CopyOverInstruction />}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user