feat(web): workflow variable type
This commit is contained in:
@@ -2,12 +2,13 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:29:41
|
* @Date: 2026-02-03 16:29:41
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-26 15:24:41
|
* @Last Modified time: 2026-04-10 17:02:07
|
||||||
*/
|
*/
|
||||||
import { type FC, useState, useEffect, useRef } from 'react';
|
import { type FC, useState, useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Space, Input, Form, App, Flex } from 'antd';
|
import { Space, Input, Form, App, Flex } from 'antd';
|
||||||
|
import copy from 'copy-to-clipboard';
|
||||||
|
|
||||||
import Tag, { type TagProps } from './components/Tag'
|
import Tag, { type TagProps } from './components/Tag'
|
||||||
import RbCard from '@/components/RbCard/Card'
|
import RbCard from '@/components/RbCard/Card'
|
||||||
@@ -15,8 +16,9 @@ import { getReleaseList, rollbackRelease, appExport } from '@/api/application'
|
|||||||
import ReleaseModal from './components/ReleaseModal'
|
import ReleaseModal from './components/ReleaseModal'
|
||||||
import ReleaseShareModal from './components/ReleaseShareModal'
|
import ReleaseShareModal from './components/ReleaseShareModal'
|
||||||
import AppSharingModal from './components/AppSharingModal'
|
import AppSharingModal from './components/AppSharingModal'
|
||||||
import type { Release, ReleaseModalRef, ReleaseShareModalRef, AppSharingModalRef } from './types'
|
import type { Release, ReleaseModalRef, ReleaseShareModalRef, AppSharingModalRef, WorkflowRef } from './types'
|
||||||
import type { Application } from '@/views/ApplicationManagement/types'
|
import type { Application } from '@/views/ApplicationManagement/types'
|
||||||
|
import { runCheckOnGraph } from '@/views/Workflow/components/CheckList'
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
import { formatDateTime } from '@/utils/format';
|
import { formatDateTime } from '@/utils/format';
|
||||||
import Markdown from '@/components/Markdown'
|
import Markdown from '@/components/Markdown'
|
||||||
@@ -37,7 +39,7 @@ const heightClass = 'rb:max-h-[calc(100vh-140px)]'
|
|||||||
* @param data - Application data
|
* @param data - Application data
|
||||||
* @param refresh - Function to refresh application data
|
* @param refresh - Function to refresh application data
|
||||||
*/
|
*/
|
||||||
const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refresh}) => {
|
const ReleasePage: FC<{data: Application; refresh: () => void; workflowRef?: React.RefObject<WorkflowRef>}> = ({data, refresh, workflowRef}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { message } = App.useApp()
|
const { message } = App.useApp()
|
||||||
const releaseModalRef = useRef<ReleaseModalRef>(null)
|
const releaseModalRef = useRef<ReleaseModalRef>(null)
|
||||||
@@ -75,6 +77,10 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres
|
|||||||
if (!selectedVersion) return
|
if (!selectedVersion) return
|
||||||
appExport(data.id, data.name, { release_id: selectedVersion.id})
|
appExport(data.id, data.name, { release_id: selectedVersion.id})
|
||||||
}
|
}
|
||||||
|
const handleCopy = (id: string) => {
|
||||||
|
copy(id)
|
||||||
|
message.success(t('common.copySuccess'))
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Flex gap={12}>
|
<Flex gap={12}>
|
||||||
<div className="rb:w-101 rb:h-full">
|
<div className="rb:w-101 rb:h-full">
|
||||||
@@ -102,7 +108,7 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres
|
|||||||
</Tag>}
|
</Tag>}
|
||||||
</>}
|
</>}
|
||||||
className={clsx("rb:hover:shadow-[0px_2px_8px_0px_rgba(0,0,0,0.2)]! rb:cursor-pointer rb:bg-white", {
|
className={clsx("rb:hover:shadow-[0px_2px_8px_0px_rgba(0,0,0,0.2)]! rb:cursor-pointer rb:bg-white", {
|
||||||
'rb:border-[#171719]!': version.id === selectedVersion.id,
|
'rb:border! rb:border-[#171719]!': version.id === selectedVersion.id,
|
||||||
'rb:border-[#DFE4ED] ': version.id !== selectedVersion.id
|
'rb:border-[#DFE4ED] ': version.id !== selectedVersion.id
|
||||||
})}
|
})}
|
||||||
headerType="borderless"
|
headerType="borderless"
|
||||||
@@ -140,13 +146,33 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres
|
|||||||
<RbButton type="primary" ghost onClick={() => releaseShareModalRef.current?.handleOpen()}>{t('application.share')}</RbButton>
|
<RbButton type="primary" ghost onClick={() => releaseShareModalRef.current?.handleOpen()}>{t('application.share')}</RbButton>
|
||||||
{data?.type !== 'multi_agent' && <RbButton type="primary" ghost onClick={() => appSharingModalRef.current?.handleOpen()}>{t('application.sharing')}</RbButton>}
|
{data?.type !== 'multi_agent' && <RbButton type="primary" ghost onClick={() => appSharingModalRef.current?.handleOpen()}>{t('application.sharing')}</RbButton>}
|
||||||
</>}
|
</>}
|
||||||
<RbButton type="primary" onClick={() => releaseModalRef.current?.handleOpen()}>{t('application.release')}</RbButton>
|
<RbButton type="primary" onClick={async () => {
|
||||||
|
if (data?.type === 'workflow') {
|
||||||
|
const graph = workflowRef?.current?.graphRef?.current
|
||||||
|
if (graph) {
|
||||||
|
const errors = await runCheckOnGraph(graph, t)
|
||||||
|
if (errors.length) {
|
||||||
|
message.error(t('workflow.checkListHasErrors'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
releaseModalRef.current?.handleOpen()
|
||||||
|
}}>{t('application.release')}</RbButton>
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</Flex>
|
||||||
{selectedVersion &&
|
{selectedVersion &&
|
||||||
<Flex gap={16} vertical className={`${heightClass} rb:overflow-y-auto`}>
|
<Flex gap={16} vertical className={`${heightClass} rb:overflow-y-auto`}>
|
||||||
<RbCard
|
<RbCard
|
||||||
title={t('application.VersionInformation')}
|
title={() => <Flex>{t('application.VersionInformation')}
|
||||||
|
<Flex align="center" className="rb:text-[#5B6167] rb:text-[12px]">
|
||||||
|
(ID: {selectedVersion.id}
|
||||||
|
<div className="rb:size-4.5 rb:ml-1 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/common/copy_dark.svg')]"
|
||||||
|
onClick={() => handleCopy(selectedVersion.id)}
|
||||||
|
></div>
|
||||||
|
)
|
||||||
|
</Flex>
|
||||||
|
</Flex>}
|
||||||
headerType="borderless"
|
headerType="borderless"
|
||||||
>
|
>
|
||||||
<div className="rb:grid rb:grid-cols-3 rb:gap-4">
|
<div className="rb:grid rb:grid-cols-3 rb:gap-4">
|
||||||
|
|||||||
@@ -38,13 +38,6 @@ const EditableTable: FC<EditableTableProps> = ({
|
|||||||
...(typeOptions.length > 0 && { type: typeOptions[0].value })
|
...(typeOptions.length > 0 && { type: typeOptions[0].value })
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter options based on boolean type if needed
|
|
||||||
const booleanFilterOptions = useMemo(() => {
|
|
||||||
return filterBooleanType
|
|
||||||
? options.filter(option => option.dataType !== 'boolean')
|
|
||||||
: options
|
|
||||||
}, [options, filterBooleanType])
|
|
||||||
|
|
||||||
const namefilterOptions = useMemo(() => {
|
const namefilterOptions = useMemo(() => {
|
||||||
const filterList: Suggestion[] = [];
|
const filterList: Suggestion[] = [];
|
||||||
options.forEach(vo => {
|
options.forEach(vo => {
|
||||||
@@ -76,7 +69,7 @@ const EditableTable: FC<EditableTableProps> = ({
|
|||||||
...vo,
|
...vo,
|
||||||
disabled: true
|
disabled: true
|
||||||
})
|
})
|
||||||
} else {
|
} else if (vo.dataType !== 'array[file]') {
|
||||||
filterOptions.push(vo)
|
filterOptions.push(vo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 15:39:59
|
* @Date: 2026-02-03 15:39:59
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-08 14:10:40
|
* @Last Modified time: 2026-04-10 17:24:19
|
||||||
*/
|
*/
|
||||||
import { type FC, useEffect, useState, useMemo } from "react";
|
import { type FC, useEffect, useState, useMemo } from "react";
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
@@ -315,8 +315,24 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
|
|
||||||
return filteredList;
|
return filteredList;
|
||||||
}
|
}
|
||||||
if (nodeType === 'knowledge-retrieval'
|
if (nodeType === 'knowledge-retrieval') {
|
||||||
|| (nodeType === 'parameter-extractor' && key === 'text')
|
const allList = addParentIterationVars(variableList);
|
||||||
|
let filteredList: Suggestion[] = []
|
||||||
|
allList.forEach(variable => {
|
||||||
|
if (variable.dataType === 'string') {
|
||||||
|
filteredList.push(variable)
|
||||||
|
} else if (variable.dataType === 'file') {
|
||||||
|
filteredList.push({
|
||||||
|
...variable,
|
||||||
|
disabled: true,
|
||||||
|
children: variable.children.filter((child: Suggestion) => child.dataType === 'string')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return filteredList
|
||||||
|
}
|
||||||
|
if ((nodeType === 'parameter-extractor' && key === 'text')
|
||||||
|| (nodeType === 'question-classifier' && ['input_variable', 'categories'].includes(key as string))
|
|| (nodeType === 'question-classifier' && ['input_variable', 'categories'].includes(key as string))
|
||||||
) {
|
) {
|
||||||
const allList = addParentIterationVars(variableList);
|
const allList = addParentIterationVars(variableList);
|
||||||
@@ -359,8 +375,20 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
return filteredList;
|
return filteredList;
|
||||||
}
|
}
|
||||||
if (nodeType === 'memory-write') {
|
if (nodeType === 'memory-write') {
|
||||||
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType.includes('file'));
|
const allList = addParentIterationVars(variableList);
|
||||||
return filteredList;
|
let filteredList: Suggestion[] = []
|
||||||
|
allList.forEach(variable => {
|
||||||
|
if (['string', 'array[file]'].includes(variable.dataType)) {
|
||||||
|
filteredList.push(variable)
|
||||||
|
} else if (variable.dataType === 'file') {
|
||||||
|
filteredList.push({
|
||||||
|
...variable,
|
||||||
|
children: variable.children.filter((child: Suggestion) => child.dataType === 'string')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return filteredList
|
||||||
}
|
}
|
||||||
if (nodeType === 'parameter-extractor' && key === 'prompt') {
|
if (nodeType === 'parameter-extractor' && key === 'prompt') {
|
||||||
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
|
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
|
||||||
@@ -400,9 +428,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
return variableList.filter(variable => variable.dataType.includes('array'));
|
return variableList.filter(variable => variable.dataType.includes('array'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeType === 'code'
|
if ((nodeType === 'if-else' && key === 'cases')) {
|
||||||
|| (nodeType === 'if-else' && key === 'cases')
|
|
||||||
) {
|
|
||||||
const allList = addParentIterationVars(variableList);
|
const allList = addParentIterationVars(variableList);
|
||||||
let filteredList: Suggestion[] = []
|
let filteredList: Suggestion[] = []
|
||||||
allList.forEach(variable => {
|
allList.forEach(variable => {
|
||||||
@@ -919,7 +945,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
/>
|
/>
|
||||||
: config.type === 'editor'
|
: config.type === 'editor'
|
||||||
? <Editor options={variableList} variant="outlined" size="small" placeholder={config.placeholder || t('common.pleaseEnter')} />
|
? <Editor options={getFilteredVariableList(selectedNode?.data?.type, key)} variant="outlined" size="small" placeholder={config.placeholder || t('common.pleaseEnter')} />
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
Reference in New Issue
Block a user