| | |
| | | import produce from 'immer' |
| | | import type { |
| | | ChatConfig, |
| | | ChatItem, |
| | | Feedback, |
| | | } from '../types' |
| | | import { CONVERSATION_ID_INFO } from '../constants' |
| | | import { buildChatItemTree, getProcessedInputsFromUrlParams, getProcessedSystemVariablesFromUrlParams } from '../utils' |
| | | import { getProcessedFilesFromResponse } from '../../file-uploader/utils' |
| | | import { getPrevChatList, getProcessedInputsFromUrlParams } from '../utils' |
| | | import { |
| | | fetchAppInfo, |
| | | fetchAppMeta, |
| | |
| | | import { changeLanguage } from '@/i18n/i18next-config' |
| | | import { InputVarType } from '@/app/components/workflow/types' |
| | | import { TransferMethod } from '@/types/app' |
| | | import { addFileInfos, sortAgentSorts } from '@/app/components/tools/utils' |
| | | import { noop } from 'lodash-es' |
| | | |
| | | function getFormattedChatList(messages: any[]) { |
| | | const newChatList: ChatItem[] = [] |
| | | messages.forEach((item) => { |
| | | const questionFiles = item.message_files?.filter((file: any) => file.belongs_to === 'user') || [] |
| | | newChatList.push({ |
| | | id: `question-${item.id}`, |
| | | content: item.query, |
| | | isAnswer: false, |
| | | message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))), |
| | | parentMessageId: item.parent_message_id || undefined, |
| | | }) |
| | | const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [] |
| | | newChatList.push({ |
| | | id: item.id, |
| | | content: item.answer, |
| | | agent_thoughts: addFileInfos(item.agent_thoughts ? sortAgentSorts(item.agent_thoughts) : item.agent_thoughts, item.message_files), |
| | | feedback: item.feedback, |
| | | isAnswer: true, |
| | | citation: item.retriever_resources, |
| | | message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))), |
| | | parentMessageId: `question-${item.id}`, |
| | | }) |
| | | }) |
| | | return newChatList |
| | | } |
| | | |
| | | export const useEmbeddedChatbot = () => { |
| | | const isInstalledApp = false |
| | |
| | | }, [appInfo]) |
| | | const appId = useMemo(() => appData?.app_id, [appData]) |
| | | |
| | | const [userId, setUserId] = useState<string>() |
| | | const [conversationId, setConversationId] = useState<string>() |
| | | useEffect(() => { |
| | | getProcessedSystemVariablesFromUrlParams().then(({ user_id, conversation_id }) => { |
| | | setUserId(user_id) |
| | | setConversationId(conversation_id) |
| | | }) |
| | | }, []) |
| | | |
| | | useEffect(() => { |
| | | const setLanguageFromParams = async () => { |
| | | // Check URL parameters for language override |
| | | const urlParams = new URLSearchParams(window.location.search) |
| | | const localeParam = urlParams.get('locale') |
| | | |
| | | // Check for encoded system variables |
| | | const systemVariables = await getProcessedSystemVariablesFromUrlParams() |
| | | const localeFromSysVar = systemVariables.locale |
| | | |
| | | if (localeParam) { |
| | | // If locale parameter exists in URL, use it instead of default |
| | | changeLanguage(localeParam) |
| | | } |
| | | else if (localeFromSysVar) { |
| | | // If locale is set as a system variable, use that |
| | | changeLanguage(localeFromSysVar) |
| | | } |
| | | else if (appInfo?.site.default_language) { |
| | | // Otherwise use the default from app config |
| | | changeLanguage(appInfo.site.default_language) |
| | | } |
| | | } |
| | | |
| | | setLanguageFromParams() |
| | | if (appInfo?.site.default_language) |
| | | changeLanguage(appInfo.site.default_language) |
| | | }, [appInfo]) |
| | | |
| | | const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, { |
| | | const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, { |
| | | defaultValue: {}, |
| | | }) |
| | | const allowResetChat = !conversationId |
| | | const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || conversationId || '', |
| | | [appId, conversationIdInfo, userId, conversationId]) |
| | | const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo]) |
| | | const handleConversationIdInfoChange = useCallback((changeConversationId: string) => { |
| | | if (appId) { |
| | | let prevValue = conversationIdInfo?.[appId || ''] |
| | | if (typeof prevValue === 'string') |
| | | prevValue = {} |
| | | setConversationIdInfo({ |
| | | ...conversationIdInfo, |
| | | [appId || '']: { |
| | | ...prevValue, |
| | | [userId || 'DEFAULT']: changeConversationId, |
| | | }, |
| | | [appId || '']: changeConversationId, |
| | | }) |
| | | } |
| | | }, [appId, conversationIdInfo, setConversationIdInfo, userId]) |
| | | }, [appId, conversationIdInfo, setConversationIdInfo]) |
| | | const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = useState(true) |
| | | |
| | | const [newConversationId, setNewConversationId] = useState('') |
| | | const chatShouldReloadKey = useMemo(() => { |
| | |
| | | const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR(['appConversationData', isInstalledApp, appId, false], () => fetchConversations(isInstalledApp, appId, undefined, false, 100)) |
| | | const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey, isInstalledApp, appId)) |
| | | |
| | | const [clearChatList, setClearChatList] = useState(false) |
| | | const [isResponding, setIsResponding] = useState(false) |
| | | const appPrevChatList = useMemo( |
| | | () => (currentConversationId && appChatListData?.data.length) |
| | | ? buildChatItemTree(getFormattedChatList(appChatListData.data)) |
| | | ? getPrevChatList(appChatListData.data) |
| | | : [], |
| | | [appChatListData, currentConversationId], |
| | | ) |
| | |
| | | |
| | | useEffect(() => { |
| | | // init inputs from url params |
| | | (async () => { |
| | | const inputs = await getProcessedInputsFromUrlParams() |
| | | setInitInputs(inputs) |
| | | })() |
| | | setInitInputs(getProcessedInputsFromUrlParams()) |
| | | }, []) |
| | | useEffect(() => { |
| | | const conversationInputs: Record<string, any> = {} |
| | |
| | | return conversationItem |
| | | }, [conversationList, currentConversationId, pinnedConversationList]) |
| | | |
| | | const currentConversationLatestInputs = useMemo(() => { |
| | | if (!currentConversationId || !appChatListData?.data.length) |
| | | return newConversationInputsRef.current || {} |
| | | return appChatListData.data.slice().pop().inputs || {} |
| | | }, [appChatListData, currentConversationId]) |
| | | const [currentConversationInputs, setCurrentConversationInputs] = useState<Record<string, any>>(currentConversationLatestInputs || {}) |
| | | useEffect(() => { |
| | | if (currentConversationItem) |
| | | setCurrentConversationInputs(currentConversationLatestInputs || {}) |
| | | }, [currentConversationItem, currentConversationLatestInputs]) |
| | | |
| | | const { notify } = useToastContext() |
| | | const checkInputsRequired = useCallback((silent?: boolean) => { |
| | | let hasEmptyInput = '' |
| | |
| | | |
| | | return true |
| | | }, [inputsForms, notify, t]) |
| | | const handleStartChat = useCallback((callback?: any) => { |
| | | const handleStartChat = useCallback(() => { |
| | | if (checkInputsRequired()) { |
| | | setShowConfigPanelBeforeChat(false) |
| | | setShowNewConversationItemInList(true) |
| | | callback?.() |
| | | } |
| | | }, [setShowNewConversationItemInList, checkInputsRequired]) |
| | | const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: noop }) |
| | | }, [setShowConfigPanelBeforeChat, setShowNewConversationItemInList, checkInputsRequired]) |
| | | const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: () => { } }) |
| | | const handleChangeConversation = useCallback((conversationId: string) => { |
| | | currentChatInstanceRef.current.handleStop() |
| | | setNewConversationId('') |
| | | handleConversationIdInfoChange(conversationId) |
| | | if (conversationId) |
| | | setClearChatList(false) |
| | | }, [handleConversationIdInfoChange, setClearChatList]) |
| | | const handleNewConversation = useCallback(async () => { |
| | | |
| | | if (conversationId === '' && !checkInputsRequired(true)) |
| | | setShowConfigPanelBeforeChat(true) |
| | | else |
| | | setShowConfigPanelBeforeChat(false) |
| | | }, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired]) |
| | | const handleNewConversation = useCallback(() => { |
| | | currentChatInstanceRef.current.handleStop() |
| | | setShowNewConversationItemInList(true) |
| | | handleChangeConversation('') |
| | | handleNewConversationInputsChange(await getProcessedInputsFromUrlParams()) |
| | | setClearChatList(true) |
| | | }, [handleChangeConversation, setShowNewConversationItemInList, handleNewConversationInputsChange, setClearChatList]) |
| | | setNewConversationId('') |
| | | |
| | | if (showNewConversationItemInList) { |
| | | handleChangeConversation('') |
| | | } |
| | | else if (currentConversationId) { |
| | | handleConversationIdInfoChange('') |
| | | setShowConfigPanelBeforeChat(true) |
| | | setShowNewConversationItemInList(true) |
| | | handleNewConversationInputsChange({}) |
| | | } |
| | | }, [handleChangeConversation, currentConversationId, handleConversationIdInfoChange, setShowConfigPanelBeforeChat, setShowNewConversationItemInList, showNewConversationItemInList, handleNewConversationInputsChange]) |
| | | |
| | | const handleNewConversationCompleted = useCallback((newConversationId: string) => { |
| | | setNewConversationId(newConversationId) |
| | |
| | | appInfoError, |
| | | appInfoLoading, |
| | | isInstalledApp, |
| | | allowResetChat, |
| | | appId, |
| | | currentConversationId, |
| | | currentConversationItem, |
| | |
| | | appPrevChatList, |
| | | pinnedConversationList, |
| | | conversationList, |
| | | showConfigPanelBeforeChat, |
| | | setShowConfigPanelBeforeChat, |
| | | setShowNewConversationItemInList, |
| | | newConversationInputs, |
| | | newConversationInputsRef, |
| | |
| | | chatShouldReloadKey, |
| | | handleFeedback, |
| | | currentChatInstanceRef, |
| | | clearChatList, |
| | | setClearChatList, |
| | | isResponding, |
| | | setIsResponding, |
| | | currentConversationInputs, |
| | | setCurrentConversationInputs, |
| | | } |
| | | } |