Compare commits
14 Commits
v4.8-previ
...
v4.8-alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
caa0755d9a | ||
|
|
fef1a1702b | ||
|
|
2a99e46353 | ||
|
|
8f9203c053 | ||
|
|
2053bbdb1b | ||
|
|
9e192c6d11 | ||
|
|
eef609a063 | ||
|
|
5bb9c550f6 | ||
|
|
db1c27cdc7 | ||
|
|
8863337606 | ||
|
|
59bd2a47b6 | ||
|
|
d057ba29f0 | ||
|
|
b500631a4d | ||
|
|
bf6084da69 |
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -6,10 +6,10 @@
|
||||
"i18n-ally.localesPaths": [
|
||||
"projects/app/i18n",
|
||||
],
|
||||
"i18n-ally.enabledParsers": ["json"],
|
||||
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": true,
|
||||
"i18n-ally.keepFulfilled": false,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "zh", // 显示语言
|
||||
"i18n-ally.displayLanguage": "zh" // 显示语言
|
||||
}
|
||||
@@ -17,8 +17,6 @@ images: []
|
||||
4. 无法解决时,可以找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue,私有部署错误,务必提供详细的日志,否则很难排查。
|
||||
|
||||
|
||||
|
||||
|
||||
## 二、通用问题
|
||||
|
||||
### 能否纯本地运行
|
||||
@@ -47,7 +45,7 @@ images: []
|
||||
|
||||
### 模型响应为空(core.chat.Chat API is error or undefined)
|
||||
|
||||
1. 检查 key 问题。
|
||||
1. 检查 key 问题。curl 请求看是否正常。务必用 stream=true 模式。并且 maxToken 等相关参数尽量一致。
|
||||
2. 如果是国内模型,可能是命中风控了。
|
||||
3. 查看模型请求日志,检查出入参数是否异常。
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.8(进行中)'
|
||||
title: 'V4.8(开发中)'
|
||||
description: 'FastGPT V4.8 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -18,10 +18,14 @@ FastGPT workflow V2上线,支持更加简洁的工作流模式。
|
||||
## V4.8 更新说明
|
||||
|
||||
1. 重构 - 工作流
|
||||
2. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流。
|
||||
3. 新增 - 定时执行应用。可轻松实现定时任务。
|
||||
4. 新增 - 插件自定义输入优化,可以渲染输入组件。
|
||||
6. 优化 - 工作流连线,可以四向连接,方便构建循环工作流。
|
||||
7. 优化 - 工作流上下文传递,性能🚀。
|
||||
8. 优化 - 简易模式,更新配置后自动更新调试框内容,无需保存。
|
||||
9. 优化 - worker进程管理,并将计算 Token 任务分配给 worker 进程。
|
||||
2. 新增 - 判断器。支持 if elseIf else 判断。
|
||||
3. 新增 - 变量更新节点。支持更新运行中工作流输出变量,或更新全局变量。
|
||||
4. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流。
|
||||
5. 新增 - 定时执行应用。可轻松实现定时任务。
|
||||
6. 新增 - 插件自定义输入优化,可以渲染输入组件。
|
||||
7. 优化 - 工作流连线,可以四向连接,方便构建循环工作流。
|
||||
8. 优化 - 工作流上下文传递,性能🚀。
|
||||
9. 优化 - 简易模式,更新配置后自动更新调试框内容,无需保存。
|
||||
10. 优化 - worker进程管理,并将计算 Token 任务分配给 worker 进程。
|
||||
11. 修复 - 工具调用时候,name不能是数字开头(随机数有概率数字开头)
|
||||
12. 修复 - 分享链接, query 全局变量会被缓存。
|
||||
@@ -50,8 +50,18 @@ export const replaceSensitiveText = (text: string) => {
|
||||
return text;
|
||||
};
|
||||
|
||||
/* Make sure the first letter is definitely lowercase */
|
||||
export const getNanoid = (size = 12) => {
|
||||
return customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', size)();
|
||||
const firstChar = customAlphabet('abcdefghijklmnopqrstuvwxyz', 1)();
|
||||
|
||||
if (size === 1) return firstChar;
|
||||
|
||||
const randomsStr = customAlphabet(
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
|
||||
size - 1
|
||||
)();
|
||||
|
||||
return `${firstChar}${randomsStr}`;
|
||||
};
|
||||
|
||||
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
2
packages/global/core/chat/type.d.ts
vendored
2
packages/global/core/chat/type.d.ts
vendored
@@ -155,6 +155,6 @@ export type ToolModuleResponseItemType = {
|
||||
|
||||
/* dispatch run time */
|
||||
export type RuntimeUserPromptType = {
|
||||
files?: UserChatItemValueItemType['file'][];
|
||||
files: UserChatItemValueItemType['file'][];
|
||||
text: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DispatchNodeResponseType } from '../workflow/runtime/type';
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../workflow/node/constant';
|
||||
import { FlowNodeTypeEnum } from '../workflow/node/constant';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
|
||||
import { ChatHistoryItemResType, ChatItemType } from './type.d';
|
||||
import { ChatHistoryItemResType, ChatItemType, UserChatItemValueItemType } from './type.d';
|
||||
|
||||
export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => {
|
||||
// @ts-ignore
|
||||
@@ -77,3 +77,15 @@ export const filterPublicNodeResponseData = ({
|
||||
return obj as ChatHistoryItemResType;
|
||||
});
|
||||
};
|
||||
|
||||
export const removeEmptyUserInput = (input: UserChatItemValueItemType[]) => {
|
||||
return input.filter((item) => {
|
||||
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ export enum NodeInputKeyEnum {
|
||||
welcomeText = 'welcomeText',
|
||||
switch = 'switch', // a trigger switch
|
||||
history = 'history',
|
||||
userChatInput = 'userChatInput',
|
||||
answerText = 'text',
|
||||
|
||||
// system config
|
||||
@@ -47,6 +46,10 @@ export enum NodeInputKeyEnum {
|
||||
variables = 'variables',
|
||||
scheduleTrigger = 'scheduleTrigger',
|
||||
|
||||
// entry
|
||||
userChatInput = 'userChatInput',
|
||||
inputFiles = 'inputFiles',
|
||||
|
||||
agents = 'agents', // cq agent key
|
||||
|
||||
// latest
|
||||
@@ -101,7 +104,10 @@ export enum NodeInputKeyEnum {
|
||||
|
||||
// if else
|
||||
condition = 'condition',
|
||||
ifElseList = 'ifElseList'
|
||||
ifElseList = 'ifElseList',
|
||||
|
||||
// variable update
|
||||
updateList = 'updateList'
|
||||
}
|
||||
|
||||
export enum NodeOutputKeyEnum {
|
||||
@@ -135,15 +141,14 @@ export enum NodeOutputKeyEnum {
|
||||
// plugin
|
||||
pluginStart = 'pluginStart',
|
||||
|
||||
if = 'IF',
|
||||
else = 'ELSE'
|
||||
ifElseResult = 'ifElseResult'
|
||||
}
|
||||
|
||||
export enum VariableInputEnum {
|
||||
input = 'input',
|
||||
textarea = 'textarea',
|
||||
select = 'select',
|
||||
external = 'external'
|
||||
custom = 'custom'
|
||||
}
|
||||
export const variableMap = {
|
||||
[VariableInputEnum.input]: {
|
||||
@@ -161,10 +166,10 @@ export const variableMap = {
|
||||
title: 'core.module.variable.select type',
|
||||
desc: ''
|
||||
},
|
||||
[VariableInputEnum.external]: {
|
||||
[VariableInputEnum.custom]: {
|
||||
icon: 'core/app/variable/external',
|
||||
title: 'core.module.variable.External type',
|
||||
desc: '可以通过API接口或分享链接的Query传递变量。增加该类型变量的主要目的是用于变量提示。使用例子: 你可以通过分享链接Query中拼接Token,来实现内部系统身份鉴权。'
|
||||
title: 'core.module.variable.Custom type',
|
||||
desc: '可以定义一个无需用户填写的全局变量。\n该变量的值可以来自于 API 接口,分享链接的 Query 或通过【变量更新】模块进行赋值。'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -112,7 +112,8 @@ export enum FlowNodeTypeEnum {
|
||||
tools = 'tools',
|
||||
stopTool = 'stopTool',
|
||||
lafModule = 'lafModule',
|
||||
ifElseNode = 'ifElseNode'
|
||||
ifElseNode = 'ifElseNode',
|
||||
variableUpdate = 'variableUpdate'
|
||||
}
|
||||
|
||||
export const EDGE_TYPE = 'default';
|
||||
|
||||
@@ -9,7 +9,8 @@ export enum SseResponseEventEnum {
|
||||
toolCall = 'toolCall', // tool start
|
||||
toolParams = 'toolParams', // tool params return
|
||||
toolResponse = 'toolResponse', // tool response return
|
||||
flowResponses = 'flowResponses' // sse response request
|
||||
flowResponses = 'flowResponses', // sse response request
|
||||
updateVariables = 'updateVariables'
|
||||
}
|
||||
|
||||
export enum DispatchNodeResponseKeyEnum {
|
||||
|
||||
@@ -75,7 +75,7 @@ export type DispatchNodeResponseType = {
|
||||
pluginDetail?: ChatHistoryItemResType[];
|
||||
|
||||
// if-else
|
||||
ifElseResult?: 'IF' | 'ELSE';
|
||||
ifElseResult?: string;
|
||||
|
||||
// tool
|
||||
toolCallTokens?: number;
|
||||
|
||||
@@ -5,6 +5,8 @@ import { StoreNodeItemType } from '../type';
|
||||
import { StoreEdgeItemType } from '../type/edge';
|
||||
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
|
||||
import { VARIABLE_NODE_ID } from '../constants';
|
||||
import { isReferenceValue } from '../utils';
|
||||
import { ReferenceValueProps } from '../type/io';
|
||||
|
||||
export const initWorkflowEdgeStatus = (edges: StoreEdgeItemType[]): RuntimeEdgeItemType[] => {
|
||||
return (
|
||||
@@ -138,16 +140,11 @@ export const getReferenceVariableValue = ({
|
||||
nodes,
|
||||
variables
|
||||
}: {
|
||||
value: [string, string];
|
||||
value: ReferenceValueProps;
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>;
|
||||
}) => {
|
||||
if (
|
||||
!Array.isArray(value) ||
|
||||
value.length !== 2 ||
|
||||
typeof value[0] !== 'string' ||
|
||||
typeof value[1] !== 'string'
|
||||
) {
|
||||
if (!isReferenceValue(value)) {
|
||||
return value;
|
||||
}
|
||||
const sourceNodeId = value[0];
|
||||
|
||||
@@ -18,10 +18,10 @@ import { PluginOutputModule } from './system/pluginOutput';
|
||||
import { RunPluginModule } from './system/runPlugin';
|
||||
import { AiQueryExtension } from './system/queryExtension';
|
||||
|
||||
import type { FlowNodeTemplateType, nodeTemplateListType } from '../type';
|
||||
import { FlowNodeTemplateTypeEnum } from '../../workflow/constants';
|
||||
import { lafModule } from './system/laf';
|
||||
import { ifElseNode } from './system/ifElse/index';
|
||||
import type { FlowNodeTemplateType } from '../type';
|
||||
import { LafModule } from './system/laf';
|
||||
import { IfElseNode } from './system/ifElse/index';
|
||||
import { VariableUpdateNode } from './system/variableUpdate';
|
||||
|
||||
/* app flow module templates */
|
||||
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
@@ -38,8 +38,9 @@ export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
ContextExtractModule,
|
||||
HttpModule468,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
LafModule,
|
||||
IfElseNode,
|
||||
VariableUpdateNode
|
||||
];
|
||||
/* plugin flow module templates */
|
||||
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
@@ -56,8 +57,9 @@ export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
|
||||
ContextExtractModule,
|
||||
HttpModule468,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
LafModule,
|
||||
IfElseNode,
|
||||
VariableUpdateNode
|
||||
];
|
||||
|
||||
/* all module */
|
||||
@@ -80,6 +82,7 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||
PluginOutputModule,
|
||||
RunPluginModule,
|
||||
AiQueryExtension,
|
||||
lafModule,
|
||||
ifElseNode
|
||||
LafModule,
|
||||
IfElseNode,
|
||||
VariableUpdateNode
|
||||
];
|
||||
|
||||
@@ -9,9 +9,10 @@ export const Input_Template_History: FlowNodeInputItemType = {
|
||||
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||
label: 'core.module.input.label.chat history',
|
||||
description: '最多携带多少轮对话记录',
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 30,
|
||||
max: 50,
|
||||
value: 6
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../node/constant';
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../type/index.d';
|
||||
import {
|
||||
WorkflowIOValueTypeEnum,
|
||||
NodeInputKeyEnum,
|
||||
FlowNodeTemplateTypeEnum,
|
||||
NodeOutputKeyEnum
|
||||
FlowNodeTemplateTypeEnum
|
||||
} from '../../constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
@@ -26,7 +21,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
||||
{
|
||||
key: NodeInputKeyEnum.answerText,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.textarea, FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
label: 'core.module.input.label.Response content',
|
||||
description: 'core.module.input.description.Response content',
|
||||
placeholder: 'core.module.input.description.Response content'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FlowNodeTemplateTypeEnum, WorkflowIOValueTypeEnum } from '../../constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { VariableItemType } from '../../../app/type';
|
||||
import { FlowNodeTemplateType } from '../../type';
|
||||
|
||||
@@ -25,6 +25,7 @@ export const getGlobalVariableNode = ({
|
||||
id: item.key,
|
||||
key: item.key,
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.static,
|
||||
label: item.label
|
||||
}))
|
||||
};
|
||||
|
||||
@@ -20,6 +20,11 @@ export enum VariableConditionEnum {
|
||||
lengthLessThan = 'lengthLessThan',
|
||||
lengthLessThanOrEqualTo = 'lengthLessThanOrEqualTo'
|
||||
}
|
||||
export enum IfElseResultEnum {
|
||||
IF = 'IF',
|
||||
ELSE = 'ELSE',
|
||||
ELSE_IF = 'ELSE IF'
|
||||
}
|
||||
|
||||
export const stringConditionList = [
|
||||
{ label: '为空', value: VariableConditionEnum.isEmpty },
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { FlowNodeTemplateType } from '../../../type';
|
||||
import { getHandleConfig } from '../../utils';
|
||||
|
||||
export const ifElseNode: FlowNodeTemplateType = {
|
||||
export const IfElseNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.ifElseNode,
|
||||
templateType: FlowNodeTemplateTypeEnum.tools,
|
||||
flowNodeType: FlowNodeTypeEnum.ifElseNode,
|
||||
@@ -23,14 +23,6 @@ export const ifElseNode: FlowNodeTemplateType = {
|
||||
intro: '根据一定的条件,执行不同的分支。',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.condition,
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
label: '',
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
required: false,
|
||||
value: 'AND' // AND, OR
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.ifElseList,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
@@ -38,27 +30,25 @@ export const ifElseNode: FlowNodeTemplateType = {
|
||||
label: '',
|
||||
value: [
|
||||
{
|
||||
variable: undefined,
|
||||
condition: undefined,
|
||||
value: undefined
|
||||
condition: 'AND', // AND, OR
|
||||
list: [
|
||||
{
|
||||
variable: undefined,
|
||||
condition: undefined,
|
||||
value: undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
id: NodeOutputKeyEnum.if,
|
||||
key: NodeOutputKeyEnum.if,
|
||||
label: 'IF',
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
type: FlowNodeOutputTypeEnum.source
|
||||
},
|
||||
{
|
||||
id: NodeOutputKeyEnum.else,
|
||||
key: NodeOutputKeyEnum.else,
|
||||
label: 'ELSE',
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
type: FlowNodeOutputTypeEnum.source
|
||||
id: NodeOutputKeyEnum.ifElseResult,
|
||||
key: NodeOutputKeyEnum.ifElseResult,
|
||||
label: '判断结果',
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -2,8 +2,12 @@ import { ReferenceValueProps } from 'core/workflow/type/io';
|
||||
import { VariableConditionEnum } from './constant';
|
||||
|
||||
export type IfElseConditionType = 'AND' | 'OR';
|
||||
export type IfElseListItemType = {
|
||||
export type ConditionListItemType = {
|
||||
variable?: ReferenceValueProps;
|
||||
condition?: VariableConditionEnum;
|
||||
value?: string;
|
||||
};
|
||||
export type IfElseListItemType = {
|
||||
condition: IfElseConditionType;
|
||||
list: ConditionListItemType[];
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Input_Template_DynamicInput } from '../input';
|
||||
import { Output_Template_AddOutput } from '../output';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const lafModule: FlowNodeTemplateType = {
|
||||
export const LafModule: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.lafModule,
|
||||
templateType: FlowNodeTemplateTypeEnum.externalCall,
|
||||
flowNodeType: FlowNodeTypeEnum.lafModule,
|
||||
|
||||
@@ -12,7 +12,7 @@ export const PluginInputModule: FlowNodeTemplateType = {
|
||||
unique: true,
|
||||
forbidDelete: true,
|
||||
avatar: '/imgs/workflow/input.png',
|
||||
name: '定义插件输入',
|
||||
name: '自定义插件输入',
|
||||
intro: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入',
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
|
||||
@@ -12,7 +12,7 @@ export const PluginOutputModule: FlowNodeTemplateType = {
|
||||
unique: true,
|
||||
forbidDelete: true,
|
||||
avatar: '/imgs/workflow/output.png',
|
||||
name: '定义插件输出',
|
||||
name: '自定义插件输出',
|
||||
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
|
||||
showStatus: false,
|
||||
inputs: [],
|
||||
|
||||
@@ -27,7 +27,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
sourceHandle: getHandleConfig(true, true, false, true),
|
||||
targetHandle: getHandleConfig(true, true, false, true),
|
||||
avatar: '/imgs/workflow/tool.svg',
|
||||
name: '工具调用(实验)',
|
||||
name: '工具调用(实验)',
|
||||
intro: '通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。',
|
||||
showStatus: true,
|
||||
inputs: [
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../../type/index.d';
|
||||
import {
|
||||
FlowNodeTemplateTypeEnum,
|
||||
NodeInputKeyEnum,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '../../../constants';
|
||||
import { getHandleConfig } from '../../utils';
|
||||
|
||||
export const VariableUpdateNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.variableUpdate,
|
||||
templateType: FlowNodeTemplateTypeEnum.tools,
|
||||
flowNodeType: FlowNodeTypeEnum.variableUpdate,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/variable.png',
|
||||
name: '变量更新',
|
||||
intro: '可以更新指定节点的输出值和全局变量',
|
||||
showStatus: true,
|
||||
isTool: false,
|
||||
inputs: [
|
||||
{
|
||||
key: NodeInputKeyEnum.updateList,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
label: '',
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
editField: {
|
||||
key: true,
|
||||
valueType: true
|
||||
},
|
||||
value: [
|
||||
{
|
||||
variable: ['', ''],
|
||||
value: ['', ''],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
renderType: FlowNodeInputTypeEnum.input
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
outputs: []
|
||||
};
|
||||
10
packages/global/core/workflow/template/system/variableUpdate/type.d.ts
vendored
Normal file
10
packages/global/core/workflow/template/system/variableUpdate/type.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { FlowNodeInputTypeEnum } from '../../../node/constant';
|
||||
import { ReferenceValueProps } from '../../..//type/io';
|
||||
import { WorkflowIOValueTypeEnum } from '../../../constants';
|
||||
|
||||
export type TUpdateListItem = {
|
||||
variable?: ReferenceValueProps;
|
||||
value: ReferenceValueProps;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
|
||||
};
|
||||
@@ -40,7 +40,7 @@ export type FlowNodeCommonType = {
|
||||
};
|
||||
|
||||
export type FlowNodeTemplateType = FlowNodeCommonType & {
|
||||
id: string; // module id, unique
|
||||
id: string; // node id, unique
|
||||
templateType: `${FlowNodeTemplateTypeEnum}`;
|
||||
|
||||
// show handle
|
||||
@@ -132,11 +132,12 @@ export type ChatDispatchProps = {
|
||||
chatId?: string;
|
||||
responseChatItemId?: string;
|
||||
histories: ChatItemType[];
|
||||
variables: Record<string, any>;
|
||||
inputFiles?: UserChatItemValueItemType['file'][];
|
||||
variables: Record<string, any>; // global variable
|
||||
query: UserChatItemValueItemType[]; // trigger query
|
||||
stream: boolean;
|
||||
detail: boolean; // response detail
|
||||
maxRunTimes: number;
|
||||
isToolCall?: boolean;
|
||||
};
|
||||
|
||||
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
} from '../app/type';
|
||||
import { EditorVariablePickerType } from '../../../web/components/common/Textarea/PromptEditor/type';
|
||||
import { defaultWhisperConfig } from '../app/constants';
|
||||
import { IfElseResultEnum } from './template/system/ifElse/constant';
|
||||
|
||||
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
|
||||
return `${nodeId}-${type}-${key}`;
|
||||
@@ -132,3 +133,11 @@ export const formatEditorVariablePickerIcon = (
|
||||
icon: item.type ? variableMap[item.type]?.icon : variableMap['input'].icon
|
||||
}));
|
||||
};
|
||||
|
||||
export const isReferenceValue = (value: any): boolean => {
|
||||
return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string';
|
||||
};
|
||||
|
||||
export const getElseIFLabel = (i: number) => {
|
||||
return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { addLog } from '../../../common/system/log';
|
||||
import { POST } from '../../../common/api/serverRequest';
|
||||
|
||||
type PostReRankResponse = {
|
||||
@@ -38,7 +39,7 @@ export function reRankRecall({
|
||||
}
|
||||
)
|
||||
.then((data) => {
|
||||
console.log('rerank time:', Date.now() - start);
|
||||
addLog.info('ReRank finish:', { time: Date.now() - start });
|
||||
|
||||
return data?.results?.map((item) => ({
|
||||
id: documents[item.index].id,
|
||||
@@ -46,7 +47,7 @@ export function reRankRecall({
|
||||
}));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('rerank error:', err);
|
||||
addLog.error('rerank error', err);
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
1
packages/service/core/plugin/type.d.ts
vendored
1
packages/service/core/plugin/type.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
||||
|
||||
declare global {
|
||||
var communityPluginsV1: PluginTemplateType[];
|
||||
var communityPlugins: PluginTemplateType[];
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ export const runToolWithFunctionCall = async (
|
||||
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
|
||||
@@ -185,6 +185,7 @@ export const runToolWithPromptCall = async (
|
||||
|
||||
const moduleRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
|
||||
@@ -182,6 +182,7 @@ export const runToolWithToolChoice = async (
|
||||
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from '../../../../common/string/tiktoken/index';
|
||||
import {
|
||||
chats2GPTMessages,
|
||||
chatValue2RuntimePrompt,
|
||||
getSystemPrompt,
|
||||
GPTMessages2Chats,
|
||||
runtimePrompt2ChatsValue
|
||||
@@ -66,7 +67,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
user,
|
||||
histories,
|
||||
node: { name },
|
||||
inputFiles = [],
|
||||
query,
|
||||
params: {
|
||||
model,
|
||||
temperature = 0,
|
||||
@@ -80,6 +81,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
quotePrompt
|
||||
}
|
||||
} = props;
|
||||
const { files: inputFiles } = chatValue2RuntimePrompt(query);
|
||||
|
||||
if (!userChatInput && inputFiles.length === 0) {
|
||||
return Promise.reject('Question is empty');
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ import { dispatchLafRequest } from './tools/runLaf';
|
||||
import { dispatchIfElse } from './tools/runIfElse';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { dispatchSystemConfig } from './init/systemConfiig';
|
||||
import { dispatchSystemConfig } from './init/systemConfig';
|
||||
import { dispatchUpdateVariable } from './tools/runUpdateVar';
|
||||
|
||||
const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||
@@ -62,6 +63,7 @@ const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.stopTool]: dispatchStopToolCall,
|
||||
[FlowNodeTypeEnum.lafModule]: dispatchLafRequest,
|
||||
[FlowNodeTypeEnum.ifElseNode]: dispatchIfElse,
|
||||
[FlowNodeTypeEnum.variableUpdate]: dispatchUpdateVariable,
|
||||
|
||||
// none
|
||||
[FlowNodeTypeEnum.systemConfig]: dispatchSystemConfig,
|
||||
@@ -69,21 +71,25 @@ const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.globalVariable]: () => Promise.resolve()
|
||||
};
|
||||
|
||||
/* running */
|
||||
export async function dispatchWorkFlow({
|
||||
res,
|
||||
runtimeNodes = [],
|
||||
runtimeEdges = [],
|
||||
histories = [],
|
||||
variables = {},
|
||||
user,
|
||||
stream = false,
|
||||
detail = false,
|
||||
...props
|
||||
}: ChatDispatchProps & {
|
||||
type Props = ChatDispatchProps & {
|
||||
runtimeNodes: RuntimeNodeItemType[];
|
||||
runtimeEdges: RuntimeEdgeItemType[];
|
||||
}): Promise<DispatchFlowResponse> {
|
||||
};
|
||||
|
||||
/* running */
|
||||
export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowResponse> {
|
||||
let {
|
||||
res,
|
||||
runtimeNodes = [],
|
||||
runtimeEdges = [],
|
||||
histories = [],
|
||||
variables = {},
|
||||
user,
|
||||
stream = false,
|
||||
detail = false,
|
||||
...props
|
||||
} = data;
|
||||
|
||||
// set sse response headers
|
||||
if (stream && res) {
|
||||
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
|
||||
@@ -93,7 +99,7 @@ export async function dispatchWorkFlow({
|
||||
}
|
||||
|
||||
variables = {
|
||||
...getSystemVariable({ timezone: user.timezone }),
|
||||
...getSystemVariable(data),
|
||||
...variables
|
||||
};
|
||||
|
||||
@@ -283,7 +289,8 @@ export async function dispatchWorkFlow({
|
||||
node,
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
params
|
||||
params,
|
||||
mode: 'test'
|
||||
};
|
||||
|
||||
// run module
|
||||
@@ -362,7 +369,8 @@ export async function dispatchWorkFlow({
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
||||
mergeAssistantResponseAnswerText(chatAssistantResponse),
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
|
||||
newVariables: removeSystemVariable(variables)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -384,12 +392,34 @@ export function responseStatus({
|
||||
}
|
||||
|
||||
/* get system variable */
|
||||
export function getSystemVariable({ timezone }: { timezone: string }) {
|
||||
export function getSystemVariable({
|
||||
user,
|
||||
appId,
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories = []
|
||||
}: Props) {
|
||||
return {
|
||||
cTime: getSystemTime(timezone)
|
||||
appId,
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
histories,
|
||||
cTime: getSystemTime(user.timezone)
|
||||
};
|
||||
}
|
||||
|
||||
/* remove system variable */
|
||||
const removeSystemVariable = (variables: Record<string, any>) => {
|
||||
const copyVariables = { ...variables };
|
||||
delete copyVariables.appId;
|
||||
delete copyVariables.chatId;
|
||||
delete copyVariables.responseChatItemId;
|
||||
delete copyVariables.histories;
|
||||
delete copyVariables.cTime;
|
||||
|
||||
return copyVariables;
|
||||
};
|
||||
|
||||
/* Merge consecutive text messages into one */
|
||||
export const mergeAssistantResponseAnswerText = (response: AIChatItemValueItemType[]) => {
|
||||
const result: AIChatItemValueItemType[] = [];
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const dispatchSystemConfig = (props: Record<string, any>) => {
|
||||
const { variables } = props;
|
||||
return variables;
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
export type UserChatInputProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
}>;
|
||||
|
||||
export const dispatchSystemConfig = (props: Record<string, any>) => {
|
||||
const { variables } = props as UserChatInputProps;
|
||||
return variables;
|
||||
};
|
||||
@@ -1,15 +1,22 @@
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
export type UserChatInputProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.userChatInput]: string;
|
||||
[NodeInputKeyEnum.inputFiles]: UserChatItemValueItemType['file'][];
|
||||
}>;
|
||||
|
||||
export const dispatchWorkflowStart = (props: Record<string, any>) => {
|
||||
const {
|
||||
variables: { userChatInput },
|
||||
params: { userChatInput: query }
|
||||
query,
|
||||
params: { userChatInput }
|
||||
} = props as UserChatInputProps;
|
||||
|
||||
const { text, files } = chatValue2RuntimePrompt(query);
|
||||
|
||||
return {
|
||||
userChatInput: query || userChatInput
|
||||
[NodeInputKeyEnum.userChatInput]: text || userChatInput,
|
||||
[NodeInputKeyEnum.inputFiles]: files
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type/index.d';
|
||||
import { dispatchWorkFlow } from '../index';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getPluginRuntimeById } from '../../../plugin/controller';
|
||||
import { authPluginCanUse } from '../../../../support/permission/auth/plugin';
|
||||
|
||||
@@ -49,6 +49,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
variables,
|
||||
node: { outputs },
|
||||
histories,
|
||||
isToolCall,
|
||||
params: {
|
||||
system_httpMethod: httpMethod = 'POST',
|
||||
system_httpReqUrl: httpReqUrl,
|
||||
@@ -156,17 +157,21 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
};
|
||||
} catch (error) {
|
||||
addLog.error('Http request error', error);
|
||||
return {
|
||||
[NodeOutputKeyEnum.failed]: true,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
params: Object.keys(params).length > 0 ? params : undefined,
|
||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
httpResult: { error: formatHttpError(error) }
|
||||
},
|
||||
[NodeOutputKeyEnum.httpRawResponse]: getErrText(error)
|
||||
};
|
||||
|
||||
if (isToolCall) {
|
||||
return {
|
||||
[NodeOutputKeyEnum.failed]: true,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
params: Object.keys(params).length > 0 ? params : undefined,
|
||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
httpResult: { error: formatHttpError(error) }
|
||||
},
|
||||
[NodeOutputKeyEnum.httpRawResponse]: getErrText(error)
|
||||
};
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
stream,
|
||||
detail,
|
||||
histories,
|
||||
inputFiles,
|
||||
query,
|
||||
params: { userChatInput, history, app }
|
||||
} = props;
|
||||
let start = Date.now();
|
||||
@@ -71,7 +71,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(appData.edges),
|
||||
histories: chatHistories,
|
||||
inputFiles,
|
||||
query,
|
||||
variables: {
|
||||
...props.variables,
|
||||
userChatInput
|
||||
@@ -81,10 +81,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
const completeMessages = chatHistories.concat([
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: runtimePrompt2ChatsValue({
|
||||
files: inputFiles,
|
||||
text: userChatInput
|
||||
})
|
||||
value: query
|
||||
},
|
||||
{
|
||||
obj: ChatRoleEnum.AI,
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import {
|
||||
IfElseResultEnum,
|
||||
VariableConditionEnum
|
||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import {
|
||||
ConditionListItemType,
|
||||
IfElseConditionType,
|
||||
IfElseListItemType
|
||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
||||
[NodeInputKeyEnum.ifElseList]: IfElseListItemType[];
|
||||
}>;
|
||||
type Response = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.ifElseResult]: string;
|
||||
}>;
|
||||
|
||||
function checkCondition(condition: VariableConditionEnum, variableValue: any, value: string) {
|
||||
const operations = {
|
||||
@@ -41,15 +48,13 @@ function checkCondition(condition: VariableConditionEnum, variableValue: any, va
|
||||
return (operations[condition] || (() => false))();
|
||||
}
|
||||
|
||||
export const dispatchIfElse = async (props: Props): Promise<DispatchNodeResultType<{}>> => {
|
||||
const {
|
||||
params,
|
||||
runtimeNodes,
|
||||
variables,
|
||||
node: { nodeId }
|
||||
} = props;
|
||||
const { condition, ifElseList } = params;
|
||||
const listResult = ifElseList.map((item) => {
|
||||
function getResult(
|
||||
condition: IfElseConditionType,
|
||||
list: ConditionListItemType[],
|
||||
variables: any,
|
||||
runtimeNodes: any[]
|
||||
) {
|
||||
const listResult = list.map((item) => {
|
||||
const { variable, condition: variableCondition, value } = item;
|
||||
|
||||
const variableValue = getReferenceVariableValue({
|
||||
@@ -61,15 +66,41 @@ export const dispatchIfElse = async (props: Props): Promise<DispatchNodeResultTy
|
||||
return checkCondition(variableCondition as VariableConditionEnum, variableValue, value || '');
|
||||
});
|
||||
|
||||
const result = condition === 'AND' ? listResult.every(Boolean) : listResult.some(Boolean);
|
||||
return condition === 'AND' ? listResult.every(Boolean) : listResult.some(Boolean);
|
||||
}
|
||||
|
||||
export const dispatchIfElse = async (props: Props): Promise<Response> => {
|
||||
const {
|
||||
params,
|
||||
runtimeNodes,
|
||||
variables,
|
||||
node: { nodeId }
|
||||
} = props;
|
||||
const { ifElseList } = params;
|
||||
|
||||
let res = IfElseResultEnum.ELSE as string;
|
||||
for (let i = 0; i < ifElseList.length; i++) {
|
||||
const item = ifElseList[i];
|
||||
const result = getResult(item.condition, item.list, variables, runtimeNodes);
|
||||
if (result) {
|
||||
res = getElseIFLabel(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const resArray = Array.from({ length: ifElseList.length + 1 }, (_, index) => {
|
||||
const label = index < ifElseList.length ? getElseIFLabel(index) : IfElseResultEnum.ELSE;
|
||||
return getHandleId(nodeId, 'source', label);
|
||||
});
|
||||
|
||||
return {
|
||||
[NodeOutputKeyEnum.ifElseResult]: res,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
ifElseResult: result ? 'IF' : 'ELSE'
|
||||
ifElseResult: res
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]: result
|
||||
? [getHandleId(nodeId, 'source', 'ELSE')]
|
||||
: [getHandleId(nodeId, 'source', 'IF')]
|
||||
[DispatchNodeResponseKeyEnum.skipHandleId]: resArray.filter(
|
||||
(item) => item !== getHandleId(nodeId, 'source', res)
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { NodeInputKeyEnum, VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type';
|
||||
import { valueTypeFormat } from '../utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
||||
}>;
|
||||
|
||||
export const dispatchUpdateVariable = async (
|
||||
props: Props
|
||||
): Promise<DispatchNodeResultType<any>> => {
|
||||
const { params, variables, runtimeNodes } = props;
|
||||
|
||||
const { updateList } = params;
|
||||
updateList.forEach((item) => {
|
||||
const varNodeId = item.variable?.[0];
|
||||
const varKey = item.variable?.[1];
|
||||
|
||||
if (!varNodeId || !varKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = (() => {
|
||||
if (!item.value?.[0]) {
|
||||
return valueTypeFormat(item.value?.[1], item.valueType);
|
||||
} else {
|
||||
return getReferenceVariableValue({
|
||||
value: item.value,
|
||||
variables,
|
||||
nodes: runtimeNodes
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
if (varNodeId === VARIABLE_NODE_ID) {
|
||||
// update global variable
|
||||
variables[varKey] = value;
|
||||
} else {
|
||||
runtimeNodes
|
||||
.find((node) => node.nodeId === varNodeId)
|
||||
?.outputs?.find((output) => {
|
||||
if (output.id === varKey) {
|
||||
output.value = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -19,4 +19,5 @@ export type DispatchFlowResponse = {
|
||||
};
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
newVariables: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ export const filterToolNodeIdByEdges = ({
|
||||
|
||||
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
|
||||
if (!history) return [];
|
||||
if (typeof history === 'number') return histories.slice(-history);
|
||||
if (typeof history === 'number') return histories.slice(-(history * 2));
|
||||
if (Array.isArray(history)) return history;
|
||||
|
||||
return [];
|
||||
|
||||
@@ -279,7 +279,7 @@ export async function dispatchWorkFlowV1({
|
||||
)?.targets?.length;
|
||||
|
||||
return moduleOutput(module, {
|
||||
[NodeOutputKeyEnum.finish]: true,
|
||||
finish: true,
|
||||
[NodeOutputKeyEnum.userChatInput]: hasUserChatInputTarget
|
||||
? params[NodeOutputKeyEnum.userChatInput]
|
||||
: undefined,
|
||||
@@ -295,7 +295,7 @@ export async function dispatchWorkFlowV1({
|
||||
modules.forEach((item) => {
|
||||
item.isEntry = false;
|
||||
});
|
||||
|
||||
// console.log(JSON.stringify(runningModules, null, 2));
|
||||
initModules.map((module) =>
|
||||
moduleInput(module, {
|
||||
...startParams,
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/type';
|
||||
import { dispatchWorkFlowV1 } from '../index';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
FlowNodeTemplateTypeEnum,
|
||||
NodeInputKeyEnum
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { getPluginRuntimeById } from '../../../plugin/controller';
|
||||
import { splitCombinePluginId } from '../../../plugin/controller';
|
||||
import { authPluginCanUse } from '../../../../support/permission/auth/plugin';
|
||||
import { setEntryEntries, DYNAMIC_INPUT_KEY } from '../utils';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { PluginRuntimeType, PluginTemplateType } from '@fastgpt/global/core/plugin/type';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { MongoPlugin } from '../../../plugin/schema';
|
||||
|
||||
type RunPluginProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.pluginId]: string;
|
||||
@@ -15,6 +22,45 @@ type RunPluginProps = ModuleDispatchProps<{
|
||||
}>;
|
||||
type RunPluginResponse = DispatchNodeResultType<{}>;
|
||||
|
||||
const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> => {
|
||||
const { source, pluginId } = await splitCombinePluginId(id);
|
||||
if (source === PluginSourceEnum.community) {
|
||||
const item = global.communityPluginsV1?.find((plugin) => plugin.id === pluginId);
|
||||
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
|
||||
return item;
|
||||
}
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
const item = await MongoPlugin.findById(id).lean();
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
return {
|
||||
id: String(item._id),
|
||||
teamId: String(item.teamId),
|
||||
name: item.name,
|
||||
avatar: item.avatar,
|
||||
intro: item.intro,
|
||||
showStatus: true,
|
||||
source: PluginSourceEnum.personal,
|
||||
modules: item.modules,
|
||||
templateType: FlowNodeTemplateTypeEnum.personalPlugin
|
||||
};
|
||||
}
|
||||
return Promise.reject('plugin not found');
|
||||
};
|
||||
|
||||
const getPluginRuntimeById = async (id: string): Promise<PluginRuntimeType> => {
|
||||
const plugin = await getPluginTemplateById(id);
|
||||
|
||||
return {
|
||||
teamId: plugin.teamId,
|
||||
name: plugin.name,
|
||||
avatar: plugin.avatar,
|
||||
showStatus: plugin.showStatus,
|
||||
modules: plugin.modules
|
||||
};
|
||||
};
|
||||
|
||||
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
|
||||
const {
|
||||
mode,
|
||||
@@ -31,7 +77,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
const plugin = await getPluginRuntimeById(pluginId);
|
||||
|
||||
// concat dynamic inputs
|
||||
const inputModule = plugin.nodes.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
||||
const inputModule = plugin.modules.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
||||
if (!inputModule) return Promise.reject('Plugin error, It has no set input.');
|
||||
const hasDynamicInput = inputModule.inputs.find((input) => input.key === DYNAMIC_INPUT_KEY);
|
||||
|
||||
@@ -56,7 +102,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
|
||||
const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlowV1({
|
||||
...props,
|
||||
modules: setEntryEntries(plugin.nodes).map((module) => ({
|
||||
modules: setEntryEntries(plugin.modules).map((module) => ({
|
||||
...module,
|
||||
showStatus: false
|
||||
})),
|
||||
|
||||
@@ -32,6 +32,6 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
|
||||
}
|
||||
|
||||
return {
|
||||
[NodeOutputKeyEnum.answerText]: formatText
|
||||
[NodeOutputKeyEnum.answerText]: `\n${formatText}`
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,4 +12,5 @@ export type DispatchFlowResponse = {
|
||||
flowUsages: ChatNodeUsageType[];
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
newVariables: Record<string, string>;
|
||||
};
|
||||
|
||||
@@ -22,7 +22,10 @@ export async function getUserDetail({
|
||||
}): Promise<UserType> {
|
||||
const tmb = await (async () => {
|
||||
if (tmbId) {
|
||||
return getTmbInfoByTmbId({ tmbId });
|
||||
try {
|
||||
const result = await getTmbInfoByTmbId({ tmbId });
|
||||
return result;
|
||||
} catch (error) {}
|
||||
}
|
||||
if (userId) {
|
||||
return getUserDefaultTeam({ userId });
|
||||
|
||||
14
packages/web/components/common/DndDrag/DragIcon.tsx
Normal file
14
packages/web/components/common/DndDrag/DragIcon.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { DragHandleIcon } from '@chakra-ui/icons';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { DraggableProvided } from 'react-beautiful-dnd';
|
||||
|
||||
const DragIcon = ({ provided }: { provided: DraggableProvided }) => {
|
||||
return (
|
||||
<Box {...provided.dragHandleProps}>
|
||||
<DragHandleIcon color={'myGray.500'} _hover={{ color: 'primary.600' }} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DragIcon;
|
||||
61
packages/web/components/common/DndDrag/index.tsx
Normal file
61
packages/web/components/common/DndDrag/index.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
DragDropContext,
|
||||
DroppableProps,
|
||||
Droppable,
|
||||
DraggableChildrenFn,
|
||||
DragStart,
|
||||
DropResult
|
||||
} from 'react-beautiful-dnd';
|
||||
|
||||
type Props<T = any> = {
|
||||
onDragEndCb: (result: T[]) => void;
|
||||
renderClone?: DraggableChildrenFn;
|
||||
children: DroppableProps['children'];
|
||||
dataList: T[];
|
||||
};
|
||||
|
||||
function DndDrag<T>({ children, renderClone, onDragEndCb, dataList }: Props<T>) {
|
||||
const [draggingItemHeight, setDraggingItemHeight] = useState(0);
|
||||
|
||||
const onDragStart = (start: DragStart) => {
|
||||
const draggingNode = document.querySelector(`[data-rbd-draggable-id="${start.draggableId}"]`);
|
||||
setDraggingItemHeight(draggingNode?.getBoundingClientRect().height || 0);
|
||||
};
|
||||
|
||||
const onDragEnd = (result: DropResult) => {
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
setDraggingItemHeight(0);
|
||||
|
||||
const startIndex = result.source.index;
|
||||
const endIndex = result.destination.index;
|
||||
|
||||
const list = Array.from(dataList);
|
||||
const [removed] = list.splice(startIndex, 1);
|
||||
list.splice(endIndex, 0, removed);
|
||||
|
||||
onDragEndCb(list);
|
||||
};
|
||||
|
||||
return (
|
||||
<DragDropContext onDragStart={onDragStart} onDragEnd={onDragEnd}>
|
||||
<Droppable droppableId="droppable" renderClone={renderClone}>
|
||||
{(provided, snapshot) => {
|
||||
return (
|
||||
<Box {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{children(provided, snapshot)}
|
||||
{snapshot.isDraggingOver && <Box height={draggingItemHeight} />}
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
}
|
||||
|
||||
export default DndDrag;
|
||||
|
||||
export * from 'react-beautiful-dnd';
|
||||
@@ -8,13 +8,15 @@ export const useBeforeunload = (props?: { callback?: () => any; tip?: string })
|
||||
|
||||
useEffect(() => {
|
||||
const listen =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
process.env.NODE_ENV === 'production'
|
||||
? (e: any) => {
|
||||
e.preventDefault();
|
||||
e.returnValue = tip;
|
||||
callback?.();
|
||||
}
|
||||
: () => {};
|
||||
: () => {
|
||||
callback?.();
|
||||
};
|
||||
window.addEventListener('beforeunload', listen);
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -92,9 +92,9 @@ export const useEditTextarea = ({
|
||||
closeBtnText?: string;
|
||||
}) => (
|
||||
<MyModal isOpen={isOpen} onClose={onClose} iconSrc={iconSrc} title={title} maxW={'500px'}>
|
||||
<ModalBody>
|
||||
<ModalBody pt={tip ? '3 !important' : '5 !important'}>
|
||||
{!!tip && (
|
||||
<Box mb={2} color={'myGray.500'} fontSize={'sm'}>
|
||||
<Box mb={3} color={'myGray.500'} fontSize={'sm'}>
|
||||
{tip}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -31,12 +31,14 @@
|
||||
"react": "18.2.0",
|
||||
"react-day-picker": "^8.7.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-i18next": "13.5.0"
|
||||
"react-i18next": "13.5.0",
|
||||
"react-beautiful-dnd": "^13.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/papaparse": "^5.3.7",
|
||||
"@types/react": "18.2.0",
|
||||
"@types/react-dom": "18.2.0"
|
||||
"@types/react-dom": "18.2.0",
|
||||
"@types/react-beautiful-dnd": "^13.1.8"
|
||||
}
|
||||
}
|
||||
|
||||
120
pnpm-lock.yaml
generated
120
pnpm-lock.yaml
generated
@@ -292,6 +292,9 @@ importers:
|
||||
react:
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0
|
||||
react-beautiful-dnd:
|
||||
specifier: ^13.1.1
|
||||
version: 13.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||
react-day-picker:
|
||||
specifier: ^8.7.1
|
||||
version: 8.7.1(date-fns@2.30.0)(react@18.2.0)
|
||||
@@ -311,6 +314,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0
|
||||
'@types/react-beautiful-dnd':
|
||||
specifier: ^13.1.8
|
||||
version: 13.1.8
|
||||
'@types/react-dom':
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0
|
||||
@@ -3065,6 +3071,7 @@ packages:
|
||||
|
||||
/@emotion/memoize@0.7.4:
|
||||
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -3682,6 +3689,7 @@ packages:
|
||||
/@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13):
|
||||
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
detect-libc: 2.0.3
|
||||
https-proxy-agent: 5.0.1
|
||||
@@ -4741,12 +4749,27 @@ packages:
|
||||
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
||||
dev: true
|
||||
|
||||
/@types/react-beautiful-dnd@13.1.8:
|
||||
resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
|
||||
dependencies:
|
||||
'@types/react': 18.2.0
|
||||
dev: true
|
||||
|
||||
/@types/react-dom@18.2.0:
|
||||
resolution: {integrity: sha512-8yQrvS6sMpSwIovhPOwfyNf2Wz6v/B62LFSVYQ85+Rq3tLsBIG7rP5geMxaijTUxSkrO6RzN/IRuIAADYQsleA==}
|
||||
dependencies:
|
||||
'@types/react': 18.2.0
|
||||
dev: true
|
||||
|
||||
/@types/react-redux@7.1.33:
|
||||
resolution: {integrity: sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==}
|
||||
dependencies:
|
||||
'@types/hoist-non-react-statics': 3.3.5
|
||||
'@types/react': 18.2.0
|
||||
hoist-non-react-statics: 3.3.2
|
||||
redux: 4.2.1
|
||||
dev: false
|
||||
|
||||
/@types/react-syntax-highlighter@15.5.6:
|
||||
resolution: {integrity: sha512-i7wFuLbIAFlabTeD2I1cLjEOrG/xdMa/rpx2zwzAoGHuXJDhSqp9BSfDlMHSh9JSuNfxHk9eEmMX6D55GiyjGg==}
|
||||
dependencies:
|
||||
@@ -5005,6 +5028,7 @@ packages:
|
||||
|
||||
/abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -5047,6 +5071,7 @@ packages:
|
||||
/agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
@@ -5170,12 +5195,14 @@ packages:
|
||||
|
||||
/aproba@2.0.0:
|
||||
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/are-we-there-yet@2.0.0:
|
||||
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
delegates: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
@@ -5791,6 +5818,7 @@ packages:
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -5903,6 +5931,7 @@ packages:
|
||||
/color-support@1.1.3:
|
||||
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -5975,6 +6004,7 @@ packages:
|
||||
|
||||
/console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -6540,6 +6570,7 @@ packages:
|
||||
/decompress-response@4.2.1:
|
||||
resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
mimic-response: 2.1.0
|
||||
dev: false
|
||||
@@ -6644,6 +6675,7 @@ packages:
|
||||
|
||||
/delegates@1.0.0:
|
||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -6671,6 +6703,7 @@ packages:
|
||||
/detect-libc@2.0.3:
|
||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -7877,6 +7910,7 @@ packages:
|
||||
/fs-minipass@2.1.0:
|
||||
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
|
||||
engines: {node: '>= 8'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
dev: false
|
||||
@@ -7912,6 +7946,7 @@ packages:
|
||||
/gauge@3.0.2:
|
||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
aproba: 2.0.0
|
||||
color-support: 1.1.3
|
||||
@@ -8089,6 +8124,7 @@ packages:
|
||||
|
||||
/has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -8246,6 +8282,7 @@ packages:
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.4
|
||||
@@ -9088,6 +9125,7 @@ packages:
|
||||
/make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
semver: 6.3.1
|
||||
dev: false
|
||||
@@ -9289,8 +9327,13 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
dev: false
|
||||
|
||||
/memory-pager@1.5.0:
|
||||
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -9647,6 +9690,7 @@ packages:
|
||||
/mimic-response@2.1.0:
|
||||
resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -9669,6 +9713,7 @@ packages:
|
||||
/minipass@3.3.6:
|
||||
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
@@ -9677,12 +9722,14 @@ packages:
|
||||
/minipass@5.0.0:
|
||||
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
|
||||
engines: {node: '>=8'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/minizlib@2.1.2:
|
||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||
engines: {node: '>= 8'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
yallist: 4.0.0
|
||||
@@ -9700,6 +9747,7 @@ packages:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -9981,6 +10029,7 @@ packages:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
abbrev: 1.1.1
|
||||
dev: false
|
||||
@@ -9999,6 +10048,7 @@ packages:
|
||||
|
||||
/npmlog@5.0.1:
|
||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
are-we-there-yet: 2.0.0
|
||||
console-control-strings: 1.1.0
|
||||
@@ -10309,6 +10359,7 @@ packages:
|
||||
/path2d@0.1.1:
|
||||
resolution: {integrity: sha512-/+S03c8AGsDYKKBtRDqieTJv2GlkMb0bWjnqOgtF6MkjdUQ9a8ARAtxWf9NgKLGm2+WQr6+/tqJdU8HNGsIDoA==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -10587,6 +10638,10 @@ packages:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
dev: true
|
||||
|
||||
/raf-schd@4.0.3:
|
||||
resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
|
||||
dev: false
|
||||
|
||||
/randombytes@2.1.0:
|
||||
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
|
||||
dependencies:
|
||||
@@ -10614,6 +10669,25 @@ packages:
|
||||
unpipe: 1.0.0
|
||||
dev: false
|
||||
|
||||
/react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.1
|
||||
css-box-model: 1.2.1
|
||||
memoize-one: 5.2.1
|
||||
raf-schd: 4.0.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0)
|
||||
redux: 4.2.1
|
||||
use-memo-one: 1.1.3(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- react-native
|
||||
dev: false
|
||||
|
||||
/react-clientside-effect@1.2.6(react@18.2.0):
|
||||
resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==}
|
||||
peerDependencies:
|
||||
@@ -10706,6 +10780,10 @@ packages:
|
||||
/react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
/react-is@17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
dev: false
|
||||
|
||||
/react-is@18.2.0:
|
||||
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
|
||||
dev: false
|
||||
@@ -10737,6 +10815,28 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.3 || ^17 || ^18
|
||||
react-dom: '*'
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.1
|
||||
'@types/react-redux': 7.1.33
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-is: 17.0.2
|
||||
dev: false
|
||||
|
||||
/react-remove-scroll-bar@2.3.6(@types/react@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -10858,6 +10958,12 @@ packages:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
|
||||
/redux@4.2.1:
|
||||
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.1
|
||||
dev: false
|
||||
|
||||
/reflect.getprototypeof@1.0.6:
|
||||
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -11262,6 +11368,7 @@ packages:
|
||||
|
||||
/set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
@@ -11331,11 +11438,13 @@ packages:
|
||||
|
||||
/simple-concat@1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/simple-get@3.1.1:
|
||||
resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
decompress-response: 4.2.1
|
||||
once: 1.4.0
|
||||
@@ -11417,6 +11526,7 @@ packages:
|
||||
|
||||
/sparse-bitfield@3.0.3:
|
||||
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
memory-pager: 1.5.0
|
||||
dev: false
|
||||
@@ -11685,6 +11795,7 @@ packages:
|
||||
/tar@6.2.1:
|
||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||
engines: {node: '>=10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
@@ -12182,6 +12293,14 @@ packages:
|
||||
scheduler: 0.23.0
|
||||
dev: false
|
||||
|
||||
/use-memo-one@1.1.3(react@18.2.0):
|
||||
resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/use-sidecar@1.1.2(@types/react@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -12539,6 +12658,7 @@ packages:
|
||||
|
||||
/wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
dev: false
|
||||
|
||||
@@ -7,336 +7,205 @@
|
||||
"showStatus": false,
|
||||
"isTool": false,
|
||||
"weight": 0,
|
||||
"modules": [
|
||||
"nodes": [
|
||||
{
|
||||
"moduleId": "w90mfp",
|
||||
"name": "定义插件输入",
|
||||
"flowType": "pluginInput",
|
||||
"nodeId": "lmpb9v2lo2lk",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 515.1887815471657,
|
||||
"y": -169.04905809653783
|
||||
"x": 616.4226348688949,
|
||||
"y": -165.05298493910115
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "defaultFeedback",
|
||||
"valueType": "string",
|
||||
"label": "默认反馈内容",
|
||||
"type": "textarea",
|
||||
"key": "system_addInputParam",
|
||||
"valueType": "dynamic",
|
||||
"label": "动态外部数据",
|
||||
"renderTypeList": ["addInputParam"],
|
||||
"required": false,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"canEdit": true,
|
||||
"value": "",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
"key": true
|
||||
},
|
||||
"connected": true
|
||||
"dynamicParamDefaultValue": {
|
||||
"inputType": "reference",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"key": "反馈内容",
|
||||
"valueType": "string",
|
||||
"label": "自定义反馈内容",
|
||||
"type": "target",
|
||||
"required": false,
|
||||
"label": "反馈内容",
|
||||
"renderTypeList": ["textarea"],
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"canEdit": true,
|
||||
"value": "",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
"key": true
|
||||
},
|
||||
"connected": true
|
||||
"maxLength": "",
|
||||
"dynamicParamDefaultValue": {
|
||||
"inputType": "reference",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "defaultFeedback",
|
||||
"id": "ILc8GS7iU53M",
|
||||
"key": "反馈内容",
|
||||
"valueType": "string",
|
||||
"label": "默认反馈内容",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "defaultFeedback"
|
||||
}
|
||||
]
|
||||
"label": "反馈内容",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"valueType": "string",
|
||||
"label": "自定义反馈内容",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "customFeedback"
|
||||
}
|
||||
]
|
||||
"id": "2LCxDnOSculb",
|
||||
"key": "system_addInputParam",
|
||||
"valueType": "dynamic",
|
||||
"label": "动态外部数据",
|
||||
"type": "static"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"name": "HTTP模块",
|
||||
"flowType": "httpRequest468",
|
||||
"nodeId": "i7uow4wj2wdp",
|
||||
"name": "自定义插件输出",
|
||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||
"avatar": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1607.7142331269126,
|
||||
"y": -151.8669210746189
|
||||
},
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "CRT7oIEU8v2P",
|
||||
"name": "HTTP 请求",
|
||||
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
|
||||
"avatar": "/imgs/workflow/http.png",
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1086.8929621216014,
|
||||
"y": -451.7550009773506
|
||||
"x": 1070.8458389994719,
|
||||
"y": -415.09022555407836
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"type": "custom",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"list": [
|
||||
{
|
||||
"label": "GET",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"label": "POST",
|
||||
"value": "POST"
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"type": "hidden",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"value": "/api/plugins/customFeedback",
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"type": "custom",
|
||||
"valueType": "any",
|
||||
"value": "",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"key": "system_addInputParam",
|
||||
"renderTypeList": ["addInputParam"],
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"appId\": \"{{appId}}\",\r\n \"chatId\": \"{{chatId}}\",\r\n \"responseChatItemId\": \"{{responseChatItemId}}\",\r\n \"defaultFeedback\": \"{{defaultFeedback}}\",\r\n \"customFeedback\": \"{{customFeedback}}\"\r\n}",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"type": "target",
|
||||
"valueType": "any",
|
||||
"label": "core.workflow.inputType.dynamicTargetInput",
|
||||
"description": "core.module.input.description.dynamic input",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": true,
|
||||
"hideInApp": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"label": "defaultFeedback",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"description": "core.module.input.description.HTTP Dynamic Input",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
"valueType": true
|
||||
},
|
||||
"connected": true,
|
||||
"key": "defaultFeedback"
|
||||
"value": ["lmpb9v2lo2lk", "2LCxDnOSculb"]
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"valueType": "string",
|
||||
"label": "customFeedback",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"renderTypeList": ["reference"],
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
"valueType": true
|
||||
},
|
||||
"connected": true
|
||||
"value": ["lmpb9v2lo2lk", "ILc8GS7iU53M"]
|
||||
},
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"type": "addInputParam",
|
||||
"valueType": "any",
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": false
|
||||
"value": "/api/plugins/customFeedback/v2"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"customFeedback\":\"{{customFeedback}}\",\r\n \"system_addInputParam\": {{system_addInputParam}}\r\n}",
|
||||
"label": "",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
},
|
||||
{
|
||||
"id": "system_addOutputParam",
|
||||
"key": "system_addOutputParam",
|
||||
"type": "addOutputParam",
|
||||
"valueType": "any",
|
||||
"type": "dynamic",
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"targets": [],
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"outputType": "source",
|
||||
"valueType": "string"
|
||||
"valueType": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"valueType": "string",
|
||||
"label": "response",
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "s15f3v",
|
||||
"key": "text"
|
||||
}
|
||||
],
|
||||
"key": "response"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "s15f3v",
|
||||
"name": "指定回复",
|
||||
"flowType": "answerNode",
|
||||
"position": {
|
||||
"x": 1705.6337348182756,
|
||||
"y": -37.53826066726282
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"label": "原始响应",
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "text",
|
||||
"type": "textarea",
|
||||
"valueType": "any",
|
||||
"label": "core.module.input.label.Response content",
|
||||
"description": "core.module.input.description.Response content",
|
||||
"placeholder": "core.module.input.description.Response content",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
"type": "static"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "lmpb9v2lo2lk",
|
||||
"target": "CRT7oIEU8v2P",
|
||||
"sourceHandle": "lmpb9v2lo2lk-source-right",
|
||||
"targetHandle": "CRT7oIEU8v2P-target-left"
|
||||
},
|
||||
{
|
||||
"source": "CRT7oIEU8v2P",
|
||||
"target": "i7uow4wj2wdp",
|
||||
"sourceHandle": "CRT7oIEU8v2P-source-right",
|
||||
"targetHandle": "i7uow4wj2wdp-target-left"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "lmpb9v2lo2lk",
|
||||
"name": "定义插件输入",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
{
|
||||
"nodeId": "i7uow4wj2wdp",
|
||||
"name": "定义插件输出",
|
||||
"name": "自定义插件输出",
|
||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||
"avatar": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "lmpb9v2lo2lk",
|
||||
"name": "定义插件输入",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
@@ -77,7 +77,7 @@
|
||||
},
|
||||
{
|
||||
"nodeId": "i7uow4wj2wdp",
|
||||
"name": "定义插件输出",
|
||||
"name": "自定义插件输出",
|
||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||
"avatar": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
@@ -167,7 +167,7 @@
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "/api/plugins/textEditor"
|
||||
"value": "/api/plugins/textEditor/v2"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
|
||||
342
projects/app/data/pluginTemplates/v1/customFeedback.json
Normal file
342
projects/app/data/pluginTemplates/v1/customFeedback.json
Normal file
@@ -0,0 +1,342 @@
|
||||
{
|
||||
"author": "FastGPT Team",
|
||||
"templateType": "other",
|
||||
"name": "自定义反馈",
|
||||
"avatar": "/imgs/module/customFeedback.svg",
|
||||
"intro": "该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。",
|
||||
"showStatus": false,
|
||||
"isTool": false,
|
||||
"weight": 0,
|
||||
"modules": [
|
||||
{
|
||||
"moduleId": "w90mfp",
|
||||
"name": "自定义插件输入",
|
||||
"flowType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 515.1887815471657,
|
||||
"y": -169.04905809653783
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "defaultFeedback",
|
||||
"valueType": "string",
|
||||
"label": "默认反馈内容",
|
||||
"type": "textarea",
|
||||
"required": false,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"valueType": "string",
|
||||
"label": "自定义反馈内容",
|
||||
"type": "target",
|
||||
"required": false,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "defaultFeedback",
|
||||
"valueType": "string",
|
||||
"label": "默认反馈内容",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "defaultFeedback"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"valueType": "string",
|
||||
"label": "自定义反馈内容",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "customFeedback"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"name": "HTTP模块",
|
||||
"flowType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1086.8929621216014,
|
||||
"y": -451.7550009773506
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"type": "custom",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"list": [
|
||||
{
|
||||
"label": "GET",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"label": "POST",
|
||||
"value": "POST"
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"type": "hidden",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"value": "/api/plugins/customFeedback",
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"type": "custom",
|
||||
"valueType": "any",
|
||||
"value": "",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"appId\": \"{{appId}}\",\r\n \"chatId\": \"{{chatId}}\",\r\n \"responseChatItemId\": \"{{responseChatItemId}}\",\r\n \"defaultFeedback\": \"{{defaultFeedback}}\",\r\n \"customFeedback\": \"{{customFeedback}}\"\r\n}",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"type": "target",
|
||||
"valueType": "any",
|
||||
"label": "core.module.inputType.dynamicTargetInput",
|
||||
"description": "core.module.input.description.dynamic input",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": true,
|
||||
"hideInApp": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"label": "defaultFeedback",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"connected": true,
|
||||
"key": "defaultFeedback"
|
||||
},
|
||||
{
|
||||
"key": "customFeedback",
|
||||
"valueType": "string",
|
||||
"label": "customFeedback",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"type": "addInputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
},
|
||||
{
|
||||
"key": "system_addOutputParam",
|
||||
"type": "addOutputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"targets": [],
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"outputType": "source",
|
||||
"valueType": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"valueType": "string",
|
||||
"label": "response",
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "s15f3v",
|
||||
"key": "text"
|
||||
}
|
||||
],
|
||||
"key": "response"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "s15f3v",
|
||||
"name": "指定回复",
|
||||
"flowType": "answerNode",
|
||||
"position": {
|
||||
"x": 1705.6337348182756,
|
||||
"y": -37.53826066726282
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "text",
|
||||
"type": "textarea",
|
||||
"valueType": "any",
|
||||
"label": "core.module.input.label.Response content",
|
||||
"description": "core.module.input.description.Response content",
|
||||
"placeholder": "core.module.input.description.Response content",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
195
projects/app/data/pluginTemplates/v1/getCurrentTime.json
Normal file
195
projects/app/data/pluginTemplates/v1/getCurrentTime.json
Normal file
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"author": "FastGPT Team",
|
||||
"templateType": "tools",
|
||||
"name": "获取当前时间",
|
||||
"avatar": "/imgs/module/getCurrentTime.svg",
|
||||
"intro": "获取用户当前时区的时间。",
|
||||
"showStatus": false,
|
||||
"isTool": true,
|
||||
"weight": 10,
|
||||
"modules": [
|
||||
{
|
||||
"moduleId": "m8dupj",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
|
||||
"avatar": "/imgs/module/input.png",
|
||||
"flowType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 187.94161749205568,
|
||||
"y": 179.78772129776746
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "pluginStart",
|
||||
"type": "hidden",
|
||||
"valueType": "boolean",
|
||||
"label": "插件开始运行",
|
||||
"description": "插件开始运行时,会输出一个 True 的标识。有时候,插件不会有额外的的输入,为了顺利的进入下一个阶段,你可以将该值连接到下一个节点的触发器中。",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "pluginStart",
|
||||
"label": "插件开始运行",
|
||||
"type": "source",
|
||||
"valueType": "boolean",
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "cv13yt",
|
||||
"key": "switch"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "bjsa7r",
|
||||
"name": "自定义插件输出",
|
||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||
"avatar": "/imgs/module/output.png",
|
||||
"flowType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1176.9471084832217,
|
||||
"y": 138.94098316727695
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "time",
|
||||
"valueType": "string",
|
||||
"label": "time",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": false,
|
||||
"dataType": true,
|
||||
"inputType": false
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "time",
|
||||
"valueType": "string",
|
||||
"label": "time",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "cv13yt",
|
||||
"name": "文本加工",
|
||||
"intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",
|
||||
"avatar": "/imgs/module/textEditor.svg",
|
||||
"flowType": "pluginModule",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 600.7190079155914,
|
||||
"y": 1.4754510232677944
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "pluginId",
|
||||
"type": "hidden",
|
||||
"label": "",
|
||||
"value": "community-textEditor",
|
||||
"valueType": "string",
|
||||
"connected": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false
|
||||
},
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "triggerAndFinish",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "textarea",
|
||||
"valueType": "string",
|
||||
"label": "文本内容",
|
||||
"type": "textarea",
|
||||
"required": true,
|
||||
"description": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。",
|
||||
"edit": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
},
|
||||
"connected": false,
|
||||
"placeholder": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。",
|
||||
"value": "{{cTime}}"
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"valueType": "any",
|
||||
"label": "需要加工的输入",
|
||||
"type": "addInputParam",
|
||||
"required": false,
|
||||
"description": "可动态的添加字符串类型变量,在文本编辑中通过 {{key}} 使用变量。非字符串类型,会自动转成字符串类型。",
|
||||
"edit": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": false
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "text",
|
||||
"valueType": "string",
|
||||
"label": "core.module.output.label.text",
|
||||
"type": "source",
|
||||
"edit": false,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "bjsa7r",
|
||||
"key": "time"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "",
|
||||
"description": "",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
331
projects/app/data/pluginTemplates/v1/textEditor.json
Normal file
331
projects/app/data/pluginTemplates/v1/textEditor.json
Normal file
@@ -0,0 +1,331 @@
|
||||
{
|
||||
"author": "FastGPT Team",
|
||||
"templateType": "tools",
|
||||
"name": "文本加工",
|
||||
"avatar": "/imgs/module/textEditor.svg",
|
||||
"intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",
|
||||
"showStatus": false,
|
||||
"isTool": false,
|
||||
"weight": 100,
|
||||
"modules": [
|
||||
{
|
||||
"moduleId": "w90mfp",
|
||||
"name": "自定义插件输入",
|
||||
"flowType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 616.4226348688949,
|
||||
"y": -165.05298493910115
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "textarea",
|
||||
"valueType": "string",
|
||||
"label": "文本内容",
|
||||
"type": "textarea",
|
||||
"required": true,
|
||||
"description": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"valueType": "any",
|
||||
"label": "需要加工的输入",
|
||||
"type": "addInputParam",
|
||||
"required": false,
|
||||
"description": "可动态的添加字符串类型变量,在文本编辑中通过 {{key}} 使用变量。非字符串类型,会自动转成字符串类型。",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": false
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "textarea",
|
||||
"valueType": "string",
|
||||
"label": "文本内容",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"valueType": "any",
|
||||
"label": "需要加工的输入",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"key": "DYNAMIC_INPUT_KEY"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "tze1ju",
|
||||
"name": "自定义插件输出",
|
||||
"flowType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1607.7142331269126,
|
||||
"y": -145.93201540017395
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "text",
|
||||
"valueType": "string",
|
||||
"label": "core.module.output.label.text",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": false,
|
||||
"dataType": true,
|
||||
"inputType": false
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "text",
|
||||
"valueType": "string",
|
||||
"label": "core.module.output.label.text",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "49de3g",
|
||||
"name": "HTTP模块",
|
||||
"flowType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1086.8929621216014,
|
||||
"y": -451.7550009773506
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"type": "custom",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"list": [
|
||||
{
|
||||
"label": "GET",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"label": "POST",
|
||||
"value": "POST"
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"type": "hidden",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"value": "/api/plugins/textEditor",
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"type": "custom",
|
||||
"valueType": "any",
|
||||
"value": "",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"text\": \"{{text}}\"\r\n}",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"type": "target",
|
||||
"valueType": "any",
|
||||
"label": "core.module.inputType.dynamicTargetInput",
|
||||
"description": "core.module.input.description.dynamic input",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": true,
|
||||
"hideInApp": true,
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "text",
|
||||
"valueType": "string",
|
||||
"label": "text",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"type": "addInputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
},
|
||||
{
|
||||
"key": "system_addOutputParam",
|
||||
"type": "addOutputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"targets": [],
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"outputType": "source",
|
||||
"valueType": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"valueType": "string",
|
||||
"key": "text",
|
||||
"label": "core.module.output.label.text",
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "tze1ju",
|
||||
"key": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
369
projects/app/data/pluginTemplates/v1/tfSwitch.json
Normal file
369
projects/app/data/pluginTemplates/v1/tfSwitch.json
Normal file
@@ -0,0 +1,369 @@
|
||||
{
|
||||
"abandon": true,
|
||||
"author": "FastGPT Team",
|
||||
"templateType": "tools",
|
||||
"name": "判断器",
|
||||
"avatar": "/imgs/module/tfSwitch.svg",
|
||||
"intro": "根据传入的内容进行 True False 输出。默认情况下,当传入的内容为 false, undefined, null, 0, none 时,会输出 false。你也可以增加一些自定义的字符串来补充输出 false 的内容。非字符、非数字、非布尔类型,直接输出 True。",
|
||||
"showStatus": false,
|
||||
"isTool": false,
|
||||
"weight": 10,
|
||||
"modules": [
|
||||
{
|
||||
"moduleId": "w90mfp",
|
||||
"name": "自定义插件输入",
|
||||
"flowType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 616.4226348688949,
|
||||
"y": -165.05298493910115
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "input",
|
||||
"valueType": "any",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.TFSwitch input tip",
|
||||
"required": true,
|
||||
"edit": true,
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "rule",
|
||||
"valueType": "string",
|
||||
"label": "core.module.input.label.TFSwitch textarea",
|
||||
"type": "textarea",
|
||||
"required": false,
|
||||
"description": "core.module.input.description.TFSwitch textarea",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true,
|
||||
"inputType": true
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "input",
|
||||
"valueType": "any",
|
||||
"label": "core.module.input.label.TFSwitch input tip",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "8kld99",
|
||||
"key": "input"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "rule",
|
||||
"valueType": "string",
|
||||
"label": "core.module.input.label.TFSwitch textarea",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "8kld99",
|
||||
"key": "rule"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "tze1ju",
|
||||
"name": "自定义插件输出",
|
||||
"flowType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1985.3791673445353,
|
||||
"y": -144.90535546692078
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "true",
|
||||
"type": "target",
|
||||
"valueType": "boolean",
|
||||
"label": "True",
|
||||
"required": true,
|
||||
"edit": true,
|
||||
"connected": true,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"key": "false",
|
||||
"valueType": "boolean",
|
||||
"label": "False",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": false,
|
||||
"dataType": true,
|
||||
"inputType": false
|
||||
},
|
||||
"connected": true
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "true",
|
||||
"valueType": "boolean",
|
||||
"label": "True",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": []
|
||||
},
|
||||
{
|
||||
"key": "false",
|
||||
"valueType": "boolean",
|
||||
"label": "False",
|
||||
"type": "source",
|
||||
"edit": true,
|
||||
"targets": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"moduleId": "8kld99",
|
||||
"name": "HTTP模块",
|
||||
"flowType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1210.560012858087,
|
||||
"y": -387.62433050951756
|
||||
},
|
||||
"inputs": [
|
||||
{
|
||||
"key": "switch",
|
||||
"type": "target",
|
||||
"label": "core.module.input.label.switch",
|
||||
"description": "core.module.input.description.Trigger",
|
||||
"valueType": "any",
|
||||
"showTargetInApp": true,
|
||||
"showTargetInPlugin": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"type": "custom",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"list": [
|
||||
{
|
||||
"label": "GET",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"label": "POST",
|
||||
"value": "POST"
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"type": "hidden",
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"value": "/api/plugins/TFSwitch",
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"type": "custom",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"type": "hidden",
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"input\": \"{{input}}\",\r\n \"rule\": \"{{rule}}\"\r\n}",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "DYNAMIC_INPUT_KEY",
|
||||
"type": "target",
|
||||
"valueType": "any",
|
||||
"label": "core.module.inputType.dynamicTargetInput",
|
||||
"description": "core.module.input.description.dynamic input",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": true,
|
||||
"hideInApp": true,
|
||||
"connected": false
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"valueType": "any",
|
||||
"label": "input",
|
||||
"type": "target",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "rule",
|
||||
"valueType": "string",
|
||||
"label": "rule",
|
||||
"type": "target",
|
||||
"required": false,
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"connected": true
|
||||
},
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"type": "addInputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"showTargetInApp": false,
|
||||
"showTargetInPlugin": false,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"required": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"inputType": "target",
|
||||
"valueType": "string",
|
||||
"required": true
|
||||
},
|
||||
"connected": false
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"key": "finish",
|
||||
"label": "core.module.output.label.running done",
|
||||
"description": "core.module.output.description.running done",
|
||||
"valueType": "boolean",
|
||||
"type": "hidden",
|
||||
"targets": []
|
||||
},
|
||||
{
|
||||
"key": "system_addOutputParam",
|
||||
"type": "addOutputParam",
|
||||
"valueType": "any",
|
||||
"label": "",
|
||||
"targets": [],
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"defaultEditField": {
|
||||
"label": "",
|
||||
"key": "",
|
||||
"description": "",
|
||||
"outputType": "source",
|
||||
"valueType": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"valueType": "boolean",
|
||||
"key": "true",
|
||||
"label": "true",
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "tze1ju",
|
||||
"key": "true"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "source",
|
||||
"valueType": "boolean",
|
||||
"key": "false",
|
||||
"label": "false",
|
||||
"description": "",
|
||||
"edit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"name": true,
|
||||
"description": true,
|
||||
"dataType": true
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"moduleId": "tze1ju",
|
||||
"key": "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"App": "应用",
|
||||
"Create New": "",
|
||||
"Export": "导出",
|
||||
"Folder": "文件夹",
|
||||
"Move": "移动",
|
||||
"Name": "名称",
|
||||
"Rename": "重命名",
|
||||
"Running": "运行中",
|
||||
"Select value is empty": "选择的内容为空",
|
||||
"UnKnow": "未知",
|
||||
"Warning": "提示",
|
||||
"app": {
|
||||
@@ -646,8 +644,7 @@
|
||||
"success": "开始同步"
|
||||
}
|
||||
},
|
||||
"training": {
|
||||
}
|
||||
"training": {}
|
||||
},
|
||||
"data": {
|
||||
"Auxiliary Data": "辅助数据",
|
||||
@@ -922,7 +919,7 @@
|
||||
"AppId": "应用的ID",
|
||||
"ChatId": "当前对话ID",
|
||||
"Current time": "当前时间",
|
||||
"Histories": "历史记录,最多取10条",
|
||||
"Histories": "最近10条聊天记录",
|
||||
"Key already exists": "Key 已经存在",
|
||||
"Key cannot be empty": "参数名不能为空",
|
||||
"Props name": "参数名",
|
||||
@@ -939,6 +936,7 @@
|
||||
"Add Input": "添加入参",
|
||||
"Input Number": "入参: {{length}}",
|
||||
"add": "添加条件",
|
||||
"Add Branch": "添加分支",
|
||||
"description": {
|
||||
"Background": "你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。",
|
||||
"HTTP Dynamic Input": "接收前方节点的输出值作为变量,这些变量可以被HTTP请求参数使用。",
|
||||
@@ -975,6 +973,17 @@
|
||||
"Classify background": "例如: \n1. AIGC(人工智能生成内容)是指使用人工智能技术自动或半自动地生成数字内容,如文本、图像、音乐、视频等。\n2. AIGC技术包括但不限于自然语言处理、计算机视觉、机器学习和深度学习。这些技术可以创建新内容或修改现有内容,以满足特定的创意、教育、娱乐或信息需求。"
|
||||
}
|
||||
},
|
||||
"inputType": {
|
||||
"chat history": "",
|
||||
"dynamicTargetInput": "",
|
||||
"input": "",
|
||||
"selectApp": "",
|
||||
"selectDataset": "",
|
||||
"selectLLMModel": "",
|
||||
"switch": "",
|
||||
"target": "",
|
||||
"textarea": ""
|
||||
},
|
||||
"laf": {
|
||||
"Select laf function": "选择laf函数"
|
||||
},
|
||||
@@ -1069,7 +1078,7 @@
|
||||
"tools": "工具调用"
|
||||
},
|
||||
"variable": {
|
||||
"External type": "外部传入",
|
||||
"Custom type": "自定义变量",
|
||||
"add option": "添加选项",
|
||||
"input type": "文本",
|
||||
"key": "变量 key",
|
||||
@@ -1120,6 +1129,9 @@
|
||||
"Stop debug": "停止调试",
|
||||
"Success": "运行成功",
|
||||
"Value type": "数据类型",
|
||||
"Variable": {
|
||||
"Variable type": "变量类型"
|
||||
},
|
||||
"Variable outputs": "全局变量",
|
||||
"chat": {
|
||||
"Quote prompt": "引用提示词"
|
||||
@@ -1154,13 +1166,15 @@
|
||||
},
|
||||
"publish": {
|
||||
"OnRevert version": "点击回退到该版本",
|
||||
"OnRevert version confirm": "确认回退该版本?",
|
||||
"OnRevert version confirm": "确认回退至该版本?会为您保存编辑中版本的配置,并为回退版本创建一个新的发布版本。",
|
||||
"histories": "发布记录"
|
||||
},
|
||||
"tool": {
|
||||
"Handle": "工具连接器",
|
||||
"Select Tool": "选择工具"
|
||||
}
|
||||
},
|
||||
"value": "值",
|
||||
"variable": "变量"
|
||||
}
|
||||
},
|
||||
"dataset": {
|
||||
@@ -1189,7 +1203,6 @@
|
||||
"Select Dataset": "选择该知识库",
|
||||
"Select Dataset Tips": "仅能选择同一个索引模型的知识库",
|
||||
"Select Folder": "进入文件夹",
|
||||
"System Data Queue": "排队长度",
|
||||
"Training Name": "数据训练",
|
||||
"Upload Time": "上传时间",
|
||||
"collections": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { VariableItemType } from '@fastgpt/global/core/app/type.d';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Card, Input, Textarea } from '@chakra-ui/react';
|
||||
@@ -24,10 +24,23 @@ const VariableInput = ({
|
||||
chatForm: UseFormReturn<ChatBoxInputFormType>;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const { register, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
|
||||
const { register, unregister, setValue, handleSubmit: handleSubmitChat, watch } = chatForm;
|
||||
const variables = watch('variables');
|
||||
|
||||
useEffect(() => {
|
||||
// 重新注册所有字段
|
||||
variableModules.forEach((item) => {
|
||||
register(`variables.${item.key}`, { required: item.required });
|
||||
});
|
||||
|
||||
return () => {
|
||||
// 组件卸载时注销所有字段
|
||||
variableModules.forEach((item) => {
|
||||
unregister(`variables.${item.key}`);
|
||||
});
|
||||
};
|
||||
}, [register, unregister, variableModules]);
|
||||
|
||||
return (
|
||||
<Box py={3}>
|
||||
{/* avatar */}
|
||||
@@ -92,7 +105,6 @@ const VariableInput = ({
|
||||
value={variables[item.key]}
|
||||
onchange={(e) => {
|
||||
setValue(`variables.${item.key}`, e);
|
||||
setRefresh((state) => !state);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -116,4 +128,4 @@ const VariableInput = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(VariableInput);
|
||||
export default VariableInput;
|
||||
|
||||
@@ -91,6 +91,7 @@ type Props = OutLinkChatAuthProps & {
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<{
|
||||
responseText: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
|
||||
newVariables?: Record<string, any>;
|
||||
isNewChat?: boolean;
|
||||
}>;
|
||||
onDelMessage?: (e: { contentId: string }) => void;
|
||||
@@ -157,12 +158,6 @@ const ChatBox = (
|
||||
isChatting
|
||||
} = useChatProviderStore();
|
||||
|
||||
/* variable */
|
||||
const filterVariableModules = useMemo(
|
||||
() => variableModules.filter((item) => item.type !== VariableInputEnum.external),
|
||||
[variableModules]
|
||||
);
|
||||
|
||||
// compute variable input is finish.
|
||||
const chatForm = useForm<ChatBoxInputFormType>({
|
||||
defaultValues: {
|
||||
@@ -173,9 +168,15 @@ const ChatBox = (
|
||||
}
|
||||
});
|
||||
const { setValue, watch, handleSubmit } = chatForm;
|
||||
const variables = watch('variables');
|
||||
const chatStarted = watch('chatStarted');
|
||||
const variableIsFinish = useMemo(() => {
|
||||
|
||||
/* variable */
|
||||
const variables = watch('variables');
|
||||
const filterVariableModules = useMemo(
|
||||
() => variableModules.filter((item) => item.type !== VariableInputEnum.custom),
|
||||
[variableModules]
|
||||
);
|
||||
const variableIsFinish = (() => {
|
||||
if (!filterVariableModules || filterVariableModules.length === 0 || chatHistories.length > 0)
|
||||
return true;
|
||||
|
||||
@@ -187,7 +188,7 @@ const ChatBox = (
|
||||
}
|
||||
|
||||
return chatStarted;
|
||||
}, [filterVariableModules, chatHistories.length, chatStarted, variables]);
|
||||
})();
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = (behavior: 'smooth' | 'auto' = 'smooth') => {
|
||||
@@ -359,6 +360,12 @@ const ChatBox = (
|
||||
[questionGuide, shareId, outLinkUid, teamId, teamToken]
|
||||
);
|
||||
|
||||
/* Abort chat completions, questionGuide */
|
||||
const abortRequest = useCallback(() => {
|
||||
chatController.current?.abort('stop');
|
||||
questionGuideController.current?.abort('stop');
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* user confirm send prompt
|
||||
*/
|
||||
@@ -382,6 +389,8 @@ const ChatBox = (
|
||||
return;
|
||||
}
|
||||
|
||||
abortRequest();
|
||||
|
||||
text = text.trim();
|
||||
|
||||
if (!text && files.length === 0) {
|
||||
@@ -462,6 +471,7 @@ const ChatBox = (
|
||||
const {
|
||||
responseData,
|
||||
responseText,
|
||||
newVariables,
|
||||
isNewChat = false
|
||||
} = await onStartChat({
|
||||
chatList: newChatList,
|
||||
@@ -471,6 +481,8 @@ const ChatBox = (
|
||||
variables
|
||||
});
|
||||
|
||||
newVariables && setValue('variables', newVariables);
|
||||
|
||||
isNewChatReplace.current = isNewChat;
|
||||
|
||||
// set finish status
|
||||
@@ -537,6 +549,7 @@ const ChatBox = (
|
||||
})();
|
||||
},
|
||||
[
|
||||
abortRequest,
|
||||
chatHistories,
|
||||
createQuestionGuide,
|
||||
finishSegmentedAudio,
|
||||
@@ -549,6 +562,7 @@ const ChatBox = (
|
||||
resetInputVal,
|
||||
setAudioPlayingChatId,
|
||||
setChatHistories,
|
||||
setValue,
|
||||
splitText2Audio,
|
||||
startSegmentedAudio,
|
||||
t,
|
||||
@@ -706,7 +720,7 @@ const ChatBox = (
|
||||
});
|
||||
};
|
||||
},
|
||||
[appId, chatId, feedbackType, teamId, teamToken]
|
||||
[appId, chatId, feedbackType, setChatHistories, teamId, teamToken]
|
||||
);
|
||||
const onADdUserDislike = useCallback(
|
||||
(chat: ChatSiteItemType) => {
|
||||
@@ -743,7 +757,7 @@ const ChatBox = (
|
||||
return () => setFeedbackId(chat.dataId);
|
||||
}
|
||||
},
|
||||
[appId, chatId, feedbackType, outLinkUid, shareId, teamId, teamToken]
|
||||
[appId, chatId, feedbackType, outLinkUid, setChatHistories, shareId, teamId, teamToken]
|
||||
);
|
||||
const onReadUserDislike = useCallback(
|
||||
(chat: ChatSiteItemType) => {
|
||||
@@ -864,6 +878,7 @@ const ChatBox = (
|
||||
setValue('variables', e || defaultVal);
|
||||
},
|
||||
resetHistory(e) {
|
||||
abortRequest();
|
||||
setValue('chatStarted', e.length > 0);
|
||||
setChatHistories(e);
|
||||
},
|
||||
|
||||
@@ -17,13 +17,13 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
const CodeLight = dynamic(() => import('./CodeLight'));
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
|
||||
const MdImage = dynamic(() => import('./img/Image'));
|
||||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
|
||||
const CodeLight = dynamic(() => import('./CodeLight'), { ssr: false });
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||
const MdImage = dynamic(() => import('./img/Image'), { ssr: false });
|
||||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'), { ssr: false });
|
||||
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'));
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
|
||||
export enum CodeClassName {
|
||||
guide = 'guide',
|
||||
|
||||
@@ -173,7 +173,7 @@ const VariableEdit = ({
|
||||
maxW={['90vw', '500px']}
|
||||
>
|
||||
<ModalBody>
|
||||
{variableType !== VariableInputEnum.external && (
|
||||
{variableType !== VariableInputEnum.custom && (
|
||||
<Flex alignItems={'center'}>
|
||||
<Box w={'70px'}>{t('common.Require Input')}</Box>
|
||||
<Switch {...registerEdit('variable.required')} />
|
||||
@@ -197,7 +197,7 @@ const VariableEdit = ({
|
||||
</Flex>
|
||||
|
||||
<Box mt={5} mb={2}>
|
||||
{t('core.module.Field Type')}
|
||||
{t('core.workflow.Variable.Variable type')}
|
||||
</Box>
|
||||
<MyRadio
|
||||
gridGap={4}
|
||||
|
||||
@@ -65,10 +65,10 @@ const ChatTest = (
|
||||
}
|
||||
});
|
||||
});
|
||||
const history = chatList.slice(-historyMaxLen - 2, -2);
|
||||
const history = chatList.slice(-(historyMaxLen * 2) - 2, -2);
|
||||
|
||||
// 流请求,获取数据
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
url: '/api/core/chat/chatTest',
|
||||
data: {
|
||||
history,
|
||||
@@ -84,7 +84,7 @@ const ChatTest = (
|
||||
abortCtrl: controller
|
||||
});
|
||||
|
||||
return { responseText, responseData };
|
||||
return { responseText, responseData, newVariables };
|
||||
},
|
||||
[app._id, app.name, edges, nodes]
|
||||
);
|
||||
@@ -99,7 +99,7 @@ const ChatTest = (
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
zIndex={3}
|
||||
zIndex={101}
|
||||
flexDirection={'column'}
|
||||
position={'absolute'}
|
||||
top={5}
|
||||
|
||||
@@ -57,7 +57,8 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = {
|
||||
<NodeSimple {...data} minW={'100px'} maxW={'300px'} />
|
||||
),
|
||||
[FlowNodeTypeEnum.lafModule]: dynamic(() => import('./nodes/NodeLaf')),
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse'))
|
||||
[FlowNodeTypeEnum.ifElseNode]: dynamic(() => import('./nodes/NodeIfElse')),
|
||||
[FlowNodeTypeEnum.variableUpdate]: dynamic(() => import('./nodes/NodeVariableUpdate'))
|
||||
};
|
||||
const edgeTypes = {
|
||||
[EDGE_TYPE]: ButtonEdge
|
||||
|
||||
@@ -90,7 +90,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
</Box>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'transparentBase'}
|
||||
variant={'whitePrimary'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
|
||||
@@ -1,381 +0,0 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import NodeCard from './render/NodeCard';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Flex, background } from '@chakra-ui/react';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import RenderOutput from './render/RenderOutput';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import {
|
||||
IfElseConditionType,
|
||||
IfElseListItemType
|
||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferSelector, useReference } from './render/RenderInput/templates/Reference';
|
||||
import {
|
||||
VariableConditionEnum,
|
||||
allConditionList,
|
||||
arrayConditionList,
|
||||
booleanConditionList,
|
||||
numberConditionList
|
||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import { stringConditionList } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MyInput from '@/components/MyInput';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../context';
|
||||
|
||||
const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, inputs = [], outputs } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const condition = useMemo(
|
||||
() =>
|
||||
(inputs.find((input) => input.key === NodeInputKeyEnum.condition)
|
||||
?.value as IfElseConditionType) || 'OR',
|
||||
[inputs]
|
||||
);
|
||||
const ifElseList = useMemo(
|
||||
() =>
|
||||
(inputs.find((input) => input.key === NodeInputKeyEnum.ifElseList)
|
||||
?.value as IfElseListItemType[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
const onUpdateIfElseList = useCallback(
|
||||
(value: IfElseListItemType[]) => {
|
||||
const ifElseListInput = inputs.find((input) => input.key === NodeInputKeyEnum.ifElseList);
|
||||
if (!ifElseListInput) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.ifElseList,
|
||||
value: {
|
||||
...ifElseListInput,
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const RenderAddCondition = useMemo(() => {
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onUpdateIfElseList([
|
||||
...ifElseList,
|
||||
{
|
||||
variable: undefined,
|
||||
condition: undefined,
|
||||
value: undefined
|
||||
}
|
||||
]);
|
||||
}}
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
my={3}
|
||||
w={'full'}
|
||||
>
|
||||
{t('core.module.input.add')}
|
||||
</Button>
|
||||
);
|
||||
}, [ifElseList, onUpdateIfElseList, t]);
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW={'1000px'} {...data}>
|
||||
<Box px={6}>
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={[outputs[0]]} />
|
||||
</Box>
|
||||
<Box py={3} px={4}>
|
||||
<Box className="nowheel">
|
||||
{ifElseList.map((item, i) => {
|
||||
return (
|
||||
<Box key={i}>
|
||||
{/* border */}
|
||||
{i !== 0 && (
|
||||
<Flex alignItems={'center'} w={'full'} py={'5px'}>
|
||||
<Box
|
||||
w={'auto'}
|
||||
flex={1}
|
||||
height={'1px'}
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(90deg, rgba(232, 235, 240, 0.00) 0%, #E8EBF0 100%)'
|
||||
}}
|
||||
></Box>
|
||||
<Flex
|
||||
px={'2.5'}
|
||||
color={'primary.600'}
|
||||
fontWeight={'medium'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
rounded={'md'}
|
||||
onClick={() => {
|
||||
const conditionInput = inputs.find(
|
||||
(input) => input.key === NodeInputKeyEnum.condition
|
||||
);
|
||||
if (!conditionInput) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.condition,
|
||||
value: {
|
||||
...conditionInput,
|
||||
value: conditionInput.value === 'OR' ? 'AND' : 'OR'
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
{condition}
|
||||
<MyIcon ml={1} boxSize={5} name="change" />
|
||||
</Flex>
|
||||
<Box
|
||||
w={'auto'}
|
||||
flex={1}
|
||||
height={'1px'}
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(90deg, #E8EBF0 0%, rgba(232, 235, 240, 0.00) 100%)'
|
||||
}}
|
||||
></Box>
|
||||
</Flex>
|
||||
)}
|
||||
{/* condition list */}
|
||||
<Flex gap={2} alignItems={'center'}>
|
||||
{/* variable reference */}
|
||||
<Box minW={'250px'}>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={item.variable}
|
||||
onSelect={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...ifElse,
|
||||
variable: e
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* condition select */}
|
||||
<Box w={'130px'} flex={1}>
|
||||
<ConditionSelect
|
||||
condition={item.condition}
|
||||
variable={item.variable}
|
||||
onSelect={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...ifElse,
|
||||
condition: e
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* value */}
|
||||
<Box w={'200px'}>
|
||||
<ConditionValueInput
|
||||
value={item.value}
|
||||
condition={item.condition}
|
||||
variable={item.variable}
|
||||
onChange={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...ifElse,
|
||||
value: e
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* delete */}
|
||||
{ifElseList.length > 1 && (
|
||||
<MyIcon
|
||||
ml={2}
|
||||
boxSize={5}
|
||||
name="delete"
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
color={'myGray.400'}
|
||||
onClick={() => {
|
||||
onUpdateIfElseList(ifElseList.filter((_, index) => index !== i));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{RenderAddCondition}
|
||||
</Box>
|
||||
<Box px={6} mb={4}>
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={[outputs[1]]} />
|
||||
</Box>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeIfElse);
|
||||
|
||||
const Reference = ({
|
||||
nodeId,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
value: variable
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('选择引用变量')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/* Different data types have different options */
|
||||
const ConditionSelect = ({
|
||||
condition,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
condition?: VariableConditionEnum;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: VariableConditionEnum) => void;
|
||||
}) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
// get condition type
|
||||
const valueType = useMemo(() => {
|
||||
if (!variable) return;
|
||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
||||
|
||||
if (!node) return WorkflowIOValueTypeEnum.any;
|
||||
const output = node.outputs.find((item) => item.id === variable[1]);
|
||||
|
||||
if (!output) return WorkflowIOValueTypeEnum.any;
|
||||
return output.valueType;
|
||||
}, [nodeList, variable]);
|
||||
|
||||
const conditionList = useMemo(() => {
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) return stringConditionList;
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) return numberConditionList;
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) return booleanConditionList;
|
||||
if (
|
||||
valueType === WorkflowIOValueTypeEnum.chatHistory ||
|
||||
valueType === WorkflowIOValueTypeEnum.datasetQuote ||
|
||||
valueType === WorkflowIOValueTypeEnum.dynamic ||
|
||||
valueType === WorkflowIOValueTypeEnum.selectApp ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayBoolean ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayNumber ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayObject ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayString ||
|
||||
valueType === WorkflowIOValueTypeEnum.object
|
||||
)
|
||||
return arrayConditionList;
|
||||
|
||||
if (valueType === WorkflowIOValueTypeEnum.any) return allConditionList;
|
||||
|
||||
return [];
|
||||
}, [valueType]);
|
||||
|
||||
return (
|
||||
<MySelect
|
||||
w={'100%'}
|
||||
list={conditionList}
|
||||
value={condition}
|
||||
onchange={onSelect}
|
||||
placeholder="选择条件"
|
||||
></MySelect>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
Different condition can be entered differently
|
||||
empty, notEmpty: forbid input
|
||||
boolean type: select true/false
|
||||
*/
|
||||
const ConditionValueInput = ({
|
||||
value = '',
|
||||
variable,
|
||||
condition,
|
||||
onChange
|
||||
}: {
|
||||
value?: string;
|
||||
variable?: ReferenceValueProps;
|
||||
condition?: VariableConditionEnum;
|
||||
onChange: (e: string) => void;
|
||||
}) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
// get value type
|
||||
const valueType = useMemo(() => {
|
||||
if (!variable) return;
|
||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
||||
|
||||
if (!node) return WorkflowIOValueTypeEnum.any;
|
||||
const output = node.outputs.find((item) => item.id === variable[1]);
|
||||
|
||||
if (!output) return WorkflowIOValueTypeEnum.any;
|
||||
return output.valueType;
|
||||
}, [nodeList, variable]);
|
||||
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<MySelect
|
||||
list={[
|
||||
{ label: 'True', value: 'true' },
|
||||
{ label: 'False', value: 'false' }
|
||||
]}
|
||||
onchange={onChange}
|
||||
value={value}
|
||||
placeholder={'选择值'}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<MyInput
|
||||
value={value}
|
||||
placeholder={'输入值'}
|
||||
w={'100%'}
|
||||
isDisabled={
|
||||
condition === VariableConditionEnum.isEmpty ||
|
||||
condition === VariableConditionEnum.isNotEmpty
|
||||
}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,415 @@
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import {
|
||||
DraggableProvided,
|
||||
DraggableStateSnapshot
|
||||
} from '@fastgpt/web/components/common/DndDrag/index';
|
||||
import Container from '../../components/Container';
|
||||
import { DragHandleIcon, MinusIcon, SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReferSelector, useReference } from '../render/RenderInput/templates/Reference';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
VariableConditionEnum,
|
||||
allConditionList,
|
||||
arrayConditionList,
|
||||
booleanConditionList,
|
||||
numberConditionList,
|
||||
stringConditionList
|
||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import React, { useMemo } from 'react';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MyInput from '@/components/MyInput';
|
||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { SourceHandle } from '../render/Handle';
|
||||
import { Position, useReactFlow } from 'reactflow';
|
||||
import { getReferenceDataValueType } from '@/web/core/workflow/utils';
|
||||
import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon';
|
||||
|
||||
const ListItem = ({
|
||||
provided,
|
||||
snapshot,
|
||||
conditionIndex,
|
||||
conditionItem,
|
||||
ifElseList,
|
||||
onUpdateIfElseList,
|
||||
nodeId
|
||||
}: {
|
||||
provided: DraggableProvided;
|
||||
snapshot: DraggableStateSnapshot;
|
||||
conditionIndex: number;
|
||||
conditionItem: IfElseListItemType;
|
||||
ifElseList: IfElseListItemType[];
|
||||
onUpdateIfElseList: (value: IfElseListItemType[]) => void;
|
||||
nodeId: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { getZoom } = useReactFlow();
|
||||
|
||||
return (
|
||||
<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(${getZoom()})` : ''}
|
||||
transformOrigin={'top left'}
|
||||
>
|
||||
<Container w={snapshot.isDragging ? '' : 'full'} className="nodrag">
|
||||
<Flex mb={4} alignItems={'center'}>
|
||||
{ifElseList.length > 1 && <DragIcon provided={provided} />}
|
||||
<Box color={'black'} fontSize={'lg'} ml={2}>
|
||||
{getElseIFLabel(conditionIndex)}
|
||||
</Box>
|
||||
{conditionItem.list?.length > 1 && (
|
||||
<Flex
|
||||
px={'2.5'}
|
||||
color={'primary.600'}
|
||||
fontWeight={'medium'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
rounded={'md'}
|
||||
onClick={() => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
condition: ifElse.condition === 'AND' ? 'OR' : 'AND'
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
{conditionItem.condition}
|
||||
<MyIcon ml={1} boxSize={5} name="change" />
|
||||
</Flex>
|
||||
)}
|
||||
<Box flex={1}></Box>
|
||||
{ifElseList.length > 1 && (
|
||||
<MyIcon
|
||||
ml={2}
|
||||
boxSize={5}
|
||||
name="delete"
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
color={'myGray.400'}
|
||||
onClick={() => {
|
||||
onUpdateIfElseList(ifElseList.filter((_, index) => index !== conditionIndex));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Box>
|
||||
{conditionItem.list?.map((item, i) => {
|
||||
return (
|
||||
<Box key={i}>
|
||||
{/* condition list */}
|
||||
<Flex gap={2} mb={2} alignItems={'center'}>
|
||||
{/* variable reference */}
|
||||
<Box minW={'250px'}>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={item.variable}
|
||||
onSelect={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
list: ifElse.list.map((item, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...item,
|
||||
variable: e
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* condition select */}
|
||||
<Box w={'130px'} flex={1}>
|
||||
<ConditionSelect
|
||||
condition={item.condition}
|
||||
variable={item.variable}
|
||||
onSelect={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
list: ifElse.list.map((item, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...item,
|
||||
condition: e
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* value */}
|
||||
<Box w={'200px'}>
|
||||
<ConditionValueInput
|
||||
value={item.value}
|
||||
condition={item.condition}
|
||||
variable={item.variable}
|
||||
onChange={(e) => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
list: ifElse.list.map((item, index) => {
|
||||
if (index === i) {
|
||||
return {
|
||||
...item,
|
||||
value: e
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/* delete */}
|
||||
{conditionItem.list.length > 1 && (
|
||||
<MinusIcon
|
||||
ml={2}
|
||||
boxSize={3}
|
||||
name="delete"
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.600' }}
|
||||
color={'myGray.400'}
|
||||
onClick={() => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
list: ifElse.list.filter((_, index) => index !== i)
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onUpdateIfElseList(
|
||||
ifElseList.map((ifElse, index) => {
|
||||
if (index === conditionIndex) {
|
||||
return {
|
||||
...ifElse,
|
||||
list: ifElse.list.concat({
|
||||
variable: undefined,
|
||||
condition: undefined,
|
||||
value: undefined
|
||||
})
|
||||
};
|
||||
}
|
||||
return ifElse;
|
||||
})
|
||||
);
|
||||
}}
|
||||
variant={'link'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
my={3}
|
||||
color={'primary.600'}
|
||||
>
|
||||
{t('core.module.input.add')}
|
||||
</Button>
|
||||
</Container>
|
||||
{!snapshot.isDragging && (
|
||||
<SourceHandle
|
||||
nodeId={nodeId}
|
||||
handleId={getHandleId(nodeId, 'source', getElseIFLabel(conditionIndex))}
|
||||
position={Position.Right}
|
||||
translate={[18, 0]}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ListItem);
|
||||
|
||||
const Reference = ({
|
||||
nodeId,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
value: variable
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('选择引用变量')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/* Different data types have different options */
|
||||
const ConditionSelect = ({
|
||||
condition,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
condition?: VariableConditionEnum;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: VariableConditionEnum) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
// get condition type
|
||||
const valueType = useMemo(() => {
|
||||
return getReferenceDataValueType({
|
||||
variable,
|
||||
nodeList,
|
||||
t
|
||||
});
|
||||
}, [nodeList, t, variable]);
|
||||
|
||||
const conditionList = useMemo(() => {
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) return stringConditionList;
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) return numberConditionList;
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) return booleanConditionList;
|
||||
if (
|
||||
valueType === WorkflowIOValueTypeEnum.chatHistory ||
|
||||
valueType === WorkflowIOValueTypeEnum.datasetQuote ||
|
||||
valueType === WorkflowIOValueTypeEnum.dynamic ||
|
||||
valueType === WorkflowIOValueTypeEnum.selectApp ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayBoolean ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayNumber ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayObject ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayString ||
|
||||
valueType === WorkflowIOValueTypeEnum.object
|
||||
)
|
||||
return arrayConditionList;
|
||||
|
||||
if (valueType === WorkflowIOValueTypeEnum.any) return allConditionList;
|
||||
|
||||
return [];
|
||||
}, [valueType]);
|
||||
|
||||
return (
|
||||
<MySelect
|
||||
w={'100%'}
|
||||
list={conditionList}
|
||||
value={condition}
|
||||
onchange={onSelect}
|
||||
placeholder="选择条件"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
Different condition can be entered differently
|
||||
empty, notEmpty: forbid input
|
||||
boolean type: select true/false
|
||||
*/
|
||||
const ConditionValueInput = ({
|
||||
value = '',
|
||||
variable,
|
||||
condition,
|
||||
onChange
|
||||
}: {
|
||||
value?: string;
|
||||
variable?: ReferenceValueProps;
|
||||
condition?: VariableConditionEnum;
|
||||
onChange: (e: string) => void;
|
||||
}) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
// get value type
|
||||
const valueType = useMemo(() => {
|
||||
if (!variable) return;
|
||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
||||
|
||||
if (!node) return WorkflowIOValueTypeEnum.any;
|
||||
const output = node.outputs.find((item) => item.id === variable[1]);
|
||||
|
||||
if (!output) return WorkflowIOValueTypeEnum.any;
|
||||
return output.valueType;
|
||||
}, [nodeList, variable]);
|
||||
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<MySelect
|
||||
list={[
|
||||
{ label: 'True', value: 'true' },
|
||||
{ label: 'False', value: 'false' }
|
||||
]}
|
||||
onchange={onChange}
|
||||
value={value}
|
||||
placeholder={'选择值'}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<MyInput
|
||||
value={value}
|
||||
placeholder={'输入值'}
|
||||
w={'100%'}
|
||||
bg={'white'}
|
||||
isDisabled={
|
||||
condition === VariableConditionEnum.isEmpty ||
|
||||
condition === VariableConditionEnum.isNotEmpty
|
||||
}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,136 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeProps, Position } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import Container from '../../components/Container';
|
||||
import DndDrag, { Draggable, DropResult } from '@fastgpt/web/components/common/DndDrag/index';
|
||||
import { SourceHandle } from '../render/Handle';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import ListItem from './ListItem';
|
||||
import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
|
||||
const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, inputs = [] } = data;
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const ifElseList = useMemo(
|
||||
() =>
|
||||
(inputs.find((input) => input.key === NodeInputKeyEnum.ifElseList)
|
||||
?.value as IfElseListItemType[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
const onUpdateIfElseList = useCallback(
|
||||
(value: IfElseListItemType[]) => {
|
||||
const ifElseListInput = inputs.find((input) => input.key === NodeInputKeyEnum.ifElseList);
|
||||
if (!ifElseListInput) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.ifElseList,
|
||||
value: {
|
||||
...ifElseListInput,
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW={'1000px'} {...data}>
|
||||
<Box px={4} cursor={'default'}>
|
||||
<DndDrag<IfElseListItemType>
|
||||
onDragEndCb={(list) => onUpdateIfElseList(list)}
|
||||
dataList={ifElseList}
|
||||
renderClone={(provided, snapshot, rubric) => (
|
||||
<ListItem
|
||||
provided={provided}
|
||||
snapshot={snapshot}
|
||||
conditionItem={ifElseList[rubric.source.index]}
|
||||
conditionIndex={rubric.source.index}
|
||||
ifElseList={ifElseList}
|
||||
onUpdateIfElseList={onUpdateIfElseList}
|
||||
nodeId={nodeId}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{(provided) => (
|
||||
<Box {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{ifElseList.map((conditionItem, conditionIndex) => (
|
||||
<Draggable
|
||||
key={conditionIndex}
|
||||
draggableId={conditionIndex.toString()}
|
||||
index={conditionIndex}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<ListItem
|
||||
provided={provided}
|
||||
snapshot={snapshot}
|
||||
conditionItem={conditionItem}
|
||||
conditionIndex={conditionIndex}
|
||||
ifElseList={ifElseList}
|
||||
onUpdateIfElseList={onUpdateIfElseList}
|
||||
nodeId={nodeId}
|
||||
/>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</DndDrag>
|
||||
|
||||
<Container position={'relative'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box color={'black'} fontSize={'lg'} ml={2}>
|
||||
{IfElseResultEnum.ELSE}
|
||||
</Box>
|
||||
<SourceHandle
|
||||
nodeId={nodeId}
|
||||
handleId={getHandleId(nodeId, 'source', IfElseResultEnum.ELSE)}
|
||||
position={Position.Right}
|
||||
translate={[26, 0]}
|
||||
/>
|
||||
</Flex>
|
||||
</Container>
|
||||
</Box>
|
||||
<Box py={3} px={6}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
w={'full'}
|
||||
onClick={() => {
|
||||
const ifElseListInput = inputs.find(
|
||||
(input) => input.key === NodeInputKeyEnum.ifElseList
|
||||
);
|
||||
if (!ifElseListInput) return;
|
||||
|
||||
onUpdateIfElseList([
|
||||
...ifElseList,
|
||||
{
|
||||
condition: 'AND',
|
||||
list: [
|
||||
{
|
||||
variable: undefined,
|
||||
condition: undefined,
|
||||
value: undefined
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
}}
|
||||
>
|
||||
{t('core.module.input.Add Branch')}
|
||||
</Button>
|
||||
</Box>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeIfElse);
|
||||
@@ -0,0 +1,309 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import NodeCard from './render/NodeCard';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
Switch,
|
||||
Textarea
|
||||
} from '@chakra-ui/react';
|
||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/components/core/workflow/context';
|
||||
import {
|
||||
FlowNodeInputMap,
|
||||
FlowNodeInputTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import Container from '../components/Container';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferSelector, useReference } from './render/RenderInput/templates/Reference';
|
||||
import { getReferenceDataValueType } from '@/web/core/workflow/utils';
|
||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { inputs = [], nodeId } = data;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
const updateList = useMemo(
|
||||
() =>
|
||||
(inputs.find((input) => input.key === NodeInputKeyEnum.updateList)
|
||||
?.value as TUpdateListItem[]) || [],
|
||||
[inputs]
|
||||
);
|
||||
|
||||
const onUpdateList = useCallback(
|
||||
(value: TUpdateListItem[]) => {
|
||||
const updateListInput = inputs.find((input) => input.key === NodeInputKeyEnum.updateList);
|
||||
if (!updateListInput) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.updateList,
|
||||
value: {
|
||||
...updateListInput,
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputs, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
const menuList = [
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.input,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.input].icon,
|
||||
label: t('core.workflow.inputType.Manual input')
|
||||
},
|
||||
{
|
||||
renderType: FlowNodeInputTypeEnum.reference,
|
||||
icon: FlowNodeInputMap[FlowNodeInputTypeEnum.reference].icon,
|
||||
label: t('core.workflow.inputType.Reference')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
{updateList.map((updateItem, index) => {
|
||||
const valueType = getReferenceDataValueType({
|
||||
variable: updateItem.variable,
|
||||
nodeList,
|
||||
t
|
||||
});
|
||||
|
||||
const renderTypeData = menuList.find((item) => item.renderType === updateItem.renderType);
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue)) {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
)
|
||||
);
|
||||
} else {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: ['', newValue as string] } : update
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container key={index} mt={4} w={'full'} mx={0}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('core.workflow.variable')}</Flex>
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
onSelect={(value) => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
value: ['', ''],
|
||||
valueType,
|
||||
variable: value
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Box flex={1} />
|
||||
{updateList.length > 1 && (
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'red.500' }}
|
||||
position={'absolute'}
|
||||
top={3}
|
||||
right={3}
|
||||
onClick={() => {
|
||||
onUpdateList(updateList.filter((_, i) => i !== index));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex mt={2} w={'full'} alignItems={'center'} className="nodrag">
|
||||
<Flex w={'60px'}>
|
||||
<Box>{t('core.workflow.value')}</Box>
|
||||
<MyTooltip
|
||||
label={
|
||||
menuList.find((item) => item.renderType === updateItem.renderType)?.label
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size={'xs'}
|
||||
bg={'white'}
|
||||
borderRadius={'xs'}
|
||||
mx={2}
|
||||
onClick={() => {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...update,
|
||||
value: ['', ''],
|
||||
renderType:
|
||||
updateItem.renderType === FlowNodeInputTypeEnum.input
|
||||
? FlowNodeInputTypeEnum.reference
|
||||
: FlowNodeInputTypeEnum.input
|
||||
};
|
||||
}
|
||||
return update;
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
<MyIcon name={renderTypeData?.icon as any} w={'14px'} />
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
|
||||
{/* Render input components */}
|
||||
{(() => {
|
||||
if (updateItem.renderType === FlowNodeInputTypeEnum.reference) {
|
||||
return (
|
||||
<Reference
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.value}
|
||||
valueType={valueType}
|
||||
onSelect={handleUpdate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) {
|
||||
return (
|
||||
<Textarea
|
||||
bg="white"
|
||||
value={updateItem.value?.[1] || ''}
|
||||
w="300px"
|
||||
onChange={(e) => handleUpdate(e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) {
|
||||
return (
|
||||
<NumberInput value={Number(updateItem.value?.[1]) || 0}>
|
||||
<NumberInputField
|
||||
bg="white"
|
||||
onChange={(e) => handleUpdate(e.target.value)}
|
||||
/>
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
<NumberDecrementStepper />
|
||||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
);
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<Switch
|
||||
size="lg"
|
||||
defaultChecked={updateItem.value?.[1] === 'true'}
|
||||
onChange={(e) => handleUpdate(String(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<JsonEditor
|
||||
bg="white"
|
||||
resize
|
||||
w="300px"
|
||||
value={String(updateItem.value?.[1] || '')}
|
||||
onChange={(e) => {
|
||||
handleUpdate(e);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
</Flex>
|
||||
</Container>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}, [nodeId, nodeList, onUpdateList, t, updateList]);
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW={'1000px'} {...data}>
|
||||
<Box px={4} pb={4}>
|
||||
{Render}
|
||||
<Flex className="nodrag" cursor={'default'} alignItems={'center'} position={'relative'}>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
w={'full'}
|
||||
size={'sm'}
|
||||
onClick={() => {
|
||||
onUpdateList([
|
||||
...updateList,
|
||||
{
|
||||
variable: ['', ''],
|
||||
value: ['', ''],
|
||||
renderType: FlowNodeInputTypeEnum.input
|
||||
}
|
||||
]);
|
||||
}}
|
||||
>
|
||||
{t('common.Add New')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeVariableUpdate);
|
||||
|
||||
const Reference = ({
|
||||
nodeId,
|
||||
variable,
|
||||
valueType,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType,
|
||||
value: variable
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('选择引用变量')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -38,6 +38,8 @@ type Props = FlowNodeItemType & {
|
||||
|
||||
const NodeCard = (props: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const {
|
||||
children,
|
||||
avatar = LOGO_ICON,
|
||||
@@ -59,6 +61,13 @@ const NodeCard = (props: Props) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const setHoverNodeId = useContextSelector(WorkflowContext, (v) => v.setHoverNodeId);
|
||||
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
// custom title edit
|
||||
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
|
||||
title: t('common.Custom Title'),
|
||||
placeholder: t('app.module.Custom Title Tip') || ''
|
||||
});
|
||||
|
||||
const showToolHandle = useMemo(
|
||||
() => isTool && !!nodeList.find((item) => item?.flowNodeType === FlowNodeTypeEnum.tools),
|
||||
@@ -70,7 +79,6 @@ const NodeCard = (props: Props) => {
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
{/* debug */}
|
||||
<NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
|
||||
<Box className="custom-drag-handle" px={4} py={3}>
|
||||
{/* tool target handle */}
|
||||
{showToolHandle && <ToolTargetHandle nodeId={nodeId} />}
|
||||
@@ -81,13 +89,42 @@ const NodeCard = (props: Props) => {
|
||||
<Box ml={3} fontSize={'lg'} fontWeight={'medium'}>
|
||||
{t(name)}
|
||||
</Box>
|
||||
{!menuForbid?.rename && (
|
||||
<MyIcon
|
||||
className="controller-rename"
|
||||
display={'none'}
|
||||
name={'edit'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={1}
|
||||
color={'myGray.500'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => {
|
||||
onOpenCustomTitleModal({
|
||||
defaultVal: name,
|
||||
onSuccess: (e) => {
|
||||
if (!e) {
|
||||
return toast({
|
||||
title: t('app.modules.Title is required'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'name',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
<MenuRender
|
||||
name={name}
|
||||
nodeId={nodeId}
|
||||
pluginId={pluginId}
|
||||
flowNodeType={flowNodeType}
|
||||
inputs={inputs}
|
||||
menuForbid={menuForbid}
|
||||
/>
|
||||
<NodeIntro nodeId={nodeId} intro={intro} />
|
||||
@@ -96,16 +133,17 @@ const NodeCard = (props: Props) => {
|
||||
);
|
||||
}, [
|
||||
nodeId,
|
||||
debugResult,
|
||||
showToolHandle,
|
||||
avatar,
|
||||
t,
|
||||
name,
|
||||
menuForbid,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
inputs,
|
||||
menuForbid,
|
||||
intro
|
||||
intro,
|
||||
onOpenCustomTitleModal,
|
||||
onChangeNode,
|
||||
toast
|
||||
]);
|
||||
|
||||
return (
|
||||
@@ -123,6 +161,9 @@ const NodeCard = (props: Props) => {
|
||||
},
|
||||
'& .controller-debug': {
|
||||
display: 'block'
|
||||
},
|
||||
'& .controller-rename': {
|
||||
display: 'block'
|
||||
}
|
||||
}}
|
||||
onMouseEnter={() => setHoverNodeId(nodeId)}
|
||||
@@ -136,10 +177,13 @@ const NodeCard = (props: Props) => {
|
||||
borderColor: selected ? 'primary.600' : 'borderColor.base'
|
||||
})}
|
||||
>
|
||||
<NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
|
||||
{Header}
|
||||
{children}
|
||||
<ConnectionSourceHandle nodeId={nodeId} />
|
||||
<ConnectionTargetHandle nodeId={nodeId} />
|
||||
|
||||
<EditTitleModal maxLength={20} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -147,18 +191,14 @@ const NodeCard = (props: Props) => {
|
||||
export default React.memo(NodeCard);
|
||||
|
||||
const MenuRender = React.memo(function MenuRender({
|
||||
name,
|
||||
nodeId,
|
||||
pluginId,
|
||||
flowNodeType,
|
||||
inputs,
|
||||
menuForbid
|
||||
}: {
|
||||
name: string;
|
||||
nodeId: string;
|
||||
pluginId?: string;
|
||||
flowNodeType: Props['flowNodeType'];
|
||||
inputs: Props['inputs'];
|
||||
menuForbid?: Props['menuForbid'];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -169,11 +209,7 @@ const MenuRender = React.memo(function MenuRender({
|
||||
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
|
||||
content: t('module.Confirm Sync Plugin')
|
||||
});
|
||||
// custom title edit
|
||||
const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
|
||||
title: t('common.Custom Title'),
|
||||
placeholder: t('app.module.Custom Title Tip') || ''
|
||||
});
|
||||
|
||||
const { openConfirm: onOpenConfirmDeleteNode, ConfirmModal: ConfirmDeleteModal } = useConfirm({
|
||||
content: t('core.module.Confirm Delete Node'),
|
||||
type: 'delete'
|
||||
@@ -182,7 +218,6 @@ const MenuRender = React.memo(function MenuRender({
|
||||
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
|
||||
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
|
||||
const setEdges = useContextSelector(WorkflowContext, (v) => v.setEdges);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const onCopyNode = useCallback(
|
||||
(nodeId: string) => {
|
||||
@@ -223,6 +258,22 @@ const MenuRender = React.memo(function MenuRender({
|
||||
},
|
||||
[setEdges, setNodes]
|
||||
);
|
||||
const onclickSyncVersion = useCallback(async () => {
|
||||
if (!pluginId) return;
|
||||
try {
|
||||
setLoading(true);
|
||||
onResetNode({
|
||||
id: nodeId,
|
||||
node: await getPreviewPluginModule(pluginId)
|
||||
});
|
||||
} catch (e) {
|
||||
return toast({
|
||||
status: 'error',
|
||||
title: getErrText(e, t('plugin.Get Plugin Module Detail Failed'))
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
}, [nodeId, onResetNode, pluginId, setLoading, t, toast]);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
const menuList = [
|
||||
@@ -236,61 +287,6 @@ const MenuRender = React.memo(function MenuRender({
|
||||
onClick: () => openDebugNode({ entryNodeId: nodeId })
|
||||
}
|
||||
]),
|
||||
...(flowNodeType === FlowNodeTypeEnum.pluginModule
|
||||
? [
|
||||
{
|
||||
icon: 'common/refreshLight',
|
||||
label: t('plugin.Synchronous version'),
|
||||
variant: 'whiteBase',
|
||||
onClick: () => {
|
||||
if (!pluginId) return;
|
||||
onOpenConfirmSync(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const pluginModule = await getPreviewPluginModule(pluginId);
|
||||
onResetNode({
|
||||
id: nodeId,
|
||||
module: pluginModule
|
||||
});
|
||||
} catch (e) {
|
||||
return toast({
|
||||
status: 'error',
|
||||
title: getErrText(e, t('plugin.Get Plugin Module Detail Failed'))
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
})();
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(menuForbid?.rename
|
||||
? []
|
||||
: [
|
||||
{
|
||||
icon: 'edit',
|
||||
label: t('common.Rename'),
|
||||
variant: 'whiteBase',
|
||||
onClick: () =>
|
||||
onOpenCustomTitleModal({
|
||||
defaultVal: name,
|
||||
onSuccess: (e) => {
|
||||
if (!e) {
|
||||
return toast({
|
||||
title: t('app.modules.Title is required'),
|
||||
status: 'warning'
|
||||
});
|
||||
}
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'attr',
|
||||
key: 'name',
|
||||
value: e
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
]),
|
||||
...(menuForbid?.copy
|
||||
? []
|
||||
: [
|
||||
@@ -301,6 +297,17 @@ const MenuRender = React.memo(function MenuRender({
|
||||
onClick: () => onCopyNode(nodeId)
|
||||
}
|
||||
]),
|
||||
...(flowNodeType === FlowNodeTypeEnum.pluginModule
|
||||
? [
|
||||
{
|
||||
icon: 'common/refreshLight',
|
||||
label: t('plugin.Synchronous version'),
|
||||
variant: 'whiteBase',
|
||||
onClick: onOpenConfirmSync(onclickSyncVersion)
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
||||
...(menuForbid?.delete
|
||||
? []
|
||||
: [
|
||||
@@ -342,7 +349,6 @@ const MenuRender = React.memo(function MenuRender({
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<EditTitleModal maxLength={20} />
|
||||
<ConfirmSyncModal />
|
||||
<ConfirmDeleteModal />
|
||||
<DebugInputModal />
|
||||
@@ -352,26 +358,18 @@ const MenuRender = React.memo(function MenuRender({
|
||||
ConfirmDeleteModal,
|
||||
ConfirmSyncModal,
|
||||
DebugInputModal,
|
||||
EditTitleModal,
|
||||
flowNodeType,
|
||||
menuForbid?.copy,
|
||||
menuForbid?.debug,
|
||||
menuForbid?.delete,
|
||||
menuForbid?.rename,
|
||||
name,
|
||||
nodeId,
|
||||
onChangeNode,
|
||||
onCopyNode,
|
||||
onDelNode,
|
||||
onOpenConfirmDeleteNode,
|
||||
onOpenConfirmSync,
|
||||
onOpenCustomTitleModal,
|
||||
onResetNode,
|
||||
onclickSyncVersion,
|
||||
openDebugNode,
|
||||
pluginId,
|
||||
setLoading,
|
||||
t,
|
||||
toast
|
||||
t
|
||||
]);
|
||||
|
||||
return Render;
|
||||
@@ -388,7 +386,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const moduleIsTool = useMemo(() => {
|
||||
const NodeIsTool = useMemo(() => {
|
||||
const { isTool } = splitToolInputs([], nodeId);
|
||||
return isTool;
|
||||
}, [nodeId, splitToolInputs]);
|
||||
@@ -407,7 +405,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
<Box fontSize={'xs'} color={'myGray.600'} flex={'1 0 0'}>
|
||||
{t(intro)}
|
||||
</Box>
|
||||
{moduleIsTool && (
|
||||
{NodeIsTool && (
|
||||
<Button
|
||||
size={'xs'}
|
||||
variant={'whiteBase'}
|
||||
@@ -432,7 +430,7 @@ const NodeIntro = React.memo(function NodeIntro({
|
||||
<EditIntroModal maxLength={500} />
|
||||
</>
|
||||
);
|
||||
}, [EditIntroModal, intro, moduleIsTool, nodeId, onChangeNode, onOpenIntroModal, t]);
|
||||
}, [EditIntroModal, intro, NodeIsTool, nodeId, onChangeNode, onOpenIntroModal, t]);
|
||||
|
||||
return Render;
|
||||
});
|
||||
@@ -526,7 +524,8 @@ const NodeDebugResponse = React.memo(function NodeDebugResponse({
|
||||
top={0}
|
||||
zIndex={10}
|
||||
w={'420px'}
|
||||
maxH={'540px'}
|
||||
maxH={'100%'}
|
||||
minH={'300px'}
|
||||
overflowY={'auto'}
|
||||
border={'base'}
|
||||
>
|
||||
|
||||
@@ -90,7 +90,7 @@ export default React.memo(Reference);
|
||||
|
||||
export const useReference = ({
|
||||
nodeId,
|
||||
valueType,
|
||||
valueType = WorkflowIOValueTypeEnum.any,
|
||||
value
|
||||
}: {
|
||||
nodeId: string;
|
||||
|
||||
@@ -36,7 +36,6 @@ import { createContext } from 'use-context-selector';
|
||||
import { defaultRunningStatus } from './constants';
|
||||
import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
|
||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||
|
||||
@@ -56,7 +55,7 @@ type WorkflowContextType = {
|
||||
hoverNodeId?: string;
|
||||
setHoverNodeId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
onUpdateNodeError: (node: string, isError: Boolean) => void;
|
||||
onResetNode: (e: { id: string; module: FlowNodeTemplateType }) => void;
|
||||
onResetNode: (e: { id: string; node: FlowNodeTemplateType }) => void;
|
||||
onChangeNode: (e: FlowNodeChangeProps) => void;
|
||||
|
||||
// edges
|
||||
@@ -160,7 +159,7 @@ export const WorkflowContext = createContext<WorkflowContextType>({
|
||||
onEdgesChange: function (changes: EdgeChange[]): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
onResetNode: function (e: { id: string; module: FlowNodeTemplateType }): void {
|
||||
onResetNode: function (e: { id: string; node: FlowNodeTemplateType }): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
onDelEdge: function (e: {
|
||||
@@ -256,7 +255,11 @@ const WorkflowContextProvider = ({
|
||||
const [nodes = [], setNodes, onNodesChange] = useNodesState<FlowNodeItemType>([]);
|
||||
const [hoverNodeId, setHoverNodeId] = useState<string>();
|
||||
|
||||
const nodeList = useCreation(() => nodes.map((node) => node.data), [nodes]);
|
||||
const nodeListString = JSON.stringify(nodes.map((node) => node.data));
|
||||
const nodeList = useMemo(
|
||||
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
||||
[nodeListString]
|
||||
);
|
||||
|
||||
const hasToolNode = useMemo(() => {
|
||||
return !!nodes.find((node) => node.data.flowNodeType === FlowNodeTypeEnum.tools);
|
||||
@@ -276,31 +279,30 @@ const WorkflowContextProvider = ({
|
||||
});
|
||||
|
||||
// reset a node data. delete edge and replace it
|
||||
const onResetNode = useMemoizedFn(
|
||||
({ id, module }: { id: string; module: FlowNodeTemplateType }) => {
|
||||
setNodes((state) =>
|
||||
state.map((node) => {
|
||||
if (node.id === id) {
|
||||
// delete edge
|
||||
node.data.inputs.forEach((item) => {
|
||||
onDelEdge({ nodeId: id, targetHandle: item.key });
|
||||
});
|
||||
node.data.outputs.forEach((item) => {
|
||||
onDelEdge({ nodeId: id, sourceHandle: item.key });
|
||||
});
|
||||
return {
|
||||
const onResetNode = useMemoizedFn(({ id, node }: { id: string; node: FlowNodeTemplateType }) => {
|
||||
setNodes((state) =>
|
||||
state.map((item) => {
|
||||
if (item.id === id) {
|
||||
return {
|
||||
...item,
|
||||
data: {
|
||||
...item.data,
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
...module
|
||||
}
|
||||
};
|
||||
}
|
||||
return node;
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
inputs: node.inputs.map((input) => {
|
||||
const value =
|
||||
item.data.inputs.find((i) => i.key === input.key)?.value ?? input.value;
|
||||
return {
|
||||
...input,
|
||||
value
|
||||
};
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const onChangeNode = useMemoizedFn((props: FlowNodeChangeProps) => {
|
||||
const { nodeId, type } = props;
|
||||
@@ -410,7 +412,7 @@ const WorkflowContextProvider = ({
|
||||
});
|
||||
|
||||
/* If the module is connected by a tool, the tool input and the normal input are separated */
|
||||
const splitToolInputs = useMemoizedFn((inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const splitToolInputs = (inputs: FlowNodeInputItemType[], nodeId: string) => {
|
||||
const isTool = !!edges.find(
|
||||
(edge) => edge.targetHandle === NodeOutputKeyEnum.selectedTools && edge.target === nodeId
|
||||
);
|
||||
@@ -423,12 +425,11 @@ const WorkflowContextProvider = ({
|
||||
return !item.toolDescription;
|
||||
})
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const initData = useMemoizedFn(
|
||||
async (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => {
|
||||
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item })));
|
||||
|
||||
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -63,7 +63,10 @@ export async function getInitConfig() {
|
||||
initSystemConfig(),
|
||||
// getSimpleModeTemplates(),
|
||||
getSystemVersion(),
|
||||
getSystemPlugin()
|
||||
getSystemPlugin(),
|
||||
|
||||
// abandon
|
||||
getSystemPluginV1()
|
||||
]);
|
||||
|
||||
console.log({
|
||||
@@ -164,3 +167,29 @@ function getSystemPlugin() {
|
||||
|
||||
global.communityPlugins = fileTemplates;
|
||||
}
|
||||
function getSystemPluginV1() {
|
||||
if (global.communityPluginsV1 && global.communityPluginsV1.length > 0) return;
|
||||
|
||||
const basePath =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'data/pluginTemplates/v1'
|
||||
: '/app/data/pluginTemplates/v1';
|
||||
// read data/pluginTemplates directory, get all json file
|
||||
const files = readdirSync(basePath);
|
||||
// filter json file
|
||||
const filterFiles = files.filter((item) => item.endsWith('.json'));
|
||||
|
||||
// read json file
|
||||
const fileTemplates: (PluginTemplateType & { weight: number })[] = filterFiles.map((filename) => {
|
||||
const content = readFileSync(`${basePath}/${filename}`, 'utf-8');
|
||||
return {
|
||||
...JSON.parse(content),
|
||||
id: `${PluginSourceEnum.community}-${filename.replace('.json', '')}`,
|
||||
source: PluginSourceEnum.community
|
||||
};
|
||||
});
|
||||
|
||||
fileTemplates.sort((a, b) => b.weight - a.weight);
|
||||
|
||||
global.communityPluginsV1 = fileTemplates;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/cons
|
||||
import { responseWrite } from '@fastgpt/service/common/response';
|
||||
import { pushChatUsage } from '@/service/support/wallet/usage/push';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import type { ChatItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import type {
|
||||
ChatItemType,
|
||||
ChatItemValueItemType,
|
||||
UserChatItemValueItemType
|
||||
} from '@fastgpt/global/core/chat/type';
|
||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
@@ -13,10 +17,11 @@ import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/a
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils';
|
||||
|
||||
export type Props = {
|
||||
history: ChatItemType[];
|
||||
prompt: ChatItemValueItemType[];
|
||||
prompt: UserChatItemValueItemType[];
|
||||
nodes: RuntimeNodeItemType[];
|
||||
edges: RuntimeEdgeItemType[];
|
||||
variables: Record<string, any>;
|
||||
@@ -33,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
res.end();
|
||||
});
|
||||
|
||||
const {
|
||||
let {
|
||||
nodes = [],
|
||||
edges = [],
|
||||
history = [],
|
||||
@@ -66,10 +71,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
// auth balance
|
||||
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
|
||||
|
||||
const { text, files } = chatValue2RuntimePrompt(prompt);
|
||||
|
||||
/* start process */
|
||||
const { flowResponses, flowUsages } = await dispatchWorkFlow({
|
||||
const { flowResponses, flowUsages, newVariables } = await dispatchWorkFlow({
|
||||
res,
|
||||
mode: 'test',
|
||||
teamId,
|
||||
@@ -78,11 +81,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
appId,
|
||||
runtimeNodes: nodes,
|
||||
runtimeEdges: edges,
|
||||
variables: {
|
||||
...variables,
|
||||
userChatInput: text
|
||||
},
|
||||
inputFiles: files,
|
||||
variables,
|
||||
query: removeEmptyUserInput(prompt),
|
||||
histories: history,
|
||||
stream: true,
|
||||
detail: true,
|
||||
@@ -99,6 +99,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
event: SseResponseEventEnum.flowResponses,
|
||||
data: JSON.stringify(flowResponses)
|
||||
});
|
||||
responseWrite({
|
||||
res,
|
||||
event: SseResponseEventEnum.updateVariables,
|
||||
data: JSON.stringify(newVariables)
|
||||
});
|
||||
res.end();
|
||||
|
||||
pushChatUsage({
|
||||
|
||||
@@ -53,11 +53,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
appId,
|
||||
runtimeNodes: nodes,
|
||||
runtimeEdges: edges,
|
||||
variables: {
|
||||
...variables,
|
||||
userChatInput: ''
|
||||
},
|
||||
inputFiles: [],
|
||||
variables,
|
||||
query: [],
|
||||
histories: [],
|
||||
stream: false,
|
||||
detail: true,
|
||||
|
||||
72
projects/app/src/pages/api/plugins/TFSwitch/index.ts
Normal file
72
projects/app/src/pages/api/plugins/TFSwitch/index.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
// @ts-ignore
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/module/api.d';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { authRequestFromLocal } from '@fastgpt/service/support/permission/auth/common';
|
||||
|
||||
type Props = HttpBodyType<{
|
||||
input: string;
|
||||
rule?: string;
|
||||
}>;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { input, rule = '' } = req.body as Props;
|
||||
|
||||
await authRequestFromLocal({ req });
|
||||
|
||||
const result = (() => {
|
||||
if (typeof input === 'string') {
|
||||
const defaultReg: any[] = [
|
||||
'',
|
||||
undefined,
|
||||
'undefined',
|
||||
null,
|
||||
'null',
|
||||
false,
|
||||
'false',
|
||||
0,
|
||||
'0',
|
||||
'none'
|
||||
];
|
||||
const customReg = rule.split('\n');
|
||||
defaultReg.push(...customReg);
|
||||
|
||||
return !defaultReg.find((item) => {
|
||||
const reg = typeof item === 'string' ? stringToRegex(item) : null;
|
||||
if (reg) {
|
||||
return reg.test(input);
|
||||
}
|
||||
return input === item;
|
||||
});
|
||||
}
|
||||
|
||||
return !!input;
|
||||
})();
|
||||
|
||||
res.json({
|
||||
...(result
|
||||
? {
|
||||
true: true
|
||||
}
|
||||
: {
|
||||
false: false
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(500).send(getErrText(err));
|
||||
}
|
||||
}
|
||||
|
||||
function stringToRegex(str: string) {
|
||||
const regexFormat = /^\/(.+)\/([gimuy]*)$/;
|
||||
const match = str.match(regexFormat);
|
||||
|
||||
if (match) {
|
||||
const [, pattern, flags] = match;
|
||||
return new RegExp(pattern, flags);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,36 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/workflow/api.d';
|
||||
//@ts-ignore
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/module/api.d';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { addCustomFeedbacks } from '@fastgpt/service/core/chat/controller';
|
||||
import { authRequestFromLocal } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
type Props = HttpBodyType<{
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
responseChatItemId?: string;
|
||||
defaultFeedback: string;
|
||||
customFeedback: string;
|
||||
}>;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const {
|
||||
customFeedback,
|
||||
system_addInputParam: { appId, chatId, responseChatItemId: chatItemId }
|
||||
appId,
|
||||
chatId,
|
||||
responseChatItemId: chatItemId,
|
||||
defaultFeedback,
|
||||
customFeedback
|
||||
} = req.body as Props;
|
||||
|
||||
await authRequestFromLocal({ req });
|
||||
|
||||
if (!customFeedback) {
|
||||
return res.json({});
|
||||
const feedback = customFeedback || defaultFeedback;
|
||||
|
||||
if (!feedback) {
|
||||
return res.json({
|
||||
response: ''
|
||||
});
|
||||
}
|
||||
|
||||
// wait the chat finish
|
||||
@@ -28,17 +39,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
feedbacks: [customFeedback]
|
||||
feedbacks: [feedback]
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
if (!chatId || !chatItemId) {
|
||||
return res.json({
|
||||
[NodeOutputKeyEnum.answerText]: `\\n\\n**自动反馈调试**: "${customFeedback}"\\n\\n`
|
||||
response: `\\n\\n**自动反馈调试**: ${feedback}\\n\\n`
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({});
|
||||
return res.json({
|
||||
response: ''
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(500).send(getErrText(err));
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/workflow/api.d';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { addCustomFeedbacks } from '@fastgpt/service/core/chat/controller';
|
||||
import { authRequestFromLocal } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
type Props = HttpBodyType<{
|
||||
customFeedback: string;
|
||||
}>;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const {
|
||||
customFeedback,
|
||||
[NodeInputKeyEnum.addInputParam]: { appId, chatId, responseChatItemId: chatItemId }
|
||||
} = req.body as Props;
|
||||
|
||||
await authRequestFromLocal({ req });
|
||||
|
||||
if (!customFeedback) {
|
||||
return res.json({});
|
||||
}
|
||||
|
||||
// wait the chat finish
|
||||
setTimeout(() => {
|
||||
addCustomFeedbacks({
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
feedbacks: [customFeedback]
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
if (!chatId || !chatItemId) {
|
||||
return res.json({
|
||||
[NodeOutputKeyEnum.answerText]: `\\n\\n**自动反馈调试**: "${customFeedback}"\\n\\n`
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(500).send(getErrText(err));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/workflow/api.d';
|
||||
//@ts-ignore
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/module/api.d';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { authRequestFromLocal } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
type Props = HttpBodyType<{
|
||||
text: string;
|
||||
@@ -12,7 +12,10 @@ type Props = HttpBodyType<{
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { text, [NodeInputKeyEnum.addInputParam]: obj } = req.body as Props;
|
||||
const {
|
||||
text,
|
||||
DYNAMIC_INPUT_KEY: { ...obj }
|
||||
} = req.body as Props;
|
||||
|
||||
await authRequestFromLocal({ req });
|
||||
|
||||
|
||||
42
projects/app/src/pages/api/plugins/textEditor/v2/index.ts
Normal file
42
projects/app/src/pages/api/plugins/textEditor/v2/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { HttpBodyType } from '@fastgpt/global/core/workflow/api.d';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { authRequestFromLocal } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
|
||||
type Props = HttpBodyType<{
|
||||
text: string;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { text, [NodeInputKeyEnum.addInputParam]: obj } = req.body as Props;
|
||||
|
||||
await authRequestFromLocal({ req });
|
||||
|
||||
// string all value
|
||||
Object.keys(obj).forEach((key) => {
|
||||
let val = obj[key];
|
||||
|
||||
if (typeof val === 'object') {
|
||||
val = JSON.stringify(val);
|
||||
} else if (typeof val === 'number') {
|
||||
val = String(val);
|
||||
} else if (typeof val === 'boolean') {
|
||||
val = val ? 'true' : 'false';
|
||||
}
|
||||
|
||||
obj[key] = val;
|
||||
});
|
||||
|
||||
const textResult = replaceVariable(text, obj);
|
||||
res.json({
|
||||
text: textResult
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.status(500).send(getErrText(err));
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,10 @@ import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/out
|
||||
import requestIp from 'request-ip';
|
||||
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
|
||||
import {
|
||||
filterPublicNodeResponseData,
|
||||
removeEmptyUserInput
|
||||
} from '@fastgpt/global/core/chat/utils';
|
||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
|
||||
@@ -42,7 +45,6 @@ import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runti
|
||||
import { dispatchWorkFlowV1 } from '@fastgpt/service/core/workflow/dispatchV1';
|
||||
import { setEntryEntries } from '@fastgpt/service/core/workflow/dispatchV1/utils';
|
||||
import { NextAPI } from '@/service/middle/entry';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/controller';
|
||||
|
||||
type FastGptWebChatProps = {
|
||||
@@ -179,7 +181,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
|
||||
|
||||
/* start flow controller */
|
||||
const { flowResponses, flowUsages, assistantResponses } = await (async () => {
|
||||
const { flowResponses, flowUsages, assistantResponses, newVariables } = await (async () => {
|
||||
if (app.version === 'v2') {
|
||||
return dispatchWorkFlow({
|
||||
res,
|
||||
@@ -192,11 +194,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
responseChatItemId,
|
||||
runtimeNodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(edges),
|
||||
variables: {
|
||||
...variables,
|
||||
userChatInput: text
|
||||
},
|
||||
inputFiles: files,
|
||||
variables,
|
||||
query: removeEmptyUserInput(question.value),
|
||||
histories: concatHistories,
|
||||
stream,
|
||||
detail,
|
||||
@@ -247,7 +246,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
variables,
|
||||
variables: newVariables,
|
||||
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
|
||||
shareId,
|
||||
outLinkUid: outLinkUserId,
|
||||
@@ -288,6 +287,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
event: detail ? SseResponseEventEnum.answer : undefined,
|
||||
data: '[DONE]'
|
||||
});
|
||||
responseWrite({
|
||||
res,
|
||||
event: SseResponseEventEnum.updateVariables,
|
||||
data: JSON.stringify(newVariables)
|
||||
});
|
||||
|
||||
if (responseDetail && detail) {
|
||||
responseWrite({
|
||||
|
||||
@@ -75,6 +75,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
WorkflowContext,
|
||||
(v) => v.setIsShowVersionHistories
|
||||
);
|
||||
const workflowDebugData = useContextSelector(WorkflowContext, (v) => v.workflowDebugData);
|
||||
|
||||
const flowData2StoreDataAndCheck = useCallback(async () => {
|
||||
const { nodes } = await getWorkflowStore();
|
||||
@@ -93,35 +94,40 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
}
|
||||
}, [edges, onUpdateNodeError, t, toast]);
|
||||
|
||||
const onclickSave = useCallback(async () => {
|
||||
if (isShowVersionHistories) return;
|
||||
const { nodes } = await getWorkflowStore();
|
||||
const onclickSave = useCallback(
|
||||
async (forbid?: boolean) => {
|
||||
// version preview / debug mode, not save
|
||||
if (isShowVersionHistories || forbid) return;
|
||||
|
||||
if (nodes.length === 0) return null;
|
||||
setIsSaving(true);
|
||||
const { nodes } = await getWorkflowStore();
|
||||
|
||||
const storeWorkflow = flowNode2StoreNodes({ nodes, edges });
|
||||
if (nodes.length === 0) return null;
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
await updateAppDetail(app._id, {
|
||||
...storeWorkflow,
|
||||
type: AppTypeEnum.advanced,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
const storeWorkflow = flowNode2StoreNodes({ nodes, edges });
|
||||
|
||||
setSaveLabel(
|
||||
t('core.app.Auto Save time', {
|
||||
time: formatTime2HM()
|
||||
})
|
||||
);
|
||||
// ChatTestRef.current?.resetChatTest();
|
||||
} catch (error) {}
|
||||
try {
|
||||
await updateAppDetail(app._id, {
|
||||
...storeWorkflow,
|
||||
type: AppTypeEnum.advanced,
|
||||
//@ts-ignore
|
||||
version: 'v2'
|
||||
});
|
||||
|
||||
setIsSaving(false);
|
||||
setSaveLabel(
|
||||
t('core.app.Auto Save time', {
|
||||
time: formatTime2HM()
|
||||
})
|
||||
);
|
||||
// ChatTestRef.current?.resetChatTest();
|
||||
} catch (error) {}
|
||||
|
||||
return null;
|
||||
}, [isShowVersionHistories, edges, updateAppDetail, app._id, t]);
|
||||
setIsSaving(false);
|
||||
|
||||
return null;
|
||||
},
|
||||
[isShowVersionHistories, edges, updateAppDetail, app._id, t]
|
||||
);
|
||||
|
||||
const onclickPublish = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
@@ -182,7 +188,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
|
||||
useInterval(() => {
|
||||
if (!app._id) return;
|
||||
onclickSave();
|
||||
onclickSave(!!workflowDebugData);
|
||||
}, 20000);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
@@ -221,7 +227,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
display={'inline-block'}
|
||||
borderRadius={'xs'}
|
||||
cursor={'pointer'}
|
||||
onClick={onclickSave}
|
||||
onClick={() => onclickSave()}
|
||||
color={'myGray.500'}
|
||||
>
|
||||
{saveLabel}
|
||||
@@ -232,39 +238,43 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
|
||||
|
||||
<Box flex={1} />
|
||||
|
||||
<MyMenu
|
||||
Button={
|
||||
{!isShowVersionHistories && (
|
||||
<>
|
||||
<MyMenu
|
||||
Button={
|
||||
<IconButton
|
||||
mr={[2, 4]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
variant={'whitePrimary'}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
label: t('app.Import Configs'),
|
||||
icon: 'common/importLight',
|
||||
onClick: onOpenImport
|
||||
},
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: onExportWorkflow
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
mr={[2, 4]}
|
||||
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
|
||||
icon={<MyIcon name={'history'} w={'18px'} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
w={'30px'}
|
||||
variant={'whitePrimary'}
|
||||
onClick={() => setIsShowVersionHistories(true)}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
label: t('app.Import Configs'),
|
||||
icon: 'common/importLight',
|
||||
onClick: onOpenImport
|
||||
},
|
||||
{
|
||||
label: t('app.Export Configs'),
|
||||
icon: 'export',
|
||||
onClick: onExportWorkflow
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
mr={[2, 4]}
|
||||
icon={<MyIcon name={'history'} w={'18px'} />}
|
||||
aria-label={''}
|
||||
size={'sm'}
|
||||
w={'30px'}
|
||||
variant={'whitePrimary'}
|
||||
onClick={() => setIsShowVersionHistories(true)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button
|
||||
size={'sm'}
|
||||
|
||||
@@ -28,7 +28,7 @@ const Render = ({ app, onClose }: Props) => {
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow) return;
|
||||
initData(JSON.parse(workflowStringData));
|
||||
}, [isV2Workflow, initData, workflowStringData]);
|
||||
}, [isV2Workflow, initData, app._id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isV2Workflow) {
|
||||
|
||||
@@ -62,7 +62,7 @@ const ChatTest = ({
|
||||
}
|
||||
});
|
||||
});
|
||||
const history = chatList.slice(-historyMaxLen - 2, -2);
|
||||
const history = chatList.slice(-(historyMaxLen * 2) - 2, -2);
|
||||
|
||||
// 流请求,获取数据
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
|
||||
@@ -74,7 +74,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
const prompts = messages.slice(-2);
|
||||
const completionChatId = chatId ? chatId : nanoid();
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables,
|
||||
@@ -123,7 +123,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||
);
|
||||
|
||||
@@ -95,12 +95,12 @@ const OutLink = ({
|
||||
'*'
|
||||
);
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables: {
|
||||
...customVariables,
|
||||
...variables
|
||||
...variables,
|
||||
...customVariables
|
||||
},
|
||||
shareId,
|
||||
chatId: completionChatId,
|
||||
@@ -169,7 +169,7 @@ const OutLink = ({
|
||||
'*'
|
||||
);
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[
|
||||
chatId,
|
||||
|
||||
@@ -79,7 +79,7 @@ const OutLink = () => {
|
||||
const prompts = messages.slice(-2);
|
||||
const completionChatId = chatId ? chatId : nanoid();
|
||||
|
||||
const { responseText, responseData } = await streamFetch({
|
||||
const { responseText, responseData, newVariables } = await streamFetch({
|
||||
data: {
|
||||
messages: prompts,
|
||||
variables: {
|
||||
@@ -135,7 +135,7 @@ const OutLink = () => {
|
||||
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||
}));
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current, newVariables };
|
||||
},
|
||||
[appId, teamToken, chatId, histories, pushHistory, router, setChatData, teamId, updateHistory]
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ export const defaultForm: EditFormType = {
|
||||
modules: [
|
||||
{
|
||||
nodeId: nanoid(),
|
||||
name: '定义插件输入',
|
||||
name: '自定义插件输入',
|
||||
avatar: '/imgs/workflow/input.png',
|
||||
flowNodeType: 'pluginInput',
|
||||
showStatus: false,
|
||||
@@ -44,7 +44,7 @@ export const defaultForm: EditFormType = {
|
||||
},
|
||||
{
|
||||
nodeId: nanoid(),
|
||||
name: '定义插件输出',
|
||||
name: '自定义插件输出',
|
||||
avatar: '/imgs/workflow/output.png',
|
||||
flowNodeType: 'pluginOutput',
|
||||
showStatus: false,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
|
||||
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
getDefaultEntryNodeIds,
|
||||
initWorkflowEdgeStatus,
|
||||
@@ -34,9 +35,15 @@ export const getScheduleTriggerApp = async () => {
|
||||
appId: String(app._id),
|
||||
runtimeNodes: storeNodes2RuntimeNodes(app.modules, getDefaultEntryNodeIds(app.modules)),
|
||||
runtimeEdges: initWorkflowEdgeStatus(app.edges),
|
||||
variables: {
|
||||
userChatInput: app.scheduledTriggerConfig?.defaultPrompt
|
||||
},
|
||||
variables: {},
|
||||
query: [
|
||||
{
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: app.scheduledTriggerConfig?.defaultPrompt
|
||||
}
|
||||
}
|
||||
],
|
||||
histories: [],
|
||||
stream: false,
|
||||
detail: false,
|
||||
|
||||
@@ -42,7 +42,6 @@ export const pushChatUsage = ({
|
||||
addLog.info(`finish completions`, {
|
||||
source,
|
||||
teamId,
|
||||
tmbId,
|
||||
totalPoints
|
||||
});
|
||||
return { totalPoints };
|
||||
|
||||
@@ -69,6 +69,7 @@ export async function saveChat({
|
||||
chat.title = title;
|
||||
chat.updateTime = new Date();
|
||||
chat.metadata = metadataUpdate;
|
||||
chat.variables = variables || {};
|
||||
await chat.save({ session });
|
||||
} else {
|
||||
await MongoChat.create(
|
||||
|
||||
@@ -22,6 +22,7 @@ type StreamFetchProps = {
|
||||
type StreamResponseType = {
|
||||
responseText: string;
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[];
|
||||
newVariables: Record<string, any>;
|
||||
};
|
||||
class FatalError extends Error {}
|
||||
|
||||
@@ -50,6 +51,7 @@ export const streamFetch = ({
|
||||
)[] = [];
|
||||
let errMsg: string | undefined;
|
||||
let responseData: ChatHistoryItemResType[] = [];
|
||||
let newVariables: Record<string, any> = {};
|
||||
let finished = false;
|
||||
|
||||
const finish = () => {
|
||||
@@ -57,6 +59,7 @@ export const streamFetch = ({
|
||||
return failedFinish();
|
||||
}
|
||||
return resolve({
|
||||
newVariables,
|
||||
responseText,
|
||||
responseData
|
||||
});
|
||||
@@ -198,6 +201,8 @@ export const streamFetch = ({
|
||||
});
|
||||
} else if (event === SseResponseEventEnum.flowResponses && Array.isArray(parseJson)) {
|
||||
responseData = parseJson;
|
||||
} else if (event === SseResponseEventEnum.updateVariables) {
|
||||
newVariables = parseJson;
|
||||
} else if (event === SseResponseEventEnum.error) {
|
||||
if (parseJson.statusText === TeamErrEnum.aiPointsNotEnough) {
|
||||
useSystemStore.getState().setIsNotSufficientModal(true);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { EmptyNode } from '@fastgpt/global/core/workflow/template/system/emptyNo
|
||||
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { getGlobalVariableNode } from './adapt';
|
||||
import { VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { VARIABLE_NODE_ID, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { EditorVariablePickerType } from '@fastgpt/web/components/common/Textarea/PromptEditor/type';
|
||||
import {
|
||||
@@ -24,6 +24,9 @@ import {
|
||||
} from '@fastgpt/global/core/workflow/utils';
|
||||
import { getSystemVariables } from '../app/utils';
|
||||
import { TFunction } from 'next-i18next';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
|
||||
export const nodeTemplate2FlowNode = ({
|
||||
template,
|
||||
@@ -140,6 +143,26 @@ export const computedNodeInputReference = ({
|
||||
|
||||
return sourceNodes;
|
||||
};
|
||||
export const getReferenceDataValueType = ({
|
||||
variable,
|
||||
nodeList,
|
||||
t
|
||||
}: {
|
||||
variable?: ReferenceValueProps;
|
||||
nodeList: FlowNodeItemType[];
|
||||
t: TFunction;
|
||||
}) => {
|
||||
if (!variable) return WorkflowIOValueTypeEnum.any;
|
||||
|
||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
||||
const systemVariables = getWorkflowGlobalVariables(nodeList, t);
|
||||
|
||||
if (!node) return systemVariables.find((item) => item.key === variable?.[1])?.valueType;
|
||||
|
||||
const output = node.outputs.find((item) => item.id === variable[1]);
|
||||
if (!output) return WorkflowIOValueTypeEnum.any;
|
||||
return output.valueType;
|
||||
};
|
||||
|
||||
/* Connection rules */
|
||||
export const checkWorkflowNodeAndConnection = ({
|
||||
@@ -167,6 +190,29 @@ export const checkWorkflowNodeAndConnection = ({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.flowNodeType === FlowNodeTypeEnum.ifElseNode) {
|
||||
const ifElseList: IfElseListItemType[] = inputs.find(
|
||||
(input) => input.key === NodeInputKeyEnum.ifElseList
|
||||
)?.value;
|
||||
if (
|
||||
ifElseList.some((item) => {
|
||||
return item.list.some((listItem) => {
|
||||
return (
|
||||
listItem.variable === undefined ||
|
||||
listItem.condition === undefined ||
|
||||
(listItem.value === undefined &&
|
||||
listItem.condition !== VariableConditionEnum.isEmpty &&
|
||||
listItem.condition !== VariableConditionEnum.isNotEmpty)
|
||||
);
|
||||
});
|
||||
})
|
||||
) {
|
||||
return [data.nodeId];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check node input
|
||||
if (
|
||||
inputs.some((input) => {
|
||||
@@ -243,7 +289,10 @@ export const getWorkflowGlobalVariables = (
|
||||
): EditorVariablePickerType[] => {
|
||||
const globalVariables = formatEditorVariablePickerIcon(
|
||||
splitGuideModule(getGuideModule(nodes))?.variableModules || []
|
||||
);
|
||||
).map((item) => ({
|
||||
...item,
|
||||
valueType: WorkflowIOValueTypeEnum.any
|
||||
}));
|
||||
|
||||
const systemVariables = getSystemVariables(t);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user