[feature](webapp) add copilot and modify domain to model

This commit is contained in:
williamhliu
2023-08-15 20:04:04 +08:00
parent 2732d8fee1
commit 86b93876c7
43 changed files with 738 additions and 431 deletions

View File

@@ -9,21 +9,21 @@ import { searchRecommend } from 'supersonic-chat-sdk';
import { SemanticTypeEnum, SEMANTIC_TYPE_MAP } from '../constants';
import styles from './style.less';
import { PLACE_HOLDER } from '../constants';
import { DefaultEntityType, DomainType } from '../type';
import { DefaultEntityType, ModelType } from '../type';
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
type Props = {
inputMsg: string;
chatId?: number;
currentDomain?: DomainType;
currentModel?: ModelType;
defaultEntity?: DefaultEntityType;
isCopilotMode?: boolean;
copilotFullscreen?: boolean;
domains: DomainType[];
models: ModelType[];
collapsed: boolean;
onToggleCollapseBtn: () => void;
onInputMsgChange: (value: string) => void;
onSendMsg: (msg: string, domainId?: number) => void;
onSendMsg: (msg: string, modelId?: number) => void;
onAddConversation: () => void;
onCancelDefaultFilter: () => void;
};
@@ -44,9 +44,9 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
{
inputMsg,
chatId,
currentDomain,
currentModel,
defaultEntity,
domains,
models,
collapsed,
isCopilotMode,
copilotFullscreen,
@@ -58,7 +58,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
},
ref,
) => {
const [domainOptions, setDomainOptions] = useState<DomainType[]>([]);
const [modelOptions, setModelOptions] = useState<ModelType[]>([]);
const [stepOptions, setStepOptions] = useState<Record<string, any[]>>({});
const [open, setOpen] = useState(false);
const [focused, setFocused] = useState(false);
@@ -100,7 +100,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
}, []);
const getStepOptions = (recommends: any[]) => {
const data = groupByColumn(recommends, 'domainName');
const data = groupByColumn(recommends, 'modelName');
return isMobile && recommends.length > 6
? Object.keys(data)
.slice(0, 4)
@@ -114,23 +114,23 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
: data;
};
const processMsg = (msg: string, domains: DomainType[]) => {
const processMsg = (msg: string, models: ModelType[]) => {
let msgValue = msg;
let domainId: number | undefined;
let modelId: number | undefined;
if (msg?.[0] === '@') {
const domain = domains.find((item) => msg.includes(`@${item.name}`));
msgValue = domain ? msg.replace(`@${domain.name}`, '') : msg;
domainId = domain?.id;
const model = models.find((item) => msg.includes(`@${item.name}`));
msgValue = model ? msg.replace(`@${model.name}`, '') : msg;
modelId = model?.id;
}
return { msgValue, domainId };
return { msgValue, modelId };
};
const debounceGetWordsFunc = useCallback(() => {
const getAssociateWords = async (
msg: string,
domains: DomainType[],
models: ModelType[],
chatId?: number,
domain?: DomainType,
model?: ModelType,
) => {
if (isPinyin) {
return;
@@ -140,9 +140,9 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
}
fetchRef.current += 1;
const fetchId = fetchRef.current;
const { msgValue, domainId } = processMsg(msg, domains);
const domainIdValue = domainId || domain?.id;
const res = await searchRecommend(msgValue.trim(), chatId, domainIdValue);
const { msgValue, modelId } = processMsg(msg, models);
const modelIdValue = modelId || model?.id;
const res = await searchRecommend(msgValue.trim(), chatId, modelIdValue);
if (fetchId !== fetchRef.current) {
return;
}
@@ -165,19 +165,19 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
useEffect(() => {
if (inputMsg.length === 1 && inputMsg[0] === '@') {
setOpen(true);
setDomainOptions(domains);
setModelOptions(models);
setStepOptions({});
return;
} else {
setOpen(false);
if (domainOptions.length > 0) {
if (modelOptions.length > 0) {
setTimeout(() => {
setDomainOptions([]);
setModelOptions([]);
}, 500);
}
}
if (!isSelect) {
debounceGetWords(inputMsg, domains, chatId, currentDomain);
debounceGetWords(inputMsg, models, chatId, currentModel);
} else {
isSelect = false;
}
@@ -219,10 +219,10 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
.find((item) =>
Object.keys(stepOptions).length === 1
? item.recommend === value
: `${item.domainName || ''}${item.recommend}` === value,
: `${item.modelName || ''}${item.recommend}` === value,
);
if (option && isSelect) {
onSendMsg(option.recommend, option.domainId);
onSendMsg(option.recommend, option.modelId);
} else {
onSendMsg(value.trim());
}
@@ -230,12 +230,12 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
const autoCompleteDropdownClass = classNames(styles.autoCompleteDropdown, {
[styles.mobile]: isMobile,
[styles.domainOptions]: domainOptions.length > 0,
[styles.modelOptions]: modelOptions.length > 0,
});
const onSelect = (value: string) => {
isSelect = true;
if (domainOptions.length === 0) {
if (modelOptions.length === 0) {
sendMsg(value);
}
setOpen(false);
@@ -263,19 +263,16 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
/>
</Tooltip>
<div className={styles.composerInputWrapper}>
{currentDomain && (
<div className={styles.currentDomain}>
<div className={styles.currentDomainName}>
{currentModel && (
<div className={styles.currentModel}>
<div className={styles.currentModelName}>
<span className={styles.quoteText}>
{currentDomain.name}
{currentModel.name}
{defaultEntity && (
<>
<span></span>
<span>{`${currentDomain.name.slice(
0,
currentDomain.name.length - 1,
)}`}</span>
<span>{`${currentModel.name.slice(0, currentModel.name.length - 1)}`}</span>
<span className={styles.entityName} title={defaultEntity.entityName}>
{defaultEntity.entityName}
</span>
@@ -285,7 +282,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
</span>
</div>
<div className={styles.cancelDomain} onClick={onCancelDefaultFilter}>
<div className={styles.cancelModel} onClick={onCancelDefaultFilter}>
</div>
</div>
@@ -293,8 +290,8 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
<AutoComplete
className={styles.composerInput}
placeholder={
currentDomain
? `请输入【${currentDomain.name}】主题的问题,可使用@切换到其他主题`
currentModel
? `请输入【${currentModel.name}】主题的问题,可使用@切换到其他主题`
: PLACE_HOLDER
}
value={inputMsg}
@@ -323,15 +320,15 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
open={open}
getPopupContainer={(triggerNode) => triggerNode.parentNode}
>
{domainOptions.length > 0
? domainOptions.map((domain) => {
{modelOptions.length > 0
? modelOptions.map((model) => {
return (
<Option
key={domain.id}
value={`@${domain.name} `}
key={model.id}
value={`@${model.name} `}
className={styles.searchOption}
>
{domain.name}
{model.name}
</Option>
);
})
@@ -342,17 +339,15 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
let optionValue =
Object.keys(stepOptions).length === 1
? option.recommend
: `${option.domainName || ''}${option.recommend}`;
: `${option.modelName || ''}${option.recommend}`;
if (inputMsg[0] === '@') {
const domain = domains.find((item) => inputMsg.includes(item.name));
optionValue = domain
? `@${domain.name} ${option.recommend}`
: optionValue;
const model = models.find((item) => inputMsg.includes(item.name));
optionValue = model ? `@${model.name} ${option.recommend}` : optionValue;
}
return (
<Option
key={`${option.recommend}${
option.domainName ? `_${option.domainName}` : ''
option.modelName ? `_${option.modelName}` : ''
}`}
value={optionValue}
className={styles.searchOption}
@@ -363,7 +358,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
className={styles.semanticType}
color={
option.schemaElementType === SemanticTypeEnum.DIMENSION ||
option.schemaElementType === SemanticTypeEnum.DOMAIN
option.schemaElementType === SemanticTypeEnum.MODEL
? 'blue'
: option.schemaElementType === SemanticTypeEnum.VALUE
? 'geekblue'

View File

@@ -45,7 +45,7 @@
position: relative;
flex: 1;
.currentDomain {
.currentModel {
position: absolute;
top: -30px;
left: 15px;
@@ -61,7 +61,7 @@
border-top-left-radius: 6px;
border-top-right-radius: 6px;
.currentDomainName {
.currentModelName {
margin-right: 12px;
font-size: 14px;
@@ -75,7 +75,7 @@
}
}
.cancelDomain {
.cancelModel {
padding: 0 6px;
font-size: 13px;
border: 1px solid var(--text-color-fourth);
@@ -206,7 +206,7 @@
}
}
.domain {
.model {
margin-top: 2px;
color: var(--text-color-fourth);
font-size: 13px;
@@ -219,7 +219,7 @@
min-width: 100px !important;
border-radius: 6px;
&.domainOptions {
&.modelOptions {
width: 150px !important;
.searchOption {

View File

@@ -21,14 +21,14 @@ type Props = {
currentConversation?: ConversationDetailType;
collapsed?: boolean;
isCopilotMode?: boolean;
defaultDomainName?: string;
defaultModelName?: string;
defaultEntityFilter?: DefaultEntityType;
triggerNewConversation?: boolean;
onNewConversationTriggered?: () => void;
onSelectConversation: (
conversation: ConversationDetailType,
name?: string,
domainId?: number,
modelId?: number,
entityId?: string,
) => void;
};
@@ -38,7 +38,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
currentConversation,
collapsed,
isCopilotMode,
defaultDomainName,
defaultModelName,
defaultEntityFilter,
triggerNewConversation,
onNewConversationTriggered,
@@ -47,7 +47,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
ref,
) => {
const location = useLocation();
const { q, cid, domainId, entityId } = (location as any).query;
const { q, cid, modelId, entityId } = (location as any).query;
const [conversations, setConversations] = useState<ConversationDetailType[]>([]);
const [editModalVisible, setEditModalVisible] = useState(false);
const [editConversation, setEditConversation] = useState<ConversationDetailType>();
@@ -89,7 +89,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
const conversationName =
defaultEntityFilter?.entityName && window.location.pathname.includes('detail')
? defaultEntityFilter.entityName
: defaultDomainName;
: defaultModelName;
onAddConversation({ name: conversationName, type: 'CUSTOMIZE' });
onNewConversationTriggered?.();
}
@@ -100,7 +100,7 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
return;
}
if (q && cid === undefined && window.location.href.includes('/workbench/chat')) {
onAddConversation({ name: q, domainId: domainId ? +domainId : undefined, entityId });
onAddConversation({ name: q, modelId: modelId ? +modelId : undefined, entityId });
} else {
initData();
}
@@ -118,17 +118,17 @@ const Conversation: ForwardRefRenderFunction<any, Props> = (
const onAddConversation = async ({
name,
domainId,
modelId,
entityId,
type,
}: {
name?: string;
domainId?: number;
modelId?: number;
entityId?: string;
type?: string;
} = {}) => {
const data = await addConversation(name);
onSelectConversation(data[0], type || name, domainId, entityId);
onSelectConversation(data[0], type || name, modelId, entityId);
};
const onOperate = (key: string, conversation: ConversationDetailType) => {

View File

@@ -23,7 +23,7 @@ type Props = {
valid: boolean,
) => void;
onCheckMore: (data: MsgDataType) => void;
onApplyAuth: (domain: string) => void;
onApplyAuth: (model: string) => void;
};
const MessageContainer: React.FC<Props> = ({
@@ -71,15 +71,15 @@ const MessageContainer: React.FC<Props> = ({
for (let i = 0; i < msgs.length; i++) {
const msg = msgs[i];
const msgDomainId = msg.msgData?.chatContext?.domainId;
const msgModelId = msg.msgData?.chatContext?.modelId;
const msgEntityId = msg.msgData?.entityInfo?.entityId;
const currentMsgDomainId = currentMsgData?.chatContext?.domainId;
const currentMsgModelId = currentMsgData?.chatContext?.modelId;
const currentMsgEntityId = currentMsgData?.entityInfo?.entityId;
if (
(msg.type === MessageTypeEnum.QUESTION || msg.type === MessageTypeEnum.PLUGIN) &&
!!currentMsgDomainId &&
msgDomainId === currentMsgDomainId &&
!!currentMsgModelId &&
msgModelId === currentMsgModelId &&
msgEntityId === currentMsgEntityId &&
msg.msg
) {
@@ -91,8 +91,8 @@ const MessageContainer: React.FC<Props> = ({
return followQuestions;
};
const getFilters = (domainId?: number, entityId?: string) => {
if (!domainId || !entityId) {
const getFilters = (modelId?: number, entityId?: string) => {
if (!modelId || !entityId) {
return undefined;
}
return [
@@ -108,7 +108,7 @@ const MessageContainer: React.FC<Props> = ({
{messageList.map((msgItem: MessageItem, index: number) => {
const {
id: msgId,
domainId,
modelId,
entityId,
type,
msg,
@@ -117,6 +117,7 @@ const MessageContainer: React.FC<Props> = ({
msgData,
score,
isHistory,
parseOptions,
} = msgItem;
const followQuestions = getFollowQuestions(index);
@@ -132,8 +133,8 @@ const MessageContainer: React.FC<Props> = ({
msg={msgValue || msg || ''}
msgData={msgData}
conversationId={chatId}
domainId={domainId}
filter={getFilters(domainId, entityId)}
modelId={modelId}
filter={getFilters(modelId, entityId)}
isLastMessage={index === messageList.length - 1}
isMobileMode={isMobileMode}
triggerResize={triggerResize}
@@ -144,6 +145,22 @@ const MessageContainer: React.FC<Props> = ({
/>
</>
)}
{type === MessageTypeEnum.PARSE_OPTIONS && (
<ChatItem
msg={msgValue || msg || ''}
conversationId={chatId}
modelId={modelId}
filter={getFilters(modelId, entityId)}
isLastMessage={index === messageList.length - 1}
isMobileMode={isMobileMode}
triggerResize={triggerResize}
parseOptions={parseOptions}
onMsgDataLoaded={(data: MsgDataType, valid: boolean) => {
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid);
}}
onUpdateMessageScroll={updateMessageContainerScroll}
/>
)}
{type === MessageTypeEnum.PLUGIN && (
<>
<Plugin

View File

@@ -6,42 +6,16 @@ type Props = {
width?: number | string;
height?: number | string;
bubbleClassName?: string;
domainName?: string;
question?: string;
followQuestions?: string[];
};
const Message: React.FC<Props> = ({
position,
width,
height,
children,
bubbleClassName,
domainName,
question,
followQuestions,
}) => {
const Message: React.FC<Props> = ({ position, width, height, children, bubbleClassName }) => {
const messageClass = classNames(styles.message, {
[styles.left]: position === 'left',
[styles.right]: position === 'right',
});
const leftTitle = question
? followQuestions && followQuestions.length > 0
? `多轮对话:${[question, ...followQuestions].join(' ← ')}`
: `单轮对话:${question}`
: '';
return (
<div className={messageClass} style={{ width }}>
{/* <div className={styles.messageTitleBar}>
{!!domainName && <div className={styles.domainName}>{domainName}</div>}
{position === 'left' && leftTitle && (
<div className={styles.messageTopBar} title={leftTitle}>
({leftTitle})
</div>
)}
</div> */}
<div className={styles.messageContent}>
<div className={styles.messageBody}>
<div
@@ -51,11 +25,6 @@ const Message: React.FC<Props> = ({
e.stopPropagation();
}}
>
{/* {position === 'left' && question && (
<div className={styles.messageTopBar} title={leftTitle}>
{leftTitle}
</div>
)} */}
{children}
</div>
</div>

View File

@@ -161,15 +161,7 @@ const Plugin: React.FC<Props> = ({
<div className={reportClass}>
<LeftAvatar />
<div className={styles.msgContent}>
<Message
position="left"
width="100%"
height={height}
bubbleClassName={styles.reportBubble}
domainName={data.chatContext?.domainName}
question={msg}
followQuestions={followQuestions}
>
<Message position="left" width="100%" height={height} bubbleClassName={styles.reportBubble}>
<iframe
id={`reportIframe_${id}`}
src={pluginUrl}

View File

@@ -5,7 +5,7 @@
margin-bottom: 6px;
column-gap: 10px;
.domainName {
.modelName {
margin-left: 4px;
color: var(--text-color);
font-weight: 500;

View File

@@ -14,14 +14,14 @@ export const THEME_COLOR_LIST = [
];
export enum SemanticTypeEnum {
DOMAIN = 'DOMAIN',
MODEL = 'MODEL',
DIMENSION = 'DIMENSION',
METRIC = 'METRIC',
VALUE = 'VALUE',
}
export const SEMANTIC_TYPE_MAP = {
[SemanticTypeEnum.DOMAIN]: '主题域',
[SemanticTypeEnum.MODEL]: '主题域',
[SemanticTypeEnum.DIMENSION]: '维度',
[SemanticTypeEnum.METRIC]: '指标',
[SemanticTypeEnum.VALUE]: '维度值',

View File

@@ -6,11 +6,11 @@ import styles from './style.less';
import {
ConversationDetailType,
DefaultEntityType,
DomainType,
ModelType,
MessageItem,
MessageTypeEnum,
} from './type';
import { getDomainList } from './service';
import { getModelList } from './service';
import { useThrottleFn } from 'ahooks';
import Conversation from './Conversation';
import ChatFooter from './ChatFooter';
@@ -25,12 +25,12 @@ import { AUTH_TOKEN_KEY } from '@/common/constants';
type Props = {
isCopilotMode?: boolean;
copilotFullscreen?: boolean;
defaultDomainName?: string;
defaultModelName?: string;
defaultEntityFilter?: DefaultEntityType;
copilotSendMsg?: string;
triggerNewConversation?: boolean;
onNewConversationTriggered?: () => void;
onCurrentDomainChange?: (domain?: DomainType) => void;
onCurrentModelChange?: (model?: ModelType) => void;
onCancelCopilotFilter?: () => void;
onCheckMoreDetail?: () => void;
};
@@ -38,17 +38,16 @@ type Props = {
const Chat: React.FC<Props> = ({
isCopilotMode,
copilotFullscreen,
defaultDomainName,
defaultModelName,
defaultEntityFilter,
copilotSendMsg,
triggerNewConversation,
onNewConversationTriggered,
onCurrentDomainChange,
onCurrentModelChange,
onCancelCopilotFilter,
onCheckMoreDetail,
}) => {
const isMobileMode = isMobile || isCopilotMode;
const localConversationCollapsed = localStorage.getItem('CONVERSATION_COLLAPSED');
const [messageList, setMessageList] = useState<MessageItem[]>([]);
const [inputMsg, setInputMsg] = useState('');
@@ -58,54 +57,52 @@ const Chat: React.FC<Props> = ({
const [currentConversation, setCurrentConversation] = useState<
ConversationDetailType | undefined
>(isMobile ? { chatId: 0, chatName: `${CHAT_TITLE}问答` } : undefined);
const [conversationCollapsed, setConversationCollapsed] = useState(
!localConversationCollapsed ? true : localConversationCollapsed === 'true',
);
const [domains, setDomains] = useState<DomainType[]>([]);
const [currentDomain, setCurrentDomain] = useState<DomainType>();
const [conversationCollapsed, setConversationCollapsed] = useState(isCopilotMode);
const [models, setModels] = useState<ModelType[]>([]);
const [currentModel, setCurrentModel] = useState<ModelType>();
const [defaultEntity, setDefaultEntity] = useState<DefaultEntityType>();
const [applyAuthVisible, setApplyAuthVisible] = useState(false);
const [applyAuthDomain, setApplyAuthDomain] = useState('');
const [initialDomainName, setInitialDomainName] = useState('');
const [applyAuthModel, setApplyAuthModel] = useState('');
const [initialModelName, setInitialModelName] = useState('');
const location = useLocation();
const dispatch = useDispatch();
const { domainName } = (location as any).query;
const { modelName } = (location as any).query;
const conversationRef = useRef<any>();
const chatFooterRef = useRef<any>();
useEffect(() => {
setChatSdkToken(localStorage.getItem(AUTH_TOKEN_KEY) || '');
initDomains();
initModels();
}, []);
useEffect(() => {
if (domains.length > 0 && initialDomainName && !currentDomain) {
changeDomain(domains.find((domain) => domain.name === initialDomainName));
if (models.length > 0 && initialModelName && !currentModel) {
changeModel(models.find((model) => model.name === initialModelName));
}
}, [domains]);
}, [models]);
useEffect(() => {
if (domainName) {
setInitialDomainName(domainName);
if (modelName) {
setInitialModelName(modelName);
}
}, [domainName]);
}, [modelName]);
useEffect(() => {
if (defaultDomainName !== undefined && domains.length > 0) {
changeDomain(domains.find((domain) => domain.name === defaultDomainName));
if (defaultModelName !== undefined && models.length > 0) {
changeModel(models.find((model) => model.name === defaultModelName));
}
}, [defaultDomainName]);
}, [defaultModelName]);
useEffect(() => {
if (!currentConversation) {
return;
}
const { initMsg, domainId, entityId } = currentConversation;
const { initMsg, modelId, entityId } = currentConversation;
if (initMsg) {
inputFocus();
if (initMsg === 'CUSTOMIZE' && copilotSendMsg) {
onSendMsg(copilotSendMsg, [], domainId, entityId);
onSendMsg(copilotSendMsg, [], modelId, entityId);
dispatch({
type: 'globalState/setCopilotSendMsg',
payload: '',
@@ -116,7 +113,7 @@ const Chat: React.FC<Props> = ({
sendHelloRsp();
return;
}
onSendMsg(initMsg, [], domainId, entityId);
onSendMsg(initMsg, [], modelId, entityId);
return;
}
updateHistoryMsg(1);
@@ -147,13 +144,13 @@ const Chat: React.FC<Props> = ({
{
id: uuid(),
type: MessageTypeEnum.TEXT,
msg: defaultDomainName
msg: defaultModelName
? `您好,请输入关于${
defaultEntityFilter?.entityName
? `${defaultDomainName?.slice(0, defaultDomainName?.length - 1)}${
? `${defaultModelName?.slice(0, defaultModelName?.length - 1)}${
defaultEntityFilter?.entityName
}`
: `${defaultDomainName}`
: `${defaultModelName}`
}的问题`
: '您好,请问有什么我能帮您吗?',
},
@@ -208,19 +205,19 @@ const Chat: React.FC<Props> = ({
},
);
const changeDomain = (domain?: DomainType) => {
setCurrentDomain(domain);
if (onCurrentDomainChange) {
onCurrentDomainChange(domain);
const changeModel = (model?: ModelType) => {
setCurrentModel(model);
if (onCurrentModelChange) {
onCurrentModelChange(model);
}
};
const initDomains = async () => {
const res = await getDomainList();
const domainList = getLeafList(res.data);
setDomains([{ id: -1, name: '全部', bizName: 'all', parentId: 0 }, ...domainList].slice(0, 11));
if (defaultDomainName !== undefined) {
changeDomain(domainList.find((domain) => domain.name === defaultDomainName));
const initModels = async () => {
const res = await getModelList();
const modelList = getLeafList(res.data);
setModels([{ id: -1, name: '全部', bizName: 'all', parentId: 0 }, ...modelList].slice(0, 11));
if (defaultModelName !== undefined) {
changeModel(modelList.find((model) => model.name === defaultModelName));
}
};
@@ -237,7 +234,7 @@ const Chat: React.FC<Props> = ({
const onSendMsg = async (
msg?: string,
list?: MessageItem[],
domainId?: number,
modelId?: number,
entityId?: string,
) => {
const currentMsg = msg || inputMsg;
@@ -245,25 +242,25 @@ const Chat: React.FC<Props> = ({
setInputMsg('');
return;
}
const msgDomain = domains.find((item) => currentMsg.includes(item.name));
const certainDomain = currentMsg[0] === '@' && msgDomain;
let domainChanged = false;
const msgModel = models.find((item) => currentMsg.includes(item.name));
const certainModel = currentMsg[0] === '@' && msgModel;
let modelChanged = false;
if (certainDomain) {
const toDomain = msgDomain.id === -1 ? undefined : msgDomain;
changeDomain(toDomain);
domainChanged = currentDomain?.id !== toDomain?.id;
if (certainModel) {
const toModel = msgModel.id === -1 ? undefined : msgModel;
changeModel(toModel);
modelChanged = currentModel?.id !== toModel?.id;
}
const domainIdValue = domainId || msgDomain?.id || currentDomain?.id;
const modelIdValue = modelId || msgModel?.id || currentModel?.id;
const msgs = [
...(list || messageList),
{
id: uuid(),
msg: currentMsg,
msgValue: certainDomain ? currentMsg.replace(`@${msgDomain.name}`, '').trim() : currentMsg,
domainId: domainIdValue === -1 ? undefined : domainIdValue,
entityId: entityId || (domainChanged ? undefined : defaultEntity?.entityId),
identityMsg: certainDomain ? getIdentityMsgText(msgDomain) : undefined,
msgValue: certainModel ? currentMsg.replace(`@${msgModel.name}`, '').trim() : currentMsg,
modelId: modelIdValue === -1 ? undefined : modelIdValue,
entityId: entityId || (modelChanged ? undefined : defaultEntity?.entityId),
identityMsg: certainModel ? getIdentityMsgText(msgModel) : undefined,
type: MessageTypeEnum.QUESTION,
},
];
@@ -290,7 +287,7 @@ const Chat: React.FC<Props> = ({
const onSelectConversation = (
conversation: ConversationDetailType,
name?: string,
domainId?: number,
modelId?: number,
entityId?: string,
) => {
if (!isMobileMode) {
@@ -299,12 +296,31 @@ const Chat: React.FC<Props> = ({
setCurrentConversation({
...conversation,
initMsg: name,
domainId,
modelId,
entityId,
});
saveConversationToLocal(conversation);
};
const updateChatFilter = (data: MsgDataType) => {
const { queryMode, dimensionFilters, elementMatches, modelName, model } = data.chatContext;
if (queryMode !== 'ENTITY_LIST_FILTER') {
return;
}
const entityId = dimensionFilters?.length > 0 ? dimensionFilters[0].value : undefined;
const entityName = elementMatches?.find((item: any) => item.element?.type === 'ID')?.element
?.name;
if (typeof entityId === 'string' && entityName) {
setCurrentModel(model);
setDefaultEntity({
entityId,
entityName,
modelName,
});
}
};
const onMsgDataLoaded = (data: MsgDataType, questionId: string | number) => {
if (!isMobile) {
conversationRef?.current?.updateData();
@@ -312,6 +328,15 @@ const Chat: React.FC<Props> = ({
if (!data) {
return;
}
let parseOptionsItem: any;
if (data.parseOptions && data.parseOptions.length > 0) {
parseOptionsItem = {
id: uuid(),
msg: messageList[messageList.length - 1]?.msg,
type: MessageTypeEnum.PARSE_OPTIONS,
parseOptions: data.parseOptions,
};
}
if (data.queryMode === 'WEB_PAGE') {
setMessageList([
...messageList,
@@ -321,16 +346,19 @@ const Chat: React.FC<Props> = ({
type: MessageTypeEnum.PLUGIN,
msgData: data,
},
...(parseOptionsItem ? [parseOptionsItem] : []),
]);
} else {
const msgs = cloneDeep(messageList);
const msg = msgs.find((item) => item.id === questionId);
if (msg) {
msg.msgData = data;
setMessageList(msgs);
setMessageList([...msgs, ...(parseOptionsItem ? [parseOptionsItem] : [])]);
}
updateMessageContainerScroll();
}
updateChatFilter(data);
};
const onCheckMore = (data: MsgDataType) => {
@@ -354,14 +382,14 @@ const Chat: React.FC<Props> = ({
localStorage.setItem('CONVERSATION_COLLAPSED', `${!conversationCollapsed}`);
};
const getIdentityMsgText = (domain?: DomainType) => {
return domain
? `您好,我当前身份是【${domain.name}】主题专家,我将尽力帮您解答相关问题~`
const getIdentityMsgText = (model?: ModelType) => {
return model
? `您好,我当前身份是【${model.name}】主题专家,我将尽力帮您解答相关问题~`
: '您好,我将尽力帮您解答所有主题相关问题~';
};
const onApplyAuth = (domain: string) => {
setApplyAuthDomain(domain);
const onApplyAuth = (model: string) => {
setApplyAuthModel(model);
setApplyAuthVisible(true);
};
@@ -385,7 +413,7 @@ const Chat: React.FC<Props> = ({
currentConversation={currentConversation}
collapsed={conversationCollapsed}
isCopilotMode={isCopilotMode}
defaultDomainName={defaultDomainName}
defaultModelName={defaultModelName}
defaultEntityFilter={defaultEntityFilter}
triggerNewConversation={triggerNewConversation}
onNewConversationTriggered={onNewConversationTriggered}
@@ -411,23 +439,24 @@ const Chat: React.FC<Props> = ({
<ChatFooter
inputMsg={inputMsg}
chatId={currentConversation?.chatId}
domains={domains}
currentDomain={currentDomain}
models={models}
currentModel={currentModel}
defaultEntity={defaultEntity}
collapsed={conversationCollapsed}
isCopilotMode={isCopilotMode}
copilotFullscreen={copilotFullscreen}
onToggleCollapseBtn={onToggleCollapseBtn}
onInputMsgChange={onInputMsgChange}
onSendMsg={(msg: string, domainId?: number) => {
onSendMsg(msg, messageList, domainId);
onSendMsg={(msg: string, modelId?: number) => {
onSendMsg(msg, messageList, modelId);
if (isMobile) {
inputBlur();
}
}}
onAddConversation={onAddConversation}
onCancelDefaultFilter={() => {
changeDomain(undefined);
changeModel(undefined);
setDefaultEntity(undefined);
if (onCancelCopilotFilter) {
onCancelCopilotFilter();
}

View File

@@ -1,5 +1,5 @@
import { request } from 'umi';
import { DomainType } from './type';
import { ModelType } from './type';
const prefix = '/api';
@@ -24,9 +24,9 @@ export function getAllConversations() {
return request<Result<any>>(`${prefix}/chat/manage/getAll`);
}
export function getMiniProgramList(entityId: string, domainId: number) {
export function getMiniProgramList(entityId: string, modelId: number) {
return request<Result<any>>(
`${prefix}/chat/plugin/extend/getAvailablePlugin/${entityId}/${domainId}`,
`${prefix}/chat/plugin/extend/getAvailablePlugin/${entityId}/${modelId}`,
{
method: 'GET',
skipErrorHandler: true,
@@ -34,8 +34,8 @@ export function getMiniProgramList(entityId: string, domainId: number) {
);
}
export function getDomainList() {
return request<Result<DomainType[]>>(`${prefix}/chat/conf/domainList/view`, {
export function getModelList() {
return request<Result<ModelType[]>>(`${prefix}/chat/conf/modelList/view`, {
method: 'GET',
});
}
@@ -49,14 +49,14 @@ export function updateQAFeedback(questionId: number, score: number) {
);
}
export function queryMetricSuggestion(domainId: number) {
return request<Result<any>>(`${prefix}/chat/recommend/metric/${domainId}`, {
export function queryMetricSuggestion(modelId: number) {
return request<Result<any>>(`${prefix}/chat/recommend/metric/${modelId}`, {
method: 'GET',
});
}
export function querySuggestion(domainId: number) {
return request<Result<any>>(`${prefix}/chat/recommend/${domainId}`, {
export function querySuggestion(modelId: number) {
return request<Result<any>>(`${prefix}/chat/recommend/${modelId}`, {
method: 'GET',
});
}

View File

@@ -444,9 +444,9 @@
color: var(--primary-color);
}
.messageItem {
margin-top: 12px;
}
// .messageItem {
// margin-top: 12px;
// }
.messageTime {
display: flex;

View File

@@ -1,4 +1,4 @@
import { MsgDataType } from 'supersonic-chat-sdk';
import { ChatContextType, MsgDataType } from 'supersonic-chat-sdk';
export enum MessageTypeEnum {
TEXT = 'text', // 指标文本
@@ -10,6 +10,7 @@ export enum MessageTypeEnum {
PLUGIN = 'PLUGIN', // 插件
WEB_PAGE = 'WEB_PAGE', // 插件
RECOMMEND_QUESTIONS = 'recommend_questions', // 推荐问题
PARSE_OPTIONS = 'parse_options', // 解析选项
}
export type MessageItem = {
@@ -18,13 +19,14 @@ export type MessageItem = {
msg?: string;
msgValue?: string;
identityMsg?: string;
domainId?: number;
modelId?: number;
entityId?: string;
msgData?: MsgDataType;
quote?: string;
score?: number;
feedback?: string;
isHistory?: boolean;
parseOptions?: ChatContextType[];
};
export type ConversationDetailType = {
@@ -35,7 +37,7 @@ export type ConversationDetailType = {
lastQuestion?: string;
lastTime?: string;
initMsg?: string;
domainId?: number;
modelId?: number;
entityId?: string;
};
@@ -43,7 +45,7 @@ export enum MessageModeEnum {
INTERPRET = 'interpret',
}
export type DomainType = {
export type ModelType = {
id: number;
parentId: number;
name: string;
@@ -66,12 +68,12 @@ export type PluginType = {
export type DefaultEntityType = {
entityId: string;
entityName: string;
domainName?: string;
modelName?: string;
};
export type SuggestionItemType = {
id: number;
domain: number;
model: number;
name: string;
bizName: string;
};