import { ChatContextType, DateInfoType, EntityInfoType, FilterItemType, MsgDataType, ParseStateEnum, ParseTimeCostType, SimilarQuestionType, } from '../../common/type'; import { useEffect, useState } from 'react'; import { chatExecute, chatParse, queryData, queryEntityInfo, switchEntity } from '../../service'; import { PARSE_ERROR_TIP, PREFIX_CLS, SEARCH_EXCEPTION_TIP } from '../../common/constants'; import IconFont from '../IconFont'; import ParseTip from './ParseTip'; import ExecuteItem from './ExecuteItem'; import { isMobile } from '../../utils/utils'; import classNames from 'classnames'; import Tools from '../Tools'; import SqlItem from './SqlItem'; import SimilarQuestionItem from './SimilarQuestionItem'; import dayjs from 'dayjs'; type Props = { msg: string; conversationId?: number; modelId?: number; agentId?: number; score?: number; filter?: any[]; parseInfos?: ChatContextType[]; parseTimeCostValue?: ParseTimeCostType; msgData?: MsgDataType; triggerResize?: boolean; isDeveloper?: boolean; integrateSystem?: string; executeItemNode?: React.ReactNode; renderCustomExecuteNode?: boolean; isSimpleMode?: boolean; isDebugMode?: boolean; onMsgDataLoaded?: (data: MsgDataType, valid: boolean, isRefresh?: boolean) => void; onUpdateMessageScroll?: () => void; onSendMsg?: (msg: string) => void; }; const ChatItem: React.FC = ({ msg, conversationId, modelId, agentId, score, filter, triggerResize, parseInfos, parseTimeCostValue, msgData, isDeveloper, integrateSystem, executeItemNode, renderCustomExecuteNode, isSimpleMode, isDebugMode, onMsgDataLoaded, onUpdateMessageScroll, onSendMsg, }) => { const [parseLoading, setParseLoading] = useState(false); const [parseTimeCost, setParseTimeCost] = useState(); const [parseInfo, setParseInfo] = useState(); const [parseInfoOptions, setParseInfoOptions] = useState([]); const [parseTip, setParseTip] = useState(''); const [executeMode, setExecuteMode] = useState(false); const [executeLoading, setExecuteLoading] = useState(false); const [executeTip, setExecuteTip] = useState(''); const [data, setData] = useState(); const [entitySwitchLoading, setEntitySwitchLoading] = useState(false); const [dimensionFilters, setDimensionFilters] = useState([]); const [dateInfo, setDateInfo] = useState({} as DateInfoType); const [entityInfo, setEntityInfo] = useState({} as EntityInfoType); const [dataCache, setDataCache] = useState>( {} ); const prefixCls = `${PREFIX_CLS}-item`; const updateData = (res: Result) => { let tip: string = ''; let data: MsgDataType | undefined = undefined; const { queryColumns, queryResults, queryState, queryMode, response, chatContext, textResult } = res.data || {}; if (res.code === 400 || res.code === 401 || res.code === 412) { tip = res.msg; } else if (res.code !== 200) { tip = SEARCH_EXCEPTION_TIP; } else if (queryState !== 'SUCCESS') { tip = response && typeof response === 'string' ? response : SEARCH_EXCEPTION_TIP; } else if ( (queryColumns && queryColumns.length > 0 && queryResults) || queryMode === 'WEB_PAGE' || queryMode === 'PLAIN_TEXT' ) { data = res.data; tip = ''; } if (chatContext) { setDataCache({ ...dataCache, [chatContext!.id!]: { tip, data } }); } if (data) { setData(data); setExecuteTip(''); return true; } setExecuteTip(tip || SEARCH_EXCEPTION_TIP); return false; }; const onExecute = async ( parseInfoValue: ChatContextType, parseInfos?: ChatContextType[], isSwitchParseInfo?: boolean ) => { setExecuteMode(true); if (isSwitchParseInfo) { setEntitySwitchLoading(true); } else { setExecuteLoading(true); } try { const res: any = await chatExecute(msg, conversationId!, parseInfoValue, agentId); const valid = updateData(res); onMsgDataLoaded?.( { ...res.data, parseInfos, queryId: parseInfoValue.queryId, }, valid ); } catch (e) { const tip = SEARCH_EXCEPTION_TIP; setExecuteTip(SEARCH_EXCEPTION_TIP); setDataCache({ ...dataCache, [parseInfoValue!.id!]: { tip } }); } if (isSwitchParseInfo) { setEntitySwitchLoading(false); } else { setExecuteLoading(false); } }; const updateDimensionFitlers = (filters: FilterItemType[]) => { setDimensionFilters( filters.sort((a, b) => { if (a.name < b.name) { return -1; } if (a.name > b.name) { return 1; } return 0; }) ); }; const sendMsg = async () => { setParseLoading(true); const parseData: any = await chatParse(msg, conversationId, modelId, agentId, filter); setParseLoading(false); const { code, data } = parseData || {}; const { state, selectedParses, candidateParses, queryId, parseTimeCost } = data || {}; const parses = selectedParses?.concat(candidateParses || []) || []; if ( code !== 200 || state === ParseStateEnum.FAILED || !parses.length || (!parses[0]?.properties?.type && !parses[0]?.queryMode) ) { setParseTip(PARSE_ERROR_TIP); setParseInfo({ queryId } as any); return; } onUpdateMessageScroll?.(); const parseInfos = parses.slice(0, 5).map((item: any) => ({ ...item, queryId, })); setParseInfoOptions(parseInfos || []); const parseInfoValue = parseInfos[0]; setParseInfo(parseInfoValue); setParseTimeCost(parseTimeCost); setEntityInfo(parseInfoValue.entityInfo || {}); updateDimensionFitlers(parseInfoValue?.dimensionFilters || []); setDateInfo(parseInfoValue?.dateInfo); onExecute(parseInfoValue, parseInfos); }; useEffect(() => { if (data !== undefined || executeTip !== '' || parseLoading) { return; } if (msgData) { const parseInfoOptionsValue = parseInfos && parseInfos.length > 0 ? parseInfos.map(item => ({ ...item, queryId: msgData.queryId })) : [{ ...msgData.chatContext, queryId: msgData.queryId }]; const parseInfoValue = parseInfoOptionsValue[0]; setParseInfoOptions(parseInfoOptionsValue); setParseInfo(parseInfoValue); setParseTimeCost(parseTimeCostValue); updateDimensionFitlers(parseInfoValue.dimensionFilters || []); setDateInfo(parseInfoValue.dateInfo); setExecuteMode(true); updateData({ code: 200, data: msgData, msg: 'success' }); } else if (msg) { sendMsg(); } }, [msg, msgData]); const onSwitchEntity = async (entityId: string) => { setEntitySwitchLoading(true); const res = await switchEntity(entityId, data?.chatContext?.modelId, conversationId || 0); setEntitySwitchLoading(false); setData(res.data); const { chatContext, entityInfo } = res.data || {}; const chatContextValue = { ...(chatContext || {}), queryId: parseInfo?.queryId }; setParseInfo(chatContextValue); setEntityInfo(entityInfo); updateDimensionFitlers(chatContextValue?.dimensionFilters || []); setDateInfo(chatContextValue?.dateInfo); setDataCache({ ...dataCache, [chatContextValue.id!]: { tip: '', data: res.data } }); }; const onFiltersChange = (dimensionFilters: FilterItemType[]) => { setDimensionFilters(dimensionFilters); }; const onDateInfoChange = (dateRange: any) => { setDateInfo({ ...(dateInfo || {}), startDate: dayjs(dateRange[0]).format('YYYY-MM-DD'), endDate: dayjs(dateRange[1]).format('YYYY-MM-DD'), dateMode: 'BETWEEN', unit: 0, }); }; const onRefresh = async () => { setEntitySwitchLoading(true); const { dimensions, metrics, id, queryId } = parseInfo || {}; const chatContextValue = { dimensions, metrics, dateInfo, dimensionFilters, parseId: id, queryId, }; const res: any = await queryData(chatContextValue); setEntitySwitchLoading(false); if (res.code === 200) { const resChatContext = res.data?.chatContext; const contextValue = { ...(resChatContext || chatContextValue), queryId }; const dataValue = { ...res.data, chatContext: contextValue, parseInfos: parseInfoOptions, queryId, }; onMsgDataLoaded?.(dataValue, true, true); setData(dataValue); setParseInfo(contextValue); setDataCache({ ...dataCache, [id!]: { tip: '', data: dataValue } }); } }; const getEntityInfo = async (parseInfoValue: ChatContextType) => { const res = await queryEntityInfo(parseInfoValue.queryId, parseInfoValue.id); setEntityInfo(res.data); }; const onSelectParseInfo = async (parseInfoValue: ChatContextType) => { setParseInfo(parseInfoValue); updateDimensionFitlers(parseInfoValue.dimensionFilters || []); setDateInfo(parseInfoValue.dateInfo); if (parseInfoValue.entityInfo) { setEntityInfo(parseInfoValue.entityInfo); } else { getEntityInfo(parseInfoValue); } if (dataCache[parseInfoValue.id!]) { const { tip, data } = dataCache[parseInfoValue.id!]; setExecuteTip(tip); setData(data); onMsgDataLoaded?.( { ...(data as any), parseInfos, queryId: parseInfoValue.queryId, }, true, true ); } else { onExecute(parseInfoValue, parseInfoOptions, true); } }; const onSelectQuestion = (question: SimilarQuestionType) => { onSendMsg?.(question.queryText); }; const contentClass = classNames(`${prefixCls}-content`, { [`${prefixCls}-content-mobile`]: isMobile, }); const { llmReq, llmResp } = parseInfo?.properties?.CONTEXT || {}; return (
{!isMobile && integrateSystem !== 'wiki' && ( )}
{executeMode && ( <> {!isMobile && parseInfo?.sqlInfo && isDeveloper && isDebugMode && !isSimpleMode && ( )} )} {(parseTip !== '' || (executeMode && !executeLoading)) && !isSimpleMode && parseInfo?.queryMode !== 'PLAIN_TEXT' && ( )}
{(parseTip !== '' || (executeMode && !executeLoading)) && parseInfo?.queryMode !== 'PLAIN_TEXT' && ( )}
); }; export default ChatItem;