Commit 33d40fd0 by Archer Committed by GitHub

feature: System plugin (#5131)

* feat: system Tool (#4959)

* feat: independent system tool

* chore: use ToolNode instead of PluginModule

* chore: tools

* chore: tools templateDir

* refactor: templates

* feat: flush code

* chore: update template

* refactor: migrate delay

* feat: worker pool

* chore: Dockerfile

* docs:  add tools.template.json

* feat: auto flush system tools

* fix: ts error

* chore: create new pool temporarily

* chore: system tool migration

* chore: migration

* fix: fix pnpm-workspace.yaml

* chore: update pnpm-lock.yaml to integrate tool

* chore(systemTool): chore

* chore: add system plugin

* chore(deps): update @fastgpt-sdk/plugin

* fix: type error

* chore: remove plugin package

* chore: move pro plugins code to open source

* feat: support system tool config input

* fix: type error

* perf: i18n

* fix: cr

* chore: update sdk

* feat: system plugin cache

* update mcp server (#5076)

* update mcp server

* fix: action

* fix: dockerfile

* fix: dockerfile

* fix: dockerfile

* fix: dockerfile

* fix: dockerfile

* fix: dockerfile

* feat: system Tool (#4959)

* feat: independent system tool

* chore: use ToolNode instead of PluginModule

* chore: tools

* chore: tools templateDir

* refactor: templates

* feat: flush code

* chore: update template

* refactor: migrate delay

* feat: worker pool

* chore: Dockerfile

* docs:  add tools.template.json

* feat: auto flush system tools

* fix: ts error

* chore: create new pool temporarily

* chore: system tool migration

* chore: migration

* fix: fix pnpm-workspace.yaml

* chore: update pnpm-lock.yaml to integrate tool

* chore(systemTool): chore

* chore: add system plugin

* chore(deps): update @fastgpt-sdk/plugin

* fix: type error

* chore: remove plugin package

* chore: move pro plugins code to open source

* feat: support system tool config input

* fix: type error

* perf: i18n

* fix: cr

* chore: update sdk

* feat: system plugin cache

* perf: run tool

* update package

* perf: config key

* fix: tool ini

* tool config params

* perf: workflow type

* rename tools to  agent

* version list

* perf: tool error

* config secret ux

* perf: config secret ux

* fix: tool config field

* add course to secret input

* feat: support inputConfig switch (#5099)

* feat: support inputConfig switch

* deps: update @fastgpt-sdk/plugin

* chore: update workflows

* fix: inputType

* fix: secret

* add default value to node

* update i18n

* eslint

* add precision to number input

* feat: add number input and select

* perf: number ux

* fix: code

* Proxies image requests to plugin service (#5111)

* Proxies image requests to plugin service

Adds a rewrite rule and API endpoint to proxy image requests
to the plugin service. This allows the app to fetch images from
the plugin's tools directory.

It also adds the plugin base URL to the service's constants, so that
it can use the plugin URL when proxying requests.

* fix: update FastGPTPluginUrl to remove unnecessary API path

* feat: update image proxy destination and add plugin image handler

* Adapt plugin id

* replace avatar

* remove rewrite

* fix: plugin avatar

* update system tool doc

* feat: system tool type

* yml sh

* yml sh

* update doc

* fix: simple app tool select

* fix: switch ui

* update pacakge

* Yamljs (#5129)

* update docker-compose configuration: bump fastgpt and fastgpt-plugin images, change minio host to service name, and adjust service dependencies

* refactor: comment out port exposure in docker-compose configuration

* update: uncomment port exposure in docker-compose configuration

* update: change MINIO_HOST to use specific IP address in docker configuration

* update: modify fastgpt-plugin image version in docker configuration

* update readme

* doc

* remove

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com>
parent 0ead4239
......@@ -3,6 +3,7 @@ dist/
build/
.next/
out/
local/
# 依赖目录
node_modules/
......
......@@ -71,25 +71,16 @@ jobs:
command: pages deploy ./docSite/public --project-name=fastgpt-doc
packageManager: npm
- name: Create deployment status comment
if: always()
env:
JOB_STATUS: ${{ job.status }}
PREVIEW_URL: ${{ steps.deploy.outputs.deployment-url }}
uses: actions/github-script@v6
- name: '@finleyge/github-tools'
uses: FinleyGe/github-tools@0.0.1
id: print-image-label
if: success()
with:
token: ${{ secrets.GITHUB_TOKEN }}
script: |
const success = process.env.JOB_STATUS === 'success';
const deploymentUrl = `${process.env.PREVIEW_URL}`;
const status = success ? '✅ Success' : '❌ Failed';
console.log(process.env.JOB_STATUS);
const commentBody = `**Deployment Status: ${status}**
${success ? `🔗 Preview URL: ${deploymentUrl}` : ''}`;
await github.rest.issues.createComment({
...context.repo,
issue_number: context.payload.pull_request.number,
body: commentBody
});
tool: issue-comment
title: 'Docs Preview:'
body: |
Deployment Status: ${status}
```
${success ? `🔗 Preview URL: ${deploymentUrl}` : ''}
```
......@@ -77,13 +77,15 @@ jobs:
-t ${{ steps.config.outputs.DOCKER_REPO_TAGGED }} \
.
- uses: actions/github-script@v7
- name: '@finleyge/github-tools'
uses: FinleyGe/github-tools@0.0.1
id: print-image-label
if: success()
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Preview ${{ matrix.image }} Image: `${{ steps.config.outputs.DOCKER_REPO_TAGGED }}`'
})
token: ${{ secrets.GITHUB_TOKEN }}
tool: issue-comment
title: 'Preview ${{ matrix.image }} Image:'
body: |
```
${{ steps.config.outputs.DOCKER_REPO_TAGGED }}`
```
......@@ -120,6 +120,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
## 💪 相关项目
- [FastGPT-plugin](https://github.com/labring/fastgpt-plugin)
- [Laf:3 分钟快速接入三方应用](https://github.com/labring/laf)
- [Sealos:快速部署集群应用](https://github.com/labring/sealos)
- [One API:多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api)
......
......@@ -21,8 +21,9 @@ weight: 707
{{% alert icon="🤖" context="success" %}}
- MongoDB:用于存储除了向量外的各类数据
- PostgreSQL/Milvus:存储向量数据
- OneAPI: 聚合各类 AI API,支持多模型调用 (任何模型问题,先自行通过 OneAPI 测试校验)
- MinIO: 用于存储插件的文件数据
- PostgreSQL/Milvus/Oceanbase:存储向量数据
- AIProxy: 聚合各类 AI API,支持多模型调用
{{% /alert %}}
......@@ -66,7 +67,14 @@ Zilliz Cloud 由 Milvus 原厂打造,是全托管的 SaaS 向量数据库服
如果使用`OpenAI`等国外模型接口,请确保可以正常访问,否则会报错:`Connection error` 等。 方案可以参考:[代理方案](/docs/development/proxy/)
### 2. 准备 Docker 环境
### 2. 开放防火墙端口
1. 3000: fastgpt 主服务端口
2. 3005: mcp server 端口
3. 9000: minio 端口
4. 9001: minio web控制台端口
### 3. 准备 Docker 环境
{{< tabs tabTotal="3" >}}
{{< tab tabName="Linux" >}}
......@@ -148,29 +156,26 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
找到 yml 文件中,fastgpt 容器的环境变量进行下面操作:
{{< tabs tabTotal="3" >}}
{{< tab tabName="PgVector版本" >}}
{{< tabs tabTotal="2" >}}
{{< tab tabName="所有版本通用操作" >}}
{{< markdownify >}}
无需操作
修改环境变量:
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="Oceanbase版本" >}}
{{< markdownify >}}
```
# fastgpt 容器
FE_DOMAIN=前端外部可访问的地址。用于自动补全文件资源路径。例如 https:fastgpt.cn,不能填 localhost。
DEFAULT_ROOT_PSW=root 用户密码,每次重启root 都会修改成这个密码
AES256_SECRET_KEY=密钥加密 key,尽量取复杂密钥,一旦使用后,不能随便修改,否则会导致系统加密的数据无法正常解密
无需操作
# fastgpt-plugin容器
MINIO_HOST=minio 公网地址,用于给系统工具提供文件存储。例如 http://ip:9000
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="Milvus版本" >}}
{{< markdownify >}}
无需操作
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="Zilliz版本" >}}
{{< tab tabName="Zilliz版本特有" >}}
{{< markdownify >}}
打开 [Zilliz Cloud](https://zilliz.com.cn/), 创建实例并获取相关秘钥。
......@@ -189,7 +194,7 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
### 3. 修改 config.json 配置文件
修改`config.json`文件中的`mcpServerProxyEndpoint`值,设置成`mcp server`的公网可访问地址,yml 文件中默认给出了映射到 3005 端口,如通过 IP 访问,则可能是:`120.172.2.10:3005`
修改`config.json`文件中的`mcpServerProxyEndpoint`值,设置成`mcp server`的公网可访问地址,yml 文件中默认给出了映射到 3005 端口,如通过 IP 访问,则可能是:`ip:3005`
### 4. 启动容器
......
---
title: 'V4.10.0'
description: 'FastGPT V4.10.0 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 785
---
## 更新指南
### Docker 版本
- 参考最新的[docker-compose.yml](https://github.com/labring/FastGPT/blob/main/deploy/docker/docker-compose-pgvector.yml)文件,加入`fastgpt-plugin``minio`服务。
- 修改`fastgpt-plugin`环境变量`AUTH_TOKEN`为较复杂的值。
- 修改`fastgpt-plugin`环境变量`MINIO_HOST``ip:9000` 或相关域名,要求公网可访问。
- 更新`fastgpt``fastgpt-pro`(商业版)容器的环境变量:
```
PLUGIN_BASE_URL=http://fastgpt-plugin:3000
PLUGIN_TOKEN=刚修改的 AUTH_TOKEN 值
```
- 更新`fastgpt``fastgpt-pro`镜像 tag: v4.10.0。
- `docker-compose up -d`启动/更新所有服务。
### Sealos 版本
- 在 Sealos 桌面的`对象存储`中,新建一个存储桶,设置`publicRead`权限。并获取相关密钥:
![](/imgs/sealos-s3.png)
- 部署`fastgpt-plugin`服务,镜像`registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.1`,无需公网访问,设置环境变量:
```
AUTH_TOKEN=鉴权 token
# 日志等级: debug,info,warn,error
LOG_LEVEL=info
# S3 配置
MINIO_HOST=External
MINIO_PORT=443
MINIO_USE_SSL=true
MINIO_ACCESS_KEY=Access Key
MINIO_SECRET_KEY=Secret Key
MINIO_BUCKET=存储桶名
```
- 更新`fastgpt``fastgpt-pro`(商业版)容器的环境变量以及镜像 tag: v4.10.0。
```
PLUGIN_BASE_URL=fastgpt-plugin 服务的内网地址
PLUGIN_TOKEN=刚修改的 AUTH_TOKEN 值
```
## 🚀 新增内容
1. 独立系统工具服务,支持系统工具独立开发和调试。
2. 更新系统工具开发指南[系统工具开发指南](/docs/guide/plugins/dev_system_tool/)
3. 更新系统工具设计
## ⚙️ 优化
## 🐛 修复
......@@ -33,6 +33,7 @@ weight: 786
7. 过滤 CSV 导出时可能存在的危险字符串。
8. 添加安全请求头。
9. 修改密码,强制其他登录端失效。
10. Cite 引用展示优化,识别前置的 url 并自动加空格。
## 🐛 修复
......
---
title: "系统插件设计"
description: "FastGPT 系统插件设计方案"
icon: "extension"
draft: false
toc: true
weight: 301
---
## 背景
原先 FastGPT 的各项功能均采用单体应用,子 packages 的方式进行构建。随着用户范围增多,个性化需求丰富,以及多次交付时遇到的系统工具定制困难问题,导致我们对
我们决定设计一种更为灵活的扩展
主要有如下的目的:
1. 解耦合,模块化。
2. FastGPT-plugin 可以快速迭代,版本不依赖于 FastGPT
3. 降低开发复杂度(不需要运行 FastGPT 环境)
4. 插件市场
更远期的设想:
1. 以纯代码的形式构建应用
2. 加入其他可以自定义的模块
---
title: "如何开发系统工具"
description: "FastGPT 系统工具开发指南"
icon: "home_repair_service"
draft: false
toc: true
weight: 302
---
## 介绍
FastGPT 系统工具项目从 4.10.0 版本后移动到独立的`fastgpt-plugin`项目中,采用纯代码的模式进行工具编写。你可以在`fastgpt-plugin`项目中进行独立开发和调试好插件后,直接向 FastGPT 官方提交 PR 即可,无需运行 FastGPT 主服务。
## 概念
- 工具(Tool):最小的运行单元,每个工具都有唯一 ID 和特定的输入和输出。
- 工具集(Toolset):工具的集合,可以包含多个工具。
`fastgpt-plugin`中,你可以每次创建一个工具/工具集,每次提交时,仅接收一个工具/工具集。如需开发多个,可以创建多个 PR 进行提交。
## 1. 准备工作
- Fork[fastgpt-plugin 项目](https://github.com/labring/fastgpt-plugin)
- 安装[Bun](https://bun.sh/)
- 本地拉取项目进行 Dev 开发。
- bun install
- bun run dev
## 2. 初始化一个新的工具/攻击
### 2.1 执行创建命令
```bash
bun run new:tool
```
依据提示分别选择创建工具/工具集,以及目录名(小驼峰命名)。
执行完后,系统会在 `packages/tool/packages/[your-tool-name]`下生成一个工具/工具集的目录。
系统工具 (Tool) 文件结构如下:
```plaintext
src // 源代码,处理逻辑
└── index.ts
test // 测试样例
└── index.test.ts
config.ts // 配置,配置工具的名称、描述、类型、图标等
index.ts // 入口,不要改这个文件
logo.svg // Logo,替换成你的工具的 Logo
package.json // npm 包
```
工具集(toolset) 的文件结构如下:
```plaintext
children
└── tool // 这个里面的结构就和上面的 tool 基本一致
config.ts
index.ts
logo.svg
package.json
```
### 2.2 修改 config.ts
- **name****description** 字段为中文和英文两种语言
- **courseUrl** 密钥获取链接,或官网链接。
- **author** 开发者名
- **type** 为枚举类型,目前有:
- tools: 工具
- search: 搜索
- multimodal: 多模态
- communication: 通讯
- other: 其他
- **versionList** (工具中配置)用于版本管理,是一个列表,其中的元素格式:
- value:版本号,建议使用 semver
- description: 描述
- inputs 入参
- outputs 返回值
- **children**:(工具集 toolset 配置),需要将 tool import 后手动写入。
对于 ToolSet 下的 tool 来说,无需填写 `type``courseUrl``author`,这几个字段会继承 ToolSet 的配置。
#### inputs 参数格式
一般格式:
```ts
{
key: '本工具内唯一的 key,和 src/index.ts 中的 InputType 定义相同',
label: '前端显示的 label',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference], // 前端输入框的类型
valueType: WorkflowIOValueTypeEnum.string, // 数据类型
toolDescription: '工具调用时用到的描述' // 如果需要设置成工具调用参数,需要设置这个字段
}
```
有一个特殊的 key `'system_input_config'`,其用于配置工具的`激活信息`,通常包含`密钥``Endpoint``Port`等。
配置中`inputType=secret`的数据,将会通过对称加密的方式保存,以保证安全性。
**参考 dalle3**:
```
"inputs": [
{
key: 'system_input_config', // 必须为这个值
label: '', // 为空即可
inputList: [
{
key: 'url',
label: 'Dalle3 接口基础地址',
description: '例如:https://api.openai.com',
required: true,
inputType: 'input'
},
{
key: 'authorization',
label: '接口凭证(不需要 Bearer)',
description: 'sk-xxxx',
required: true,
inputType: 'secret'
}
],
renderTypeList: [FlowNodeInputTypeEnum.hidden], // 必须为这个值
valueType: WorkflowIOValueTypeEnum.object // 必须为这个值
},
....
]
```
#### outputs 参数格式
```
{
key: 'link', // 唯一键值对
valueType: WorkflowIOValueTypeEnum.string, // 具体可以看这个 Enum 的类型定义
label: '图片访问链接', // 名字
description: '图片访问链接' // 描述,可选
}
```
## 2. 编写处理逻辑
`[your-tool-name]/src/index.ts` 为入口编写处理逻辑,需要注意:
1. 使用 zod 进行类型定义,导出为 InputType 和 OutputType 两个 Schema。
2. 入口函数为 `tool`,可以定义其他的函数。
```ts
import { format } from 'date-fns';
import { z } from 'zod';
export const InputType = z.object({
formatStr: z.string().optional()
});
export const OutputType = z.object({
time: z.string()
});
export async function tool(props: z.infer<typeof InputType>): Promise<z.infer<typeof OutputType>> {
const formatStr = props.formatStr || 'yyyy-MM-dd HH:mm:ss';
return {
time: format(new Date(), formatStr)
};
}
```
上述例子给出了一个传入 formatStr (格式化字符串)并且返回当前时间的简单样例,如需安装包,可以在`/packages/tools/packages/[your-tool-name]`路径下,使用`bun install PACKAGE` 进行安装。
## 3. 调试
### 单测
`test/index.test.ts` 中编写测试样例,使用 `bun run test index.test.ts完整路径` 即可运行测试。
### 从 OpenAPI 文件进行测试
浏览器打开`localhost:3000/openapi`可进入`fastgpt-plugin`的 OpenAPI 页面,进行 API 调试。
![](/imgs/plugin-openapi.png)
可以先通过`/tool/list`接口,获取工具列表,找到需要调试的工具的`toolId`。紧接着,通过`/tool/run`来运行工具获取实际结果。
![](/imgs/plugin-openapi2.png)
### 从 FastGPT 主服务进行测试
如果本地运行有`FastGPT`主服务,则可以直接添加对应的工具进行测试。
### 可视化调试(TODO)
## 4. 提交工具至官方目录
完毕上述所有内容后,向官方仓库 `https://github.com/labring/fastgpt-plugin` 提交 PR。官方人员审核通过后即可收录为 FastGPT 的官方插件。
如无需官方收录,可自行对该项目进行 Docker 打包,并替换官方镜像即可。
---
title: "如何提交系统插件"
description: "FastGPT 系统插件提交指南"
icon: "plugin_submission"
draft: false
toc: true
weight: 302
---
> 如何向 FastGPT 社区提交系统插件
## 系统插件原则
- 尽可能的轻量简洁,以解决实际问题的工具为主
- 不允许有密集 cpu 计算,不会占用大量内存占用或网络消耗
- 不允许操作数据库
- 不允许往固定的私人地址发送请求(不包含请求某些在线服务,例如 gapier, firecrawl等)
- 不允许使用私人包,可使用主流的开源包
## 什么插件可以合并
由于目前未采用按需安装的模式,合并进仓库的插件会全部展示给用户使用。
为了控制插件的质量以及避免数量过多带来的繁琐,并不是所有的插件都会被合并到开源仓库中,你可以提前 PR 与我们沟通插件的内容。
后续实现插件按需安装后,我们会允许更多的社区插件合入。
## 如何写一个系统插件 - 初步
FastGPT 系统插件和用户工作台的插件效果是一致的,所以你需要提前了解“插件”的定义和功能。
在 FastGPT 中,插件是一种特殊的工作流,它允许你将一个工作流封装起来,并自定义入口参数和出口参数,类似于代码里的 “子函数”。
1. ### 跑通 FastGPT dev 环境
需要在 dev 环境下执行下面的操作。
1. ### 在 FastGPT 工作台中,创建一个插件
选择基础模板即可。
![](/imgs/plugin_submission1.png)
1. ### 创建系统插件配置
系统插件配置以及自定义代码,都会在 **packages/plugins** 目录下。
1.**packages/plugins/src** 下,复制一份 **template** 目录,并修改名字。
2. 打开目录里面的 template.json 文件,配置如下:
3. 目录还有一个 index.ts 文件,下文再提。
```TypeScript
{
"author": "填写你的名字",
"version": "当前系统版本号",
"name": "插件名",
"avatar": "插件头像,需要配成 icon 格式。直接把 logo 图在 pr 评论区提交即可,我们会帮你加入。",
"intro": " 插件的描述,这个描述会影响工具调用",
"showStatus": false, // 是否在对话过程展示状态
"weight": 10, // 排序权重,均默认 10
"isTool": true, // 是否作为工具调用节点
"templateType": "tools", // 都填写 tools 即可,由官方来分类
"workflow": { // 这个对象先不管,待会直接粘贴导出的工作流即可
"nodes": [],
"edges": []
}
}
```
1. 打开 **packages/plugins/register** 文件,注册你的插件。在 list 数组中,加入一个你插件目录的名字,如下图的例子。如需构建插件组(带目录),可参考 DuckDuckGo 插件。
无需额外写代码的插件,直接放在 staticPluginList 内,需要在项目内额外写代码的,写在 packagePluginList 中。
![](/imgs/plugin_submission2.png)
1. ### 完成工作流编排并测试
完成工作流编排后,可以点击右上角的发布,并在其他工作流中引入进行测试(此时属于团队插件)。
1. ### 复制配置到 template.json
鼠标放置在左上角插件的头像和名称上,会出现对于下拉框操作,可以导出工作流配置。
导出的配置,会自动到剪切板,可以直接到 template.json 文件中粘贴使用,替换步骤 2 中,**workflow** 的值。
![](/imgs/plugin_submission3.png)
1. ### 验证插件是否加载成功
刷新页面,打开系统插件,看其是否成功加载,并将其添加到工作流中使用。
![](/imgs/plugin_submission4.png)
1. ### 提交 PR
如果你觉得你的插件需要提交到开源仓库,可以通过 PR 形式向我们提交。
- 写清楚插件的介绍和功能
- 配上插件运行的效果图
- 插件参数填写说明,需要在 PR 中写清楚。例如,有些插件需要去某个提供商申请 key,需要附上对应的地址和教材,后续我们会加入到文档中。
## 写一个复杂的系统插件 - 进阶
这一章会介绍如何增加一些无法单纯通过编排实现的插件。因为可能需要用到网络请求或第三方包。
上一章提到,在插件的 **template** 目录下,还有一个 **index.ts** 文件,这个文件就是用来执行一些插件的代码的。你可以通过在 HTTP 节点中的 URL,填写插件的名字,即可触发该方法,下面以 **duckduckgo/search** 这个插件为例:
![](/imgs/plugin_submission5.png)
![](/imgs/plugin_submission6.png)
![](/imgs/plugin_submission7.png)
参考上面 3 张图片,当 HTTP 节点的 URL 为系统插件注册的名字时,该请求不会以 HTTP 形式发送,而是会请求到 index.ts 文件中的 main 方法。出入参则对应了 body 和自定义输出的字段名。
由于目前插件会默认插件输出均作为“工具调用”的结果,无法单独指定某些字段作为工具输出,所以,请避免插件的自定义输出携带大量说明字段。
\ No newline at end of file
......@@ -13,8 +13,7 @@
"previewIcon": "node ./scripts/icon/index.js",
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
"create:i18n": "node ./scripts/i18n/index.js",
"lint": "eslint \"**/*.{ts,tsx}\" --ignore-path .eslintignore",
"lint:fix": "eslint \"**/*.{ts,tsx}\" --fix --ignore-path .eslintignore",
"lint": "eslint \"**/*.{ts,tsx}\" --fix --ignore-path .eslintignore",
"test": "vitest run",
"test:workflow": "vitest run workflow"
},
......@@ -41,7 +40,7 @@
"pnpm format-code"
],
"./**/**/*.{ts,tsx}": [
"pnpm lint:fix"
"pnpm lint"
],
"./docSite/**/**/*.md": "pnpm format-doc && pnpm gen:llms"
},
......
......@@ -10,6 +10,7 @@ export const getErrText = (err: any, def = ''): any => {
err?.response?.data?.msg ||
err?.response?.msg ||
err?.msg ||
err?.error ||
def;
// msg && console.log('error =>', msg);
return replaceSensitiveText(msg);
......
import { type I18nNsType } from '@fastgpt/web/types/i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export type I18nStringType = {
'zh-CN'?: string;
'zh-Hant'?: string;
en: string;
};
export enum LangEnum {
'zh_CN' = 'zh-CN',
'zh_Hant' = 'zh-Hant',
'en' = 'en'
}
export type localeType = `${LangEnum}`;
export const LocaleList = ['en', 'zh-CN', 'zh-Hant'] as const;
export const langMap = {
[LangEnum.en]: {
label: 'English(US)',
......
import type { I18nStringType, localeType } from './type';
export const parseI18nString = (str: I18nStringType | string = '', lang: localeType = 'en') => {
if (!str || typeof str === 'string') return str;
return str[lang] ?? str['en'];
};
......@@ -8,7 +8,7 @@ import { WorkflowIOValueTypeEnum } from '../../workflow/constants';
import { PluginInputModule } from '../../workflow/template/system/pluginInput';
import { PluginOutputModule } from '../../workflow/template/system/pluginOutput';
import { HttpNode468 } from '../../workflow/template/system/http468';
import { type HttpParamAndHeaderItemType } from '../../workflow/api';
import { type HttpParamAndHeaderItemType } from '../../workflow/type/io';
import { type StoreNodeItemType } from '../../workflow/type/node';
import { HttpImgUrl } from '../../../common/file/image/constants';
import SwaggerParser from '@apidevtools/swagger-parser';
......
......@@ -3,13 +3,13 @@ import {
NodeOutputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../workflow/constants';
import { i18nT } from '../../../../web/i18n/utils';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../workflow/node/constant';
import { type McpToolConfigType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
import { type StoreSecretValueType } from '../../../common/secret/type';
import { jsonSchema2NodeInput } from '../jsonschema';
......
export enum PluginSourceEnum {
personal = 'personal', // this is a app.
systemTool = 'systemTool', // FastGPT-plugin tools, pure code.
commercial = 'commercial', // configured in Pro, with associatedPluginId. Specially, commercial-dalle3 is a systemTool
// @deprecated
community = 'community' // this is deprecated, will be replaced by systemTool
}
import type { StoreEdgeItemType } from 'core/workflow/type/edge';
import { ModuleTemplateTypeEnum } from '../../workflow/constants';
import type { StoreNodeItemType } from '../../workflow/type/node';
import { MethodType } from './controller';
import type { FlowNodeTemplateType } from '../../workflow/type/node';
import type { WorkflowTemplateType } from '../../workflow/type';
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from '../../workflow/type/io';
import type { ParentIdType } from 'common/parentFolder/type';
export type PluginRuntimeType = {
id: string;
teamId?: string;
tmbId?: string;
name: string;
avatar: string;
showStatus?: boolean;
isTool?: boolean;
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
currentCost?: number;
hasTokenFee?: boolean;
};
// system plugin
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
parentId?: ParentIdType;
isFolder?: boolean;
templateType: string;
// FastGPT-plugin tool
inputs?: FlowNodeInputItemType[];
outputs?: FlowNodeOutputItemType[];
versionList?: {
value: string;
description?: string;
}[];
// Admin workflow tool
associatedPluginId?: string;
userGuide?: string;
// commercial plugin config
originCost?: number; // n points/one time
currentCost?: number;
hasTokenFee?: boolean;
pluginOrder?: number;
isActive?: boolean;
isOfficial?: boolean;
// Admin config
inputList?: FlowNodeInputItemType['inputList'];
hasSystemSecret?: boolean;
};
export type SystemPluginTemplateListItemType = Omit<
SystemPluginTemplateItemType,
'name' | 'intro'
> & {
name: string;
intro: string;
};
import { i18nT } from '../../../../web/i18n/utils';
export enum SystemToolInputTypeEnum {
system = 'system',
team = 'team',
manual = 'manual'
}
export const SystemToolInputTypeMap = {
[SystemToolInputTypeEnum.system]: {
text: i18nT('common:System')
},
[SystemToolInputTypeEnum.team]: {
text: i18nT('common:Team')
},
[SystemToolInputTypeEnum.manual]: {
text: i18nT('common:Manual')
}
};
......@@ -6,7 +6,7 @@ import type {
VariableInputEnum,
WorkflowIOValueTypeEnum
} from '../workflow/constants';
import type { SelectedDatasetType } from '../workflow/api';
import type { SelectedDatasetType } from '../workflow/type/io';
import type { DatasetSearchModeEnum } from '../dataset/constants';
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
import type { StoreEdgeItemType } from '../workflow/type/edge';
......
......@@ -53,7 +53,7 @@ export const appWorkflow2Form = ({
nodes.forEach((node) => {
if (
node.flowNodeType === FlowNodeTypeEnum.chatNode ||
node.flowNodeType === FlowNodeTypeEnum.tools
node.flowNodeType === FlowNodeTypeEnum.agent
) {
defaultAppForm.aiSettings.model = findInputValueByKey(node.inputs, NodeInputKeyEnum.aiModel);
defaultAppForm.aiSettings.systemPrompt = findInputValueByKey(
......
......@@ -94,7 +94,7 @@ export const filterPublicNodeResponseData = ({
const publicNodeMap: Record<string, any> = {
[FlowNodeTypeEnum.pluginModule]: true,
[FlowNodeTypeEnum.datasetSearchNode]: true,
[FlowNodeTypeEnum.tools]: true,
[FlowNodeTypeEnum.agent]: true,
[FlowNodeTypeEnum.pluginOutput]: true
};
......
export enum PluginTypeEnum {
folder = 'folder',
custom = 'custom',
http = 'http'
}
export const pluginTypeMap = {
[PluginTypeEnum.folder]: {
label: '文件夹',
icon: 'file/fill/folder'
},
[PluginTypeEnum.custom]: {
label: '自定义',
icon: 'common/custom'
},
[PluginTypeEnum.http]: {
label: 'HTTP',
icon: 'common/http'
}
};
export enum PluginSourceEnum {
personal = 'personal',
community = 'community',
commercial = 'commercial'
}
import type { StoreEdgeItemType } from 'core/workflow/type/edge';
import type { StoreNodeItemType } from '../workflow/type/node';
import type { PluginTypeEnum } from './constants';
import { HttpAuthMethodType } from '../app/httpPlugin/type';
export type CreateOnePluginParams = {
name: string;
avatar: string;
intro: string;
modules: StoreNodeItemType[];
parentId: string | null;
type: `${PluginTypeEnum}`;
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};
export type UpdatePluginParams = {
id: string;
parentId?: string | null;
name?: string;
avatar?: string;
intro?: string;
modules?: StoreNodeItemType[];
edges?: StoreEdgeItemType[];
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};
export type PluginListItemType = {
_id: string;
parentId: string;
type: `${PluginTypeEnum}`;
name: string;
avatar: string;
intro: string;
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};
import type { StoreEdgeItemType } from 'core/workflow/type/edge';
import { ModuleTemplateTypeEnum } from '../workflow/constants';
import type { StoreNodeItemType } from '../workflow/type/node';
import type { PluginSourceEnum, PluginTypeEnum } from './constants';
import { MethodType } from './controller';
import type { FlowNodeTemplateType } from '../workflow/type/node';
export type PluginItemSchema = {
_id: string;
userId: string;
teamId: string;
tmbId: string;
name: string;
avatar: string;
intro: string;
updateTime: Date;
modules: StoreNodeItemType[];
edges: StoreEdgeItemType[];
parentId: string;
type: `${PluginTypeEnum}`;
metadata?: {
pluginUid?: string;
apiSchemaStr?: string;
customHeaders?: string;
};
version?: 'v1' | 'v2';
nodeVersion?: string;
inited?: boolean;
};
/* plugin template */
export type PluginTemplateType = PluginRuntimeType & {
author?: string;
id: string;
source: PluginSourceEnum;
templateType: FlowNodeTemplateType['templateType'];
intro: string;
version: string;
};
export type PluginRuntimeType = {
id: string;
teamId?: string;
tmbId?: string;
name: string;
avatar: string;
showStatus?: boolean;
isTool?: boolean;
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
currentCost?: number;
hasTokenFee?: boolean;
};
import type { EmbeddingModelItemType } from '../ai/model.d';
import { NodeInputKeyEnum } from './constants';
export type SelectedDatasetType = {
datasetId: string;
avatar: string;
name: string;
vectorModel: EmbeddingModelItemType;
}[];
export type HttpBodyType<T = Record<string, any>> = {
// [NodeInputKeyEnum.addInputParam]: Record<string, any>;
} & T;
export type HttpQueryType = {
appId: string;
chatId?: string;
responseChatItemId?: string;
variables: Record<string, any>;
[key: string]: any;
};
/* http node */
export type HttpParamAndHeaderItemType = {
key: string;
type: string;
value: string;
};
......@@ -5,14 +5,23 @@ export enum FlowNodeTemplateTypeEnum {
systemInput = 'systemInput',
ai = 'ai',
function = 'function',
tools = 'tools',
interactive = 'interactive',
// System tool type
tools = 'tools',
search = 'search',
multimodal = 'multimodal',
communication = 'communication',
finance = 'finance',
design = 'design',
productivity = 'productivity',
news = 'news',
entertainment = 'entertainment',
social = 'social',
scientific = 'scientific',
other = 'other',
// Team app type
teamApp = 'teamApp'
}
......@@ -143,6 +152,7 @@ export enum NodeInputKeyEnum {
addInputParam = 'system_addInputParam',
forbidStream = 'system_forbid_stream',
headerSecret = 'system_header_secret',
systemInputConfig = 'system_input_config',
// history
historyMaxAmount = 'maxContext',
......@@ -263,6 +273,7 @@ export enum NodeOutputKeyEnum {
text = 'system_text',
addOutputParam = 'system_addOutputParam',
rawResponse = 'system_rawResponse',
systemError = 'system_error',
// start
userFiles = 'userFiles',
......
import { WorkflowIOValueTypeEnum } from '../constants';
import { i18nT } from '../../../../web/i18n/utils';
import { WorkflowIOValueTypeEnum } from '../constants';
export enum FlowNodeInputTypeEnum { // render ui
reference = 'reference', // reference to other node output
input = 'input', // one line input
......@@ -125,7 +125,7 @@ export enum FlowNodeTypeEnum {
pluginInput = 'pluginInput',
pluginOutput = 'pluginOutput',
queryExtension = 'cfr',
tools = 'tools',
agent = 'tools',
stopTool = 'stopTool',
toolParams = 'toolParams',
lafModule = 'lafModule',
......
import { type FlowNodeInputItemType } from '../../type/io';
export const getInputComponentProps = (input: FlowNodeInputItemType) => {
return {
referencePlaceholder: input.referencePlaceholder,
placeholder: input.placeholder,
maxLength: input.maxLength,
list: input.list,
markList: input.markList,
step: input.step,
max: input.max,
min: input.min,
defaultValue: input.defaultValue,
llmModelType: input.llmModelType,
customInputConfig: input.customInputConfig
};
};
import type { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import type {
ChatItemType,
UserChatItemValueItemType,
ToolRunResponseItemType,
AIChatItemValueItemType
} from '../../chat/type';
......@@ -96,15 +95,15 @@ export type RuntimeNodeItemType = {
flowNodeType: StoreNodeItemType['flowNodeType'];
showStatus?: StoreNodeItemType['showStatus'];
isEntry?: boolean;
version?: string;
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
pluginId?: string; // workflow id / plugin id
version?: string;
// tool
toolConfig?: NodeToolConfigType;
// Tool
toolConfig?: StoreNodeItemType['toolConfig'];
};
export type RuntimeEdgeItemType = StoreEdgeItemType & {
......
import json5 from 'json5';
import { replaceVariable, valToStr } from '../../../common/string/tools';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
import type { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatCompletionRequestMessageRoleEnum } from '../../ai/constants';
import { NodeInputKeyEnum, NodeOutputKeyEnum, WorkflowIOValueTypeEnum } from '../constants';
import {
NodeInputKeyEnum,
NodeOutputKeyEnum,
VARIABLE_NODE_ID,
WorkflowIOValueTypeEnum
} from '../constants';
import { FlowNodeTypeEnum } from '../node/constant';
import { type StoreNodeItemType } from '../type/node';
import { type StoreEdgeItemType } from '../type/edge';
import { type RuntimeEdgeItemType, type RuntimeNodeItemType } from './type';
import { VARIABLE_NODE_ID } from '../constants';
import { isValidReferenceValueFormat } from '../utils';
import { type FlowNodeOutputItemType, type ReferenceValueType } from '../type/io';
import { type ChatItemType, type NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
import { replaceVariable, valToStr } from '../../../common/string/tools';
import json5 from 'json5';
import {
type InteractiveNodeResponseType,
type WorkflowInteractiveResponseType
} from '../template/system/interactive/type';
import type { StoreEdgeItemType } from '../type/edge';
import type { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import type { StoreNodeItemType } from '../type/node';
import { isValidReferenceValueFormat } from '../utils';
import type { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
export const extractDeepestInteractive = (
interactive: WorkflowInteractiveResponseType
......@@ -151,7 +155,7 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
return value;
};
/*
/*
Get interaction information (if any) from the last AI message.
What can be done:
1. Get the interactive data
......@@ -254,7 +258,8 @@ export const storeNodes2RuntimeNodes = (
inputs: node.inputs,
outputs: node.outputs,
pluginId: node.pluginId,
version: node.version
version: node.version,
toolConfig: node.toolConfig
};
}) || []
);
......@@ -268,7 +273,7 @@ export const filterWorkflowEdges = (edges: RuntimeEdgeItemType[]) => {
);
};
/*
/*
1. 输入线分类:普通线和递归线(可以追溯到自身)
2. 起始线全部非 waiting 执行,或递归线全部非 waiting 执行
*/
......@@ -279,7 +284,7 @@ export const checkNodeRunStatus = ({
node: RuntimeNodeItemType;
runtimeEdges: RuntimeEdgeItemType[];
}) => {
/*
/*
区分普通连线和递归连线
递归连线:可以通过往上查询 nodes,最终追溯到自身
*/
......@@ -363,7 +368,7 @@ export const checkNodeRunStatus = ({
return 'wait';
};
/*
/*
Get the value of the reference variable/node output
1. [string,string]
2. [string,string][]
......
import { SystemConfigNode } from './system/systemConfig';
import { PluginConfigNode } from './system/pluginConfig';
import { EmptyNode } from './system/emptyNode';
import { WorkflowStart } from './system/workflowStart';
import { AiChatModule } from './system/aiChat';
import { DatasetSearchModule } from './system/datasetSearch';
import { DatasetConcatModule } from './system/datasetConcat';
import { AssignedAnswerModule } from './system/assignedAnswer';
import { ClassifyQuestionModule } from './system/classifyQuestion/index';
import { ContextExtractModule } from './system/contextExtract/index';
import { DatasetConcatModule } from './system/datasetConcat';
import { DatasetSearchModule } from './system/datasetSearch';
import { EmptyNode } from './system/emptyNode';
import { HttpNode468 } from './system/http468';
import { PluginConfigNode } from './system/pluginConfig';
import { SystemConfigNode } from './system/systemConfig';
import { WorkflowStart } from './system/workflowStart';
import { ToolModule } from './system/tools';
import { StopToolNode } from './system/stopTool';
import { AgentNode } from './system/agent';
import { RunAppModule } from './system/abandoned/runApp/index';
import { PluginInputModule } from './system/pluginInput';
import { PluginOutputModule } from './system/pluginOutput';
import { RunPluginModule } from './system/runPlugin';
import { RunAppNode } from './system/runApp';
import { AiQueryExtension } from './system/queryExtension';
import { RunAppNode } from './system/runApp';
import { RunPluginModule } from './system/runPlugin';
import type { FlowNodeTemplateType } from '../type/node';
import { LafModule } from './system/laf';
import { IfElseNode } from './system/ifElse/index';
import { VariableUpdateNode } from './system/variableUpdate';
import { CodeNode } from './system/sandbox';
import { TextEditorNode } from './system/textEditor';
import { CustomFeedbackNode } from './system/customFeedback';
import { ReadFilesNode } from './system/readFiles';
import { IfElseNode } from './system/ifElse/index';
import { FormInputNode } from './system/interactive/formInput';
import { UserSelectNode } from './system/interactive/userSelect';
import { LafModule } from './system/laf';
import { LoopNode } from './system/loop/loop';
import { LoopStartNode } from './system/loop/loopStart';
import { LoopEndNode } from './system/loop/loopEnd';
import { FormInputNode } from './system/interactive/formInput';
import { ToolParamsNode } from './system/toolParams';
import { LoopStartNode } from './system/loop/loopStart';
import { ReadFilesNode } from './system/readFiles';
import { RunToolNode } from './system/runTool';
import { RunToolSetNode } from './system/runToolSet';
import { CodeNode } from './system/sandbox';
import { TextEditorNode } from './system/textEditor';
import { ToolParamsNode } from './system/toolParams';
import { VariableUpdateNode } from './system/variableUpdate';
const systemNodes: FlowNodeTemplateType[] = [
AiChatModule,
......@@ -45,7 +45,7 @@ const systemNodes: FlowNodeTemplateType[] = [
ClassifyQuestionModule,
ContextExtractModule,
DatasetConcatModule,
ToolModule,
AgentNode,
ToolParamsNode,
StopToolNode,
ReadFilesNode,
......
......@@ -12,15 +12,14 @@ import {
FlowNodeTemplateTypeEnum
} from '../../../../constants';
import { Input_Template_History, Input_Template_UserChatInput } from '../../../input';
import { getHandleConfig } from '../../../utils';
import { i18nT } from '../../../../../../../web/i18n/utils';
export const RunAppModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.runApp,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.runApp,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/runApp',
name: i18nT('workflow:application_call'),
intro: i18nT('workflow:select_another_application_to_call'),
......
......@@ -3,7 +3,7 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node.d';
import { type FlowNodeTemplateType } from '../../type/node';
import {
WorkflowIOValueTypeEnum,
NodeOutputKeyEnum,
......@@ -18,19 +18,18 @@ import {
} from '../input';
import { chatNodeSystemPromptTip, systemPromptTip } from '../tip';
import { LLMModelTypeEnum } from '../../../ai/constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
import { Input_Template_File_Link } from '../input';
export const ToolModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.tools,
export const AgentNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.agent,
flowNodeType: FlowNodeTypeEnum.agent,
templateType: FlowNodeTemplateTypeEnum.ai,
sourceHandle: getHandleConfig(true, true, false, true),
targetHandle: getHandleConfig(true, true, false, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/toolCall',
name: i18nT('workflow:template.tool_call'),
intro: i18nT('workflow:template.tool_call_intro'),
name: i18nT('workflow:template.agent'),
intro: i18nT('workflow:template.agent_intro'),
showStatus: true,
courseUrl: '/docs/guide/dashboard/workflow/tool/',
version: '4.9.2',
......
......@@ -19,7 +19,6 @@ import {
Input_Template_UserChatInput,
Input_Template_File_Link
} from '../../input';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils';
export const AiChatQuoteRole = {
......@@ -46,8 +45,8 @@ export const AiChatModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.chatNode,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.chatNode,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/aiChat',
name: i18nT('workflow:template.ai_chat'),
intro: i18nT('workflow:template.ai_chat_intro'),
......
......@@ -5,15 +5,14 @@ import {
NodeInputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const AssignedAnswerModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.answerNode,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.answerNode,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/reply',
name: i18nT('workflow:assigned_reply'),
intro: i18nT('workflow:intro_assigned_reply'),
......
......@@ -17,15 +17,14 @@ import {
} from '../../input';
import { Input_Template_System_Prompt } from '../../input';
import { LLMModelTypeEnum } from '../../../../ai/constants';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils';
export const ClassifyQuestionModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.classifyQuestion,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.classifyQuestion,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true),
showSourceHandle: false,
showTargetHandle: true,
avatar: 'core/workflow/template/questionClassify',
name: i18nT('workflow:question_classification'),
intro: i18nT('workflow:intro_question_classification'),
......
......@@ -5,14 +5,13 @@ import {
NodeInputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../constants';
import { getHandleConfig } from '../utils';
export const CommentNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.comment,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.comment,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: false,
showTargetHandle: false,
avatar: '',
name: '',
intro: '',
......
......@@ -12,15 +12,14 @@ import {
} from '../../../constants';
import { Input_Template_SelectAIModel, Input_Template_History } from '../../input';
import { LLMModelTypeEnum } from '../../../../ai/constants';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils';
export const ContextExtractModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.contentExtract,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.contentExtract,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/extractJson',
name: i18nT('workflow:text_content_extraction'),
intro: i18nT('workflow:intro_text_content_extraction'),
......
......@@ -5,15 +5,14 @@ import {
FlowNodeTemplateTypeEnum,
NodeInputKeyEnum
} from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const CustomFeedbackNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.customFeedback,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.customFeedback,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/customFeedback',
name: i18nT('workflow:custom_feedback'),
intro: i18nT('workflow:intro_custom_feedback'),
......
......@@ -12,7 +12,6 @@ import {
FlowNodeTemplateTypeEnum
} from '../../constants';
import { getNanoid } from '../../../../common/string/tools';
import { getHandleConfig } from '../utils';
import { type FlowNodeInputItemType } from '../../type/io.d';
import { i18nT } from '../../../../../web/i18n/utils';
......@@ -35,8 +34,8 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.datasetConcatNode,
flowNodeType: FlowNodeTypeEnum.datasetConcatNode,
templateType: FlowNodeTemplateTypeEnum.other,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/datasetConcat',
name: i18nT('workflow:knowledge_base_search_merge'),
intro: i18nT('workflow:intro_knowledge_base_search_merge'),
......
......@@ -14,7 +14,6 @@ import {
} from '../../constants';
import { Input_Template_UserChatInput } from '../input';
import { DatasetSearchModeEnum } from '../../../dataset/constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const Dataset_SEARCH_DESC = i18nT('workflow:template.dataset_search_intro');
......@@ -23,8 +22,8 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.datasetSearchNode,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/datasetSearch',
name: i18nT('workflow:template.dataset_search'),
intro: Dataset_SEARCH_DESC,
......
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { getHandleConfig } from '../utils';
export const EmptyNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.emptyNode,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.emptyNode,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: false,
showTargetHandle: false,
avatar: '',
name: '',
intro: '',
......
......@@ -13,15 +13,14 @@ import {
} from '../../constants';
import { Input_Template_DynamicInput } from '../input';
import { Output_Template_AddOutput } from '../output';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const HttpNode468: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.httpRequest468,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.httpRequest468,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/httpRequest',
name: i18nT('workflow:http_request'),
intro: i18nT('workflow:intro_http_request'),
......
......@@ -11,14 +11,13 @@ import {
FlowNodeTypeEnum
} from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node';
import { getHandleConfig } from '../../utils';
export const IfElseNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.ifElseNode,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.ifElseNode,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true),
showSourceHandle: false,
showTargetHandle: true,
avatar: 'core/workflow/template/ifelse',
name: i18nT('workflow:condition_checker'),
intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
......
......@@ -11,14 +11,13 @@ import {
FlowNodeTypeEnum
} from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node';
import { getHandleConfig } from '../../utils';
export const FormInputNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.formInput,
templateType: FlowNodeTemplateTypeEnum.interactive,
flowNodeType: FlowNodeTypeEnum.formInput,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/formInput',
name: i18nT('app:workflow.form_input'),
intro: i18nT(`app:workflow.form_input_tip`),
......
......@@ -11,14 +11,13 @@ import {
FlowNodeTypeEnum
} from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node.d';
import { getHandleConfig } from '../../utils';
export const UserSelectNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.userSelect,
templateType: FlowNodeTemplateTypeEnum.interactive,
flowNodeType: FlowNodeTypeEnum.userSelect,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true),
showSourceHandle: false,
showTargetHandle: true,
avatar: 'core/workflow/template/userSelect',
diagram: '/imgs/app/userSelect.svg',
name: i18nT('app:workflow.user_select'),
......
......@@ -12,7 +12,6 @@ import {
} from '../../constants';
import { Input_Template_DynamicInput } from '../input';
import { Output_Template_AddOutput } from '../output';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const nodeLafCustomInputConfig = {
......@@ -25,8 +24,8 @@ export const LafModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.lafModule,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.lafModule,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/lafDispatch',
name: i18nT('workflow:laf_function_call_test'),
intro: i18nT('workflow:intro_laf_function_call'),
......
......@@ -10,7 +10,6 @@ import {
NodeOutputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../../constants';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils';
import {
Input_Template_Children_Node_List,
......@@ -23,8 +22,8 @@ export const LoopNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.loop,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.loop,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/loop',
name: i18nT('workflow:loop'),
intro: i18nT('workflow:intro_loop'),
......
......@@ -6,14 +6,13 @@ import {
} from '../../../constants';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node';
import { getHandleConfig } from '../../utils';
export const LoopEndNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.loopEnd,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.loopEnd,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, true),
showSourceHandle: false,
showTargetHandle: true,
unique: true,
forbidDelete: true,
avatar: 'core/workflow/template/loopEnd',
......
......@@ -10,15 +10,14 @@ import {
NodeOutputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../../constants';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/utils';
export const LoopStartNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.loopStart,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.loopStart,
sourceHandle: getHandleConfig(false, true, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: true,
showTargetHandle: false,
avatar: 'core/workflow/template/loopStart',
name: i18nT('workflow:loop_start'),
unique: true,
......
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node.d';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const PluginConfigNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginConfig,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.pluginConfig,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: false,
showTargetHandle: false,
avatar: 'core/workflow/template/systemConfig',
name: i18nT('workflow:template.system_config'),
intro: '',
......
......@@ -2,14 +2,13 @@ import { i18nT } from '../../../../../web/i18n/utils';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const PluginInputModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginInput,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.pluginInput,
sourceHandle: getHandleConfig(false, true, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: true,
showTargetHandle: false,
unique: true,
forbidDelete: true,
avatar: 'core/workflow/template/workflowStart',
......
......@@ -2,14 +2,13 @@ import { i18nT } from '../../../../../web/i18n/utils';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const PluginOutputModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginOutput,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.pluginOutput,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, true),
showSourceHandle: false,
showTargetHandle: true,
unique: true,
forbidDelete: true,
avatar: 'core/workflow/template/pluginOutput',
......
......@@ -15,16 +15,14 @@ import {
Input_Template_UserChatInput,
Input_Template_SelectAIModel
} from '../input';
import { LLMModelTypeEnum } from '../../../ai/constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const AiQueryExtension: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.queryExtension,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.queryExtension,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/queryExtension',
name: i18nT('workflow:question_optimization'),
intro: i18nT('workflow:intro_question_optimization'),
......
......@@ -11,14 +11,13 @@ import {
FlowNodeTypeEnum
} from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node';
import { getHandleConfig } from '../../utils';
export const ReadFilesNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.readFiles,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.readFiles,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/readFiles',
name: i18nT('app:workflow.read_files'),
intro: i18nT('app:workflow.read_files_tip'),
......
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunAppNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.appModule,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.appModule,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
intro: '',
name: '',
showStatus: false,
......
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunPluginModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginModule,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.pluginModule,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
intro: '',
name: '',
showStatus: false,
......
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunToolNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.tool,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.tool,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
intro: '',
name: '',
showStatus: false,
......
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunToolSetNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.toolSet,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.toolSet,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: false,
showTargetHandle: false,
isTool: true,
intro: '',
name: '',
showStatus: false,
isTool: true,
inputs: [],
outputs: []
};
......@@ -10,7 +10,6 @@ import {
FlowNodeTypeEnum
} from '../../../node/constant';
import { type FlowNodeTemplateType } from '../../../type/node';
import { getHandleConfig } from '../../utils';
import { Input_Template_DynamicInput } from '../../input';
import { Output_Template_AddOutput } from '../../output';
import { JS_TEMPLATE } from './constants';
......@@ -20,8 +19,8 @@ export const CodeNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.code,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.code,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/codeRun',
name: i18nT('workflow:code_execution'),
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
......
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const StopToolNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.stopTool,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.stopTool,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: false,
showTargetHandle: true,
avatar: 'core/workflow/template/stopTool',
name: i18nT('workflow:tool_call_termination'),
intro: i18nT('workflow:intro_tool_call_termination'),
......
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node.d';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const SystemConfigNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.systemConfig,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.systemConfig,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: false,
showTargetHandle: false,
avatar: 'core/workflow/template/systemConfig',
name: i18nT('workflow:template.system_config'),
intro: '',
......
......@@ -10,16 +10,14 @@ import {
FlowNodeTemplateTypeEnum,
NodeInputKeyEnum
} from '../../constants';
import { getHandleConfig } from '../utils';
import { Input_Template_DynamicInput } from '../input';
import { i18nT } from '../../../../../web/i18n/utils';
export const TextEditorNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.textEditor,
templateType: FlowNodeTemplateTypeEnum.tools,
flowNodeType: FlowNodeTypeEnum.textEditor,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/textConcat',
name: i18nT('workflow:text_concatenation'),
intro: i18nT('workflow:intro_text_concatenation'),
......
import { FlowNodeTypeEnum } from '../../node/constant';
import { type FlowNodeTemplateType } from '../../type/node';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { getHandleConfig } from '../utils';
import { i18nT } from '../../../../../web/i18n/utils';
export const ToolParamsNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.toolParams,
templateType: FlowNodeTemplateTypeEnum.ai,
flowNodeType: FlowNodeTypeEnum.toolParams,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/toolParams',
name: i18nT('workflow:tool_custom_field'),
intro: i18nT('workflow:intro_tool_params_config'),
......
......@@ -5,15 +5,14 @@ import {
NodeInputKeyEnum,
WorkflowIOValueTypeEnum
} from '../../../constants';
import { getHandleConfig } from '../../utils';
import { i18nT } from '../../../../../../web/i18n/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),
showSourceHandle: true,
showTargetHandle: true,
avatar: 'core/workflow/template/variableUpdate',
name: i18nT('workflow:variable_update'),
intro: i18nT('workflow:update_specified_node_output_or_global_variable'),
......
......@@ -5,7 +5,6 @@ import {
NodeOutputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
import { getHandleConfig } from '../utils';
import { Input_Template_UserChatInput } from '../input';
import { i18nT } from '../../../../../web/i18n/utils';
import { type FlowNodeOutputItemType } from '../../type/io';
......@@ -23,8 +22,8 @@ export const WorkflowStart: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.workflowStart,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowNodeType: FlowNodeTypeEnum.workflowStart,
sourceHandle: getHandleConfig(false, true, false, false),
targetHandle: getHandleConfig(false, false, false, false),
showSourceHandle: true,
showTargetHandle: false,
avatar: 'core/workflow/template/workflowStart',
name: i18nT('workflow:template.workflow_start'),
intro: '',
......
export const chatNodeSystemPromptTip = 'core.app.tip.chatNodeSystemPromptTip';
export const systemPromptTip = 'core.app.tip.systemPromptTip';
import { i18nT } from '../../../../web/i18n/utils';
export const chatNodeSystemPromptTip = i18nT('common:core.app.tip.chatNodeSystemPromptTip');
export const systemPromptTip = i18nT('common:core.app.tip.systemPromptTip');
export const getHandleConfig = (top: boolean, right: boolean, bottom: boolean, left: boolean) => ({
top,
right,
bottom,
left
});
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from './io';
export type FlowNodeChangeProps = { nodeId: string } & (
| {
type: 'attr'; // key: attr, value: new value
key: string;
value: any;
}
| {
type: 'updateInput'; // key: update input key, value: new input value
key: string;
value: FlowNodeInputItemType;
}
| {
type: 'replaceInput'; // key: old input key, value: new input value
key: string;
value: FlowNodeInputItemType;
}
| {
type: 'addInput'; // key: null, value: new input value
value: FlowNodeInputItemType;
index?: number;
}
| {
type: 'delInput'; // key: delete input key, value: null
key: string;
}
| {
type: 'updateOutput'; // key: update output key, value: new output value
key: string;
value: FlowNodeOutputItemType;
}
| {
type: 'replaceOutput'; // key: old output key, value: new output value
key: string;
value: FlowNodeOutputItemType;
}
| {
type: 'addOutput'; // key: null, value: new output value
value: FlowNodeOutputItemType;
index?: number;
}
| {
type: 'delOutput'; // key: delete output key, value: null
key: string;
}
);
......@@ -6,7 +6,8 @@ import {
VariableInputEnum
} from '../constants';
import { DispatchNodeResponseKeyEnum } from '../runtime/constants';
import { CustomInputItemType, FlowNodeInputItemType, FlowNodeOutputItemType } from './io.d';
import type { FlowNodeInputItemType, FlowNodeOutputItemType } from './io.d';
import { CustomInputItemType } from './io.d';
import {
ChatHistoryItemResType,
ChatItemType,
......@@ -14,13 +15,14 @@ import {
ToolRunResponseItemType
} from '../../chat/type';
import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import { PluginTypeEnum } from '../../plugin/constants';
import type { StoreEdgeItemType } from './edge';
import type { AppChatConfigType } from '../../app/type';
import type { ParentIdType } from 'common/parentFolder/type';
import type { AppTypeEnum } from 'core/app/constants';
import type { StoreNodeItemType } from './node';
import { FlowNodeTemplateType } from './node';
import type { SecretValueType } from './../../../common/secret/type';
import type { I18nStringType } from '../../../common/i18n/type';
export type WorkflowTemplateBasicType = {
nodes: StoreNodeItemType[];
......@@ -32,19 +34,15 @@ export type WorkflowTemplateType = {
parentId?: ParentIdType;
isFolder?: boolean;
name: string;
avatar: string;
intro?: string;
name: I18nStringType | string;
intro?: I18nStringType | string;
author?: string;
courseUrl?: string;
version?: string;
versionLabel?: string;
isLatestVersion?: boolean;
showStatus?: boolean;
weight?: number;
version?: string;
workflow: WorkflowTemplateBasicType;
};
......@@ -63,43 +61,3 @@ export type TemplateMarketListItemType = {
type: AppTypeEnum.simple | AppTypeEnum.workflow | AppTypeEnum.plugin;
avatar: string;
};
// system plugin
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
customWorkflow?: string;
associatedPluginId?: string;
userGuide?: string;
templateType: string;
isTool?: boolean;
// commercial plugin config
originCost: number; // n points/one time
currentCost: number;
hasTokenFee: boolean;
pluginOrder: number;
isActive?: boolean;
isOfficial?: boolean;
inputConfig?: {
// Render config input form. Find the corresponding node and replace the variable directly
key: string;
label: string;
description: string;
value?: any;
}[];
};
export type THelperLine = {
position: number;
nodes: {
left: number;
right: number;
top: number;
bottom: number;
width: number;
height: number;
centerX: number;
centerY: number;
}[];
};
import type { LLMModelItemType } from '../../ai/model.d';
import type { EmbeddingModelItemType, LLMModelItemType } from '../../ai/model.d';
import type { LLMModelTypeEnum } from '../../ai/constants';
import type { WorkflowIOValueTypeEnum, NodeInputKeyEnum, NodeOutputKeyEnum } from '../constants';
import type { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../node/constant';
import type { SecretValueType } from '../../../common/secret/type';
// Dynamic input field configuration
export type CustomFieldConfigType = {
......@@ -10,11 +11,6 @@ export type CustomFieldConfigType = {
// reference
selectValueTypeList?: WorkflowIOValueTypeEnum[]; // 可以选哪个数据类型, 只有1个的话,则默认选择
// showIsToolParam?: boolean; // 是否作为工具参数
// showRequired?: boolean;
// defaultRequired?: boolean;
showDefaultValue?: boolean;
showDescription?: boolean;
};
......@@ -29,6 +25,7 @@ export type InputComponentPropsType = {
step?: number; // slider
max?: number; // slider, number input
min?: number; // slider, number input
precision?: number; // number input
defaultValue?: string;
......@@ -37,6 +34,17 @@ export type InputComponentPropsType = {
// dynamic input
customInputConfig?: CustomFieldConfigType;
};
export type InputConfigType = {
key: string;
label: string;
description?: string;
required?: boolean;
inputType: 'input' | 'numberInput' | 'secret' | 'switch' | 'select';
value?: SecretValueType;
// Selector
list?: { label: string; value: string }[];
};
export type FlowNodeInputItemType = InputComponentPropsType & {
selectedTypeIndex?: number;
......@@ -52,6 +60,8 @@ export type FlowNodeInputItemType = InputComponentPropsType & {
required?: boolean;
enum?: string;
inputList?: InputConfigType[]; // when key === 'system_input_config', this field is used
toolDescription?: string; // If this field is not empty, it is entered as a tool
// render components params
......@@ -92,6 +102,21 @@ export type FlowNodeOutputItemType = {
deprecated?: boolean;
};
// Field value type
export type ReferenceItemValueType = [string, string | undefined];
export type ReferenceArrayValueType = ReferenceItemValueType[];
export type ReferenceValueType = ReferenceItemValueType | ReferenceArrayValueType;
export type SelectedDatasetType = {
datasetId: string;
avatar: string;
name: string;
vectorModel: EmbeddingModelItemType;
}[];
/* http node */
export type HttpParamAndHeaderItemType = {
key: string;
type: string;
value: string;
};
......@@ -17,7 +17,6 @@ import {
} from '../../chat/type';
import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import { RuntimeNodeItemType } from '../runtime/type';
import { PluginTypeEnum } from '../../plugin/constants';
import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge';
import { NextApiResponse } from 'next';
import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type';
......@@ -29,6 +28,9 @@ export type NodeToolConfigType = {
mcpTool?: McpToolConfigType & {
url: string;
};
systemTool?: {
toolId: string;
};
};
export type FlowNodeCommonType = {
......@@ -55,7 +57,12 @@ export type FlowNodeCommonType = {
pluginData?: PluginDataType;
// tool data
toolData?: NodeToolConfigType;
toolConfig?: NodeToolConfigType;
// Not store, just computed
currentCost?: number;
hasTokenFee?: boolean;
hasSystemSecret?: boolean;
};
export type PluginDataType = {
......@@ -78,9 +85,8 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
id: string; // node id, unique
templateType: string;
// show handle
sourceHandle?: HandleType;
targetHandle?: HandleType;
showSourceHandle?: boolean;
showTargetHandle?: boolean;
// info
isTool?: boolean; // can be connected by tool
......@@ -92,6 +98,11 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
diagram?: string; // diagram url
courseUrl?: string; // course url
userGuide?: string; // user guide
// @deprecated
// show handle
sourceHandle?: HandleType;
targetHandle?: HandleType;
};
export type NodeTemplateListItemType = {
......
......@@ -274,9 +274,6 @@ export const appData2FlowNodeIO = ({
};
});
// const showFileLink =
// chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg;
return {
inputs: [
Input_Template_Stream_MODE,
......@@ -356,7 +353,7 @@ export const formatEditorVariablePickerIcon = (
export const isValidReferenceValueFormat = (value: any): value is ReferenceItemValueType => {
return Array.isArray(value) && value.length === 2 && typeof value[0] === 'string';
};
/*
/*
Check whether the value([variableId, outputId]) value is a valid reference value:
1. The value must be an array of length 2
2. The first item of the array must be one of VARIABLE_NODE_ID or nodeIds
......@@ -370,7 +367,7 @@ export const isValidReferenceValue = (
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
return validIdSet.has(value[0]);
};
/*
/*
Check whether the value([variableId, outputId][]) value is a valid reference value array:
1. The value must be an array
2. The array must contain at least one element
......
# Package 说明
该 package 存放工作流系统插件。
\ No newline at end of file
{
"name": "@fastgpt/plugins",
"version": "1.0.0",
"type": "module",
"dependencies": {
"cheerio": "1.0.0-rc.12",
"@types/pg": "^8.6.6",
"@types/nodemailer": "^6.4.17",
"axios": "^1.8.2",
"duck-duck-scrape": "^2.2.5",
"echarts": "5.4.1",
"expr-eval": "^2.0.2",
"lodash": "^4.17.21",
"mssql": "^11.0.1",
"mysql2": "^3.11.3",
"json5": "^2.2.3",
"nodemailer": "^6.10.0",
"pg": "^8.10.0",
"wikijs": "^6.4.1"
},
"devDependencies": {
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*",
"@types/lodash": "^4.14.191",
"@types/node": "20.14.0"
}
}
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { type SystemPluginResponseType } from './type';
import { type SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
import { cloneDeep } from 'lodash';
import { WorkerNameEnum, runWorker } from '@fastgpt/service/worker/utils';
// Run in main thread
const staticPluginList = [
'getTime',
'fetchUrl',
'feishu',
'DingTalkWebhook',
'WeWorkWebhook',
'google',
'bing',
'bocha',
'delay'
];
// Run in worker thread (Have npm packages)
const packagePluginList = [
'mathExprVal',
'duckduckgo',
'duckduckgo/search',
'duckduckgo/searchImg',
'duckduckgo/searchNews',
'duckduckgo/searchVideo',
'drawing',
'drawing/baseChart',
'wiki',
'databaseConnection',
'Doc2X',
'Doc2X/PDF2text',
'searchXNG',
'smtpEmail'
];
export const list = [...staticPluginList, ...packagePluginList];
/* Get plugins */
export const getCommunityPlugins = () => {
return Promise.all(
list.map<Promise<SystemPluginTemplateItemType>>(async (name) => {
const config = (await import(`./src/${name}/template.json`))?.default;
const isFolder = list.find((item) => item.startsWith(`${name}/`));
const parentIdList = name.split('/').slice(0, -1);
const parentId =
parentIdList.length > 0 ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` : null;
return {
...config,
id: `${PluginSourceEnum.community}-${name}`,
isFolder,
parentId,
isActive: true,
isOfficial: true
};
})
);
};
export const getSystemPluginTemplates = () => {
if (!global.systemPlugins) return [];
const oldPlugins = global.communityPlugins ?? [];
return [...oldPlugins, ...cloneDeep(global.systemPlugins)];
};
export const getCommunityCb = async () => {
const loadCommunityModule = async (name: string) => {
const pluginModule = await import(`./src/${name}/index`);
return pluginModule.default;
};
const result = (
await Promise.all(
list.map(async (name) => {
try {
return {
name,
cb: staticPluginList.includes(name)
? await loadCommunityModule(name)
: (e: any) => {
return runWorker(WorkerNameEnum.systemPluginRun, {
pluginName: name,
data: e
});
}
};
} catch (error) {}
})
)
).filter(Boolean) as {
name: string;
cb: any;
}[];
return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
(acc, { name, cb }) => {
acc[name] = cb;
return acc;
},
{}
);
};
import { type SystemPluginResponseType } from '../type';
import { parentPort } from 'worker_threads';
const loadModule = async (name: string): Promise<(e: any) => SystemPluginResponseType> => {
const pluginModule = await import(`../src/${name}/index`);
return pluginModule.default;
};
parentPort?.on('message', async ({ pluginName, data }: { pluginName: string; data: any }) => {
try {
const cb = await loadModule(pluginName);
parentPort?.postMessage({
type: 'success',
data: await cb(data)
});
} catch (error) {
parentPort?.postMessage({
type: 'error',
data: error
});
}
process.exit();
});
import { delay } from '@fastgpt/global/common/system/utils';
import axios from 'axios';
import { getErrText } from '@fastgpt/global/common/error/utils';
type Props = {
apikey: string;
HTMLtable: boolean;
files: string[];
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
success: boolean;
error?: Record<string, any>;
}>;
function processContent(content: string, HTMLtable: boolean): string {
if (HTMLtable) {
return content;
}
return content.replace(/<table>[\s\S]*?<\/table>/g, (htmlTable) => {
try {
// Clean up whitespace and newlines
const cleanHtml = htmlTable.replace(/\n\s*/g, '');
const rows = cleanHtml.match(/<tr>(.*?)<\/tr>/g);
if (!rows) return htmlTable;
// Parse table data
let tableData: string[][] = [];
let maxColumns = 0;
// Try to convert to markdown table
try {
rows.forEach((row, rowIndex) => {
if (!tableData[rowIndex]) {
tableData[rowIndex] = [];
}
let colIndex = 0;
const cells = row.match(/<td.*?>(.*?)<\/td>/g) || [];
cells.forEach((cell) => {
while (tableData[rowIndex][colIndex]) {
colIndex++;
}
const colspan = parseInt(cell.match(/colspan="(\d+)"/)?.[1] || '1');
const rowspan = parseInt(cell.match(/rowspan="(\d+)"/)?.[1] || '1');
const content = cell.replace(/<td.*?>|<\/td>/g, '').trim();
for (let i = 0; i < rowspan; i++) {
for (let j = 0; j < colspan; j++) {
if (!tableData[rowIndex + i]) {
tableData[rowIndex + i] = [];
}
tableData[rowIndex + i][colIndex + j] = i === 0 && j === 0 ? content : '^^';
}
}
colIndex += colspan;
maxColumns = Math.max(maxColumns, colIndex);
});
for (let i = 0; i < maxColumns; i++) {
if (!tableData[rowIndex][i]) {
tableData[rowIndex][i] = ' ';
}
}
});
const chunks: string[] = [];
const headerCells = tableData[0]
.slice(0, maxColumns)
.map((cell) => (cell === '^^' ? ' ' : cell || ' '));
const headerRow = '| ' + headerCells.join(' | ') + ' |';
chunks.push(headerRow);
const separator = '| ' + Array(headerCells.length).fill('---').join(' | ') + ' |';
chunks.push(separator);
tableData.slice(1).forEach((row) => {
const paddedRow = row
.slice(0, maxColumns)
.map((cell) => (cell === '^^' ? ' ' : cell || ' '));
while (paddedRow.length < maxColumns) {
paddedRow.push(' ');
}
chunks.push('| ' + paddedRow.join(' | ') + ' |');
});
return chunks.join('\n');
} catch (error) {
return htmlTable;
}
} catch (error) {
return htmlTable;
}
});
}
const main = async ({ apikey, files, HTMLtable }: Props): Response => {
// Check the apikey
if (!apikey) {
return Promise.reject(`API key is required`);
}
const successResult = [];
const failedResult = [];
const axiosInstance = axios.create({
timeout: 30000 // 30 seconds timeout
});
//Process each file one by one
for await (const url of files) {
try {
//Fetch the pdf and check its content type
const PDFResponse = await axios
.get(url, {
responseType: 'arraybuffer',
proxy: false,
timeout: 20000
})
.catch((error) => {
throw new Error(`[Fetch PDF Error] Failed to fetch PDF: ${getErrText(error)}`);
});
if (PDFResponse.status !== 200) {
throw new Error(
`[Fetch PDF Error] Failed with status ${PDFResponse.status}: ${PDFResponse.data}`
);
}
const contentType = PDFResponse.headers['content-type'];
const file_name = url.match(/read\/([^?]+)/)?.[1] || 'unknown.pdf';
if (!contentType || !contentType.startsWith('application/pdf')) {
throw new Error(`The provided file does not point to a PDF: ${contentType}`);
}
const blob = new Blob([PDFResponse.data], { type: 'application/pdf' });
// Get pre-upload URL first
const preupload_response = await axiosInstance
.post('https://v2.doc2x.noedgeai.com/api/v2/parse/preupload', null, {
headers: {
Authorization: `Bearer ${apikey}`
}
})
.catch((error) => {
throw new Error(`[Pre-upload Error] Failed to get pre-upload URL: ${getErrText(error)}`);
});
if (preupload_response.status !== 200) {
throw new Error(`Failed to get pre-upload URL: ${preupload_response.data}`);
}
const preupload_data = preupload_response.data;
if (preupload_data.code !== 'success') {
throw new Error(`Failed to get pre-upload URL: ${JSON.stringify(preupload_data)}`);
}
const upload_url = preupload_data.data.url;
const uid = preupload_data.data.uid;
// Upload file to pre-signed URL with binary stream
const response = await axiosInstance
.put(upload_url, blob, {
headers: {
'Content-Type': 'application/pdf'
}
})
.catch((error) => {
throw new Error(`[Upload Error] Failed to upload file: ${getErrText(error)}`);
});
if (response.status !== 200) {
throw new Error(`Upload failed with status ${response.status}: ${response.statusText}`);
}
// Get the result by uid
// Wait for the result
const checkResult = async (retry = 20) => {
if (retry <= 0)
return Promise.reject(
`File:${file_name}\n<Content>\n[Parse Timeout Error] Failed to get result (uid: ${uid}): Process timeout\n</Content>`
);
try {
const result_response = await axiosInstance
.get(`https://v2.doc2x.noedgeai.com/api/v2/parse/status?uid=${uid}`, {
headers: {
Authorization: `Bearer ${apikey}`
}
})
.catch((error) => {
throw new Error(
`[Parse Status Error] Failed to get parse status: ${getErrText(error)}`
);
});
const result_data = result_response.data;
if (!['ok', 'success'].includes(result_data.code)) {
return Promise.reject(
`File:${file_name}\n<Content>\nFailed to get result (uid: ${uid}): ${JSON.stringify(result_data)}\n</Content>`
);
}
if (['ready', 'processing'].includes(result_data.data.status)) {
await delay(4000);
return checkResult(retry - 1);
}
if (result_data.data.status === 'success') {
const result = processContent(
await Promise.all(
result_data.data.result.pages.map((page: { md: any }) => page.md)
).then((pages) => pages.join('\n')),
HTMLtable
)
// Do some post-processing
.replace(/\\[\(\)]/g, '$')
.replace(/\\[\[\]]/g, '$$')
.replace(/<img\s+src="([^"]+)"(?:\s*\?[^>]*)?(?:\s*\/>|>)/g, '![img]($1)')
.replace(/<!-- Media -->/g, '')
.replace(/<!-- Footnote -->/g, '')
.replace(/\$(.+?)\s+\\tag\{(.+?)\}\$/g, '$$$1 \\qquad \\qquad ($2)$$')
.replace(/\\text\{([^}]*?)(\b\w+)_(\w+\b)([^}]*?)\}/g, '\\text{$1$2\\_$3$4}');
return `File:${file_name}\n<Content>\n${result}\n</Content>`;
}
return checkResult(retry - 1);
} catch (error) {
if (retry > 1) {
await delay(100);
return checkResult(retry - 1);
}
throw error;
}
};
const result = await checkResult();
successResult.push(result);
} catch (error) {
failedResult.push(
`File:${url} \n<Content>\nFailed to fetch file from URL: ${getErrText(error)}\n</Content>`
);
}
}
return {
result: successResult.join('\n******\n'),
error: {
message: failedResult.join('\n******\n')
},
success: failedResult.length === 0
};
};
export default main;
{
"author": "Menghuan1918",
"name": "Doc2X服务",
"avatar": "plugins/doc2x",
"intro": "将传入的图片或PDF文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本。",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [],
"edges": []
}
}
import { Client as PgClient } from 'pg'; // PostgreSQL 客户端
import mysql from 'mysql2/promise'; // MySQL 客户端
import mssql from 'mssql'; // SQL Server 客户端
type Props = {
databaseType: string;
host: string;
port: string;
databaseName: string;
user: string;
password: string;
sql: string;
};
type Response = Promise<{
result: any; // 根据你的 SQL 查询结果类型调整
}>;
const main = async ({
databaseType,
host,
port,
databaseName,
user,
password,
sql
}: Props): Response => {
let result;
try {
if (databaseType === 'PostgreSQL') {
const client = new PgClient({
host,
port: parseInt(port, 10),
database: databaseName,
user,
password,
connectionTimeoutMillis: 30000
});
await client.connect();
const res = await client.query(sql);
result = res.rows;
await client.end();
} else if (databaseType === 'MySQL') {
const connection = await mysql.createConnection({
host,
port: parseInt(port, 10),
database: databaseName,
user,
password,
connectTimeout: 30000
});
const [rows] = await connection.execute(sql);
result = rows;
await connection.end();
} else if (databaseType === 'Microsoft SQL Server') {
const pool = await mssql.connect({
server: host,
port: parseInt(port, 10),
database: databaseName,
user,
password,
options: {
trustServerCertificate: true
}
});
result = await pool.query(sql);
await pool.close();
}
return {
result
};
} catch (error: unknown) {
// 使用类型断言来处理错误
if (error instanceof Error) {
console.error('Database query error:', error.message);
return Promise.reject(error.message);
}
console.error('Database query error:', error);
return Promise.reject('An unknown error occurred');
}
};
export default main;
import { delay } from '@fastgpt/global/common/system/utils';
type Props = {
ms: number;
};
type Response = Promise<Number>;
const main = async ({ ms }: Props): Response => {
if (typeof ms !== 'number' || ms <= 0 || ms > 300000) {
return ms;
}
await delay(ms);
return ms;
};
export default main;
{
"author": "collin",
"name": "流程等待",
"avatar": "core/workflow/template/sleep",
"intro": "让工作流等待指定时间后运行",
"showStatus": true,
"weight": 1,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "workflow:template.plugin_start",
"intro": "workflow:intro_plugin_input",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 627.6352390819724,
"y": -165.05298493910118
},
"version": "481",
"inputs": [
{
"renderTypeList": ["numberInput", "reference"],
"selectedTypeIndex": 0,
"valueType": "number",
"canEdit": true,
"key": "延迟时长",
"label": "延迟时长",
"description": "需要暂停的时间,单位毫秒",
"defaultValue": 1000,
"list": [
{
"label": "",
"value": ""
}
],
"maxFiles": 5,
"canSelectFile": true,
"canSelectImg": true,
"required": true,
"toolDescription": "需要暂停的时间,单位毫秒",
"max": 300000,
"min": 1
}
],
"outputs": [
{
"id": "ms",
"valueType": "number",
"key": "延迟时长",
"label": "延迟时长",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "common:core.module.template.self_output",
"intro": "workflow:intro_custom_plugin_output",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1921.839722563351,
"y": -160.05298493910115
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "any",
"canEdit": true,
"key": "result",
"label": "result",
"isToolOutput": true,
"description": "",
"required": true,
"value": ["zCJC6zw7c14i", "httpRawResponse"]
}
],
"outputs": []
},
{
"nodeId": "pluginConfig",
"name": "common:core.module.template.system_config",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": 184.66337662472682,
"y": -216.05298493910115
},
"version": "4811",
"inputs": [],
"outputs": []
},
{
"nodeId": "zCJC6zw7c14i",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1154.4041630064592,
"y": -455.0529849391012
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "delay",
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n\"ms\": {{$pluginInput.ms$}}\n}",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "输出字段提取",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "可以通过 JSONPath 语法来提取响应值中的指定字段",
"valueDesc": ""
}
]
}
],
"edges": [
{
"source": "pluginInput",
"target": "zCJC6zw7c14i",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "zCJC6zw7c14i-target-left"
},
{
"source": "zCJC6zw7c14i",
"target": "pluginOutput",
"sourceHandle": "zCJC6zw7c14i-source-right",
"targetHandle": "pluginOutput-target-left"
}
],
"chatConfig": {
"welcomeText": ""
}
}
}
import * as echarts from 'echarts';
import json5 from 'json5';
import { type SystemPluginSpecialResponse } from '../../../type.d';
type Props = {
title: string;
xAxis: string[];
yAxis: string[];
chartType: string;
};
type Response = Promise<{
result: SystemPluginSpecialResponse;
}>;
type SeriesData = {
name: string;
type: 'bar' | 'line' | 'pie'; // 只允许这三种类型
data: number[] | { value: number; name: string }[]; // 根据图表类型的数据结构
};
type Option = {
backgroundColor: string;
title: { text: string };
tooltip: {};
xAxis: { data: string[] };
yAxis: {};
series: SeriesData[]; // 使用定义的类型
};
const generateChart = async (
title: string,
xAxis: string[],
yAxis: string[],
chartType: string
) => {
// @ts-ignore 无法使用dom,如使用jsdom会出现生成图片无法正常展示,有高手可以帮忙解决
const chart = echarts.init(undefined, undefined, {
renderer: 'svg', // 必须使用 SVG 模式
ssr: true, // 开启 SSR
width: 400, // 需要指明高和宽
height: 300
});
const option: Option = {
backgroundColor: '#f5f5f5',
title: { text: title },
tooltip: {},
xAxis: { data: xAxis },
yAxis: {},
series: [] // 初始化为空数组
};
// 根据 chartType 生成不同的图表
switch (chartType) {
case '柱状图':
option.series.push({ name: 'Sample', type: 'bar', data: yAxis.map(Number) });
break;
case '折线图':
option.series.push({ name: 'Sample', type: 'line', data: yAxis.map(Number) });
break;
case '饼图':
option.series.push({
name: 'Sample',
type: 'pie',
data: yAxis.map((value, index) => ({
value: Number(value),
name: xAxis[index] // 使用 xAxis 作为饼图的名称
}))
});
break;
default:
console.error('不支持的图表类型:', chartType);
return '';
}
chart.setOption(option);
// 生成 Base64 图像
const base64Image = chart.getDataURL({
type: 'png',
pixelRatio: 2 // 可以设置更高的像素比以获得更清晰的图像
});
const svgContent = decodeURIComponent(base64Image.split(',')[1]);
const base64 = `data:image/svg+xml;base64,${Buffer.from(svgContent).toString('base64')}`;
return base64;
};
const main = async ({ title, xAxis, yAxis, chartType }: Props): Response => {
const base64 = await generateChart(title, xAxis, yAxis, chartType);
return {
result: {
type: 'SYSTEM_PLUGIN_BASE64',
value: base64,
extension: 'svg'
}
};
};
export default main;
{
"author": "silencezhang",
"name": "BI图表功能",
"avatar": "core/workflow/template/BI",
"intro": "BI图表功能,可以生成一些常用的图表,如饼图,柱状图,折线图等",
"showStatus": false,
"weight": 100,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [],
"edges": []
}
}
import { search, SafeSearchType } from 'duck-duck-scrape';
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
import { getErrText } from '@fastgpt/global/common/error/utils';
type Props = {
query: string;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
}>;
const main = async (props: Props, retry = 3): Response => {
const { query } = props;
try {
const searchResults = await search(query, {
safeSearch: SafeSearchType.STRICT,
time: 'y'
});
const result = searchResults.results
.map((item) => ({
title: item.title,
link: item.url,
snippet: item.description
}))
.slice(0, 10);
return {
result: JSON.stringify(result)
};
} catch (error) {
console.log(error);
if (retry <= 0) {
addLog.warn('DuckDuckGo error', { error });
return {
result: getErrText(error, 'Failed to fetch data from DuckDuckGo')
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;
{
"author": "",
"name": "DuckDuckGo 网络搜索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行网络搜索",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "search",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "插件开始",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 484.02074451450517,
"y": -79.06127656499825
},
"version": "481",
"inputs": [
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "query",
"label": "query",
"description": "检索词",
"required": true,
"toolDescription": "检索词"
}
],
"outputs": [
{
"id": "query",
"valueType": "string",
"key": "query",
"label": "query",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1759.5180706702588,
"y": -60.56127656499825
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "result",
"label": "result",
"description": " 检索结果",
"value": ["hjnVuJAOwyXV", "lEyy5QqyIBrK"]
}
],
"outputs": []
},
{
"nodeId": "hjnVuJAOwyXV",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1054.6774638324207,
"y": -403.06127656499825
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "duckduckgo/search",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n \"query\": \"{{query}}\"\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "query",
"label": "query",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "query"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"id": "lEyy5QqyIBrK",
"valueType": "string",
"type": "dynamic",
"key": "result",
"label": "result"
}
]
},
{
"nodeId": "f1mRh1D85H2D",
"name": "系统配置",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": -28.511358745511643,
"y": -103.56127656499825
},
"version": "4811",
"inputs": [],
"outputs": []
}
],
"edges": [
{
"source": "pluginInput",
"target": "hjnVuJAOwyXV",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "hjnVuJAOwyXV-target-left"
},
{
"source": "hjnVuJAOwyXV",
"target": "pluginOutput",
"sourceHandle": "hjnVuJAOwyXV-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}
import { searchImages, SafeSearchType } from 'duck-duck-scrape';
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
import { getErrText } from '@fastgpt/global/common/error/utils';
type Props = {
query: string;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
}>;
const main = async (props: Props, retry = 3): Response => {
const { query } = props;
try {
const searchResults = await searchImages(query, {
safeSearch: SafeSearchType.STRICT
});
const result = searchResults.results
.map((item) => ({
title: item.title,
image: item.image
}))
.slice(0, 10);
return {
result: JSON.stringify(result)
};
} catch (error) {
if (retry <= 0) {
addLog.warn('DuckDuckGo error', { error });
return {
result: getErrText(error, 'Failed to fetch data from DuckDuckGo')
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;
{
"author": "",
"name": "DuckDuckGo 图片搜索",
"avatar": "core/workflow/template/duckduckgo",
"intro": "使用 DuckDuckGo 进行图片搜索",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "search",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "插件开始",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 422.59478119647315,
"y": -79.06127656499825
},
"version": "481",
"inputs": [
{
"renderTypeList": ["input", "reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "query",
"label": "query",
"description": "检索词",
"required": true,
"toolDescription": "检索词"
}
],
"outputs": [
{
"id": "query",
"valueType": "string",
"key": "query",
"label": "query",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1777.58453046968,
"y": -60.56127656499825
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "result",
"label": "result",
"description": " 检索结果",
"value": ["hjnVuJAOwyXV", "lEyy5QqyIBrK"]
}
],
"outputs": []
},
{
"nodeId": "hjnVuJAOwyXV",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1054.6774638324207,
"y": -403.06127656499825
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "duckduckgo/searchImg",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n \"query\": \"{{query}}\"\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "query",
"label": "query",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "query"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"id": "lEyy5QqyIBrK",
"valueType": "string",
"type": "dynamic",
"key": "result",
"label": "result"
}
]
},
{
"nodeId": "y3A7CnUp4VOH",
"name": "系统配置",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": -35.73794266528006,
"y": -103.56127656499825
},
"version": "4811",
"inputs": [],
"outputs": []
}
],
"edges": [
{
"source": "pluginInput",
"target": "hjnVuJAOwyXV",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "hjnVuJAOwyXV-target-left"
},
{
"source": "hjnVuJAOwyXV",
"target": "pluginOutput",
"sourceHandle": "hjnVuJAOwyXV-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}
import { searchNews, SafeSearchType } from 'duck-duck-scrape';
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
import { getErrText } from '@fastgpt/global/common/error/utils';
type Props = {
query: string;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
}>;
const main = async (props: Props, retry = 3): Response => {
const { query } = props;
try {
const searchResults = await searchNews(query, {
safeSearch: SafeSearchType.STRICT
});
const result = searchResults.results
.map((item) => ({
title: item.title,
excerpt: item.excerpt,
url: item.url
}))
.slice(0, 10);
return {
result: JSON.stringify(result)
};
} catch (error) {
if (retry <= 0) {
addLog.warn('DuckDuckGo error', { error });
return {
result: getErrText(error, 'Failed to fetch data from DuckDuckGo')
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment