Merge pull request #100 from SuanmoSuanyangTechnology/feature/workflow_zy
Feature/workflow zy
This commit is contained in:
@@ -117,26 +117,26 @@ export const getRagContent = (end_user_id: string) => {
|
|||||||
}
|
}
|
||||||
// 情感分布分析
|
// 情感分布分析
|
||||||
export const getWordCloud = (group_id: string) => {
|
export const getWordCloud = (group_id: string) => {
|
||||||
return request.post(`/memory/emotion/wordcloud`, { group_id, limit: 20 })
|
return request.post(`/memory/emotion-memory/wordcloud`, { group_id, limit: 20 })
|
||||||
}
|
}
|
||||||
// 高频情绪关键词
|
// 高频情绪关键词
|
||||||
export const getEmotionTags = (group_id: string) => {
|
export const getEmotionTags = (group_id: string) => {
|
||||||
return request.post(`/memory/emotion/tags`, { group_id, limit: 20 })
|
return request.post(`/memory/emotion-memory/tags`, { group_id, limit: 20 })
|
||||||
}
|
}
|
||||||
// 情绪健康指数
|
// 情绪健康指数
|
||||||
export const getEmotionHealth = (group_id: string) => {
|
export const getEmotionHealth = (group_id: string) => {
|
||||||
return request.post(`/memory/emotion/health`, { group_id, limit: 20 })
|
return request.post(`/memory/emotion-memory/health`, { group_id, limit: 20 })
|
||||||
}
|
}
|
||||||
// 个性化建议
|
// 个性化建议
|
||||||
export const getEmotionSuggestions = (group_id: string) => {
|
export const getEmotionSuggestions = (group_id: string) => {
|
||||||
return request.post(`/memory/emotion/suggestions`, { group_id, limit: 20 })
|
return request.post(`/memory/emotion-memory/suggestions`, { group_id, limit: 20 })
|
||||||
}
|
}
|
||||||
export const analyticsRefresh = (end_user_id: string) => {
|
export const analyticsRefresh = (end_user_id: string) => {
|
||||||
return request.post('/memory-storage/analytics/generate_cache', { end_user_id })
|
return request.post('/memory-storage/analytics/generate_cache', { end_user_id })
|
||||||
}
|
}
|
||||||
// 遗忘
|
// 遗忘
|
||||||
export const getForgetStats = (group_id: string) => {
|
export const getForgetStats = (group_id: string) => {
|
||||||
return request.get(`/memory/forget/stats`, { group_id })
|
return request.get(`/memory/forget-memory/stats`, { group_id })
|
||||||
}
|
}
|
||||||
// 隐性记忆-偏好
|
// 隐性记忆-偏好
|
||||||
export const getImplicitPreferences = (end_user_id: string) => {
|
export const getImplicitPreferences = (end_user_id: string) => {
|
||||||
@@ -176,10 +176,10 @@ export const getPerceptualTimeline = (end_user: string) => {
|
|||||||
}
|
}
|
||||||
// 情景记忆-总览
|
// 情景记忆-总览
|
||||||
export const getEpisodicOverview = (data: { end_user_id: string; time_range: string; episodic_type: string; } ) => {
|
export const getEpisodicOverview = (data: { end_user_id: string; time_range: string; episodic_type: string; } ) => {
|
||||||
return request.post(`/memory-storage/classifications/episodic-memory`, data)
|
return request.post(`/memory/episodic-memory/overview`, data)
|
||||||
}
|
}
|
||||||
export const getEpisodicDetail = (data: { end_user_id: string; summary_id: string; } ) => {
|
export const getEpisodicDetail = (data: { end_user_id: string; summary_id: string; } ) => {
|
||||||
return request.post(`/memory-storage/classifications/episodic-memory-details`, data)
|
return request.post(`/memory/episodic-memory/details`, data)
|
||||||
}
|
}
|
||||||
// 关系演化
|
// 关系演化
|
||||||
export const getRelationshipEvolution = (data: { id: string; label: string; } ) => {
|
export const getRelationshipEvolution = (data: { id: string; label: string; } ) => {
|
||||||
@@ -190,10 +190,10 @@ export const getTimelineMemories = (data: { id: string; label: string; }) => {
|
|||||||
return request.get(`/memory-storage/memory_space/timeline_memories`, data)
|
return request.get(`/memory-storage/memory_space/timeline_memories`, data)
|
||||||
}
|
}
|
||||||
export const getExplicitMemory = (end_user_id: string) => {
|
export const getExplicitMemory = (end_user_id: string) => {
|
||||||
return request.post(`/memory-storage/classifications/explicit-memory`, { end_user_id })
|
return request.post(`/memory/explicit-memory/overview`, { end_user_id })
|
||||||
}
|
}
|
||||||
export const getExplicitMemoryDetails = (data: { end_user_id: string, memory_id: string; }) => {
|
export const getExplicitMemoryDetails = (data: { end_user_id: string, memory_id: string; }) => {
|
||||||
return request.post(`/memory-storage/classifications/explicit-memory-details`, data)
|
return request.post(`/memory/explicit-memory/details`, data)
|
||||||
}
|
}
|
||||||
export const getConversations = (end_user: string) => {
|
export const getConversations = (end_user: string) => {
|
||||||
return request.get(`/memory/work/${end_user}/conversations`)
|
return request.get(`/memory/work/${end_user}/conversations`)
|
||||||
@@ -205,7 +205,7 @@ export const getConversationDetail = (end_user: string, conversation_id: string)
|
|||||||
return request.get(`/memory/work/${end_user}/detail`, { conversation_id })
|
return request.get(`/memory/work/${end_user}/detail`, { conversation_id })
|
||||||
}
|
}
|
||||||
export const forgetTrigger = (data: { max_merge_batch_size: number; min_days_since_access: number; end_user_id: string;}) => {
|
export const forgetTrigger = (data: { max_merge_batch_size: number; min_days_since_access: number; end_user_id: string;}) => {
|
||||||
return request.post(`/memory/forget/trigger`, data)
|
return request.post(`/memory/forget-memory/trigger`, data)
|
||||||
}
|
}
|
||||||
/*************** end 用户记忆 相关接口 ******************************/
|
/*************** end 用户记忆 相关接口 ******************************/
|
||||||
|
|
||||||
@@ -229,11 +229,11 @@ export const deleteMemoryConfig = (config_id: number) => {
|
|||||||
}
|
}
|
||||||
// 遗忘引擎-获取配置
|
// 遗忘引擎-获取配置
|
||||||
export const getMemoryForgetConfig = (config_id: number | string) => {
|
export const getMemoryForgetConfig = (config_id: number | string) => {
|
||||||
return request.get('/memory/forget/read_config', { config_id })
|
return request.get('/memory/forget-memory/read_config', { config_id })
|
||||||
}
|
}
|
||||||
// 遗忘引擎-更新配置
|
// 遗忘引擎-更新配置
|
||||||
export const updateMemoryForgetConfig = (values: ForgetConfigForm) => {
|
export const updateMemoryForgetConfig = (values: ForgetConfigForm) => {
|
||||||
return request.post('/memory/forget/update_config', values)
|
return request.post('/memory/forget-memory/update_config', values)
|
||||||
}
|
}
|
||||||
// 记忆萃取引擎-获取配置
|
// 记忆萃取引擎-获取配置
|
||||||
export const getMemoryExtractionConfig = (config_id: number | string) => {
|
export const getMemoryExtractionConfig = (config_id: number | string) => {
|
||||||
|
|||||||
17
web/src/assets/images/menu/spaceConfig.svg
Normal file
17
web/src/assets/images/menu/spaceConfig.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>模型 (1)</title>
|
||||||
|
<g id="v0.2.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="红熊空间-记忆管理" transform="translate(-24, -409)" stroke="#5B6167">
|
||||||
|
<g id="记忆对话备份-2" transform="translate(12, 401)">
|
||||||
|
<g id="模型-(1)" transform="translate(12, 8)">
|
||||||
|
<g id="编组-21" transform="translate(1.5, 1.5)">
|
||||||
|
<path d="M7,0.288675135 L11.6291651,2.96132487 C11.9385662,3.13995766 12.1291651,3.47008468 12.1291651,3.82735027 L12.1291651,9.17264973 C12.1291651,9.52991532 11.9385662,9.86004234 11.6291651,10.0386751 L7,12.7113249 C6.69059892,12.8899577 6.30940108,12.8899577 6,12.7113249 L1.37083488,10.0386751 C1.0614338,9.86004234 0.870834875,9.52991532 0.870834875,9.17264973 L0.870834875,3.82735027 C0.870834875,3.47008468 1.0614338,3.13995766 1.37083488,2.96132487 L6,0.288675135 C6.30940108,0.11004234 6.69059892,0.11004234 7,0.288675135 Z" id="多边形"></path>
|
||||||
|
<polyline id="路径-15" points="0.931223827 3.37218958 6.5 6.5 6.5 12.8581283"></polyline>
|
||||||
|
<line x1="6.5" y1="6.49748419" x2="12.0714286" y2="3.37218958" id="路径-16"></line>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
17
web/src/assets/images/menu/spaceConfig_active.svg
Normal file
17
web/src/assets/images/menu/spaceConfig_active.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>模型 (1)</title>
|
||||||
|
<g id="v0.2.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="红熊空间-记忆管理" transform="translate(-24, -409)" stroke="#212332">
|
||||||
|
<g id="记忆对话备份-2" transform="translate(12, 401)">
|
||||||
|
<g id="模型-(1)" transform="translate(12, 8)">
|
||||||
|
<g id="编组-21" transform="translate(1.5, 1.5)">
|
||||||
|
<path d="M7,0.288675135 L11.6291651,2.96132487 C11.9385662,3.13995766 12.1291651,3.47008468 12.1291651,3.82735027 L12.1291651,9.17264973 C12.1291651,9.52991532 11.9385662,9.86004234 11.6291651,10.0386751 L7,12.7113249 C6.69059892,12.8899577 6.30940108,12.8899577 6,12.7113249 L1.37083488,10.0386751 C1.0614338,9.86004234 0.870834875,9.52991532 0.870834875,9.17264973 L0.870834875,3.82735027 C0.870834875,3.47008468 1.0614338,3.13995766 1.37083488,2.96132487 L6,0.288675135 C6.30940108,0.11004234 6.69059892,0.11004234 7,0.288675135 Z" id="多边形"></path>
|
||||||
|
<polyline id="路径-15" points="0.931223827 3.37218958 6.5 6.5 6.5 12.8581283"></polyline>
|
||||||
|
<line x1="6.5" y1="6.49748419" x2="12.0714286" y2="3.37218958" id="路径-16"></line>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
19
web/src/assets/images/userMemory/goto.svg
Normal file
19
web/src/assets/images/userMemory/goto.svg
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>编组 13备份</title>
|
||||||
|
<g id="V1.1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="红熊空间-记忆管理" transform="translate(-947, -144)">
|
||||||
|
<g id="1备份-2" transform="translate(651, 128)">
|
||||||
|
<g id="编组-13备份" transform="translate(296, 16)">
|
||||||
|
<rect id="矩形" stroke="#DFE4ED" x="0.5" y="0.5" width="27" height="27" rx="6"></rect>
|
||||||
|
<g id="进入@2x" transform="translate(5.8333, 5.8333)">
|
||||||
|
<g id="编组-11" transform="translate(2.0417, 2.5521)">
|
||||||
|
<path d="M5.42385066,3.34516089 L8.15899029,5.47250014 C8.23746067,5.5335329 8.25159666,5.64662254 8.1905639,5.72509292 C8.1813906,5.73688711 8.17078448,5.74749323 8.15899029,5.75666652 L5.42385066,7.88400578 C5.34538028,7.94503854 5.23229064,7.93090256 5.17125788,7.85243218 C5.14668314,7.82083621 5.13334107,7.78195037 5.13334107,7.74192259 L5.13334107,6.2384308 L5.13334107,6.2384308 L0,6.2384308 L0,4.99073587 L5.13334107,4.99073587 L5.13334107,3.48724407 C5.13334107,3.38783282 5.21392981,3.30724407 5.31334107,3.30724407 C5.35336884,3.30724407 5.39225469,3.32058615 5.42385066,3.34516089 Z" id="路径" fill="#5B6167" fill-rule="nonzero"></path>
|
||||||
|
<path d="M1.60417096,2.83745334 L1.60417096,0.9 C1.60417096,0.402943725 2.00711469,0 2.50417096,-1.11022302e-16 L10.3291667,-1.11022302e-16 C10.8262229,-2.22044605e-16 11.2291667,0.402943725 11.2291667,0.9 L11.2291667,10.3291667 C11.2291667,10.8262229 10.8262229,11.2291667 10.3291667,11.2291667 L2.50417096,11.2291667 C2.00711469,11.2291667 1.60417096,10.8262229 1.60417096,10.3291667 L1.60417096,8.46506778 L1.60417096,8.46506778" id="路径" stroke="#5B6167" stroke-width="1.1" stroke-linejoin="round"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, useCallback, useRef, type FC, type Key } from 'react';
|
import { useEffect, useState, type FC, type Key } from 'react';
|
||||||
import { Select } from 'antd'
|
import { Select } from 'antd'
|
||||||
import type { SelectProps, DefaultOptionType } from 'antd/es/select'
|
import type { SelectProps, DefaultOptionType } from 'antd/es/select'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -26,7 +26,7 @@ interface CustomSelectProps extends Omit<SelectProps, 'filterOption'> {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
filterOption?: (inputValue: string, option: DefaultOptionType) => boolean;
|
filterOption?: (inputValue: string, option?: DefaultOptionType) => boolean;
|
||||||
}
|
}
|
||||||
interface OptionType {
|
interface OptionType {
|
||||||
[key: string]: Key | string | number;
|
[key: string]: Key | string | number;
|
||||||
@@ -48,44 +48,27 @@ const CustomSelect: FC<CustomSelectProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [options, setOptions] = useState<OptionType[]>([]);
|
const [options, setOptions] = useState<OptionType[]>([]);
|
||||||
// 创建防抖定时器引用
|
|
||||||
const debounceRef = useRef<number>();
|
|
||||||
|
|
||||||
// 防抖搜索函数
|
// 默认模糊搜索函数
|
||||||
const handleSearch = useCallback((value?: string) => {
|
const defaultFilterOption = (inputValue: string, option?: DefaultOptionType) => {
|
||||||
// 清除之前的定时器
|
if (!option || !inputValue) return true;
|
||||||
if (debounceRef.current) {
|
const label = String(option.children || option.label || '');
|
||||||
clearTimeout(debounceRef.current);
|
return label.toLowerCase().includes(inputValue.toLowerCase());
|
||||||
}
|
};
|
||||||
|
// 组件挂载时获取初始数据
|
||||||
// 设置新的定时器
|
useEffect(() => {
|
||||||
debounceRef.current = window.setTimeout(() => {
|
request.get<ApiResponse<OptionType>>(url, params).then((res) => {
|
||||||
request.get<ApiResponse<OptionType>>(url, {...params, [optionFilterProp]: value}).then((res) => {
|
|
||||||
const data = res;
|
const data = res;
|
||||||
setOptions(Array.isArray(data) ? data || [] : Array.isArray(data?.items) ? data.items || [] : []);
|
setOptions(Array.isArray(data) ? data || [] : Array.isArray(data?.items) ? data.items || [] : []);
|
||||||
});
|
});
|
||||||
}, 300); // 300毫秒防抖延迟
|
}, []);
|
||||||
}, [url, params, optionFilterProp]);
|
|
||||||
|
|
||||||
// 组件挂载时获取初始数据
|
|
||||||
useEffect(() => {
|
|
||||||
handleSearch();
|
|
||||||
|
|
||||||
// 组件卸载时清除定时器
|
|
||||||
return () => {
|
|
||||||
if (debounceRef.current) {
|
|
||||||
clearTimeout(debounceRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [url, handleSearch]);
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
placeholder={placeholder ? placeholder : t('common.select')}
|
placeholder={placeholder ? placeholder : t('common.select')}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
defaultValue={hasAll ? null : undefined}
|
defaultValue={hasAll ? null : undefined}
|
||||||
showSearch={showSearch}
|
showSearch={showSearch}
|
||||||
onSearch={handleSearch}
|
filterOption={filterOption || defaultFilterOption}
|
||||||
filterOption={filterOption || false} // 禁用本地过滤,使用服务器端过滤
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{hasAll && (<Select.Option>{allTitle || t('common.all')}</Select.Option>)}
|
{hasAll && (<Select.Option>{allTitle || t('common.all')}</Select.Option>)}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ import apiKeyIcon from '@/assets/images/menu/apiKey.png';
|
|||||||
import apiKeyActiveIcon from '@/assets/images/menu/apiKey_active.png';
|
import apiKeyActiveIcon from '@/assets/images/menu/apiKey_active.png';
|
||||||
import pricingIcon from '@/assets/images/menu/pricing.svg'
|
import pricingIcon from '@/assets/images/menu/pricing.svg'
|
||||||
import pricingActiveIcon from '@/assets/images/menu/pricing_active.svg'
|
import pricingActiveIcon from '@/assets/images/menu/pricing_active.svg'
|
||||||
|
import spaceConfigIcon from '@/assets/images/menu/spaceConfig.svg'
|
||||||
|
import spaceConfigActiveIcon from '@/assets/images/menu/spaceConfig_active.svg'
|
||||||
|
|
||||||
// 图标路径映射表
|
// 图标路径映射表
|
||||||
const iconPathMap: Record<string, string> = {
|
const iconPathMap: Record<string, string> = {
|
||||||
@@ -68,7 +70,9 @@ const iconPathMap: Record<string, string> = {
|
|||||||
'apiKey': apiKeyIcon,
|
'apiKey': apiKeyIcon,
|
||||||
'apiKeyActive': apiKeyActiveIcon,
|
'apiKeyActive': apiKeyActiveIcon,
|
||||||
'pricing': pricingIcon,
|
'pricing': pricingIcon,
|
||||||
'pricingActive': pricingActiveIcon
|
'pricingActive': pricingActiveIcon,
|
||||||
|
'spaceConfig': spaceConfigIcon,
|
||||||
|
'spaceConfigActive': spaceConfigActiveIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { Sider } = Layout;
|
const { Sider } = Layout;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export const en = {
|
|||||||
modelManagement: 'Model Management',
|
modelManagement: 'Model Management',
|
||||||
memoryStore: 'Memory Store',
|
memoryStore: 'Memory Store',
|
||||||
apiParameters: 'API Parameters',
|
apiParameters: 'API Parameters',
|
||||||
userMemory: 'User Memory',
|
userMemory: 'Memory Store',
|
||||||
memberManagement: 'Member Management',
|
memberManagement: 'Member Management',
|
||||||
memorySummary: 'Memory Summary',
|
memorySummary: 'Memory Summary',
|
||||||
memoryConversation: 'Memory Validation',
|
memoryConversation: 'Memory Validation',
|
||||||
@@ -110,6 +110,7 @@ export const en = {
|
|||||||
pricing: 'Pricing Management',
|
pricing: 'Pricing Management',
|
||||||
orderPayment: 'Order Payment',
|
orderPayment: 'Order Payment',
|
||||||
orderHistory: 'Order History',
|
orderHistory: 'Order History',
|
||||||
|
spaceConfig: 'Space Configuration'
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
total_models: 'Total number of available models',
|
total_models: 'Total number of available models',
|
||||||
@@ -1232,6 +1233,8 @@ export const en = {
|
|||||||
hire_date: 'Hire Date',
|
hire_date: 'Hire Date',
|
||||||
memoryContent: 'Memory Content',
|
memoryContent: 'Memory Content',
|
||||||
created_at: 'Created At',
|
created_at: 'Created At',
|
||||||
|
updated_at: 'Updated At',
|
||||||
|
fullScreen: 'Full Screen',
|
||||||
|
|
||||||
memoryWindow: "{{name}}'s Window of Memory",
|
memoryWindow: "{{name}}'s Window of Memory",
|
||||||
memory_insight: 'Overall Overview',
|
memory_insight: 'Overall Overview',
|
||||||
@@ -1258,7 +1261,7 @@ export const en = {
|
|||||||
unix: 'items',
|
unix: 'items',
|
||||||
completeMemory: 'Complete Memory',
|
completeMemory: 'Complete Memory',
|
||||||
relationshipEvolution: 'Relationship Evolution',
|
relationshipEvolution: 'Relationship Evolution',
|
||||||
timelineMemories: 'Shared Memory Timeline',
|
timelineMemories: 'Long-term Memory',
|
||||||
emotionLine: 'Emotion Changes Over Time',
|
emotionLine: 'Emotion Changes Over Time',
|
||||||
interaction: 'Interaction Frequency & Relationship Stages',
|
interaction: 'Interaction Frequency & Relationship Stages',
|
||||||
timelines_memory: 'All',
|
timelines_memory: 'All',
|
||||||
@@ -1269,6 +1272,12 @@ export const en = {
|
|||||||
negative: 'Negative Emotion',
|
negative: 'Negative Emotion',
|
||||||
neutral: 'Neutral Emotion',
|
neutral: 'Neutral Emotion',
|
||||||
interactionCountData: 'Interaction Count',
|
interactionCountData: 'Interaction Count',
|
||||||
|
capacity: 'Capacity',
|
||||||
|
type: 'Type',
|
||||||
|
person: 'Personal',
|
||||||
|
memoryNum: 'memories',
|
||||||
|
memory_config_name: 'Memory Engine',
|
||||||
|
searchPlaceholder: 'Search memory store name',
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
createSpace: 'Create Space',
|
createSpace: 'Create Space',
|
||||||
@@ -1284,7 +1293,8 @@ export const en = {
|
|||||||
neo4jDesc: 'Based on knowledge graph, suitable for relational reasoning and path query',
|
neo4jDesc: 'Based on knowledge graph, suitable for relational reasoning and path query',
|
||||||
llmModel: 'LLM Model',
|
llmModel: 'LLM Model',
|
||||||
embeddingModel: 'Embedding Model',
|
embeddingModel: 'Embedding Model',
|
||||||
rerankModel: 'Rerank Model'
|
rerankModel: 'Rerank Model',
|
||||||
|
configAlert: 'Space model configuration ensures that the space can correctly call the corresponding models to process business data during runtime.',
|
||||||
},
|
},
|
||||||
memoryExtractionEngine: {
|
memoryExtractionEngine: {
|
||||||
title: 'Memory Engine Module Configuration Center',
|
title: 'Memory Engine Module Configuration Center',
|
||||||
@@ -1459,6 +1469,8 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
quickReply: 'Quick Reply',
|
quickReply: 'Quick Reply',
|
||||||
web_search: 'Online search',
|
web_search: 'Online search',
|
||||||
memory: 'Memory',
|
memory: 'Memory',
|
||||||
|
memoryConversationAnalysisEmpty: 'There is currently no dialogue analysis content available',
|
||||||
|
memoryConversationAnalysisEmptySubTitle: 'After entering your user ID, click on "Test Memory" to view the conversation memory',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
title: 'Red Bear Memory Science',
|
title: 'Red Bear Memory Science',
|
||||||
@@ -1613,19 +1625,17 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
|
|
||||||
JsonTool_desc: 'Data Format Conversion',
|
JsonTool_desc: 'Data Format Conversion',
|
||||||
JsonTool_features: 'JSON formatting, compression, validation and conversion functions',
|
JsonTool_features: 'JSON formatting, compression, validation and conversion functions',
|
||||||
jsonFormat: 'JSON Formatting',
|
jsonParse: 'JSON Parse',
|
||||||
jsonGzip: 'JSON Compression',
|
jsonInsert: 'JSON Insert',
|
||||||
jsonCheck: 'JSON Validation',
|
jsonReplace: 'JSON Validation',
|
||||||
jsonConversion: 'Format Conversion',
|
jsonDelete: 'JSON Delete',
|
||||||
jsonEg: 'Example JSON',
|
jsonEg: 'Example JSON',
|
||||||
enterJson: 'Enter JSON',
|
enterJson: 'Enter JSON',
|
||||||
jsonPlaceholder: 'Enter JSON data, e.g.: {"name": "test", "value": 123}',
|
jsonPlaceholder: 'Enter JSON data, e.g.: {"name": "test", "value": 123}',
|
||||||
clear: 'Clear',
|
clear: 'Clear',
|
||||||
parse: 'Paste',
|
paste: 'Paste',
|
||||||
format: 'Format',
|
parse: 'Parse',
|
||||||
minify: 'Minify',
|
json_path: 'JSON Path Parameters',
|
||||||
validate: 'Validate',
|
|
||||||
convert: 'Escape',
|
|
||||||
outputResult: 'Output Result',
|
outputResult: 'Output Result',
|
||||||
validJosn: 'JSON format is correct, validation passed!',
|
validJosn: 'JSON format is correct, validation passed!',
|
||||||
|
|
||||||
@@ -1944,7 +1954,8 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
|||||||
variableConfig: 'Variable Configuration',
|
variableConfig: 'Variable Configuration',
|
||||||
variableRequired: 'Required',
|
variableRequired: 'Required',
|
||||||
addMessage: 'Add Message',
|
addMessage: 'Add Message',
|
||||||
answerDesc: 'Reply'
|
answerDesc: 'Reply',
|
||||||
|
addNode: 'Add Node',
|
||||||
},
|
},
|
||||||
emotionEngine: {
|
emotionEngine: {
|
||||||
emotionEngineConfig: 'Emotion Engine Configuration',
|
emotionEngineConfig: 'Emotion Engine Configuration',
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export const zh = {
|
|||||||
modelManagement: '模型管理',
|
modelManagement: '模型管理',
|
||||||
memoryStore: '记忆存储',
|
memoryStore: '记忆存储',
|
||||||
apiParameters: 'API参数',
|
apiParameters: 'API参数',
|
||||||
userMemory: '用户记忆',
|
userMemory: '记忆库',
|
||||||
memberManagement: '成员管理',
|
memberManagement: '成员管理',
|
||||||
memorySummary: '记忆摘要',
|
memorySummary: '记忆摘要',
|
||||||
memoryConversation: '记忆验证',
|
memoryConversation: '记忆验证',
|
||||||
@@ -102,7 +102,7 @@ export const zh = {
|
|||||||
knowledgeShare: '详情',
|
knowledgeShare: '详情',
|
||||||
knowledgeCreateDataset: '新建数据集',
|
knowledgeCreateDataset: '新建数据集',
|
||||||
knowledgeDocumentDetails: '详情',
|
knowledgeDocumentDetails: '详情',
|
||||||
userMemoryDetail: '用户记忆详情',
|
userMemoryDetail: '记忆库详情',
|
||||||
toolManagement: '工具管理',
|
toolManagement: '工具管理',
|
||||||
emotionEngine: '情感引擎',
|
emotionEngine: '情感引擎',
|
||||||
statementDetail: '情绪记忆',
|
statementDetail: '情绪记忆',
|
||||||
@@ -110,6 +110,7 @@ export const zh = {
|
|||||||
pricing: '收费管理',
|
pricing: '收费管理',
|
||||||
orderPayment: '订单支付',
|
orderPayment: '订单支付',
|
||||||
orderHistory: '订单记录',
|
orderHistory: '订单记录',
|
||||||
|
spaceConfig: '空间配置'
|
||||||
},
|
},
|
||||||
knowledgeBase: {
|
knowledgeBase: {
|
||||||
home: '首页',
|
home: '首页',
|
||||||
@@ -1338,7 +1339,7 @@ export const zh = {
|
|||||||
unix: '个',
|
unix: '个',
|
||||||
completeMemory: '完整记忆',
|
completeMemory: '完整记忆',
|
||||||
relationshipEvolution: '关系演化',
|
relationshipEvolution: '关系演化',
|
||||||
timelineMemories: '共同记忆时间线',
|
timelineMemories: '长期记忆',
|
||||||
emotionLine: '情绪随时间变化',
|
emotionLine: '情绪随时间变化',
|
||||||
interaction: '互动频率 & 关系阶段',
|
interaction: '互动频率 & 关系阶段',
|
||||||
timelines_memory: '全部',
|
timelines_memory: '全部',
|
||||||
@@ -1349,6 +1350,12 @@ export const zh = {
|
|||||||
negative: '负向情绪',
|
negative: '负向情绪',
|
||||||
neutral: '中性情绪',
|
neutral: '中性情绪',
|
||||||
interactionCountData: '互动次数',
|
interactionCountData: '互动次数',
|
||||||
|
capacity: '容量',
|
||||||
|
type: '类型',
|
||||||
|
person: '个人',
|
||||||
|
memoryNum: '条记忆',
|
||||||
|
memory_config_name: '记忆引擎',
|
||||||
|
searchPlaceholder: '搜索记忆库名称',
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
createSpace: '创建空间',
|
createSpace: '创建空间',
|
||||||
@@ -1364,7 +1371,8 @@ export const zh = {
|
|||||||
neo4jDesc: '基于知识图谱,适合关系推理和路径查询',
|
neo4jDesc: '基于知识图谱,适合关系推理和路径查询',
|
||||||
llmModel: 'LLM 模型',
|
llmModel: 'LLM 模型',
|
||||||
embeddingModel: 'Embedding 模型',
|
embeddingModel: 'Embedding 模型',
|
||||||
rerankModel: 'Rerank 模型'
|
rerankModel: 'Rerank 模型',
|
||||||
|
configAlert: '空间模型配置为空间的模型模型,保障空间运行时能正确的调用到相应的模型来处理业务数据。',
|
||||||
},
|
},
|
||||||
memoryExtractionEngine: {
|
memoryExtractionEngine: {
|
||||||
title: '记忆引擎模块配置中心',
|
title: '记忆引擎模块配置中心',
|
||||||
@@ -1537,6 +1545,8 @@ export const zh = {
|
|||||||
quickReply: '快速回复',
|
quickReply: '快速回复',
|
||||||
web_search: '联网搜索',
|
web_search: '联网搜索',
|
||||||
memory: '记忆',
|
memory: '记忆',
|
||||||
|
memoryConversationAnalysisEmpty: '目前没有可用的对话分析内容',
|
||||||
|
memoryConversationAnalysisEmptySubTitle: '输入您的用户ID后,点击"测试记忆"查看对话记忆',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
title: '红熊记忆科学',
|
title: '红熊记忆科学',
|
||||||
@@ -1711,19 +1721,17 @@ export const zh = {
|
|||||||
|
|
||||||
JsonTool_desc: '数据格式转换',
|
JsonTool_desc: '数据格式转换',
|
||||||
JsonTool_features: 'JSON格式化、压缩、验证和转换功能',
|
JsonTool_features: 'JSON格式化、压缩、验证和转换功能',
|
||||||
jsonFormat: 'JSON格式化',
|
jsonParse: 'JSON解析',
|
||||||
jsonGzip: 'JSON压缩',
|
jsonInsert: 'JSON插入',
|
||||||
jsonCheck: 'JSON验证',
|
jsonReplace: 'JSON验证',
|
||||||
jsonConversion: '格式转换',
|
jsonDelete: 'JSON删除',
|
||||||
jsonEg: '示例JSON',
|
jsonEg: '示例JSON',
|
||||||
enterJson: '输入JSON',
|
enterJson: '输入JSON',
|
||||||
jsonPlaceholder: '输入JSON数据,例如:{"name": "测试", "value": 123}',
|
jsonPlaceholder: '输入JSON数据,例如:{"name": "测试", "value": 123}',
|
||||||
clear: '清空',
|
clear: '清空',
|
||||||
parse: '粘贴',
|
paste: '粘贴',
|
||||||
format: '格式化',
|
parse: '解析',
|
||||||
minify: '压缩',
|
json_path: 'JSON 路径参数',
|
||||||
validate: '验证',
|
|
||||||
convert: '转义',
|
|
||||||
outputResult: '输出结果',
|
outputResult: '输出结果',
|
||||||
validJosn: 'JSON格式正确,验证通过!',
|
validJosn: 'JSON格式正确,验证通过!',
|
||||||
|
|
||||||
@@ -2043,7 +2051,8 @@ export const zh = {
|
|||||||
variableConfig: '变量配置',
|
variableConfig: '变量配置',
|
||||||
variableRequired: '必填',
|
variableRequired: '必填',
|
||||||
addMessage: '添加消息',
|
addMessage: '添加消息',
|
||||||
answerDesc: '回复'
|
answerDesc: '回复',
|
||||||
|
addNode: '添加节点',
|
||||||
},
|
},
|
||||||
emotionEngine: {
|
emotionEngine: {
|
||||||
emotionEngineConfig: '情感引擎配置',
|
emotionEngineConfig: '情感引擎配置',
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ const componentMap: Record<string, LazyExoticComponent<ComponentType<object>>> =
|
|||||||
OrderHistory: lazy(() => import('@/views/OrderHistory')),
|
OrderHistory: lazy(() => import('@/views/OrderHistory')),
|
||||||
Pricing: lazy(() => import('@/views/Pricing')),
|
Pricing: lazy(() => import('@/views/Pricing')),
|
||||||
ToolManagement: lazy(() => import('@/views/ToolManagement')),
|
ToolManagement: lazy(() => import('@/views/ToolManagement')),
|
||||||
|
SpaceConfig: lazy(() => import('@/views/SpaceConfig')),
|
||||||
Login: lazy(() => import('@/views/Login')),
|
Login: lazy(() => import('@/views/Login')),
|
||||||
InviteRegister: lazy(() => import('@/views/InviteRegister')),
|
InviteRegister: lazy(() => import('@/views/InviteRegister')),
|
||||||
NoPermission: lazy(() => import('@/views/NoPermission')),
|
NoPermission: lazy(() => import('@/views/NoPermission')),
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
{ "path": "/api-key", "element": "ApiKeyManagement" },
|
{ "path": "/api-key", "element": "ApiKeyManagement" },
|
||||||
{ "path": "/emotion-engine/:id", "element": "EmotionEngine" },
|
{ "path": "/emotion-engine/:id", "element": "EmotionEngine" },
|
||||||
{ "path": "/reflection-engine/:id", "element": "SelfReflectionEngine" },
|
{ "path": "/reflection-engine/:id", "element": "SelfReflectionEngine" },
|
||||||
|
{ "path": "/space-config", "element": "SpaceConfig" },
|
||||||
{ "path": "/no-permission", "element": "NoPermission" },
|
{ "path": "/no-permission", "element": "NoPermission" },
|
||||||
{ "path": "/*", "element": "NotFound" }
|
{ "path": "/*", "element": "NotFound" }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -376,6 +376,21 @@
|
|||||||
"icon": null,
|
"icon": null,
|
||||||
"iconActive": null,
|
"iconActive": null,
|
||||||
"subs": null
|
"subs": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"parent": 0,
|
||||||
|
"code": "spaceConfig",
|
||||||
|
"label": "空间配置",
|
||||||
|
"i18nKey": "menu.spaceConfig",
|
||||||
|
"path": "/space-config",
|
||||||
|
"enable": true,
|
||||||
|
"display": true,
|
||||||
|
"level": 1,
|
||||||
|
"sort": 0,
|
||||||
|
"icon": null,
|
||||||
|
"iconActive": null,
|
||||||
|
"subs": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
118
web/src/views/SpaceConfig/index.tsx
Normal file
118
web/src/views/SpaceConfig/index.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { type FC, useEffect, useState } from 'react';
|
||||||
|
import { Form, App, Button, Skeleton } from 'antd';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import type { SpaceConfigData } from './types'
|
||||||
|
import { getWorkspaceModels, updateWorkspaceModels } from '@/api/workspaces'
|
||||||
|
import { getModelListUrl } from '@/api/models'
|
||||||
|
import CustomSelect from '@/components/CustomSelect'
|
||||||
|
import RbAlert from '@/components/RbAlert';
|
||||||
|
|
||||||
|
const SpaceConfig: FC = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const [pageLoading, setPageLoding] = useState(false)
|
||||||
|
const [form] = Form.useForm<SpaceConfigData>();
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
const values = Form.useWatch([], form);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setPageLoding(true)
|
||||||
|
getWorkspaceModels().then((res) => {
|
||||||
|
const { llm, embedding, rerank } = res as SpaceConfigData
|
||||||
|
form.setFieldsValue({
|
||||||
|
llm,
|
||||||
|
embedding,
|
||||||
|
rerank
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setPageLoding(false)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
// 封装保存方法,添加提交逻辑
|
||||||
|
const handleSave = () => {
|
||||||
|
form
|
||||||
|
.validateFields()
|
||||||
|
.then(() => {
|
||||||
|
setLoading(true)
|
||||||
|
updateWorkspaceModels(values)
|
||||||
|
.then(() => {
|
||||||
|
setLoading(false)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setLoading(false)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('err', err)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rb:h-full rb:max-w-140 rb:mx-auto">
|
||||||
|
{pageLoading
|
||||||
|
? <Skeleton active />
|
||||||
|
: <Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
label={t('space.llmModel')}
|
||||||
|
name="llm"
|
||||||
|
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||||
|
>
|
||||||
|
<CustomSelect
|
||||||
|
url={getModelListUrl}
|
||||||
|
params={{ type: 'llm', pagesize: 100 }}
|
||||||
|
valueKey="id"
|
||||||
|
labelKey="name"
|
||||||
|
hasAll={false}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('space.embeddingModel')}
|
||||||
|
name="embedding"
|
||||||
|
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||||
|
>
|
||||||
|
<CustomSelect
|
||||||
|
url={getModelListUrl}
|
||||||
|
params={{ type: 'embedding', pagesize: 100 }}
|
||||||
|
valueKey="id"
|
||||||
|
labelKey="name"
|
||||||
|
hasAll={false}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t('space.rerankModel')}
|
||||||
|
name="rerank"
|
||||||
|
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
||||||
|
>
|
||||||
|
<CustomSelect
|
||||||
|
url={getModelListUrl}
|
||||||
|
params={{ type: 'rerank', pagesize: 100 }}
|
||||||
|
valueKey="id"
|
||||||
|
labelKey="name"
|
||||||
|
hasAll={false}
|
||||||
|
style={{width: '100%'}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<RbAlert>{t('space.configAlert')}</RbAlert>
|
||||||
|
|
||||||
|
<Form.Item className="rb:text-right">
|
||||||
|
<Button type="primary" className="rb:mt-6" onClick={handleSave} loading={loading}>
|
||||||
|
{t('common.save')}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpaceConfig;
|
||||||
8
web/src/views/SpaceConfig/types.ts
Normal file
8
web/src/views/SpaceConfig/types.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export interface SpaceConfigData {
|
||||||
|
llm: string;
|
||||||
|
embedding: string;
|
||||||
|
rerank: string;
|
||||||
|
}
|
||||||
|
export interface SpaceConfigRef {
|
||||||
|
handleOpen: () => void;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
import { forwardRef, useImperativeHandle, useState } from 'react';
|
||||||
import { Form, Input, Button, Space, Tree } from 'antd';
|
import { Form, Input, Button, Space } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { TreeDataNode } from 'antd';
|
import type { TreeDataNode } from 'antd';
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import { execute } from '@/api/tools';
|
|||||||
const JsonToolModal = forwardRef<JsonToolModalRef>((_props, ref) => {
|
const JsonToolModal = forwardRef<JsonToolModalRef>((_props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [form] = Form.useForm<{ json: string; }>();
|
const [form] = Form.useForm<{ json: string; json_path: string; }>();
|
||||||
const [data, setData] = useState<ToolItem>({} as ToolItem)
|
const [data, setData] = useState<ToolItem>({} as ToolItem)
|
||||||
const [formatValue, setFormatValue] = useState<string | Record<string, any> | null>(null)
|
const [formatValue, setFormatValue] = useState<string | Record<string, any> | null>(null)
|
||||||
|
|
||||||
@@ -60,44 +60,29 @@ const JsonToolModal = forwardRef<JsonToolModalRef>((_props, ref) => {
|
|||||||
}
|
}
|
||||||
const handleOperate = (type: string) => {
|
const handleOperate = (type: string) => {
|
||||||
const json = form.getFieldValue('json')
|
const json = form.getFieldValue('json')
|
||||||
|
const json_path = form.getFieldValue('json_path')
|
||||||
if (!json || !data.id) return
|
if (!json || !data.id) return
|
||||||
let params: ExecuteData = {
|
let params: ExecuteData = {
|
||||||
tool_id: data.id,
|
tool_id: data.id,
|
||||||
parameters: {
|
parameters: {
|
||||||
operation: type,
|
operation: type,
|
||||||
input_data: json
|
input_data: json,
|
||||||
|
json_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (type === 'format') {
|
if (type === 'parse') {
|
||||||
params = {
|
params = {
|
||||||
...params,
|
...params,
|
||||||
parameters: {
|
parameters: {
|
||||||
...params.parameters,
|
...params.parameters,
|
||||||
indent: 2,
|
|
||||||
ensure_ascii: false,
|
|
||||||
sort_keys: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(params)
|
execute(params)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const { data } = res as {data: {
|
const { data } = res as { data: string; }
|
||||||
formatted_json: string;
|
setFormatValue(data);
|
||||||
minified_json: string;
|
|
||||||
is_valid: boolean;
|
|
||||||
converted_json: string;
|
|
||||||
error: string;
|
|
||||||
structure: Record<string, string | number>
|
|
||||||
}}
|
|
||||||
switch (type) {
|
|
||||||
case 'format':
|
|
||||||
setFormatValue(data.formatted_json);
|
|
||||||
break
|
|
||||||
case 'minify':
|
|
||||||
setFormatValue(data.minified_json)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const clear = () => {
|
const clear = () => {
|
||||||
@@ -126,15 +111,20 @@ const JsonToolModal = forwardRef<JsonToolModalRef>((_props, ref) => {
|
|||||||
label={<Space size={8}>
|
label={<Space size={8}>
|
||||||
{t('tool.enterJson')}
|
{t('tool.enterJson')}
|
||||||
<Button onClick={clear}>{t('tool.clear')}</Button>
|
<Button onClick={clear}>{t('tool.clear')}</Button>
|
||||||
<Button onClick={handleParse}>{t('tool.parse')}</Button>
|
<Button onClick={handleParse}>{t('tool.paste')}</Button>
|
||||||
</Space>}
|
</Space>}
|
||||||
>
|
>
|
||||||
<Input.TextArea rows={10} placeholder={t('tool.jsonPlaceholder')} />
|
<Input.TextArea rows={10} placeholder={t('tool.jsonPlaceholder')} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
<FormItem
|
||||||
|
name="json_path"
|
||||||
|
label={t('tool.json_path')}
|
||||||
|
>
|
||||||
|
<Input placeholder={t('common.pleaseEnter')} />
|
||||||
|
</FormItem>
|
||||||
|
|
||||||
<Space size={8} className="rb:mb-3">
|
<Space size={8} className="rb:mb-3">
|
||||||
<Button onClick={() => handleOperate('format')}>{t('tool.format')}</Button>
|
<Button onClick={() => handleOperate('parse')}>{t('tool.parse')}</Button>
|
||||||
<Button onClick={() => handleOperate('minify')}>{t('tool.minify')}</Button>
|
|
||||||
</Space>
|
</Space>
|
||||||
<FormItem
|
<FormItem
|
||||||
label={t('tool.outputResult')}
|
label={t('tool.outputResult')}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface CurrentTimeObj {
|
|||||||
iso_format: string;
|
iso_format: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
timestamp_ms: string;
|
timestamp_ms: string;
|
||||||
|
utc_datetime: string;
|
||||||
}
|
}
|
||||||
const TimeToolModal = forwardRef<TimeToolModalRef>((_props, ref) => {
|
const TimeToolModal = forwardRef<TimeToolModalRef>((_props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -88,8 +89,8 @@ const TimeToolModal = forwardRef<TimeToolModalRef>((_props, ref) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
const response = res as { data: CurrentTimeObj }
|
const response = res as { data: string }
|
||||||
setTimestampFormat(response.data.datetime)
|
setTimestampFormat(response.data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const handleChangeFormatType = () => {
|
const handleChangeFormatType = () => {
|
||||||
@@ -149,7 +150,7 @@ const TimeToolModal = forwardRef<TimeToolModalRef>((_props, ref) => {
|
|||||||
<Input disabled value={currentTime?.datetime} />
|
<Input disabled value={currentTime?.datetime} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label={t('tool.utcTime')} >
|
<FormItem label={t('tool.utcTime')} >
|
||||||
<Input disabled value={currentTime?.iso_format} />
|
<Input disabled value={currentTime?.utc_datetime} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label={t('tool.secondsTimestamp')} >
|
<FormItem label={t('tool.secondsTimestamp')} >
|
||||||
<Input disabled value={currentTime?.timestamp} />
|
<Input disabled value={currentTime?.timestamp} />
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ export const InnerConfigData: Record<string, InnerConfigItem> = {
|
|||||||
},
|
},
|
||||||
JsonTool: {
|
JsonTool: {
|
||||||
features: [
|
features: [
|
||||||
'jsonFormat',
|
'jsonParse',
|
||||||
'jsonGzip',
|
'jsonInsert',
|
||||||
'jsonCheck',
|
'jsonReplace',
|
||||||
'jsonConversion'
|
'jsonDelete'
|
||||||
],
|
],
|
||||||
eg: '{"name":"工具","tool_class":"内置"}'
|
eg: '{"name":"工具","tool_class":"内置"}'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export interface ExecuteData {
|
|||||||
ensure_ascii?: boolean;
|
ensure_ascii?: boolean;
|
||||||
sort_keys?: boolean;
|
sort_keys?: boolean;
|
||||||
input_data?: string;
|
input_data?: string;
|
||||||
|
json_path?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export interface CustomToolModalRef {
|
export interface CustomToolModalRef {
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
import { forwardRef, useImperativeHandle, useState } from 'react';
|
|
||||||
import { Form, App } from 'antd';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import type { ConfigModalData, ConfigModalRef } from '../types'
|
|
||||||
import { getWorkspaceModels, updateWorkspaceModels } from '@/api/workspaces'
|
|
||||||
import { getModelListUrl } from '@/api/models'
|
|
||||||
import CustomSelect from '@/components/CustomSelect'
|
|
||||||
import RbModal from '@/components/RbModal'
|
|
||||||
|
|
||||||
const ConfigModal = forwardRef<ConfigModalRef>((_props, ref) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { message } = App.useApp();
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const [form] = Form.useForm<ConfigModalData>();
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
const values = Form.useWatch([], form);
|
|
||||||
|
|
||||||
// 封装取消方法,添加关闭弹窗逻辑
|
|
||||||
const handleClose = () => {
|
|
||||||
setVisible(false);
|
|
||||||
form.resetFields();
|
|
||||||
setLoading(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpen = () => {
|
|
||||||
getWorkspaceModels().then((res) => {
|
|
||||||
const { llm, embedding, rerank } = res as ConfigModalData
|
|
||||||
form.setFieldsValue({
|
|
||||||
llm,
|
|
||||||
embedding,
|
|
||||||
rerank
|
|
||||||
})
|
|
||||||
})
|
|
||||||
setVisible(true);
|
|
||||||
};
|
|
||||||
// 封装保存方法,添加提交逻辑
|
|
||||||
const handleSave = () => {
|
|
||||||
form
|
|
||||||
.validateFields()
|
|
||||||
.then(() => {
|
|
||||||
setLoading(true)
|
|
||||||
updateWorkspaceModels(values)
|
|
||||||
.then(() => {
|
|
||||||
setLoading(false)
|
|
||||||
handleClose()
|
|
||||||
message.success(t('common.updateSuccess'))
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setLoading(false)
|
|
||||||
});
|
|
||||||
|
|
||||||
handleClose()
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log('err', err)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 暴露给父组件的方法
|
|
||||||
useImperativeHandle(ref, () => ({
|
|
||||||
handleOpen,
|
|
||||||
handleClose
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RbModal
|
|
||||||
title={t(`userMemory.editConfig`)}
|
|
||||||
open={visible}
|
|
||||||
onCancel={handleClose}
|
|
||||||
okText={t('common.save')}
|
|
||||||
onOk={handleSave}
|
|
||||||
confirmLoading={loading}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
label={t('space.llmModel')}
|
|
||||||
name="llm"
|
|
||||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
|
||||||
>
|
|
||||||
<CustomSelect
|
|
||||||
url={getModelListUrl}
|
|
||||||
params={{ type: 'llm', pagesize: 100 }}
|
|
||||||
valueKey="id"
|
|
||||||
labelKey="name"
|
|
||||||
hasAll={false}
|
|
||||||
style={{width: '100%'}}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('space.embeddingModel')}
|
|
||||||
name="embedding"
|
|
||||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
|
||||||
>
|
|
||||||
<CustomSelect
|
|
||||||
url={getModelListUrl}
|
|
||||||
params={{ type: 'embedding', pagesize: 100 }}
|
|
||||||
valueKey="id"
|
|
||||||
labelKey="name"
|
|
||||||
hasAll={false}
|
|
||||||
style={{width: '100%'}}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t('space.rerankModel')}
|
|
||||||
name="rerank"
|
|
||||||
rules={[{ required: true, message: t('common.pleaseSelect') }]}
|
|
||||||
>
|
|
||||||
<CustomSelect
|
|
||||||
url={getModelListUrl}
|
|
||||||
params={{ type: 'rerank', pagesize: 100 }}
|
|
||||||
valueKey="id"
|
|
||||||
labelKey="name"
|
|
||||||
hasAll={false}
|
|
||||||
style={{width: '100%'}}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</RbModal>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ConfigModal;
|
|
||||||
@@ -1,56 +1,28 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Row, Col, Radio, Button, List, Skeleton, Space } from 'antd';
|
import { Row, Col, List, Skeleton } from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
|
||||||
import type { RadioChangeEvent } from 'antd';
|
|
||||||
import { AppstoreOutlined, MenuOutlined } from '@ant-design/icons';
|
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
|
|
||||||
import type { Data, ConfigModalRef } from './types'
|
import type { Data } from './types'
|
||||||
import totalNum from '@/assets/images/memory/totalNum.svg'
|
import { getUserMemoryList } from '@/api/memory';
|
||||||
import onlineNum from '@/assets/images/memory/onlineNum.svg'
|
|
||||||
import Table from '@/components/Table'
|
|
||||||
import { getTotalEndUsers, userMemoryListUrl, getUserMemoryList } from '@/api/memory';
|
|
||||||
import ConfigModal from './components/ConfigModal';
|
|
||||||
import { useUser } from '@/store/user'
|
import { useUser } from '@/store/user'
|
||||||
|
import RbCard from '@/components/RbCard/Card'
|
||||||
|
import SearchInput from '@/components/SearchInput';
|
||||||
|
|
||||||
const bgList = [
|
|
||||||
'linear-gradient( 180deg, #F1F6FE 0%, #FBFDFF 100%)',
|
|
||||||
'linear-gradient( 180deg, #F1F9FE 0%, #FBFDFF 100%)',
|
|
||||||
'linear-gradient( 180deg, #FEFBF7 0%, #FBFDFF 100%)',
|
|
||||||
'linear-gradient( 180deg, #F1F9FE 0%, #FBFDFF 100%)',
|
|
||||||
]
|
|
||||||
|
|
||||||
const countList = [
|
|
||||||
'total_num', 'online_num',
|
|
||||||
]
|
|
||||||
const IconList: Record<string, string> = {
|
|
||||||
total_num: totalNum,
|
|
||||||
online_num: onlineNum,
|
|
||||||
}
|
|
||||||
export default function UserMemory() {
|
export default function UserMemory() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { storageType } = useUser()
|
const { storageType } = useUser()
|
||||||
const configModalRef = useRef<ConfigModalRef>(null)
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
const [data, setData] = useState<Data[]>([]);
|
const [data, setData] = useState<Data[]>([]);
|
||||||
const [countData, setCountData] = useState<Record<string, number>>({});
|
const [search, setSearch] = useState<string | undefined>(undefined);
|
||||||
const [layout, setLayout] = useState<'card' | 'list'>('card');
|
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getCountData()
|
|
||||||
getData()
|
getData()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 用户记忆统计
|
|
||||||
const getCountData = () => {
|
|
||||||
getTotalEndUsers().then((res) => {
|
|
||||||
setCountData(res as Record<string, number> || {})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
getUserMemoryList().then((res) => {
|
getUserMemoryList().then((res) => {
|
||||||
@@ -60,7 +32,6 @@ export default function UserMemory() {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
console.log('storageType', storageType)
|
|
||||||
const handleViewDetail = (id: string | number) => {
|
const handleViewDetail = (id: string | number) => {
|
||||||
switch (storageType) {
|
switch (storageType) {
|
||||||
case 'neo4j':
|
case 'neo4j':
|
||||||
@@ -70,112 +41,77 @@ export default function UserMemory() {
|
|||||||
navigate(`/user-memory/${id}`)
|
navigate(`/user-memory/${id}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleChangeLayout = (e: RadioChangeEvent) => {
|
const handleViewMemoryConfig = () => {
|
||||||
const type = e.target.value
|
navigate(`/memory`)
|
||||||
setLayout(type)
|
|
||||||
}
|
}
|
||||||
// 表格列配置
|
|
||||||
const columns: ColumnsType = [
|
const filterData = useMemo(() => {
|
||||||
{
|
if (search && search.trim() !== '') {
|
||||||
title: t('userMemory.user'),
|
return data.filter((item) => {
|
||||||
dataIndex: 'end_user',
|
const { end_user } = item as Data;
|
||||||
key: 'end_user',
|
const name = end_user?.other_name && end_user?.other_name !== '' ? end_user?.other_name : end_user?.id
|
||||||
render: (value) => value?.other_name && value?.other_name !== '' ? value?.other_name : value?.id || '-'
|
return name?.includes(search)
|
||||||
},
|
})
|
||||||
{
|
}
|
||||||
title: t('userMemory.knowledgeEntryCount'),
|
|
||||||
dataIndex: 'memory_num',
|
return data
|
||||||
key: 'memory_num',
|
}, [search, data])
|
||||||
render: (value) => value?.total || 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t('common.operation'),
|
|
||||||
key: 'action',
|
|
||||||
render: (_, record) => (
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
onClick={() => handleViewDetail(record.end_user?.id)}
|
|
||||||
>
|
|
||||||
{t('common.viewDetail')}
|
|
||||||
</Button>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Row gutter={16} className="rb:mb-4">
|
<Row gutter={16} className="rb:mb-4">
|
||||||
{countList.map(key => (
|
<Col span={8}>
|
||||||
<Col key={key} span={6}>
|
<SearchInput
|
||||||
<div className="rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-xl rb:p-[18px_20px_20px_20px]">
|
placeholder={t('userMemory.searchPlaceholder')}
|
||||||
<div className="rb:text-[28px] rb:font-extrabold rb:leading-8.75 rb:flex rb:items-center rb:justify-between rb:mb-3">
|
onSearch={(value) => setSearch(value)}
|
||||||
{countData[key] || 0}{key === 'avgInteractionTime' ? 's' : ''}
|
style={{ width: '100%' }}
|
||||||
<img className="rb:w-6 rb:h-6" src={IconList[key]} />
|
/>
|
||||||
</div>
|
|
||||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`userMemory.${key}`)}</div>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
<Col span={12} className="rb:text-right">
|
|
||||||
<Space>
|
|
||||||
<Button type="primary" onClick={() => configModalRef?.current?.handleOpen()}>{t('userMemory.chooseModel')}</Button>
|
|
||||||
<Radio.Group value={layout} onChange={handleChangeLayout}>
|
|
||||||
<Radio.Button value="card" disabled={layout === 'card'}><AppstoreOutlined /></Radio.Button>
|
|
||||||
<Radio.Button value="list" disabled={layout === 'list'}><MenuOutlined /></Radio.Button>
|
|
||||||
</Radio.Group>
|
|
||||||
</Space>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{layout === 'card' &&
|
|
||||||
<>
|
|
||||||
{loading ?
|
{loading ?
|
||||||
<Skeleton active />
|
<Skeleton active />
|
||||||
: data.length > 0 ? (
|
: filterData.length > 0 ? (
|
||||||
<List
|
<List
|
||||||
grid={{ gutter: 16, column: 4 }}
|
grid={{ gutter: 16, column: 3 }}
|
||||||
dataSource={data}
|
dataSource={filterData}
|
||||||
renderItem={(item, index) => {
|
renderItem={(item, index) => {
|
||||||
const { end_user, memory_num } = item as Data;
|
const { end_user, memory_num, memory_config } = item as Data;
|
||||||
const name = end_user?.other_name && end_user?.other_name !== '' ? end_user?.other_name : end_user?.id
|
const name = end_user?.other_name && end_user?.other_name !== '' ? end_user?.other_name : end_user?.id
|
||||||
return (
|
return (
|
||||||
<List.Item key={index}>
|
<List.Item key={index}>
|
||||||
<div
|
<RbCard
|
||||||
className="rb:p-5 rb:rounded-xl rb:border rb:border-[#DFE4ED] rb:cursor-pointer"
|
avatar={<div className="rb:w-12 rb:h-12 rb:text-center rb:font-semibold rb:text-[28px] rb:leading-12 rb:rounded-lg rb:text-[#FBFDFF] rb:bg-[#155EEF] rb:mr-2">{name[0]}</div>}
|
||||||
style={{
|
title={name || '-'}
|
||||||
background: bgList[index % bgList.length],
|
extra={<div
|
||||||
}}
|
className="rb:w-7 rb:h-7 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/goto.svg')]"
|
||||||
onClick={() => handleViewDetail(end_user.id)}
|
onClick={() => handleViewDetail(end_user.id)}
|
||||||
|
></div>}
|
||||||
>
|
>
|
||||||
<div className="rb:flex rb:items-center">
|
<div className="rb:flex rb:justify-between rb:items-center">
|
||||||
<div className="rb:w-12 rb:h-12 rb:text-center rb:font-semibold rb:text-[28px] rb:leading-12 rb:rounded-lg rb:text-[#FBFDFF] rb:bg-[#155EEF]">{name[0]}</div>
|
<div>{t('userMemory.capacity')}</div>
|
||||||
<div className="rb:max-w-[calc(100%-60px)] rb:text-base rb:font-medium rb:leading-6 rb:ml-3 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">
|
<div>{memory_num?.total || 0} {t('userMemory.memoryNum')}</div>
|
||||||
{name || '-'}<br/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="rb:flex rb:justify-between rb:items-center rb:mt-2.5">
|
||||||
|
<div>{t('userMemory.type')}</div>
|
||||||
|
<div>{t(`userMemory.${item.type || 'person'}`)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rb:grid rb:grid-cols-1 rb:gap-3 rb:mt-7 rb:mb-7">
|
|
||||||
<div className="rb:text-center">
|
<div className="rb:mt-3 rb:bg-[#F6F8FC] rb:rounded-lg rb:border rb:border-[#DFE4ED] rb:py-2 rb:px-3" onClick={handleViewMemoryConfig}>
|
||||||
<div className="rb:text-[24px] rb:leading-7.5 rb:font-extrabold">{memory_num.total || 0}</div>
|
<div className="rb:text-[#5B6167] rb:leading-5 rb:flex rb:justify-between rb:items-center">
|
||||||
<div className="rb:wrap-break-word">{t(`userMemory.knowledgeEntryCount`)}</div>
|
{t('userMemory.memory_config_name')}
|
||||||
</div>
|
<div
|
||||||
|
className="rb:w-7 rb:h-7 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/arrow_right.svg')]"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="rb:font-medium rb:leading-5 rb:mt-1">{memory_config?.memory_config_name || '-'}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</RbCard>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : <Empty />}
|
) : <Empty />
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{layout === 'list' &&
|
|
||||||
<Table
|
|
||||||
apiUrl={userMemoryListUrl}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="end_user.id"
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
<ConfigModal ref={configModalRef} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -17,13 +17,10 @@ export interface Data {
|
|||||||
entity: number;
|
entity: number;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
memory_config: {
|
||||||
|
memory_config_id: string;
|
||||||
|
memory_config_name: string;
|
||||||
|
},
|
||||||
|
type: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
export interface ConfigModalData {
|
|
||||||
llm: string;
|
|
||||||
embedding: string;
|
|
||||||
rerank: string;
|
|
||||||
}
|
|
||||||
export interface ConfigModalRef {
|
|
||||||
handleOpen: () => void;
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import ReactEcharts from 'echarts-for-react';
|
import ReactEcharts from 'echarts-for-react';
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
import Loading from '@/components/Empty/Loading'
|
import Loading from '@/components/Empty/Loading'
|
||||||
import type { Emotion } from './GraphDetail'
|
import type { Emotion } from '../pages/GraphDetail'
|
||||||
import { format } from 'echarts';
|
|
||||||
|
|
||||||
interface EmotionLineProps {
|
interface EmotionLineProps {
|
||||||
chartData: Emotion[];
|
chartData: Emotion[];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import ReactEcharts from 'echarts-for-react'
|
import ReactEcharts from 'echarts-for-react'
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
import Loading from '@/components/Empty/Loading'
|
import Loading from '@/components/Empty/Loading'
|
||||||
import type { Interaction } from './GraphDetail'
|
import type { Interaction } from '../pages/GraphDetail'
|
||||||
|
|
||||||
interface InteractionBarProps {
|
interface InteractionBarProps {
|
||||||
chartData: Interaction[];
|
chartData: Interaction[];
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import React, { type FC, useEffect, useState, useRef, useCallback } from 'react'
|
import React, { type FC, useEffect, useState, useRef, useCallback } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { Col, Row, Space, Button } from 'antd'
|
import { Col, Row, Space, Button } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
import RbCard from '@/components/RbCard/Card'
|
import RbCard from '@/components/RbCard/Card'
|
||||||
import ReactEcharts from 'echarts-for-react'
|
import ReactEcharts from 'echarts-for-react'
|
||||||
import detailEmpty from '@/assets/images/userMemory/detail_empty.png'
|
import detailEmpty from '@/assets/images/userMemory/detail_empty.png'
|
||||||
import type { Node, Edge, GraphData, StatementNodeProperties, ExtractedEntityNodeProperties, GraphDetailRef } from '../types'
|
import type { Node, Edge, GraphData, StatementNodeProperties, ExtractedEntityNodeProperties } from '../types'
|
||||||
import {
|
import {
|
||||||
getMemorySearchEdges,
|
getMemorySearchEdges,
|
||||||
} from '@/api/memory'
|
} from '@/api/memory'
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
import Tag from '@/components/Tag'
|
import Tag from '@/components/Tag'
|
||||||
import GraphDetail from '../components/GraphDetail'
|
|
||||||
|
|
||||||
const colors = ['#155EEF', '#369F21', '#4DA8FF', '#FF5D34', '#9C6FFF', '#FF8A4C', '#8BAEF7', '#FFB048']
|
const colors = ['#155EEF', '#369F21', '#4DA8FF', '#FF5D34', '#9C6FFF', '#FF8A4C', '#8BAEF7', '#FFB048']
|
||||||
const RelationshipNetwork:FC = () => {
|
const RelationshipNetwork:FC = () => {
|
||||||
@@ -26,7 +25,7 @@ const RelationshipNetwork:FC = () => {
|
|||||||
const [categories, setCategories] = useState<{ name: string }[]>([])
|
const [categories, setCategories] = useState<{ name: string }[]>([])
|
||||||
const [selectedNode, setSelectedNode] = useState<Node | null>(null)
|
const [selectedNode, setSelectedNode] = useState<Node | null>(null)
|
||||||
// const [fullScreen, setFullScreen] = useState<boolean>(false)
|
// const [fullScreen, setFullScreen] = useState<boolean>(false)
|
||||||
const graphDetailRef = useRef<GraphDetailRef>(null)
|
const navigate = useNavigate()
|
||||||
|
|
||||||
console.log('categories', categories)
|
console.log('categories', categories)
|
||||||
// 关系网络
|
// 关系网络
|
||||||
@@ -133,15 +132,14 @@ const RelationshipNetwork:FC = () => {
|
|||||||
}
|
}
|
||||||
}, [nodes])
|
}, [nodes])
|
||||||
|
|
||||||
// const handleFullScreen = () => {
|
|
||||||
// setFullScreen(prev => !prev)
|
|
||||||
// }
|
|
||||||
|
|
||||||
console.log('selectedNode', selectedNode)
|
|
||||||
|
|
||||||
const handleViewAll = () => {
|
const handleViewAll = () => {
|
||||||
if (!selectedNode) return
|
if (!selectedNode) return
|
||||||
graphDetailRef.current?.handleOpen(selectedNode)
|
const params = new URLSearchParams({
|
||||||
|
nodeId: selectedNode.id,
|
||||||
|
nodeLabel: selectedNode.label,
|
||||||
|
nodeName: selectedNode.name || ''
|
||||||
|
})
|
||||||
|
navigate(`/user-memory/detail/${id}/GRAPH?${params.toString()}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -336,8 +334,6 @@ const RelationshipNetwork:FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</RbCard>
|
</RbCard>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<GraphDetail ref={graphDetailRef} />
|
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import { useState, forwardRef, useImperativeHandle, useMemo } from 'react'
|
import { useState, forwardRef, useImperativeHandle, useMemo, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import { Row, Col, Tabs, Space, Skeleton } from 'antd'
|
import { Row, Col, Tabs, Space, Skeleton } from 'antd'
|
||||||
|
|
||||||
import { getRelationshipEvolution, getTimelineMemories } from '@/api/memory'
|
import { getRelationshipEvolution, getTimelineMemories } from '@/api/memory'
|
||||||
import type { Node, GraphDetailRef } from '../types'
|
import type { Node, GraphDetailRef } from '../types'
|
||||||
import RbDrawer from '@/components/RbDrawer'
|
|
||||||
import RbCard from '@/components/RbCard/Card'
|
import RbCard from '@/components/RbCard/Card'
|
||||||
import EmotionLine from './EmotionLine'
|
import EmotionLine from '../components/EmotionLine'
|
||||||
import { formatDateTime } from '@/utils/format'
|
import { formatDateTime } from '@/utils/format'
|
||||||
import Tag from '@/components/Tag'
|
import Tag from '@/components/Tag'
|
||||||
import InteractionBar from './InteractionBar'
|
import InteractionBar from '../components/InteractionBar'
|
||||||
import Empty from '@/components/Empty'
|
import Empty from '@/components/Empty'
|
||||||
|
import PageHeader from '../components/PageHeader'
|
||||||
|
|
||||||
export interface Emotion {
|
export interface Emotion {
|
||||||
emotion_intensity: number;
|
emotion_intensity: number;
|
||||||
@@ -35,7 +36,7 @@ interface Timeline {
|
|||||||
|
|
||||||
const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [open, setOpen] = useState(false);
|
const [searchParams] = useSearchParams()
|
||||||
const [vo, setVo] = useState<Node | null>(null)
|
const [vo, setVo] = useState<Node | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [emotionData, setEmotionData] = useState<Emotion[]>([])
|
const [emotionData, setEmotionData] = useState<Emotion[]>([])
|
||||||
@@ -43,14 +44,23 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
|||||||
const [activeTab, setActiveTab] = useState('timelines_memory')
|
const [activeTab, setActiveTab] = useState('timelines_memory')
|
||||||
const [timelineLoading, setTimelineLoading] = useState(false)
|
const [timelineLoading, setTimelineLoading] = useState(false)
|
||||||
const [timelineMemories, setTimelineMemories] = useState<Timeline>({ timelines_memory: [], MemorySummary: [], Statement: [], ExtractedEntity: []})
|
const [timelineMemories, setTimelineMemories] = useState<Timeline>({ timelines_memory: [], MemorySummary: [], Statement: [], ExtractedEntity: []})
|
||||||
|
useEffect(() => {
|
||||||
|
const nodeId = searchParams.get('nodeId')
|
||||||
|
const nodeLabel = searchParams.get('nodeLabel')
|
||||||
|
const nodeName = searchParams.get('nodeName')
|
||||||
|
|
||||||
const handleCancel = () => {
|
if (nodeId && nodeLabel) {
|
||||||
setVo(null)
|
const nodeFromUrl = {
|
||||||
setOpen(false)
|
id: nodeId,
|
||||||
|
label: nodeLabel,
|
||||||
|
name: nodeName || nodeLabel
|
||||||
}
|
}
|
||||||
|
handleOpen(nodeFromUrl as Node)
|
||||||
|
}
|
||||||
|
}, [searchParams])
|
||||||
|
|
||||||
const handleOpen = (vo: Node) => {
|
const handleOpen = (vo: Node) => {
|
||||||
setActiveTab('timelines_memory')
|
setActiveTab('timelines_memory')
|
||||||
setOpen(true)
|
|
||||||
setVo(vo)
|
setVo(vo)
|
||||||
getRelationshipEvolutionData(vo)
|
getRelationshipEvolutionData(vo)
|
||||||
getTimelineMemoriesData(vo)
|
getTimelineMemoriesData(vo)
|
||||||
@@ -85,12 +95,12 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
|||||||
}, [activeTab, timelineMemories])
|
}, [activeTab, timelineMemories])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RbDrawer
|
<>
|
||||||
title={vo?.name}
|
<PageHeader
|
||||||
open={open}
|
name={vo?.name}
|
||||||
onClose={handleCancel}
|
source="node"
|
||||||
width={1000}
|
/>
|
||||||
>
|
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||||
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3">{t('userMemory.relationshipEvolution')}</div>
|
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3">{t('userMemory.relationshipEvolution')}</div>
|
||||||
<RbCard>
|
<RbCard>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
@@ -107,7 +117,7 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
|||||||
<RbCard>
|
<RbCard>
|
||||||
<Tabs
|
<Tabs
|
||||||
activeKey={activeTab}
|
activeKey={activeTab}
|
||||||
items={['timelines_memory', 'ExtractedEntity', 'Statement', 'MemorySummary'].map(key => ({
|
items={['timelines_memory', 'Statement', 'MemorySummary'].map(key => ({
|
||||||
label: t(`userMemory.${key}`),
|
label: t(`userMemory.${key}`),
|
||||||
key
|
key
|
||||||
}))}
|
}))}
|
||||||
@@ -134,7 +144,8 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
|||||||
|
|
||||||
|
|
||||||
</RbCard>
|
</RbCard>
|
||||||
</RbDrawer>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
export default GraphDetail
|
export default GraphDetail
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { type FC, useEffect, useState, useMemo, useRef } from 'react'
|
import { type FC, useEffect, useState, useMemo, useRef } from 'react'
|
||||||
import { useParams, useNavigate } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dropdown, Space, Button } from 'antd'
|
import { Dropdown, Button } from 'antd'
|
||||||
|
|
||||||
import PageHeader from '../components/PageHeader'
|
import PageHeader from '../components/PageHeader'
|
||||||
import StatementDetail from './StatementDetail'
|
import StatementDetail from './StatementDetail'
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
getEndUserProfile,
|
getEndUserProfile,
|
||||||
} from '@/api/memory'
|
} from '@/api/memory'
|
||||||
import refreshIcon from '@/assets/images/refresh_hover.svg'
|
import refreshIcon from '@/assets/images/refresh_hover.svg'
|
||||||
|
import GraphDetail from './GraphDetail'
|
||||||
|
|
||||||
const Detail: FC = () => {
|
const Detail: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -47,6 +48,10 @@ const Detail: FC = () => {
|
|||||||
forgetDetailRef.current?.handleRefresh()
|
forgetDetailRef.current?.handleRefresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'GRAPH') {
|
||||||
|
return <GraphDetail />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rb:h-full rb:w-full">
|
<div className="rb:h-full rb:w-full">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
<div style={{ maxHeight: '300px', overflowY: 'auto', minWidth: '240px' }}>
|
<div style={{ maxHeight: '300px', overflowY: 'auto', minWidth: '240px' }}>
|
||||||
{nodeLibrary.map((category, categoryIndex) => {
|
{nodeLibrary.map((category, categoryIndex) => {
|
||||||
const filteredNodes = category.nodes.filter(nodeType =>
|
const filteredNodes = category.nodes.filter(nodeType =>
|
||||||
nodeType.type !== 'start' && nodeType.type !== 'end' && nodeType.type !== 'loop' && nodeType.type !== 'cycle-start'
|
nodeType.type !== 'start' && nodeType.type !== 'end' && nodeType.type !== 'iteration' && nodeType.type !== 'loop' && nodeType.type !== 'cycle-start'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (filteredNodes.length === 0) return null;
|
if (filteredNodes.length === 0) return null;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
y: cycleStartBBox.y,
|
y: cycleStartBBox.y,
|
||||||
data: {
|
data: {
|
||||||
type: 'add-node',
|
type: 'add-node',
|
||||||
label: '添加节点',
|
label: t('workflow.addNode'),
|
||||||
icon: '+',
|
icon: '+',
|
||||||
parentId: node.id,
|
parentId: node.id,
|
||||||
cycle: data.id,
|
cycle: data.id,
|
||||||
@@ -61,7 +61,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zIndex: 3
|
zIndex: 10
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
y: centerY,
|
y: centerY,
|
||||||
data: {
|
data: {
|
||||||
type: 'add-node',
|
type: 'add-node',
|
||||||
label: '添加节点',
|
label: t('workflow.addNode'),
|
||||||
icon: '+',
|
icon: '+',
|
||||||
parentId: node.id,
|
parentId: node.id,
|
||||||
cycle: data.id,
|
cycle: data.id,
|
||||||
@@ -128,7 +128,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zIndex: 3
|
zIndex: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
graph.addEdge(edgeConfig)
|
graph.addEdge(edgeConfig)
|
||||||
|
|||||||
@@ -151,11 +151,11 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
|
|||||||
|
|
||||||
let filteredNodes;
|
let filteredNodes;
|
||||||
if (isChildOfLoop) {
|
if (isChildOfLoop) {
|
||||||
// Use same filtering as AddNode for child nodes of loop
|
// Use same filtering as AddNode for child nodes of loop, but allow break
|
||||||
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'cycle-start', 'iteration'].includes(nodeType.type));
|
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'cycle-start', 'iteration'].includes(nodeType.type));
|
||||||
} else if (isChildOfIteration) {
|
} else if (isChildOfIteration) {
|
||||||
// Filter out loop and iteration nodes for children of iteration nodes
|
// Filter out loop and iteration nodes for children of iteration nodes, but allow break
|
||||||
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'break', 'cycle-start', 'iteration'].includes(nodeType.type));
|
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'loop', 'cycle-start', 'iteration'].includes(nodeType.type));
|
||||||
} else {
|
} else {
|
||||||
// Original filtering for non-loop child nodes
|
// Original filtering for non-loop child nodes
|
||||||
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'break', 'cycle-start'].includes(nodeType.type));
|
filteredNodes = category.nodes.filter(nodeType => !['start', 'end', 'break', 'cycle-start'].includes(nodeType.type));
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const AssignmentList: FC<AssignmentListProps> = ({
|
|||||||
>
|
>
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={options}
|
options={options.filter(vo => vo.nodeData.type === 'loop' || vo.value.includes('conv.'))}
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
form.setFieldValue([parentName, name, 'operation'], undefined);
|
form.setFieldValue([parentName, name, 'operation'], undefined);
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { type FC } from 'react';
|
import { type FC } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Input, Button, Form, Space } from 'antd';
|
import { Button, Form, Space } from 'antd';
|
||||||
import { PlusOutlined, CopyOutlined, DeleteOutlined, ExpandOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
import { Graph, Node } from '@antv/x6';
|
import { Graph, Node } from '@antv/x6';
|
||||||
import type { PortMetadata } from '@antv/x6/lib/model/port';
|
import Editor from '../../Editor';
|
||||||
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
|
|
||||||
interface CategoryListProps {
|
interface CategoryListProps {
|
||||||
parentName: string;
|
parentName: string;
|
||||||
|
options: Suggestion[];
|
||||||
selectedNode?: Node | null;
|
selectedNode?: Node | null;
|
||||||
graphRef?: React.MutableRefObject<Graph | undefined>;
|
graphRef?: React.MutableRefObject<Graph | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRef }) => {
|
const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRef, options }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const form = Form.useFormInstance();
|
const form = Form.useFormInstance();
|
||||||
const formValues = Form.useWatch([parentName], form);
|
const formValues = Form.useWatch([parentName], form);
|
||||||
@@ -167,9 +169,9 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
|
|||||||
name={[name, 'class_name']}
|
name={[name, 'class_name']}
|
||||||
noStyle
|
noStyle
|
||||||
>
|
>
|
||||||
<Input.TextArea
|
<Editor
|
||||||
placeholder={t('common.pleaseEnter')}
|
placeholder={t('common.pleaseEnter')}
|
||||||
rows={2}
|
options={options}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Form, Button, Select, Row, Col, InputNumber, Radio, type SelectProps } from 'antd'
|
import { Form, Button, Select, Row, Col, InputNumber, Radio, Input, type SelectProps } from 'antd'
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||||
@@ -114,7 +114,7 @@ const ConditionList: FC<CaseListProps> = ({
|
|||||||
<Col span={14}>
|
<Col span={14}>
|
||||||
<Form.Item name={[field.name, 'left']} noStyle>
|
<Form.Item name={[field.name, 'left']} noStyle>
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
options={options}
|
options={options.filter(vo => vo.value.includes('sys.') || vo.value.includes('conv.') || vo.nodeData.type === 'loop')}
|
||||||
size="small"
|
size="small"
|
||||||
allowClear={false}
|
allowClear={false}
|
||||||
popupMatchSelectWidth={false}
|
popupMatchSelectWidth={false}
|
||||||
@@ -186,7 +186,7 @@ const ConditionList: FC<CaseListProps> = ({
|
|||||||
<Radio.Button value={true}>True</Radio.Button>
|
<Radio.Button value={true}>True</Radio.Button>
|
||||||
<Radio.Button value={false}>False</Radio.Button>
|
<Radio.Button value={false}>False</Radio.Button>
|
||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
: <Editor options={options} />
|
: <Input placeholder={t('common.pleaseEnter')} />
|
||||||
}
|
}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Form, Button, Select, Row, Col, Input } from 'antd'
|
import { Form, Select, Row, Col, Input } from 'antd'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
import VariableSelect from '../VariableSelect'
|
import VariableSelect from '../VariableSelect'
|
||||||
|
|
||||||
@@ -36,7 +36,6 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
|||||||
value = [],
|
value = [],
|
||||||
options,
|
options,
|
||||||
parentName,
|
parentName,
|
||||||
onChange,
|
|
||||||
selectedNode,
|
selectedNode,
|
||||||
graphRef
|
graphRef
|
||||||
}) => {
|
}) => {
|
||||||
@@ -139,12 +138,17 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
|||||||
<Form.Item name={[name, 'value']} noStyle>
|
<Form.Item name={[name, 'value']} noStyle>
|
||||||
{currentInputType === 'variable' ? (
|
{currentInputType === 'variable' ? (
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder="选择变量"
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={availableOptions}
|
options={availableOptions.filter(option => {
|
||||||
|
const currentType = value?.[index]?.type;
|
||||||
|
if (!currentType) return true;
|
||||||
|
|
||||||
|
return option.dataType === currentType
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
placeholder="输入值"
|
placeholder={t('common.pleaseEnter')}
|
||||||
rows={3}
|
rows={3}
|
||||||
className="rb:w-full"
|
className="rb:w-full"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,8 +18,22 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
|||||||
isCanAdd = false
|
isCanAdd = false
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const value = form.getFieldValue(name) || [];
|
||||||
|
|
||||||
|
console.log('GroupVariableList', value)
|
||||||
|
|
||||||
if (!isCanAdd) {
|
if (!isCanAdd) {
|
||||||
|
// Filter options based on first variable's dataType if value exists
|
||||||
|
let filteredOptions = options;
|
||||||
|
if (value.length > 0) {
|
||||||
|
const firstVariableValue = value[0];
|
||||||
|
const firstVariable = options.find(opt => `{{${opt.value}}}` === firstVariableValue);
|
||||||
|
if (firstVariable) {
|
||||||
|
filteredOptions = options.filter(opt => opt.dataType === firstVariable.dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rb:mb-4">
|
<div className="rb:mb-4">
|
||||||
<Row gutter={12} className="rb:mb-2!">
|
<Row gutter={12} className="rb:mb-2!">
|
||||||
@@ -38,7 +52,7 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
|||||||
>
|
>
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={options}
|
options={filteredOptions}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -77,7 +91,18 @@ const GroupVariableList: FC<GroupVariableListProps> = ({
|
|||||||
>
|
>
|
||||||
<VariableSelect
|
<VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={options}
|
options={(() => {
|
||||||
|
const currentGroupValue = value[name]?.value || [];
|
||||||
|
if (currentGroupValue.length > 0) {
|
||||||
|
const firstVariableValue = currentGroupValue[0];
|
||||||
|
const firstVariable = options.find(opt => `{{${opt.value}}}` === firstVariableValue);
|
||||||
|
if (firstVariable) {
|
||||||
|
return options.filter(opt => opt.dataType === firstVariable.dataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
})()
|
||||||
|
}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={16}>
|
<Col span={16}>
|
||||||
<Form.Item name="url">
|
<Form.Item name="url">
|
||||||
<Editor options={options} variant="outlined" />
|
<Editor options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')} variant="outlined" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -144,7 +144,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
|||||||
<Form.Item name={['body', 'data']} noStyle>
|
<Form.Item name={['body', 'data']} noStyle>
|
||||||
<EditableTable
|
<EditableTable
|
||||||
parentName={['body', 'data']}
|
parentName={['body', 'data']}
|
||||||
options={options}
|
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||||
filterBooleanType={true}
|
filterBooleanType={true}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -154,7 +154,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
|||||||
<MessageEditor
|
<MessageEditor
|
||||||
key="json"
|
key="json"
|
||||||
parentName={['body', 'data']}
|
parentName={['body', 'data']}
|
||||||
options={options}
|
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||||
isArray={false}
|
isArray={false}
|
||||||
title="JSON"
|
title="JSON"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
|||||||
showSearch
|
showSearch
|
||||||
allowClear={allowClear}
|
allowClear={allowClear}
|
||||||
filterOption={(input, option) => {
|
filterOption={(input, option) => {
|
||||||
|
if (input === '/') return true;
|
||||||
if (option?.options) {
|
if (option?.options) {
|
||||||
return option.label?.toLowerCase().includes(input.toLowerCase()) ||
|
return option.label?.toLowerCase().includes(input.toLowerCase()) ||
|
||||||
option.options.some((opt: any) =>
|
option.options.some((opt: any) =>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import ConditionList from './ConditionList'
|
|||||||
import CycleVarsList from './CycleVarsList'
|
import CycleVarsList from './CycleVarsList'
|
||||||
import AssignmentList from './AssignmentList'
|
import AssignmentList from './AssignmentList'
|
||||||
import ToolConfig from './ToolConfig'
|
import ToolConfig from './ToolConfig'
|
||||||
|
import { calculateVariableList } from './utils/variableListCalculator'
|
||||||
|
|
||||||
interface PropertiesProps {
|
interface PropertiesProps {
|
||||||
selectedNode?: Node | null;
|
selectedNode?: Node | null;
|
||||||
@@ -338,111 +339,34 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
const parentLoopNode = getParentLoopNode(selectedNode.id);
|
const parentLoopNode = getParentLoopNode(selectedNode.id);
|
||||||
|
|
||||||
console.log('childNodeIds', selectedNode, childNodeIds)
|
console.log('childNodeIds', selectedNode, childNodeIds)
|
||||||
const allRelevantNodeIds = [...allPreviousNodeIds, ...childNodeIds];
|
let allRelevantNodeIds = [...allPreviousNodeIds, ...childNodeIds];
|
||||||
|
|
||||||
// Add parent loop/iteration node variables if current node is a child
|
// Add variables from nodes preceding the parent loop/iteration node if current node is a child
|
||||||
if (parentLoopNode) {
|
if (parentLoopNode) {
|
||||||
const parentData = parentLoopNode.getData();
|
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
|
||||||
const parentNodeId = parentLoopNode.getData().id;
|
allRelevantNodeIds.push(...parentPreviousNodeIds);
|
||||||
|
}
|
||||||
|
|
||||||
if (parentData.type === 'loop') {
|
|
||||||
const cycleVars = parentData.cycle_vars || [];
|
|
||||||
cycleVars.forEach((cycleVar: any) => {
|
// Add conversation variables from global config
|
||||||
const key = `${parentNodeId}_cycle_${cycleVar.name}`;
|
const conversationVariables = workflowConfig?.variables || [];
|
||||||
|
|
||||||
|
conversationVariables.forEach((variable: any) => {
|
||||||
|
const key = `CONVERSATION_${variable.name}`;
|
||||||
if (!addedKeys.has(key)) {
|
if (!addedKeys.has(key)) {
|
||||||
addedKeys.add(key);
|
addedKeys.add(key);
|
||||||
variableList.push({
|
variableList.push({
|
||||||
key,
|
key,
|
||||||
label: cycleVar.name,
|
label: variable.name,
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: cycleVar.type || 'String',
|
dataType: variable.type,
|
||||||
value: `${parentNodeId}.${cycleVar.name}`,
|
value: `conv.${variable.name}`,
|
||||||
nodeData: parentData,
|
nodeData: { type: 'CONVERSATION', name: 'CONVERSATION', icon: '' },
|
||||||
|
group: 'CONVERSATION'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (parentData.type === 'iteration') {
|
|
||||||
// Add item and index variables for iteration parent
|
|
||||||
const itemKey = `${parentNodeId}_item`;
|
|
||||||
const indexKey = `${parentNodeId}_index`;
|
|
||||||
|
|
||||||
if (!addedKeys.has(itemKey)) {
|
|
||||||
addedKeys.add(itemKey);
|
|
||||||
variableList.push({
|
|
||||||
key: itemKey,
|
|
||||||
label: 'item',
|
|
||||||
type: 'variable',
|
|
||||||
dataType: 'Object',
|
|
||||||
value: `${parentNodeId}.item`,
|
|
||||||
nodeData: parentData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addedKeys.has(indexKey)) {
|
|
||||||
addedKeys.add(indexKey);
|
|
||||||
variableList.push({
|
|
||||||
key: indexKey,
|
|
||||||
label: 'index',
|
|
||||||
type: 'variable',
|
|
||||||
dataType: 'Number',
|
|
||||||
value: `${parentNodeId}.index`,
|
|
||||||
nodeData: parentData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if parent loop/iteration is connected to http-request via ERROR connection
|
|
||||||
if (parentData.type === 'loop' || parentData.type === 'iteration') {
|
|
||||||
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
|
|
||||||
parentPreviousNodeIds.forEach(prevNodeId => {
|
|
||||||
const prevNode = nodes.find(n => n.id === prevNodeId);
|
|
||||||
if (!prevNode) return;
|
|
||||||
|
|
||||||
const prevNodeData = prevNode.getData();
|
|
||||||
if (prevNodeData.type === 'http-request') {
|
|
||||||
// Check if connected via ERROR connection point
|
|
||||||
const errorEdges = edges.filter(edge => {
|
|
||||||
return edge.getTargetCellId() === parentLoopNode.id &&
|
|
||||||
edge.getSourceCellId() === prevNodeId &&
|
|
||||||
edge.getSourcePortId() === 'ERROR'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errorEdges.length > 0) {
|
|
||||||
const errorMessageKey = `${prevNodeData.id}_error_message`;
|
|
||||||
const errorTypeKey = `${prevNodeData.id}_error_type`;
|
|
||||||
|
|
||||||
if (!addedKeys.has(errorMessageKey)) {
|
|
||||||
addedKeys.add(errorMessageKey);
|
|
||||||
variableList.push({
|
|
||||||
key: errorMessageKey,
|
|
||||||
label: 'error_message',
|
|
||||||
type: 'variable',
|
|
||||||
dataType: 'string',
|
|
||||||
value: `${prevNodeData.id}.error_message`,
|
|
||||||
nodeData: prevNodeData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addedKeys.has(errorTypeKey)) {
|
|
||||||
addedKeys.add(errorTypeKey);
|
|
||||||
variableList.push({
|
|
||||||
key: errorTypeKey,
|
|
||||||
label: 'error_type',
|
|
||||||
type: 'variable',
|
|
||||||
dataType: 'string',
|
|
||||||
value: `${prevNodeData.id}.error_type`,
|
|
||||||
nodeData: prevNodeData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add variables from nodes preceding the parent loop/iteration node
|
|
||||||
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
|
|
||||||
allRelevantNodeIds.push(...parentPreviousNodeIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
allRelevantNodeIds.forEach(nodeId => {
|
allRelevantNodeIds.forEach(nodeId => {
|
||||||
const node = nodes.find(n => n.id === nodeId);
|
const node = nodes.find(n => n.id === nodeId);
|
||||||
@@ -496,7 +420,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
key: llmKey,
|
key: llmKey,
|
||||||
label: 'output',
|
label: 'output',
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: 'String',
|
dataType: 'string',
|
||||||
value: `${dataNodeId}.output`,
|
value: `${dataNodeId}.output`,
|
||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
@@ -565,6 +489,17 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
const groupVariables = nodeData.config.group_variables.defaultValue || [];
|
const groupVariables = nodeData.config.group_variables.defaultValue || [];
|
||||||
groupVariables?.forEach((groupVar: any) => {
|
groupVariables?.forEach((groupVar: any) => {
|
||||||
if (!groupVar || !groupVar.key) return;
|
if (!groupVar || !groupVar.key) return;
|
||||||
|
|
||||||
|
// Determine dataType from first variable in the group
|
||||||
|
let groupDataType = 'string';
|
||||||
|
if (groupVar.value && Array.isArray(groupVar.value) && groupVar.value.length > 0) {
|
||||||
|
const firstVariableValue = groupVar.value[0];
|
||||||
|
const firstVariable = variableList.find(v => `{{${v.value}}}` === firstVariableValue);
|
||||||
|
if (firstVariable) {
|
||||||
|
groupDataType = firstVariable.dataType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const groupVarKey = `${dataNodeId}_${groupVar.key}`;
|
const groupVarKey = `${dataNodeId}_${groupVar.key}`;
|
||||||
if (!addedKeys.has(groupVarKey)) {
|
if (!addedKeys.has(groupVarKey)) {
|
||||||
addedKeys.add(groupVarKey);
|
addedKeys.add(groupVarKey);
|
||||||
@@ -572,14 +507,26 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
key: groupVarKey,
|
key: groupVarKey,
|
||||||
label: groupVar.key,
|
label: groupVar.key,
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: 'string',
|
dataType: groupDataType,
|
||||||
value: `${dataNodeId}.${groupVar.key}`,
|
value: `${dataNodeId}.${groupVar.key}`,
|
||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// If group=false, add output variable
|
// If group=false, add output variable with type from first group_variable
|
||||||
|
const groupVariables = nodeData.config.group_variables.defaultValue || [];
|
||||||
|
const firstVariable = groupVariables[0];
|
||||||
|
let outputDataType: string = 'any';
|
||||||
|
if (firstVariable) {
|
||||||
|
const filterVo = [...variableList].find(v => {
|
||||||
|
return `{{${v.value}}}` === firstVariable
|
||||||
|
})
|
||||||
|
if (filterVo) {
|
||||||
|
outputDataType = filterVo?.dataType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const varAggregatorKey = `${dataNodeId}_output`;
|
const varAggregatorKey = `${dataNodeId}_output`;
|
||||||
if (!addedKeys.has(varAggregatorKey)) {
|
if (!addedKeys.has(varAggregatorKey)) {
|
||||||
addedKeys.add(varAggregatorKey);
|
addedKeys.add(varAggregatorKey);
|
||||||
@@ -587,7 +534,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
key: varAggregatorKey,
|
key: varAggregatorKey,
|
||||||
label: 'output',
|
label: 'output',
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: 'string',
|
dataType: outputDataType,
|
||||||
value: `${dataNodeId}.output`,
|
value: `${dataNodeId}.output`,
|
||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
@@ -684,21 +631,20 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!addedKeys.has(outputKey)) {
|
// if (!addedKeys.has(outputKey)) {
|
||||||
addedKeys.add(outputKey);
|
// addedKeys.add(outputKey);
|
||||||
variableList.push({
|
// variableList.push({
|
||||||
key: outputKey,
|
// key: outputKey,
|
||||||
label: 'output',
|
// label: 'output',
|
||||||
type: 'variable',
|
// type: 'variable',
|
||||||
dataType: 'string',
|
// dataType: 'string',
|
||||||
value: `${dataNodeId}.output`,
|
// value: `${dataNodeId}.output`,
|
||||||
nodeData: nodeData,
|
// nodeData: nodeData,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
break
|
break
|
||||||
case 'iteration':
|
case 'iteration':
|
||||||
const iterationOutputKey = `${dataNodeId}_output`;
|
const iterationOutputKey = `${dataNodeId}_output`;
|
||||||
const iterationItemKey = `${dataNodeId}_item`;
|
|
||||||
if (!addedKeys.has(iterationOutputKey)) {
|
if (!addedKeys.has(iterationOutputKey)) {
|
||||||
addedKeys.add(iterationOutputKey);
|
addedKeys.add(iterationOutputKey);
|
||||||
// Get the data type from the output configuration, default to string
|
// Get the data type from the output configuration, default to string
|
||||||
@@ -715,22 +661,11 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
key: iterationOutputKey,
|
key: iterationOutputKey,
|
||||||
label: 'output',
|
label: 'output',
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: outputDataType,
|
dataType: `array[${outputDataType}]`,
|
||||||
value: `${dataNodeId}.output`,
|
value: `${dataNodeId}.output`,
|
||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!addedKeys.has(iterationItemKey)) {
|
|
||||||
addedKeys.add(iterationItemKey);
|
|
||||||
variableList.push({
|
|
||||||
key: iterationItemKey,
|
|
||||||
label: 'item',
|
|
||||||
type: 'variable',
|
|
||||||
dataType: 'string',
|
|
||||||
value: `${dataNodeId}.item`,
|
|
||||||
nodeData: nodeData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
case 'loop':
|
case 'loop':
|
||||||
const cycleVars = nodeData.config.cycle_vars.defaultValue || [];
|
const cycleVars = nodeData.config.cycle_vars.defaultValue || [];
|
||||||
@@ -760,47 +695,337 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
key: toolDataKey,
|
key: toolDataKey,
|
||||||
label: 'data',
|
label: 'data',
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: 'object',
|
dataType: 'string',
|
||||||
value: `${dataNodeId}.data`,
|
value: `${dataNodeId}.data`,
|
||||||
nodeData: nodeData,
|
nodeData: nodeData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case 'memory-read':
|
||||||
|
const memoryReadAnswerKey = `${dataNodeId}_answer`;
|
||||||
|
const memoryReadIntermediateOutputs = `${dataNodeId}_intermediate_outputs`;
|
||||||
|
if (!addedKeys.has(memoryReadAnswerKey)) {
|
||||||
|
addedKeys.add(memoryReadAnswerKey);
|
||||||
|
variableList.push({
|
||||||
|
key: memoryReadAnswerKey,
|
||||||
|
label: 'answer',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'string',
|
||||||
|
value: `${dataNodeId}.answer`,
|
||||||
|
nodeData: nodeData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!addedKeys.has(memoryReadIntermediateOutputs)) {
|
||||||
|
addedKeys.add(memoryReadIntermediateOutputs);
|
||||||
|
variableList.push({
|
||||||
|
key: memoryReadIntermediateOutputs,
|
||||||
|
label: 'intermediate_outputs',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'array[object]',
|
||||||
|
value: `${dataNodeId}.intermediate_outputs`,
|
||||||
|
nodeData: nodeData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add conversation variables from global config
|
|
||||||
const conversationVariables = workflowConfig?.variables || [];
|
|
||||||
|
|
||||||
conversationVariables.forEach((variable: any) => {
|
// Add parent loop/iteration node variables if current node is a child
|
||||||
const key = `CONVERSATION_${variable.name}`;
|
if (parentLoopNode) {
|
||||||
|
const parentData = parentLoopNode.getData();
|
||||||
|
const parentNodeId = parentLoopNode.getData().id;
|
||||||
|
|
||||||
|
if (parentData.type === 'loop') {
|
||||||
|
const cycleVars = parentData.cycle_vars || [];
|
||||||
|
cycleVars.forEach((cycleVar: any) => {
|
||||||
|
const key = `${parentNodeId}_cycle_${cycleVar.name}`;
|
||||||
if (!addedKeys.has(key)) {
|
if (!addedKeys.has(key)) {
|
||||||
addedKeys.add(key);
|
addedKeys.add(key);
|
||||||
variableList.push({
|
variableList.push({
|
||||||
key,
|
key,
|
||||||
label: variable.name,
|
label: cycleVar.name,
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
dataType: variable.type,
|
dataType: cycleVar.type || 'String',
|
||||||
value: `conv.${variable.name}`,
|
value: `${parentNodeId}.${cycleVar.name}`,
|
||||||
nodeData: { type: 'CONVERSATION', name: 'CONVERSATION', icon: '' },
|
nodeData: parentData,
|
||||||
group: 'CONVERSATION'
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (parentData.type === 'iteration') {
|
||||||
|
// Add item and index variables for iteration parent only if input has value
|
||||||
|
if (parentData.config.input.defaultValue) {
|
||||||
|
const itemKey = `${parentNodeId}_item`;
|
||||||
|
const indexKey = `${parentNodeId}_index`;
|
||||||
|
|
||||||
|
// Determine item dataType from input variable
|
||||||
|
let itemDataType = 'object';
|
||||||
|
const inputVariable = variableList.find(v => `{{${v.value}}}` === parentData.config.input.defaultValue);
|
||||||
|
console.log('itemDataType defaultValue', parentData.config.input.defaultValue, variableList, inputVariable)
|
||||||
|
if (inputVariable && inputVariable.dataType.startsWith('array[')) {
|
||||||
|
itemDataType = inputVariable.dataType.replace(/^array\[(.+)\]$/, '$1');
|
||||||
|
console.log('itemDataType', itemDataType)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!addedKeys.has(itemKey)) {
|
||||||
|
addedKeys.add(itemKey);
|
||||||
|
variableList.push({
|
||||||
|
key: itemKey,
|
||||||
|
label: 'item',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: itemDataType,
|
||||||
|
value: `${parentNodeId}.item`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addedKeys.has(indexKey)) {
|
||||||
|
addedKeys.add(indexKey);
|
||||||
|
variableList.push({
|
||||||
|
key: indexKey,
|
||||||
|
label: 'index',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'number',
|
||||||
|
value: `${parentNodeId}.index`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if parent loop/iteration is connected to http-request via ERROR connection
|
||||||
|
if (parentData.type === 'loop' || parentData.type === 'iteration') {
|
||||||
|
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
|
||||||
|
parentPreviousNodeIds.forEach(prevNodeId => {
|
||||||
|
const prevNode = nodes.find(n => n.id === prevNodeId);
|
||||||
|
if (!prevNode) return;
|
||||||
|
|
||||||
|
const prevNodeData = prevNode.getData();
|
||||||
|
if (prevNodeData.type === 'http-request') {
|
||||||
|
// Check if connected via ERROR connection point
|
||||||
|
const errorEdges = edges.filter(edge => {
|
||||||
|
return edge.getTargetCellId() === parentLoopNode.id &&
|
||||||
|
edge.getSourceCellId() === prevNodeId &&
|
||||||
|
edge.getSourcePortId() === 'ERROR'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorEdges.length > 0) {
|
||||||
|
const errorMessageKey = `${prevNodeData.id}_error_message`;
|
||||||
|
const errorTypeKey = `${prevNodeData.id}_error_type`;
|
||||||
|
|
||||||
|
if (!addedKeys.has(errorMessageKey)) {
|
||||||
|
addedKeys.add(errorMessageKey);
|
||||||
|
variableList.push({
|
||||||
|
key: errorMessageKey,
|
||||||
|
label: 'error_message',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'string',
|
||||||
|
value: `${prevNodeData.id}.error_message`,
|
||||||
|
nodeData: prevNodeData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addedKeys.has(errorTypeKey)) {
|
||||||
|
addedKeys.add(errorTypeKey);
|
||||||
|
variableList.push({
|
||||||
|
key: errorTypeKey,
|
||||||
|
label: 'error_type',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'string',
|
||||||
|
value: `${prevNodeData.id}.error_type`,
|
||||||
|
nodeData: prevNodeData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return variableList;
|
return variableList;
|
||||||
}, [selectedNode, graphRef, workflowConfig?.variables]);
|
}, [selectedNode, graphRef, workflowConfig?.variables]);
|
||||||
|
|
||||||
// Filter out boolean type variables for loop and llm nodes
|
// Filter out boolean type variables for loop and llm nodes
|
||||||
const getFilteredVariableList = (nodeType?: string) => {
|
const getFilteredVariableList = (nodeType?: string, key?: string) => {
|
||||||
if (nodeType === 'loop' || nodeType === 'llm') {
|
// Check if current node is a child of iteration node
|
||||||
return variableList.filter(variable => variable.dataType !== 'boolean');
|
const parentIterationNode = selectedNode ? (() => {
|
||||||
|
const nodes = graphRef.current?.getNodes() || [];
|
||||||
|
const nodeData = selectedNode.getData();
|
||||||
|
const cycle = nodeData?.cycle;
|
||||||
|
|
||||||
|
if (cycle) {
|
||||||
|
const parentNode = nodes.find(n => n.getData().id === cycle);
|
||||||
|
if (parentNode) {
|
||||||
|
const parentData = parentNode.getData();
|
||||||
|
if (parentData?.type === 'iteration') {
|
||||||
|
return parentNode;
|
||||||
}
|
}
|
||||||
return variableList;
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})() : null;
|
||||||
|
|
||||||
|
// Helper function to add parent iteration variables
|
||||||
|
const addParentIterationVars = (filteredList: any[]) => {
|
||||||
|
if (parentIterationNode) {
|
||||||
|
const parentData = parentIterationNode.getData();
|
||||||
|
const parentNodeId = parentData.id;
|
||||||
|
|
||||||
|
if (parentData.config?.input?.defaultValue) {
|
||||||
|
const itemKey = `${parentNodeId}_item`;
|
||||||
|
const indexKey = `${parentNodeId}_index`;
|
||||||
|
|
||||||
|
const existingItemVar = filteredList.find(v => v.key === itemKey);
|
||||||
|
const existingIndexVar = filteredList.find(v => v.key === indexKey);
|
||||||
|
|
||||||
|
if (!existingItemVar) {
|
||||||
|
// Determine item dataType from input variable
|
||||||
|
let itemDataType = 'object';
|
||||||
|
const inputVariable = variableList.find(v => `{{${v.value}}}` === parentData.config.input.defaultValue);
|
||||||
|
if (inputVariable && inputVariable.dataType.startsWith('array[')) {
|
||||||
|
itemDataType = inputVariable.dataType.replace(/^array\[(.+)\]$/, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredList.push({
|
||||||
|
key: itemKey,
|
||||||
|
label: 'item',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: itemDataType,
|
||||||
|
value: `${parentNodeId}.item`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existingIndexVar) {
|
||||||
|
filteredList.push({
|
||||||
|
key: indexKey,
|
||||||
|
label: 'index',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'number',
|
||||||
|
value: `${parentNodeId}.index`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredList;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (nodeType === 'llm') {
|
||||||
|
// For LLM nodes that are children of iteration or loop nodes, include parent variables
|
||||||
|
const parentLoopNode = selectedNode ? (() => {
|
||||||
|
const nodes = graphRef.current?.getNodes() || [];
|
||||||
|
const nodeData = selectedNode.getData();
|
||||||
|
const cycle = nodeData?.cycle;
|
||||||
|
|
||||||
|
if (cycle) {
|
||||||
|
const parentNode = nodes.find(n => n.getData().id === cycle);
|
||||||
|
if (parentNode) {
|
||||||
|
const parentData = parentNode.getData();
|
||||||
|
if (parentData?.type === 'loop' || parentData?.type === 'iteration') {
|
||||||
|
return parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})() : null;
|
||||||
|
|
||||||
|
let filteredList = variableList.filter(variable => variable.dataType !== 'boolean');
|
||||||
|
|
||||||
|
// If this LLM node is a child of iteration/loop, ensure parent variables are included
|
||||||
|
if (parentLoopNode) {
|
||||||
|
const parentData = parentLoopNode.getData();
|
||||||
|
const parentNodeId = parentData.id;
|
||||||
|
|
||||||
|
// Ensure parent loop/iteration variables are included
|
||||||
|
if (parentData.type === 'loop') {
|
||||||
|
const cycleVars = parentData.cycle_vars || [];
|
||||||
|
cycleVars.forEach((cycleVar: any) => {
|
||||||
|
const key = `${parentNodeId}_cycle_${cycleVar.name}`;
|
||||||
|
const existingVar = filteredList.find(v => v.key === key);
|
||||||
|
if (!existingVar && cycleVar.name && cycleVar.type !== 'boolean') {
|
||||||
|
filteredList.push({
|
||||||
|
key,
|
||||||
|
label: cycleVar.name,
|
||||||
|
type: 'variable',
|
||||||
|
dataType: cycleVar.type || 'String',
|
||||||
|
value: `${parentNodeId}.${cycleVar.name}`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (parentData.type === 'iteration') {
|
||||||
|
// Add item and index variables for iteration parent
|
||||||
|
if (parentData.config?.input?.defaultValue) {
|
||||||
|
const itemKey = `${parentNodeId}_item`;
|
||||||
|
const indexKey = `${parentNodeId}_index`;
|
||||||
|
|
||||||
|
const existingItemVar = filteredList.find(v => v.key === itemKey);
|
||||||
|
const existingIndexVar = filteredList.find(v => v.key === indexKey);
|
||||||
|
|
||||||
|
if (!existingItemVar) {
|
||||||
|
// Determine item dataType from input variable
|
||||||
|
let itemDataType = 'object';
|
||||||
|
const inputVariable = variableList.find(v => `{{${v.value}}}` === parentData.config.input.defaultValue);
|
||||||
|
if (inputVariable && inputVariable.dataType.startsWith('array[')) {
|
||||||
|
itemDataType = inputVariable.dataType.replace(/^array\[(.+)\]$/, '$1');
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredList.push({
|
||||||
|
key: itemKey,
|
||||||
|
label: 'item',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: itemDataType,
|
||||||
|
value: `${parentNodeId}.item`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existingIndexVar) {
|
||||||
|
filteredList.push({
|
||||||
|
key: indexKey,
|
||||||
|
label: 'index',
|
||||||
|
type: 'variable',
|
||||||
|
dataType: 'Number',
|
||||||
|
value: `${parentNodeId}.index`,
|
||||||
|
nodeData: parentData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredList;
|
||||||
|
}
|
||||||
|
if (nodeType === 'knowledge-retrieval' || nodeType === 'parameter-extractor' && key !== 'prompt' || nodeType === 'memory-read' || nodeType === 'memory-write' || nodeType === 'question-classifier') {
|
||||||
|
let filteredList = variableList.filter(variable => variable.dataType === 'string');
|
||||||
|
return addParentIterationVars(filteredList);
|
||||||
|
}
|
||||||
|
if (nodeType === 'parameter-extractor' && key === 'prompt') {
|
||||||
|
let filteredList = variableList.filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
|
||||||
|
return addParentIterationVars(filteredList);
|
||||||
|
}
|
||||||
|
if (nodeType === 'iteration' && key === 'output') {
|
||||||
|
return variableList.filter(variable => variable.value.includes('sys.'));
|
||||||
|
}
|
||||||
|
if (nodeType === 'iteration') {
|
||||||
|
return variableList.filter(variable => variable.dataType.includes('array'));
|
||||||
|
}
|
||||||
|
if (nodeType === 'loop' && key === 'condition') {
|
||||||
|
let filteredList = variableList.filter(variable => variable.nodeData.type !== 'loop');
|
||||||
|
return addParentIterationVars(filteredList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For all other node types, add parent iteration variables if applicable
|
||||||
|
let baseList = variableList;
|
||||||
|
return addParentIterationVars(baseList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultVariableList = calculateVariableList(selectedNode as Node, graphRef, workflowConfig )
|
||||||
|
|
||||||
console.log('values', values)
|
console.log('values', values)
|
||||||
console.log('variableList', variableList, selectedNode?.data)
|
console.log('variableList', variableList, defaultVariableList)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rb:w-75 rb:fixed rb:right-0 rb:top-16 rb:bottom-0 rb:p-3">
|
<div className="rb:w-75 rb:fixed rb:right-0 rb:top-16 rb:bottom-0 rb:p-3">
|
||||||
@@ -901,7 +1126,6 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item key={key} name={key}>
|
<Form.Item key={key} name={key}>
|
||||||
<MessageEditor
|
<MessageEditor
|
||||||
@@ -915,7 +1139,12 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
if (selectedNode?.data?.type === 'end' && key === 'output') {
|
if (selectedNode?.data?.type === 'end' && key === 'output') {
|
||||||
return (
|
return (
|
||||||
<Form.Item key={key} name={key}>
|
<Form.Item key={key} name={key}>
|
||||||
<MessageEditor key={key} isArray={false} parentName={key} options={variableList} />
|
<MessageEditor
|
||||||
|
key={key}
|
||||||
|
isArray={false}
|
||||||
|
parentName={key}
|
||||||
|
options={variableList.filter(variable => variable.nodeData?.type !== 'knowledge-retrieval')}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -943,7 +1172,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
isArray={!!config.isArray}
|
isArray={!!config.isArray}
|
||||||
parentName={key}
|
parentName={key}
|
||||||
enableJinja2={config.enableJinja2 as boolean}
|
enableJinja2={config.enableJinja2 as boolean}
|
||||||
options={getFilteredVariableList(selectedNode?.data?.type)}
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
@@ -964,7 +1193,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
<Form.Item key={key} name={key}>
|
<Form.Item key={key} name={key}>
|
||||||
<GroupVariableList
|
<GroupVariableList
|
||||||
name={key}
|
name={key}
|
||||||
options={getFilteredVariableList(selectedNode?.data?.type)}
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
isCanAdd={!!(values as any)?.group}
|
isCanAdd={!!(values as any)?.group}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -976,7 +1205,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
<Form.Item key={key} name={key}>
|
<Form.Item key={key} name={key}>
|
||||||
<CaseList
|
<CaseList
|
||||||
name={key}
|
name={key}
|
||||||
options={getFilteredVariableList(selectedNode?.data?.type)}
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
selectedNode={selectedNode}
|
selectedNode={selectedNode}
|
||||||
graphRef={graphRef}
|
graphRef={graphRef}
|
||||||
/>
|
/>
|
||||||
@@ -989,7 +1218,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
<Form.Item key={key} name={key}
|
<Form.Item key={key} name={key}
|
||||||
label={t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
label={t(`workflow.config.${selectedNode?.data?.type}.${key}`)}
|
||||||
>
|
>
|
||||||
<MappingList name={key} options={getFilteredVariableList(selectedNode?.data?.type)} />
|
<MappingList name={key} options={getFilteredVariableList(selectedNode?.data?.type, key)} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -999,7 +1228,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
<Form.Item key={key} name={key}>
|
<Form.Item key={key} name={key}>
|
||||||
<CycleVarsList
|
<CycleVarsList
|
||||||
parentName={key}
|
parentName={key}
|
||||||
options={getFilteredVariableList(selectedNode?.data?.type)}
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
@@ -1013,9 +1242,9 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
if (config.filterLoopIterationVars) {
|
if (config.filterLoopIterationVars) {
|
||||||
const loopIterationVars: Suggestion[] = [];
|
const loopIterationVars: Suggestion[] = [];
|
||||||
|
|
||||||
return [...getFilteredVariableList(selectedNode?.data?.type), ...loopIterationVars];
|
return [...getFilteredVariableList(selectedNode?.data?.type, key), ...loopIterationVars];
|
||||||
}
|
}
|
||||||
return getFilteredVariableList(selectedNode?.data?.type);
|
return getFilteredVariableList(selectedNode?.data?.type, key);
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -1060,7 +1289,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
? <VariableSelect
|
? <VariableSelect
|
||||||
placeholder={t('common.pleaseSelect')}
|
placeholder={t('common.pleaseSelect')}
|
||||||
options={(() => {
|
options={(() => {
|
||||||
const baseVariableList = getFilteredVariableList(selectedNode?.data?.type);
|
const baseVariableList = getFilteredVariableList(selectedNode?.data?.type, key);
|
||||||
// Apply filtering if specified in config
|
// Apply filtering if specified in config
|
||||||
if (config.filterNodeTypes || config.filterVariableNames) {
|
if (config.filterNodeTypes || config.filterVariableNames) {
|
||||||
return baseVariableList.filter(variable => {
|
return baseVariableList.filter(variable => {
|
||||||
@@ -1068,7 +1297,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
(Array.isArray(config.filterNodeTypes) && config.filterNodeTypes.includes(variable.nodeData?.type));
|
(Array.isArray(config.filterNodeTypes) && config.filterNodeTypes.includes(variable.nodeData?.type));
|
||||||
const variableNameMatch = !config.filterVariableNames ||
|
const variableNameMatch = !config.filterVariableNames ||
|
||||||
(Array.isArray(config.filterVariableNames) && config.filterVariableNames.includes(variable.label));
|
(Array.isArray(config.filterVariableNames) && config.filterVariableNames.includes(variable.label));
|
||||||
return nodeTypeMatch && variableNameMatch;
|
return nodeTypeMatch || variableNameMatch;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Filter child nodes for iteration output
|
// Filter child nodes for iteration output
|
||||||
@@ -1085,7 +1314,7 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return baseVariableList.filter(variable =>
|
return baseVariableList.filter(variable =>
|
||||||
childNodes.some(node => node.id === variable.nodeData?.id)
|
childNodes.some(node => node.id === variable.nodeData?.id) || selectedNode?.data?.type === 'iteration' && key === 'output' && variable.value.includes('sys.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return baseVariableList;
|
return baseVariableList;
|
||||||
@@ -1095,7 +1324,12 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
: config.type === 'switch'
|
: config.type === 'switch'
|
||||||
? <Switch onChange={key === 'group' ? () => { form.setFieldValue('group_variables', []) } : undefined} />
|
? <Switch onChange={key === 'group' ? () => { form.setFieldValue('group_variables', []) } : undefined} />
|
||||||
: config.type === 'categoryList'
|
: config.type === 'categoryList'
|
||||||
? <CategoryList parentName={key} selectedNode={selectedNode} graphRef={graphRef} />
|
? <CategoryList
|
||||||
|
parentName={key}
|
||||||
|
selectedNode={selectedNode}
|
||||||
|
graphRef={graphRef}
|
||||||
|
options={getFilteredVariableList(selectedNode?.data?.type, key)}
|
||||||
|
/>
|
||||||
: config.type === 'conditionList'
|
: config.type === 'conditionList'
|
||||||
? <ConditionList
|
? <ConditionList
|
||||||
parentName={key}
|
parentName={key}
|
||||||
@@ -1109,18 +1343,9 @@ const Properties: FC<PropertiesProps> = ({
|
|||||||
value: `${selectedNode.getData().id}.${cycleVar.name}`,
|
value: `${selectedNode.getData().id}.${cycleVar.name}`,
|
||||||
nodeData: selectedNode.getData(),
|
nodeData: selectedNode.getData(),
|
||||||
}));
|
}));
|
||||||
return [...variableList.filter(variable => {
|
|
||||||
// Keep conversation variables
|
return [...getFilteredVariableList(selectedNode?.data?.type, key), ...cycleVarSuggestions];
|
||||||
if (variable.group === 'CONVERSATION') return true;
|
})()}
|
||||||
// Keep sys variables from start nodes
|
|
||||||
if (variable.nodeData?.type === 'start' && variable.value?.startsWith('sys.')) return true;
|
|
||||||
// Keep variables from non-start nodes
|
|
||||||
if (variable.nodeData?.type !== 'start' && variable.nodeData?.type !== 'http-request' && variable.dataType !== 'boolean') return true;
|
|
||||||
// Filter out custom variables from start nodes
|
|
||||||
return false;
|
|
||||||
}), ...cycleVarSuggestions];
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
selectedNode={selectedNode}
|
selectedNode={selectedNode}
|
||||||
graphRef={graphRef}
|
graphRef={graphRef}
|
||||||
addBtnText={t('workflow.config.addCase')}
|
addBtnText={t('workflow.config.addCase')}
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
|||||||
config: {
|
config: {
|
||||||
input: {
|
input: {
|
||||||
type: 'variableList',
|
type: 'variableList',
|
||||||
filterNodeTypes: ['knowledge-retrieval'],
|
filterNodeTypes: ['knowledge-retrieval', 'iteration', 'loop'],
|
||||||
filterVariableNames: ['message']
|
filterVariableNames: ['message']
|
||||||
},
|
},
|
||||||
parallel: {
|
parallel: {
|
||||||
@@ -334,8 +334,7 @@ export const nodeLibrary: NodeLibrary[] = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{ type: "assigner", icon: assignerIcon,
|
||||||
type: "assigner", icon: assignerIcon,
|
|
||||||
config: {
|
config: {
|
||||||
assignments: {
|
assignments: {
|
||||||
type: 'assignmentList',
|
type: 'assignmentList',
|
||||||
@@ -629,3 +628,113 @@ export const graphNodeLibrary: Record<string, NodeConfig> = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface OutputVariable {
|
||||||
|
default?: Array<{
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}>;
|
||||||
|
define?: string[];
|
||||||
|
sys?: Array<{
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}>;
|
||||||
|
error?: Array<{
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
export const outputVariable: { [key: string]: OutputVariable } = {
|
||||||
|
start: {
|
||||||
|
sys: [
|
||||||
|
{ name: "message", type: "string" },
|
||||||
|
{ name: "conversation_id", type: "string" },
|
||||||
|
{ name: "execution_id", type: "string", },
|
||||||
|
{ name: "workspace_id", type: "string" },
|
||||||
|
{ name: "user_id", type: "string" },
|
||||||
|
],
|
||||||
|
define: ['variables']
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
},
|
||||||
|
llm: {
|
||||||
|
default: [
|
||||||
|
{ name: "output", type: "string" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'knowledge-retrieval': {
|
||||||
|
default: [
|
||||||
|
{ name: "output", type: "array[object]" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'parameter-extractor': {
|
||||||
|
default: [
|
||||||
|
{ name: "__is_success", type: "number" },
|
||||||
|
{ name: "__reason", type: "string" },
|
||||||
|
],
|
||||||
|
define: ['params']
|
||||||
|
},
|
||||||
|
'memory-read': {
|
||||||
|
default: [
|
||||||
|
{ name: "answer", type: "string" },
|
||||||
|
{ name: "intermediate_outputs", type: "array[object]" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'memory-write': {
|
||||||
|
|
||||||
|
},
|
||||||
|
'if-else': {
|
||||||
|
|
||||||
|
},
|
||||||
|
'question-classifier': {
|
||||||
|
default: [
|
||||||
|
{ name: "class_name", type: "string" },
|
||||||
|
// { name: "output", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'iteration': {
|
||||||
|
default: [
|
||||||
|
// { name: "item", type: "string" }, // 仅内部使用
|
||||||
|
{ name: "output", type: "array[string]" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'loop': {
|
||||||
|
define: ['cycle_vars']
|
||||||
|
},
|
||||||
|
'cycle-start': {
|
||||||
|
|
||||||
|
},
|
||||||
|
'break': {
|
||||||
|
|
||||||
|
},
|
||||||
|
'var-aggregator': {
|
||||||
|
// default: [
|
||||||
|
// { name: "output", type: "string" },
|
||||||
|
// ],
|
||||||
|
define: ['group_variables']
|
||||||
|
},
|
||||||
|
'assigner': {
|
||||||
|
|
||||||
|
},
|
||||||
|
'http-request': {
|
||||||
|
default: [
|
||||||
|
{ name: "body", type: "string" },
|
||||||
|
{ name: "status_code", type: "number" },
|
||||||
|
],
|
||||||
|
error: [
|
||||||
|
{ name: "error_message", type: "string" },
|
||||||
|
{ name: "error_type", type: "string" },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'tool': {
|
||||||
|
default: [
|
||||||
|
{ name: "data", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'jinja-render': {
|
||||||
|
default: [
|
||||||
|
{ name: "output", type: "string" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user