mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-10 19:51:00 +00:00
(feature)(webapp) add show case and support multiple selection and deletion of filter conditions (#251)
This commit is contained in:
@@ -21,6 +21,7 @@ type Props = {
|
||||
onSendMsg: (msg: string, modelId?: number) => void;
|
||||
onAddConversation: (agent?: AgentType) => void;
|
||||
onSelectAgent: (agent: AgentType) => void;
|
||||
onOpenShowcase: () => void;
|
||||
};
|
||||
|
||||
const { OptGroup, Option } = Select;
|
||||
@@ -47,6 +48,7 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
||||
onSendMsg,
|
||||
onAddConversation,
|
||||
onSelectAgent,
|
||||
onOpenShowcase,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -313,14 +315,24 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
||||
<IconFont type="icon-zhinengzhuli" className={styles.toolIcon} />
|
||||
<div>智能助理</div>
|
||||
</div>
|
||||
{!isMobile && (
|
||||
<div className={styles.toolItem} onClick={onOpenShowcase}>
|
||||
<IconFont type="icon-showcase" className={styles.toolIcon} />
|
||||
<div>showcase</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.composer}>
|
||||
<div className={styles.composerInputWrapper}>
|
||||
<AutoComplete
|
||||
className={styles.composerInput}
|
||||
placeholder={`智能助理${
|
||||
isMobile ? `[${currentAgent?.name}]` : `【${currentAgent?.name}】`
|
||||
}将与您对话,输入“/”可切换助理`}
|
||||
placeholder={
|
||||
currentAgent
|
||||
? `智能助理${
|
||||
isMobile ? `[${currentAgent?.name}]` : `【${currentAgent?.name}】`
|
||||
}将与您对话,输入“/”可切换助理`
|
||||
: '请输入您的问题'
|
||||
}
|
||||
value={inputMsg}
|
||||
onChange={(value: string) => {
|
||||
onInputMsgChange(value);
|
||||
|
||||
@@ -22,7 +22,8 @@ type Props = {
|
||||
data: MsgDataType,
|
||||
questionId: string | number,
|
||||
question: string,
|
||||
valid: boolean
|
||||
valid: boolean,
|
||||
isRefresh?: boolean
|
||||
) => void;
|
||||
onSendMsg: (value: string) => void;
|
||||
};
|
||||
@@ -72,6 +73,7 @@ const MessageContainer: React.FC<Props> = ({
|
||||
type,
|
||||
msg,
|
||||
msgValue,
|
||||
score,
|
||||
identityMsg,
|
||||
msgData,
|
||||
filters,
|
||||
@@ -93,13 +95,14 @@ const MessageContainer: React.FC<Props> = ({
|
||||
conversationId={chatId}
|
||||
modelId={modelId}
|
||||
agentId={agentId}
|
||||
score={score}
|
||||
filter={filters}
|
||||
isLastMessage={index === messageList.length - 1}
|
||||
triggerResize={triggerResize}
|
||||
isDeveloper={isDeveloper}
|
||||
integrateSystem={integrateSystem}
|
||||
onMsgDataLoaded={(data: MsgDataType, valid: boolean) => {
|
||||
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid);
|
||||
onMsgDataLoaded={(data: MsgDataType, valid: boolean, isRefresh) => {
|
||||
onMsgDataLoaded(data, msgId, msgValue || msg || '', valid, isRefresh);
|
||||
}}
|
||||
onUpdateMessageScroll={updateMessageContainerScroll}
|
||||
onSendMsg={onSendMsg}
|
||||
|
||||
@@ -5,14 +5,16 @@ import LeftAvatar from './CopilotAvatar';
|
||||
import Message from './Message';
|
||||
import styles from './style.module.less';
|
||||
import { userAvatarUrl } from '../../common/env';
|
||||
import IconFont from '../../components/IconFont';
|
||||
|
||||
type Props = {
|
||||
position: 'left' | 'right';
|
||||
data: any;
|
||||
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, {
|
||||
[styles.rightTextWrapper]: position === 'right',
|
||||
});
|
||||
@@ -25,7 +27,13 @@ const Text: React.FC<Props> = ({ position, data, quote }) => {
|
||||
<div className={styles.text}>{data}</div>
|
||||
</Message>
|
||||
{!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>
|
||||
);
|
||||
|
||||
@@ -20,6 +20,8 @@ import AgentList from './AgentList';
|
||||
import MobileAgents from './MobileAgents';
|
||||
import { HistoryMsgItemType, MsgDataType, SendMsgParamsType } from '../common/type';
|
||||
import { getHistoryMsg } from '../service';
|
||||
import ShowCase from '../ShowCase';
|
||||
import { Modal } from 'antd';
|
||||
|
||||
type Props = {
|
||||
token?: string;
|
||||
@@ -30,7 +32,6 @@ type Props = {
|
||||
isDeveloper?: boolean;
|
||||
integrateSystem?: string;
|
||||
isCopilot?: boolean;
|
||||
apiUrl?: string;
|
||||
onCurrentAgentChange?: (agent?: AgentType) => void;
|
||||
onReportMsgEvent?: (msg: string, valid: boolean) => void;
|
||||
};
|
||||
@@ -45,7 +46,6 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
isDeveloper,
|
||||
integrateSystem,
|
||||
isCopilot,
|
||||
apiUrl,
|
||||
onCurrentAgentChange,
|
||||
onReportMsgEvent,
|
||||
},
|
||||
@@ -64,6 +64,7 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
const [currentAgent, setCurrentAgent] = useState<AgentType>();
|
||||
const [mobileAgentsVisible, setMobileAgentsVisible] = useState(false);
|
||||
const [agentListVisible, setAgentListVisible] = useState(true);
|
||||
const [showCaseVisible, setShowCaseVisible] = useState(false);
|
||||
|
||||
const conversationRef = useRef<any>();
|
||||
const chatFooterRef = useRef<any>();
|
||||
@@ -120,12 +121,6 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => {
|
||||
if (apiUrl) {
|
||||
localStorage.setItem('SUPERSONIC_CHAT_API_URL', apiUrl);
|
||||
}
|
||||
}, [apiUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatVisible) {
|
||||
inputFocus();
|
||||
@@ -300,7 +295,8 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
data: MsgDataType,
|
||||
questionId: string | number,
|
||||
question: string,
|
||||
valid: boolean
|
||||
valid: boolean,
|
||||
isRefresh?: boolean
|
||||
) => {
|
||||
onReportMsgEvent?.(question, valid);
|
||||
if (!isMobile) {
|
||||
@@ -315,7 +311,9 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
msg.msgData = data;
|
||||
setMessageList(msgs);
|
||||
}
|
||||
updateMessageContainerScroll(`${questionId}`);
|
||||
if (!isRefresh) {
|
||||
updateMessageContainerScroll(`${questionId}`);
|
||||
}
|
||||
};
|
||||
|
||||
const onToggleHistoryVisible = () => {
|
||||
@@ -404,6 +402,9 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
setAgentListVisible(!agentListVisible);
|
||||
}
|
||||
}}
|
||||
onOpenShowcase={() => {
|
||||
setShowCaseVisible(!showCaseVisible);
|
||||
}}
|
||||
ref={chatFooterRef}
|
||||
/>
|
||||
)}
|
||||
@@ -419,6 +420,23 @@ const Chat: ForwardRefRenderFunction<any, Props> = (
|
||||
onCloseConversation={onCloseConversation}
|
||||
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>
|
||||
<MobileAgents
|
||||
open={mobileAgentsVisible}
|
||||
|
||||
@@ -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 {
|
||||
.ss-chat-recommend-options {
|
||||
.ant-table-thead .ant-table-cell {
|
||||
|
||||
71
webapp/packages/chat-sdk/src/ShowCase/index.tsx
Normal file
71
webapp/packages/chat-sdk/src/ShowCase/index.tsx
Normal 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;
|
||||
12
webapp/packages/chat-sdk/src/ShowCase/service.ts
Normal file
12
webapp/packages/chat-sdk/src/ShowCase/service.ts
Normal 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 }
|
||||
);
|
||||
}
|
||||
27
webapp/packages/chat-sdk/src/ShowCase/style.module.less
Normal file
27
webapp/packages/chat-sdk/src/ShowCase/style.module.less
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
webapp/packages/chat-sdk/src/ShowCase/type.ts
Normal file
9
webapp/packages/chat-sdk/src/ShowCase/type.ts
Normal 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,
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export type EntityDimensionType = {
|
||||
}
|
||||
|
||||
export type SqlInfoType = {
|
||||
llmParseSql: string;
|
||||
s2QL: string;
|
||||
logicSql: string;
|
||||
querySql: string;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type Props = {
|
||||
chatContext: ChatContextType;
|
||||
agentId?: number;
|
||||
entityAlias?: string;
|
||||
integrateSystem?: string;
|
||||
onFiltersChange: (filters: FilterItemType[]) => void;
|
||||
onSwitchEntity: (entityId: string) => void;
|
||||
};
|
||||
@@ -24,6 +25,7 @@ const FilterItem: React.FC<Props> = ({
|
||||
chatContext,
|
||||
agentId,
|
||||
entityAlias,
|
||||
integrateSystem,
|
||||
onFiltersChange,
|
||||
onSwitchEntity,
|
||||
}) => {
|
||||
@@ -50,7 +52,11 @@ const FilterItem: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
}, []);
|
||||
@@ -148,17 +154,24 @@ const FilterItem: React.FC<Props> = ({
|
||||
onSearch={debounceFetcher}
|
||||
notFoundContent={loading ? <Spin size="small" /> : null}
|
||||
onChange={onChange}
|
||||
mode={isArray(filter.value) ? 'multiple' : undefined}
|
||||
mode="multiple"
|
||||
showSearch
|
||||
allowClear
|
||||
/>
|
||||
) : entityAlias &&
|
||||
['歌曲', '艺人'].includes(entityAlias) &&
|
||||
filter.bizName?.includes('_id') ? (
|
||||
<SwicthEntity
|
||||
entityName={filter.value}
|
||||
chatContext={chatContext}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
/>
|
||||
<>
|
||||
<SwicthEntity
|
||||
entityName={filter.value}
|
||||
chatContext={chatContext}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
/>
|
||||
<span className={`${prefixCls}-switch-entity-tip`}>
|
||||
(如未匹配到相关{entityAlias},可点击{entityAlias === '艺人' ? '歌手' : entityAlias}
|
||||
ID切换)
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className={`${prefixCls}-filter-value`}>{filter.value}</span>
|
||||
)}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Props = {
|
||||
dimensionFilters: FilterItemType[];
|
||||
dateInfo: DateInfoType;
|
||||
entityInfo: EntityInfoType;
|
||||
integrateSystem?: string;
|
||||
onSelectParseInfo: (parseInfo: ChatContextType) => void;
|
||||
onSwitchEntity: (entityId: string) => void;
|
||||
onFiltersChange: (filters: FilterItemType[]) => void;
|
||||
@@ -35,6 +36,7 @@ const ParseTip: React.FC<Props> = ({
|
||||
dimensionFilters,
|
||||
dateInfo,
|
||||
entityInfo,
|
||||
integrateSystem,
|
||||
onSelectParseInfo,
|
||||
onSwitchEntity,
|
||||
onFiltersChange,
|
||||
@@ -115,14 +117,14 @@ const ParseTip: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div className={`${prefixCls}-tip-content`}>
|
||||
{!!agentType && queryMode !== 'DSL' ? (
|
||||
{!!agentType && queryMode !== 'LLM_S2QL' ? (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
将由{agentType === 'plugin' ? '插件' : '内置'}工具
|
||||
<span className={itemValueClass}>{agentName}</span>来解答
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{(queryMode?.includes('ENTITY') || queryMode === 'DSL') &&
|
||||
{(queryMode?.includes('ENTITY') || queryMode === 'LLM_S2QL') &&
|
||||
typeof entityId === 'string' &&
|
||||
!!entityAlias &&
|
||||
!!entityName ? (
|
||||
@@ -144,12 +146,14 @@ const ParseTip: React.FC<Props> = ({
|
||||
<div className={itemValueClass}>{metric.name}</div>
|
||||
</div>
|
||||
)}
|
||||
{['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'DSL'].includes(queryMode!) &&
|
||||
{['METRIC_GROUPBY', 'METRIC_ORDERBY', 'ENTITY_DETAIL', 'LLM_S2QL'].includes(
|
||||
queryMode!
|
||||
) &&
|
||||
fields &&
|
||||
fields.length > 0 && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>
|
||||
{queryMode === 'DSL'
|
||||
{queryMode === 'LLM_S2QL'
|
||||
? nativeQuery
|
||||
? '查询字段'
|
||||
: '下钻维度'
|
||||
@@ -178,12 +182,14 @@ const ParseTip: React.FC<Props> = ({
|
||||
<div className={itemValueClass}>{dimension.value}</div>
|
||||
</div>
|
||||
))}
|
||||
{queryMode === 'METRIC_ORDERBY' && aggType && aggType !== 'NONE' && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>聚合方式:</div>
|
||||
<div className={itemValueClass}>{AGG_TYPE_MAP[aggType]}</div>
|
||||
</div>
|
||||
)}
|
||||
{(queryMode === 'METRIC_ORDERBY' || queryMode === 'METRIC_MODEL') &&
|
||||
aggType &&
|
||||
aggType !== 'NONE' && (
|
||||
<div className={`${prefixCls}-tip-item`}>
|
||||
<div className={`${prefixCls}-tip-item-name`}>聚合方式:</div>
|
||||
<div className={itemValueClass}>{AGG_TYPE_MAP[aggType]}</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -218,6 +224,7 @@ const ParseTip: React.FC<Props> = ({
|
||||
chatContext={currentParseInfo!}
|
||||
entityAlias={entityAlias}
|
||||
agentId={agentId}
|
||||
integrateSystem={integrateSystem}
|
||||
onFiltersChange={onFiltersChange}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
key={filter.name}
|
||||
@@ -240,11 +247,6 @@ const ParseTip: React.FC<Props> = ({
|
||||
<div className={`${prefixCls}-tip`}>
|
||||
{getTipNode()}
|
||||
{getFiltersNode()}
|
||||
{(!type || queryMode === 'DSL') && entityAlias && entityAlias !== '厂牌' && entityName && (
|
||||
<div className={`${prefixCls}-switch-entity-tip`}>
|
||||
(如未匹配到相关{entityAlias},可点击{entityAlias === '艺人' ? '歌手' : entityAlias}ID切换)
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
|
||||
setSqlType('');
|
||||
};
|
||||
|
||||
if (!sqlInfo.llmParseSql && !sqlInfo.logicSql && !sqlInfo.querySql) {
|
||||
if (!sqlInfo.s2QL && !sqlInfo.logicSql && !sqlInfo.querySql) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -44,13 +44,13 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo }) => {
|
||||
)}
|
||||
</div>
|
||||
<div className={`${tipPrefixCls}-content-options`}>
|
||||
{sqlInfo.llmParseSql && (
|
||||
{sqlInfo.s2QL && (
|
||||
<div
|
||||
className={`${tipPrefixCls}-content-option ${
|
||||
sqlType === 'llmParseSql' ? `${tipPrefixCls}-content-option-active` : ''
|
||||
sqlType === 's2QL' ? `${tipPrefixCls}-content-option-active` : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSqlType(sqlType === 'llmParseSql' ? '' : 'llmParseSql');
|
||||
setSqlType(sqlType === 's2QL' ? '' : 's2QL');
|
||||
}}
|
||||
>
|
||||
LLM解析SQL
|
||||
|
||||
@@ -25,6 +25,7 @@ type Props = {
|
||||
conversationId?: number;
|
||||
modelId?: number;
|
||||
agentId?: number;
|
||||
score?: number;
|
||||
filter?: any[];
|
||||
isLastMessage?: boolean;
|
||||
msgData?: MsgDataType;
|
||||
@@ -33,7 +34,7 @@ type Props = {
|
||||
integrateSystem?: string;
|
||||
executeItemNode?: React.ReactNode;
|
||||
renderCustomExecuteNode?: boolean;
|
||||
onMsgDataLoaded?: (data: MsgDataType, valid: boolean) => void;
|
||||
onMsgDataLoaded?: (data: MsgDataType, valid: boolean, isRefresh?: boolean) => void;
|
||||
onUpdateMessageScroll?: () => void;
|
||||
onSendMsg?: (msg: string) => void;
|
||||
};
|
||||
@@ -43,6 +44,7 @@ const ChatItem: React.FC<Props> = ({
|
||||
conversationId,
|
||||
modelId,
|
||||
agentId,
|
||||
score,
|
||||
filter,
|
||||
isLastMessage,
|
||||
triggerResize,
|
||||
@@ -68,8 +70,6 @@ const ChatItem: React.FC<Props> = ({
|
||||
const [dateInfo, setDateInfo] = useState<DateInfoType>({} as DateInfoType);
|
||||
const [entityInfo, setEntityInfo] = useState<EntityInfoType>({} as EntityInfoType);
|
||||
|
||||
// const [chartIndex, setChartIndex] = useState(0);
|
||||
|
||||
const prefixCls = `${PREFIX_CLS}-item`;
|
||||
|
||||
const updateData = (res: Result<MsgDataType>) => {
|
||||
@@ -208,9 +208,7 @@ const ChatItem: React.FC<Props> = ({
|
||||
const resChatContext = res.data?.chatContext;
|
||||
const contextValue = { ...(resChatContext || chatContextValue), queryId };
|
||||
const dataValue = { ...res.data, chatContext: contextValue };
|
||||
if (onMsgDataLoaded) {
|
||||
onMsgDataLoaded(dataValue, true);
|
||||
}
|
||||
onMsgDataLoaded?.(dataValue, true, true);
|
||||
setData(dataValue);
|
||||
setParseInfo(contextValue);
|
||||
}
|
||||
@@ -230,7 +228,6 @@ const ChatItem: React.FC<Props> = ({
|
||||
} else {
|
||||
getEntityInfo(parseInfoValue);
|
||||
}
|
||||
onUpdateMessageScroll?.();
|
||||
};
|
||||
|
||||
const onSelectQuestion = (question: SimilarQuestionType) => {
|
||||
@@ -261,6 +258,7 @@ const ChatItem: React.FC<Props> = ({
|
||||
dimensionFilters={dimensionFilters}
|
||||
dateInfo={dateInfo}
|
||||
entityInfo={entityInfo}
|
||||
integrateSystem={integrateSystem}
|
||||
onSelectParseInfo={onSelectParseInfo}
|
||||
onSwitchEntity={onSwitchEntity}
|
||||
onFiltersChange={onFiltersChange}
|
||||
@@ -294,11 +292,11 @@ const ChatItem: React.FC<Props> = ({
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!isMetricCard && data && (
|
||||
{integrateSystem !== 'showcase' && (
|
||||
<Tools
|
||||
queryId={parseInfo?.queryId || 0}
|
||||
scoreValue={undefined}
|
||||
isLastMessage={isLastMessage}
|
||||
scoreValue={score}
|
||||
// isLastMessage={isLastMessage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -164,15 +164,6 @@
|
||||
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 {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -372,6 +363,16 @@
|
||||
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 {
|
||||
background-color: #f5f8fb !important;
|
||||
border-color: #ececec !important;
|
||||
|
||||
@@ -28,11 +28,7 @@ const MetricCard: React.FC<Props> = ({ data, loading, onApplyAuth }) => {
|
||||
const prefixCls = `${PREFIX_CLS}-metric-card`;
|
||||
|
||||
const matricCardClass = classNames(prefixCls, {
|
||||
[`${PREFIX_CLS}-metric-card-dsl`]: queryMode === 'DSL',
|
||||
});
|
||||
|
||||
const indicatorClass = classNames(`${prefixCls}-indicator`, {
|
||||
[`${prefixCls}-indicator-period-compare`]: metricInfos?.length > 0,
|
||||
[`${PREFIX_CLS}-metric-card-dsl`]: queryMode === 'LLM_S2QL',
|
||||
});
|
||||
|
||||
const [isNumber, setIsNumber] = useState(false);
|
||||
@@ -50,7 +46,7 @@ const MetricCard: React.FC<Props> = ({ data, loading, onApplyAuth }) => {
|
||||
)}
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
<div className={indicatorClass}>
|
||||
<div className={`${prefixCls}-indicator`}>
|
||||
{indicatorColumn && !indicatorColumn?.authorized ? (
|
||||
<ApplyAuth model={entityInfo?.modelInfo.name || ''} onApplyAuth={onApplyAuth} />
|
||||
) : (
|
||||
|
||||
@@ -55,12 +55,8 @@
|
||||
&-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&-indicator-period-compare {
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&-date-range {
|
||||
@@ -90,6 +86,7 @@
|
||||
column-gap: 40px;
|
||||
font-size: 13px;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&-period-compare-item {
|
||||
|
||||
@@ -13,6 +13,7 @@ import moment from 'moment';
|
||||
import { ColumnType } from '../../../common/type';
|
||||
import NoPermissionChart from '../NoPermissionChart';
|
||||
import classNames from 'classnames';
|
||||
import { isArray } from 'lodash';
|
||||
|
||||
type Props = {
|
||||
model?: string;
|
||||
@@ -83,7 +84,9 @@ const MetricTrendChart: React.FC<Props> = ({
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -1,11 +1,12 @@
|
||||
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 MetricTrendChart from './MetricTrendChart';
|
||||
import { Spin } from 'antd';
|
||||
import Table from '../Table';
|
||||
import MetricInfo from './MetricInfo';
|
||||
import DateOptions from '../DateOptions';
|
||||
import MultiMetricsTrendChart from './MultiMetricsTrendChart';
|
||||
|
||||
type Props = {
|
||||
data: MsgDataType;
|
||||
@@ -13,6 +14,7 @@ type Props = {
|
||||
triggerResize?: boolean;
|
||||
loading: boolean;
|
||||
activeMetricField?: FieldType;
|
||||
drillDownDimension?: DrillDownDimensionType;
|
||||
currentDateOption?: number;
|
||||
onApplyAuth?: (model: string) => void;
|
||||
onSelectDateOption: (value: number) => void;
|
||||
@@ -24,6 +26,7 @@ const MetricTrend: React.FC<Props> = ({
|
||||
triggerResize,
|
||||
loading,
|
||||
activeMetricField,
|
||||
drillDownDimension,
|
||||
currentDateOption,
|
||||
onApplyAuth,
|
||||
onSelectDateOption,
|
||||
@@ -36,6 +39,7 @@ const MetricTrend: React.FC<Props> = ({
|
||||
const dateColumnName = dateField?.nameEn || '';
|
||||
const categoryColumnName =
|
||||
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');
|
||||
|
||||
@@ -48,19 +52,23 @@ const MetricTrend: React.FC<Props> = ({
|
||||
return (
|
||||
<div className={prefixCls}>
|
||||
<div className={`${prefixCls}-charts`}>
|
||||
<div className={`${prefixCls}-top-bar`}>
|
||||
<div
|
||||
className={`${prefixCls}-metric-fields ${prefixCls}-metric-field-single`}
|
||||
key={activeMetricField?.bizName}
|
||||
>
|
||||
{activeMetricField?.name}
|
||||
{metricFields?.length === 1 && (
|
||||
<div className={`${prefixCls}-top-bar`}>
|
||||
<div
|
||||
className={`${prefixCls}-metric-fields ${prefixCls}-metric-field-single`}
|
||||
key={activeMetricField?.bizName}
|
||||
>
|
||||
{activeMetricField?.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Spin spinning={loading}>
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{!isMobile && aggregateInfo?.metricInfos?.length > 0 && (
|
||||
<MetricInfo aggregateInfo={aggregateInfo} currentMetricField={currentMetricField} />
|
||||
)}
|
||||
{!isMobile &&
|
||||
aggregateInfo?.metricInfos?.length > 0 &&
|
||||
drillDownDimension === undefined && (
|
||||
<MetricInfo aggregateInfo={aggregateInfo} currentMetricField={currentMetricField} />
|
||||
)}
|
||||
<DateOptions
|
||||
chatContext={chatContext}
|
||||
currentDateOption={currentDateOption}
|
||||
@@ -68,6 +76,13 @@ const MetricTrend: React.FC<Props> = ({
|
||||
/>
|
||||
{queryResults?.length === 1 || chartIndex % 2 === 1 ? (
|
||||
<Table data={{ ...data, queryResults }} onApplyAuth={onApplyAuth} />
|
||||
) : metricFields.length > 1 ? (
|
||||
<MultiMetricsTrendChart
|
||||
dateColumnName={dateColumnName}
|
||||
metricFields={metricFields}
|
||||
resultList={queryResults}
|
||||
triggerResize={triggerResize}
|
||||
/>
|
||||
) : (
|
||||
<MetricTrendChart
|
||||
model={entityInfo?.modelInfo.name}
|
||||
|
||||
@@ -59,11 +59,9 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
|
||||
const metricFields = columns.filter(item => item.showType === 'NUMBER');
|
||||
|
||||
const isDslMetricCard =
|
||||
queryMode === 'DSL' && singleData && metricFields.length === 1 && columns.length === 1;
|
||||
queryMode === 'LLM_S2QL' && singleData && metricFields.length === 1 && columns.length === 1;
|
||||
|
||||
const isMetricCard =
|
||||
(queryMode.includes('METRIC') || isDslMetricCard) &&
|
||||
(singleData || chatContext?.dateInfo?.startDate === chatContext?.dateInfo?.endDate);
|
||||
const isMetricCard = (queryMode.includes('METRIC') || isDslMetricCard) && singleData;
|
||||
|
||||
const isText =
|
||||
columns.length === 1 &&
|
||||
@@ -95,24 +93,27 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
|
||||
if (isTable) {
|
||||
return <Table data={{ ...data, queryColumns: columns, queryResults: dataSource }} />;
|
||||
}
|
||||
if (dateField && metricFields.length > 0) {
|
||||
if (!dataSource.every(item => item[dateField.nameEn] === dataSource[0][dateField.nameEn])) {
|
||||
return (
|
||||
<MetricTrend
|
||||
data={{
|
||||
...data,
|
||||
queryColumns: columns,
|
||||
queryResults: dataSource,
|
||||
}}
|
||||
loading={loading}
|
||||
chartIndex={chartIndex}
|
||||
triggerResize={triggerResize}
|
||||
activeMetricField={activeMetricField}
|
||||
currentDateOption={currentDateOption}
|
||||
onSelectDateOption={selectDateOption}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (
|
||||
dateField &&
|
||||
metricFields.length > 0 &&
|
||||
!dataSource.every(item => item[dateField.nameEn] === dataSource[0][dateField.nameEn])
|
||||
) {
|
||||
return (
|
||||
<MetricTrend
|
||||
data={{
|
||||
...data,
|
||||
queryColumns: columns,
|
||||
queryResults: dataSource,
|
||||
}}
|
||||
loading={loading}
|
||||
chartIndex={chartIndex}
|
||||
triggerResize={triggerResize}
|
||||
activeMetricField={activeMetricField}
|
||||
drillDownDimension={drillDownDimension}
|
||||
currentDateOption={currentDateOption}
|
||||
onSelectDateOption={selectDateOption}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (categoryField?.length > 0 && metricFields?.length > 0) {
|
||||
return (
|
||||
@@ -209,7 +210,11 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
|
||||
<div>
|
||||
{getMsgContent()}
|
||||
{(isMultipleMetric || existDrillDownDimension) && (
|
||||
<div className={`${prefixCls}-bottom-tools`}>
|
||||
<div
|
||||
className={`${prefixCls}-bottom-tools ${
|
||||
isMetricCard ? `${prefixCls}-metric-card-tools` : ''
|
||||
}`}
|
||||
>
|
||||
{isMultipleMetric && (
|
||||
<MetricOptions
|
||||
metrics={chatContext.metrics}
|
||||
@@ -221,6 +226,7 @@ const ChatMsg: React.FC<Props> = ({ queryId, data, chartIndex, triggerResize })
|
||||
{existDrillDownDimension && (
|
||||
<DrillDownDimensions
|
||||
modelId={chatContext.modelId}
|
||||
metricId={activeMetricField?.id || defaultMetricField?.id}
|
||||
drillDownDimension={drillDownDimension}
|
||||
originDimensions={chatContext.dimensions}
|
||||
dimensionFilters={chatContext.dimensionFilters}
|
||||
|
||||
@@ -14,5 +14,10 @@
|
||||
column-gap: 20px;
|
||||
font-size: 14px;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&-metric-card-tools {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import classNames from 'classnames';
|
||||
|
||||
type Props = {
|
||||
modelId: number;
|
||||
metricId?: number;
|
||||
drillDownDimension?: DrillDownDimensionType;
|
||||
isMetricCard?: boolean;
|
||||
originDimensions?: DrillDownDimensionType[];
|
||||
@@ -17,8 +18,11 @@ type Props = {
|
||||
|
||||
const MAX_DIMENSION_COUNT = 20;
|
||||
|
||||
const DEFAULT_DIMENSION_COUNT = 5;
|
||||
|
||||
const DrillDownDimensions: React.FC<Props> = ({
|
||||
modelId,
|
||||
metricId,
|
||||
drillDownDimension,
|
||||
isMetricCard,
|
||||
originDimensions,
|
||||
@@ -27,12 +31,10 @@ const DrillDownDimensions: React.FC<Props> = ({
|
||||
}) => {
|
||||
const [dimensions, setDimensions] = useState<DrillDownDimensionType[]>([]);
|
||||
|
||||
const DEFAULT_DIMENSION_COUNT = isMetricCard ? 3 : 5;
|
||||
|
||||
const prefixCls = `${CLS_PREFIX}-drill-down-dimensions`;
|
||||
|
||||
const initData = async () => {
|
||||
const res = await queryDrillDownDimensions(modelId);
|
||||
const res = await queryDrillDownDimensions(modelId, metricId);
|
||||
setDimensions(
|
||||
res.data.dimensions
|
||||
.filter(
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
.@{drill-down-dimensions-prefix-cls} {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 2px;
|
||||
|
||||
&-section {
|
||||
width: 100%;
|
||||
@@ -13,7 +12,6 @@
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
&-metric-card {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
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;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 6px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
&-metric-card {
|
||||
|
||||
@@ -35,7 +35,7 @@ const Tools: React.FC<Props> = ({ queryId, scoreValue, isLastMessage }) => {
|
||||
|
||||
return (
|
||||
<div className={prefixCls}>
|
||||
{!isMobile && isLastMessage && (
|
||||
{!isMobile && (
|
||||
<div className={`${prefixCls}-feedback`}>
|
||||
<div>这个回答正确吗?</div>
|
||||
<LikeOutlined className={likeClass} onClick={like} />
|
||||
|
||||
@@ -5,7 +5,7 @@ import './styles/index.less';
|
||||
// import ChatDemo from './demo/ChatDemo';
|
||||
// import CopilotDemo from './demo/CopilotDemo';
|
||||
// const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||
// root.render(<Chat />);
|
||||
// root.render(<ChatDemo />);
|
||||
|
||||
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 ShowCase } from './ShowCase';
|
||||
|
||||
export type {
|
||||
SearchRecommendItem,
|
||||
FieldType,
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getToken } from '../utils/utils';
|
||||
// 创建axios实例
|
||||
const axiosInstance: AxiosInstance = axios.create({
|
||||
// 设置基本URL,所有请求都会使用这个URL作为前缀
|
||||
baseURL: localStorage.getItem('SUPERSONIC_CHAT_API_URL') || '',
|
||||
baseURL: '',
|
||||
// 设置请求超时时间(毫秒)
|
||||
timeout: 120000,
|
||||
// 设置请求头
|
||||
@@ -19,7 +19,6 @@ axiosInstance.interceptors.request.use(
|
||||
(config: any) => {
|
||||
const token = getToken();
|
||||
if (token && config?.headers) {
|
||||
// config.headers.Auth = `Bearer ${token}`;
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
|
||||
@@ -77,8 +77,8 @@ export function updateQAFeedback(questionId: number, score: number) {
|
||||
return axios.post<any>(`${prefix}/chat/manage/updateQAFeedback?id=${questionId}&score=${score}&feedback=`);
|
||||
}
|
||||
|
||||
export function queryDrillDownDimensions(modelId: number) {
|
||||
return axios.get<{ dimensions: DrillDownDimensionType[] }>(`${prefix}/chat/recommend/metric/${modelId}`);
|
||||
export function queryDrillDownDimensions(modelId: number, metricId?: number) {
|
||||
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) {
|
||||
@@ -86,7 +86,7 @@ export function queryDimensionValues(modelId: number, bizName: string, agentId:
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -113,7 +113,7 @@ const ToolModal: React.FC<Props> = ({ editTool, onSaveTool, onCancel }) => {
|
||||
<FormItem name="name" label="名称">
|
||||
<Input placeholder="请输入工具名称" />
|
||||
</FormItem>
|
||||
{(toolType === AgentToolTypeEnum.RULE || toolType === AgentToolTypeEnum.DSL) && (
|
||||
{(toolType === AgentToolTypeEnum.RULE || toolType === AgentToolTypeEnum.LLM_S2QL) && (
|
||||
<FormItem name="modelIds" label="主题域">
|
||||
<Select
|
||||
options={modelList.map((model) => ({ label: model.name, value: model.id }))}
|
||||
@@ -122,7 +122,7 @@ const ToolModal: React.FC<Props> = ({ editTool, onSaveTool, onCancel }) => {
|
||||
/>
|
||||
</FormItem>
|
||||
)}
|
||||
{toolType === AgentToolTypeEnum.DSL && (
|
||||
{toolType === AgentToolTypeEnum.LLM_S2QL && (
|
||||
<FormItem name="exampleQuestions" label="示例问题">
|
||||
<div className={styles.paramsSection}>
|
||||
{examples.map((example) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ export type MetricOptionType = {
|
||||
|
||||
export enum AgentToolTypeEnum {
|
||||
RULE = 'RULE',
|
||||
DSL = 'DSL',
|
||||
LLM_S2QL = 'LLM_S2QL',
|
||||
PLUGIN = 'PLUGIN',
|
||||
INTERPRET = 'INTERPRET'
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export const AGENT_TOOL_TYPE_LIST = [
|
||||
},
|
||||
{
|
||||
label: '大模型语义解析',
|
||||
value: AgentToolTypeEnum.DSL
|
||||
value: AgentToolTypeEnum.LLM_S2QL
|
||||
},
|
||||
{
|
||||
label: '大模型指标解读',
|
||||
|
||||
@@ -216,7 +216,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
||||
}))}
|
||||
onChange={(value) => {
|
||||
setPluginType(value);
|
||||
if (value === PluginTypeEnum.DSL) {
|
||||
if (value === PluginTypeEnum.LLM_S2QL) {
|
||||
form.setFieldsValue({ parseMode: ParseModeEnum.FUNCTION_CALL });
|
||||
setFunctionParams([
|
||||
{
|
||||
@@ -243,7 +243,7 @@ const DetailModal: React.FC<Props> = ({ detail, onSubmit, onCancel }) => {
|
||||
<FormItem name="pattern" label="函数描述">
|
||||
<TextArea placeholder="请输入函数描述,多个描述换行分隔" allowClear />
|
||||
</FormItem>
|
||||
{/* <FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.DSL}>
|
||||
{/* <FormItem name="params" label="函数参数" hidden={pluginType === PluginTypeEnum.LLM_S2QL}>
|
||||
<div className={styles.paramsSection}>
|
||||
{functionParams.map((functionParam: FunctionParamFormItemType) => {
|
||||
const { id, name, type, description } = functionParam;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export const PLUGIN_TYPE_MAP = {
|
||||
WEB_PAGE: 'Web页面',
|
||||
WEB_SERVICE: 'Web服务',
|
||||
// DSL: 'LLM语义解析',
|
||||
// LLM_S2QL: 'LLM语义解析',
|
||||
// CONTENT_INTERPRET: '内容解读',
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export type PluginConfigType = {
|
||||
export enum PluginTypeEnum {
|
||||
WEB_PAGE = 'WEB_PAGE',
|
||||
WEB_SERVICE = 'WEB_SERVICE',
|
||||
DSL = 'DSL'
|
||||
LLM_S2QL = 'LLM_S2QL'
|
||||
}
|
||||
|
||||
export enum ParseModeEnum {
|
||||
|
||||
2
webapp/pnpm-lock.yaml
generated
2
webapp/pnpm-lock.yaml
generated
@@ -430,7 +430,7 @@ importers:
|
||||
specifier: '3.5'
|
||||
version: 3.5.41(react-router@5.3.4)
|
||||
umi-request:
|
||||
specifier: ^1.0.8
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
devDependencies:
|
||||
'@ant-design/pro-cli':
|
||||
|
||||
Reference in New Issue
Block a user