Compare commits

...

19 Commits

Author SHA1 Message Date
Archer
caa0755d9a perf: global variable any type (#1387) 2024-05-07 18:54:32 +08:00
Archer
fef1a1702b 4.8 test fix (#1385)
* fix: tool name cannot startwith number

* fix: chatbox update

* fix: chatbox

* perf: drag ui

* perf: drag component

* drag component
2024-05-07 18:41:34 +08:00
heheer
2a99e46353 fix: if else node (#1383)
* fix: if else node

* fix

* fix
2024-05-07 17:16:33 +08:00
Archer
8f9203c053 4.8 test (#1382)
* perf: some log, chatTest histories slice; http request failed tip

* fix: ssr render

* perf: if else node ui and fix value type select
2024-05-07 15:27:05 +08:00
heheer
2053bbdb1b feat: add elseif to ifelse node (#1378) 2024-05-07 14:50:58 +08:00
Archer
9e192c6d11 Ai histories (#1376)
* perf: workflow node ui

* i18n

* rename controller

* fix: zindex

* fix: leave page callback

* revert button
2024-05-07 13:32:01 +08:00
Archer
eef609a063 Fix 4.8 node (#1370)
* perf: runtime props

* fix: Plugin run faied in debug mode

* perf: variable update

* fix: ts

* perf: variable ui
2024-05-06 17:13:50 +08:00
heheer
5bb9c550f6 fix: add judge to update variable node input (#1369) 2024-05-06 15:54:15 +08:00
Archer
db1c27cdc7 feat: adapt v1 system plugin (#1366) 2024-05-06 15:21:29 +08:00
heheer
8863337606 fix: reference input of updateVariable node (#1367)
* fix: reference input of updateVariable node

* fix
2024-05-06 13:51:15 +08:00
heheer
59bd2a47b6 feat: add update variable node (#1362)
* feat: add variable update node

* fix

* fix

* change component quote
2024-05-06 12:20:29 +08:00
Archer
d057ba29f0 feat: custom feedback plugin (#1365) 2024-05-06 11:01:50 +08:00
Archer
b500631a4d fix: leave user will can not login (#1345) 2024-05-06 10:41:01 +08:00
Carson Yang
bf6084da69 Enhance English language support in i18n (#1348)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2024-05-02 08:35:59 +08:00
Archer
b5f0ac3e1d Perf: read file woker (#1337)
* perf: read file worker

* fix: Http node url input

* fix: htm2md

* fix: html2md

* fix: ts

* perf: Problem classification increases the matching order

* feat: tool response answer
2024-04-30 18:12:20 +08:00
Cheer
1529c1e991 fix: issues/1334 useTransition 导致光标刷新后移问题; (#1338) 2024-04-30 15:59:39 +08:00
Archer
db6fc53840 Publish histories (#1331)
* fix http plugin edge (#95)

* fix http plugin edge

* use getHandleId

* perf: i18n file

* feat: histories list

* perf: request lock

* fix: ts

* move box components

* fix: edit form refresh

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-04-30 12:42:13 +08:00
Archer
a0c1320d47 4.8-preview fix (#1324)
* feishu app release (#85)

* Revert "lafAccount add pat & re request when token invalid (#76)" (#77)

This reverts commit 83d85dfe37adcaef4833385ea52ee79fd84720be.

* perf: workflow ux

* system config

* feat: feishu app release

* chore: sovle the conflicts files; fix the feishu entry

* fix: rename Feishu interface to FeishuType

* fix: fix type problem in app.ts

* fix: type problem

* fix: style problem

---------

Co-authored-by: Archer <545436317@qq.com>

* perf: publish channel code

* change system variable position (#94)

* perf: workflow context

* perf: variable select

* hide publish

* perf: simple edit auto refresh

* perf: simple edit data refresh

* fix: target handle

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-04-29 11:13:10 +08:00
Cheer
5ca4049757 feat: 增加自定义 meta description; (#1246)
* feat: 增加自定义 meta description;

* fix: 环境变量使用错误;

---------

Co-authored-by: junshun.mq <junshun.mq@alibaba-inc.com>
2024-04-28 18:31:47 +08:00
237 changed files with 8133 additions and 4366 deletions

View File

@@ -21,7 +21,7 @@ assignees: ''
- [ ] 公有云版本
- [ ] 私有部署版本, 具体版本号:
**问题描述**
**问题描述, 日志截图**
**复现步骤**

View File

@@ -17,7 +17,7 @@
"",
"type Response = {};",
"",
"async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {",
"async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<Response> {",
" $1",
" return {}",
"}",

View File

@@ -4,12 +4,12 @@
"typescript.tsdk": "node_modules/typescript/lib",
"prettier.prettierPath": "",
"i18n-ally.localesPaths": [
"projects/app/public/locales",
"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" // 显示语言
}

View File

@@ -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. 查看模型请求日志,检查出入参数是否异常。

View File

@@ -106,6 +106,7 @@ FastGPT 商业版共包含了2个应用fastgpt, fastgpt-plus和2个数据
```
SYSTEM_NAME=FastGPT
SYSTEM_DESCRIPTION=
SYSTEM_FAVICON=/favicon.ico
HOME_URL=/app/list
```

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8(进行中)'
title: 'V4.8(开发中)'
description: 'FastGPT V4.8 更新说明'
icon: 'upgrade'
draft: false
@@ -18,8 +18,14 @@ FastGPT workflow V2上线支持更加简洁的工作流模式。
## V4.8 更新说明
1. 重构 - 工作流
2. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流
3. 新增 - 定时执行应用。可轻松实现定时任务
4. 新增 - 插件自定义输入优化,可以渲染输入组件
5. 优化 - 工作流连线,可以四向连接,方便构建循环工作流
6. 优化 - worker进程管理并将计算 Token 任务分配给 worker 进程。
2. 新增 - 判断器。支持 if elseIf else 判断
3. 新增 - 变量更新节点。支持更新运行中工作流输出变量,或更新全局变量
4. 新增 - 工作流 Debug 模式,可以调试单个节点或者逐步调试工作流
5. 新增 - 定时执行应用。可轻松实现定时任务
6. 新增 - 插件自定义输入优化,可以渲染输入组件。
7. 优化 - 工作流连线,可以四向连接,方便构建循环工作流。
8. 优化 - 工作流上下文传递,性能🚀。
9. 优化 - 简易模式,更新配置后自动更新调试框内容,无需保存。
10. 优化 - worker进程管理并将计算 Token 任务分配给 worker 进程。
11. 修复 - 工具调用时候name不能是数字开头随机数有概率数字开头
12. 修复 - 分享链接, query 全局变量会被缓存。

View File

@@ -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, '\\$&');

View File

@@ -37,6 +37,7 @@ export type FastGPTFeConfigsType = {
chatbotUrl?: string;
openAPIDocUrl?: string;
systemTitle?: string;
systemDescription?: string;
googleClientVerKey?: string;
isPlus?: boolean;
show_phoneLogin?: boolean;

View File

@@ -125,6 +125,7 @@ export const appWorkflow2Form = ({ nodes }: { nodes: StoreNodeItemType[] }) => {
defaultAppForm.selectedTools.push({
id: node.pluginId,
pluginId: node.pluginId,
name: node.name,
avatar: node.avatar,
intro: node.intro || '',

View File

@@ -2,6 +2,7 @@ import { StoreNodeItemType } from '../workflow/type';
import { StoreEdgeItemType } from '../workflow/type/edge';
export type AppVersionSchemaType = {
_id: string;
appId: string;
time: Date;
nodes: StoreNodeItemType[];

View File

@@ -155,6 +155,6 @@ export type ToolModuleResponseItemType = {
/* dispatch run time */
export type RuntimeUserPromptType = {
files?: UserChatItemValueItemType['file'][];
files: UserChatItemValueItemType['file'][];
text: string;
};

View File

@@ -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;
});
};

View File

@@ -14,6 +14,7 @@ import { CreateOnePluginParams } from '../controller';
import { StoreNodeItemType } from '../../workflow/type';
import { HttpImgUrl } from '../../../common/file/image/constants';
import SwaggerParser from '@apidevtools/swagger-parser';
import { getHandleId } from '../../../core/workflow/utils';
export const str2OpenApiSchema = async (yamlStr = ''): Promise<OpenApiJsonSchema> => {
try {
@@ -378,14 +379,14 @@ export const httpApiSchema2Plugins = async ({
{
source: pluginInputId,
target: httpId,
sourcePort: `${pluginInputId}-source-right`,
targetPort: `${httpId}-target-left`
sourceHandle: getHandleId(pluginInputId, 'source', 'right'),
targetHandle: getHandleId(httpId, 'target', 'left')
},
{
source: httpId,
target: pluginOutputId,
sourcePort: `${httpId}-source-right`,
targetPort: `${pluginOutputId}-target-left`
sourceHandle: getHandleId(httpId, 'source', 'right'),
targetHandle: getHandleId(pluginOutputId, 'target', 'left')
}
];

View File

@@ -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 或通过【变量更新】模块进行赋值。'
}
};

View File

@@ -112,7 +112,8 @@ export enum FlowNodeTypeEnum {
tools = 'tools',
stopTool = 'stopTool',
lafModule = 'lafModule',
ifElseNode = 'ifElseNode'
ifElseNode = 'ifElseNode',
variableUpdate = 'variableUpdate'
}
export const EDGE_TYPE = 'default';

View File

@@ -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 {

View File

@@ -75,7 +75,7 @@ export type DispatchNodeResponseType = {
pluginDetail?: ChatHistoryItemResType[];
// if-else
ifElseResult?: 'IF' | 'ELSE';
ifElseResult?: string;
// tool
toolCallTokens?: number;

View File

@@ -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];

View File

@@ -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,44 +82,7 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
PluginOutputModule,
RunPluginModule,
AiQueryExtension,
lafModule,
ifElseNode
];
export const moduleTemplatesList: nodeTemplateListType = [
{
type: FlowNodeTemplateTypeEnum.systemInput,
label: 'core.module.template.System input module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.textAnswer,
label: 'core.module.template.Response module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.functionCall,
label: 'core.module.template.Function module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.tools,
label: 'core.module.template.Tool module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.externalCall,
label: 'core.module.template.External module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.personalPlugin,
label: '',
list: []
},
{
type: FlowNodeTemplateTypeEnum.other,
label: '其他',
list: []
}
LafModule,
IfElseNode,
VariableUpdateNode
];

View File

@@ -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
};

View File

@@ -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'

View File

@@ -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
}))
};

View File

@@ -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 },

View File

@@ -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
}
]
};

View File

@@ -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[];
};

View File

@@ -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,

View File

@@ -12,7 +12,7 @@ export const PluginInputModule: FlowNodeTemplateType = {
unique: true,
forbidDelete: true,
avatar: '/imgs/workflow/input.png',
name: '定义插件输入',
name: '定义插件输入',
intro: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入',
showStatus: false,
inputs: [],

View File

@@ -12,7 +12,7 @@ export const PluginOutputModule: FlowNodeTemplateType = {
unique: true,
forbidDelete: true,
avatar: '/imgs/workflow/output.png',
name: '定义插件输出',
name: '定义插件输出',
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
showStatus: false,
inputs: [],

View File

@@ -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: [
@@ -64,5 +64,14 @@ export const ToolModule: FlowNodeTemplateType = {
Input_Template_History,
Input_Template_UserChatInput
],
outputs: []
outputs: [
{
id: NodeOutputKeyEnum.answerText,
key: NodeOutputKeyEnum.answerText,
label: 'core.module.output.label.Ai response content',
description: 'core.module.output.description.Ai response content',
valueType: WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static
}
]
};

View File

@@ -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: []
};

View 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;
};

View File

@@ -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 & {

View File

@@ -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}`;
};

View File

@@ -1,5 +1,6 @@
export enum OutLinkTypeEnum {
export enum PublishChannelEnum {
share = 'share',
iframe = 'iframe',
apikey = 'apikey'
apikey = 'apikey',
feishu = 'feishu'
}

View File

@@ -1,31 +1,79 @@
import { AppSchema } from 'core/app/type';
import { OutLinkTypeEnum } from './constant';
import { PublishChannelEnum } from './constant';
export type OutLinkSchema = {
// Feishu Config interface
export interface FeishuType {
appId: string;
appSecret: string;
// Encrypt config
// refer to: https://open.feishu.cn/document/server-docs/event-subscription-guide/event-subscription-configure-/configure-encrypt-key
encryptKey?: string; // no secret if null
// Token Verification
// refer to: https://open.feishu.cn/document/server-docs/event-subscription-guide/event-subscription-configure-/encrypt-key-encryption-configuration-case
verificationToken: string;
}
// TODO: Unused
export interface WecomType {
ReplyLimit: Boolean;
defaultResponse: string;
immediateResponse: boolean;
WXWORK_TOKEN: string;
WXWORK_AESKEY: string;
WXWORK_SECRET: string;
WXWORD_ID: string;
}
export type OutLinkSchema<T = void> = {
_id: string;
shareId: string;
teamId: string;
tmbId: string;
appId: string;
// teamId: Schema.Types.ObjectId;
// tmbId: Schema.Types.ObjectId;
// appId: Schema.Types.ObjectId;
name: string;
usagePoints: number;
lastTime: Date;
type: `${OutLinkTypeEnum}`;
type: PublishChannelEnum;
// whether the response content is detailed
responseDetail: boolean;
// response when request
immediateResponse?: string;
// response when error or other situation
defaultResponse?: string;
limit?: {
expiredTime?: Date;
// Questions per minute
QPM: number;
maxUsagePoints: number;
// Verification message hook url
hookUrl?: string;
};
app?: T;
};
// to handle MongoDB querying
export type OutLinkWithAppType = Omit<OutLinkSchema, 'appId'> & {
appId: AppSchema;
};
export type OutLinkEditType = {
// Edit the Outlink
export type OutLinkEditType<T = void> = {
_id?: string;
name: string;
responseDetail: OutLinkSchema['responseDetail'];
limit: OutLinkSchema['limit'];
responseDetail: OutLinkSchema<T>['responseDetail'];
// response when request
immediateResponse?: string;
// response when error or other situation
defaultResponse?: string;
limit?: OutLinkSchema<T>['limit'];
// config for specific platform
app?: T;
};

View File

@@ -6,7 +6,6 @@ import { DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
import { MongoFileSchema } from './schema';
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { ReadFileByBufferParams } from '../read/type';
import { MongoRwaTextBuffer } from '../../buffer/rawText/schema';
import { readFileRawContent } from '../read/utils';
import { PassThrough } from 'stream';
@@ -197,19 +196,15 @@ export const readFileContentFromMongo = async ({
});
})();
const params: ReadFileByBufferParams = {
const { rawText } = await readFileRawContent({
extension,
csvFormat,
teamId,
buffer: fileBuffers,
encoding,
metadata: {
relatedId: fileId
}
};
const { rawText } = await readFileRawContent({
extension,
csvFormat,
params
});
if (rawText.trim()) {

View File

@@ -1,23 +0,0 @@
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import { initMarkdownText } from './utils';
import { htmlToMarkdown } from '../../string/markdown';
import { readFileRawText } from './rawText';
export const readHtmlRawText = async (
params: ReadFileByBufferParams
): Promise<ReadFileResponse> => {
const { teamId, metadata } = params;
const { rawText: html } = readFileRawText(params);
const md = await htmlToMarkdown(html);
const rawText = await initMarkdownText({
teamId,
md,
metadata
});
return {
rawText
};
};

View File

@@ -1,18 +0,0 @@
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import { initMarkdownText } from './utils';
import { readFileRawText } from './rawText';
export const readMarkdown = async (params: ReadFileByBufferParams): Promise<ReadFileResponse> => {
const { teamId, metadata } = params;
const { rawText: md } = readFileRawText(params);
const rawText = await initMarkdownText({
teamId,
md,
metadata
});
return {
rawText
};
};

View File

@@ -1,12 +0,0 @@
export type ReadFileByBufferParams = {
teamId: string;
buffer: Buffer;
encoding: string;
metadata?: Record<string, any>;
};
export type ReadFileResponse = {
rawText: string;
formatText?: string;
metadata?: Record<string, any>;
};

View File

@@ -1,16 +1,10 @@
import { markdownProcess } from '@fastgpt/global/common/string/markdown';
import { markdownProcess, simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
import { uploadMongoImg } from '../image/controller';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
import { addHours } from 'date-fns';
import { ReadFileByBufferParams } from './type';
import { readFileRawText } from '../read/rawText';
import { readMarkdown } from '../read/markdown';
import { readHtmlRawText } from '../read/html';
import { readPdfFile } from '../read/pdf';
import { readWordFile } from '../read/word';
import { readCsvRawText } from '../read/csv';
import { readPptxRawText } from '../read/pptx';
import { readXlsxRawText } from '../read/xlsx';
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
import { ReadFileResponse } from '../../../worker/file/type';
export const initMarkdownText = ({
teamId,
@@ -36,46 +30,39 @@ export const initMarkdownText = ({
export const readFileRawContent = async ({
extension,
csvFormat,
params
teamId,
buffer,
encoding,
metadata
}: {
csvFormat?: boolean;
extension: string;
params: ReadFileByBufferParams;
teamId: string;
buffer: Buffer;
encoding: string;
metadata?: Record<string, any>;
}) => {
switch (extension) {
case 'txt':
return readFileRawText(params);
case 'md':
return readMarkdown(params);
case 'html':
return readHtmlRawText(params);
case 'pdf':
return readPdfFile(params);
case 'docx':
return readWordFile(params);
case 'pptx':
return readPptxRawText(params);
case 'xlsx':
const xlsxResult = await readXlsxRawText(params);
if (csvFormat) {
return {
rawText: xlsxResult.formatText || ''
};
}
return {
rawText: xlsxResult.rawText
};
case 'csv':
const csvResult = await readCsvRawText(params);
if (csvFormat) {
return {
rawText: csvResult.formatText || ''
};
}
return {
rawText: csvResult.rawText
};
default:
return Promise.reject('Only support .txt, .md, .html, .pdf, .docx, pptx, .csv, .xlsx');
const result = await runWorker<ReadFileResponse>(WorkerNameEnum.readFile, {
extension,
csvFormat,
encoding,
buffer
});
// markdown data format
if (['md', 'html', 'docx'].includes(extension)) {
result.rawText = await initMarkdownText({
teamId: teamId,
md: result.rawText,
metadata: metadata
});
}
return result;
};
export const htmlToMarkdown = async (html?: string | null) => {
const md = await runWorker<string>(WorkerNameEnum.htmlStr2Md, { html: html || '' });
return simpleMarkdownText(md);
};

View File

@@ -1,35 +0,0 @@
import mammoth from 'mammoth';
import { htmlToMarkdown } from '../../string/markdown';
import { ReadFileByBufferParams, ReadFileResponse } from './type';
import { initMarkdownText } from './utils';
/**
* read docx to markdown
*/
export const readWordFile = async ({
teamId,
buffer,
metadata = {}
}: ReadFileByBufferParams): Promise<ReadFileResponse> => {
try {
const { value: html } = await mammoth.convertToHtml({
buffer
});
const md = await htmlToMarkdown(html);
const rawText = await initMarkdownText({
teamId,
md,
metadata
});
return {
rawText,
metadata: {}
};
} catch (error) {
console.log('error doc read:', error);
return Promise.reject('Can not read doc file, please convert to PDF');
}
};

View File

@@ -1,7 +1,7 @@
import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/api';
import * as cheerio from 'cheerio';
import axios from 'axios';
import { htmlToMarkdown } from './markdown';
import { htmlToMarkdown } from '../file/read/utils';
export const cheerioToHtml = ({
fetchUrl,
@@ -77,7 +77,9 @@ export const urlsFetch = async ({
$,
selector
});
console.log('html====', html);
const md = await htmlToMarkdown(html);
console.log('html====', md);
return {
url,

View File

@@ -1,9 +0,0 @@
import { simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
import { WorkerNameEnum, runWorker } from '../../worker/utils';
/* html string to markdown */
export const htmlToMarkdown = async (html?: string | null) => {
const md = await runWorker<string>(WorkerNameEnum.htmlStr2Md, { html: html || '' });
return simpleMarkdownText(md);
};

View File

@@ -23,7 +23,7 @@ export async function initPg() {
`);
await PgClient.query(
`CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${PgDatasetTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 64);`
`CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${PgDatasetTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 100);`
);
await PgClient.query(
`CREATE INDEX CONCURRENTLY IF NOT EXISTS team_dataset_collection_index ON ${PgDatasetTableName} USING btree(team_id, dataset_id, collection_id);`

View File

@@ -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 [];
});

View File

@@ -1,5 +1,6 @@
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
declare global {
var communityPluginsV1: PluginTemplateType[];
var communityPlugins: PluginTemplateType[];
}

View File

@@ -131,7 +131,9 @@ const completions = async ({
console.log(answer, '----');
const id =
agents.find((item) => answer.includes(item.key) || answer.includes(item.value))?.key || '';
agents.find((item) => answer.includes(item.key))?.key ||
agents.find((item) => answer.includes(item.value))?.key ||
'';
return {
tokens: await countMessagesTokens(messages),

View File

@@ -161,6 +161,7 @@ export const runToolWithFunctionCall = async (
const toolRunResponse = await dispatchWorkFlow({
...props,
isToolCall: true,
runtimeNodes: runtimeNodes.map((item) =>
item.nodeId === toolNode.nodeId
? {

View File

@@ -23,7 +23,9 @@ import { runToolWithPromptCall } from './promptCall';
import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { Prompt_Tool_Call } from './constants';
type Response = DispatchNodeResultType<{}>;
type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]: string;
}>;
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
const {
@@ -129,6 +131,10 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
const flatUsages = dispatchFlowResponse.map((item) => item.flowUsages).flat();
return {
[NodeOutputKeyEnum.answerText]: assistantResponses
.filter((item) => item.text?.content)
.map((item) => item.text?.content || '')
.join(''),
[DispatchNodeResponseKeyEnum.assistantResponses]: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
totalPoints: totalPointsUsage,

View File

@@ -185,6 +185,7 @@ export const runToolWithPromptCall = async (
const moduleRunResponse = await dispatchWorkFlow({
...props,
isToolCall: true,
runtimeNodes: runtimeNodes.map((item) =>
item.nodeId === toolNode.nodeId
? {

View File

@@ -182,6 +182,7 @@ export const runToolWithToolChoice = async (
const toolRunResponse = await dispatchWorkFlow({
...props,
isToolCall: true,
runtimeNodes: runtimeNodes.map((item) =>
item.nodeId === toolNode.nodeId
? {

View File

@@ -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');
}

View File

@@ -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
};
@@ -142,10 +148,8 @@ export async function dispatchWorkFlow({
}
if (assistantResponses) {
chatAssistantResponse = chatAssistantResponse.concat(assistantResponses);
}
// save assistant text response
if (answerText) {
} else if (answerText) {
// save assistant text response
const isResponseAnswerText =
inputs.find((item) => item.key === NodeInputKeyEnum.aiChatIsResponseText)?.value ?? true;
if (isResponseAnswerText) {
@@ -285,7 +289,8 @@ export async function dispatchWorkFlow({
node,
runtimeNodes,
runtimeEdges,
params
params,
mode: 'test'
};
// run module
@@ -364,7 +369,8 @@ export async function dispatchWorkFlow({
},
[DispatchNodeResponseKeyEnum.assistantResponses]:
mergeAssistantResponseAnswerText(chatAssistantResponse),
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse
[DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse,
newVariables: removeSystemVariable(variables)
};
}
@@ -386,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[] = [];

View File

@@ -0,0 +1,4 @@
export const dispatchSystemConfig = (props: Record<string, any>) => {
const { variables } = props;
return variables;
};

View File

@@ -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;
};

View File

@@ -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
};
};

View File

@@ -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';

View File

@@ -19,24 +19,24 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
res,
detail,
stream,
node: { name },
params: { text = '' }
} = props as AnswerProps;
const formatText = typeof text === 'string' ? text : JSON.stringify(text, null, 2);
const responseText = `\n${formatText}`;
if (res && stream) {
responseWrite({
res,
event: detail ? SseResponseEventEnum.fastAnswer : undefined,
data: textAdaptGptResponse({
text: `\n${formatText}`
text: responseText
})
});
}
return {
[NodeOutputKeyEnum.answerText]: formatText,
[NodeOutputKeyEnum.answerText]: responseText,
[DispatchNodeResponseKeyEnum.nodeResponse]: {
textOutput: formatText
}

View File

@@ -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);
}
};

View File

@@ -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,

View File

@@ -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)
)
};
};

View File

@@ -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
}
};
};

View File

@@ -19,4 +19,5 @@ export type DispatchFlowResponse = {
};
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
newVariables: Record<string, string>;
};

View File

@@ -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 [];

View File

@@ -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,

View File

@@ -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
})),

View File

@@ -32,6 +32,6 @@ export const dispatchAnswer = (props: Record<string, any>): AnswerResponse => {
}
return {
[NodeOutputKeyEnum.answerText]: formatText
[NodeOutputKeyEnum.answerText]: `\n${formatText}`
};
};

View File

@@ -12,4 +12,5 @@ export type DispatchFlowResponse = {
flowUsages: ChatNodeUsageType[];
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
newVariables: Record<string, string>;
};

View File

@@ -1,7 +1,6 @@
import { connectionMongo, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type';
import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant';
import {
TeamCollectionName,
TeamMemberCollectionName
@@ -30,7 +29,7 @@ const OutLinkSchema = new Schema({
},
type: {
type: String,
default: OutLinkTypeEnum.share
required: true
},
name: {
type: String,
@@ -62,6 +61,26 @@ const OutLinkSchema = new Schema({
hookUrl: {
type: String
}
},
app: {
appId: {
type: String
},
appSecret: {
type: String
},
encryptKey: {
type: String
},
verificationToken: {
type: String
}
},
immediateResponse: {
type: String
},
defaultResponse: {
type: String
}
});

View File

@@ -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 });

View File

@@ -1,9 +1,9 @@
import Papa from 'papaparse';
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
import { readFileRawText } from './rawText';
// 加载源文件内容
export const readCsvRawText = async (params: ReadFileByBufferParams): Promise<ReadFileResponse> => {
export const readCsvRawText = async (params: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const { rawText } = readFileRawText(params);
const csvArr = Papa.parse(rawText).data as string[][];

View File

@@ -0,0 +1,23 @@
import mammoth from 'mammoth';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
import { html2md } from '../../htmlStr2Md/utils';
/**
* read docx to markdown
*/
export const readDocsFile = async ({ buffer }: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
try {
const { value: html } = await mammoth.convertToHtml({
buffer
});
const rawText = html2md(html);
return {
rawText
};
} catch (error) {
console.log('error doc read:', error);
return Promise.reject('Can not read doc file, please convert to PDF');
}
};

View File

@@ -0,0 +1,13 @@
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
import { readFileRawText } from './rawText';
import { html2md } from '../../htmlStr2Md/utils';
export const readHtmlRawText = async (params: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const { rawText: html } = readFileRawText(params);
const rawText = html2md(html);
return {
rawText
};
};

View File

@@ -1,7 +1,7 @@
import * as pdfjs from 'pdfjs-dist/legacy/build/pdf.mjs';
// @ts-ignore
import('pdfjs-dist/legacy/build/pdf.worker.min.mjs');
import { ReadFileByBufferParams, ReadFileResponse } from './type';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
type TokenType = {
str: string;
@@ -13,9 +13,7 @@ type TokenType = {
hasEOL: boolean;
};
export const readPdfFile = async ({
buffer
}: ReadFileByBufferParams): Promise<ReadFileResponse> => {
export const readPdfFile = async ({ buffer }: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const readPDFPage = async (doc: any, pageNo: number) => {
const page = await doc.getPage(pageNo);
const tokenizedText = await page.getTextContent();
@@ -65,7 +63,6 @@ export const readPdfFile = async ({
loadingTask.destroy();
return {
rawText: pageTexts.join(''),
metadata: {}
rawText: pageTexts.join('')
};
};

View File

@@ -1,11 +1,11 @@
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
// import { parseOfficeAsync } from 'officeparser';
import { parseOffice } from './parseOffice';
import { parseOffice } from '../parseOffice';
export const readPptxRawText = async ({
buffer,
encoding
}: ReadFileByBufferParams): Promise<ReadFileResponse> => {
}: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const result = await parseOffice({
buffer,
encoding: encoding as BufferEncoding,

View File

@@ -1,5 +1,5 @@
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import iconv from 'iconv-lite';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
const rawEncodingList = [
'ascii',
@@ -17,7 +17,7 @@ const rawEncodingList = [
];
// 加载源文件内容
export const readFileRawText = ({ buffer, encoding }: ReadFileByBufferParams): ReadFileResponse => {
export const readFileRawText = ({ buffer, encoding }: ReadRawTextByBuffer): ReadFileResponse => {
const content = rawEncodingList.includes(encoding)
? buffer.toString(encoding as BufferEncoding)
: iconv.decode(buffer, 'gbk');

View File

@@ -1,10 +1,10 @@
import { ReadFileByBufferParams, ReadFileResponse } from './type.d';
import { ReadRawTextByBuffer, ReadFileResponse } from '../type';
import xlsx from 'node-xlsx';
import Papa from 'papaparse';
export const readXlsxRawText = async ({
buffer
}: ReadFileByBufferParams): Promise<ReadFileResponse> => {
}: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const result = xlsx.parse(buffer, {
skipHidden: false,
defval: ''

View File

@@ -2,8 +2,8 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
import fs from 'fs';
import decompress from 'decompress';
import { DOMParser } from '@xmldom/xmldom';
import { clearDirFiles } from '../utils';
import { addLog } from '../../system/log';
import { clearDirFiles } from '../../common/file/utils';
import { addLog } from '../../common/system/log';
const DEFAULTDECOMPRESSSUBLOCATION = '/tmp';

View File

@@ -0,0 +1,71 @@
import { parentPort } from 'worker_threads';
import { readFileRawText } from './extension/rawText';
import { ReadRawTextByBuffer, ReadRawTextProps } from './type';
import { readHtmlRawText } from './extension/html';
import { readPdfFile } from './extension/pdf';
import { readDocsFile } from './extension/docx';
import { readPptxRawText } from './extension/pptx';
import { readXlsxRawText } from './extension/xlsx';
import { readCsvRawText } from './extension/csv';
parentPort?.on('message', async (props: ReadRawTextProps<Uint8Array>) => {
const readFileRawContent = async (params: ReadRawTextByBuffer) => {
switch (params.extension) {
case 'txt':
case 'md':
return readFileRawText(params);
case 'html':
return readHtmlRawText(params);
case 'pdf':
return readPdfFile(params);
case 'docx':
return readDocsFile(params);
case 'pptx':
return readPptxRawText(params);
case 'xlsx':
const xlsxResult = await readXlsxRawText(params);
if (params.csvFormat) {
return {
rawText: xlsxResult.formatText || ''
};
}
return {
rawText: xlsxResult.rawText
};
case 'csv':
const csvResult = await readCsvRawText(params);
if (params.csvFormat) {
return {
rawText: csvResult.formatText || ''
};
}
return {
rawText: csvResult.rawText
};
default:
return Promise.reject('Only support .txt, .md, .html, .pdf, .docx, pptx, .csv, .xlsx');
}
};
// params.buffer: Uint8Array -> buffer
const buffer = Buffer.from(props.buffer);
const newProps: ReadRawTextByBuffer = {
...props,
buffer
};
try {
parentPort?.postMessage({
type: 'success',
data: await readFileRawContent(newProps)
});
} catch (error) {
console.log(error);
parentPort?.postMessage({
type: 'error',
data: error
});
}
global?.close?.();
});

15
packages/service/worker/file/type.d.ts vendored Normal file
View File

@@ -0,0 +1,15 @@
import { ReadFileByBufferParams } from '../../common/file/read/type';
export type ReadRawTextProps<T> = {
csvFormat?: boolean;
extension: string;
buffer: T;
encoding: string;
};
export type ReadRawTextByBuffer = ReadRawTextProps<Buffer>;
export type ReadFileResponse = {
rawText: string;
formatText?: string;
};

View File

@@ -1,60 +0,0 @@
import { parentPort } from 'worker_threads';
import TurndownService from 'turndown';
//@ts-ignore
import domino from 'domino';
//@ts-ignore
import * as turndownPluginGfm from 'joplin-turndown-plugin-gfm';
const turndownService = new TurndownService({
headingStyle: 'atx',
bulletListMarker: '-',
codeBlockStyle: 'fenced',
fence: '```',
emDelimiter: '_',
strongDelimiter: '**',
linkStyle: 'inlined',
linkReferenceStyle: 'full'
});
parentPort?.on('message', (params: { html: string }) => {
const html2md = (html: string): string => {
try {
const window = domino.createWindow(html);
const document = window.document;
turndownService.remove(['i', 'script', 'iframe']);
turndownService.addRule('codeBlock', {
filter: 'pre',
replacement(_, node) {
const content = node.textContent?.trim() || '';
// @ts-ignore
const codeName = node?._attrsByQName?.class?.data?.trim() || '';
return `\n\`\`\`${codeName}\n${content}\n\`\`\`\n`;
}
});
turndownService.use(turndownPluginGfm.gfm);
// @ts-ignore
return turndownService.turndown(document);
} catch (error) {
return '';
}
};
try {
const md = html2md(params?.html || '');
parentPort?.postMessage({
type: 'success',
data: md
});
} catch (error) {
parentPort?.postMessage({
type: 'error',
data: error
});
}
global?.close?.();
});

View File

@@ -0,0 +1,20 @@
import { parentPort } from 'worker_threads';
import { html2md } from './utils';
parentPort?.on('message', (params: { html: string }) => {
try {
const md = html2md(params?.html || '');
parentPort?.postMessage({
type: 'success',
data: md
});
} catch (error) {
parentPort?.postMessage({
type: 'error',
data: error
});
}
global?.close?.();
});

View File

@@ -0,0 +1,40 @@
import TurndownService from 'turndown';
const domino = require('domino-ext');
const turndownPluginGfm = require('joplin-turndown-plugin-gfm');
export const html2md = (html: string): string => {
const turndownService = new TurndownService({
headingStyle: 'atx',
bulletListMarker: '-',
codeBlockStyle: 'fenced',
fence: '```',
emDelimiter: '_',
strongDelimiter: '**',
linkStyle: 'inlined',
linkReferenceStyle: 'full'
});
try {
const window = domino.createWindow(html);
const document = window.document;
turndownService.remove(['i', 'script', 'iframe']);
turndownService.addRule('codeBlock', {
filter: 'pre',
replacement(_, node) {
const content = node.textContent?.trim() || '';
// @ts-ignore
const codeName = node?._attrsByQName?.class?.data?.trim() || '';
return `\n\`\`\`${codeName}\n${content}\n\`\`\`\n`;
}
});
turndownService.use(turndownPluginGfm.gfm);
return turndownService.turndown(document);
} catch (error) {
console.log('html 2 markdown error', error);
return '';
}
};

View File

@@ -2,6 +2,7 @@ import { Worker } from 'worker_threads';
import path from 'path';
export enum WorkerNameEnum {
readFile = 'readFile',
htmlStr2Md = 'htmlStr2Md',
countGptMessagesTokens = 'countGptMessagesTokens'
}

8
packages/web/common/fetch/type.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
export type PaginationProps<T = {}> = T & {
current: number;
pageSize: number;
};
export type PaginationResponse<T = any> = {
total: number;
list: T[];
};

View 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;

View 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';

View File

@@ -65,6 +65,7 @@ export const iconPaths = {
'core/app/headphones': () => import('./icons/core/app/headphones.svg'),
'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'),
'core/app/markLight': () => import('./icons/core/app/markLight.svg'),
'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'),
'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'),
'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'),
'core/app/simpleMode/ai': () => import('./icons/core/app/simpleMode/ai.svg'),
@@ -141,10 +142,12 @@ export const iconPaths = {
import('./icons/core/workflow/inputType/selectLLM.svg'),
'core/workflow/inputType/switch': () => import('./icons/core/workflow/inputType/switch.svg'),
'core/workflow/inputType/textarea': () => import('./icons/core/workflow/inputType/textarea.svg'),
'core/workflow/revertVersion': () => import('./icons/core/workflow/revertVersion.svg'),
'core/workflow/runError': () => import('./icons/core/workflow/runError.svg'),
'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'),
'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'),
'core/workflow/running': () => import('./icons/core/workflow/running.svg'),
'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'),
date: () => import('./icons/date.svg'),
delete: () => import('./icons/delete.svg'),
edit: () => import('./icons/edit.svg'),

View File

@@ -1,6 +1,4 @@
<svg t="1714186779322" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1213"
width="128" height="128">
<path
d="M957.78379975 134.65795581c-2.56331135 83.17185735-20.44423515 163.12467052-50.30317593 240.57510884-35.69958418 92.67796903-87.96862781 174.24030494-161.20251807 241.63355181-5.94396957 5.46839892-7.80783541 11.32361235-7.01830818 19.026796 2.5328431 24.92705029 4.5596486 49.91503711 7.12295994 74.84208611 5.9598665 58.12425835-14.32010468 105.71575589-61.01609761 139.47861502-50.06340414 36.20694738-102.1695094 69.6412786-153.84243498 103.55515469-32.57194606 21.38212813-72.60730269 1.98176462-76.2277327-36.80306673-4.17150847-44.53539301-7.45016355-89.16219079-10.5340864-133.78633995-1.53533793-22.33459502-0.93921859-22.52932723-22.48428636-25.38937392-57.18636407-7.56938794-101.94563325-34.8213021-132.68418171-84.27401429-15.81040175-25.46488176-23.6473811-53.61097493-26.40410034-83.18642866-0.81866989-8.86495278-4.3808133-11.13020574-12.70925895-11.59252939-46.28003447-2.57788397-92.52960201-5.3332789-138.74869996-8.8199132-38.44173211-2.92098321-57.93085045-43.61339568-37.32367695-77.56966271 14.4671474-23.86993165 29.87483771-47.17288918 44.81888136-70.76065808 16.1071371-25.38937393 32.07915418-50.85425569 48.21675953-76.21316007 29.53173846-46.41383019 72.1012638-71.48924752 126.5905208-71.93699984 31.2008721-0.23844748 62.46003078 5.27366671 93.64633157 8.64107663 5.89892998 0.64115892 10.0863341-0.16426396 14.16908774-4.73848387 72.39799786-81.24970362 162.00661664-136.08206117 263.446211-172.76457792 57.33473241-20.74096921 116.33859714-34.28479495 176.89237242-40.54271983 30.51467231-3.17400331 61.23864946-4.32120112 91.72285352 1.34060572 16.61450031 3.08392285 17.46363973 3.77012135 20.02562678 20.05609502C956.4869093 101.71509088 958.51238919 118.07524806 957.78379975 134.65795581L957.78379975 134.65795581zM813.20902898 339.81696881c0.87828208-71.05871775-57.73744384-132.1635715-127.410515-133.309445-72.35428259-1.20680999-135.02361821 51.61331338-136.73779142 134.08439962-1.44525748 68.77756914 60.61338619 131.07466028 129.88374722 131.55155523C752.69897026 472.67866149 812.29895435 413.94371248 813.20902898 339.81696881L813.20902898 339.81696881zM195.18722288 640.09538918c13.69219278 0.17883529 21.29204896 6.24335355 26.19479682 20.29321819 19.25067215 55.04033549 54.28127779 96.80443794 105.06002435 125.11479508 11.20438926 6.24335355 23.0207935 11.30904103 35.22401354 15.48187512 21.93320788 7.50845143 26.93928317 28.96476307 10.66788212 45.47328728-24.10705481 24.43558273-48.46977833 48.60357404-72.56226181 73.02590976-7.97209938 8.07542683-16.74829602 11.90383476-27.95268527 7.71775497-11.38322454-4.2470176-16.88209172-12.72383026-17.46363974-24.55480712-0.35767186-7.49520313-0.17883529-15.03544713-0.35767188-22.55846986-0.31263099-13.81274148-0.44775102-13.91739325-13.11197036-9.71541653-27.08765021 9.01464541-54.11701383 18.10347435-81.20466405 27.10354714-7.9866707 2.66796441-16.00248532 3.77012135-23.81032074-0.67030285-11.44416105-6.51226928-15.76403785-18.14851392-11.160674-32.46729429 8.99874849-27.93811395 18.43200097-55.71196266 27.64005428-83.57456879 3.99267318-12.09856827 3.7396531-12.35158706-9.26766421-12.55956629-7.83830367-0.11922438-15.70442566 0.01457133-23.54140371-0.37224318-11.4136928-0.55107846-19.5341592-6.09498652-23.75070856-16.94170392-4.14104022-10.56455466-1.49029705-19.77260668 6.19831398-27.54997384 25.15092515-25.40526957 50.42240031-50.68999172 75.6620816-76.00518086C182.68726879 642.31560258 188.64713398 639.45423026 195.18722288 640.09538918L195.18722288 640.09538918z"
p-id="1214"></path>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 19" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M13.8412 2.25558L14.04 2.28053C14.5655 2.35304 15.261 2.4505 15.6547 2.84502C15.9775 3.16702 16.1015 3.69097 16.1756 4.15878L16.2192 4.45974C16.3175 5.16925 16.3432 6.09863 16.1756 7.13093C15.845 9.16824 14.7604 11.5993 12.037 13.4814C12.0222 13.628 12.0214 13.7762 12.0245 13.9243L12.0323 14.1457C12.0448 14.4864 12.0573 14.8264 11.9622 15.1578C11.814 15.6723 11.2862 16.0115 10.7926 16.2548L10.5509 16.3694L10.2391 16.5066C9.65587 16.7545 8.86839 17.0087 8.37485 16.5144C8.07857 16.2189 7.95694 15.7854 7.85792 15.3542L7.82128 15.1928C7.77999 14.9891 7.72794 14.7876 7.66534 14.5894C7.62636 14.4755 7.58426 14.3594 7.53903 14.2424C7.48926 14.3035 7.43643 14.3621 7.38076 14.4178C7.11177 14.6868 6.71023 14.8747 6.37965 15.0065C6.01865 15.1492 5.6101 15.2739 5.23196 15.3768L5.03781 15.4283L4.66591 15.5211L4.32908 15.5991L3.92599 15.6848L3.67415 15.7339C3.54855 15.7572 3.41918 15.7495 3.29719 15.7117C3.17521 15.6738 3.06427 15.6068 2.97395 15.5165C2.88363 15.4261 2.81664 15.3152 2.77875 15.1932C2.74086 15.0712 2.73321 14.9419 2.75646 14.8163L2.82352 14.4802L2.94359 13.936L3.04027 13.5384L3.11356 13.2577C3.21648 12.8803 3.34123 12.4718 3.48469 12.1115C3.61567 11.7802 3.80358 11.3786 4.07257 11.1096L4.13494 11.0496L4.08504 11.0293C3.95179 10.9787 3.81683 10.9326 3.68039 10.8913L3.46442 10.8251C2.92332 10.6613 2.34479 10.4851 1.98536 10.1249C1.54874 9.68906 1.6961 9.02556 1.90661 8.47354L1.99238 8.25991L2.13038 7.94803L2.24499 7.70633C2.48825 7.21357 2.82742 6.68573 3.34201 6.53759C3.61489 6.45962 3.8987 6.45494 4.18406 6.46274L4.35559 6.4682C4.57858 6.47599 4.80079 6.48457 5.01832 6.46352C6.90047 3.73931 9.33152 2.65477 11.3688 2.32419C12.1861 2.19014 13.0177 2.16706 13.8412 2.25558ZM6.19096 12.138C6.06825 12.0472 5.9216 11.9944 5.76915 11.9861C5.61671 11.9777 5.46518 12.0143 5.33331 12.0913L5.24755 12.149L5.17504 12.2137L5.07758 12.3369C4.87486 12.63 4.75947 13.0355 4.6698 13.4144L4.5856 13.7793L4.54583 13.9453L4.69475 13.9095L5.02066 13.8346C5.47132 13.7294 5.97109 13.5929 6.27829 13.3161C6.41186 13.1826 6.49243 13.0052 6.50505 12.8168C6.51767 12.6284 6.46147 12.4418 6.3469 12.2916L6.28297 12.2184L6.26425 12.2004L6.19096 12.138ZM11.7914 6.70054C11.6466 6.55571 11.4748 6.44082 11.2856 6.36242C11.0964 6.28402 10.8936 6.24365 10.6888 6.24361C10.4841 6.24357 10.2813 6.28387 10.0921 6.36221C9.90285 6.44054 9.73092 6.55537 9.58609 6.70015C9.44126 6.84493 9.32636 7.01682 9.24796 7.206C9.16956 7.39518 9.12919 7.59795 9.12915 7.80274C9.12912 8.00752 9.16942 8.21031 9.24775 8.39952C9.32609 8.58873 9.44092 8.76065 9.5857 8.90548C9.87809 9.19798 10.2747 9.36235 10.6883 9.36242C11.1019 9.36249 11.4985 9.19827 11.791 8.90587C12.0835 8.61348 12.2479 8.21687 12.248 7.80329C12.248 7.38971 12.0838 6.99304 11.7914 6.70054Z" />
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,12 @@
<svg t="1714285522209" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5343"
width="128" height="128">
<path
d="M891.318857 340.845714c4.900571 0 9.728 0.292571 14.628572 0.804572a409.965714 409.965714 0 0 1 108.836571 30.061714c10.093714 4.534857 12.580571 8.192 3.949714 17.334857-24.868571 26.624-45.494857 57.051429-61.001143 89.965714-16.822857 35.328-35.108571 69.851429-52.297142 105.033143a225.28 225.28 0 0 1-52.150858 69.412572c-53.613714 48.493714-116.150857 68.973714-187.538285 59.099428-81.92-11.337143-159.451429-38.985143-232.740572-75.483428a143.506286 143.506286 0 0 1-10.459428-5.485715 5.339429 5.339429 0 0 1 0.292571-9.216l5.12-2.706285c59.245714-31.670857 108.836571-75.849143 156.525714-122.294857 20.187429-19.529143 39.497143-40.009143 59.904-59.318858A345.014857 345.014857 0 0 1 804.571429 352.256c13.165714-3.218286 26.550857-5.778286 39.789714-8.630857h0.585143l28.233143-2.56"
fill="#133C9A" p-id="5344"></path>
<path
d="M317.659429 913.846857c-8.996571-0.512-31.158857-3.584-33.865143-3.949714a536.429714 536.429714 0 0 1-165.083429-48.274286c-30.208-14.116571-59.245714-30.72-88.356571-46.957714-19.163429-10.678857-27.794286-27.282286-27.648-49.883429 0.585143-83.382857 0.585143-166.765714 0-250.148571C2.413714 461.019429 0.731429 407.405714 0 353.718857c0-4.754286 0.731429-9.508571 2.194286-13.897143 3.291429-9.728 9.947429-10.24 16.530285-3.949714 7.606857 7.314286 13.677714 16.237714 21.211429 23.405714 67.291429 66.413714 138.752 127.195429 218.770286 177.225143 45.056 28.891429 91.940571 54.710857 140.434285 77.385143 77.750857 35.328 157.549714 66.486857 241.078858 86.235429 73.874286 17.481143 145.627429 6.436571 205.458285-40.374858 18.285714-15.652571 27.282286-27.062857 48.932572-55.881142a359.862857 359.862857 0 0 1-37.376 72.850285c-13.897143 21.942857-45.348571 51.2-69.193143 74.093715-36.278857 35.108571-83.748571 63.561143-128.292572 87.552-48.566857 26.185143-99.035429 47.104-152.941714 58.514285-27.648 6.948571-67.584 14.848-81.334857 15.579429-2.413714-0.146286-10.678857 1.682286-14.848 1.389714-35.547429 2.633143-57.490286 3.657143-92.891429 0z"
fill="#3370FF" p-id="5345"></path>
<path
d="M165.083429 110.518857a52.443429 52.443429 0 0 1 7.460571 0c152.649143 0 304.128 2.486857 456.630857 2.486857 0.292571 0 0.585143 0 0.731429 0.219429 14.189714 12.361143 27.282286 25.746286 39.277714 40.155428 34.450286 34.230857 60.123429 93.622857 77.677714 129.755429 8.777143 25.014857 21.942857 48.859429 28.16 76.8v0.438857c-15.579429 5.046857-30.72 11.190857-45.348571 18.505143-44.178286 22.381714-64.219429 38.765714-100.790857 74.752-19.968 19.529143-37.010286 37.083429-63.488 62.098286a563.346286 563.346286 0 0 1-29.769143 26.916571c-7.021714-12.434286-125.732571-244.589714-364.251429-427.300571"
fill="#00D6B9" p-id="5346"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M4.31146 8.0038C4.78201 5.34167 7.10812 3.31934 9.90528 3.31934C13.0427 3.31934 15.586 5.8627 15.586 9.0001C15.586 12.1375 13.0427 14.6809 9.90528 14.6809C8.99212 14.6809 8.01118 14.3988 7.15115 13.9231C6.28734 13.4453 5.60309 12.8045 5.234 12.1428C5.0322 11.7811 4.57537 11.6514 4.21364 11.8532C3.8519 12.055 3.72225 12.5119 3.92404 12.8736C4.46219 13.8383 5.38209 14.6588 6.42511 15.2357C7.47191 15.8147 8.70047 16.1809 9.90528 16.1809C13.8711 16.1809 17.086 12.9659 17.086 9.0001C17.086 5.03428 13.8711 1.81934 9.90528 1.81934C6.43524 1.81934 3.54116 4.28003 2.87078 7.55181L2.46483 6.84336C2.2589 6.48396 1.80061 6.35956 1.44122 6.56549C1.08182 6.77142 0.957418 7.22971 1.16335 7.58911L2.4808 9.88833C2.62411 10.1384 2.8896 10.2747 3.15895 10.2651C3.29431 10.27 3.43265 10.2383 3.55876 10.1658L5.88309 8.83098C6.24228 8.6247 6.36624 8.16629 6.15996 7.80709C5.95367 7.4479 5.49526 7.32394 5.13607 7.53023L4.31146 8.0038ZM8.96233 9.52028C8.96177 9.55065 8.96306 9.58096 8.96616 9.61102C8.97694 9.71784 9.01015 9.81804 9.06098 9.90684C9.11368 9.99929 9.18665 10.0815 9.27792 10.1462C9.30081 10.1625 9.32465 10.1775 9.34934 10.1912L11.4739 11.4178C11.8326 11.6249 12.2913 11.502 12.4984 11.1433C12.7055 10.7846 12.5826 10.3259 12.2239 10.1188L10.4623 9.10176V6.92775C10.4623 6.51353 10.1265 6.17775 9.71233 6.17775C9.29811 6.17775 8.96233 6.51353 8.96233 6.92775V9.52028Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M8.99992 2.08659C5.18166 2.08659 2.08634 5.1819 2.08634 9.00017C2.08634 12.8184 5.18166 15.9137 8.99992 15.9137C12.8182 15.9137 15.9135 12.8184 15.9135 9.00017C15.9135 5.1819 12.8182 2.08659 8.99992 2.08659ZM0.419678 9.00017C0.419678 4.26143 4.26118 0.419922 8.99992 0.419922C13.7387 0.419922 17.5802 4.26143 17.5802 9.00017C17.5802 13.7389 13.7387 17.5804 8.99992 17.5804C4.26118 17.5804 0.419678 13.7389 0.419678 9.00017ZM8.99992 3.51869C9.46016 3.51869 9.83326 3.89178 9.83326 4.35202V8.48514L12.4714 9.80419C12.883 10.01 13.0499 10.5106 12.844 10.9222C12.6382 11.3339 12.1377 11.5007 11.726 11.2949L8.62725 9.74552C8.34493 9.60436 8.16659 9.31581 8.16659 9.00017V4.35202C8.16659 3.89178 8.53969 3.51869 8.99992 3.51869Z"
fill="#3370FF" />
</svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 19" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M10 3.08634C6.18178 3.08634 3.08647 6.18166 3.08647 9.99992C3.08647 13.8182 6.18178 16.9135 10 16.9135C13.8183 16.9135 16.9136 13.8182 16.9136 9.99992C16.9136 6.18166 13.8183 3.08634 10 3.08634ZM1.4198 9.99992C1.4198 5.26118 5.26131 1.41968 10 1.41968C14.7388 1.41968 18.5803 5.26118 18.5803 9.99992C18.5803 14.7387 14.7388 18.5802 10 18.5802C5.26131 18.5802 1.4198 14.7387 1.4198 9.99992ZM10 4.51844C10.4603 4.51844 10.8334 4.89154 10.8334 5.35178V9.48489L13.4715 10.8039C13.8831 11.0098 14.05 11.5103 13.8442 11.922C13.6383 12.3336 13.1378 12.5005 12.7261 12.2947L9.62737 10.7453C9.34505 10.6041 9.16671 10.3156 9.16671 9.99992V5.35178C9.16671 4.89154 9.53981 4.51844 10 4.51844Z" />
d="M9.00005 3.27783C5.56362 3.27783 2.77783 6.06362 2.77783 9.50005C2.77783 12.9365 5.56362 15.7223 9.00005 15.7223C12.4365 15.7223 15.2223 12.9365 15.2223 9.50005C15.2223 6.06362 12.4365 3.27783 9.00005 3.27783ZM1.27783 9.50005C1.27783 5.23519 4.73519 1.77783 9.00005 1.77783C13.2649 1.77783 16.7223 5.23519 16.7223 9.50005C16.7223 13.7649 13.2649 17.2223 9.00005 17.2223C4.73519 17.2223 1.27783 13.7649 1.27783 9.50005ZM9.00005 4.56672C9.41427 4.56672 9.75005 4.90251 9.75005 5.31672V9.03653L12.1244 10.2237C12.4948 10.4089 12.645 10.8594 12.4598 11.2299C12.2745 11.6004 11.824 11.7506 11.4535 11.5653L8.66464 10.1709C8.41056 10.0438 8.25005 9.78413 8.25005 9.50005V5.31672C8.25005 4.90251 8.58584 4.56672 9.00005 4.56672Z" />
</svg>

Before

Width:  |  Height:  |  Size: 814 B

After

Width:  |  Height:  |  Size: 866 B

View File

@@ -0,0 +1,19 @@
import React, { forwardRef } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import Loading from '../MyLoading';
type Props = BoxProps & {
isLoading?: boolean;
text?: string;
};
const MyBox = ({ text, isLoading, children, ...props }: Props, ref: any) => {
return (
<Box ref={ref} position={'relative'} {...props}>
{isLoading && <Loading fixed={false} text={text} />}
{children}
</Box>
);
};
export default forwardRef(MyBox);

View File

@@ -0,0 +1,80 @@
import React from 'react';
import MyIcon from '../Icon';
import { Flex, Image, Box, CloseButton, FlexProps } from '@chakra-ui/react';
import { useLoading } from '../../../hooks/useLoading';
type Props = FlexProps & {
onClose: () => void;
iconSrc?: string;
title?: any;
isLoading?: boolean;
showMask?: boolean;
};
const CustomRightDrawer = ({
onClose,
iconSrc,
title,
maxW = ['90vw', '30vw'],
children,
isLoading,
showMask = true,
...props
}: Props) => {
const { Loading } = useLoading();
return (
<Flex
flexDirection={'column'}
position={'fixed'}
right={0}
bg={'white'}
zIndex={100}
maxW={maxW}
w={'100%'}
h={'90vh'}
borderLeftRadius={'lg'}
border={'base'}
boxShadow={'2'}
{...props}
>
<Flex
display={'flex'}
alignItems={'center'}
fontWeight={500}
background={'#FBFBFC'}
borderBottom={'1px solid #F4F6F8'}
roundedTop={'lg'}
py={'10px'}
px={5}
>
{iconSrc && (
<>
{iconSrc.startsWith('/') ? (
<Image mr={3} objectFit={'contain'} alt="" src={iconSrc} w={'20px'} />
) : (
<MyIcon mr={3} name={iconSrc as any} w={'20px'} />
)}
</>
)}
<Box flex={'1'} fontSize={'lg'}>
{title}
</Box>
<CloseButton position={'relative'} fontSize={'sm'} top={0} right={0} onClick={onClose} />
</Flex>
<Box
flex={'1 0 0'}
py={props.py ?? 3}
px={props.px ?? 5}
overflow={props?.overflow ?? 'auto'}
display={'flex'}
flexDirection={'column'}
>
{children}
</Box>
<Loading loading={isLoading} fixed={false} />
</Flex>
);
};
export default React.memo(CustomRightDrawer);

View File

@@ -1,5 +1,8 @@
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
export type EditorVariablePickerType = {
key: string;
label: string;
icon?: string;
valueType?: WorkflowIOValueTypeEnum;
};

View File

@@ -0,0 +1,41 @@
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { nodeTemplateListType } from '@fastgpt/global/core/workflow/type';
import { TFunction } from 'next-i18next';
export const workflowNodeTemplateList = (t: TFunction): nodeTemplateListType => [
{
type: FlowNodeTemplateTypeEnum.systemInput,
label: t('core.module.template.System input module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.textAnswer,
label: t('core.module.template.Response module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.functionCall,
label: t('core.module.template.Function module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.tools,
label: t('core.module.template.Tool module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.externalCall,
label: t('core.module.template.External module'),
list: []
},
{
type: FlowNodeTemplateTypeEnum.personalPlugin,
label: '',
list: []
},
{
type: FlowNodeTemplateTypeEnum.other,
label: t('common.Other'),
list: []
}
];

View File

@@ -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 () => {

View File

@@ -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>
)}

View File

@@ -0,0 +1,122 @@
import { useRef, useState, useEffect } from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import { useToast } from './useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { PaginationProps, PaginationResponse } from '../common/fetch/type';
import { useBoolean, useLockFn, useMemoizedFn, useMount, useScroll, useVirtualList } from 'ahooks';
import MyBox from '../components/common/MyBox';
import { useTranslation } from 'next-i18next';
export function useScrollPagination<
TParams extends PaginationProps,
TData extends PaginationResponse
>(
api: (data: TParams) => Promise<TData>,
{
itemHeight = 50,
overscan = 10,
pageSize = 10,
defaultParams = {}
}: {
itemHeight: number;
overscan?: number;
pageSize?: number;
defaultParams?: Record<string, any>;
}
) {
const { t } = useTranslation();
const containerRef = useRef<HTMLDivElement>(null);
const wrapperRef = useRef(null);
const noMore = useRef(false);
const { toast } = useToast();
const [current, setCurrent] = useState(1);
const [data, setData] = useState<TData['list']>([]);
const [isLoading, { setTrue, setFalse }] = useBoolean(false);
const [list] = useVirtualList<TData['list'][0]>(data, {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight,
overscan
});
const loadData = useLockFn(async (num: number = current) => {
if (noMore.current) return;
setTrue();
try {
const res = await api({
current: num,
pageSize,
...defaultParams
} as TParams);
setCurrent(num);
if (num === 1) {
// reload
setData(res.list);
noMore.current = res.list.length >= res.total;
} else {
const totalLength = data.length + res.list.length;
noMore.current = totalLength >= res.total;
setData((prev) => [...prev, ...res.list]);
}
} catch (error: any) {
toast({
title: getErrText(error, '获取数据异常'),
status: 'error'
});
console.log(error);
}
setFalse();
});
const ScrollList = useMemoizedFn(
({
children,
isLoading,
...props
}: { children: React.ReactNode; isLoading?: boolean } & BoxProps) => {
return (
<MyBox isLoading={isLoading} ref={containerRef} overflow={'overlay'} {...props}>
<Box ref={wrapperRef}>{children}</Box>
{noMore.current && (
<Box pb={2} textAlign={'center'} color={'myGray.600'} fontSize={'sm'}>
{t('common.No more data')}
</Box>
)}
</MyBox>
);
}
);
useMount(() => {
loadData(1);
});
const scroll = useScroll(containerRef);
useEffect(() => {
if (!containerRef.current) return;
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
if (scrollTop + clientHeight >= scrollHeight - 100) {
loadData(current + 1);
}
}, [scroll]);
return {
containerRef,
list,
isLoading,
ScrollList,
fetchData: loadData
};
}

Some files were not shown because too many files have changed in this diff Show More