mirror of
https://github.com/tencentmusic/supersonic.git
synced 2026-04-26 10:14:19 +08:00
[feature](webapp) add copilot and modify domain to model
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
margin-bottom: 6px;
|
||||
column-gap: 10px;
|
||||
|
||||
.domainName {
|
||||
.modelName {
|
||||
margin-left: 4px;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
|
||||
@@ -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]: '维度值',
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -444,9 +444,9 @@
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.messageItem {
|
||||
margin-top: 12px;
|
||||
}
|
||||
// .messageItem {
|
||||
// margin-top: 12px;
|
||||
// }
|
||||
|
||||
.messageTime {
|
||||
display: flex;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user