fix(web): app features
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-17 14:22:25
|
* @Date: 2026-03-17 14:22:25
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-17 18:39:49
|
* @Last Modified time: 2026-03-18 15:55:13
|
||||||
*/
|
*/
|
||||||
// Toolbar component for chat input area, supporting file upload, audio recording, and variable configuration
|
// Toolbar component for chat input area, supporting file upload, audio recording, and variable configuration
|
||||||
import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react'
|
import { useRef, forwardRef, useImperativeHandle, type ReactNode, useEffect } from 'react'
|
||||||
@@ -120,7 +120,10 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
|||||||
|
|
||||||
// Build dropdown menu items based on allowed transfer methods
|
// Build dropdown menu items based on allowed transfer methods
|
||||||
const fileMenus: MenuProps['items'] = []
|
const fileMenus: MenuProps['items'] = []
|
||||||
if (file_upload?.allowed_transfer_methods?.includes('remote_url')) {
|
const enabledTypes = ['image', 'document', 'video', 'audio'].filter(
|
||||||
|
type => file_upload?.[`${type}_enabled` as keyof FeaturesConfigForm['file_upload']]
|
||||||
|
)
|
||||||
|
if (file_upload?.allowed_transfer_methods?.includes('remote_url') && enabledTypes.length > 0) {
|
||||||
fileMenus.push({
|
fileMenus.push({
|
||||||
key: 'url',
|
key: 'url',
|
||||||
label: t('memoryConversation.addRemoteFile'),
|
label: t('memoryConversation.addRemoteFile'),
|
||||||
@@ -133,9 +136,6 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const enabledTypes = ['image', 'document', 'video', 'audio'].filter(
|
|
||||||
type => file_upload?.[`${type}_enabled` as keyof FeaturesConfigForm['file_upload']]
|
|
||||||
)
|
|
||||||
if (file_upload?.allowed_transfer_methods?.includes('local_file') && enabledTypes.length > 0) {
|
if (file_upload?.allowed_transfer_methods?.includes('local_file') && enabledTypes.length > 0) {
|
||||||
fileMenus.push({
|
fileMenus.push({
|
||||||
key: 'upload',
|
key: 'upload',
|
||||||
@@ -155,7 +155,7 @@ const ChatToolbar = forwardRef<ChatToolbarRef, ChatToolbarProps>(({
|
|||||||
<Form form={form} initialValues={{ files: [], variables: [] }}>
|
<Form form={form} initialValues={{ files: [], variables: [] }}>
|
||||||
<Flex justify="space-between" className="rb:flex-1">
|
<Flex justify="space-between" className="rb:flex-1">
|
||||||
<Flex gap={8} align="center">
|
<Flex gap={8} align="center">
|
||||||
<Form.Item name="files" noStyle hidden={!file_upload?.enabled}>
|
<Form.Item name="files" noStyle hidden={!file_upload?.enabled || fileMenus.length === 0}>
|
||||||
<Dropdown menu={{ items: fileMenus }}>
|
<Dropdown menu={{ items: fileMenus }}>
|
||||||
<div className="rb:size-6 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/link.svg')] rb:hover:bg-[url('@/assets/images/conversation/link_hover.svg')]" />
|
<div className="rb:size-6 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/conversation/link.svg')] rb:hover:bg-[url('@/assets/images/conversation/link_hover.svg')]" />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -776,7 +776,7 @@ export const zh = {
|
|||||||
singleMaxSize: '单文件最大大小',
|
singleMaxSize: '单文件最大大小',
|
||||||
unix: '个',
|
unix: '个',
|
||||||
text_to_speech: '文字转语音',
|
text_to_speech: '文字转语音',
|
||||||
text_to_speech_desc: '文本可以转换成语言',
|
text_to_speech_desc: '文本可以转换成语音',
|
||||||
|
|
||||||
apps: '我的应用',
|
apps: '我的应用',
|
||||||
sharing: '共享',
|
sharing: '共享',
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ const SelectWrapper: FC<{ title: string, desc: string, name: string | string[],
|
|||||||
* Agent configuration component
|
* Agent configuration component
|
||||||
* Manages single agent configuration including prompts, knowledge, memory, variables, and tools
|
* Manages single agent configuration including prompts, knowledge, memory, variables, and tools
|
||||||
*/
|
*/
|
||||||
const Agent = forwardRef<AgentRef>((_props, ref) => {
|
const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const { message } = App.useApp()
|
const { message } = App.useApp()
|
||||||
@@ -200,6 +200,7 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
|||||||
...response,
|
...response,
|
||||||
tools: allTools
|
tools: allTools
|
||||||
})
|
})
|
||||||
|
onFeaturesLoad?.(response.features)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const MAX_LENGTH = 5;
|
|||||||
* Multi-agent cluster configuration component
|
* Multi-agent cluster configuration component
|
||||||
* Manages multi-agent orchestration, sub-agents, and collaboration modes
|
* Manages multi-agent orchestration, sub-agents, and collaboration modes
|
||||||
*/
|
*/
|
||||||
const Cluster = forwardRef<ClusterRef>((_props, ref) => {
|
const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { message } = App.useApp()
|
const { message } = App.useApp()
|
||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
@@ -131,6 +131,7 @@ const Cluster = forwardRef<ClusterRef>((_props, ref) => {
|
|||||||
} else {
|
} else {
|
||||||
setSubAgents(sub_agents)
|
setSubAgents(sub_agents)
|
||||||
}
|
}
|
||||||
|
onFeaturesLoad?.(response.features)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-13 17:19:13
|
* @Date: 2026-03-13 17:19:13
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-18 10:47:17
|
* @Last Modified time: 2026-03-18 16:03:46
|
||||||
*/
|
*/
|
||||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||||
import { Checkbox, App, Form } from 'antd';
|
import { Checkbox, App, Form } from 'antd';
|
||||||
@@ -78,7 +78,7 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
|||||||
*/
|
*/
|
||||||
const handleToggle = (id: string, isShared: boolean) => {
|
const handleToggle = (id: string, isShared: boolean) => {
|
||||||
if (isShared) return;
|
if (isShared) return;
|
||||||
const prev = form.getFieldValue('target_workspace_ids') as string[] ?? [];
|
const prev: string[] = form.getFieldValue('target_workspace_ids') ?? [];
|
||||||
form.setFieldValue(
|
form.setFieldValue(
|
||||||
'target_workspace_ids',
|
'target_workspace_ids',
|
||||||
prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id]
|
prev.includes(id) ? prev.filter(i => i !== id) : [...prev, id]
|
||||||
@@ -135,10 +135,16 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
|||||||
|
|
||||||
{/* Target space: scrollable list of workspaces with checkbox selection */}
|
{/* Target space: scrollable list of workspaces with checkbox selection */}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="target_workspace_ids"
|
|
||||||
label={t('application.selectTargetSpace')}
|
label={t('application.selectTargetSpace')}
|
||||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
required
|
||||||
>
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="target_workspace_ids"
|
||||||
|
noStyle
|
||||||
|
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||||
|
>
|
||||||
|
<input type="hidden" />
|
||||||
|
</Form.Item>
|
||||||
<div className="rb:rounded-lg rb:border rb:border-[#EBEBEB] rb:divide-y rb:divide-[#EBEBEB] rb:max-h-50 rb:overflow-y-auto">
|
<div className="rb:rounded-lg rb:border rb:border-[#EBEBEB] rb:divide-y rb:divide-[#EBEBEB] rb:max-h-50 rb:overflow-y-auto">
|
||||||
{spaceList.map(space => {
|
{spaceList.map(space => {
|
||||||
const isShared = sharedIds.includes(space.id);
|
const isShared = sharedIds.includes(space.id);
|
||||||
@@ -146,12 +152,11 @@ const AppSharingModal = forwardRef<AppSharingModalRef, AppSharingModalProps>(({
|
|||||||
<div key={space.id} className="rb:flex rb:items-center rb:gap-2 rb:px-4 rb:py-3 rb:cursor-pointer" onClick={() => handleToggle(space.id, isShared)}>
|
<div key={space.id} className="rb:flex rb:items-center rb:gap-2 rb:px-4 rb:py-3 rb:cursor-pointer" onClick={() => handleToggle(space.id, isShared)}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={isShared || selectedIds.includes(space.id)}
|
checked={isShared || selectedIds.includes(space.id)}
|
||||||
disabled={isShared} // already-shared workspaces cannot be unselected
|
disabled={isShared}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onChange={() => handleToggle(space.id, isShared)}
|
onChange={() => handleToggle(space.id, isShared)}
|
||||||
/>
|
/>
|
||||||
<span className="rb:flex-1 rb:text-sm">{space.name}</span>
|
<span className="rb:flex-1 rb:text-sm">{space.name}</span>
|
||||||
{/* Badge shown when the app is already shared with this workspace */}
|
|
||||||
{isShared && (
|
{isShared && (
|
||||||
<span className="rb:text-xs rb:text-[#5B6167]">{t('application.alreadyShared')}</span>
|
<span className="rb:text-xs rb:text-[#5B6167]">{t('application.alreadyShared')}</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:27:52
|
* @Date: 2026-02-03 16:27:52
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-16 17:04:56
|
* @Last Modified time: 2026-03-18 15:40:53
|
||||||
*/
|
*/
|
||||||
import { type FC, useRef, useMemo, useCallback } from 'react';
|
import { type FC, useRef, useMemo, useCallback } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
@@ -61,6 +61,10 @@ interface ConfigHeaderProps {
|
|||||||
workflowRef: React.RefObject<WorkflowRef>
|
workflowRef: React.RefObject<WorkflowRef>
|
||||||
/** App component ref (Agent/Cluster/Workflow) */
|
/** App component ref (Agent/Cluster/Workflow) */
|
||||||
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
|
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
|
||||||
|
/** Features config from parent state */
|
||||||
|
features?: FeaturesConfigForm;
|
||||||
|
/** Callback to update features in parent */
|
||||||
|
onFeaturesChange?: (value: FeaturesConfigForm) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,6 +75,8 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
|||||||
application, activeTab, handleChangeTab, refresh,
|
application, activeTab, handleChangeTab, refresh,
|
||||||
workflowRef,
|
workflowRef,
|
||||||
appRef,
|
appRef,
|
||||||
|
features,
|
||||||
|
onFeaturesChange,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -173,14 +179,10 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
|||||||
return items
|
return items
|
||||||
}, [t, handleClick, application])
|
}, [t, handleClick, application])
|
||||||
|
|
||||||
const features = useMemo(() => {
|
|
||||||
return (appRef?.current?.features || { file_type: [] }) as FeaturesConfigForm
|
|
||||||
}, [appRef])
|
|
||||||
const handleSaveFeaturesConfig = useCallback((value: FeaturesConfigForm) => {
|
const handleSaveFeaturesConfig = useCallback((value: FeaturesConfigForm) => {
|
||||||
appRef?.current?.handleSaveFeaturesConfig?.(value)
|
appRef?.current?.handleSaveFeaturesConfig?.(value)
|
||||||
}, [appRef])
|
onFeaturesChange?.(value)
|
||||||
|
}, [appRef, onFeaturesChange])
|
||||||
console.log('formatMenuItems', formatMenuItems)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header className="rb:w-full rb:h-16 rb:grid rb:grid-cols-3 rb:p-[16px_16px_16px_24px]! rb:border-b rb:border-[#EAECEE] rb:leading-8">
|
<Header className="rb:w-full rb:h-16 rb:grid rb:grid-cols-3 rb:p-[16px_16px_16px_24px]! rb:border-b rb:border-[#EAECEE] rb:leading-8">
|
||||||
@@ -211,7 +213,7 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
{application?.type === 'workflow'
|
{application?.type === 'workflow'
|
||||||
? <div className="rb:h-8 rb:flex rb:items-center rb:justify-end rb:gap-2.5">
|
? <div className="rb:h-8 rb:flex rb:items-center rb:justify-end rb:gap-2.5">
|
||||||
<FeaturesConfig value={features} refresh={handleSaveFeaturesConfig} />
|
<FeaturesConfig source={application?.type} value={features} refresh={handleSaveFeaturesConfig} />
|
||||||
<Button onClick={clear}>{t('workflow.clear')}</Button>
|
<Button onClick={clear}>{t('workflow.clear')}</Button>
|
||||||
<Button onClick={addvariable}>{t('workflow.addvariable')}</Button>
|
<Button onClick={addvariable}>{t('workflow.addvariable')}</Button>
|
||||||
<Button onClick={run}>{t('workflow.run')}</Button>
|
<Button onClick={run}>{t('workflow.run')}</Button>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:27:56
|
* @Date: 2026-02-03 16:27:56
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-16 18:31:58
|
* @Last Modified time: 2026-03-18 15:38:14
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Copy Application Modal
|
* Copy Application Modal
|
||||||
@@ -18,9 +18,11 @@ import type { FeaturesConfigModalRef, FeaturesConfigForm } from '../../types'
|
|||||||
import RbModal from '@/components/RbModal'
|
import RbModal from '@/components/RbModal'
|
||||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||||
import FileUploadSettingModal from './FileUploadSettingModal'
|
import FileUploadSettingModal from './FileUploadSettingModal'
|
||||||
|
import type { Application } from '@/views/ApplicationManagement/types';
|
||||||
|
|
||||||
interface FeaturesConfigModalProps {
|
interface FeaturesConfigModalProps {
|
||||||
refresh: (value: FeaturesConfigForm) => void;
|
refresh: (value: FeaturesConfigForm) => void;
|
||||||
|
source?: Application['type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +30,7 @@ interface FeaturesConfigModalProps {
|
|||||||
*/
|
*/
|
||||||
const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigModalProps>(({
|
const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigModalProps>(({
|
||||||
refresh,
|
refresh,
|
||||||
|
source,
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
@@ -44,6 +47,7 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
|||||||
/** Open modal */
|
/** Open modal */
|
||||||
const handleOpen = (initValue: FeaturesConfigForm) => {
|
const handleOpen = (initValue: FeaturesConfigForm) => {
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
console.log('initValue', initValue)
|
||||||
form.setFieldsValue(initValue)
|
form.setFieldsValue(initValue)
|
||||||
};
|
};
|
||||||
/** Copy application with new name */
|
/** Copy application with new name */
|
||||||
@@ -66,7 +70,6 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
|||||||
handleClose
|
handleClose
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('settings values', values)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RbModal
|
<RbModal
|
||||||
@@ -81,20 +84,22 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<Flex vertical gap={12}>
|
<Flex vertical gap={12}>
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
{source !== 'workflow' && <>
|
||||||
<SwitchFormItem
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
title={t(`memoryConversation.web_search`)}
|
<SwitchFormItem
|
||||||
name={['web_search', "enabled"]}
|
title={t(`memoryConversation.web_search`)}
|
||||||
/>
|
name={['web_search', "enabled"]}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
<SwitchFormItem
|
<SwitchFormItem
|
||||||
title={t('application.text_to_speech')}
|
title={t('application.text_to_speech')}
|
||||||
name={['text_to_speech', "enabled"]}
|
name={['text_to_speech', "enabled"]}
|
||||||
desc={t('application.text_to_speech_desc')}
|
desc={t('application.text_to_speech_desc')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</>}
|
||||||
|
|
||||||
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
<div className="rb:relative rb:border rb:border-[#DFE4ED] rb:p-3 rb:rounded-lg rb:bg-[#f5f7fc]">
|
||||||
<SwitchFormItem
|
<SwitchFormItem
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-13 17:20:21
|
* @Date: 2026-03-13 17:20:21
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-16 18:31:43
|
* @Last Modified time: 2026-03-18 15:38:59
|
||||||
*/
|
*/
|
||||||
import { type FC, useRef } from 'react';
|
import { type FC, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -10,6 +10,7 @@ import { Button } from 'antd';
|
|||||||
|
|
||||||
import FeaturesConfigModal from './FeaturesConfigModal'
|
import FeaturesConfigModal from './FeaturesConfigModal'
|
||||||
import type { FeaturesConfigModalRef, FeaturesConfigForm } from '../../types'
|
import type { FeaturesConfigModalRef, FeaturesConfigForm } from '../../types'
|
||||||
|
import type { Application } from '@/views/ApplicationManagement/types';
|
||||||
|
|
||||||
/** Props for the FeaturesConfig component */
|
/** Props for the FeaturesConfig component */
|
||||||
interface FeaturesConfigProps {
|
interface FeaturesConfigProps {
|
||||||
@@ -17,11 +18,13 @@ interface FeaturesConfigProps {
|
|||||||
value: FeaturesConfigForm;
|
value: FeaturesConfigForm;
|
||||||
/** Callback to propagate updated config back to the parent */
|
/** Callback to propagate updated config back to the parent */
|
||||||
refresh: (value: FeaturesConfigForm) => void;
|
refresh: (value: FeaturesConfigForm) => void;
|
||||||
|
source?: Application['type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
||||||
value,
|
value,
|
||||||
refresh
|
refresh,
|
||||||
|
source
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// Ref used to imperatively open the config modal
|
// Ref used to imperatively open the config modal
|
||||||
@@ -29,6 +32,7 @@ const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
|||||||
|
|
||||||
/** Open the feature config modal pre-populated with the current values */
|
/** Open the feature config modal pre-populated with the current values */
|
||||||
const handleFeaturesConfig = () => {
|
const handleFeaturesConfig = () => {
|
||||||
|
console.log('handleFeaturesConfig', value)
|
||||||
funConfigModalRef.current?.handleOpen(value)
|
funConfigModalRef.current?.handleOpen(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +45,7 @@ const FeaturesConfig: FC<FeaturesConfigProps> = ({
|
|||||||
<FeaturesConfigModal
|
<FeaturesConfigModal
|
||||||
ref={funConfigModalRef}
|
ref={funConfigModalRef}
|
||||||
refresh={refresh}
|
refresh={refresh}
|
||||||
|
source={source}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const ApplicationConfig: React.FC = () => {
|
|||||||
// State
|
// State
|
||||||
const [application, setApplication] = useState<Application | null>(null);
|
const [application, setApplication] = useState<Application | null>(null);
|
||||||
const [activeTab, setActiveTab] = useState('arrangement');
|
const [activeTab, setActiveTab] = useState('arrangement');
|
||||||
|
const [features, setFeatures] = useState<import('./types').FeaturesConfigForm | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveTab(source === 'sharing' ? 'test' : 'arrangement')
|
setActiveTab(source === 'sharing' ? 'test' : 'arrangement')
|
||||||
@@ -114,10 +115,12 @@ const ApplicationConfig: React.FC = () => {
|
|||||||
refresh={getApplicationInfo}
|
refresh={getApplicationInfo}
|
||||||
appRef={application?.type === 'agent' ? agentRef : application?.type === 'multi_agent' ? clusterRef : application?.type === 'workflow' ? workflowRef : undefined}
|
appRef={application?.type === 'agent' ? agentRef : application?.type === 'multi_agent' ? clusterRef : application?.type === 'workflow' ? workflowRef : undefined}
|
||||||
workflowRef={workflowRef}
|
workflowRef={workflowRef}
|
||||||
|
features={features}
|
||||||
|
onFeaturesChange={setFeatures}
|
||||||
/>
|
/>
|
||||||
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />}
|
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} onFeaturesLoad={setFeatures} />}
|
||||||
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} />}
|
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} onFeaturesLoad={setFeatures} />}
|
||||||
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />}
|
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} onFeaturesLoad={setFeatures} />}
|
||||||
{activeTab === 'api' && <Api application={application} />}
|
{activeTab === 'api' && <Api application={application} />}
|
||||||
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}
|
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}
|
||||||
{activeTab === 'statistics' && <Statistics application={application} />}
|
{activeTab === 'statistics' && <Statistics application={application} />}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:34:12
|
* @Date: 2026-02-03 16:34:12
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-18 11:20:45
|
* @Last Modified time: 2026-03-18 15:58:36
|
||||||
*/
|
*/
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -73,6 +73,11 @@ const MySharing: React.FC = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
/** Navigate to application configuration page */
|
||||||
|
const handleEdit = (item: MySharedOutItem) => {
|
||||||
|
let url = `/#/application/config/${item.source_app_id}`
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex vertical gap={12} className="rb:h-[calc(100vh-148px)]! rb:overflow-y-auto!">
|
<Flex vertical gap={12} className="rb:h-[calc(100vh-148px)]! rb:overflow-y-auto!">
|
||||||
@@ -108,7 +113,7 @@ const MySharing: React.FC = () => {
|
|||||||
children: (
|
children: (
|
||||||
<Row gutter={[12, 12]}>
|
<Row gutter={[12, 12]}>
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
<Col key={item.id} span={6} className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-3! rb:px-4! rb:relative">
|
<Col key={item.id} span={6} className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-3! rb:px-4! rb:relative" onClick={() => handleEdit(item)}>
|
||||||
<div
|
<div
|
||||||
className="rb:absolute rb:top-3 rb:right-3 rb:cursor-pointer rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/close.svg')]"
|
className="rb:absolute rb:top-3 rb:right-3 rb:cursor-pointer rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/close.svg')]"
|
||||||
onClick={() => handleCancelOne(item)}
|
onClick={() => handleCancelOne(item)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-06 21:09:47
|
* @Date: 2026-02-06 21:09:47
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-17 10:28:04
|
* @Last Modified time: 2026-03-18 15:50:31
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Upload File List Modal Component
|
* Upload File List Modal Component
|
||||||
@@ -115,7 +115,6 @@ const UploadFileListModal = forwardRef<UploadFileListModalRef, UploadFileListMod
|
|||||||
<FormItem
|
<FormItem
|
||||||
{...restField}
|
{...restField}
|
||||||
name={[name, 'type']}
|
name={[name, 'type']}
|
||||||
initialValue="image"
|
|
||||||
className="rb:mb-0!"
|
className="rb:mb-0!"
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 16:58:03
|
* @Date: 2026-02-03 16:58:03
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-17 18:30:58
|
* @Last Modified time: 2026-03-18 15:35:05
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Conversation Page
|
* Conversation Page
|
||||||
@@ -60,6 +60,7 @@ const Conversation: FC = () => {
|
|||||||
const [shareToken, setShareToken] = useState<string | null>(localStorage.getItem(`shareToken_${token}`))
|
const [shareToken, setShareToken] = useState<string | null>(localStorage.getItem(`shareToken_${token}`))
|
||||||
const [fileList, setFileList] = useState<any[]>([])
|
const [fileList, setFileList] = useState<any[]>([])
|
||||||
const [webSearch, setWebSearch] = useState(false)
|
const [webSearch, setWebSearch] = useState(false)
|
||||||
|
const [isHasMemory, setIsHasMemory] = useState(false)
|
||||||
const [memory, setMemory] = useState(true)
|
const [memory, setMemory] = useState(true)
|
||||||
const [features, setFeatures] = useState<FeaturesConfigForm>({} as FeaturesConfigForm)
|
const [features, setFeatures] = useState<FeaturesConfigForm>({} as FeaturesConfigForm)
|
||||||
|
|
||||||
@@ -85,9 +86,10 @@ const Conversation: FC = () => {
|
|||||||
if (shareToken && token) {
|
if (shareToken && token) {
|
||||||
getExperienceConfig(token)
|
getExperienceConfig(token)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const response = res as { variables: Variable[]; features: FeaturesConfigForm }
|
const response = res as { variables: Variable[]; features: FeaturesConfigForm; app_type: string; memory?: boolean; }
|
||||||
toolbarRef.current?.setVariables(response.variables || [])
|
toolbarRef.current?.setVariables(response.variables || [])
|
||||||
setFeatures(response.features)
|
setFeatures(response.features)
|
||||||
|
setIsHasMemory((response.app_type === 'workflow' && response.memory) || (response.app_type !== 'workflow'))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
setChatList([])
|
setChatList([])
|
||||||
@@ -379,14 +381,16 @@ const Conversation: FC = () => {
|
|||||||
{t('memoryConversation.web_search')}
|
{t('memoryConversation.web_search')}
|
||||||
</ButtonCheckbox>
|
</ButtonCheckbox>
|
||||||
}
|
}
|
||||||
<ButtonCheckbox
|
{isHasMemory &&
|
||||||
icon={MemoryFunctionIcon}
|
<ButtonCheckbox
|
||||||
checkedIcon={MemoryFunctionCheckedIcon}
|
icon={MemoryFunctionIcon}
|
||||||
checked={memory}
|
checkedIcon={MemoryFunctionCheckedIcon}
|
||||||
onChange={handleChangeMemory}
|
checked={memory}
|
||||||
>
|
onChange={handleChangeMemory}
|
||||||
{t('memoryConversation.memory')}
|
>
|
||||||
</ButtonCheckbox>
|
{t('memoryConversation.memory')}
|
||||||
|
</ButtonCheckbox>
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,10 +10,6 @@ interface CanvasToolbarProps {
|
|||||||
isHandMode: boolean;
|
isHandMode: boolean;
|
||||||
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
zoomLevel: number;
|
zoomLevel: number;
|
||||||
canUndo: boolean;
|
|
||||||
canRedo: boolean;
|
|
||||||
onUndo: () => void;
|
|
||||||
onRedo: () => void;
|
|
||||||
addNotes: () => void;
|
addNotes: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 15:17:48
|
* @Date: 2026-02-03 15:17:48
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-18 12:07:03
|
* @Last Modified time: 2026-03-18 16:08:17
|
||||||
*/
|
*/
|
||||||
import { useRef, useEffect, useState } from 'react';
|
import { useRef, useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
@@ -12,10 +12,11 @@ import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '
|
|||||||
import { register } from '@antv/x6-react-shape';
|
import { register } from '@antv/x6-react-shape';
|
||||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
||||||
|
|
||||||
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, noteNode, notesConfig } from '../constant';
|
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, notesConfig } from '../constant';
|
||||||
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
|
||||||
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
|
||||||
import { useUser } from '@/store/user';
|
import { useUser } from '@/store/user';
|
||||||
|
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for useWorkflowGraph hook
|
* Props for useWorkflowGraph hook
|
||||||
@@ -25,6 +26,8 @@ export interface UseWorkflowGraphProps {
|
|||||||
containerRef: React.RefObject<HTMLDivElement>;
|
containerRef: React.RefObject<HTMLDivElement>;
|
||||||
/** Reference to the minimap container element */
|
/** Reference to the minimap container element */
|
||||||
miniMapRef: React.RefObject<HTMLDivElement>;
|
miniMapRef: React.RefObject<HTMLDivElement>;
|
||||||
|
/** Callback when features config is loaded */
|
||||||
|
onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +70,7 @@ export interface UseWorkflowGraphReturn {
|
|||||||
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
|
||||||
|
|
||||||
handleAddNotes: () => void;
|
handleAddNotes: () => void;
|
||||||
|
handleSaveFeaturesConfig: (value: FeaturesConfigForm) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +82,7 @@ export interface UseWorkflowGraphReturn {
|
|||||||
export const useWorkflowGraph = ({
|
export const useWorkflowGraph = ({
|
||||||
containerRef,
|
containerRef,
|
||||||
miniMapRef,
|
miniMapRef,
|
||||||
|
onFeaturesLoad,
|
||||||
}: UseWorkflowGraphProps): UseWorkflowGraphReturn => {
|
}: UseWorkflowGraphProps): UseWorkflowGraphReturn => {
|
||||||
// Hooks
|
// Hooks
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
@@ -115,6 +120,7 @@ export const useWorkflowGraph = ({
|
|||||||
})
|
})
|
||||||
setChatVariables(initChatVariables)
|
setChatVariables(initChatVariables)
|
||||||
setConfig({ ...rest, variables: initChatVariables })
|
setConfig({ ...rest, variables: initChatVariables })
|
||||||
|
onFeaturesLoad?.(rest.features)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +138,7 @@ export const useWorkflowGraph = ({
|
|||||||
if (nodes.length) {
|
if (nodes.length) {
|
||||||
const nodeList = nodes.map(node => {
|
const nodeList = nodes.map(node => {
|
||||||
const { id, type, name, position, config = {} } = node
|
const { id, type, name, position, config = {} } = node
|
||||||
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode, notesConfig] }]
|
let nodeLibraryConfig: NodeProperties | undefined = [...nodeLibrary, { nodes: [unknownNode, notesConfig] }]
|
||||||
.flatMap(category => category.nodes)
|
.flatMap(category => category.nodes)
|
||||||
.find(n => n.type === type)
|
.find(n => n.type === type)
|
||||||
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
|
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
|
||||||
@@ -994,6 +1000,9 @@ export const useWorkflowGraph = ({
|
|||||||
}) || [];
|
}) || [];
|
||||||
const edges = graphRef.current?.getEdges() || []
|
const edges = graphRef.current?.getEdges() || []
|
||||||
|
|
||||||
|
|
||||||
|
console.log('config', config)
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...config,
|
...config,
|
||||||
variables: chatVariables.map(v => {
|
variables: chatVariables.map(v => {
|
||||||
@@ -1187,6 +1196,9 @@ export const useWorkflowGraph = ({
|
|||||||
data: { ...cleanNodeData },
|
data: { ...cleanNodeData },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const handleSaveFeaturesConfig = (value?: FeaturesConfigForm) => {
|
||||||
|
setConfig(prev => prev ? { ...prev, features: value } as WorkflowConfig : prev)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
config,
|
config,
|
||||||
@@ -1206,6 +1218,7 @@ export const useWorkflowGraph = ({
|
|||||||
handleSave,
|
handleSave,
|
||||||
chatVariables,
|
chatVariables,
|
||||||
setChatVariables,
|
setChatVariables,
|
||||||
handleAddNotes
|
handleAddNotes,
|
||||||
|
handleSaveFeaturesConfig
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import Properties from './components/Properties';
|
|||||||
import CanvasToolbar from './components/CanvasToolbar';
|
import CanvasToolbar from './components/CanvasToolbar';
|
||||||
import PortClickHandler from './components/PortClickHandler';
|
import PortClickHandler from './components/PortClickHandler';
|
||||||
import { useWorkflowGraph } from './hooks/useWorkflowGraph';
|
import { useWorkflowGraph } from './hooks/useWorkflowGraph';
|
||||||
import type { WorkflowRef } from '@/views/ApplicationConfig/types'
|
import type { WorkflowRef, FeaturesConfigForm } from '@/views/ApplicationConfig/types'
|
||||||
import Chat from './components/Chat/Chat';
|
import Chat from './components/Chat/Chat';
|
||||||
import type { ChatRef, AddChatVariableRef } from './types'
|
import type { ChatRef, AddChatVariableRef } from './types'
|
||||||
import arrowIcon from '@/assets/images/workflow/arrow.png'
|
import arrowIcon from '@/assets/images/workflow/arrow.png'
|
||||||
import AddChatVariable from './components/AddChatVariable';
|
import AddChatVariable from './components/AddChatVariable';
|
||||||
|
|
||||||
const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
const Workflow = forwardRef<WorkflowRef, { onFeaturesLoad?: (features: FeaturesConfigForm | undefined) => void }>(({ onFeaturesLoad }, ref) => {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const miniMapRef = useRef<HTMLDivElement>(null);
|
const miniMapRef = useRef<HTMLDivElement>(null);
|
||||||
const addChatVariableRef = useRef<AddChatVariableRef>(null)
|
const addChatVariableRef = useRef<AddChatVariableRef>(null)
|
||||||
@@ -25,12 +25,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
selectedNode,
|
selectedNode,
|
||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
zoomLevel,
|
zoomLevel,
|
||||||
canUndo,
|
|
||||||
canRedo,
|
|
||||||
isHandMode,
|
isHandMode,
|
||||||
setIsHandMode,
|
setIsHandMode,
|
||||||
onUndo,
|
|
||||||
onRedo,
|
|
||||||
onDrop,
|
onDrop,
|
||||||
blankClick,
|
blankClick,
|
||||||
deleteEvent,
|
deleteEvent,
|
||||||
@@ -39,8 +35,9 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
handleSave,
|
handleSave,
|
||||||
chatVariables,
|
chatVariables,
|
||||||
setChatVariables,
|
setChatVariables,
|
||||||
handleAddNotes
|
handleAddNotes,
|
||||||
} = useWorkflowGraph({ containerRef, miniMapRef });
|
handleSaveFeaturesConfig
|
||||||
|
} = useWorkflowGraph({ containerRef, miniMapRef, onFeaturesLoad });
|
||||||
|
|
||||||
const onDragOver = (event: React.DragEvent) => {
|
const onDragOver = (event: React.DragEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -61,7 +58,8 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
graphRef,
|
graphRef,
|
||||||
addVariable,
|
addVariable,
|
||||||
config,
|
config,
|
||||||
features: config?.features
|
features: config?.features,
|
||||||
|
handleSaveFeaturesConfig
|
||||||
}))
|
}))
|
||||||
return (
|
return (
|
||||||
<div className="rb:h-[calc(100vh-64px)] rb:relative">
|
<div className="rb:h-[calc(100vh-64px)] rb:relative">
|
||||||
@@ -93,10 +91,6 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
|
|||||||
isHandMode={isHandMode}
|
isHandMode={isHandMode}
|
||||||
setIsHandMode={setIsHandMode}
|
setIsHandMode={setIsHandMode}
|
||||||
zoomLevel={zoomLevel}
|
zoomLevel={zoomLevel}
|
||||||
canUndo={canUndo}
|
|
||||||
canRedo={canRedo}
|
|
||||||
onUndo={onUndo}
|
|
||||||
onRedo={onRedo}
|
|
||||||
addNotes={handleAddNotes}
|
addNotes={handleAddNotes}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user