From 650156367ccbf197d960ae65460bccff6fa99fa9 Mon Sep 17 00:00:00 2001 From: wb-lh513319 Date: Tue, 13 Aug 2024 15:05:26 +0800 Subject: [PATCH] feat: edit chat_dashboard style logic --- web_new/locales/en/chat.ts | 2 +- web_new/locales/zh/chat.ts | 2 +- web_new/pages/chat/index-new.tsx | 298 +++++++++++++++++ web_new/pages/chat/index.tsx | 313 ++---------------- web_new/pages/construct/app/index.tsx | 15 - web_new/pages/construct/flow/index.tsx | 4 +- .../pages/construct/prompt/[type]/index.tsx | 1 + 7 files changed, 325 insertions(+), 310 deletions(-) create mode 100644 web_new/pages/chat/index-new.tsx diff --git a/web_new/locales/en/chat.ts b/web_new/locales/en/chat.ts index 57a757fe1..6c6171e28 100644 --- a/web_new/locales/en/chat.ts +++ b/web_new/locales/en/chat.ts @@ -15,7 +15,7 @@ export const ChatEn = { copy_success: 'Copy success', copy_failed: 'Copy failed', file_tip: 'File cannot be changed after upload', - assistant: 'DataFun Assistant', + assistant: 'Platform Assistant', // DataFun Assistant model_tip: 'Model selection is not supported for the current application', temperature_tip: 'The current application does not support temperature configuration', extend_tip: 'Extended configuration is not supported for the current application', diff --git a/web_new/locales/zh/chat.ts b/web_new/locales/zh/chat.ts index 450746bdb..97ef1555d 100644 --- a/web_new/locales/zh/chat.ts +++ b/web_new/locales/zh/chat.ts @@ -24,7 +24,7 @@ export const ChatZh: Resources['translation'] = { copy_nothing: '内容复制为空', file_tip: '文件上传后无法更改', chat_online: '在线对话', - assistant: '灵数平台小助手', + assistant: '平台小助手', // 灵数平台小助手 model_tip: '当前应用暂不支持模型选择', temperature_tip: '当前应用暂不支持温度配置', extend_tip: '当前应用暂不支持拓展配置', diff --git a/web_new/pages/chat/index-new.tsx b/web_new/pages/chat/index-new.tsx new file mode 100644 index 000000000..e3faebf42 --- /dev/null +++ b/web_new/pages/chat/index-new.tsx @@ -0,0 +1,298 @@ +import ChatContentContainer from '@/ant-components/chat/ChatContentContainer'; +import ChatDefault from '@/ant-components/chat/content/ChatDefault'; +import ChatInputPanel from '@/ant-components/chat/input/ChatInputPanel'; +import ChatSider from '@/ant-components/chat/sider/ChatSider'; +import { ChatContext } from '@/app/chat-context'; +import { apiInterceptors, getAppInfo, getChatHistory, getDialogueList } from '@/client/api'; +import useChat from '@/hooks/use-chat'; +import { IApp } from '@/types/app'; +import { ChartData, ChatHistoryResponse, IChatDialogueSchema } from '@/types/chat'; +import { getInitMessage } from '@/utils'; +import { useAsyncEffect, useRequest } from 'ahooks'; +import { Flex, Layout, Spin } from 'antd'; +import { useSearchParams } from 'next/navigation'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; + +const { Content } = Layout; + +interface ChatContentProps { + history: ChatHistoryResponse; // 会话记录列表 + replyLoading: boolean; // 对话回复loading + scrollRef: React.RefObject; // 会话内容可滚动dom + canAbort: boolean; // 是否能中断回复 + chartsData: ChartData[]; + agent: string; + currentDialogue: IChatDialogueSchema; // 当前选择的会话 + appInfo: IApp; + temperatureValue: any; + resourceValue: any; + modelValue: string; + setModelValue: React.Dispatch>; + setTemperatureValue: React.Dispatch>; + setResourceValue: React.Dispatch>; + setAppInfo: React.Dispatch>; + setAgent: React.Dispatch>; + setCanAbort: React.Dispatch>; + setReplyLoading: React.Dispatch>; + handleChat: (content: string, data?: Record) => Promise; // 处理会话请求逻辑函数 + refreshDialogList: () => void; + refreshHistory: () => void; + refreshAppInfo: () => void; + setHistory: React.Dispatch>; +} +export const ChatContentContext = createContext({ + history: [], + replyLoading: false, + scrollRef: { current: null }, + canAbort: false, + chartsData: [], + agent: '', + currentDialogue: {} as any, + appInfo: {} as any, + temperatureValue: 0.5, + resourceValue: {}, + modelValue: '', + setModelValue: () => {}, + setResourceValue: () => {}, + setTemperatureValue: () => {}, + setAppInfo: () => {}, + setAgent: () => {}, + setCanAbort: () => {}, + setReplyLoading: () => {}, + refreshDialogList: () => {}, + refreshHistory: () => {}, + refreshAppInfo: () => {}, + setHistory: () => {}, + handleChat: () => Promise.resolve(), +}); + +const Chat: React.FC = () => { + const { model, currentDialogInfo } = useContext(ChatContext); + const { chat, ctrl } = useChat({ app_code: currentDialogInfo.app_code || '' }); + + const searchParams = useSearchParams(); + const chatId = searchParams?.get('id') ?? ''; + const scene = searchParams?.get('scene') ?? ''; + const knowledgeId = searchParams?.get('knowledge_id') ?? ''; + const dbName = searchParams?.get('db_name') ?? ''; + + const scrollRef = useRef(null); + const order = useRef(1); + + const [history, setHistory] = useState([]); + const [chartsData, setChartsData] = useState>(); + const [replyLoading, setReplyLoading] = useState(false); + const [canAbort, setCanAbort] = useState(false); + const [agent, setAgent] = useState(''); + const [appInfo, setAppInfo] = useState({} as IApp); + const [temperatureValue, setTemperatureValue] = useState(); + const [resourceValue, setResourceValue] = useState(); + const [modelValue, setModelValue] = useState(''); + + useEffect(() => { + setTemperatureValue(appInfo?.param_need?.filter((item) => item.type === 'temperature')[0]?.value || 0.5); + setModelValue(appInfo?.param_need?.filter((item) => item.type === 'model')[0]?.value || model); + setResourceValue(Number(knowledgeId) || dbName || appInfo?.param_need?.filter((item) => item.type === 'resource')[0]?.bind_value); + }, [appInfo, dbName, knowledgeId, model]); + + // 是否是默认小助手 + const isChatDefault = useMemo(() => { + return !chatId && !scene; + }, [chatId, scene]); + + // 获取会话列表 + const { + data: dialogueList = [], + refresh: refreshDialogList, + loading: listLoading, + } = useRequest(async () => { + return await apiInterceptors(getDialogueList()); + }); + + // 获取应用详情 + const { run: queryAppInfo, refresh: refreshAppInfo } = useRequest( + async () => + await apiInterceptors( + getAppInfo({ + ...currentDialogInfo, + }), + ), + { + manual: true, + onSuccess: (data) => { + const [, res] = data; + setAppInfo(res || ({} as IApp)); + }, + }, + ); + + // 列表当前活跃对话 + const currentDialogue = useMemo(() => { + const [, list] = dialogueList; + return list?.find((item) => item.conv_uid === chatId) || ({} as IChatDialogueSchema); + }, [chatId, dialogueList]); + + useEffect(() => { + const initMessage = getInitMessage(); + if (currentDialogInfo.chat_scene === scene && !isChatDefault && !(initMessage && initMessage.message)) { + queryAppInfo(); + } + }, [chatId, currentDialogInfo, isChatDefault, queryAppInfo, scene]); + + // 获取会话历史记录 + const { + run: getHistory, + loading: historyLoading, + refresh: refreshHistory, + } = useRequest(async () => await apiInterceptors(getChatHistory(chatId)), { + manual: true, + onSuccess: (data) => { + const [, res] = data; + const viewList = res?.filter((item) => item.role === 'view'); + if (viewList && viewList.length > 0) { + order.current = viewList[viewList.length - 1].order + 1; + } + setHistory(res || []); + }, + }); + + // 会话提问 + const handleChat = useCallback( + (content: string, data?: Record) => { + return new Promise((resolve) => { + const initMessage = getInitMessage(); + const ctrl = new AbortController(); + setReplyLoading(true); + if (history && history.length > 0) { + const viewList = history?.filter((item) => item.role === 'view'); + const humanList = history?.filter((item) => item.role === 'human'); + order.current = (viewList[viewList.length - 1]?.order || humanList[humanList.length - 1]?.order) + 1; + } + const tempHistory: ChatHistoryResponse = [ + ...(initMessage && initMessage.id === chatId ? [] : history), + { role: 'human', context: content, model_name: data?.model_name || modelValue, order: order.current, time_stamp: 0 }, + { role: 'view', context: '', model_name: data?.model_name || modelValue, order: order.current, time_stamp: 0, thinking: true }, + ]; + const index = tempHistory.length - 1; + setHistory([...tempHistory]); + chat({ + data: { + chat_mode: scene, + model_name: modelValue, + user_input: content, + ...data, + }, + ctrl, + chatId, + onMessage: (message) => { + setCanAbort(true); + if (data?.incremental) { + tempHistory[index].context += message; + tempHistory[index].thinking = false; + } else { + tempHistory[index].context = message; + tempHistory[index].thinking = false; + } + setHistory([...tempHistory]); + }, + onDone: () => { + setReplyLoading(false); + setCanAbort(false); + resolve(); + }, + onClose: () => { + setReplyLoading(false); + setCanAbort(false); + resolve(); + }, + onError: (message) => { + setReplyLoading(false); + setCanAbort(false); + tempHistory[index].context = message; + tempHistory[index].thinking = false; + setHistory([...tempHistory]); + resolve(); + }, + }); + }); + }, + [chatId, history, modelValue, chat, scene], + ); + + useAsyncEffect(async () => { + // 如果是默认小助手,不获取历史记录 + if (isChatDefault) { + return; + } + const initMessage = getInitMessage(); + if (initMessage && initMessage.id === chatId) { + return; + } + await getHistory(); + }, [chatId, scene, getHistory]); + + useEffect(() => { + if (isChatDefault) { + order.current = 1; + setHistory([]); + } + }, [isChatDefault]); + + return ( + + + + + + {isChatDefault ? ( + + + + ) : ( + + + + + + + )} + + + + + ); +}; + +export default Chat; diff --git a/web_new/pages/chat/index.tsx b/web_new/pages/chat/index.tsx index e3faebf42..6d7891298 100644 --- a/web_new/pages/chat/index.tsx +++ b/web_new/pages/chat/index.tsx @@ -1,298 +1,27 @@ -import ChatContentContainer from '@/ant-components/chat/ChatContentContainer'; -import ChatDefault from '@/ant-components/chat/content/ChatDefault'; -import ChatInputPanel from '@/ant-components/chat/input/ChatInputPanel'; -import ChatSider from '@/ant-components/chat/sider/ChatSider'; +import React, { useContext, useEffect } from 'react'; +import { useRouter } from 'next/router'; import { ChatContext } from '@/app/chat-context'; -import { apiInterceptors, getAppInfo, getChatHistory, getDialogueList } from '@/client/api'; -import useChat from '@/hooks/use-chat'; -import { IApp } from '@/types/app'; -import { ChartData, ChatHistoryResponse, IChatDialogueSchema } from '@/types/chat'; -import { getInitMessage } from '@/utils'; -import { useAsyncEffect, useRequest } from 'ahooks'; -import { Flex, Layout, Spin } from 'antd'; -import { useSearchParams } from 'next/navigation'; -import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import dynamic from 'next/dynamic'; -const { Content } = Layout; +const DbEditor = dynamic(() => import('@/components/chat/db-editor'), { ssr: false }); +const ChatContainer = dynamic(() => import('@/components/chat/chat-container'), { ssr: false }); -interface ChatContentProps { - history: ChatHistoryResponse; // 会话记录列表 - replyLoading: boolean; // 对话回复loading - scrollRef: React.RefObject; // 会话内容可滚动dom - canAbort: boolean; // 是否能中断回复 - chartsData: ChartData[]; - agent: string; - currentDialogue: IChatDialogueSchema; // 当前选择的会话 - appInfo: IApp; - temperatureValue: any; - resourceValue: any; - modelValue: string; - setModelValue: React.Dispatch>; - setTemperatureValue: React.Dispatch>; - setResourceValue: React.Dispatch>; - setAppInfo: React.Dispatch>; - setAgent: React.Dispatch>; - setCanAbort: React.Dispatch>; - setReplyLoading: React.Dispatch>; - handleChat: (content: string, data?: Record) => Promise; // 处理会话请求逻辑函数 - refreshDialogList: () => void; - refreshHistory: () => void; - refreshAppInfo: () => void; - setHistory: React.Dispatch>; +function Chat() { + const { + query: { id, scene }, + } = useRouter(); + const { isContract, setIsContract, setIsMenuExpand } = useContext(ChatContext); + + useEffect(() => { + // 仅初始化执行,防止dashboard页面无法切换状态 + setIsMenuExpand(scene !== 'chat_dashboard'); + // 路由变了要取消Editor模式,再进来是默认的Preview模式 + if (id && scene) { + setIsContract(false); + } + }, [id, scene]); + + return <>{isContract ? : }; } -export const ChatContentContext = createContext({ - history: [], - replyLoading: false, - scrollRef: { current: null }, - canAbort: false, - chartsData: [], - agent: '', - currentDialogue: {} as any, - appInfo: {} as any, - temperatureValue: 0.5, - resourceValue: {}, - modelValue: '', - setModelValue: () => {}, - setResourceValue: () => {}, - setTemperatureValue: () => {}, - setAppInfo: () => {}, - setAgent: () => {}, - setCanAbort: () => {}, - setReplyLoading: () => {}, - refreshDialogList: () => {}, - refreshHistory: () => {}, - refreshAppInfo: () => {}, - setHistory: () => {}, - handleChat: () => Promise.resolve(), -}); - -const Chat: React.FC = () => { - const { model, currentDialogInfo } = useContext(ChatContext); - const { chat, ctrl } = useChat({ app_code: currentDialogInfo.app_code || '' }); - - const searchParams = useSearchParams(); - const chatId = searchParams?.get('id') ?? ''; - const scene = searchParams?.get('scene') ?? ''; - const knowledgeId = searchParams?.get('knowledge_id') ?? ''; - const dbName = searchParams?.get('db_name') ?? ''; - - const scrollRef = useRef(null); - const order = useRef(1); - - const [history, setHistory] = useState([]); - const [chartsData, setChartsData] = useState>(); - const [replyLoading, setReplyLoading] = useState(false); - const [canAbort, setCanAbort] = useState(false); - const [agent, setAgent] = useState(''); - const [appInfo, setAppInfo] = useState({} as IApp); - const [temperatureValue, setTemperatureValue] = useState(); - const [resourceValue, setResourceValue] = useState(); - const [modelValue, setModelValue] = useState(''); - - useEffect(() => { - setTemperatureValue(appInfo?.param_need?.filter((item) => item.type === 'temperature')[0]?.value || 0.5); - setModelValue(appInfo?.param_need?.filter((item) => item.type === 'model')[0]?.value || model); - setResourceValue(Number(knowledgeId) || dbName || appInfo?.param_need?.filter((item) => item.type === 'resource')[0]?.bind_value); - }, [appInfo, dbName, knowledgeId, model]); - - // 是否是默认小助手 - const isChatDefault = useMemo(() => { - return !chatId && !scene; - }, [chatId, scene]); - - // 获取会话列表 - const { - data: dialogueList = [], - refresh: refreshDialogList, - loading: listLoading, - } = useRequest(async () => { - return await apiInterceptors(getDialogueList()); - }); - - // 获取应用详情 - const { run: queryAppInfo, refresh: refreshAppInfo } = useRequest( - async () => - await apiInterceptors( - getAppInfo({ - ...currentDialogInfo, - }), - ), - { - manual: true, - onSuccess: (data) => { - const [, res] = data; - setAppInfo(res || ({} as IApp)); - }, - }, - ); - - // 列表当前活跃对话 - const currentDialogue = useMemo(() => { - const [, list] = dialogueList; - return list?.find((item) => item.conv_uid === chatId) || ({} as IChatDialogueSchema); - }, [chatId, dialogueList]); - - useEffect(() => { - const initMessage = getInitMessage(); - if (currentDialogInfo.chat_scene === scene && !isChatDefault && !(initMessage && initMessage.message)) { - queryAppInfo(); - } - }, [chatId, currentDialogInfo, isChatDefault, queryAppInfo, scene]); - - // 获取会话历史记录 - const { - run: getHistory, - loading: historyLoading, - refresh: refreshHistory, - } = useRequest(async () => await apiInterceptors(getChatHistory(chatId)), { - manual: true, - onSuccess: (data) => { - const [, res] = data; - const viewList = res?.filter((item) => item.role === 'view'); - if (viewList && viewList.length > 0) { - order.current = viewList[viewList.length - 1].order + 1; - } - setHistory(res || []); - }, - }); - - // 会话提问 - const handleChat = useCallback( - (content: string, data?: Record) => { - return new Promise((resolve) => { - const initMessage = getInitMessage(); - const ctrl = new AbortController(); - setReplyLoading(true); - if (history && history.length > 0) { - const viewList = history?.filter((item) => item.role === 'view'); - const humanList = history?.filter((item) => item.role === 'human'); - order.current = (viewList[viewList.length - 1]?.order || humanList[humanList.length - 1]?.order) + 1; - } - const tempHistory: ChatHistoryResponse = [ - ...(initMessage && initMessage.id === chatId ? [] : history), - { role: 'human', context: content, model_name: data?.model_name || modelValue, order: order.current, time_stamp: 0 }, - { role: 'view', context: '', model_name: data?.model_name || modelValue, order: order.current, time_stamp: 0, thinking: true }, - ]; - const index = tempHistory.length - 1; - setHistory([...tempHistory]); - chat({ - data: { - chat_mode: scene, - model_name: modelValue, - user_input: content, - ...data, - }, - ctrl, - chatId, - onMessage: (message) => { - setCanAbort(true); - if (data?.incremental) { - tempHistory[index].context += message; - tempHistory[index].thinking = false; - } else { - tempHistory[index].context = message; - tempHistory[index].thinking = false; - } - setHistory([...tempHistory]); - }, - onDone: () => { - setReplyLoading(false); - setCanAbort(false); - resolve(); - }, - onClose: () => { - setReplyLoading(false); - setCanAbort(false); - resolve(); - }, - onError: (message) => { - setReplyLoading(false); - setCanAbort(false); - tempHistory[index].context = message; - tempHistory[index].thinking = false; - setHistory([...tempHistory]); - resolve(); - }, - }); - }); - }, - [chatId, history, modelValue, chat, scene], - ); - - useAsyncEffect(async () => { - // 如果是默认小助手,不获取历史记录 - if (isChatDefault) { - return; - } - const initMessage = getInitMessage(); - if (initMessage && initMessage.id === chatId) { - return; - } - await getHistory(); - }, [chatId, scene, getHistory]); - - useEffect(() => { - if (isChatDefault) { - order.current = 1; - setHistory([]); - } - }, [isChatDefault]); - - return ( - - - - - - {isChatDefault ? ( - - - - ) : ( - - - - - - - )} - - - - - ); -}; export default Chat; diff --git a/web_new/pages/construct/app/index.tsx b/web_new/pages/construct/app/index.tsx index 482e45386..b4222ba89 100644 --- a/web_new/pages/construct/app/index.tsx +++ b/web_new/pages/construct/app/index.tsx @@ -381,21 +381,6 @@ export default function AppContent() { ), }, - { - key: 'admin', - label: ( - { - e.stopPropagation(); - setAdminOpen(true); - setCurApp(item); - }} - > - 权限管理 - - ), - }, { key: 'del', label: ( diff --git a/web_new/pages/construct/flow/index.tsx b/web_new/pages/construct/flow/index.tsx index 93e8b49c9..9eb8c77f1 100644 --- a/web_new/pages/construct/flow/index.tsx +++ b/web_new/pages/construct/flow/index.tsx @@ -270,7 +270,9 @@ function Flow() { rightTopHover={false} Tags={
- {flow.source} + {flow.source} + {flow.editable ? 'Editable' : 'Can not Edit'} + {flow.state}
} LeftBottom={ diff --git a/web_new/pages/construct/prompt/[type]/index.tsx b/web_new/pages/construct/prompt/[type]/index.tsx index fd2336cbb..9d035c426 100644 --- a/web_new/pages/construct/prompt/[type]/index.tsx +++ b/web_new/pages/construct/prompt/[type]/index.tsx @@ -257,6 +257,7 @@ const AddOrEditPrompt: React.FC = () => { }, onmessage: (event) => { let message = event.data; + if (!message) return; try { message = JSON.parse(message).vis; } catch (e) {