Merge branch 'develop' into feature/tool_yjp

This commit is contained in:
yujiangping
2026-03-09 16:11:37 +08:00
71 changed files with 1499 additions and 440 deletions

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-28 14:08:14
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-02 17:39:49
* @Last Modified time: 2026-03-06 12:05:46
*/
/**
* UploadWorkflowModal Component
@@ -101,6 +101,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
formData.append('platform', values.platform);
formData.append('file', values.file[0]);
setLoading(true)
// Call import workflow API
importWorkflow(formData)
.then(res => {
@@ -114,21 +115,24 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
} else {
setCurrent(2);
// Pre-fill form with file information
const fileNameSplit = values.file[0].name.split('.')
form.setFieldsValue({
name: values.file[0].name.split('.')[0],
name: fileNameSplit.slice(0, fileNameSplit.length - 1).join('.'),
platform: values.platform,
fileName: values.file[0].name,
fileSize: values.file[0].size,
});
}
});
})
.finally(() => setLoading(false));
break;
case 1: // Step 2: Error/warning display
if (firstFormData) {
const { file, platform } = firstFormData;
const fileNameSplit = firstFormData.file[0].name.split('.')
// Pre-fill form with file information
form.setFieldsValue({
name: file[0].name.split('.')[0],
name: fileNameSplit.slice(0, fileNameSplit.length - 1).join('.'),
platform: platform,
fileName: file[0].name,
fileSize: file[0].size,
@@ -138,6 +142,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
break;
case 2: // Step 3: Confirm information
if (data) {
setLoading(true);
// Complete import workflow
completeImportWorkflow({
temp_id: data.temp_id,
@@ -148,7 +153,8 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
const response = res as { id: string };
setCurrent(3);
setAppId(response.id);
});
})
.finally(() => setLoading(false));
}
break;
default:
@@ -175,7 +181,9 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
}
// Reset form if not going back to error/warning step
if (newStep !== 1) {
if (newStep === 0) {
form.setFieldsValue(firstFormData || {})
} else if (newStep !== 1) {
form.resetFields();
}
setCurrent(newStep);
@@ -186,14 +194,16 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
* @param {string} type - Navigation type ('detail' or 'list')
*/
const handleJump = (type: string) => {
switch(type) {
case 'detail':
// Open application detail page in new tab
window.open(`/#/application/config/${appId}`, '_blank');
break;
}
refresh();
handleClose();
refresh();
setTimeout(() => {
switch (type) {
case 'detail':
// Open application detail page in new tab
window.open(`/#/application/config/${appId}`, '_blank');
break;
}
}, 100)
};
/**
@@ -235,7 +245,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
</Button>
];
}
}, [current]);
}, [current, loading]);
return (
<RbModal
@@ -350,7 +360,7 @@ const UploadWorkflowModal = forwardRef<UploadWorkflowModalRef, UploadWorkflowMod
title={t('application.importSuccess')}
subTitle={t('application.importSuccessDesc')}
extra={[
<Button key="back" onClick={() => handleJump('list')}>
<Button key="back" onClick={() => handleJump('list')}>
{t('application.gotoList')}
</Button>,
<Button

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-06 21:09:42
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-05 15:09:22
* @Last Modified time: 2026-03-06 12:20:43
*/
/**
* File Upload Component
@@ -208,6 +208,7 @@ const UploadFiles = forwardRef<UploadFilesRef, UploadFilesProps>(({
newFileList.map(file => {
const type = (file.type && transform_file_type[file.type as keyof typeof transform_file_type]) || file.type || 'document'
file.type = type
file.thumbUrl = file.thumbUrl || URL.createObjectURL(file.originFileObj as Blob)
})
setFileList(newFileList);
if (onChange) {

View File

@@ -672,9 +672,17 @@ const CreateModal = forwardRef<CreateModalRef, CreateModalRefProps>(({
{currentType !== 'Folder' && dynamicTypeList.map((tp) => {
const fieldKey = typeToFieldKey(tp);
// When tp is 'llm', merge llm and chat options
const options = tp.toLowerCase() === 'llm' || tp.toLowerCase() === 'image2text'
let options = tp.toLowerCase() === 'llm' || tp.toLowerCase() === 'image2text'
? [...(modelOptionsByType['llm'] || []), ...(modelOptionsByType['chat'] || [])]
: modelOptionsByType[tp] || [];
// When tp is 'image2text', filter to only include models with 'vision' capability
if (tp.toLowerCase() === 'image2text') {
options = options.filter((opt: any) => {
const model = models?.items?.find((m: any) => m.id === opt.value);
return model?.capability?.includes('vision');
});
}
return (
<Form.Item
key={tp}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:30:06
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-04 10:09:45
* @Last Modified time: 2026-03-06 13:49:00
*/
/**
* Memory Extraction Engine Configuration Constants
@@ -140,13 +140,8 @@ export const configList: ConfigVo[] = [
{
label: 'intelligentSemanticPruningScene',
variableName: 'pruning_scene',
control: 'select',
control: 'text',
type: 'enum',
options: [
{ label: 'education', value: 'education' },
{ label: 'online_service', value: 'online_service' },
{ label: 'outbound', value: 'outbound' },
],
meaning: 'intelligentSemanticPruningSceneDesc',
},
// Intelligent semantic pruning阈值

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 17:30:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 17:30:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-06 13:50:05
*/
/**
* Memory Extraction Engine Configuration Page
@@ -13,7 +13,7 @@
import { type FC, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
import { Row, Col, Space, Select, InputNumber, Slider, App, Form } from 'antd'
import { Row, Col, Space, Select, InputNumber, Slider, App, Form, Input } from 'antd'
import clsx from 'clsx'
import Card from './components/Card'
@@ -35,15 +35,15 @@ const keys = [
/**
* Configuration description component
*/
const ConfigDesc: FC<{ config: Variable, className?: string }> = ({config, className}) => {
const ConfigDesc: FC<{ config: Variable, className?: string; onlyMeaning?: boolean; }> = ({ config, className, onlyMeaning = false}) => {
const { t } = useTranslation();
return (
<div className={className}>
<Space size={8} className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
{!onlyMeaning && <Space size={8} className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
{config.variableName && <span className="rb:font-regular">{t('memoryExtractionEngine.variableName')}: {config.variableName}</span>}
{config.control && <span className="rb:font-regular">{t('memoryExtractionEngine.control')}: {t(`memoryExtractionEngine.${config.control}`)}</span>}
{config.type && <span className="rb:font-regular">{t('memoryExtractionEngine.type')}: {config.type}</span>}
</Space>
</Space>}
{config.meaning && <div className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>{t('memoryExtractionEngine.Meaning')}: {t(`memoryExtractionEngine.${config.meaning}`)}</div>}
</div>
)
@@ -253,6 +253,21 @@ const MemoryExtractionEngine: FC = () => {
</div>
</>
}
{config.control === 'text' &&
<>
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
-{t(`memoryExtractionEngine.${config.label}`)}
</div>
<div className="rb:pl-2">
<Form.Item
name={config.variableName}
>
<Input placeholder={t('common.pleaseEnter')} disabled />
</Form.Item>
<ConfigDesc config={config} onlyMeaning={true} className="rb:-mt-4!" />
</div>
</>
}
</div>
))}
</div>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:33:15
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-05 16:28:58
* @Last Modified time: 2026-03-06 13:53:53
*/
/**
* Memory Management Page
@@ -154,10 +154,10 @@ const MemoryManagement: React.FC = () => {
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/edit.svg')] rb:hover:bg-[url('@/assets/images/edit_hover.svg')]"
onClick={() => handleEdit(item)}
></div>
<div
{!item.is_system_default && <div
className="rb:w-5 rb:h-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/delete.svg')] rb:hover:bg-[url('@/assets/images/delete_hover.svg')]"
onClick={() => handleDelete(item)}
></div>
></div>}
</Space>
</div>
</RbCard>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:49:45
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-04 11:50:47
* @Last Modified time: 2026-03-06 12:26:12
*/
/**
* Model List Detail Drawer
@@ -144,7 +144,7 @@ const ModelListDetail = forwardRef<ModelListDetailRef, ModelListDetailProps>(({
{item.name[0]}
</div>
}
extra={<Switch defaultChecked={item.is_active} disabled={loading} onChange={() => handleChange(item)} />}
extra={<Switch checked={item.is_active} disabled={loading} onChange={() => handleChange(item)} />}
bodyClassName="rb:relative rb:pb-[64px]! rb:h-[calc(100%-64px)]!"
>
<Tooltip title={item.description}>
@@ -153,7 +153,7 @@ const ModelListDetail = forwardRef<ModelListDetailRef, ModelListDetailProps>(({
<div className="rb:absolute rb:bottom-4 rb:left-6 rb:right-6">
<Row gutter={12}>
<Col span={12}>
<Button block onClick={() => handleEdit(item)}>{t('modelNew.modelConfiguration')}</Button>
{!item.model_id && <Button block onClick={() => handleEdit(item)}>{t('modelNew.modelConfiguration')}</Button>}
</Col>
<Col span={12}>
<Button type="primary" ghost block onClick={() => handleKeyConfig(item)}>{t('modelNew.keyConfig')}</Button>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:50:18
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-04 11:39:20
* @Last Modified time: 2026-03-06 12:26:11
*/
/**
* Type definitions for Model Management
@@ -121,6 +121,7 @@ export interface ModelApiKey {
* Model list item data structure
*/
export interface ModelListItem {
model_id?: string;
/** Model name */
model_name?: string;
/** Associated model config IDs */

View File

@@ -102,7 +102,7 @@ const Detail: FC = () => {
<PageHeader
name={<Space>
{data.scene_name}
<Tag color="warning">{t('common.default')}</Tag>
{data.is_system_default ? <Tag color="warning">{t('common.default')}</Tag> : undefined}
</Space>}
subTitle={<Tooltip title={data.scene_description}><div className="rb:h-4 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{data.scene_description}</div></Tooltip>}
extra={data.is_system_default ? undefined : (<Space>

View File

@@ -25,6 +25,7 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
const textContent = root.getTextContent();
if (textContent !== prevValueRef.current) {
isUserInputRef.current = true;
prevValueRef.current = textContent;
}
});
});
@@ -33,7 +34,13 @@ const InitialValuePlugin: React.FC<InitialValuePluginProps> = ({ value, options
}, [editor]);
useEffect(() => {
if ((value !== prevValueRef.current || enableLineNumbers !== prevEnableLineNumbersRef.current) && !isUserInputRef.current) {
if (value !== prevValueRef.current || enableLineNumbers !== prevEnableLineNumbersRef.current) {
// Skip reset if the change was triggered by user input (avoid cursor jump)
if (isUserInputRef.current && enableLineNumbers === prevEnableLineNumbersRef.current) {
prevValueRef.current = value;
isUserInputRef.current = false;
return;
}
queueMicrotask(() => {
editor.update(() => {
const root = $getRoot();

View File

@@ -35,7 +35,8 @@ const NODE_VARIABLES = {
],
'http-request': [
{ label: 'body', dataType: 'string', field: 'body' },
{ label: 'status_code', dataType: 'number', field: 'status_code' }
{ label: 'status_code', dataType: 'number', field: 'status_code' },
{ label: 'headers', dataType: 'object', field: 'headers' },
],
'question-classifier': [{ label: 'class_name', dataType: 'string', field: 'class_name' }],
'memory-read': [
@@ -390,11 +391,6 @@ export const useVariableList = (
addVariable(list, keys, `${pid}_item`, 'item', itemType, `${pid}.item`, pd);
addVariable(list, keys, `${pid}_index`, 'index', 'number', `${pid}.index`, pd);
} else if (pd.type === 'iteration' && !pd.config.input.defaultValue) {
let itemType = 'object';
const iv = list.find(v => `{{${v.value}}}` === pd.config.input.defaultValue);
if (iv?.dataType.startsWith('array[')) {
itemType = iv.dataType.replace(/^array\[(.+)\]$/, '$1');
}
addVariable(list, keys, `${pid}_item`, 'item', 'string', `${pid}.item`, pd);
addVariable(list, keys, `${pid}_index`, 'index', 'number', `${pid}.index`, pd);
}

View File

@@ -95,7 +95,7 @@ const Properties: FC<PropertiesProps> = ({
initialValue[key] = config[key].defaultValue
}
})
form.setFieldsValue({
type,
id: selectedNode.id,
@@ -114,16 +114,16 @@ const Properties: FC<PropertiesProps> = ({
*/
const updateNodeLabel = (newLabel: string) => {
if (selectedNode && form) {
const nodeData = selectedNode.data as NodeProperties;
const nodeData = selectedNode.getData() as NodeProperties;
selectedNode.setAttrByPath('text/text', `${nodeData.icon} ${newLabel}`);
selectedNode.setData({ ...selectedNode.data, name: newLabel });
selectedNode.setData({ ...selectedNode.getData(), name: newLabel });
}
};
useEffect(() => {
if (values && selectedNode) {
const { id, knowledge_retrieval, group, group_variables, ...rest } = values
const { knowledge_bases = [], ...restKnowledgeConfig } = (knowledge_retrieval as any) || {}
const { knowledge_bases = [], name: _name, description: _description, ...restKnowledgeConfig } = (knowledge_retrieval as any) || {}
let allRest = {
...rest,
@@ -136,21 +136,23 @@ const Properties: FC<PropertiesProps> = ({
}))
}
const nodeData = selectedNode.getData()
Object.keys(values).forEach(key => {
if (selectedNode.data?.config?.[key]) {
if (nodeData?.config?.[key]) {
// Create a deep copy to avoid reference sharing between nodes
if (!selectedNode.data.config[key]) {
selectedNode.data.config[key] = {};
if (!nodeData.config[key]) {
nodeData.config[key] = {};
}
selectedNode.data.config[key] = {
...selectedNode.data.config[key],
nodeData.config[key] = {
...nodeData.config[key],
defaultValue: values[key]
};
}
})
selectedNode?.setData({
...selectedNode.data,
...nodeData,
...allRest,
})
}

View File

@@ -529,6 +529,10 @@ export const unknownNode = {
type: 'unknown',
icon: unknownIcon
}
export const noteNode = {
type: 'notes',
icon: unknownIcon
}
export const nodeWidth = 240;
/**

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:17:48
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-28 17:59:34
* @Last Modified time: 2026-03-07 15:23:39
*/
import { useRef, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
@@ -12,7 +12,7 @@ import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '
import { register } from '@antv/x6-react-shape';
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 } from '../constant';
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color, portTextAttrs, defaultAbsolutePortGroups, nodeWidth, unknownNode, noteNode } from '../constant';
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
@@ -128,7 +128,7 @@ export const useWorkflowGraph = ({
if (nodes.length) {
const nodeList = nodes.map(node => {
const { id, type, name, position, config = {} } = node
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode] }]
let nodeLibraryConfig = [...nodeLibrary, { nodes: [unknownNode, noteNode] }]
.flatMap(category => category.nodes)
.find(n => n.type === type)
nodeLibraryConfig = JSON.parse(JSON.stringify({ config: {}, ...nodeLibraryConfig })) as NodeProperties
@@ -715,6 +715,8 @@ export const useWorkflowGraph = ({
panning: isHandMode,
mousewheel: {
enabled: true,
factor: 0.1,
modifiers: null,
},
connecting: {
connector: {