4.8.12 dev (#2928)
* perf: optimize global variables (#2863) * feat: add global variable types * add global variables to debug * fix select dnd * unify InputTypeConfig params * feat: http node url support variables (#2891) * feat: http node url support variables * change to prompt editor * fix: global variables (#2892) * fix global variables * fix type * perf: global variables * perf: workflow delete node error (#2905) * update lock * update 4812 doc * feat: add node course url config (#2897) * feat: add node course url config * change plugin course url * change default doc url * change url store * delete unused code * fix: global variable (#2915) * fix: global variable * add comment * fix: interactive check * locj * perf: debug switch to global tab when click run & global var default reset (#2925) * fix: tool course url * fix: global var default value & wrap variable form (#2926) * fix: add dataset tags not update render (#2927) * feat: tool will save histories * perf: global variables code * perf: FE_DOMAIN config --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
parent
00638d6ee7
commit
d4e0a43771
@ -9,8 +9,12 @@ weight: 812
|
|||||||
|
|
||||||
## 更新说明
|
## 更新说明
|
||||||
|
|
||||||
1. 新增 - 全局变量支持更多数据类型
|
1. 新增 - 全局变量支持数字类型,并且支持配置默认值和部分输入框参数。
|
||||||
2. 新增 - FE_DOMAIN 环境变量,配置该环境变量后,上传文件/图片会补全后缀后得到完整地址。(可解决 docx 文件图片链接,有时会无法被模型识别问题)
|
2. 新增 - FE_DOMAIN 环境变量,配置该环境变量后,上传文件/图片会补全后缀后得到完整地址。(可解决 docx 文件图片链接,有时会无法被模型识别问题)
|
||||||
3. 新增 - 工具调用支持交互模式
|
3. 新增 - 工具调用支持交互模式
|
||||||
4. 修复 - 文件后缀判断,去除 query 影响。
|
4. 新增 - Debug 模式支持输入全局变量
|
||||||
5. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。
|
5. 新增 - chat openapi 文档
|
||||||
|
6. 新增 - wiki 搜索插件
|
||||||
|
7. 修复 - 文件后缀判断,去除 query 影响。
|
||||||
|
8. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。
|
||||||
|
9. 修复 - 用户交互节点未阻塞流程。
|
||||||
|
|||||||
@ -154,7 +154,7 @@ services:
|
|||||||
- MILVUS_TOKEN=none
|
- MILVUS_TOKEN=none
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
# 前端地址
|
# 前端地址: http://localhost:3000
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# 日志等级: debug, info, warn, error
|
# 日志等级: debug, info, warn, error
|
||||||
- LOG_LEVEL=info
|
- LOG_LEVEL=info
|
||||||
|
|||||||
@ -111,7 +111,7 @@ services:
|
|||||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
# 前端地址
|
# 前端地址: http://localhost:3000
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# 日志等级: debug, info, warn, error
|
# 日志等级: debug, info, warn, error
|
||||||
- LOG_LEVEL=info
|
- LOG_LEVEL=info
|
||||||
|
|||||||
@ -92,7 +92,7 @@ services:
|
|||||||
- MILVUS_TOKEN=zilliz_cloud_token
|
- MILVUS_TOKEN=zilliz_cloud_token
|
||||||
# sandbox 地址
|
# sandbox 地址
|
||||||
- SANDBOX_URL=http://sandbox:3000
|
- SANDBOX_URL=http://sandbox:3000
|
||||||
# 前端地址
|
# 前端地址: http://localhost:3000
|
||||||
- FE_DOMAIN=
|
- FE_DOMAIN=
|
||||||
# 日志等级: debug, info, warn, error
|
# 日志等级: debug, info, warn, error
|
||||||
- LOG_LEVEL=info
|
- LOG_LEVEL=info
|
||||||
|
|||||||
17
packages/global/core/app/type.d.ts
vendored
17
packages/global/core/app/type.d.ts
vendored
@ -13,6 +13,7 @@ import { StoreEdgeItemType } from '../workflow/type/edge';
|
|||||||
import { PermissionSchemaType, PermissionValueType } from '../../support/permission/type';
|
import { PermissionSchemaType, PermissionValueType } from '../../support/permission/type';
|
||||||
import { AppPermission } from '../../support/permission/app/controller';
|
import { AppPermission } from '../../support/permission/app/controller';
|
||||||
import { ParentIdType } from '../../common/parentFolder/type';
|
import { ParentIdType } from '../../common/parentFolder/type';
|
||||||
|
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
||||||
|
|
||||||
export type AppSchema = {
|
export type AppSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -114,11 +115,19 @@ export type VariableItemType = {
|
|||||||
id: string;
|
id: string;
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
type: `${VariableInputEnum}`;
|
type: VariableInputEnum;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
maxLen: number;
|
description: string;
|
||||||
enums: { value: string }[];
|
valueType?: WorkflowIOValueTypeEnum;
|
||||||
valueType: WorkflowIOValueTypeEnum;
|
defaultValue?: any;
|
||||||
|
|
||||||
|
// input
|
||||||
|
maxLength?: number;
|
||||||
|
// numberInput
|
||||||
|
max?: number;
|
||||||
|
min?: number;
|
||||||
|
// select
|
||||||
|
enums?: { value: string; label: string }[];
|
||||||
};
|
};
|
||||||
// tts
|
// tts
|
||||||
export type AppTTSConfigType = {
|
export type AppTTSConfigType = {
|
||||||
|
|||||||
@ -122,6 +122,9 @@ export const chats2GPTMessages = ({
|
|||||||
value.type === ChatItemValueTypeEnum.text &&
|
value.type === ChatItemValueTypeEnum.text &&
|
||||||
typeof value.text?.content === 'string'
|
typeof value.text?.content === 'string'
|
||||||
) {
|
) {
|
||||||
|
if (!value.text.content && item.value.length > 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Concat text
|
// Concat text
|
||||||
const lastValue = item.value[i - 1];
|
const lastValue = item.value[i - 1];
|
||||||
const lastResult = aiResults[aiResults.length - 1];
|
const lastResult = aiResults[aiResults.length - 1];
|
||||||
|
|||||||
@ -267,29 +267,51 @@ export enum NodeOutputKeyEnum {
|
|||||||
export enum VariableInputEnum {
|
export enum VariableInputEnum {
|
||||||
input = 'input',
|
input = 'input',
|
||||||
textarea = 'textarea',
|
textarea = 'textarea',
|
||||||
|
numberInput = 'numberInput',
|
||||||
select = 'select',
|
select = 'select',
|
||||||
custom = 'custom'
|
custom = 'custom'
|
||||||
}
|
}
|
||||||
export const variableMap = {
|
export const variableMap: Record<
|
||||||
|
VariableInputEnum,
|
||||||
|
{
|
||||||
|
icon: string;
|
||||||
|
label: string;
|
||||||
|
value: VariableInputEnum;
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
[VariableInputEnum.input]: {
|
[VariableInputEnum.input]: {
|
||||||
icon: 'core/app/variable/input',
|
icon: 'core/workflow/inputType/input',
|
||||||
title: i18nT('common:core.module.variable.input type'),
|
label: i18nT('common:core.workflow.inputType.input'),
|
||||||
desc: ''
|
value: VariableInputEnum.input,
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||||
},
|
},
|
||||||
[VariableInputEnum.textarea]: {
|
[VariableInputEnum.textarea]: {
|
||||||
icon: 'core/app/variable/textarea',
|
icon: 'core/workflow/inputType/textarea',
|
||||||
title: i18nT('common:core.module.variable.textarea type'),
|
label: i18nT('common:core.workflow.inputType.textarea'),
|
||||||
desc: i18nT('app:variable.textarea_type_desc')
|
value: VariableInputEnum.textarea,
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
description: i18nT('app:variable.textarea_type_desc')
|
||||||
|
},
|
||||||
|
[VariableInputEnum.numberInput]: {
|
||||||
|
icon: 'core/workflow/inputType/numberInput',
|
||||||
|
label: i18nT('common:core.workflow.inputType.number input'),
|
||||||
|
value: VariableInputEnum.numberInput,
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum.number
|
||||||
},
|
},
|
||||||
[VariableInputEnum.select]: {
|
[VariableInputEnum.select]: {
|
||||||
icon: 'core/app/variable/select',
|
icon: 'core/workflow/inputType/option',
|
||||||
title: i18nT('common:core.module.variable.select type'),
|
label: i18nT('common:core.workflow.inputType.select'),
|
||||||
desc: ''
|
value: VariableInputEnum.select,
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||||
},
|
},
|
||||||
[VariableInputEnum.custom]: {
|
[VariableInputEnum.custom]: {
|
||||||
icon: 'core/app/variable/external',
|
icon: 'core/workflow/inputType/customVariable',
|
||||||
title: i18nT('common:core.module.variable.Custom type'),
|
label: i18nT('common:core.workflow.inputType.custom'),
|
||||||
desc: i18nT('app:variable.select type_desc')
|
value: VariableInputEnum.custom,
|
||||||
|
defaultValueType: WorkflowIOValueTypeEnum.string,
|
||||||
|
description: i18nT('app:variable.select type_desc')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
|||||||
intro: i18nT('workflow:template.ai_chat_intro'),
|
intro: i18nT('workflow:template.ai_chat_intro'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/ai_chat/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
Input_Template_SettingAiModel,
|
Input_Template_SettingAiModel,
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
|||||||
avatar: 'core/workflow/template/reply',
|
avatar: 'core/workflow/template/reply',
|
||||||
name: i18nT('workflow:assigned_reply'),
|
name: i18nT('workflow:assigned_reply'),
|
||||||
intro: i18nT('workflow:intro_assigned_reply'),
|
intro: i18nT('workflow:intro_assigned_reply'),
|
||||||
|
courseUrl: '/docs/workflow/modules/reply/',
|
||||||
version: '481',
|
version: '481',
|
||||||
isTool: true,
|
isTool: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
|||||||
intro: i18nT('workflow:intro_question_classification'),
|
intro: i18nT('workflow:intro_question_classification'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
version: '481',
|
version: '481',
|
||||||
|
courseUrl: '/docs/workflow/modules/question_classify/',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
...Input_Template_SelectAIModel,
|
...Input_Template_SelectAIModel,
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
|||||||
intro: i18nT('workflow:intro_text_content_extraction'),
|
intro: i18nT('workflow:intro_text_content_extraction'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/content_extract/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -17,6 +17,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
|
|||||||
avatar: 'core/workflow/template/customFeedback',
|
avatar: 'core/workflow/template/customFeedback',
|
||||||
name: i18nT('workflow:custom_feedback'),
|
name: i18nT('workflow:custom_feedback'),
|
||||||
intro: i18nT('workflow:intro_custom_feedback'),
|
intro: i18nT('workflow:intro_custom_feedback'),
|
||||||
|
courseUrl: '/docs/workflow/modules/custom_feedback/',
|
||||||
version: '486',
|
version: '486',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
|||||||
intro: Dataset_SEARCH_DESC,
|
intro: Dataset_SEARCH_DESC,
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/dataset_search/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -27,6 +27,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
|||||||
intro: i18nT('workflow:intro_http_request'),
|
intro: i18nT('workflow:intro_http_request'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/http/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export const IfElseNode: FlowNodeTemplateType = {
|
|||||||
name: i18nT('workflow:condition_checker'),
|
name: i18nT('workflow:condition_checker'),
|
||||||
intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
|
intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/tfswitch/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -32,6 +32,7 @@ export const LafModule: FlowNodeTemplateType = {
|
|||||||
intro: i18nT('workflow:intro_laf_function_call'),
|
intro: i18nT('workflow:intro_laf_function_call'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/laf/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export const CodeNode: FlowNodeTemplateType = {
|
|||||||
name: i18nT('workflow:code_execution'),
|
name: i18nT('workflow:code_execution'),
|
||||||
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
|
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/sandbox/',
|
||||||
version: '482',
|
version: '482',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export const TextEditorNode: FlowNodeTemplateType = {
|
|||||||
avatar: 'core/workflow/template/textConcat',
|
avatar: 'core/workflow/template/textConcat',
|
||||||
name: i18nT('workflow:text_concatenation'),
|
name: i18nT('workflow:text_concatenation'),
|
||||||
intro: i18nT('workflow:intro_text_concatenation'),
|
intro: i18nT('workflow:intro_text_concatenation'),
|
||||||
|
courseUrl: '/docs/workflow/modules/text_editor/',
|
||||||
version: '486',
|
version: '486',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
|||||||
name: i18nT('workflow:template.tool_call'),
|
name: i18nT('workflow:template.tool_call'),
|
||||||
intro: i18nT('workflow:template.tool_call_intro'),
|
intro: i18nT('workflow:template.tool_call_intro'),
|
||||||
showStatus: true,
|
showStatus: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/tool/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -30,6 +30,7 @@ export const WorkflowStart: FlowNodeTemplateType = {
|
|||||||
intro: '',
|
intro: '',
|
||||||
forbidDelete: true,
|
forbidDelete: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
|
courseUrl: '/docs/workflow/modules/input/',
|
||||||
version: '481',
|
version: '481',
|
||||||
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
|
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
|
||||||
outputs: [
|
outputs: [
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export type WorkflowTemplateType = {
|
|||||||
avatar: string;
|
avatar: string;
|
||||||
intro?: string;
|
intro?: string;
|
||||||
author?: string;
|
author?: string;
|
||||||
inputExplanationUrl?: string;
|
courseUrl?: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
|
||||||
showStatus?: boolean;
|
showStatus?: boolean;
|
||||||
|
|||||||
2
packages/global/core/workflow/type/node.d.ts
vendored
2
packages/global/core/workflow/type/node.d.ts
vendored
@ -32,7 +32,6 @@ export type FlowNodeCommonType = {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
name: string;
|
name: string;
|
||||||
intro?: string; // template list intro
|
intro?: string; // template list intro
|
||||||
inputExplanationUrl?: string;
|
|
||||||
showStatus?: boolean; // chatting response step status
|
showStatus?: boolean; // chatting response step status
|
||||||
version: string;
|
version: string;
|
||||||
|
|
||||||
@ -69,6 +68,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
|
|||||||
unique?: boolean;
|
unique?: boolean;
|
||||||
|
|
||||||
diagram?: string; // diagram url
|
diagram?: string; // diagram url
|
||||||
|
courseUrl?: string; // course url
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodeTemplateListItemType = {
|
export type NodeTemplateListItemType = {
|
||||||
|
|||||||
@ -230,6 +230,7 @@ export const appData2FlowNodeIO = ({
|
|||||||
FlowNodeInputTypeEnum.textarea,
|
FlowNodeInputTypeEnum.textarea,
|
||||||
FlowNodeInputTypeEnum.reference
|
FlowNodeInputTypeEnum.reference
|
||||||
],
|
],
|
||||||
|
[VariableInputEnum.numberInput]: [FlowNodeInputTypeEnum.numberInput],
|
||||||
[VariableInputEnum.select]: [FlowNodeInputTypeEnum.select],
|
[VariableInputEnum.select]: [FlowNodeInputTypeEnum.select],
|
||||||
[VariableInputEnum.custom]: [
|
[VariableInputEnum.custom]: [
|
||||||
FlowNodeInputTypeEnum.input,
|
FlowNodeInputTypeEnum.input,
|
||||||
@ -246,7 +247,7 @@ export const appData2FlowNodeIO = ({
|
|||||||
description: '',
|
description: '',
|
||||||
valueType: WorkflowIOValueTypeEnum.any,
|
valueType: WorkflowIOValueTypeEnum.any,
|
||||||
required: item.required,
|
required: item.required,
|
||||||
list: item.enums.map((enumItem) => ({
|
list: item.enums?.map((enumItem) => ({
|
||||||
label: enumItem.value,
|
label: enumItem.value,
|
||||||
value: enumItem.value
|
value: enumItem.value
|
||||||
}))
|
}))
|
||||||
@ -391,7 +392,13 @@ export function replaceEditorVariable({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [];
|
return [
|
||||||
|
{
|
||||||
|
id: item.key,
|
||||||
|
value: item.value,
|
||||||
|
nodeId: runningNode.nodeId
|
||||||
|
}
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
|
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"name": "Doc2X 图像(文件)识别",
|
"name": "Doc2X 图像(文件)识别",
|
||||||
"avatar": "plugins/doc2x",
|
"avatar": "plugins/doc2x",
|
||||||
"intro": "将上传的图片文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
"intro": "将上传的图片文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"name": "Doc2X PDF文件(文件)识别",
|
"name": "Doc2X PDF文件(文件)识别",
|
||||||
"avatar": "plugins/doc2x",
|
"avatar": "plugins/doc2x",
|
||||||
"intro": "将上传的PDF文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
"intro": "将上传的PDF文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"name": "Doc2X 图像(URL)识别",
|
"name": "Doc2X 图像(URL)识别",
|
||||||
"avatar": "plugins/doc2x",
|
"avatar": "plugins/doc2x",
|
||||||
"intro": "从URL下载图片并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
"intro": "从URL下载图片并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"name": "Doc2X PDF文件(URL)识别",
|
"name": "Doc2X PDF文件(URL)识别",
|
||||||
"avatar": "plugins/doc2x",
|
"avatar": "plugins/doc2x",
|
||||||
"intro": "从URL下载PDF文件,并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
"intro": "从URL下载PDF文件,并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||||
"showStatus": true,
|
"showStatus": true,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"name": "飞书机器人 webhook",
|
"name": "飞书机器人 webhook",
|
||||||
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
|
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
|
||||||
"intro": "向飞书机器人发起 webhook 请求。",
|
"intro": "向飞书机器人发起 webhook 请求。",
|
||||||
"inputExplanationUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
||||||
"showStatus": false,
|
"showStatus": false,
|
||||||
"weight": 10,
|
"weight": 10,
|
||||||
|
|
||||||
|
|||||||
@ -96,7 +96,7 @@ export async function getChildAppPreviewNode({
|
|||||||
avatar: app.avatar,
|
avatar: app.avatar,
|
||||||
name: app.name,
|
name: app.name,
|
||||||
intro: app.intro,
|
intro: app.intro,
|
||||||
inputExplanationUrl: app.inputExplanationUrl,
|
courseUrl: app.courseUrl,
|
||||||
showStatus: app.showStatus,
|
showStatus: app.showStatus,
|
||||||
isTool: true,
|
isTool: true,
|
||||||
version: app.version,
|
version: app.version,
|
||||||
|
|||||||
@ -150,7 +150,11 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
|||||||
assistantResponses = [], // FastGPT system store assistant.value response
|
assistantResponses = [], // FastGPT system store assistant.value response
|
||||||
runTimes
|
runTimes
|
||||||
} = await (async () => {
|
} = await (async () => {
|
||||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
const adaptMessages = chats2GPTMessages({
|
||||||
|
messages,
|
||||||
|
reserveId: false,
|
||||||
|
reserveTool: !!toolModel.toolChoice
|
||||||
|
});
|
||||||
|
|
||||||
if (toolModel.toolChoice) {
|
if (toolModel.toolChoice) {
|
||||||
return runToolWithToolChoice({
|
return runToolWithToolChoice({
|
||||||
|
|||||||
@ -275,6 +275,7 @@ export const iconPaths = {
|
|||||||
'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'),
|
'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'),
|
||||||
date: () => import('./icons/date.svg'),
|
date: () => import('./icons/date.svg'),
|
||||||
delete: () => import('./icons/delete.svg'),
|
delete: () => import('./icons/delete.svg'),
|
||||||
|
drag: () => import('./icons/drag.svg'),
|
||||||
edit: () => import('./icons/edit.svg'),
|
edit: () => import('./icons/edit.svg'),
|
||||||
empty: () => import('./icons/empty.svg'),
|
empty: () => import('./icons/empty.svg'),
|
||||||
export: () => import('./icons/export.svg'),
|
export: () => import('./icons/export.svg'),
|
||||||
|
|||||||
8
packages/web/components/common/Icon/icons/drag.svg
Normal file
8
packages/web/components/common/Icon/icons/drag.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg viewBox="0 0 19 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.34434 4C5.3625 4 4.56656 4.79594 4.56656 5.77778C4.56656 6.75962 5.3625 7.55555 6.34434 7.55555C7.32618 7.55555 8.12211 6.75962 8.12211 5.77778C8.12211 4.79594 7.32618 4 6.34434 4Z" />
|
||||||
|
<path d="M6.34434 10.2222C5.3625 10.2222 4.56656 11.0182 4.56656 12C4.56656 12.9818 5.3625 13.7778 6.34434 13.7778C7.32618 13.7778 8.12211 12.9818 8.12211 12C8.12211 11.0182 7.32618 10.2222 6.34434 10.2222Z" />
|
||||||
|
<path d="M6.34434 16.4444C5.3625 16.4444 4.56656 17.2404 4.56656 18.2222C4.56656 19.2041 5.3625 20 6.34434 20C7.32618 20 8.12211 19.2041 8.12211 18.2222C8.12211 17.2404 7.32618 16.4444 6.34434 16.4444Z" />
|
||||||
|
<path d="M13.4555 4C12.4736 4 11.6777 4.79594 11.6777 5.77778C11.6777 6.75962 12.4736 7.55555 13.4555 7.55555C14.4373 7.55555 15.2332 6.75962 15.2332 5.77778C15.2332 4.79594 14.4373 4 13.4555 4Z" />
|
||||||
|
<path d="M13.4555 10.2222C12.4736 10.2222 11.6777 11.0182 11.6777 12C11.6777 12.9818 12.4736 13.7778 13.4555 13.7778C14.4373 13.7778 15.2332 12.9818 15.2332 12C15.2332 11.0182 14.4373 10.2222 13.4555 10.2222Z" />
|
||||||
|
<path d="M13.4555 16.4444C12.4736 16.4444 11.6777 17.2404 11.6777 18.2222C11.6777 19.2041 12.4736 20 13.4555 20C14.4373 20 15.2332 19.2041 15.2332 18.2222C15.2332 17.2404 14.4373 16.4444 13.4555 16.4444Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
@ -21,6 +21,7 @@
|
|||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
"Move": "Move",
|
"Move": "Move",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
"None": "None",
|
||||||
"Rename": "Rename",
|
"Rename": "Rename",
|
||||||
"Resume": "Resume",
|
"Resume": "Resume",
|
||||||
"Running": "Running",
|
"Running": "Running",
|
||||||
@ -750,7 +751,6 @@
|
|||||||
"core.module.variable.select type": "Dropdown Single Select",
|
"core.module.variable.select type": "Dropdown Single Select",
|
||||||
"core.module.variable.text max length": "Max Length",
|
"core.module.variable.text max length": "Max Length",
|
||||||
"core.module.variable.textarea type": "Paragraph",
|
"core.module.variable.textarea type": "Paragraph",
|
||||||
"core.module.variable.variable name": "Variable Name",
|
|
||||||
"core.module.variable.variable name is required": "Variable Name Cannot Be Empty",
|
"core.module.variable.variable name is required": "Variable Name Cannot Be Empty",
|
||||||
"core.module.variable.variable option is required": "Options Cannot Be All Empty",
|
"core.module.variable.variable option is required": "Options Cannot Be All Empty",
|
||||||
"core.module.variable.variable option is value is required": "Option Content Cannot Be Empty",
|
"core.module.variable.variable option is value is required": "Option Content Cannot Be Empty",
|
||||||
@ -781,7 +781,6 @@
|
|||||||
"core.workflow.Stop debug": "Stop Debugging",
|
"core.workflow.Stop debug": "Stop Debugging",
|
||||||
"core.workflow.Success": "Run Successful",
|
"core.workflow.Success": "Run Successful",
|
||||||
"core.workflow.Value type": "Data Type",
|
"core.workflow.Value type": "Data Type",
|
||||||
"core.workflow.Variable.Variable type": "Variable Type",
|
|
||||||
"core.workflow.debug.Done": "Debugging Completed",
|
"core.workflow.debug.Done": "Debugging Completed",
|
||||||
"core.workflow.debug.Hide result": "Hide Result",
|
"core.workflow.debug.Hide result": "Hide Result",
|
||||||
"core.workflow.debug.Not result": "No Run Result",
|
"core.workflow.debug.Not result": "No Run Result",
|
||||||
|
|||||||
@ -2,7 +2,11 @@
|
|||||||
"Array_element": "Array element",
|
"Array_element": "Array element",
|
||||||
"Code": "Code",
|
"Code": "Code",
|
||||||
"Confirm_sync_node": "It will be updated to the latest node configuration and fields that do not exist in the template will be deleted (including all custom fields).\n\nIf the fields are complex, it is recommended that you copy a node first and then update the original node to facilitate parameter copying.",
|
"Confirm_sync_node": "It will be updated to the latest node configuration and fields that do not exist in the template will be deleted (including all custom fields).\n\nIf the fields are complex, it is recommended that you copy a node first and then update the original node to facilitate parameter copying.",
|
||||||
|
"Node_variables": "Node variables",
|
||||||
|
"Node.Open_Node_Course": "Open node course",
|
||||||
"Quote_prompt_setting": "Quote prompt",
|
"Quote_prompt_setting": "Quote prompt",
|
||||||
|
"Variable.Variable type": "Variable type",
|
||||||
|
"Variable_name": "Variable name",
|
||||||
"add_new_input": "Add New Input",
|
"add_new_input": "Add New Input",
|
||||||
"add_new_output": "New output",
|
"add_new_output": "New output",
|
||||||
"append_application_reply_to_history_as_new_context": "Append the application's reply to the history as new context",
|
"append_application_reply_to_history_as_new_context": "Append the application's reply to the history as new context",
|
||||||
@ -179,6 +183,7 @@
|
|||||||
"user_question": "User Question",
|
"user_question": "User Question",
|
||||||
"user_question_tool_desc": "User input questions (questions need to be improved)",
|
"user_question_tool_desc": "User input questions (questions need to be improved)",
|
||||||
"value_type": "Value type",
|
"value_type": "Value type",
|
||||||
|
"variable_description": "Variable description",
|
||||||
"variable_picker_tips": "Type node name or variable name to search",
|
"variable_picker_tips": "Type node name or variable name to search",
|
||||||
"variable_update": "Variable Update",
|
"variable_update": "Variable Update",
|
||||||
"workflow.My edit": "My Edit",
|
"workflow.My edit": "My Edit",
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"Login": "登录",
|
"Login": "登录",
|
||||||
"Move": "移动",
|
"Move": "移动",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
|
"None": "无",
|
||||||
"Rename": "重命名",
|
"Rename": "重命名",
|
||||||
"Resume": "恢复",
|
"Resume": "恢复",
|
||||||
"Running": "运行中",
|
"Running": "运行中",
|
||||||
@ -755,7 +756,6 @@
|
|||||||
"core.module.variable.select type": "下拉单选",
|
"core.module.variable.select type": "下拉单选",
|
||||||
"core.module.variable.text max length": "最大长度",
|
"core.module.variable.text max length": "最大长度",
|
||||||
"core.module.variable.textarea type": "段落",
|
"core.module.variable.textarea type": "段落",
|
||||||
"core.module.variable.variable name": "变量名",
|
|
||||||
"core.module.variable.variable name is required": "变量名不能为空",
|
"core.module.variable.variable name is required": "变量名不能为空",
|
||||||
"core.module.variable.variable option is required": "选项不能全空",
|
"core.module.variable.variable option is required": "选项不能全空",
|
||||||
"core.module.variable.variable option is value is required": "选项内容不能为空",
|
"core.module.variable.variable option is value is required": "选项内容不能为空",
|
||||||
@ -786,7 +786,6 @@
|
|||||||
"core.workflow.Stop debug": "停止调试",
|
"core.workflow.Stop debug": "停止调试",
|
||||||
"core.workflow.Success": "运行成功",
|
"core.workflow.Success": "运行成功",
|
||||||
"core.workflow.Value type": "数据类型",
|
"core.workflow.Value type": "数据类型",
|
||||||
"core.workflow.Variable.Variable type": "变量类型",
|
|
||||||
"core.workflow.debug.Done": "完成调试",
|
"core.workflow.debug.Done": "完成调试",
|
||||||
"core.workflow.debug.Hide result": "隐藏结果",
|
"core.workflow.debug.Hide result": "隐藏结果",
|
||||||
"core.workflow.debug.Not result": "无运行结果",
|
"core.workflow.debug.Not result": "无运行结果",
|
||||||
|
|||||||
@ -2,7 +2,11 @@
|
|||||||
"Array_element": "数组元素",
|
"Array_element": "数组元素",
|
||||||
"Code": "代码",
|
"Code": "代码",
|
||||||
"Confirm_sync_node": "将会更新至最新的节点配置,不存在模板中的字段将会被删除(包括所有自定义字段)。\n如果字段较为复杂,建议您先复制一份节点,再更新原来的节点,便于参数复制。",
|
"Confirm_sync_node": "将会更新至最新的节点配置,不存在模板中的字段将会被删除(包括所有自定义字段)。\n如果字段较为复杂,建议您先复制一份节点,再更新原来的节点,便于参数复制。",
|
||||||
|
"Node_variables": "节点变量",
|
||||||
|
"Node.Open_Node_Course": "查看节点教程",
|
||||||
"Quote_prompt_setting": "引用提示词配置",
|
"Quote_prompt_setting": "引用提示词配置",
|
||||||
|
"Variable.Variable type": "变量类型",
|
||||||
|
"Variable_name": "变量名",
|
||||||
"add_new_input": "新增输入",
|
"add_new_input": "新增输入",
|
||||||
"add_new_output": "新增输出",
|
"add_new_output": "新增输出",
|
||||||
"append_application_reply_to_history_as_new_context": "将该应用回复内容拼接到历史记录中,作为新的上下文返回",
|
"append_application_reply_to_history_as_new_context": "将该应用回复内容拼接到历史记录中,作为新的上下文返回",
|
||||||
@ -180,6 +184,7 @@
|
|||||||
"user_question": "用户问题",
|
"user_question": "用户问题",
|
||||||
"user_question_tool_desc": "用户输入的问题(问题需要完善)",
|
"user_question_tool_desc": "用户输入的问题(问题需要完善)",
|
||||||
"value_type": "数据类型",
|
"value_type": "数据类型",
|
||||||
|
"variable_description": "变量描述",
|
||||||
"variable_picker_tips": "可输入节点名或变量名搜索",
|
"variable_picker_tips": "可输入节点名或变量名搜索",
|
||||||
"variable_update": "变量更新",
|
"variable_update": "变量更新",
|
||||||
"workflow.My edit": "我的编辑",
|
"workflow.My edit": "我的编辑",
|
||||||
|
|||||||
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@ -2710,28 +2710,24 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@14.2.5':
|
'@next/swc-linux-arm64-musl@14.2.5':
|
||||||
resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==}
|
resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@14.2.5':
|
'@next/swc-linux-x64-gnu@14.2.5':
|
||||||
resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==}
|
resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@14.2.5':
|
'@next/swc-linux-x64-musl@14.2.5':
|
||||||
resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==}
|
resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@14.2.5':
|
'@next/swc-win32-arm64-msvc@14.2.5':
|
||||||
resolution: {integrity: sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==}
|
resolution: {integrity: sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==}
|
||||||
@ -2792,28 +2788,24 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@node-rs/jieba-linux-arm64-musl@1.10.0':
|
'@node-rs/jieba-linux-arm64-musl@1.10.0':
|
||||||
resolution: {integrity: sha512-gxqoAVOQsn9sgYK6mFO9dsMZ/yOMvVecLZW5rGvLErjiugVvYUlESXIvCqxp2GSws8RtTqJj6p9u/lBmCCuvaw==}
|
resolution: {integrity: sha512-gxqoAVOQsn9sgYK6mFO9dsMZ/yOMvVecLZW5rGvLErjiugVvYUlESXIvCqxp2GSws8RtTqJj6p9u/lBmCCuvaw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@node-rs/jieba-linux-x64-gnu@1.10.0':
|
'@node-rs/jieba-linux-x64-gnu@1.10.0':
|
||||||
resolution: {integrity: sha512-rS5Shs8JITxJjFIjoIZ5a9O+GO21TJgKu03g2qwFE3QaN5ZOvXtz+/AqqyfT4GmmMhCujD83AGqfOGXDmItF9w==}
|
resolution: {integrity: sha512-rS5Shs8JITxJjFIjoIZ5a9O+GO21TJgKu03g2qwFE3QaN5ZOvXtz+/AqqyfT4GmmMhCujD83AGqfOGXDmItF9w==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@node-rs/jieba-linux-x64-musl@1.10.0':
|
'@node-rs/jieba-linux-x64-musl@1.10.0':
|
||||||
resolution: {integrity: sha512-BvSiF2rR8Birh2oEVHcYwq0WGC1cegkEdddWsPrrSmpKmukJE2zyjcxaOOggq2apb8fIRsjyeeUh6X3R5AgjvA==}
|
resolution: {integrity: sha512-BvSiF2rR8Birh2oEVHcYwq0WGC1cegkEdddWsPrrSmpKmukJE2zyjcxaOOggq2apb8fIRsjyeeUh6X3R5AgjvA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@node-rs/jieba-wasm32-wasi@1.10.0':
|
'@node-rs/jieba-wasm32-wasi@1.10.0':
|
||||||
resolution: {integrity: sha512-EzeAAbRrFTdYw61rd8Mfwdp/fA21d58z9vLY06CDbI+dqANfMFn1IUdwzKWi8S5J/MRhvbzonbbh3yHlz6F43Q==}
|
resolution: {integrity: sha512-EzeAAbRrFTdYw61rd8Mfwdp/fA21d58z9vLY06CDbI+dqANfMFn1IUdwzKWi8S5J/MRhvbzonbbh3yHlz6F43Q==}
|
||||||
@ -2967,55 +2959,46 @@ packages:
|
|||||||
resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==}
|
resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm-musleabihf@4.18.1':
|
'@rollup/rollup-linux-arm-musleabihf@4.18.1':
|
||||||
resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==}
|
resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-gnu@4.18.1':
|
'@rollup/rollup-linux-arm64-gnu@4.18.1':
|
||||||
resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==}
|
resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-musl@4.18.1':
|
'@rollup/rollup-linux-arm64-musl@4.18.1':
|
||||||
resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==}
|
resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
|
'@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
|
||||||
resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==}
|
resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-gnu@4.18.1':
|
'@rollup/rollup-linux-riscv64-gnu@4.18.1':
|
||||||
resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==}
|
resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-s390x-gnu@4.18.1':
|
'@rollup/rollup-linux-s390x-gnu@4.18.1':
|
||||||
resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==}
|
resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-gnu@4.18.1':
|
'@rollup/rollup-linux-x64-gnu@4.18.1':
|
||||||
resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==}
|
resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-musl@4.18.1':
|
'@rollup/rollup-linux-x64-musl@4.18.1':
|
||||||
resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==}
|
resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-win32-arm64-msvc@4.18.1':
|
'@rollup/rollup-win32-arm64-msvc@4.18.1':
|
||||||
resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==}
|
resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==}
|
||||||
|
|||||||
@ -2,17 +2,7 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
ModalFooter,
|
|
||||||
ModalBody,
|
|
||||||
NumberInput,
|
|
||||||
NumberInputField,
|
|
||||||
NumberInputStepper,
|
|
||||||
NumberIncrementStepper,
|
|
||||||
NumberDecrementStepper,
|
|
||||||
Flex,
|
Flex,
|
||||||
Switch,
|
|
||||||
Input,
|
|
||||||
FormControl,
|
|
||||||
Table,
|
Table,
|
||||||
Thead,
|
Thead,
|
||||||
Tbody,
|
Tbody,
|
||||||
@ -20,7 +10,7 @@ import {
|
|||||||
Th,
|
Th,
|
||||||
Td,
|
Td,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
useDisclosure
|
Stack
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||||
import {
|
import {
|
||||||
@ -31,38 +21,34 @@ import {
|
|||||||
import type { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
import type { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useFieldArray } from 'react-hook-form';
|
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import MyRadio from '@/components/common/MyRadio';
|
|
||||||
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/utils';
|
||||||
import ChatFunctionTip from './Tip';
|
import ChatFunctionTip from './Tip';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import { FlowValueTypeMap } from '@fastgpt/global/core/workflow/node/constant';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
import InputTypeConfig from '@/pages/app/detail/components/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig';
|
||||||
|
|
||||||
export const defaultVariable: VariableItemType = {
|
export const defaultVariable: VariableItemType = {
|
||||||
id: nanoid(),
|
id: nanoid(),
|
||||||
key: 'key',
|
key: '',
|
||||||
label: 'label',
|
label: '',
|
||||||
type: VariableInputEnum.input,
|
type: VariableInputEnum.input,
|
||||||
|
description: '',
|
||||||
required: true,
|
required: true,
|
||||||
maxLen: 50,
|
|
||||||
enums: [{ value: '' }],
|
|
||||||
valueType: WorkflowIOValueTypeEnum.string
|
valueType: WorkflowIOValueTypeEnum.string
|
||||||
};
|
};
|
||||||
export const addVariable = () => {
|
|
||||||
const newVariable = { ...defaultVariable, key: '', id: '' };
|
type InputItemType = VariableItemType & {
|
||||||
return newVariable;
|
list: { label: string; value: string }[];
|
||||||
};
|
};
|
||||||
const valueTypeMap = {
|
|
||||||
[VariableInputEnum.input]: WorkflowIOValueTypeEnum.string,
|
export const addVariable = () => {
|
||||||
[VariableInputEnum.select]: WorkflowIOValueTypeEnum.string,
|
const newVariable = { ...defaultVariable, key: '', id: '', list: [{ value: '', label: '' }] };
|
||||||
[VariableInputEnum.textarea]: WorkflowIOValueTypeEnum.string,
|
return newVariable;
|
||||||
[VariableInputEnum.custom]: WorkflowIOValueTypeEnum.any
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const VariableEdit = ({
|
const VariableEdit = ({
|
||||||
@ -74,46 +60,37 @@ const VariableEdit = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [refresh, setRefresh] = useState(false);
|
|
||||||
|
|
||||||
const VariableTypeList = useMemo(
|
const form = useForm<VariableItemType>();
|
||||||
|
const { setValue, reset, watch, getValues } = form;
|
||||||
|
const value = getValues();
|
||||||
|
const type = watch('type');
|
||||||
|
const valueType = watch('valueType');
|
||||||
|
const max = watch('max');
|
||||||
|
const min = watch('min');
|
||||||
|
const defaultValue = watch('defaultValue');
|
||||||
|
|
||||||
|
const inputTypeList = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Object.entries(variableMap).map(([key, value]) => ({
|
Object.values(variableMap).map((item) => ({
|
||||||
title: t(value.title as any),
|
icon: item.icon,
|
||||||
icon: value.icon,
|
label: t(item.label as any),
|
||||||
value: key
|
value: item.value,
|
||||||
|
defaultValueType: item.defaultValueType,
|
||||||
|
description: item.description ? t(item.description as any) : ''
|
||||||
})),
|
})),
|
||||||
[t]
|
[t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isOpen: isOpenEdit, onOpen: onOpenEdit, onClose: onCloseEdit } = useDisclosure();
|
const defaultValueType = useMemo(() => {
|
||||||
const {
|
const item = inputTypeList.find((item) => item.value === type);
|
||||||
setValue,
|
return item?.defaultValueType;
|
||||||
reset: resetEdit,
|
}, [inputTypeList, type]);
|
||||||
register: registerEdit,
|
|
||||||
getValues: getValuesEdit,
|
|
||||||
setValue: setValuesEdit,
|
|
||||||
control: editVariableController,
|
|
||||||
handleSubmit: handleSubmitEdit,
|
|
||||||
watch
|
|
||||||
} = useForm<{ variable: VariableItemType }>();
|
|
||||||
|
|
||||||
const variableType = watch('variable.type');
|
|
||||||
const valueType = watch('variable.valueType');
|
|
||||||
|
|
||||||
const {
|
|
||||||
fields: selectEnums,
|
|
||||||
append: appendEnums,
|
|
||||||
remove: removeEnums
|
|
||||||
} = useFieldArray({
|
|
||||||
control: editVariableController,
|
|
||||||
name: 'variable.enums'
|
|
||||||
});
|
|
||||||
|
|
||||||
const formatVariables = useMemo(() => {
|
const formatVariables = useMemo(() => {
|
||||||
const results = formatEditorVariablePickerIcon(variables);
|
const results = formatEditorVariablePickerIcon(variables);
|
||||||
return results.map((item) => {
|
return results.map<VariableItemType & { icon?: string }>((item) => {
|
||||||
const variable = variables.find((variable) => variable.key === item.key);
|
const variable = variables.find((variable) => variable.key === item.key)!;
|
||||||
return {
|
return {
|
||||||
...variable,
|
...variable,
|
||||||
icon: item.icon
|
icon: item.icon
|
||||||
@ -121,45 +98,12 @@ const VariableEdit = ({
|
|||||||
});
|
});
|
||||||
}, [variables]);
|
}, [variables]);
|
||||||
|
|
||||||
const valueTypeSelectList = useMemo(
|
const onSubmitSuccess = useCallback(
|
||||||
() =>
|
(data: InputItemType, action: 'confirm' | 'continue') => {
|
||||||
Object.values(FlowValueTypeMap)
|
data.label = data?.label?.trim();
|
||||||
.map((item) => ({
|
|
||||||
label: t(item.label as any),
|
|
||||||
value: item.value
|
|
||||||
}))
|
|
||||||
.filter(
|
|
||||||
(item) =>
|
|
||||||
![
|
|
||||||
WorkflowIOValueTypeEnum.arrayAny,
|
|
||||||
WorkflowIOValueTypeEnum.selectApp,
|
|
||||||
WorkflowIOValueTypeEnum.selectDataset,
|
|
||||||
WorkflowIOValueTypeEnum.dynamic
|
|
||||||
].includes(item.value)
|
|
||||||
),
|
|
||||||
[t]
|
|
||||||
);
|
|
||||||
const showValueTypeSelect = variableType === VariableInputEnum.custom;
|
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
|
||||||
({ variable }: { variable: VariableItemType }) => {
|
|
||||||
variable.key = variable.key.trim();
|
|
||||||
|
|
||||||
// check select
|
|
||||||
if (variable.type === VariableInputEnum.select) {
|
|
||||||
const enums = variable.enums.filter((item) => item.value);
|
|
||||||
if (enums.length === 0) {
|
|
||||||
toast({
|
|
||||||
status: 'warning',
|
|
||||||
title: t('common:core.module.variable.variable option is required')
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check repeat key
|
|
||||||
const existingVariable = variables.find(
|
const existingVariable = variables.find(
|
||||||
(item) => item.key === variable.key && item.id !== variable.id
|
(item) => item.label === data.label && item.id !== data.id
|
||||||
);
|
);
|
||||||
if (existingVariable) {
|
if (existingVariable) {
|
||||||
toast({
|
toast({
|
||||||
@ -169,32 +113,59 @@ const VariableEdit = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set valuetype based on variable.type
|
data.key = data.label;
|
||||||
variable.valueType =
|
data.enums = data.list;
|
||||||
variable.type === VariableInputEnum.custom
|
|
||||||
? variable.valueType
|
|
||||||
: valueTypeMap[variable.type];
|
|
||||||
|
|
||||||
// set default required value based on variableType
|
if (data.type === VariableInputEnum.custom) {
|
||||||
if (variable.type === VariableInputEnum.custom) {
|
data.required = false;
|
||||||
variable.required = false;
|
}
|
||||||
|
|
||||||
|
if (data.type === VariableInputEnum.numberInput) {
|
||||||
|
data.valueType = WorkflowIOValueTypeEnum.number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChangeVariable = [...variables];
|
const onChangeVariable = [...variables];
|
||||||
// update
|
if (data.id) {
|
||||||
if (variable.id) {
|
const index = variables.findIndex((item) => item.id === data.id);
|
||||||
const index = variables.findIndex((item) => item.id === variable.id);
|
onChangeVariable[index] = data;
|
||||||
onChangeVariable[index] = variable;
|
|
||||||
} else {
|
} else {
|
||||||
onChangeVariable.push({
|
onChangeVariable.push({
|
||||||
...variable,
|
...data,
|
||||||
id: nanoid()
|
id: nanoid()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
onChange(onChangeVariable);
|
|
||||||
onCloseEdit();
|
if (action === 'confirm') {
|
||||||
|
onChange(onChangeVariable);
|
||||||
|
reset({});
|
||||||
|
} else if (action === 'continue') {
|
||||||
|
onChange(onChangeVariable);
|
||||||
|
toast({
|
||||||
|
status: 'success',
|
||||||
|
title: t('common:common.Add Success')
|
||||||
|
});
|
||||||
|
reset({
|
||||||
|
...addVariable(),
|
||||||
|
defaultValue: ''
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[onChange, onCloseEdit, t, toast, variables]
|
[variables, toast, t, onChange, reset]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onSubmitError = useCallback(
|
||||||
|
(e: Object) => {
|
||||||
|
for (const item of Object.values(e)) {
|
||||||
|
if (item.message) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: item.message
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[toast]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -212,8 +183,7 @@ const VariableEdit = ({
|
|||||||
size={'sm'}
|
size={'sm'}
|
||||||
mr={'-5px'}
|
mr={'-5px'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
resetEdit({ variable: addVariable() });
|
reset(addVariable());
|
||||||
onOpenEdit();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('common:common.Add New')}
|
{t('common:common.Add New')}
|
||||||
@ -232,7 +202,7 @@ const VariableEdit = ({
|
|||||||
w={'18px !important'}
|
w={'18px !important'}
|
||||||
p={0}
|
p={0}
|
||||||
/>
|
/>
|
||||||
<Th fontSize={'mini'}>{t('common:core.module.variable.variable name')}</Th>
|
<Th fontSize={'mini'}>{t('workflow:Variable_name')}</Th>
|
||||||
<Th fontSize={'mini'}>{t('common:core.module.variable.key')}</Th>
|
<Th fontSize={'mini'}>{t('common:core.module.variable.key')}</Th>
|
||||||
<Th fontSize={'mini'}>{t('common:common.Require Input')}</Th>
|
<Th fontSize={'mini'}>{t('common:common.Require Input')}</Th>
|
||||||
<Th fontSize={'mini'} borderRadius={'none !important'}></Th>
|
<Th fontSize={'mini'} borderRadius={'none !important'}></Th>
|
||||||
@ -241,8 +211,8 @@ const VariableEdit = ({
|
|||||||
<Tbody>
|
<Tbody>
|
||||||
{formatVariables.map((item) => (
|
{formatVariables.map((item) => (
|
||||||
<Tr key={item.id}>
|
<Tr key={item.id}>
|
||||||
<Td textAlign={'center'} p={0} pl={3}>
|
<Td p={0} pl={3}>
|
||||||
<MyIcon name={item.icon as any} w={'14px'} color={'myGray.500'} />
|
<MyIcon name={item.icon as any} w={'16px'} color={'myGray.500'} />
|
||||||
</Td>
|
</Td>
|
||||||
<Td>{item.label}</Td>
|
<Td>{item.label}</Td>
|
||||||
<Td>{item.key}</Td>
|
<Td>{item.key}</Td>
|
||||||
@ -254,8 +224,11 @@ const VariableEdit = ({
|
|||||||
w={'16px'}
|
w={'16px'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
resetEdit({ variable: item });
|
const formattedItem = {
|
||||||
onOpenEdit();
|
...item,
|
||||||
|
list: item.enums || []
|
||||||
|
};
|
||||||
|
reset(formattedItem);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<MyIcon
|
<MyIcon
|
||||||
@ -274,160 +247,100 @@ const VariableEdit = ({
|
|||||||
</TableContainer>
|
</TableContainer>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Edit modal */}
|
{/* Edit modal */}
|
||||||
<MyModal
|
{!!Object.keys(value).length && (
|
||||||
iconSrc="core/app/simpleMode/variable"
|
<MyModal
|
||||||
title={t('common:core.module.Variable Setting')}
|
iconSrc="core/app/simpleMode/variable"
|
||||||
isOpen={isOpenEdit}
|
title={t('common:core.module.Variable Setting')}
|
||||||
onClose={onCloseEdit}
|
isOpen={true}
|
||||||
maxW={['90vw', '500px']}
|
onClose={() => reset({})}
|
||||||
>
|
maxW={['90vw', '928px']}
|
||||||
<ModalBody>
|
w={'100%'}
|
||||||
{variableType !== VariableInputEnum.custom && (
|
isCentered
|
||||||
<Flex alignItems={'center'}>
|
>
|
||||||
<FormLabel w={'70px'}>{t('common:common.Require Input')}</FormLabel>
|
<Flex h={'560px'}>
|
||||||
<Switch {...registerEdit('variable.required')} />
|
<Stack gap={4} p={8}>
|
||||||
</Flex>
|
<FormLabel color={'myGray.600'} fontWeight={'medium'}>
|
||||||
)}
|
{t('workflow:Variable.Variable type')}
|
||||||
<Flex mt={5} alignItems={'center'}>
|
|
||||||
<FormLabel w={'80px'}>{t('common:core.module.variable.variable name')}</FormLabel>
|
|
||||||
<Input
|
|
||||||
{...registerEdit('variable.label', {
|
|
||||||
required: t('common:core.module.variable.variable name is required')
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={5} alignItems={'center'}>
|
|
||||||
<FormLabel w={'80px'}>{t('common:core.module.variable.key')}</FormLabel>
|
|
||||||
<Input
|
|
||||||
{...registerEdit('variable.key', {
|
|
||||||
required: t('common:core.module.variable.key is required')
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex mt={5} alignItems={'center'}>
|
|
||||||
<FormLabel w={'80px'}>{t('workflow:value_type')}</FormLabel>
|
|
||||||
{showValueTypeSelect ? (
|
|
||||||
<Box flex={1}>
|
|
||||||
<MySelect<WorkflowIOValueTypeEnum>
|
|
||||||
list={valueTypeSelectList.filter(
|
|
||||||
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
|
|
||||||
)}
|
|
||||||
value={valueType}
|
|
||||||
onchange={(e) => {
|
|
||||||
setValue('variable.valueType', e);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Box fontSize={'14px'}>{valueTypeMap[variableType]}</Box>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<FormLabel mt={5} mb={2}>
|
|
||||||
{t('common:core.workflow.Variable.Variable type')}
|
|
||||||
</FormLabel>
|
|
||||||
<MyRadio
|
|
||||||
gridGap={4}
|
|
||||||
gridTemplateColumns={'repeat(2,1fr)'}
|
|
||||||
value={variableType}
|
|
||||||
list={VariableTypeList}
|
|
||||||
color={'myGray.600'}
|
|
||||||
hiddenCircle
|
|
||||||
onChange={(e) => {
|
|
||||||
setValuesEdit('variable.type', e as any);
|
|
||||||
setRefresh(!refresh);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* desc */}
|
|
||||||
{variableMap[variableType]?.desc && (
|
|
||||||
<Box mt={2} fontSize={'sm'} color={'myGray.500'} whiteSpace={'pre-wrap'}>
|
|
||||||
{t(variableMap[variableType].desc as any)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{variableType === VariableInputEnum.input && (
|
|
||||||
<>
|
|
||||||
<FormLabel mt={5} mb={2}>
|
|
||||||
{t('common:core.module.variable.text max length')}
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Box>
|
<Flex flexDirection={'column'} gap={4}></Flex>
|
||||||
<NumberInput max={500} min={1} step={1} position={'relative'}>
|
<Box display={'grid'} gridTemplateColumns={'repeat(2, 1fr)'} gap={4}>
|
||||||
<NumberInputField
|
{inputTypeList.map((item) => {
|
||||||
{...registerEdit('variable.maxLen', {
|
const isSelected = type === item.value;
|
||||||
min: 1,
|
return (
|
||||||
max: 500,
|
<Box
|
||||||
valueAsNumber: true
|
display={'flex'}
|
||||||
})}
|
key={item.label}
|
||||||
max={500}
|
border={isSelected ? '1px solid #3370FF' : '1px solid #DFE2EA'}
|
||||||
/>
|
p={3}
|
||||||
<NumberInputStepper>
|
rounded={'6px'}
|
||||||
<NumberIncrementStepper />
|
fontWeight={'medium'}
|
||||||
<NumberDecrementStepper />
|
fontSize={'14px'}
|
||||||
</NumberInputStepper>
|
alignItems={'center'}
|
||||||
</NumberInput>
|
cursor={'pointer'}
|
||||||
</Box>
|
boxShadow={isSelected ? '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)' : 'none'}
|
||||||
</>
|
_hover={{
|
||||||
)}
|
'& > svg': {
|
||||||
|
color: 'primary.600'
|
||||||
{variableType === VariableInputEnum.select && (
|
},
|
||||||
<>
|
'& > span': {
|
||||||
<Box mt={5} mb={2}>
|
color: 'myGray.900'
|
||||||
{t('common:core.module.variable.variable options')}
|
},
|
||||||
</Box>
|
border: '1px solid #3370FF',
|
||||||
<Box>
|
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
|
||||||
{selectEnums.map((item, i) => (
|
}}
|
||||||
<Flex key={item.id} mb={2} alignItems={'center'}>
|
onClick={() => {
|
||||||
<FormControl>
|
const defaultValIsNumber = !isNaN(Number(value.defaultValue));
|
||||||
<Input
|
// 如果切换到 numberInput,不是数字,则清空
|
||||||
{...registerEdit(`variable.enums.${i}.value`, {
|
if (
|
||||||
required: t(
|
item.value === VariableInputEnum.select ||
|
||||||
'common:core.module.variable.variable option is value is required'
|
(item.value === VariableInputEnum.numberInput && !defaultValIsNumber)
|
||||||
)
|
) {
|
||||||
})}
|
setValue('defaultValue', '');
|
||||||
/>
|
}
|
||||||
</FormControl>
|
setValue('type', item.value);
|
||||||
{selectEnums.length > 1 && (
|
}}
|
||||||
|
>
|
||||||
<MyIcon
|
<MyIcon
|
||||||
ml={3}
|
name={item.icon as any}
|
||||||
name={'delete'}
|
w={'20px'}
|
||||||
w={'16px'}
|
mr={1.5}
|
||||||
cursor={'pointer'}
|
color={isSelected ? 'primary.600' : 'myGray.400'}
|
||||||
p={2}
|
|
||||||
borderRadius={'md'}
|
|
||||||
_hover={{ bg: 'red.100' }}
|
|
||||||
onClick={() => removeEnums(i)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
<Box
|
||||||
</Flex>
|
as="span"
|
||||||
))}
|
color={isSelected ? 'myGray.900' : 'inherit'}
|
||||||
|
pr={4}
|
||||||
|
whiteSpace="nowrap"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Box>
|
||||||
|
{item.description && (
|
||||||
|
<QuestionTip label={item.description as string} ml={1} />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
</Stack>
|
||||||
variant={'solid'}
|
<InputTypeConfig
|
||||||
w={'100%'}
|
form={form}
|
||||||
textAlign={'left'}
|
type={'variable'}
|
||||||
leftIcon={<SmallAddIcon />}
|
isEdit={!!value.key}
|
||||||
bg={'myGray.100 !important'}
|
inputType={type}
|
||||||
onClick={() => appendEnums({ value: '' })}
|
valueType={valueType}
|
||||||
>
|
defaultValue={defaultValue}
|
||||||
{t('common:core.module.variable add option')}
|
defaultValueType={defaultValueType}
|
||||||
</Button>
|
max={max}
|
||||||
</>
|
min={min}
|
||||||
)}
|
onClose={() => reset({})}
|
||||||
</ModalBody>
|
onSubmitSuccess={onSubmitSuccess}
|
||||||
|
onSubmitError={onSubmitError}
|
||||||
<ModalFooter>
|
/>
|
||||||
<Button variant={'whiteBase'} mr={3} onClick={onCloseEdit}>
|
</Flex>
|
||||||
{t('common:common.Close')}
|
</MyModal>
|
||||||
</Button>
|
)}
|
||||||
<Button onClick={handleSubmitEdit(onSubmit)}>
|
|
||||||
{getValuesEdit('variable.id')
|
|
||||||
? t('common:common.Confirm Update')
|
|
||||||
: t('common:common.Add New')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</MyModal>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -81,16 +81,11 @@ const ChatInput = ({
|
|||||||
const canSendMessage = havInput && !hasFileUploading;
|
const canSendMessage = havInput && !hasFileUploading;
|
||||||
|
|
||||||
// Upload files
|
// Upload files
|
||||||
useRequest2(
|
useRequest2(uploadFiles, {
|
||||||
async () => {
|
manual: false,
|
||||||
uploadFiles();
|
errorToast: t('common:upload_file_error'),
|
||||||
},
|
refreshDeps: [fileList, outLinkAuthData, chatId]
|
||||||
{
|
});
|
||||||
manual: false,
|
|
||||||
errorToast: t('common:upload_file_error'),
|
|
||||||
refreshDeps: [fileList, outLinkAuthData, chatId]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/* on send */
|
/* on send */
|
||||||
const handleSend = useCallback(
|
const handleSend = useCallback(
|
||||||
|
|||||||
@ -1,7 +1,18 @@
|
|||||||
import React from 'react';
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { Controller, UseFormReturn } from 'react-hook-form';
|
import { Controller, UseFormReturn } from 'react-hook-form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { Box, Button, Card, FormControl, Input, Textarea } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Input,
|
||||||
|
NumberDecrementStepper,
|
||||||
|
NumberIncrementStepper,
|
||||||
|
NumberInput,
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputStepper,
|
||||||
|
Textarea
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import ChatAvatar from './ChatAvatar';
|
import ChatAvatar from './ChatAvatar';
|
||||||
import { MessageCardStyle } from '../constants';
|
import { MessageCardStyle } from '../constants';
|
||||||
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
import { VariableInputEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
@ -10,6 +21,113 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||||||
import { ChatBoxInputFormType } from '../type.d';
|
import { ChatBoxInputFormType } from '../type.d';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { ChatBoxContext } from '../Provider';
|
import { ChatBoxContext } from '../Provider';
|
||||||
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
|
import { useDeepCompareEffect } from 'ahooks';
|
||||||
|
import { VariableItemType } from '@fastgpt/global/core/app/type';
|
||||||
|
|
||||||
|
export const VariableInputItem = ({
|
||||||
|
item,
|
||||||
|
variablesForm
|
||||||
|
}: {
|
||||||
|
item: VariableItemType;
|
||||||
|
variablesForm: UseFormReturn<any>;
|
||||||
|
}) => {
|
||||||
|
const { register, control, setValue } = variablesForm;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box key={item.id} mb={4} pl={1}>
|
||||||
|
<Box
|
||||||
|
as={'label'}
|
||||||
|
display={'flex'}
|
||||||
|
position={'relative'}
|
||||||
|
mb={1}
|
||||||
|
alignItems={'center'}
|
||||||
|
w={'full'}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
{item.required && (
|
||||||
|
<Box
|
||||||
|
position={'absolute'}
|
||||||
|
top={'-2px'}
|
||||||
|
left={'-8px'}
|
||||||
|
color={'red.500'}
|
||||||
|
fontWeight={'bold'}
|
||||||
|
>
|
||||||
|
*
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{item.description && <QuestionTip ml={1} label={item.description} />}
|
||||||
|
</Box>
|
||||||
|
{item.type === VariableInputEnum.input && (
|
||||||
|
<Input
|
||||||
|
maxLength={item.maxLength || 4000}
|
||||||
|
bg={'myGray.50'}
|
||||||
|
{...register(item.key, {
|
||||||
|
required: item.required
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.type === VariableInputEnum.textarea && (
|
||||||
|
<Textarea
|
||||||
|
{...register(item.key, {
|
||||||
|
required: item.required
|
||||||
|
})}
|
||||||
|
rows={5}
|
||||||
|
bg={'myGray.50'}
|
||||||
|
maxLength={item.maxLength || 4000}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.type === VariableInputEnum.select && (
|
||||||
|
<Controller
|
||||||
|
key={item.key}
|
||||||
|
control={control}
|
||||||
|
name={item.key}
|
||||||
|
rules={{ required: item.required }}
|
||||||
|
render={({ field: { ref, value } }) => {
|
||||||
|
return (
|
||||||
|
<MySelect
|
||||||
|
ref={ref}
|
||||||
|
width={'100%'}
|
||||||
|
list={(item.enums || []).map((item: { value: any }) => ({
|
||||||
|
label: item.value,
|
||||||
|
value: item.value
|
||||||
|
}))}
|
||||||
|
value={value}
|
||||||
|
onchange={(e) => setValue(item.key, e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.type === VariableInputEnum.numberInput && (
|
||||||
|
<Controller
|
||||||
|
key={item.key}
|
||||||
|
control={control}
|
||||||
|
name={item.key}
|
||||||
|
rules={{ required: item.required, min: item.min, max: item.max }}
|
||||||
|
render={({ field: { ref, value, onChange } }) => (
|
||||||
|
<NumberInput
|
||||||
|
step={1}
|
||||||
|
min={item.min}
|
||||||
|
max={item.max}
|
||||||
|
bg={'white'}
|
||||||
|
rounded={'md'}
|
||||||
|
clampValueOnBlur={false}
|
||||||
|
value={value}
|
||||||
|
onChange={(valueString) => onChange(Number(valueString))}
|
||||||
|
>
|
||||||
|
<NumberInputField ref={ref} bg={'white'} />
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const VariableInput = ({
|
const VariableInput = ({
|
||||||
chatForm,
|
chatForm,
|
||||||
@ -21,13 +139,22 @@ const VariableInput = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { appAvatar, variableList, variablesForm } = useContextSelector(ChatBoxContext, (v) => v);
|
const { appAvatar, variableList, variablesForm } = useContextSelector(ChatBoxContext, (v) => v);
|
||||||
const { register, setValue, handleSubmit: handleSubmitChat, control } = variablesForm;
|
const { reset, handleSubmit: handleSubmitChat } = variablesForm;
|
||||||
|
|
||||||
|
const defaultValues = useMemo(() => {
|
||||||
|
return variableList.reduce((acc: Record<string, any>, item) => {
|
||||||
|
acc[item.key] = item.defaultValue;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}, [variableList]);
|
||||||
|
|
||||||
|
useDeepCompareEffect(() => {
|
||||||
|
reset(defaultValues);
|
||||||
|
}, [defaultValues]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={3}>
|
<Box py={3}>
|
||||||
{/* avatar */}
|
|
||||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||||
{/* message */}
|
|
||||||
<Box textAlign={'left'}>
|
<Box textAlign={'left'}>
|
||||||
<Card
|
<Card
|
||||||
order={2}
|
order={2}
|
||||||
@ -38,74 +165,21 @@ const VariableInput = ({
|
|||||||
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
boxShadow={'0 0 8px rgba(0,0,0,0.15)'}
|
||||||
>
|
>
|
||||||
{variableList.map((item) => (
|
{variableList.map((item) => (
|
||||||
<Box key={item.id} mb={4}>
|
<VariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
|
||||||
<Box as={'label'} display={'inline-block'} position={'relative'} mb={1}>
|
|
||||||
{item.label}
|
|
||||||
{item.required && (
|
|
||||||
<Box
|
|
||||||
position={'absolute'}
|
|
||||||
top={'-2px'}
|
|
||||||
right={'-10px'}
|
|
||||||
color={'red.500'}
|
|
||||||
fontWeight={'bold'}
|
|
||||||
>
|
|
||||||
*
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
{item.type === VariableInputEnum.input && (
|
|
||||||
<Input
|
|
||||||
bg={'myWhite.400'}
|
|
||||||
{...register(item.key, {
|
|
||||||
required: item.required
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.type === VariableInputEnum.textarea && (
|
|
||||||
<Textarea
|
|
||||||
bg={'myWhite.400'}
|
|
||||||
{...register(item.key, {
|
|
||||||
required: item.required
|
|
||||||
})}
|
|
||||||
rows={5}
|
|
||||||
maxLength={4000}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{item.type === VariableInputEnum.select && (
|
|
||||||
<Controller
|
|
||||||
key={item.key}
|
|
||||||
control={control}
|
|
||||||
name={item.key}
|
|
||||||
rules={{ required: item.required }}
|
|
||||||
render={({ field: { ref, value } }) => {
|
|
||||||
return (
|
|
||||||
<MySelect
|
|
||||||
ref={ref}
|
|
||||||
width={'100%'}
|
|
||||||
list={(item.enums || []).map((item) => ({
|
|
||||||
label: item.value,
|
|
||||||
value: item.value
|
|
||||||
}))}
|
|
||||||
value={value}
|
|
||||||
onchange={(e) => setValue(item.key, e)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
))}
|
))}
|
||||||
{!chatStarted && (
|
{!chatStarted && (
|
||||||
<Button
|
<Box>
|
||||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
<Button
|
||||||
size={'sm'}
|
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||||
maxW={'100px'}
|
size={'sm'}
|
||||||
onClick={handleSubmitChat(() => {
|
maxW={'100px'}
|
||||||
chatForm.setValue('chatStarted', true);
|
onClick={handleSubmitChat(() => {
|
||||||
})}
|
chatForm.setValue('chatStarted', true);
|
||||||
>
|
})}
|
||||||
{t('common:core.chat.Start Chat')}
|
>
|
||||||
</Button>
|
{t('common:core.chat.Start Chat')}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -181,7 +181,7 @@ export const useFileUpload = (props: UseFileUploadOptions) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Update file url
|
// Update file url
|
||||||
copyFile.url = `${location.origin}${previewUrl}`;
|
copyFile.url = previewUrl;
|
||||||
updateFiles(fileIndex, copyFile);
|
updateFiles(fileIndex, copyFile);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errorFileIndex.push(fileList.findIndex((item) => item.id === file.id)!);
|
errorFileIndex.push(fileList.findIndex((item) => item.id === file.id)!);
|
||||||
|
|||||||
@ -495,7 +495,8 @@ const ChatBox = (
|
|||||||
// 这里,无论是否为交互模式,最后都是 Human 的消息。
|
// 这里,无论是否为交互模式,最后都是 Human 的消息。
|
||||||
const messages = chats2GPTMessages({
|
const messages = chats2GPTMessages({
|
||||||
messages: newChatList.slice(0, -1),
|
messages: newChatList.slice(0, -1),
|
||||||
reserveId: true
|
reserveId: true,
|
||||||
|
reserveTool: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@ -57,9 +57,9 @@ export const checkIsInteractiveByHistories = (chatHistories: ChatSiteItemType[])
|
|||||||
) {
|
) {
|
||||||
const params = lastMessageValue.interactive.params;
|
const params = lastMessageValue.interactive.params;
|
||||||
// 如果用户选择了,则不认为是交互模式(可能是上一轮以交互结尾,发起的新的一轮对话)
|
// 如果用户选择了,则不认为是交互模式(可能是上一轮以交互结尾,发起的新的一轮对话)
|
||||||
if ('userSelectOptions' in params && 'userSelectedVal' in params) {
|
if ('userSelectOptions' in params) {
|
||||||
return !params.userSelectedVal;
|
return !params.userSelectedVal;
|
||||||
} else if ('inputForm' in params && 'submitted' in params) {
|
} else if ('inputForm' in params) {
|
||||||
return !params.submitted;
|
return !params.submitted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -228,7 +228,8 @@ const PluginRunContextProvider = ({
|
|||||||
value: []
|
value: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
reserveId: true
|
reserveId: true,
|
||||||
|
reserveTool: true
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -354,11 +354,11 @@ const RenderList = React.memo(function RenderList({
|
|||||||
<HStack mb={4} spacing={1} fontSize={'sm'}>
|
<HStack mb={4} spacing={1} fontSize={'sm'}>
|
||||||
<MyIcon name={'common/info'} w={'1.25rem'} />
|
<MyIcon name={'common/info'} w={'1.25rem'} />
|
||||||
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
|
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
|
||||||
{configTool.inputExplanationUrl && (
|
{configTool.courseUrl && (
|
||||||
<Box
|
<Box
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
color={'primary.500'}
|
color={'primary.500'}
|
||||||
onClick={() => window.open(configTool.inputExplanationUrl, '_blank')}
|
onClick={() => window.open(configTool.courseUrl, '_blank')}
|
||||||
>
|
>
|
||||||
{t('app:workflow.Input guide')}
|
{t('app:workflow.Input guide')}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,29 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, StackProps, HStack } from '@chakra-ui/react';
|
import { Box, StackProps, HStack } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
|
||||||
const IOTitle = ({
|
|
||||||
text,
|
|
||||||
inputExplanationUrl,
|
|
||||||
...props
|
|
||||||
}: { text?: 'Input' | 'Output' | string; inputExplanationUrl?: string } & StackProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
|
const IOTitle = ({ text, ...props }: { text?: 'Input' | 'Output' | string } & StackProps) => {
|
||||||
return (
|
return (
|
||||||
<HStack fontSize={'md'} alignItems={'center'} fontWeight={'medium'} mb={3} {...props}>
|
<HStack fontSize={'md'} alignItems={'center'} fontWeight={'medium'} mb={3} {...props}>
|
||||||
<Box w={'3px'} h={'14px'} borderRadius={'13px'} bg={'primary.600'} />
|
<Box w={'3px'} h={'14px'} borderRadius={'13px'} bg={'primary.600'} />
|
||||||
<Box color={'myGray.900'}>{text}</Box>
|
<Box color={'myGray.900'}>{text}</Box>
|
||||||
<Box flex={1} />
|
|
||||||
|
|
||||||
{inputExplanationUrl && (
|
|
||||||
<Box
|
|
||||||
cursor={'pointer'}
|
|
||||||
color={'primary.500'}
|
|
||||||
onClick={() => window.open(inputExplanationUrl, '_blank')}
|
|
||||||
>
|
|
||||||
{t('app:workflow.Input guide')}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { storeNodes2RuntimeNodes } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { storeNodes2RuntimeNodes } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState, useMemo, useEffect } from 'react';
|
||||||
import { checkWorkflowNodeAndConnection } from '@/web/core/workflow/utils';
|
import { checkWorkflowNodeAndConnection } from '@/web/core/workflow/utils';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
@ -21,19 +21,30 @@ import {
|
|||||||
NumberInputStepper,
|
NumberInputStepper,
|
||||||
Switch
|
Switch
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { FieldErrors, useForm } from 'react-hook-form';
|
||||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import {
|
||||||
|
VariableInputEnum,
|
||||||
|
WorkflowIOValueTypeEnum
|
||||||
|
} from '@fastgpt/global/core/workflow/constants';
|
||||||
import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils';
|
import { checkInputIsReference } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext, getWorkflowStore } from '../../context';
|
import { WorkflowContext, getWorkflowStore } from '../../context';
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
import { AppContext } from '../../../context';
|
||||||
|
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
|
||||||
|
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||||
|
|
||||||
const MyRightDrawer = dynamic(
|
const MyRightDrawer = dynamic(
|
||||||
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
||||||
);
|
);
|
||||||
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
||||||
|
|
||||||
|
enum TabEnum {
|
||||||
|
global = 'global',
|
||||||
|
node = 'node'
|
||||||
|
}
|
||||||
|
|
||||||
export const useDebug = () => {
|
export const useDebug = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -43,6 +54,23 @@ export const useDebug = () => {
|
|||||||
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||||
const onStartNodeDebug = useContextSelector(WorkflowContext, (v) => v.onStartNodeDebug);
|
const onStartNodeDebug = useContextSelector(WorkflowContext, (v) => v.onStartNodeDebug);
|
||||||
|
|
||||||
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
|
|
||||||
|
const filteredVar = useMemo(() => {
|
||||||
|
const variables = appDetail.chatConfig.variables;
|
||||||
|
return variables?.filter((item) => item.type !== VariableInputEnum.custom) || [];
|
||||||
|
}, [appDetail.chatConfig.variables]);
|
||||||
|
|
||||||
|
const [defaultGlobalVariables, setDefaultGlobalVariables] = useState<Record<string, any>>(
|
||||||
|
filteredVar.reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
acc[item.key] = item.defaultValue;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, any>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const [runtimeNodeId, setRuntimeNodeId] = useState<string>();
|
const [runtimeNodeId, setRuntimeNodeId] = useState<string>();
|
||||||
const [runtimeNodes, setRuntimeNodes] = useState<RuntimeNodeItemType[]>();
|
const [runtimeNodes, setRuntimeNodes] = useState<RuntimeNodeItemType[]>();
|
||||||
const [runtimeEdges, setRuntimeEdges] = useState<RuntimeEdgeItemType[]>();
|
const [runtimeEdges, setRuntimeEdges] = useState<RuntimeEdgeItemType[]>();
|
||||||
@ -108,6 +136,8 @@ export const useDebug = () => {
|
|||||||
const DebugInputModal = useCallback(() => {
|
const DebugInputModal = useCallback(() => {
|
||||||
if (!runtimeNodes || !runtimeEdges) return <></>;
|
if (!runtimeNodes || !runtimeEdges) return <></>;
|
||||||
|
|
||||||
|
const [currentTab, setCurrentTab] = useState<TabEnum>(TabEnum.node);
|
||||||
|
|
||||||
const runtimeNode = runtimeNodes.find((node) => node.nodeId === runtimeNodeId);
|
const runtimeNode = runtimeNodes.find((node) => node.nodeId === runtimeNodeId);
|
||||||
|
|
||||||
if (!runtimeNode) return <></>;
|
if (!runtimeNode) return <></>;
|
||||||
@ -117,20 +147,24 @@ export const useDebug = () => {
|
|||||||
if (input.required && !input.value) return true;
|
if (input.required && !input.value) return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { register, getValues, setValue, handleSubmit } = useForm<Record<string, any>>({
|
const variablesForm = useForm<Record<string, any>>({
|
||||||
defaultValues: renderInputs.reduce((acc: Record<string, any>, input) => {
|
defaultValues: {
|
||||||
const isReference = checkInputIsReference(input);
|
nodeVariables: renderInputs.reduce((acc: Record<string, any>, input) => {
|
||||||
if (isReference) {
|
const isReference = checkInputIsReference(input);
|
||||||
acc[input.key] = undefined;
|
if (isReference) {
|
||||||
} else if (typeof input.value === 'object') {
|
acc[input.key] = undefined;
|
||||||
acc[input.key] = JSON.stringify(input.value, null, 2);
|
} else if (typeof input.value === 'object') {
|
||||||
} else {
|
acc[input.key] = JSON.stringify(input.value, null, 2);
|
||||||
acc[input.key] = input.value;
|
} else {
|
||||||
}
|
acc[input.key] = input.value;
|
||||||
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {}),
|
||||||
|
globalVariables: defaultGlobalVariables
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
const { register, getValues, setValue, handleSubmit } = variablesForm;
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setRuntimeNodeId(undefined);
|
setRuntimeNodeId(undefined);
|
||||||
@ -152,12 +186,13 @@ export const useDebug = () => {
|
|||||||
input.valueType === WorkflowIOValueTypeEnum.string ||
|
input.valueType === WorkflowIOValueTypeEnum.string ||
|
||||||
input.valueType === WorkflowIOValueTypeEnum.number ||
|
input.valueType === WorkflowIOValueTypeEnum.number ||
|
||||||
input.valueType === WorkflowIOValueTypeEnum.boolean
|
input.valueType === WorkflowIOValueTypeEnum.boolean
|
||||||
)
|
) {
|
||||||
return data[input.key];
|
return data.nodeVariables[input.key];
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.parse(data[input.key]);
|
return JSON.parse(data.nodeVariables[input.key]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return data[input.key];
|
return data.nodeVariables[input.key];
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -169,11 +204,33 @@ export const useDebug = () => {
|
|||||||
}
|
}
|
||||||
: node
|
: node
|
||||||
),
|
),
|
||||||
runtimeEdges: runtimeEdges
|
runtimeEdges: runtimeEdges,
|
||||||
|
variables: data.globalVariables
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Filter global variables and set them as default global variable values
|
||||||
|
setDefaultGlobalVariables(data.globalVariables);
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCheckRunError = useCallback((e: FieldErrors<Record<string, any>>) => {
|
||||||
|
const hasRequiredNodeVar =
|
||||||
|
e.nodeVariables && Object.values(e.nodeVariables).some((item) => item.type === 'required');
|
||||||
|
|
||||||
|
if (hasRequiredNodeVar) {
|
||||||
|
return setCurrentTab(TabEnum.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasRequiredGlobalVar =
|
||||||
|
e.globalVariables &&
|
||||||
|
Object.values(e.globalVariables).some((item) => item.type === 'required');
|
||||||
|
|
||||||
|
if (hasRequiredGlobalVar) {
|
||||||
|
setCurrentTab(TabEnum.global);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyRightDrawer
|
<MyRightDrawer
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
@ -183,89 +240,124 @@ export const useDebug = () => {
|
|||||||
px={0}
|
px={0}
|
||||||
>
|
>
|
||||||
<Box flex={'1 0 0'} overflow={'auto'} px={6}>
|
<Box flex={'1 0 0'} overflow={'auto'} px={6}>
|
||||||
{renderInputs.map((input) => {
|
{filteredVar.length > 0 && (
|
||||||
const required = input.required || false;
|
<LightRowTabs<TabEnum>
|
||||||
|
gap={3}
|
||||||
|
ml={-2}
|
||||||
|
mb={5}
|
||||||
|
inlineStyles={{}}
|
||||||
|
list={[
|
||||||
|
{ label: t('workflow:Node_variables'), value: TabEnum.node },
|
||||||
|
{ label: t('common:core.module.Variable'), value: TabEnum.global }
|
||||||
|
]}
|
||||||
|
value={currentTab}
|
||||||
|
onChange={setCurrentTab}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Box display={currentTab === TabEnum.global ? 'block' : 'none'}>
|
||||||
|
{filteredVar.map((item) => (
|
||||||
|
<VariableInputItem
|
||||||
|
key={item.id}
|
||||||
|
item={{ ...item, key: `globalVariables.${item.key}` }}
|
||||||
|
variablesForm={variablesForm}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
<Box display={currentTab === TabEnum.node ? 'block' : 'none'}>
|
||||||
|
{renderInputs.map((input) => {
|
||||||
|
const required = input.required || false;
|
||||||
|
|
||||||
|
const RenderInput = (() => {
|
||||||
|
if (input.valueType === WorkflowIOValueTypeEnum.string) {
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
{...register(`nodeVariables.${input.key}`, {
|
||||||
|
required
|
||||||
|
})}
|
||||||
|
placeholder={t(input.placeholder || ('' as any))}
|
||||||
|
bg={'myGray.50'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.valueType === WorkflowIOValueTypeEnum.number) {
|
||||||
|
return (
|
||||||
|
<NumberInput step={input.step} min={input.min} max={input.max} bg={'myGray.50'}>
|
||||||
|
<NumberInputField
|
||||||
|
{...register(`nodeVariables.${input.key}`, {
|
||||||
|
required: input.required,
|
||||||
|
min: input.min,
|
||||||
|
max: input.max,
|
||||||
|
valueAsNumber: true
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Switch {...register(`nodeVariables.${input.key}`)} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = getValues(input.key) || '';
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
value = JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
const RenderInput = (() => {
|
|
||||||
if (input.valueType === WorkflowIOValueTypeEnum.string) {
|
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<JsonEditor
|
||||||
{...register(input.key, {
|
|
||||||
required
|
|
||||||
})}
|
|
||||||
placeholder={t(input.placeholder || ('' as any))}
|
|
||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
|
placeholder={t(input.placeholder || ('' as any))}
|
||||||
|
resize
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValue(`nodeVariables.${input.key}`, e);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
})();
|
||||||
if (input.valueType === WorkflowIOValueTypeEnum.number) {
|
|
||||||
return (
|
|
||||||
<NumberInput step={input.step} min={input.min} max={input.max} bg={'myGray.50'}>
|
|
||||||
<NumberInputField
|
|
||||||
{...register(input.key, {
|
|
||||||
required: input.required,
|
|
||||||
min: input.min,
|
|
||||||
max: input.max,
|
|
||||||
valueAsNumber: true
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<NumberInputStepper>
|
|
||||||
<NumberIncrementStepper />
|
|
||||||
<NumberDecrementStepper />
|
|
||||||
</NumberInputStepper>
|
|
||||||
</NumberInput>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Switch {...register(input.key)} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = getValues(input.key) || '';
|
return !!RenderInput ? (
|
||||||
if (typeof value !== 'string') {
|
<Box key={input.key} _notLast={{ mb: 4 }} px={1}>
|
||||||
value = JSON.stringify(value, null, 2);
|
<Flex alignItems={'center'} mb={1}>
|
||||||
}
|
<Box position={'relative'}>
|
||||||
|
{required && (
|
||||||
return (
|
<Box position={'absolute'} right={-2} top={'-1px'} color={'red.600'}>
|
||||||
<JsonEditor
|
*
|
||||||
bg={'myGray.50'}
|
</Box>
|
||||||
placeholder={t(input.placeholder || ('' as any))}
|
)}
|
||||||
resize
|
{t(input.debugLabel || (input.label as any))}
|
||||||
value={value}
|
</Box>
|
||||||
onChange={(e) => {
|
{input.description && <QuestionTip ml={2} label={input.description} />}
|
||||||
setValue(input.key, e);
|
</Flex>
|
||||||
}}
|
{RenderInput}
|
||||||
/>
|
</Box>
|
||||||
);
|
) : null;
|
||||||
})();
|
})}
|
||||||
|
</Box>
|
||||||
return !!RenderInput ? (
|
|
||||||
<Box key={input.key} _notLast={{ mb: 4 }} px={1}>
|
|
||||||
<Flex alignItems={'center'} mb={1}>
|
|
||||||
<Box position={'relative'}>
|
|
||||||
{required && (
|
|
||||||
<Box position={'absolute'} right={-2} top={'-1px'} color={'red.600'}>
|
|
||||||
*
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{t(input.debugLabel || (input.label as any))}
|
|
||||||
</Box>
|
|
||||||
{input.description && <QuestionTip ml={2} label={input.description} />}
|
|
||||||
</Flex>
|
|
||||||
{RenderInput}
|
|
||||||
</Box>
|
|
||||||
) : null;
|
|
||||||
})}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Flex py={2} justifyContent={'flex-end'} px={6}>
|
<Flex py={2} justifyContent={'flex-end'} px={6}>
|
||||||
<Button onClick={handleSubmit(onClickRun)}>{t('common:common.Run')}</Button>
|
<Button onClick={handleSubmit(onClickRun, onCheckRunError)}>
|
||||||
|
{t('common:common.Run')}
|
||||||
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</MyRightDrawer>
|
</MyRightDrawer>
|
||||||
);
|
);
|
||||||
}, [onStartNodeDebug, runtimeEdges, runtimeNodeId, runtimeNodes, t]);
|
}, [
|
||||||
|
defaultGlobalVariables,
|
||||||
|
filteredVar,
|
||||||
|
onStartNodeDebug,
|
||||||
|
runtimeEdges,
|
||||||
|
runtimeNodeId,
|
||||||
|
runtimeNodes,
|
||||||
|
t
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
DebugInputModal,
|
DebugInputModal,
|
||||||
|
|||||||
@ -412,41 +412,37 @@ export const useWorkflow = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* node */
|
/* node */
|
||||||
|
// Remove change node and its child nodes and edges
|
||||||
const handleRemoveNode = useMemoizedFn((change: NodeRemoveChange, nodeId: string) => {
|
const handleRemoveNode = useMemoizedFn((change: NodeRemoveChange, nodeId: string) => {
|
||||||
// If the node has child nodes, remove the child nodes
|
// If the node has child nodes, remove the child nodes
|
||||||
|
const deletedNodeIdList = [nodeId];
|
||||||
|
const deletedEdgeIdList = edges
|
||||||
|
.filter((edge) => edge.source === nodeId || edge.target === nodeId)
|
||||||
|
.map((edge) => edge.id);
|
||||||
|
|
||||||
const childNodes = nodes.filter((n) => n.data.parentNodeId === nodeId);
|
const childNodes = nodes.filter((n) => n.data.parentNodeId === nodeId);
|
||||||
if (childNodes.length > 0) {
|
if (childNodes.length > 0) {
|
||||||
const childNodeIds = childNodes.map((node) => node.id);
|
const childNodeIds = childNodes.map((node) => node.id);
|
||||||
|
deletedNodeIdList.push(...childNodeIds);
|
||||||
|
|
||||||
const childEdges = edges.filter(
|
const childEdges = edges.filter(
|
||||||
(edge) => childNodeIds.includes(edge.source) || childNodeIds.includes(edge.target)
|
(edge) => childNodeIds.includes(edge.source) || childNodeIds.includes(edge.target)
|
||||||
);
|
);
|
||||||
|
deletedEdgeIdList.push(...childEdges.map((edge) => edge.id));
|
||||||
onNodesChange(
|
|
||||||
childNodes.map<NodeRemoveChange>((node) => ({
|
|
||||||
type: 'remove',
|
|
||||||
id: node.id
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
onEdgesChange(
|
|
||||||
childEdges.map<EdgeRemoveChange>((edge) => ({
|
|
||||||
type: 'remove',
|
|
||||||
id: edge.id
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange([change]);
|
onNodesChange(
|
||||||
|
deletedNodeIdList.map<NodeRemoveChange>((id) => ({
|
||||||
// Remove the edges connected to the node
|
|
||||||
const nodeEdges = edges.filter((edge) => edge.source === nodeId || edge.target === nodeId);
|
|
||||||
onEdgesChange(
|
|
||||||
nodeEdges.map<EdgeRemoveChange>((edge) => ({
|
|
||||||
type: 'remove',
|
type: 'remove',
|
||||||
id: edge.id
|
id
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
onEdgesChange(
|
||||||
|
deletedEdgeIdList.map<EdgeRemoveChange>((id) => ({
|
||||||
|
type: 'remove',
|
||||||
|
id
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
const handleSelectNode = useMemoizedFn((change: NodeSelectionChange) => {
|
const handleSelectNode = useMemoizedFn((change: NodeSelectionChange) => {
|
||||||
// If the node is not selected and the Ctrl key is pressed, select the node
|
// If the node is not selected and the Ctrl key is pressed, select the node
|
||||||
|
|||||||
@ -110,7 +110,7 @@ const NodeLoopStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th borderBottomLeftRadius={'none !important'}>
|
<Th borderBottomLeftRadius={'none !important'}>
|
||||||
{t('common:core.module.variable.variable name')}
|
{t('workflow:Variable_name')}
|
||||||
</Th>
|
</Th>
|
||||||
<Th>{t('common:core.workflow.Value type')}</Th>
|
<Th>{t('common:core.workflow.Value type')}</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
|||||||
@ -80,12 +80,13 @@ const NodeComment = ({ data }: NodeProps<FlowNodeItemType>) => {
|
|||||||
menuForbid={{
|
menuForbid={{
|
||||||
debug: true
|
debug: true
|
||||||
}}
|
}}
|
||||||
border={'none'}
|
customStyle={{
|
||||||
rounded={'none'}
|
border: 'none',
|
||||||
bg={'#D8E9FF'}
|
rounded: 'none',
|
||||||
boxShadow={
|
bg: '#D8E9FF',
|
||||||
'0px 4px 10px 0px rgba(19, 51, 107, 0.10), 0px 0px 1px 0px rgba(19, 51, 107, 0.10)'
|
boxShadow:
|
||||||
}
|
'0px 4px 10px 0px rgba(19, 51, 107, 0.10), 0px 0px 1px 0px rgba(19, 51, 107, 0.10)'
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box w={'full'} h={'full'} position={'relative'}>
|
<Box w={'full'} h={'full'} position={'relative'}>
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@ -19,7 +19,8 @@ export const defaultFormInput: UserInputFormItemType = {
|
|||||||
maxLength: undefined,
|
maxLength: undefined,
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
valueType: WorkflowIOValueTypeEnum.string,
|
valueType: WorkflowIOValueTypeEnum.string,
|
||||||
required: false
|
required: false,
|
||||||
|
list: [{ label: '', value: '' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Modal for add or edit user input form items
|
// Modal for add or edit user input form items
|
||||||
@ -39,10 +40,7 @@ const InputFormEditModal = ({
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
defaultValues: {
|
defaultValues: defaultValue
|
||||||
...defaultValue,
|
|
||||||
list: defaultValue.list?.length ? defaultValue.list : [{ label: '', value: '' }]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const { setValue, watch, reset } = form;
|
const { setValue, watch, reset } = form;
|
||||||
|
|
||||||
@ -51,6 +49,7 @@ const InputFormEditModal = ({
|
|||||||
const maxLength = watch('maxLength');
|
const maxLength = watch('maxLength');
|
||||||
const max = watch('max');
|
const max = watch('max');
|
||||||
const min = watch('min');
|
const min = watch('min');
|
||||||
|
const defaultInputValue = watch('defaultValue');
|
||||||
|
|
||||||
const inputTypeList = [
|
const inputTypeList = [
|
||||||
{
|
{
|
||||||
@ -111,7 +110,7 @@ const InputFormEditModal = ({
|
|||||||
reset(defaultFormInput);
|
reset(defaultFormInput);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[toast, t, reset, onSubmit, onClose, defaultFormInput, defaultValueType]
|
[defaultValue.key, keys, defaultValueType, isEdit, toast, t, onSubmit, onClose, reset]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSubmitError = useCallback(
|
const onSubmitError = useCallback(
|
||||||
@ -197,6 +196,7 @@ const InputFormEditModal = ({
|
|||||||
maxLength={maxLength}
|
maxLength={maxLength}
|
||||||
max={max}
|
max={max}
|
||||||
min={min}
|
min={min}
|
||||||
|
defaultValue={defaultInputValue}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
onSubmitSuccess={onSubmitSuccess}
|
onSubmitSuccess={onSubmitSuccess}
|
||||||
onSubmitError={onSubmitError}
|
onSubmitError={onSubmitError}
|
||||||
|
|||||||
@ -81,6 +81,9 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||||
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
|
|
||||||
const { isOpen: isOpenCurl, onOpen: onOpenCurl, onClose: onCloseCurl } = useDisclosure();
|
const { isOpen: isOpenCurl, onOpen: onOpenCurl, onClose: onCloseCurl } = useDisclosure();
|
||||||
|
|
||||||
@ -91,19 +94,18 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
(item) => item.key === NodeInputKeyEnum.httpReqUrl
|
(item) => item.key === NodeInputKeyEnum.httpReqUrl
|
||||||
) as FlowNodeInputItemType;
|
) as FlowNodeInputItemType;
|
||||||
|
|
||||||
const onChangeUrl = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onChangeUrl = (value: string) => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
type: 'updateInput',
|
type: 'updateInput',
|
||||||
key: NodeInputKeyEnum.httpReqUrl,
|
key: NodeInputKeyEnum.httpReqUrl,
|
||||||
value: {
|
value: {
|
||||||
...requestUrl,
|
...requestUrl,
|
||||||
value: e.target.value
|
value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onBlurUrl = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onBlurUrl = (val: string) => {
|
||||||
const val = e.target.value;
|
|
||||||
// 拆分params和url
|
// 拆分params和url
|
||||||
const url = val.split('?')[0];
|
const url = val.split('?')[0];
|
||||||
const params = val.split('?')[1];
|
const params = val.split('?')[1];
|
||||||
@ -154,6 +156,16 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const variables = useCreation(() => {
|
||||||
|
return getEditorVariables({
|
||||||
|
nodeId,
|
||||||
|
nodeList,
|
||||||
|
edges,
|
||||||
|
appDetail,
|
||||||
|
t
|
||||||
|
});
|
||||||
|
}, [nodeId, nodeList, edges, appDetail, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box mb={2} display={'flex'} justifyContent={'space-between'}>
|
<Box mb={2} display={'flex'} justifyContent={'space-between'}>
|
||||||
@ -166,7 +178,7 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
</Box>
|
</Box>
|
||||||
<Flex alignItems={'center'} className="nodrag">
|
<Flex alignItems={'center'} className="nodrag">
|
||||||
<MySelect
|
<MySelect
|
||||||
h={'34px'}
|
h={'40px'}
|
||||||
w={'88px'}
|
w={'88px'}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
@ -205,17 +217,29 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Box
|
||||||
flex={'1 0 0'}
|
w={'full'}
|
||||||
ml={2}
|
border={'1px solid'}
|
||||||
h={'34px'}
|
borderColor={'myGray.200'}
|
||||||
|
rounded={'md'}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
value={requestUrl?.value || ''}
|
ml={2}
|
||||||
placeholder={t('common:core.module.input.label.Http Request Url')}
|
>
|
||||||
fontSize={'xs'}
|
<PromptEditor
|
||||||
onChange={onChangeUrl}
|
placeholder={
|
||||||
onBlur={onBlurUrl}
|
t('common:core.module.input.label.Http Request Url') +
|
||||||
/>
|
', ' +
|
||||||
|
t('common:textarea_variable_picker_tip')
|
||||||
|
}
|
||||||
|
value={requestUrl?.value || ''}
|
||||||
|
variableLabels={variables}
|
||||||
|
variables={variables}
|
||||||
|
onBlur={onBlurUrl}
|
||||||
|
onChange={onChangeUrl}
|
||||||
|
minH={40}
|
||||||
|
showOpenModal={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{isOpenCurl && <CurlImportModal nodeId={nodeId} inputs={inputs} onClose={onCloseCurl} />}
|
{isOpenCurl && <CurlImportModal nodeId={nodeId} inputs={inputs} onClose={onCloseCurl} />}
|
||||||
|
|||||||
@ -22,7 +22,8 @@ export const defaultInput: FlowNodeInputItemType = {
|
|||||||
key: '',
|
key: '',
|
||||||
label: '',
|
label: '',
|
||||||
description: '',
|
description: '',
|
||||||
defaultValue: ''
|
defaultValue: '',
|
||||||
|
list: [{ label: '', value: '' }]
|
||||||
};
|
};
|
||||||
|
|
||||||
const FieldEditModal = ({
|
const FieldEditModal = ({
|
||||||
@ -133,10 +134,7 @@ const FieldEditModal = ({
|
|||||||
|
|
||||||
const isEdit = !!defaultValue.key;
|
const isEdit = !!defaultValue.key;
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
defaultValues: {
|
defaultValues: defaultValue
|
||||||
...defaultValue,
|
|
||||||
list: defaultValue.list?.length ? defaultValue.list : [{ label: '', value: '' }]
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const { getValues, setValue, watch, reset } = form;
|
const { getValues, setValue, watch, reset } = form;
|
||||||
|
|
||||||
@ -149,7 +147,7 @@ const FieldEditModal = ({
|
|||||||
const max = watch('max');
|
const max = watch('max');
|
||||||
const min = watch('min');
|
const min = watch('min');
|
||||||
const selectValueTypeList = watch('customInputConfig.selectValueTypeList');
|
const selectValueTypeList = watch('customInputConfig.selectValueTypeList');
|
||||||
const defaultJsonValue = watch('defaultValue');
|
const defaultInputValue = watch('defaultValue');
|
||||||
|
|
||||||
const defaultValueType =
|
const defaultValueType =
|
||||||
inputTypeList.flat().find((item) => item.value === inputType)?.defaultValueType ||
|
inputTypeList.flat().find((item) => item.value === inputType)?.defaultValueType ||
|
||||||
@ -157,9 +155,9 @@ const FieldEditModal = ({
|
|||||||
|
|
||||||
const onSubmitSuccess = useCallback(
|
const onSubmitSuccess = useCallback(
|
||||||
(data: FlowNodeInputItemType, action: 'confirm' | 'continue') => {
|
(data: FlowNodeInputItemType, action: 'confirm' | 'continue') => {
|
||||||
data.key = data?.key?.trim();
|
data.label = data?.label?.trim();
|
||||||
|
|
||||||
if (!data.key) {
|
if (!data.label) {
|
||||||
return toast({
|
return toast({
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
title: t('common:core.module.edit.Field Name Cannot Be Empty')
|
title: t('common:core.module.edit.Field Name Cannot Be Empty')
|
||||||
@ -199,7 +197,7 @@ const FieldEditModal = ({
|
|||||||
data.toolDescription = undefined;
|
data.toolDescription = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.label = data.key;
|
data.key = data.label;
|
||||||
|
|
||||||
if (action === 'confirm') {
|
if (action === 'confirm') {
|
||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
@ -327,7 +325,7 @@ const FieldEditModal = ({
|
|||||||
max={max}
|
max={max}
|
||||||
min={min}
|
min={min}
|
||||||
selectValueTypeList={selectValueTypeList}
|
selectValueTypeList={selectValueTypeList}
|
||||||
defaultJsonValue={defaultJsonValue}
|
defaultValue={defaultInputValue}
|
||||||
isToolInput={isToolInput}
|
isToolInput={isToolInput}
|
||||||
setIsToolInput={setIsToolInput}
|
setIsToolInput={setIsToolInput}
|
||||||
valueType={valueType}
|
valueType={valueType}
|
||||||
|
|||||||
@ -6,11 +6,19 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
HStack,
|
HStack,
|
||||||
Input,
|
Input,
|
||||||
|
NumberDecrementStepper,
|
||||||
|
NumberIncrementStepper,
|
||||||
|
NumberInput,
|
||||||
|
NumberInputField,
|
||||||
|
NumberInputStepper,
|
||||||
Stack,
|
Stack,
|
||||||
Switch,
|
Switch,
|
||||||
Textarea
|
Textarea
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import {
|
||||||
|
VariableInputEnum,
|
||||||
|
WorkflowIOValueTypeEnum
|
||||||
|
} from '@fastgpt/global/core/workflow/constants';
|
||||||
import {
|
import {
|
||||||
FlowNodeInputTypeEnum,
|
FlowNodeInputTypeEnum,
|
||||||
FlowValueTypeMap
|
FlowValueTypeMap
|
||||||
@ -25,6 +33,9 @@ import React, { useMemo } from 'react';
|
|||||||
import { useFieldArray, UseFormReturn } from 'react-hook-form';
|
import { useFieldArray, UseFormReturn } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
import DndDrag, { Draggable } from '@fastgpt/web/components/common/DndDrag';
|
||||||
|
|
||||||
|
type ListValueType = { id: string; value: string; label: string }[];
|
||||||
|
|
||||||
const InputTypeConfig = ({
|
const InputTypeConfig = ({
|
||||||
form,
|
form,
|
||||||
@ -36,7 +47,7 @@ const InputTypeConfig = ({
|
|||||||
max,
|
max,
|
||||||
min,
|
min,
|
||||||
selectValueTypeList,
|
selectValueTypeList,
|
||||||
defaultJsonValue,
|
defaultValue,
|
||||||
isToolInput,
|
isToolInput,
|
||||||
setIsToolInput,
|
setIsToolInput,
|
||||||
valueType,
|
valueType,
|
||||||
@ -48,15 +59,15 @@ const InputTypeConfig = ({
|
|||||||
form: UseFormReturn<any>;
|
form: UseFormReturn<any>;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
type: 'plugin' | 'formInput';
|
type: 'plugin' | 'formInput' | 'variable';
|
||||||
inputType: FlowNodeInputTypeEnum;
|
inputType: FlowNodeInputTypeEnum | VariableInputEnum;
|
||||||
|
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
|
|
||||||
selectValueTypeList?: WorkflowIOValueTypeEnum[];
|
selectValueTypeList?: WorkflowIOValueTypeEnum[];
|
||||||
defaultJsonValue?: string;
|
defaultValue?: string;
|
||||||
|
|
||||||
// Plugin-specific fields
|
// Plugin-specific fields
|
||||||
isToolInput?: boolean;
|
isToolInput?: boolean;
|
||||||
@ -69,8 +80,23 @@ const InputTypeConfig = ({
|
|||||||
onSubmitError: (e: Object) => void;
|
onSubmitError: (e: Object) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const defaultListValue = { label: t('common:None'), value: '' };
|
||||||
|
|
||||||
const { register, setValue, handleSubmit, control } = form;
|
const { register, setValue, handleSubmit, control, watch } = form;
|
||||||
|
const listValue: ListValueType = watch('list');
|
||||||
|
|
||||||
|
const typeLabels = {
|
||||||
|
name: {
|
||||||
|
formInput: t('common:core.module.input_name'),
|
||||||
|
plugin: t('common:core.module.Field Name'),
|
||||||
|
variable: t('workflow:Variable_name')
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
formInput: t('common:core.module.input_description'),
|
||||||
|
plugin: t('workflow:field_description'),
|
||||||
|
variable: t('workflow:variable_description')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fields: selectEnums,
|
fields: selectEnums,
|
||||||
@ -81,6 +107,11 @@ const InputTypeConfig = ({
|
|||||||
name: 'list'
|
name: 'list'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mergedSelectEnums = selectEnums.map((field, index) => ({
|
||||||
|
...field,
|
||||||
|
...listValue[index]
|
||||||
|
}));
|
||||||
|
|
||||||
const valueTypeSelectList = Object.values(FlowValueTypeMap).map((item) => ({
|
const valueTypeSelectList = Object.values(FlowValueTypeMap).map((item) => ({
|
||||||
label: t(item.label as any),
|
label: t(item.label as any),
|
||||||
value: item.value
|
value: item.value
|
||||||
@ -88,21 +119,26 @@ const InputTypeConfig = ({
|
|||||||
|
|
||||||
const showValueTypeSelect =
|
const showValueTypeSelect =
|
||||||
inputType === FlowNodeInputTypeEnum.reference ||
|
inputType === FlowNodeInputTypeEnum.reference ||
|
||||||
inputType === FlowNodeInputTypeEnum.customVariable;
|
inputType === FlowNodeInputTypeEnum.customVariable ||
|
||||||
|
inputType === VariableInputEnum.custom;
|
||||||
|
|
||||||
const showRequired = useMemo(() => {
|
const showRequired = useMemo(() => {
|
||||||
const list = [FlowNodeInputTypeEnum.addInputParam, FlowNodeInputTypeEnum.customVariable];
|
const list = [
|
||||||
|
FlowNodeInputTypeEnum.addInputParam,
|
||||||
|
FlowNodeInputTypeEnum.customVariable,
|
||||||
|
VariableInputEnum.custom
|
||||||
|
];
|
||||||
return !list.includes(inputType);
|
return !list.includes(inputType);
|
||||||
}, [inputType]);
|
}, [inputType]);
|
||||||
|
|
||||||
const showMaxLenInput = useMemo(() => {
|
const showMaxLenInput = useMemo(() => {
|
||||||
const list = [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.textarea];
|
const list = [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.textarea];
|
||||||
return list.includes(inputType);
|
return list.includes(inputType as FlowNodeInputTypeEnum);
|
||||||
}, [inputType]);
|
}, [inputType]);
|
||||||
|
|
||||||
const showMinMaxInput = useMemo(() => {
|
const showMinMaxInput = useMemo(() => {
|
||||||
const list = [FlowNodeInputTypeEnum.numberInput];
|
const list = [FlowNodeInputTypeEnum.numberInput];
|
||||||
return list.includes(inputType);
|
return list.includes(inputType as FlowNodeInputTypeEnum);
|
||||||
}, [inputType]);
|
}, [inputType]);
|
||||||
|
|
||||||
const showDefaultValue = useMemo(() => {
|
const showDefaultValue = useMemo(() => {
|
||||||
@ -111,34 +147,31 @@ const InputTypeConfig = ({
|
|||||||
FlowNodeInputTypeEnum.textarea,
|
FlowNodeInputTypeEnum.textarea,
|
||||||
FlowNodeInputTypeEnum.JSONEditor,
|
FlowNodeInputTypeEnum.JSONEditor,
|
||||||
FlowNodeInputTypeEnum.numberInput,
|
FlowNodeInputTypeEnum.numberInput,
|
||||||
FlowNodeInputTypeEnum.switch
|
FlowNodeInputTypeEnum.switch,
|
||||||
|
FlowNodeInputTypeEnum.select
|
||||||
];
|
];
|
||||||
|
|
||||||
return list.includes(inputType);
|
return list.includes(inputType as FlowNodeInputTypeEnum);
|
||||||
}, [inputType]);
|
}, [inputType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack flex={1} borderLeft={'1px solid #F0F1F6'} justifyContent={'space-between'}>
|
<Stack flex={1} borderLeft={'1px solid #F0F1F6'} justifyContent={'space-between'}>
|
||||||
<Flex flexDirection={'column'} p={8} gap={4} flex={'1 0 0'} overflow={'auto'}>
|
<Flex flexDirection={'column'} p={8} pb={2} gap={4} flex={'1 0 0'} overflow={'auto'}>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||||
{type === 'formInput'
|
{typeLabels.name[type] || typeLabels.name.formInput}
|
||||||
? t('common:core.module.input_name')
|
|
||||||
: t('common:core.module.Field Name')}
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
placeholder="appointment/sql"
|
placeholder="appointment/sql"
|
||||||
{...register(type === 'formInput' ? 'label' : 'key', {
|
{...register('label', {
|
||||||
required: true
|
required: true
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex alignItems={'flex-start'}>
|
<Flex alignItems={'flex-start'}>
|
||||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||||
{type === 'formInput'
|
{typeLabels.description[type] || typeLabels.description.plugin}
|
||||||
? t('common:core.module.input_description')
|
|
||||||
: t('workflow:field_description')}
|
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Textarea
|
<Textarea
|
||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
@ -149,7 +182,7 @@ const InputTypeConfig = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* value type */}
|
{/* value type */}
|
||||||
{type === 'plugin' && (
|
{type !== 'formInput' && (
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||||
{t('common:core.module.Data Type')}
|
{t('common:core.module.Data Type')}
|
||||||
@ -167,13 +200,15 @@ const InputTypeConfig = ({
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Box fontSize={'14px'}>{defaultValueType}</Box>
|
<Box fontSize={'14px'} mb={2}>
|
||||||
|
{defaultValueType}
|
||||||
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{showRequired && (
|
{showRequired && (
|
||||||
<Flex alignItems={'center'} minH={'40px'}>
|
<Flex alignItems={'center'} minH={'40px'}>
|
||||||
<FormLabel flex={'1'} fontWeight={'medium'}>
|
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||||
{t('workflow:field_required')}
|
{t('workflow:field_required')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch {...register('required')} />
|
<Switch {...register('required')} />
|
||||||
@ -191,7 +226,6 @@ const InputTypeConfig = ({
|
|||||||
isChecked={isToolInput}
|
isChecked={isToolInput}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setIsToolInput && setIsToolInput();
|
setIsToolInput && setIsToolInput();
|
||||||
console.log(isToolInput);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -208,6 +242,7 @@ const InputTypeConfig = ({
|
|||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
placeholder={t('common:core.module.Max Length placeholder')}
|
placeholder={t('common:core.module.Max Length placeholder')}
|
||||||
value={maxLength}
|
value={maxLength}
|
||||||
|
max={50000}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
setValue('maxLength', e || '');
|
setValue('maxLength', e || '');
|
||||||
@ -258,13 +293,18 @@ const InputTypeConfig = ({
|
|||||||
{t('common:core.module.Default Value')}
|
{t('common:core.module.Default Value')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
{inputType === FlowNodeInputTypeEnum.numberInput && (
|
{inputType === FlowNodeInputTypeEnum.numberInput && (
|
||||||
<Input
|
<NumberInput flex={1} step={1} min={min} max={max} position={'relative'}>
|
||||||
bg={'myGray.50'}
|
<NumberInputField
|
||||||
max={max}
|
{...register('defaultValue', {
|
||||||
min={min}
|
min: min,
|
||||||
type={'number'}
|
max: max
|
||||||
{...register('defaultValue')}
|
})}
|
||||||
/>
|
/>
|
||||||
|
<NumberInputStepper>
|
||||||
|
<NumberIncrementStepper />
|
||||||
|
<NumberDecrementStepper />
|
||||||
|
</NumberInputStepper>
|
||||||
|
</NumberInput>
|
||||||
)}
|
)}
|
||||||
{inputType === FlowNodeInputTypeEnum.input && (
|
{inputType === FlowNodeInputTypeEnum.input && (
|
||||||
<Input bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
<Input bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
||||||
@ -280,10 +320,29 @@ const InputTypeConfig = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue('defaultValue', e);
|
setValue('defaultValue', e);
|
||||||
}}
|
}}
|
||||||
defaultValue={String(defaultJsonValue)}
|
defaultValue={defaultValue}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{inputType === FlowNodeInputTypeEnum.switch && <Switch {...register('defaultValue')} />}
|
{inputType === FlowNodeInputTypeEnum.switch && <Switch {...register('defaultValue')} />}
|
||||||
|
{inputType === FlowNodeInputTypeEnum.select && (
|
||||||
|
<MySelect<string>
|
||||||
|
list={[defaultListValue, ...listValue]
|
||||||
|
.filter((item) => item.label !== '')
|
||||||
|
.map((item) => ({
|
||||||
|
label: item.label,
|
||||||
|
value: item.value
|
||||||
|
}))}
|
||||||
|
value={
|
||||||
|
defaultValue && listValue.map((item) => item.value).includes(defaultValue)
|
||||||
|
? defaultValue
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
onchange={(e) => {
|
||||||
|
setValue('defaultValue', e);
|
||||||
|
}}
|
||||||
|
w={'200px'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -314,40 +373,116 @@ const InputTypeConfig = ({
|
|||||||
|
|
||||||
{inputType === FlowNodeInputTypeEnum.select && (
|
{inputType === FlowNodeInputTypeEnum.select && (
|
||||||
<>
|
<>
|
||||||
<Flex flexDirection={'column'} gap={4}>
|
<DndDrag<{ id: string; value: string }>
|
||||||
{selectEnums.map((item, i) => (
|
onDragEndCb={(list) => {
|
||||||
<Flex key={item.id} alignItems={'center'}>
|
const newOrder = list.map((item) => item.id);
|
||||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
const newSelectEnums = newOrder
|
||||||
{`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
.map((id) => mergedSelectEnums.find((item) => item.id === id))
|
||||||
</FormLabel>
|
.filter(Boolean) as { id: string; value: string }[];
|
||||||
<FormControl>
|
removeEnums();
|
||||||
<Input
|
newSelectEnums.forEach((item) => appendEnums(item));
|
||||||
fontSize={'12px'}
|
|
||||||
bg={'myGray.50'}
|
// 防止最后一个元素被focus
|
||||||
placeholder={`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
setTimeout(() => {
|
||||||
{...register(`list.${i}.label`, {
|
if (document.activeElement instanceof HTMLElement) {
|
||||||
required: true,
|
document.activeElement.blur();
|
||||||
onChange: (e: any) => {
|
}
|
||||||
setValue(`list.${i}.value`, e.target.value);
|
}, 0);
|
||||||
}
|
}}
|
||||||
})}
|
dataList={mergedSelectEnums}
|
||||||
/>
|
renderClone={(provided, snapshot, rubric) => {
|
||||||
</FormControl>
|
return (
|
||||||
{selectEnums.length > 1 && (
|
<Box
|
||||||
<MyIcon
|
bg={'myGray.50'}
|
||||||
ml={3}
|
border={'1px solid'}
|
||||||
name={'delete'}
|
borderColor={'myGray.200'}
|
||||||
w={'16px'}
|
p={2}
|
||||||
cursor={'pointer'}
|
borderRadius="md"
|
||||||
p={2}
|
boxShadow="md"
|
||||||
borderRadius={'md'}
|
{...provided.draggableProps}
|
||||||
_hover={{ bg: 'red.100' }}
|
{...provided.dragHandleProps}
|
||||||
onClick={() => removeEnums(i)}
|
>
|
||||||
/>
|
{mergedSelectEnums[rubric.source.index].value}
|
||||||
)}
|
</Box>
|
||||||
</Flex>
|
);
|
||||||
))}
|
}}
|
||||||
</Flex>
|
>
|
||||||
|
{(provided) => (
|
||||||
|
<Box
|
||||||
|
{...provided.droppableProps}
|
||||||
|
ref={provided.innerRef}
|
||||||
|
display={'flex'}
|
||||||
|
flexDirection={'column'}
|
||||||
|
gap={4}
|
||||||
|
>
|
||||||
|
{mergedSelectEnums.map((item, i) => (
|
||||||
|
<Draggable key={i} draggableId={i.toString()} index={i}>
|
||||||
|
{(provided, snapshot) => (
|
||||||
|
<Box
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
style={{
|
||||||
|
...provided.draggableProps.style,
|
||||||
|
opacity: snapshot.isDragging ? 0.8 : 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
alignItems={'center'}
|
||||||
|
position={'relative'}
|
||||||
|
transform={snapshot.isDragging ? `scale(0.5)` : ''}
|
||||||
|
transformOrigin={'top left'}
|
||||||
|
>
|
||||||
|
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||||
|
{`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
fontSize={'12px'}
|
||||||
|
bg={'myGray.50'}
|
||||||
|
placeholder={`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||||
|
{...register(`list.${i}.label`, {
|
||||||
|
required: true,
|
||||||
|
onChange: (e: any) => {
|
||||||
|
setValue(`list.${i}.value`, e.target.value);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
{selectEnums.length > 1 && (
|
||||||
|
<Flex>
|
||||||
|
<MyIcon
|
||||||
|
ml={3}
|
||||||
|
name={'delete'}
|
||||||
|
w={'16px'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
p={2}
|
||||||
|
borderRadius={'md'}
|
||||||
|
_hover={{ bg: 'red.100' }}
|
||||||
|
onClick={() => removeEnums(i)}
|
||||||
|
/>
|
||||||
|
<Box {...provided.dragHandleProps}>
|
||||||
|
<MyIcon
|
||||||
|
name={'drag'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
p={2}
|
||||||
|
borderRadius={'md'}
|
||||||
|
_hover={{ color: 'primary.600' }}
|
||||||
|
w={'16px'}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
<Box h="0" w="0">
|
||||||
|
{provided.placeholder}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</DndDrag>
|
||||||
<Button
|
<Button
|
||||||
variant={'whiteBase'}
|
variant={'whiteBase'}
|
||||||
leftIcon={<MyIcon name={'common/addLight'} w={'16px'} />}
|
leftIcon={<MyIcon name={'common/addLight'} w={'16px'} />}
|
||||||
|
|||||||
@ -21,9 +21,7 @@ const VariableTable = ({
|
|||||||
<Table bg={'white'}>
|
<Table bg={'white'}>
|
||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th borderBottomLeftRadius={'none !important'}>
|
<Th borderBottomLeftRadius={'none !important'}>{t('workflow:Variable_name')}</Th>
|
||||||
{t('common:core.module.variable.variable name')}
|
|
||||||
</Th>
|
|
||||||
<Th>{t('common:core.workflow.Value type')}</Th>
|
<Th>{t('common:core.workflow.Value type')}</Th>
|
||||||
{showToolColumn && <Th>{t('workflow:tool_input')}</Th>}
|
{showToolColumn && <Th>{t('workflow:tool_input')}</Th>}
|
||||||
<Th borderBottomRightRadius={'none !important'}></Th>
|
<Th borderBottomRightRadius={'none !important'}></Th>
|
||||||
|
|||||||
@ -38,10 +38,7 @@ const NodeSimple = ({
|
|||||||
{filterHiddenInputs.length > 0 && (
|
{filterHiddenInputs.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Container>
|
<Container>
|
||||||
<IOTitle
|
<IOTitle text={t('common:common.Input')} />
|
||||||
text={t('common:common.Input')}
|
|
||||||
inputExplanationUrl={data.inputExplanationUrl}
|
|
||||||
/>
|
|
||||||
<RenderInput nodeId={nodeId} flowInputList={commonInputs} />
|
<RenderInput nodeId={nodeId} flowInputList={commonInputs} />
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -25,6 +25,8 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useWorkflowUtils } from '../../hooks/useUtils';
|
import { useWorkflowUtils } from '../../hooks/useUtils';
|
||||||
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
|
||||||
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
|
import { getDocPath } from '@/web/common/system/doc';
|
||||||
|
|
||||||
type Props = FlowNodeItemType & {
|
type Props = FlowNodeItemType & {
|
||||||
children?: React.ReactNode | React.ReactNode[] | string;
|
children?: React.ReactNode | React.ReactNode[] | string;
|
||||||
@ -39,7 +41,8 @@ type Props = FlowNodeItemType & {
|
|||||||
copy?: boolean;
|
copy?: boolean;
|
||||||
delete?: boolean;
|
delete?: boolean;
|
||||||
};
|
};
|
||||||
} & Omit<FlexProps, 'children'>;
|
customStyle?: FlexProps;
|
||||||
|
};
|
||||||
|
|
||||||
const NodeCard = (props: Props) => {
|
const NodeCard = (props: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -63,9 +66,8 @@ const NodeCard = (props: Props) => {
|
|||||||
isError = false,
|
isError = false,
|
||||||
debugResult,
|
debugResult,
|
||||||
isFolded,
|
isFolded,
|
||||||
...customStyle
|
customStyle
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
const setHoverNodeId = useContextSelector(WorkflowContext, (v) => v.setHoverNodeId);
|
const setHoverNodeId = useContextSelector(WorkflowContext, (v) => v.setHoverNodeId);
|
||||||
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
||||||
@ -102,14 +104,6 @@ const NodeCard = (props: Props) => {
|
|||||||
if (!node?.pluginId) return;
|
if (!node?.pluginId) return;
|
||||||
const template = await getPreviewPluginNode({ appId: node.pluginId });
|
const template = await getPreviewPluginNode({ appId: node.pluginId });
|
||||||
|
|
||||||
// Focus update plugin latest inputExplanationUrl
|
|
||||||
onChangeNode({
|
|
||||||
nodeId,
|
|
||||||
type: 'attr',
|
|
||||||
key: 'inputExplanationUrl',
|
|
||||||
value: template.inputExplanationUrl
|
|
||||||
});
|
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
} else {
|
} else {
|
||||||
const template = moduleTemplatesFlat.find(
|
const template = moduleTemplatesFlat.find(
|
||||||
@ -275,6 +269,24 @@ const NodeCard = (props: Props) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</MyTooltip>
|
</MyTooltip>
|
||||||
)}
|
)}
|
||||||
|
{!!nodeTemplate?.diagram && node?.courseUrl && (
|
||||||
|
<Box bg={'myGray.300'} w={'1px'} h={'12px'} mx={1} />
|
||||||
|
)}
|
||||||
|
{node?.courseUrl && !hasNewVersion && (
|
||||||
|
<MyTooltip label={t('workflow:Node.Open_Node_Course')}>
|
||||||
|
<MyIcon
|
||||||
|
cursor={'pointer'}
|
||||||
|
name="book"
|
||||||
|
color={'primary.600'}
|
||||||
|
w={'18px'}
|
||||||
|
ml={1}
|
||||||
|
_hover={{
|
||||||
|
color: 'primary.800'
|
||||||
|
}}
|
||||||
|
onClick={() => window.open(getDocPath(node.courseUrl || ''), '_blank')}
|
||||||
|
/>
|
||||||
|
</MyTooltip>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||||
</Box>
|
</Box>
|
||||||
@ -295,6 +307,7 @@ const NodeCard = (props: Props) => {
|
|||||||
onOpenConfirmSync,
|
onOpenConfirmSync,
|
||||||
onClickSyncVersion,
|
onClickSyncVersion,
|
||||||
nodeTemplate?.diagram,
|
nodeTemplate?.diagram,
|
||||||
|
node?.courseUrl,
|
||||||
intro,
|
intro,
|
||||||
menuForbid,
|
menuForbid,
|
||||||
nodeList,
|
nodeList,
|
||||||
|
|||||||
@ -148,11 +148,13 @@ type WorkflowContextType = {
|
|||||||
onStartNodeDebug: ({
|
onStartNodeDebug: ({
|
||||||
entryNodeId,
|
entryNodeId,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges
|
runtimeEdges,
|
||||||
|
variables
|
||||||
}: {
|
}: {
|
||||||
entryNodeId: string;
|
entryNodeId: string;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
|
variables: Record<string, any>;
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
onStopNodeDebug: () => void;
|
onStopNodeDebug: () => void;
|
||||||
|
|
||||||
@ -749,17 +751,19 @@ const WorkflowContextProvider = ({
|
|||||||
async ({
|
async ({
|
||||||
entryNodeId,
|
entryNodeId,
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges
|
runtimeEdges,
|
||||||
|
variables
|
||||||
}: {
|
}: {
|
||||||
entryNodeId: string;
|
entryNodeId: string;
|
||||||
runtimeNodes: RuntimeNodeItemType[];
|
runtimeNodes: RuntimeNodeItemType[];
|
||||||
runtimeEdges: RuntimeEdgeItemType[];
|
runtimeEdges: RuntimeEdgeItemType[];
|
||||||
|
variables: Record<string, any>;
|
||||||
}) => {
|
}) => {
|
||||||
const data = {
|
const data = {
|
||||||
runtimeNodes,
|
runtimeNodes,
|
||||||
runtimeEdges,
|
runtimeEdges,
|
||||||
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
|
nextRunNodes: runtimeNodes.filter((node) => node.nodeId === entryNodeId),
|
||||||
variables: {}
|
variables
|
||||||
};
|
};
|
||||||
onStopNodeDebug();
|
onStopNodeDebug();
|
||||||
setWorkflowDebugData(data);
|
setWorkflowDebugData(data);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user