From fbbfaddd85109f2f42eef0dfb2536f517daa6d16 Mon Sep 17 00:00:00 2001 From: keting lu Date: Tue, 4 Mar 2025 20:03:56 +0800 Subject: [PATCH] feat: APO node select quickly --- web/app/components/workflow/apo/constant.ts | 8 +- .../apo/tool-preview/apo-tools-preview.tsx | 152 ++++++++++++++++++ .../apo/tool-preview/parameters-info.tsx | 35 ++++ .../apo/tool-preview/tool-trial-run.tsx | 42 +++++ .../workflow/block-selector/apoTools.tsx | 70 ++++---- .../components/before-run-form/form-item.tsx | 19 ++- .../_base/components/before-run-form/form.tsx | 5 +- web/i18n/en-US/apo.ts | 10 ++ web/i18n/zh-Hans/apo.ts | 10 ++ 9 files changed, 299 insertions(+), 52 deletions(-) create mode 100644 web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx create mode 100644 web/app/components/workflow/apo/tool-preview/parameters-info.tsx create mode 100644 web/app/components/workflow/apo/tool-preview/tool-trial-run.tsx diff --git a/web/app/components/workflow/apo/constant.ts b/web/app/components/workflow/apo/constant.ts index a0a39834b5..3d8db6a224 100644 --- a/web/app/components/workflow/apo/constant.ts +++ b/web/app/components/workflow/apo/constant.ts @@ -2,14 +2,14 @@ import type { ApoToolTypeInfo } from './types' export const initApoToolsEntry: Record = { select: { - label: 'APO异常检测', - description: 'APO异常检测分析描述', + label: 'APO平台查询可观测性数据', + description: '查询APO平台可观测性数据, 用于进一步分析', icon: '', type: 'select', }, analysis: { - label: 'APO查询检测', - description: 'APO查询检测', + label: 'APO数据异常检测&关联', + description: '输入可观测性数据, 通过相关算法识别出现异常数据、或者直接对数据进行关联汇总。', icon: '', type: 'analysis', }, diff --git a/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx b/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx new file mode 100644 index 0000000000..66663abacd --- /dev/null +++ b/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx @@ -0,0 +1,152 @@ +import { memo, useCallback, useEffect, useState } from 'react' +import { BlockEnum } from '../../types' +import type { ToolDefaultValue } from '../../block-selector/types' +import type { ApoToolTypeInfo } from '../types' +import Input from '../../../base/input' +import { fetchApoTools } from '@/service/tools' +import { useTranslation } from 'react-i18next' +import ToolTrialRun from './tool-trial-run' +import ParametersInfo from './parameters-info' +import cn from '@/utils/classnames' +import { debounce } from 'lodash-es' +import { useGetLanguage } from '@/context/i18n' + +type ApoToolsPreviewProps = { + onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void; + apoToolType: ApoToolTypeInfo | null; + hidePopover: any +} + +const ApoToolsPreview = ({ onSelect, apoToolType, hidePopover }: ApoToolsPreviewProps) => { + const language = useGetLanguage() + const { t } = useTranslation() + const [toolDetail, setToolDetail] = useState() + const [tools, setTools] = useState([]) + const [provider, setProvider] = useState() + const [searchText, setSearchText] = useState() + const getAllTools = async () => { + const tools = await fetchApoTools(apoToolType, searchText) + // setSearchText('') + setProvider(tools[0]) + setTools(tools[0]?.tools) + } + const handlePopoverOpen = useCallback((item) => { + setToolDetail(item) + }, []) + + const convertMetricsListToMenuItems = useCallback(() => { + return tools.map((item, index) => ( +
handlePopoverOpen(item)} + onClick={() => { + const params: Record = {} + if (item.parameters) { + item.parameters.forEach((param) => { + params[param.name] = '' + }) + } + onSelect(BlockEnum.Tool, { + provider_id: provider.id, + provider_type: provider.type, + provider_name: provider.name, + tool_name: item.name, + tool_label: item.label[language], + title: item.label[language], + is_team_authorization: provider.is_team_authorization, + output_schema: item.output_schema, + paramSchemas: item.parameters, + params, + }) + hidePopover() + }} + > +
+ {/* */} +
+ {item.label[language]} +
+
+
+ )) + }, [tools, handlePopoverOpen, toolDetail]) + + useEffect(() => { + const fetchTools = debounce(() => { + if (apoToolType) getAllTools() + }, 500) // 500ms 防抖 + + fetchTools() + + return () => { + fetchTools.cancel() // 组件卸载时清理 + } + }, [apoToolType, searchText]) + return ( +
+
+ {/* left */} +
+
+ setSearchText(e.target.value)} + /> +
+
+ {tools?.length > 0 && convertMetricsListToMenuItems()} +
+
+
+ {toolDetail && ( +
+
{toolDetail.label[language]}
+
+
+ 描述:<>{toolDetail.description[language]} +
+
+ {apoToolType === 'select' ? ( + <> + {['cpu', 'network', 'memory'].includes(toolDetail?.display.type) &&
单位:{toolDetail.display.unit}
} + {toolDetail?.display?.type &&
输出:{t(`apo.displayType.${toolDetail?.display?.type}`)}
} +
+
+
+
+ +
+ + + ) : ( +
+ 输入参数: +
+ {toolDetail?.parameters.map(parameter => ( + + ))} +
+
+ )} +
+
+
+ )} +
+
+
+ ) +} + +export default memo(ApoToolsPreview) diff --git a/web/app/components/workflow/apo/tool-preview/parameters-info.tsx b/web/app/components/workflow/apo/tool-preview/parameters-info.tsx new file mode 100644 index 0000000000..42ac2c65fc --- /dev/null +++ b/web/app/components/workflow/apo/tool-preview/parameters-info.tsx @@ -0,0 +1,35 @@ +import { memo, useContext } from 'react' +import I18n from '@/context/i18n' +import { getLanguage } from '@/i18n/language' +import { useTranslation } from 'react-i18next' +const ParametersInfo = ({ parameter }) => { + const { locale } = useContext(I18n) + const language = getLanguage(locale) + const { t } = useTranslation() + const getType = (type: string) => { + if (type === 'number-input') + return t('tools.setBuiltInTools.number') + if (type === 'text-input') + return t('tools.setBuiltInTools.string') + if (type === 'file') + return t('tools.setBuiltInTools.file') + return type + } + return
+
+
{parameter.label[language]}
+
+ {getType(parameter.type)} +
+ {parameter.required && ( +
{t('tools.setBuiltInTools.required')}
+ )} +
+ {parameter.human_description && ( +
+ {parameter.human_description?.[language]} +
+ )} +
+} +export default memo(ParametersInfo) diff --git a/web/app/components/workflow/apo/tool-preview/tool-trial-run.tsx b/web/app/components/workflow/apo/tool-preview/tool-trial-run.tsx new file mode 100644 index 0000000000..00faa8bfa7 --- /dev/null +++ b/web/app/components/workflow/apo/tool-preview/tool-trial-run.tsx @@ -0,0 +1,42 @@ +import { memo, useEffect, useState } from 'react' + +import Form from '../../nodes/_base/components/before-run-form/form' +import { InputVarType } from '../../types' +import ParametersInfo from './parameters-info' +import { useTranslation } from 'react-i18next' +import { useGetLanguage } from '@/context/i18n' +const varTypeToInputVarType = (type: string) => { + if(type === 'string') + return InputVarType.textInput + else if (type === 'file') + return InputVarType.singleFile + return type +} +const ToolTrialRun = ({ infoSchemas }) => { + const [formValues, setFormValues] = useState(null) + const [formInputs, setFormInputs] = useState([]) + const language = useGetLanguage() + const { t } = useTranslation() + useEffect(() => { + const formValues = infoSchemas?.reduce((acc, item) => { + acc[item.name] = null + return acc + }, {}) + const formInputs = infoSchemas.map(item => ({ + // label: formLable(item), + label: item.label[language], + require: item.require, + type: varTypeToInputVarType(item.type), + variable: item.name, + customLabel: , + })) + setFormValues(formValues) + setFormInputs(formInputs) + }, [infoSchemas]) + return <> +
{t('tools.setBuiltInTools.parameters')}
+
setFormValues(newValues) } + >
+ +} +export default memo(ToolTrialRun) diff --git a/web/app/components/workflow/block-selector/apoTools.tsx b/web/app/components/workflow/block-selector/apoTools.tsx index 5b1b250d05..ab5ec32bc5 100644 --- a/web/app/components/workflow/block-selector/apoTools.tsx +++ b/web/app/components/workflow/block-selector/apoTools.tsx @@ -1,70 +1,64 @@ -import Tooltip from '@/app/components/base/tooltip' -import BlockIcon from '../block-icon' -import { BlockEnum } from '../types' +import type { BlockEnum } from '../types' import cn from '@/utils/classnames' import { initApoToolsEntry } from '../apo/constant' -import AINodeRecommend from '@/app/components/workflow/apo/ai-node-recommend' import { useState } from 'react' import type { ToolDefaultValue } from './types' +import { Popover } from 'antd' +import ApoToolsPreview from '../apo/tool-preview/apo-tools-preview' +import type { ApoToolTypeInfo } from '../apo/types' type APOToolsProps = { searchText: string; onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void } const APOTools = ({ searchText, onSelect }: APOToolsProps) => { - const [apoToolType, setApoToolType] = useState() - const [showRecommendModal, setShowRecommendModal] = useState() - const handleSelect = (type: BlockEnum) => { - onSelect(type) - setShowRecommendModal(false) + const [openKey, setOpenKey] = useState({ + select: false, + analysis: false, + }) + const hide = (key: ApoToolTypeInfo) => { + setOpenKey(prev => ({ + ...prev, + [key]: false, + })) + } + + const handleOpenChange = (key: ApoToolTypeInfo, newOpen: boolean) => { + setOpenKey(prev => ({ + ...prev, + [key]: newOpen, + })) } return ( <>
{Object.entries(initApoToolsEntry).map(([key, tool]) => ( - - -
- {tool.label} -
-
- {tool.description} -
- {/*
{tool.label[language]}
-
{tool.description[language]}
*/} -
+ placement='rightTop' + trigger={['click']} + open={openKey[key]} + onOpenChange={newOpen => handleOpenChange(key, newOpen)} + content={ + hide(key)}/> } >
{ - setApoToolType(tool.type) - setShowRecommendModal(true) - }} > - + /> */}
{tool.label}
- + ))}
- { setApoToolType(null) setShowRecommendModal(false) }} - /> + /> */} ) } diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index a4885759c4..1f1e12fd43 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -1,5 +1,5 @@ 'use client' -import type { FC } from 'react' +import type { FC, ReactNode } from 'react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' import produce from 'immer' @@ -25,13 +25,14 @@ import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import cn from '@/utils/classnames' -interface Props { +type Props = { payload: InputVar value: any onChange: (value: any) => void className?: string autoFocus?: boolean inStepRun?: boolean + customLabel?: ReactNode } const FormItem: FC = ({ @@ -41,6 +42,7 @@ const FormItem: FC = ({ className, autoFocus, inStepRun = false, + customLabel, }) => { const { t } = useTranslation() const { type } = payload @@ -96,12 +98,13 @@ const FormItem: FC = ({ const isIterator = type === InputVarType.iterator return (
- {!isArrayLikeType && ( -
-
{typeof payload.label === 'object' ? nodeKey : payload.label}
- {!payload.required && {t('workflow.panel.optional')}} -
- )} + { + customLabel || <>{!isArrayLikeType && ( +
+
{typeof payload.label === 'object' ? nodeKey : payload.label}
+ {!payload.required && {t('workflow.panel.optional')}} +
+ )}}
{ type === InputVarType.textInput && ( diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx index e9ac442680..b441429aa1 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx @@ -9,10 +9,10 @@ import { InputVarType } from '@/app/components/workflow/types' import AddButton from '@/app/components/base/button/add-button' import { RETRIEVAL_OUTPUT_STRUCT } from '@/app/components/workflow/constants' -export interface Props { +export type Props = { className?: string label?: string - inputs: InputVar[] + inputs: InputVar[] | any values: Record onChange: (newValues: Record) => void } @@ -85,6 +85,7 @@ const Form: FC = ({ payload={input} value={values[input.variable]} onChange={handleChange(input.variable)} + customLabel={input.customLabel} /> ) })} diff --git a/web/i18n/en-US/apo.ts b/web/i18n/en-US/apo.ts index 94c040d851..ed5a720b78 100644 --- a/web/i18n/en-US/apo.ts +++ b/web/i18n/en-US/apo.ts @@ -6,6 +6,16 @@ const translation = { chart: { chartTitle: 'Chart Display', }, + displayType: { + cpu: 'CPU metrics', + network: 'Network metrics', + memory: 'Memory metrics', + topology: 'Topology structure', + alert: 'Alert events', + log: 'Log data', + custom: 'Custom query return data', + }, + } export default translation diff --git a/web/i18n/zh-Hans/apo.ts b/web/i18n/zh-Hans/apo.ts index a991ea9a89..6cb3c99e77 100644 --- a/web/i18n/zh-Hans/apo.ts +++ b/web/i18n/zh-Hans/apo.ts @@ -6,6 +6,16 @@ const translation = { chart: { chartTitle: '图表渲染', }, + displayType: { + cpu: 'CPU指标', + network: '网络指标', + memory: '内存指标', + topology: '拓扑结构', + alert: '告警事件', + log: '日志数据', + custom: '自定义查询返回数据', + }, + } export default translation