(feature)(webapp) add show case and support multiple selection and deletion of filter conditions (#251)

This commit is contained in:
williamhliu
2023-10-18 09:56:35 +08:00
committed by GitHub
parent 8d81f63e08
commit 36052cb4f2
36 changed files with 492 additions and 134 deletions

View File

@@ -21,6 +21,7 @@ type Props = {
onSendMsg: (msg: string, modelId?: number) => void; onSendMsg: (msg: string, modelId?: number) => void;
onAddConversation: (agent?: AgentType) => void; onAddConversation: (agent?: AgentType) => void;
onSelectAgent: (agent: AgentType) => void; onSelectAgent: (agent: AgentType) => void;
onOpenShowcase: () => void;
}; };
const { OptGroup, Option } = Select; const { OptGroup, Option } = Select;
@@ -47,6 +48,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
onSendMsg, onSendMsg,
onAddConversation, onAddConversation,
onSelectAgent, onSelectAgent,
onOpenShowcase,
}, },
ref ref
) => { ) => {
@@ -313,14 +315,24 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
<IconFont type="icon-zhinengzhuli" className={styles.toolIcon} /> <IconFont type="icon-zhinengzhuli" className={styles.toolIcon} />
<div></div> <div></div>
</div> </div>
{!isMobile && (
<div className={styles.toolItem} onClick={onOpenShowcase}>
<IconFont type="icon-showcase" className={styles.toolIcon} />
<div>showcase</div>
</div>
)}
</div> </div>
<div className={styles.composer}> <div className={styles.composer}>
<div className={styles.composerInputWrapper}> <div className={styles.composerInputWrapper}>
<AutoComplete <AutoComplete
className={styles.composerInput} className={styles.composerInput}
placeholder={`智能助理${ placeholder={
isMobile ? `[${currentAgent?.name}]` : `${currentAgent?.name}` currentAgent
}将与您对话,输入“/”可切换助理`} ? `智能助理${
isMobile ? `[${currentAgent?.name}]` : `${currentAgent?.name}`
}将与您对话,输入“/”可切换助理`
: '请输入您的问题'
}
value={inputMsg} value={inputMsg}
onChange={(value: string) => { onChange={(value: string) => {
onInputMsgChange(value); onInputMsgChange(value);

View File

@@ -22,7 +22,8 @@ type Props = {
data: MsgDataType, data: MsgDataType,
questionId: string | number, questionId: string | number,
question: string, question: string,
valid: boolean valid: boolean,
isRefresh?: boolean
) => void; ) => void;
onSendMsg: (value: string) => void; onSendMsg: (value: string) => void;
}; };
@@ -72,6 +73,7 @@ const MessageContainer: React.FC<Props> = ({
type, type,
msg, msg,
msgValue, msgValue,
score,
identityMsg, identityMsg,
msgData, msgData,
filters, filters,
@@ -93,13 +95,14 @@ const MessageContainer: React.FC<Props> = ({
conversationId={chatId} conversationId={chatId}
modelId={modelId} modelId={modelId}
agentId={agentId} agentId={agentId}
score={score}
filter={filters} filter={filters}
isLastMessage={index === messageList.length - 1} isLastMessage={index === messageList.length - 1}
triggerResize={triggerResize} triggerResize={triggerResize}
isDeveloper={isDeveloper} isDeveloper={isDeveloper}
integrateSystem={integrateSystem} integrateSystem={integrateSystem}
onMsgDataLoaded={(data: MsgDataType, valid: boolean) => { onMsgDataLoaded={(data: MsgDataType, valid: boolean, isRefresh) => {
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid); onMsgDataLoaded(data, msgId, msgValue || msg || '', valid, isRefresh);
}} }}
onUpdateMessageScroll={updateMessageContainerScroll} onUpdateMessageScroll={updateMessageContainerScroll}
onSendMsg={onSendMsg} onSendMsg={onSendMsg}

View File

@@ -5,14 +5,16 @@ import LeftAvatar from './CopilotAvatar';
import Message from './Message'; import Message from './Message';
import styles from './style.module.less'; import styles from './style.module.less';
import { userAvatarUrl } from '../../common/env'; import { userAvatarUrl } from '../../common/env';
import IconFont from '../../components/IconFont';
type Props = { type Props = {
position: 'left' | 'right'; position: 'left' | 'right';
data: any; data: any;
quote?: string; quote?: string;
anonymousUser?: boolean;
}; };
const Text: React.FC<Props> = ({ position, data, quote }) => { const Text: React.FC<Props> = ({ position, data, quote, anonymousUser }) => {
const textWrapperClass = classNames(styles.textWrapper, { const textWrapperClass = classNames(styles.textWrapper, {
[styles.rightTextWrapper]: position === 'right', [styles.rightTextWrapper]: position === 'right',
}); });
@@ -25,7 +27,13 @@ const Text: React.FC<Props> = ({ position, data, quote }) => {
<div className={styles.text}>{data}</div> <div className={styles.text}>{data}</div>
</Message> </Message>
{!isMobile && position === 'right' && rightAvatarUrl && ( {!isMobile && position === 'right' && rightAvatarUrl && (
<Avatar shape="circle" size={40} src={rightAvatarUrl} className={styles.rightAvatar} /> <Avatar
shape="circle"
size={40}
src={anonymousUser ? undefined : rightAvatarUrl}
icon={<IconFont type="icon-geren" />}
className={styles.rightAvatar}
/>
)} )}
</div> </div>
); );

View File

@@ -20,6 +20,8 @@ import AgentList from './AgentList';
import MobileAgents from './MobileAgents'; import MobileAgents from './MobileAgents';
import { HistoryMsgItemType, MsgDataType, SendMsgParamsType } from '../common/type'; import { HistoryMsgItemType, MsgDataType, SendMsgParamsType } from '../common/type';
import { getHistoryMsg } from '../service'; import { getHistoryMsg } from '../service';
import ShowCase from '../ShowCase';
import { Modal } from 'antd';
type Props = { type Props = {
token?: string; token?: string;
@@ -30,7 +32,6 @@ type Props = {
isDeveloper?: boolean; isDeveloper?: boolean;
integrateSystem?: string; integrateSystem?: string;
isCopilot?: boolean; isCopilot?: boolean;
apiUrl?: string;
onCurrentAgentChange?: (agent?: AgentType) => void; onCurrentAgentChange?: (agent?: AgentType) => void;
onReportMsgEvent?: (msg: string, valid: boolean) => void; onReportMsgEvent?: (msg: string, valid: boolean) => void;
}; };
@@ -45,7 +46,6 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
isDeveloper, isDeveloper,
integrateSystem, integrateSystem,
isCopilot, isCopilot,
apiUrl,
onCurrentAgentChange, onCurrentAgentChange,
onReportMsgEvent, onReportMsgEvent,
}, },
@@ -64,6 +64,7 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
const [currentAgent, setCurrentAgent] = useState<AgentType>(); const [currentAgent, setCurrentAgent] = useState<AgentType>();
const [mobileAgentsVisible, setMobileAgentsVisible] = useState(false); const [mobileAgentsVisible, setMobileAgentsVisible] = useState(false);
const [agentListVisible, setAgentListVisible] = useState(true); const [agentListVisible, setAgentListVisible] = useState(true);
const [showCaseVisible, setShowCaseVisible] = useState(false);
const conversationRef = useRef<any>(); const conversationRef = useRef<any>();
const chatFooterRef = useRef<any>(); const chatFooterRef = useRef<any>();
@@ -120,12 +121,6 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
} }
}, [token]); }, [token]);
useEffect(() => {
if (apiUrl) {
localStorage.setItem('SUPERSONIC_CHAT_API_URL', apiUrl);
}
}, [apiUrl]);
useEffect(() => { useEffect(() => {
if (chatVisible) { if (chatVisible) {
inputFocus(); inputFocus();
@@ -300,7 +295,8 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
data: MsgDataType, data: MsgDataType,
questionId: string | number, questionId: string | number,
question: string, question: string,
valid: boolean valid: boolean,
isRefresh?: boolean
) => { ) => {
onReportMsgEvent?.(question, valid); onReportMsgEvent?.(question, valid);
if (!isMobile) { if (!isMobile) {
@@ -315,7 +311,9 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
msg.msgData = data; msg.msgData = data;
setMessageList(msgs); setMessageList(msgs);
} }
updateMessageContainerScroll(`${questionId}`); if (!isRefresh) {
updateMessageContainerScroll(`${questionId}`);
}
}; };
const onToggleHistoryVisible = () => { const onToggleHistoryVisible = () => {
@@ -404,6 +402,9 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
setAgentListVisible(!agentListVisible); setAgentListVisible(!agentListVisible);
} }
}} }}
onOpenShowcase={() => {
setShowCaseVisible(!showCaseVisible);
}}
ref={chatFooterRef} ref={chatFooterRef}
/> />
)} )}
@@ -419,6 +420,23 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
onCloseConversation={onCloseConversation} onCloseConversation={onCloseConversation}
ref={conversationRef} ref={conversationRef}
/> />
{currentAgent && (
<Modal
title="showcase"
width="98%"
open={showCaseVisible}
centered
footer={null}
wrapClassName={styles.showCaseModal}
onCancel={() => {
setShowCaseVisible(false);
}}
>
<div className={styles.showCase}>
<ShowCase agentId={currentAgent.id} onSendMsg={onSendMsg} />
</div>
</Modal>
)}
</div> </div>
<MobileAgents <MobileAgents
open={mobileAgentsVisible} open={mobileAgentsVisible}

View File

@@ -77,6 +77,21 @@
} }
} }
.showCaseModal {
:global {
.ant-modal-body {
padding: 20px 0 !important;
}
}
}
.showCase {
height: calc(100vh - 140px);
padding: 0 20px;
overflow-y: auto;
padding-bottom: 2px;
}
:global { :global {
.ss-chat-recommend-options { .ss-chat-recommend-options {
.ant-table-thead .ant-table-cell { .ant-table-thead .ant-table-cell {

View File

@@ -0,0 +1,71 @@
import { useEffect, useState } from 'react';
import styles from './style.module.less';
import { ShowCaseMapType } from './type';
import { queryShowCase } from './service';
import Text from '../Chat/components/Text';
import ChatItem from '../components/ChatItem';
import { HistoryMsgItemType } from '../common/type';
import { Spin } from 'antd';
type Props = {
agentId: number;
onSendMsg?: (msg: string) => void;
};
const ShowCase: React.FC<Props> = ({ agentId, onSendMsg }) => {
const [showCaseMap, setShowCaseMap] = useState<ShowCaseMapType>({});
const [loading, setLoading] = useState(false);
const updateData = async (pageNo: number) => {
if (pageNo === 1) {
setLoading(true);
}
const res = await queryShowCase(agentId, pageNo, 20);
if (pageNo === 1) {
setLoading(false);
}
setShowCaseMap(
pageNo === 1 ? res.data.showCaseMap : { ...showCaseMap, ...res.data.showCaseMap }
);
};
useEffect(() => {
if (agentId) {
updateData(1);
}
}, [agentId]);
return (
<Spin spinning={loading} size="large">
<div className={styles.showCase}>
{Object.keys(showCaseMap || {}).map(key => {
const showCaseItem = showCaseMap?.[key] || [];
return (
<div key={key} className={styles.showCaseItem}>
{showCaseItem
.filter((chatItem: HistoryMsgItemType) => !!chatItem.queryResult)
.slice(0, 10)
.map((chatItem: HistoryMsgItemType) => {
return (
<div className={styles.showCaseChatItem} key={chatItem.questionId}>
<Text position="right" data={chatItem.queryText} anonymousUser />
<ChatItem
msg={chatItem.queryText}
msgData={chatItem.queryResult}
conversationId={chatItem.chatId}
agentId={agentId}
integrateSystem="showcase"
onSendMsg={onSendMsg}
/>
</div>
);
})}
</div>
);
})}
</div>
</Spin>
);
};
export default ShowCase;

View File

@@ -0,0 +1,12 @@
import axios from '../service/axiosInstance';
import { isMobile } from '../utils/utils';
import { ShowCaseType } from './type';
const prefix = isMobile ? '/openapi' : '/api';
export function queryShowCase(agentId: number, current: number, pageSize: number) {
return axios.post<ShowCaseType>(
`${prefix}/chat/manage/queryShowCase?agentId=${agentId}`,
{ current, pageSize }
);
}

View File

@@ -0,0 +1,27 @@
.showCase {
column-count: 2;
column-gap: 20px;
height: 100%;
min-height: 400px;
overflow-y: auto;
.showCaseItem {
display: flex;
flex-direction: column;
row-gap: 12px;
padding: 12px;
margin-bottom: 20px;
max-height: 800px;
overflow-y: auto;
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.14), 0 0 2px rgba(0, 0, 0, 0.12);
background: linear-gradient(180deg, rgba(23, 74, 228, 0) 29.44%, rgba(23, 74, 228, 0.06) 100%),
linear-gradient(90deg, #f3f3f7 0%, #f3f3f7 20%, #ebf0f9 60%, #f3f3f7 80%, #f3f3f7 100%);
.showCaseChatItem {
display: flex;
flex-direction: column;
row-gap: 12px;
}
}
}

View File

@@ -0,0 +1,9 @@
import { HistoryMsgItemType } from "../common/type";
export type ShowCaseMapType = Record<number, HistoryMsgItemType[]>;
export type ShowCaseType = {
showCaseMap: ShowCaseMapType,
current: number,
pageSize: number,
}

View File

@@ -71,7 +71,7 @@ export type EntityDimensionType = {
} }
export type SqlInfoType = { export type SqlInfoType = {
llmParseSql: string; s2QL: string;
logicSql: string; logicSql: string;
querySql: string; querySql: string;
} }

View File

@@ -13,6 +13,7 @@ type Props = {
chatContext: ChatContextType; chatContext: ChatContextType;
agentId?: number; agentId?: number;
entityAlias?: string; entityAlias?: string;
integrateSystem?: string;
onFiltersChange: (filters: FilterItemType[]) => void; onFiltersChange: (filters: FilterItemType[]) => void;
onSwitchEntity: (entityId: string) => void; onSwitchEntity: (entityId: string) => void;
}; };
@@ -24,6 +25,7 @@ const FilterItem: React.FC<Props> = ({
chatContext, chatContext,
agentId, agentId,
entityAlias, entityAlias,
integrateSystem,
onFiltersChange, onFiltersChange,
onSwitchEntity, onSwitchEntity,
}) => { }) => {
@@ -50,7 +52,11 @@ const FilterItem: React.FC<Props> = ({
}; };
useEffect(() => { useEffect(() => {
if ((typeof filter.value === 'string' || isArray(filter.value)) && options.length === 0) { if (
(typeof filter.value === 'string' || isArray(filter.value)) &&
options.length === 0 &&
integrateSystem !== 'showcase'
) {
initData(); initData();
} }
}, []); }, []);
@@ -148,17 +154,24 @@ const FilterItem: React.FC<Props> = ({
onSearch={debounceFetcher} onSearch={debounceFetcher}
notFoundContent={loading ? <Spin size="small" /> : null} notFoundContent={loading ? <Spin size="small" /> : null}
onChange={onChange} onChange={onChange}
mode={isArray(filter.value) ? 'multiple' : undefined} mode="multiple"
showSearch showSearch
allowClear
/> />
) : entityAlias && ) : entityAlias &&
['歌曲', '艺人'].includes(entityAlias) && ['歌曲', '艺人'].includes(entityAlias) &&
filter.bizName?.includes('_id') ? ( filter.bizName?.includes('_id') ? (
<SwicthEntity <>
entityName={filter.value} <SwicthEntity
chatContext={chatContext} entityName={filter.value}
onSwitchEntity={onSwitchEntity} chatContext={chatContext}
/> onSwitchEntity={onSwitchEntity}
/>
<span className={`${prefixCls}-switch-entity-tip`}>
({entityAlias}{entityAlias === '艺人' ? '歌手' : entityAlias}
ID切换)
</span>
</>
) : ( ) : (
<span className={`${prefixCls}-filter-value`}>{filter.value}</span> <span className={`${prefixCls}-filter-value`}>{filter.value}</span>
)} )}

View File

@@ -18,6 +18,7 @@ type Props = {
dimensionFilters: FilterItemType[]; dimensionFilters: FilterItemType[];
dateInfo: DateInfoType; dateInfo: DateInfoType;
entityInfo: EntityInfoType; entityInfo: EntityInfoType;
integrateSystem?: string;
onSelectParseInfo: (parseInfo: ChatContextType) => void; onSelectParseInfo: (parseInfo: ChatContextType) => void;
onSwitchEntity: (entityId: string) => void; onSwitchEntity: (entityId: string) => void;
onFiltersChange: (filters: FilterItemType[]) => void; onFiltersChange: (filters: FilterItemType[]) => void;
@@ -35,6 +36,7 @@ const ParseTip: React.FC<Props> = ({
dimensionFilters, dimensionFilters,
dateInfo, dateInfo,
entityInfo, entityInfo,
integrateSystem,
onSelectParseInfo, onSelectParseInfo,
onSwitchEntity, onSwitchEntity,
onFiltersChange, onFiltersChange,
@@ -115,14 +117,14 @@ const ParseTip: React.FC<Props> = ({
return ( return (
<div className={`${prefixCls}-tip-content`}> <div className={`${prefixCls}-tip-content`}>
{!!agentType && queryMode !== 'DSL' ? ( {!!agentType && queryMode !== 'LLM_S2QL' ? (
<div className={`${prefixCls}-tip-item`}> <div className={`${prefixCls}-tip-item`}>
{agentType === 'plugin' ? '插件' : '内置'} {agentType === 'plugin' ? '插件' : '内置'}
<span className={itemValueClass}>{agentName}</span> <span className={itemValueClass}>{agentName}</span>
</div> </div>
) : ( ) : (
<> <>
{(queryMode?.includes('ENTITY') || queryMode === 'DSL') && {(queryMode?.includes('ENTITY') || queryMode === 'LLM_S2QL') &&
typeof entityId === 'string' && typeof entityId === 'string' &&
!!entityAlias && !!entityAlias &&
!!entityName ? ( !!entityName ? (
@@ -144,12 +146,14 @@ const ParseTip: React.FC<Props> = ({
<div className={itemValueClass}>{metric.name}</div> <div className={itemValueClass}>{metric.name}</div>
</div> </div>
)} )}
{['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'DSL'].includes(queryMode!) && {['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'LLM_S2QL'].includes(
queryMode!
) &&
fields && fields &&
fields.length > 0 && ( fields.length > 0 && (
<div className={`${prefixCls}-tip-item`}> <div className={`${prefixCls}-tip-item`}>
<div className={`${prefixCls}-tip-item-name`}> <div className={`${prefixCls}-tip-item-name`}>
{queryMode === 'DSL' {queryMode === 'LLM_S2QL'
? nativeQuery ? nativeQuery
? '查询字段' ? '查询字段'
: '下钻维度' : '下钻维度'
@@ -178,12 +182,14 @@ const ParseTip: React.FC<Props> = ({
<div className={itemValueClass}>{dimension.value}</div> <div className={itemValueClass}>{dimension.value}</div>
</div> </div>
))} ))}
{queryMode === 'METRIC_ORDERBY' && aggType && aggType !== 'NONE' && ( {(queryMode === 'METRIC_ORDERBY' || queryMode === 'METRIC_MODEL') &&
<div className={`${prefixCls}-tip-item`}> aggType &&
<div className={`${prefixCls}-tip-item-name`}></div> aggType !== 'NONE' && (
<div className={itemValueClass}>{AGG_TYPE_MAP[aggType]}</div> <div className={`${prefixCls}-tip-item`}>
</div> <div className={`${prefixCls}-tip-item-name`}></div>
)} <div className={itemValueClass}>{AGG_TYPE_MAP[aggType]}</div>
</div>
)}
</> </>
)} )}
</div> </div>
@@ -218,6 +224,7 @@ const ParseTip: React.FC<Props> = ({
chatContext={currentParseInfo!} chatContext={currentParseInfo!}
entityAlias={entityAlias} entityAlias={entityAlias}
agentId={agentId} agentId={agentId}
integrateSystem={integrateSystem}
onFiltersChange={onFiltersChange} onFiltersChange={onFiltersChange}
onSwitchEntity={onSwitchEntity} onSwitchEntity={onSwitchEntity}
key={filter.name} key={filter.name}
@@ -240,11 +247,6 @@ const ParseTip: React.FC<Props> = ({
<div className={`${prefixCls}-tip`}> <div className={`${prefixCls}-tip`}>
{getTipNode()} {getTipNode()}
{getFiltersNode()} {getFiltersNode()}
{(!type || queryMode === 'DSL') && entityAlias && entityAlias !== '厂牌' && entityName && (
<div className={`${prefixCls}-switch-entity-tip`}>
({entityAlias}{entityAlias === '艺人' ? '歌手' : entityAlias}ID切换)
</div>
)}
</div> </div>
); );

View File

@@ -27,7 +27,7 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
setSqlType(''); setSqlType('');
}; };
if (!sqlInfo.llmParseSql && !sqlInfo.logicSql && !sqlInfo.querySql) { if (!sqlInfo.s2QL && !sqlInfo.logicSql && !sqlInfo.querySql) {
return null; return null;
} }
@@ -44,13 +44,13 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
)} )}
</div> </div>
<div className={`${tipPrefixCls}-content-options`}> <div className={`${tipPrefixCls}-content-options`}>
{sqlInfo.llmParseSql && ( {sqlInfo.s2QL && (
<div <div
className={`${tipPrefixCls}-content-option ${ className={`${tipPrefixCls}-content-option ${
sqlType === 'llmParseSql' ? `${tipPrefixCls}-content-option-active` : '' sqlType === 's2QL' ? `${tipPrefixCls}-content-option-active` : ''
}`} }`}
onClick={() => { onClick={() => {
setSqlType(sqlType === 'llmParseSql' ? '' : 'llmParseSql'); setSqlType(sqlType === 's2QL' ? '' : 's2QL');
}} }}
> >
LLM解析SQL LLM解析SQL

View File

@@ -25,6 +25,7 @@ type Props = {
conversationId?: number; conversationId?: number;
modelId?: number; modelId?: number;
agentId?: number; agentId?: number;
score?: number;
filter?: any[]; filter?: any[];
isLastMessage?: boolean; isLastMessage?: boolean;
msgData?: MsgDataType; msgData?: MsgDataType;
@@ -33,7 +34,7 @@ type Props = {
integrateSystem?: string; integrateSystem?: string;
executeItemNode?: React.ReactNode; executeItemNode?: React.ReactNode;
renderCustomExecuteNode?: boolean; renderCustomExecuteNode?: boolean;
onMsgDataLoaded?: (data: MsgDataType, valid: boolean) => void; onMsgDataLoaded?: (data: MsgDataType, valid: boolean, isRefresh?: boolean) => void;
onUpdateMessageScroll?: () => void; onUpdateMessageScroll?: () => void;
onSendMsg?: (msg: string) => void; onSendMsg?: (msg: string) => void;
}; };
@@ -43,6 +44,7 @@ const ChatItem: React.FC<Props> = ({
conversationId, conversationId,
modelId, modelId,
agentId, agentId,
score,
filter, filter,
isLastMessage, isLastMessage,
triggerResize, triggerResize,
@@ -68,8 +70,6 @@ const ChatItem: React.FC<Props> = ({
const [dateInfo, setDateInfo] = useState<DateInfoType>({} as DateInfoType); const [dateInfo, setDateInfo] = useState<DateInfoType>({} as DateInfoType);
const [entityInfo, setEntityInfo] = useState<EntityInfoType>({} as EntityInfoType); const [entityInfo, setEntityInfo] = useState<EntityInfoType>({} as EntityInfoType);
// const [chartIndex, setChartIndex] = useState(0);
const prefixCls = `${PREFIX_CLS}-item`; const prefixCls = `${PREFIX_CLS}-item`;
const updateData = (res: Result<MsgDataType>) => { const updateData = (res: Result<MsgDataType>) => {
@@ -208,9 +208,7 @@ const ChatItem: React.FC<Props> = ({
const resChatContext = res.data?.chatContext; const resChatContext = res.data?.chatContext;
const contextValue = { ...(resChatContext || chatContextValue), queryId }; const contextValue = { ...(resChatContext || chatContextValue), queryId };
const dataValue = { ...res.data, chatContext: contextValue }; const dataValue = { ...res.data, chatContext: contextValue };
if (onMsgDataLoaded) { onMsgDataLoaded?.(dataValue, true, true);
onMsgDataLoaded(dataValue, true);
}
setData(dataValue); setData(dataValue);
setParseInfo(contextValue); setParseInfo(contextValue);
} }
@@ -230,7 +228,6 @@ const ChatItem: React.FC<Props> = ({
} else { } else {
getEntityInfo(parseInfoValue); getEntityInfo(parseInfoValue);
} }
onUpdateMessageScroll?.();
}; };
const onSelectQuestion = (question: SimilarQuestionType) => { const onSelectQuestion = (question: SimilarQuestionType) => {
@@ -261,6 +258,7 @@ const ChatItem: React.FC<Props> = ({
dimensionFilters={dimensionFilters} dimensionFilters={dimensionFilters}
dateInfo={dateInfo} dateInfo={dateInfo}
entityInfo={entityInfo} entityInfo={entityInfo}
integrateSystem={integrateSystem}
onSelectParseInfo={onSelectParseInfo} onSelectParseInfo={onSelectParseInfo}
onSwitchEntity={onSwitchEntity} onSwitchEntity={onSwitchEntity}
onFiltersChange={onFiltersChange} onFiltersChange={onFiltersChange}
@@ -294,11 +292,11 @@ const ChatItem: React.FC<Props> = ({
/> />
)} )}
</div> </div>
{!isMetricCard && data && ( {integrateSystem !== 'showcase' && (
<Tools <Tools
queryId={parseInfo?.queryId || 0} queryId={parseInfo?.queryId || 0}
scoreValue={undefined} scoreValue={score}
isLastMessage={isLastMessage} // isLastMessage={isLastMessage}
/> />
)} )}
</div> </div>

View File

@@ -164,15 +164,6 @@
line-height: 20px; line-height: 20px;
} }
&-switch-entity-tip {
display: flex;
align-items: center;
column-gap: 6px;
color: var(--text-color-fourth);
font-size: 13px;
font-weight: normal;
}
&-switch-entity { &-switch-entity {
cursor: pointer; cursor: pointer;
} }
@@ -372,6 +363,16 @@
min-width: 100px; min-width: 100px;
} }
&-switch-entity-tip {
display: flex;
align-items: center;
column-gap: 6px;
margin-left: 4px;
color: var(--text-color-fourth);
font-size: 13px;
font-weight: normal;
}
.ant-select-selector, .ant-input-number-input { .ant-select-selector, .ant-input-number-input {
background-color: #f5f8fb !important; background-color: #f5f8fb !important;
border-color: #ececec !important; border-color: #ececec !important;

View File

@@ -28,11 +28,7 @@ const MetricCard: React.FC<Props> = ({ data, loading, onApplyAuth }) => {
const prefixCls = `${PREFIX_CLS}-metric-card`; const prefixCls = `${PREFIX_CLS}-metric-card`;
const matricCardClass = classNames(prefixCls, { const matricCardClass = classNames(prefixCls, {
[`${PREFIX_CLS}-metric-card-dsl`]: queryMode === 'DSL', [`${PREFIX_CLS}-metric-card-dsl`]: queryMode === 'LLM_S2QL',
});
const indicatorClass = classNames(`${prefixCls}-indicator`, {
[`${prefixCls}-indicator-period-compare`]: metricInfos?.length > 0,
}); });
const [isNumber, setIsNumber] = useState(false); const [isNumber, setIsNumber] = useState(false);
@@ -50,7 +46,7 @@ const MetricCard: React.FC<Props> = ({ data, loading, onApplyAuth }) => {
)} )}
</div> </div>
<Spin spinning={loading}> <Spin spinning={loading}>
<div className={indicatorClass}> <div className={`${prefixCls}-indicator`}>
{indicatorColumn && !indicatorColumn?.authorized ? ( {indicatorColumn && !indicatorColumn?.authorized ? (
<ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} /> <ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} />
) : ( ) : (

View File

@@ -55,12 +55,8 @@
&-indicator { &-indicator {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
justify-content: flex-start;
}
&-indicator-period-compare {
align-items: flex-start; align-items: flex-start;
justify-content: flex-start;
} }
&-date-range { &-date-range {
@@ -90,6 +86,7 @@
column-gap: 40px; column-gap: 40px;
font-size: 13px; font-size: 13px;
overflow-x: auto; overflow-x: auto;
margin-bottom: 12px;
} }
&-period-compare-item { &-period-compare-item {

View File

@@ -13,6 +13,7 @@ import moment from 'moment';
import { ColumnType } from '../../../common/type'; import { ColumnType } from '../../../common/type';
import NoPermissionChart from '../NoPermissionChart'; import NoPermissionChart from '../NoPermissionChart';
import classNames from 'classnames'; import classNames from 'classnames';
import { isArray } from 'lodash';
type Props = { type Props = {
model?: string; model?: string;
@@ -83,7 +84,9 @@ const MetricTrendChart: React.FC<Props> = ({
}); });
const xData = groupData[sortedGroupKeys[0]]?.map((item: any) => { const xData = groupData[sortedGroupKeys[0]]?.map((item: any) => {
const date = `${item[dateColumnName]}`; const date = isArray(item[dateColumnName])
? item[dateColumnName].join('-')
: `${item[dateColumnName]}`;
return date.length === 10 ? moment(date).format('MM-DD') : date; return date.length === 10 ? moment(date).format('MM-DD') : date;
}); });

View File

@@ -0,0 +1,148 @@
import { CHART_SECONDARY_COLOR, CLS_PREFIX, THEME_COLOR_LIST } from '../../../common/constants';
import { getFormattedValue } from '../../../utils/utils';
import type { ECharts } from 'echarts';
import * as echarts from 'echarts';
import React, { useEffect, useRef, useState } from 'react';
import moment from 'moment';
import { ColumnType } from '../../../common/type';
import { isArray } from 'lodash';
type Props = {
dateColumnName: string;
metricFields: ColumnType[];
resultList: any[];
triggerResize?: boolean;
};
const MultiMetricsTrendChart: React.FC<Props> = ({
dateColumnName,
metricFields,
resultList,
triggerResize,
}) => {
const chartRef = useRef<any>();
const [instance, setInstance] = useState<ECharts>();
const renderChart = () => {
let instanceObj: any;
if (!instance) {
instanceObj = echarts.init(chartRef.current);
setInstance(instanceObj);
} else {
instanceObj = instance;
instanceObj.clear();
}
const xData = resultList?.map((item: any) => {
const date = isArray(item[dateColumnName])
? item[dateColumnName].join('-')
: `${item[dateColumnName]}`;
return date.length === 10 ? moment(date).format('MM-DD') : date;
});
instanceObj.setOption({
legend: {
left: 0,
top: 0,
icon: 'rect',
itemWidth: 15,
itemHeight: 5,
type: 'scroll',
},
xAxis: {
type: 'category',
axisTick: {
alignWithLabel: true,
lineStyle: {
color: CHART_SECONDARY_COLOR,
},
},
axisLine: {
lineStyle: {
color: CHART_SECONDARY_COLOR,
},
},
axisLabel: {
showMaxLabel: true,
color: '#999',
},
data: xData,
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: {
opacity: 0.3,
},
},
axisLabel: {
formatter: function (value: any) {
return value === 0 ? 0 : getFormattedValue(value);
},
},
},
tooltip: {
trigger: 'axis',
formatter: function (params: any[]) {
const param = params[0];
const valueLabels = params
.sort((a, b) => b.value - a.value)
.map(
(item: any) =>
`<div style="margin-top: 3px;">${
item.marker
} <span style="display: inline-block; width: 70px; margin-right: 12px;">${
item.seriesName
}</span><span style="display: inline-block; width: 90px; text-align: right; font-weight: 500;">${
item.value === '' ? '-' : getFormattedValue(item.value)
}</span></div>`
)
.join('');
return `${param.name}<br />${valueLabels}`;
},
},
grid: {
left: '1%',
right: '4%',
bottom: '3%',
top: 45,
containLabel: true,
},
series: metricFields.map((metricField, index) => {
return {
type: 'line',
name: metricField.name,
symbol: 'circle',
showSymbol: resultList.length === 1,
smooth: true,
data: resultList.map((item: any) => {
const value = item[metricField.nameEn];
return (metricField.dataFormatType === 'percent' ||
metricField.dataFormatType === 'decimal') &&
metricField.dataFormat?.needMultiply100
? value * 100
: value;
}),
color: THEME_COLOR_LIST[index],
};
}),
});
instanceObj.resize();
};
useEffect(() => {
renderChart();
}, [resultList]);
useEffect(() => {
if (triggerResize && instance) {
instance.resize();
}
}, [triggerResize]);
const prefixCls = `${CLS_PREFIX}-metric-trend`;
return <div className={`${prefixCls}-flow-trend-chart`} ref={chartRef} />;
};
export default MultiMetricsTrendChart;

View File

@@ -1,11 +1,12 @@
import { CLS_PREFIX } from '../../../common/constants'; import { CLS_PREFIX } from '../../../common/constants';
import { FieldType, MsgDataType } from '../../../common/type'; import { DrillDownDimensionType, FieldType, MsgDataType } from '../../../common/type';
import { isMobile } from '../../../utils/utils'; import { isMobile } from '../../../utils/utils';
import MetricTrendChart from './MetricTrendChart'; import MetricTrendChart from './MetricTrendChart';
import { Spin } from 'antd'; import { Spin } from 'antd';
import Table from '../Table'; import Table from '../Table';
import MetricInfo from './MetricInfo'; import MetricInfo from './MetricInfo';
import DateOptions from '../DateOptions'; import DateOptions from '../DateOptions';
import MultiMetricsTrendChart from './MultiMetricsTrendChart';
type Props = { type Props = {
data: MsgDataType; data: MsgDataType;
@@ -13,6 +14,7 @@ type Props = {
triggerResize?: boolean; triggerResize?: boolean;
loading: boolean; loading: boolean;
activeMetricField?: FieldType; activeMetricField?: FieldType;
drillDownDimension?: DrillDownDimensionType;
currentDateOption?: number; currentDateOption?: number;
onApplyAuth?: (model: string) => void; onApplyAuth?: (model: string) => void;
onSelectDateOption: (value: number) => void; onSelectDateOption: (value: number) => void;
@@ -24,6 +26,7 @@ const MetricTrend: React.FC<Props> = ({
triggerResize, triggerResize,
loading, loading,
activeMetricField, activeMetricField,
drillDownDimension,
currentDateOption, currentDateOption,
onApplyAuth, onApplyAuth,
onSelectDateOption, onSelectDateOption,
@@ -36,6 +39,7 @@ const MetricTrend: React.FC<Props> = ({
const dateColumnName = dateField?.nameEn || ''; const dateColumnName = dateField?.nameEn || '';
const categoryColumnName = const categoryColumnName =
queryColumns?.find((column: any) => column.showType === 'CATEGORY')?.nameEn || ''; queryColumns?.find((column: any) => column.showType === 'CATEGORY')?.nameEn || '';
const metricFields = queryColumns?.filter((column: any) => column.showType === 'NUMBER');
const currentMetricField = queryColumns?.find((column: any) => column.showType === 'NUMBER'); const currentMetricField = queryColumns?.find((column: any) => column.showType === 'NUMBER');
@@ -48,19 +52,23 @@ const MetricTrend: React.FC<Props> = ({
return ( return (
<div className={prefixCls}> <div className={prefixCls}>
<div className={`${prefixCls}-charts`}> <div className={`${prefixCls}-charts`}>
<div className={`${prefixCls}-top-bar`}> {metricFields?.length === 1 && (
<div <div className={`${prefixCls}-top-bar`}>
className={`${prefixCls}-metric-fields ${prefixCls}-metric-field-single`} <div
key={activeMetricField?.bizName} className={`${prefixCls}-metric-fields ${prefixCls}-metric-field-single`}
> key={activeMetricField?.bizName}
{activeMetricField?.name} >
{activeMetricField?.name}
</div>
</div> </div>
</div> )}
<Spin spinning={loading}> <Spin spinning={loading}>
<div className={`${prefixCls}-content`}> <div className={`${prefixCls}-content`}>
{!isMobile && aggregateInfo?.metricInfos?.length > 0 && ( {!isMobile &&
<MetricInfo aggregateInfo={aggregateInfo} currentMetricField={currentMetricField} /> aggregateInfo?.metricInfos?.length > 0 &&
)} drillDownDimension === undefined && (
<MetricInfo aggregateInfo={aggregateInfo} currentMetricField={currentMetricField} />
)}
<DateOptions <DateOptions
chatContext={chatContext} chatContext={chatContext}
currentDateOption={currentDateOption} currentDateOption={currentDateOption}
@@ -68,6 +76,13 @@ const MetricTrend: React.FC<Props> = ({
/> />
{queryResults?.length === 1 || chartIndex % 2 === 1 ? ( {queryResults?.length === 1 || chartIndex % 2 === 1 ? (
<Table data={{ ...data, queryResults }} onApplyAuth={onApplyAuth} /> <Table data={{ ...data, queryResults }} onApplyAuth={onApplyAuth} />
) : metricFields.length > 1 ? (
<MultiMetricsTrendChart
dateColumnName={dateColumnName}
metricFields={metricFields}
resultList={queryResults}
triggerResize={triggerResize}
/>
) : ( ) : (
<MetricTrendChart <MetricTrendChart
model={entityInfo?.modelInfo.name} model={entityInfo?.modelInfo.name}

View File

@@ -59,11 +59,9 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
const metricFields = columns.filter(item => item.showType === 'NUMBER'); const metricFields = columns.filter(item => item.showType === 'NUMBER');
const isDslMetricCard = const isDslMetricCard =
queryMode === 'DSL' && singleData && metricFields.length === 1 && columns.length === 1; queryMode === 'LLM_S2QL' && singleData && metricFields.length === 1 && columns.length === 1;
const isMetricCard = const isMetricCard = (queryMode.includes('METRIC') || isDslMetricCard) && singleData;
(queryMode.includes('METRIC') || isDslMetricCard) &&
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
const isText = const isText =
columns.length === 1 && columns.length === 1 &&
@@ -95,24 +93,27 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
if (isTable) { if (isTable) {
return <Table data={{ ...data, queryColumns: columns, queryResults: dataSource }} />; return <Table data={{ ...data, queryColumns: columns, queryResults: dataSource }} />;
} }
if (dateField && metricFields.length > 0) { if (
if (!dataSource.every(item => item[dateField.nameEn] === dataSource[0][dateField.nameEn])) { dateField &&
return ( metricFields.length > 0 &&
<MetricTrend !dataSource.every(item => item[dateField.nameEn] === dataSource[0][dateField.nameEn])
data={{ ) {
...data, return (
queryColumns: columns, <MetricTrend
queryResults: dataSource, data={{
}} ...data,
loading={loading} queryColumns: columns,
chartIndex={chartIndex} queryResults: dataSource,
triggerResize={triggerResize} }}
activeMetricField={activeMetricField} loading={loading}
currentDateOption={currentDateOption} chartIndex={chartIndex}
onSelectDateOption={selectDateOption} triggerResize={triggerResize}
/> activeMetricField={activeMetricField}
); drillDownDimension={drillDownDimension}
} currentDateOption={currentDateOption}
onSelectDateOption={selectDateOption}
/>
);
} }
if (categoryField?.length > 0 && metricFields?.length > 0) { if (categoryField?.length > 0 && metricFields?.length > 0) {
return ( return (
@@ -209,7 +210,11 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
<div> <div>
{getMsgContent()} {getMsgContent()}
{(isMultipleMetric || existDrillDownDimension) && ( {(isMultipleMetric || existDrillDownDimension) && (
<div className={`${prefixCls}-bottom-tools`}> <div
className={`${prefixCls}-bottom-tools ${
isMetricCard ? `${prefixCls}-metric-card-tools` : ''
}`}
>
{isMultipleMetric && ( {isMultipleMetric && (
<MetricOptions <MetricOptions
metrics={chatContext.metrics} metrics={chatContext.metrics}
@@ -221,6 +226,7 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
{existDrillDownDimension && ( {existDrillDownDimension && (
<DrillDownDimensions <DrillDownDimensions
modelId={chatContext.modelId} modelId={chatContext.modelId}
metricId={activeMetricField?.id || defaultMetricField?.id}
drillDownDimension={drillDownDimension} drillDownDimension={drillDownDimension}
originDimensions={chatContext.dimensions} originDimensions={chatContext.dimensions}
dimensionFilters={chatContext.dimensionFilters} dimensionFilters={chatContext.dimensionFilters}

View File

@@ -14,5 +14,10 @@
column-gap: 20px; column-gap: 20px;
font-size: 14px; font-size: 14px;
margin-top: 6px; margin-top: 6px;
margin-bottom: 2px;
}
&-metric-card-tools {
margin-top: 0;
} }
} }

View File

@@ -8,6 +8,7 @@ import classNames from 'classnames';
type Props = { type Props = {
modelId: number; modelId: number;
metricId?: number;
drillDownDimension?: DrillDownDimensionType; drillDownDimension?: DrillDownDimensionType;
isMetricCard?: boolean; isMetricCard?: boolean;
originDimensions?: DrillDownDimensionType[]; originDimensions?: DrillDownDimensionType[];
@@ -17,8 +18,11 @@ type Props = {
const MAX_DIMENSION_COUNT = 20; const MAX_DIMENSION_COUNT = 20;
const DEFAULT_DIMENSION_COUNT = 5;
const DrillDownDimensions: React.FC<Props> = ({ const DrillDownDimensions: React.FC<Props> = ({
modelId, modelId,
metricId,
drillDownDimension, drillDownDimension,
isMetricCard, isMetricCard,
originDimensions, originDimensions,
@@ -27,12 +31,10 @@ const DrillDownDimensions: React.FC<Props> = ({
}) => { }) => {
const [dimensions, setDimensions] = useState<DrillDownDimensionType[]>([]); const [dimensions, setDimensions] = useState<DrillDownDimensionType[]>([]);
const DEFAULT_DIMENSION_COUNT = isMetricCard ? 3 : 5;
const prefixCls = `${CLS_PREFIX}-drill-down-dimensions`; const prefixCls = `${CLS_PREFIX}-drill-down-dimensions`;
const initData = async () => { const initData = async () => {
const res = await queryDrillDownDimensions(modelId); const res = await queryDrillDownDimensions(modelId, metricId);
setDimensions( setDimensions(
res.data.dimensions res.data.dimensions
.filter( .filter(

View File

@@ -5,7 +5,6 @@
.@{drill-down-dimensions-prefix-cls} { .@{drill-down-dimensions-prefix-cls} {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: 2px;
&-section { &-section {
width: 100%; width: 100%;
@@ -13,7 +12,6 @@
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
column-gap: 6px; column-gap: 6px;
margin-top: 6px;
} }
&-metric-card { &-metric-card {

View File

@@ -1,7 +1,7 @@
import { createFromIconfontCN } from '@ant-design/icons'; import { createFromIconfontCN } from '@ant-design/icons';
const IconFont = createFromIconfontCN({ const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4120566_46xw04fpzii.js', scriptUrl: '//at.alicdn.com/t/c/font_4120566_2vn019wsui6.js',
}); });
export default IconFont; export default IconFont;

View File

@@ -12,8 +12,6 @@
align-items: center; align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
column-gap: 6px; column-gap: 6px;
margin-top: 8px;
margin-bottom: 4px;
} }
&-metric-card { &-metric-card {

View File

@@ -35,7 +35,7 @@ const Tools: React.FC<Props> = ({ queryId, scoreValue, isLastMessage }) => {
return ( return (
<div className={prefixCls}> <div className={prefixCls}>
{!isMobile && isLastMessage && ( {!isMobile && (
<div className={`${prefixCls}-feedback`}> <div className={`${prefixCls}-feedback`}>
<div></div> <div></div>
<LikeOutlined className={likeClass} onClick={like} /> <LikeOutlined className={likeClass} onClick={like} />

View File

@@ -5,7 +5,7 @@ import './styles/index.less';
// import ChatDemo from './demo/ChatDemo'; // import ChatDemo from './demo/ChatDemo';
// import CopilotDemo from './demo/CopilotDemo'; // import CopilotDemo from './demo/CopilotDemo';
// const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); // const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
// root.render(<Chat />); // root.render(<ChatDemo />);
export { default as Chat } from './Chat'; export { default as Chat } from './Chat';
@@ -15,6 +15,8 @@ export { default as ChatMsg } from './components/ChatMsg';
export { default as ChatItem } from './components/ChatItem'; export { default as ChatItem } from './components/ChatItem';
export { default as ShowCase } from './ShowCase';
export type { export type {
SearchRecommendItem, SearchRecommendItem,
FieldType, FieldType,

View File

@@ -5,7 +5,7 @@ import { getToken } from '../utils/utils';
// 创建axios实例 // 创建axios实例
const axiosInstance: AxiosInstance = axios.create({ const axiosInstance: AxiosInstance = axios.create({
// 设置基本URL所有请求都会使用这个URL作为前缀 // 设置基本URL所有请求都会使用这个URL作为前缀
baseURL: localStorage.getItem('SUPERSONIC_CHAT_API_URL') || '', baseURL: '',
// 设置请求超时时间(毫秒) // 设置请求超时时间(毫秒)
timeout: 120000, timeout: 120000,
// 设置请求头 // 设置请求头
@@ -19,7 +19,6 @@ axiosInstance.interceptors.request.use(
(config: any) => { (config: any) => {
const token = getToken(); const token = getToken();
if (token && config?.headers) { if (token && config?.headers) {
// config.headers.Auth = `Bearer ${token}`;
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
} }
return config; return config;

View File

@@ -77,8 +77,8 @@ export function updateQAFeedback(questionId: number, score: number) {
return axios.post<any>(`${prefix}/chat/manage/updateQAFeedback?id=${questionId}&score=${score}&feedback=`); return axios.post<any>(`${prefix}/chat/manage/updateQAFeedback?id=${questionId}&score=${score}&feedback=`);
} }
export function queryDrillDownDimensions(modelId: number) { export function queryDrillDownDimensions(modelId: number, metricId?: number) {
return axios.get<{ dimensions: DrillDownDimensionType[] }>(`${prefix}/chat/recommend/metric/${modelId}`); return axios.get<{ dimensions: DrillDownDimensionType[] }>(`${prefix}/chat/recommend/metric/${modelId}${metricId ? `?metricId=${metricId}` : ''}`);
} }
export function queryDimensionValues(modelId: number, bizName: string, agentId: number, elementID: number, value: string) { export function queryDimensionValues(modelId: number, bizName: string, agentId: number, elementID: number, value: string) {
@@ -86,7 +86,7 @@ export function queryDimensionValues(modelId: number, bizName: string, agentId:
} }
export function querySimilarQuestions(queryText: string, agentId?: number) { export function querySimilarQuestions(queryText: string, agentId?: number) {
return axios.get<any>(`${prefix}/chat/manage/getSolvedQuery?queryText=${queryText}&agentId=${agentId}`); return axios.get<any>(`${prefix}/chat/manage/getSolvedQuery?queryText=${queryText}&agentId=${agentId || 0}`);
} }
export function queryEntityInfo(queryId: number, parseId: number) { export function queryEntityInfo(queryId: number, parseId: number) {

View File

@@ -113,7 +113,7 @@ const ToolModal: React.FC<Props> = ({ editTool, onSaveTool, onCancel }) => {
<FormItem name="name" label="名称"> <FormItem name="name" label="名称">
<Input placeholder="请输入工具名称" /> <Input placeholder="请输入工具名称" />
</FormItem> </FormItem>
{(toolType === AgentToolTypeEnum.RULE || toolType === AgentToolTypeEnum.DSL) && ( {(toolType === AgentToolTypeEnum.RULE || toolType === AgentToolTypeEnum.LLM_S2QL) && (
<FormItem name="modelIds" label="主题域"> <FormItem name="modelIds" label="主题域">
<Select <Select
options={modelList.map((model) => ({ label: model.name, value: model.id }))} options={modelList.map((model) => ({ label: model.name, value: model.id }))}
@@ -122,7 +122,7 @@ const ToolModal: React.FC<Props> = ({ editTool, onSaveTool, onCancel }) => {
/> />
</FormItem> </FormItem>
)} )}
{toolType === AgentToolTypeEnum.DSL && ( {toolType === AgentToolTypeEnum.LLM_S2QL && (
<FormItem name="exampleQuestions" label="示例问题"> <FormItem name="exampleQuestions" label="示例问题">
<div className={styles.paramsSection}> <div className={styles.paramsSection}>
{examples.map((example) => { {examples.map((example) => {

View File

@@ -6,7 +6,7 @@ export type MetricOptionType = {
export enum AgentToolTypeEnum { export enum AgentToolTypeEnum {
RULE = 'RULE', RULE = 'RULE',
DSL = 'DSL', LLM_S2QL = 'LLM_S2QL',
PLUGIN = 'PLUGIN', PLUGIN = 'PLUGIN',
INTERPRET = 'INTERPRET' INTERPRET = 'INTERPRET'
} }
@@ -29,7 +29,7 @@ export const AGENT_TOOL_TYPE_LIST = [
}, },
{ {
label: '大模型语义解析', label: '大模型语义解析',
value: AgentToolTypeEnum.DSL value: AgentToolTypeEnum.LLM_S2QL
}, },
{ {
label: '大模型指标解读', label: '大模型指标解读',

View File

@@ -216,7 +216,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
}))} }))}
onChange={(value) => { onChange={(value) => {
setPluginType(value); setPluginType(value);
if (value === PluginTypeEnum.DSL) { if (value === PluginTypeEnum.LLM_S2QL) {
form.setFieldsValue({ parseMode: ParseModeEnum.FUNCTION_CALL }); form.setFieldsValue({ parseMode: ParseModeEnum.FUNCTION_CALL });
setFunctionParams([ setFunctionParams([
{ {
@@ -243,7 +243,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
<FormItem name="pattern" label="函数描述"> <FormItem name="pattern" label="函数描述">
<TextArea placeholder="请输入函数描述,多个描述换行分隔" allowClear /> <TextArea placeholder="请输入函数描述,多个描述换行分隔" allowClear />
</FormItem> </FormItem>
{/* <FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.DSL}> {/* <FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.LLM_S2QL}>
<div className={styles.paramsSection}> <div className={styles.paramsSection}>
{functionParams.map((functionParam: FunctionParamFormItemType) => { {functionParams.map((functionParam: FunctionParamFormItemType) => {
const { id, name, type, description } = functionParam; const { id, name, type, description } = functionParam;

View File

@@ -1,7 +1,7 @@
export const PLUGIN_TYPE_MAP = { export const PLUGIN_TYPE_MAP = {
WEB_PAGE: 'Web页面', WEB_PAGE: 'Web页面',
WEB_SERVICE: 'Web服务', WEB_SERVICE: 'Web服务',
// DSL: 'LLM语义解析', // LLM_S2QL: 'LLM语义解析',
// CONTENT_INTERPRET: '内容解读', // CONTENT_INTERPRET: '内容解读',
} }

View File

@@ -9,7 +9,7 @@ export type PluginConfigType = {
export enum PluginTypeEnum { export enum PluginTypeEnum {
WEB_PAGE = 'WEB_PAGE', WEB_PAGE = 'WEB_PAGE',
WEB_SERVICE = 'WEB_SERVICE', WEB_SERVICE = 'WEB_SERVICE',
DSL = 'DSL' LLM_S2QL = 'LLM_S2QL'
} }
export enum ParseModeEnum { export enum ParseModeEnum {

2
webapp/pnpm-lock.yaml generated
View File

@@ -430,7 +430,7 @@ importers:
specifier: '3.5' specifier: '3.5'
version: 3.5.41(react-router@5.3.4) version: 3.5.41(react-router@5.3.4)
umi-request: umi-request:
specifier: ^1.0.8 specifier: ^1.4.0
version: 1.4.0 version: 1.4.0
devDependencies: devDependencies:
'@ant-design/pro-cli': '@ant-design/pro-cli':