Compare commits
21 Commits
v4.8.6-alp
...
v4.8.8-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf7145ab54 | ||
|
|
1eedb9caba | ||
|
|
6390d64417 | ||
|
|
3d80eb288b | ||
|
|
5cb196535f | ||
|
|
982325d066 | ||
|
|
36f8755d09 | ||
|
|
fc96bb99cc | ||
|
|
1e4ffc2481 | ||
|
|
ee7496467b | ||
|
|
b5c98a4f63 | ||
|
|
090c880860 | ||
|
|
dd2a9bdee5 | ||
|
|
8d60ef505f | ||
|
|
b14514c105 | ||
|
|
11ffaaf2c5 | ||
|
|
e2ae571d15 | ||
|
|
f548e24e7d | ||
|
|
5605f1a892 | ||
|
|
88d10451c9 | ||
|
|
8a7bd689ae |
3
.github/workflows/docs-deploy-kubeconfig.yml
vendored
3
.github/workflows/docs-deploy-kubeconfig.yml
vendored
@@ -16,6 +16,9 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Change baseURL
|
||||
run: sed -i 's|^baseURL =.*|baseURL = "https://doc.fastgpt.cn"|g' ./docSite/hugo.toml
|
||||
|
||||
- name: Get current date and time
|
||||
id: datetime
|
||||
run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
1
.npmrc
1
.npmrc
@@ -1,2 +1,3 @@
|
||||
public-hoist-pattern[]=*tiktoken*
|
||||
public-hoist-pattern[]=*@zilliz/milvus2-sdk-node*
|
||||
registry=https://registry.npmjs.org/
|
||||
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"inlang.vs-code-extension"
|
||||
]
|
||||
}
|
||||
46
.vscode/i18n-ally-custom-framework.yml
vendored
46
.vscode/i18n-ally-custom-framework.yml
vendored
@@ -1,46 +0,0 @@
|
||||
# .vscode/i18n-ally-custom-framework.yml
|
||||
|
||||
# An array of strings which contain Language Ids defined by VS Code
|
||||
# You can check available language ids here: https://code.visualstudio.com/docs/languages/identifiers
|
||||
languageIds:
|
||||
- javascript
|
||||
- typescript
|
||||
- javascriptreact
|
||||
- typescriptreact
|
||||
|
||||
# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
|
||||
# You should unescape RegEx strings in order to fit in the YAML file
|
||||
# To help with this, you can use https://www.freeformatter.com/json-escape.html
|
||||
usageMatchRegex:
|
||||
# The following example shows how to detect `t("your.i18n.keys")`
|
||||
# the `{key}` will be placed by a proper keypath matching regex,
|
||||
# you can ignore it and use your own matching rules as well
|
||||
- "[^\\w\\d]t\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]commonT\\(['\"`]({key})['\"`]"
|
||||
# 支持 appT("your.i18n.keys")
|
||||
- "[^\\w\\d]appT\\(['\"`]({key})['\"`]"
|
||||
# 支持 datasetT("your.i18n.keys")
|
||||
- "[^\\w\\d]datasetT\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]fileT\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]publishT\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]workflowT\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]userT\\(['\"`]({key})['\"`]"
|
||||
- "[^\\w\\d]chatT\\(['\"`]({key})['\"`]"
|
||||
|
||||
# A RegEx to set a custom scope range. This scope will be used as a prefix when detecting keys
|
||||
# and works like how the i18next framework identifies the namespace scope from the
|
||||
# useTranslation() hook.
|
||||
# You should unescape RegEx strings in order to fit in the YAML file
|
||||
# To help with this, you can use https://www.freeformatter.com/json-escape.html
|
||||
scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
|
||||
|
||||
# An array of strings containing refactor templates.
|
||||
# The "$1" will be replaced by the keypath specified.
|
||||
# Optional: uncomment the following two lines to use
|
||||
|
||||
# refactorTemplates:
|
||||
# - i18n.get("$1")
|
||||
|
||||
|
||||
# If set to true, only enables this custom framework (will disable all built-in frameworks)
|
||||
monopoly: true
|
||||
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@@ -2,16 +2,23 @@
|
||||
"editor.formatOnSave": true,
|
||||
"editor.mouseWheelZoom": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"prettier.prettierPath": "../node_modules/prettier",
|
||||
"prettier.prettierPath": "node_modules/prettier",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"i18n-ally.localesPaths": [
|
||||
"packages/web/i18n",
|
||||
],
|
||||
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
||||
"i18n-ally.enabledParsers": [
|
||||
"json",
|
||||
"yaml",
|
||||
"js",
|
||||
"ts"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.keepFulfilled": false,
|
||||
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
|
||||
"i18n-ally.displayLanguage": "zh", // 显示语言
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
|
||||
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key"
|
||||
}
|
||||
@@ -11,7 +11,6 @@ weight: 707
|
||||
|
||||

|
||||
|
||||
|
||||
{{% alert icon="🤖" context="success" %}}
|
||||
|
||||
- MongoDB:用于存储除了向量外的各类数据
|
||||
@@ -105,13 +104,11 @@ brew install orbstack
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
## 开始部署
|
||||
|
||||
### 1. 下载 docker-compose.yml
|
||||
|
||||
|
||||
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`
|
||||
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`,在这个文件夹中依据下载的配置文件运行docker,若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox`和`fastgpt`,并在docker配置文件中注释掉`sandbox`和`fastgpt`的部分
|
||||
|
||||
- [config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json)
|
||||
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样)
|
||||
@@ -271,7 +268,6 @@ rs.status()
|
||||
|
||||
默认是写了OneAPi的连接地址和密钥,可以通过修改`docker-compose.yml`中,fastgpt容器的环境变量实现。
|
||||
|
||||
|
||||
`OPENAI_BASE_URL`(API 接口的地址,需要加/v1)
|
||||
`CHAT_API_KEY`(API 接口的凭证)。
|
||||
|
||||
@@ -315,8 +311,7 @@ docker-compose up -d
|
||||
1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。
|
||||
2. 直接输入`env`命令查看所有环境变量。
|
||||
|
||||
|
||||
### 为什么无法连接`本地模型`镜像。
|
||||
### 为什么无法连接`本地模型`镜像
|
||||
|
||||
`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络,如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。
|
||||
|
||||
@@ -368,8 +363,8 @@ mongo连接失败,查看mongo的运行状态**对应日志**。
|
||||
|
||||
由于服务初始化错误,系统重启导致。
|
||||
|
||||
* 90%是由于配置文件写不对,导致 JSON 解析报错
|
||||
* 剩下的基本是因为向量数据库连不上
|
||||
- 90%是由于配置文件写不对,导致 JSON 解析报错
|
||||
- 剩下的基本是因为向量数据库连不上
|
||||
|
||||
### 如何修改密码
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ toc: true
|
||||
weight: 705
|
||||
---
|
||||
|
||||
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in)。
|
||||
|
||||
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in),。
|
||||
|
||||
## 前置依赖项
|
||||
|
||||
@@ -16,13 +15,14 @@ weight: 705
|
||||
|
||||
- [Git](http://git-scm.com/)
|
||||
- [Docker](https://www.docker.com/)(构建镜像)
|
||||
- [Node.js v18.17 / v20.x](http://nodejs.org)
|
||||
- [Node.js v18.17 / v20.x](http://nodejs.org)(版本尽量一样,可以使用nvm管理node版本)
|
||||
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境)
|
||||
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
|
||||
|
||||
## 开始本地开发
|
||||
|
||||
{{% alert context="success" %}}
|
||||
|
||||
1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC(+0)。
|
||||
2. 建议先服务器装好**数据库**,再进行本地开发。
|
||||
{{% /alert %}}
|
||||
@@ -47,9 +47,10 @@ git clone git@github.com:<github_username>/FastGPT.git
|
||||
|
||||
### 3. 安装数据库
|
||||
|
||||
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
|
||||
|
||||
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践,或者新建文件夹并配置相关文件用以运行docker。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
|
||||
{{% alert context="warning" %}}
|
||||
Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。
|
||||
{{% /alert %}}
|
||||
|
||||
### 4. 初始配置
|
||||
|
||||
@@ -57,7 +58,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
||||
|
||||
**1. 环境变量**
|
||||
|
||||
复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template
|
||||
复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template,主要需要修改`API_KEY`和数据库的地址与端口以及数据库账号的用户名和密码,具体配置需要和docker配置文件相同,其中用户名和密码如需修改需要修改docker配置文件、数据库和`.env.local`文件,不能只改一处。
|
||||
|
||||
**2. config 配置文件**
|
||||
|
||||
@@ -73,7 +74,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
|
||||
|
||||
### 5. 运行
|
||||
|
||||
可参考项目根目录下的 `dev.md`
|
||||
可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
|
||||
|
||||
```bash
|
||||
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
|
||||
@@ -114,7 +115,6 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
|
||||
|
||||
如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。
|
||||
|
||||
|
||||
## QA
|
||||
|
||||
### 本地数据库无法连接
|
||||
@@ -130,6 +130,7 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
|
||||
FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI`的`Type`。如果没有权限,可以先执行`chmod -R +x ./scripts/`,再执行`pnpm i`。
|
||||
|
||||
仍不可行的话,可以手动执行`./scripts/postinstall.sh`里的内容。
|
||||
*如果是Windows下的话,可以使用git bash给`postinstall`脚本添加执行权限并执行sh脚本*
|
||||
|
||||
### TypeError: Cannot read properties of null (reading 'useMemo' )
|
||||
|
||||
@@ -141,6 +142,9 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
|
||||
4. `cd projects/app`
|
||||
5. `pnpm dev`
|
||||
|
||||
### Error response from daemon: error while creating mount source path 'XXX': mkdir XXX: file exists
|
||||
|
||||
这个错误可能是之前停止容器时有文件残留导致的,首先需要确认相关镜像都全部关闭,然后手动删除相关文件或者重启docker即可
|
||||
|
||||
## 加入社区
|
||||
|
||||
@@ -155,6 +159,7 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
|
||||
FastGPT 使用了 nextjs 的 page route 作为框架。为了区分好前后端代码,在目录分配上会分成 global, service, web 3个自目录,分别对应着 `前后端共用`、`后端专用`、`前端专用`的代码。
|
||||
|
||||
### monorepo
|
||||
|
||||
FastGPT 采用 pnpm workspace 方式构建 monorepo 项目,主要分为两个部分:
|
||||
|
||||
- projects/app - FastGPT 主项目
|
||||
@@ -173,6 +178,7 @@ support - 支撑功能(用户体系,计费,鉴权等)
|
||||
common - 基础功能(日志管理,文件读写等)
|
||||
|
||||
{{% details title="代码结构说明" closed="true" %}}
|
||||
|
||||
```
|
||||
.
|
||||
├── .github // github 相关配置
|
||||
@@ -200,4 +206,5 @@ common - 基础功能(日志管理,文件读写等)
|
||||
├── README_ja.md
|
||||
├── dev.md
|
||||
```
|
||||
|
||||
{{% /details %}}
|
||||
|
||||
@@ -7,18 +7,22 @@ toc: true
|
||||
weight: 852
|
||||
---
|
||||
|
||||
## 发起对话
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
该接口的 API Key 需使用`应用特定的 key`,否则会报错。
|
||||
|
||||
有些包调用时,`BaseUrl`需要添加`v1`路径,有些不需要,如果出现404情况,可补充`v1`重试。
|
||||
{{% /alert %}}
|
||||
|
||||
## 发起对话(简易应用和工作流)
|
||||
|
||||
**对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用。**
|
||||
对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
|
||||
|
||||
## 请求
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
* 传入的`model`,`temperature`等参数字段均无效,这些字段由编排决定。
|
||||
* 不会返回实际消耗`Token`值,如果需要,可以设置`detail=true`,并手动计算 `responseData` 里的`tokens`值。
|
||||
{{% /alert %}}
|
||||
|
||||
### 请求
|
||||
|
||||
{{< tabs tabTotal="2" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
@@ -67,7 +71,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions'
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 响应
|
||||
### 响应
|
||||
|
||||
{{< tabs tabTotal="5" >}}
|
||||
{{< tab tabName="detail=false,stream=false 响应" >}}
|
||||
@@ -245,7 +249,7 @@ data: [{"moduleName":"知识库搜索","moduleType":"datasetSearchNode","running
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="detail=true,stream=true 时,event值" >}}
|
||||
{{< tab tabName="event值" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
event取值:
|
||||
@@ -265,6 +269,192 @@ event取值:
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
|
||||
## 请求插件
|
||||
|
||||
插件的接口与对话接口一致,仅请求参数略有区别,有以下规定:
|
||||
|
||||
* 调用插件类型的应用时,接口默认为`detail`模式。
|
||||
* 无需传入 `chatId`,因为插件只能运行一轮。
|
||||
* 无需传入`messages`。
|
||||
* 通过传递`variables`来代表插件的输入。
|
||||
* 通过获取`pluginData`来获取插件输出。
|
||||
|
||||
### 请求示例
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
--header 'Authorization: Bearer test-xxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"stream": false,
|
||||
"chatId": "test",
|
||||
"variables": {
|
||||
"query":"你好" # 我的插件输入有一个参数,变量名叫 query
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
|
||||
{{< tab tabName="detail=true,stream=false 响应" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
* 插件的输出可以通过查找`responseData`中, `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
|
||||
* 流输出,仍可以通过`choices`进行获取。
|
||||
|
||||
```json
|
||||
{
|
||||
"responseData": [
|
||||
{
|
||||
"nodeId": "fdDgXQ6SYn8v",
|
||||
"moduleName": "AI 对话",
|
||||
"moduleType": "chatNode",
|
||||
"totalPoints": 0.685,
|
||||
"model": "FastAI-3.5",
|
||||
"tokens": 685,
|
||||
"query": "你好",
|
||||
"maxToken": 2000,
|
||||
"historyPreview": [
|
||||
{
|
||||
"obj": "Human",
|
||||
"value": "你好"
|
||||
},
|
||||
{
|
||||
"obj": "AI",
|
||||
"value": "你好!有什么可以帮助你的吗?欢迎向我提问。"
|
||||
}
|
||||
],
|
||||
"contextTotalLen": 14,
|
||||
"runningTime": 1.73
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginOutput",
|
||||
"moduleName": "自定义插件输出",
|
||||
"moduleType": "pluginOutput",
|
||||
"totalPoints": 0,
|
||||
"pluginOutput": {
|
||||
"result": "你好!有什么可以帮助你的吗?欢迎向我提问。"
|
||||
},
|
||||
"runningTime": 0
|
||||
}
|
||||
],
|
||||
"newVariables": {
|
||||
"query": "你好"
|
||||
},
|
||||
"id": "safsafsa",
|
||||
"model": "",
|
||||
"usage": {
|
||||
"prompt_tokens": 1,
|
||||
"completion_tokens": 1,
|
||||
"total_tokens": 1
|
||||
},
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "你好!有什么可以帮助你的吗?欢迎向我提问。"
|
||||
},
|
||||
"finish_reason": "stop",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
|
||||
{{< tab tabName="detail=true,stream=true 响应" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
* 插件的输出可以通过获取`event=flowResponses`中的字符串,并将其反序列化后得到一个数组。同样的,查找 `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
|
||||
* 流输出,仍和对话接口一样获取。
|
||||
|
||||
```bash
|
||||
event: flowNodeStatus
|
||||
data: {"status":"running","name":"AI 对话"}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"好"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"!"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"有"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"什"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"么"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"可以"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"帮"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"助"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"的"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"吗"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"?"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
|
||||
|
||||
event: answer
|
||||
data: [DONE]
|
||||
|
||||
event: flowResponses
|
||||
data: [{"nodeId":"fdDgXQ6SYn8v","moduleName":"AI 对话","moduleType":"chatNode","totalPoints":0.033,"model":"FastAI-3.5","tokens":33,"query":"你好","maxToken":2000,"historyPreview":[{"obj":"Human","value":"你好"},{"obj":"AI","value":"你好!有什么可以帮助你的吗?"}],"contextTotalLen":2,"runningTime":1.42},{"nodeId":"pluginOutput","moduleName":"自定义插件输出","moduleType":"pluginOutput","totalPoints":0,"pluginOutput":{"result":"你好!有什么可以帮助你的吗?"},"runningTime":0}]
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="输出获取" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
event取值:
|
||||
|
||||
- answer: 返回给客户端的文本(最终会算作回答)
|
||||
- fastAnswer: 指定回复返回给客户端的文本(最终会算作回答)
|
||||
- toolCall: 执行工具
|
||||
- toolParams: 工具参数
|
||||
- toolResponse: 工具返回
|
||||
- flowNodeStatus: 运行到的节点状态
|
||||
- flowResponses: 节点完整响应
|
||||
- updateVariables: 更新变量
|
||||
- error: 报错
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 使用案例
|
||||
|
||||
- [接入 NextWeb/ChatGPT web 等应用](/docs/use-cases/openapi)
|
||||
|
||||
@@ -13,7 +13,7 @@ weight: 853
|
||||
|
||||
|
||||
|
||||
## 创建训练订单(4.6.9地址发生改动)
|
||||
## 创建训练订单
|
||||
|
||||
{{< tabs tabTotal="2" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
@@ -26,6 +26,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"datasetId": "知识库 ID",
|
||||
"name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx"
|
||||
}'
|
||||
```
|
||||
@@ -127,8 +128,12 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/create' \
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request GET 'http://localhost:3000/api/core/dataset/list?parentId=' \
|
||||
--header 'Authorization: Bearer {{authorization}}' \
|
||||
curl --location --request POST 'http://localhost:3000/api/core/dataset/list?parentId=' \
|
||||
--header 'Authorization: Bearer xxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"parentId":""
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
@@ -138,7 +143,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- parentId - 父级ID,不传或为空,代表获取根目录下的知识库
|
||||
- parentId - 父级ID,传空字符串或者null,代表获取根目录下的知识库
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
|
||||
@@ -77,7 +77,7 @@ docker-compose up -d
|
||||
|
||||
商业版用户需要执行一个初始化,格式化团队信息。
|
||||
|
||||
发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`,{{host}} 替换成自己域名)
|
||||
发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`,{{host}} 替换成商业版域名)
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://{{host}}/api/init/v468' \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.8.6(进行中)'
|
||||
title: 'V4.8.6(需要初始化)'
|
||||
description: 'FastGPT V4.8.6 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -13,9 +13,9 @@ weight: 818
|
||||
|
||||
### 2. 修改镜像
|
||||
|
||||
- fastgpt 镜像 tag 修改成 v4.8.6-alpha
|
||||
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha
|
||||
- 商业版镜像 tag 修改成 v4.8.6-alpha
|
||||
- fastgpt 镜像 tag 修改成 v4.8.6
|
||||
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6
|
||||
- 商业版镜像 tag 修改成 v4.8.6
|
||||
|
||||
### 3. 执行初始化
|
||||
|
||||
@@ -33,10 +33,19 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
|
||||
|
||||
## V4.8.6 更新说明
|
||||
|
||||
1. 新增 - 知识库支持单个集合禁用功能
|
||||
2. 新增 - 文件夹权限继承
|
||||
3. 新增 - 网页抓取和数学计算器系统插件
|
||||
4. 新增 - 移动文本加工和自定义反馈到基础节点中
|
||||
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
|
||||
6. 修复 - 工作流中团队插件加载异常
|
||||
7. 修复 - 知识库集合目录导航失效
|
||||
1. 新增 - 应用权限继承
|
||||
2. 新增 - 知识库支持单个集合禁用功能
|
||||
3. 新增 - 系统插件模式变更,新增链接读取和数学计算器插件,正式版会更新如何自定义系统插件
|
||||
4. 新增 - 代码沙盒运行参数
|
||||
5. 新增 - AI对话时隐藏头部的功能,主要是适配移动端
|
||||
6. 优化 - 文件读取,Mongo 默认使用从节点,减轻主节点压力
|
||||
7. 优化 - 提示词模板
|
||||
8. 优化 - Mongo model 重复加载
|
||||
9. 修复 - 创建链接集合未返回 id
|
||||
10. 修复 - 文档接口说明
|
||||
11. 修复 - api system 提示合并
|
||||
12. 修复 - 团队插件目录内的内容无法加载
|
||||
13. 修复 - 知识库集合目录面包屑无法加载
|
||||
14. 修复 - Markdown 导出对话异常
|
||||
15. 修复 - 提示模板结束标签错误
|
||||
16. 修复 - 文档描述
|
||||
|
||||
29
docSite/content/zh-cn/docs/development/upgrading/487.md
Normal file
29
docSite/content/zh-cn/docs/development/upgrading/487.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: 'V4.8.7'
|
||||
description: 'FastGPT V4.8.7 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 817
|
||||
---
|
||||
|
||||
## 升级指南
|
||||
|
||||
### 1. 做好数据库备份
|
||||
|
||||
### 2. 修改镜像
|
||||
|
||||
- fastgpt 镜像 tag 修改成 v4.8.7
|
||||
- 商业版镜像 tag 修改成 v4.8.7
|
||||
|
||||
-------
|
||||
|
||||
## V4.8.7 更新说明
|
||||
|
||||
1. 新增 - 插件支持独立运行,发布和日志查看
|
||||
2. 新增 - 应用搜索
|
||||
3. 优化 - 对话框代码
|
||||
4. 优化 - 升级 Dockerfile node 和 pnpm 版本
|
||||
5. 优化 - local 域名部署,也可以正常使用 vision 模式
|
||||
6. 修复 - 简易模式无法变更全局变量
|
||||
7. 修复 - gpt4o 无法同时使用工具和图片
|
||||
25
docSite/content/zh-cn/docs/development/upgrading/488.md
Normal file
25
docSite/content/zh-cn/docs/development/upgrading/488.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: 'V4.8.8(进行中)'
|
||||
description: 'FastGPT V4.8.8 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 816
|
||||
---
|
||||
|
||||
## 升级指南
|
||||
|
||||
### 1. 做好数据库备份
|
||||
|
||||
### 2. 修改镜像
|
||||
|
||||
- fastgpt 镜像 tag 修改成 v4.8.8-alpha
|
||||
- 商业版镜像 tag 修改成 v4.8.8-alpha
|
||||
|
||||
-------
|
||||
|
||||
## V4.8. 8 更新说明
|
||||
|
||||
1. 新增 - 重构系统插件的结构。允许向开源社区 PR 系统插件,具体可见: [如何向 FastGPT 社区提交系统插件](https://fael3z0zfze.feishu.cn/wiki/ERZnw9R26iRRG0kXZRec6WL9nwh)。欢迎
|
||||
2. 新增 - DuckDuckGo 系统插件。
|
||||
3. 优化 - 节点图标。
|
||||
@@ -78,7 +78,7 @@ weight: 404
|
||||
},
|
||||
{
|
||||
"nodeId": "u6IAOEssxoZT",
|
||||
"name": "工具调用(实验)",
|
||||
"name": "工具调用",
|
||||
"intro": "通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。",
|
||||
"avatar": "/imgs/workflow/tool.svg",
|
||||
"flowNodeType": "tools",
|
||||
|
||||
@@ -5,6 +5,7 @@ enableEmoji = true
|
||||
enableGitInfo = false # N.B. .GitInfo does not currently function with git submodule content directories
|
||||
|
||||
defaultContentLanguage = 'zh-cn'
|
||||
defaultContentLanguageInSubdir = false
|
||||
[languages]
|
||||
[languages.zh-cn]
|
||||
title = "FastGPT"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
{{ end }}
|
||||
|
||||
<!-- change -->
|
||||
{{ $repoURL = $repoURL | append "docSite/content" .Site.LanguagePrefix $filePath }}
|
||||
{{ $repoURL = $repoURL | append "docSite/content" .Site.Language.Lang $filePath }}
|
||||
{{ $repoURL = delimit $repoURL "/" }}
|
||||
{{ $editPageURL := replaceRE "(https?://)|(/)+" "$1$2" $repoURL }}
|
||||
|
||||
|
||||
@@ -121,8 +121,8 @@ services:
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.7 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.7 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -156,6 +156,7 @@ services:
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ services:
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.7 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.7 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -113,6 +113,7 @@ services:
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
@@ -60,8 +60,8 @@ services:
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.8.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.5 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.8.7 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.7 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -94,6 +94,7 @@ services:
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
- STORE_LOG_LEVEL=warn
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
12
package.json
12
package.json
@@ -14,11 +14,11 @@
|
||||
"devDependencies": {
|
||||
"@chakra-ui/cli": "^2.4.1",
|
||||
"husky": "^8.0.3",
|
||||
"i18next": "23.10.0",
|
||||
"lint-staged": "^13.3.0",
|
||||
"next-i18next": "15.2.0",
|
||||
"i18next": "23.11.5",
|
||||
"next-i18next": "15.3.0",
|
||||
"react-i18next": "14.1.2",
|
||||
"prettier": "3.2.4",
|
||||
"react-i18next": "13.5.0",
|
||||
"zhlint": "^0.7.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"./docSite/**/**/*.md": "npm run format-doc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"pnpm": ">=8.6.0"
|
||||
"node": ">=18.16.0",
|
||||
"pnpm": ">=9.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
1
packages/global/common/file/image/type.d.ts
vendored
1
packages/global/common/file/image/type.d.ts
vendored
@@ -9,6 +9,7 @@ export type MongoImageSchemaType = {
|
||||
type: `${MongoImageTypeEnum}`;
|
||||
|
||||
metadata?: {
|
||||
mime?: string; // image mime type.
|
||||
relatedId?: string; // This id is associated with a set of images
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,9 +32,12 @@ export type FastGPTFeConfigsType = {
|
||||
show_promotion?: boolean;
|
||||
show_team_chat?: boolean;
|
||||
concatMd?: string;
|
||||
|
||||
docUrl?: string;
|
||||
chatbotUrl?: string;
|
||||
openAPIDocUrl?: string;
|
||||
systemPluginCourseUrl?: string;
|
||||
|
||||
systemTitle?: string;
|
||||
systemDescription?: string;
|
||||
googleClientVerKey?: string;
|
||||
@@ -70,9 +73,3 @@ export type SystemEnvType = {
|
||||
oneapiUrl?: string;
|
||||
chatApiKey?: string;
|
||||
};
|
||||
|
||||
// declare global {
|
||||
// var feConfigs: FastGPTFeConfigsType;
|
||||
// var systemEnv: SystemEnvType;
|
||||
// var systemInitd: boolean;
|
||||
// }
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { LLMModelItemType, VectorModelItemType } from './model.d';
|
||||
|
||||
export const defaultQAModels: LLMModelItemType[] = [
|
||||
{
|
||||
model: 'gpt-3.5-turbo',
|
||||
name: 'gpt-3.5-turbo',
|
||||
model: 'gpt-4o-mini',
|
||||
name: 'gpt-4o-mini',
|
||||
maxContext: 16000,
|
||||
maxResponse: 16000,
|
||||
quoteMaxToken: 13000,
|
||||
|
||||
@@ -100,7 +100,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
|
||||
<QA>
|
||||
{{quote}}
|
||||
</QA>}
|
||||
</QA>
|
||||
|
||||
思考流程:
|
||||
1. 判断问题是否与 <QA></QA> 标记中的内容有关。
|
||||
@@ -109,7 +109,12 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
||||
4. 如果有相同的问题,直接输出对应答案。
|
||||
5. 如果只有相近的问题,请把相近的问题和答案一起输出。
|
||||
|
||||
最后,避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
回答要求:
|
||||
- 如果没有相关的问答对,你需要澄清。
|
||||
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
|
||||
- 避免提及你是从 QA 获取的知识,只需要回复答案。
|
||||
- 使用 Markdown 语法优化回答格式。
|
||||
- 使用与问题相同的语言回答。
|
||||
|
||||
问题:"""{{question}}"""`
|
||||
}
|
||||
|
||||
10
packages/global/core/app/plugin/utils.ts
Normal file
10
packages/global/core/app/plugin/utils.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { StoreNodeItemType } from '../../workflow/type/node';
|
||||
import { FlowNodeInputItemType } from '../../workflow/type/io';
|
||||
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
|
||||
|
||||
export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
|
||||
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
|
||||
};
|
||||
export const getPluginRunContent = (e: { pluginInputs: FlowNodeInputItemType[] }) => {
|
||||
return JSON.stringify(e);
|
||||
};
|
||||
1
packages/global/core/app/type.d.ts
vendored
1
packages/global/core/app/type.d.ts
vendored
@@ -21,6 +21,7 @@ export type AppSchema = {
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
|
||||
updateTime: Date;
|
||||
|
||||
modules: StoreNodeItemType[];
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||
|
||||
export const getDefaultAppForm = (): AppSimpleEditFormType => ({
|
||||
aiSettings: {
|
||||
model: 'gpt-3.5-turbo',
|
||||
model: 'gpt-4o-mini',
|
||||
systemPrompt: '',
|
||||
temperature: 0,
|
||||
isResponseAnswerText: true,
|
||||
|
||||
@@ -56,7 +56,10 @@ export const chats2GPTMessages = ({
|
||||
text: item.text?.content || ''
|
||||
};
|
||||
}
|
||||
if (item.type === 'file' && item.file?.type === ChatFileTypeEnum.image) {
|
||||
if (
|
||||
item.type === ChatItemValueTypeEnum.file &&
|
||||
item.file?.type === ChatFileTypeEnum.image
|
||||
) {
|
||||
return {
|
||||
type: 'image_url',
|
||||
image_url: {
|
||||
@@ -64,7 +67,6 @@ export const chats2GPTMessages = ({
|
||||
}
|
||||
};
|
||||
}
|
||||
return;
|
||||
})
|
||||
.filter(Boolean) as ChatCompletionContentPart[];
|
||||
|
||||
@@ -166,7 +168,7 @@ export const GPTMessages2Chats = (
|
||||
} else if (item.type === 'image_url') {
|
||||
value.push({
|
||||
//@ts-ignore
|
||||
type: 'file',
|
||||
type: ChatItemValueTypeEnum.file,
|
||||
file: {
|
||||
type: ChatFileTypeEnum.image,
|
||||
name: '',
|
||||
@@ -175,7 +177,6 @@ export const GPTMessages2Chats = (
|
||||
});
|
||||
}
|
||||
});
|
||||
// @ts-ignore
|
||||
}
|
||||
} else if (
|
||||
obj === ChatRoleEnum.AI &&
|
||||
|
||||
@@ -56,7 +56,4 @@ export enum ChatStatusEnum {
|
||||
finish = 'finish'
|
||||
}
|
||||
|
||||
export const IMG_BLOCK_KEY = 'img-block';
|
||||
export const FILE_BLOCK_KEY = 'file-block';
|
||||
|
||||
export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN';
|
||||
|
||||
3
packages/global/core/chat/type.d.ts
vendored
3
packages/global/core/chat/type.d.ts
vendored
@@ -13,8 +13,8 @@ import { DispatchNodeResponseKeyEnum } from '../workflow/runtime/constants';
|
||||
import { AppChatConfigType, AppSchema, VariableItemType } from '../app/type';
|
||||
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||
import { ChatBoxInputType } from '../../../../projects/app/src/components/ChatBox/type';
|
||||
import { DispatchNodeResponseType } from '../workflow/runtime/type.d';
|
||||
import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type';
|
||||
|
||||
export type ChatSchema = {
|
||||
_id: string;
|
||||
@@ -115,6 +115,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
|
||||
status: `${ChatStatusEnum}`;
|
||||
moduleName?: string;
|
||||
ttsBuffer?: Uint8Array;
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
} & ChatBoxInputType;
|
||||
|
||||
/* --------- team chat --------- */
|
||||
|
||||
@@ -3,6 +3,17 @@ import { FlowNodeTypeEnum } from '../workflow/node/constant';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
|
||||
import { ChatHistoryItemResType, ChatItemType, UserChatItemValueItemType } from './type.d';
|
||||
|
||||
// Concat 2 -> 1, and sort by role
|
||||
export const concatHistories = (histories1: ChatItemType[], histories2: ChatItemType[]) => {
|
||||
const newHistories = [...histories1, ...histories2];
|
||||
return newHistories.sort((a, b) => {
|
||||
if (a.obj === ChatRoleEnum.System) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
};
|
||||
|
||||
export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => {
|
||||
// @ts-ignore
|
||||
const textMsg = message?.value.find((item) => item.type === ChatItemValueTypeEnum.text);
|
||||
@@ -54,11 +65,12 @@ export const filterPublicNodeResponseData = ({
|
||||
}: {
|
||||
flowResponses?: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
const filedList = ['quoteList', 'moduleType'];
|
||||
const filedList = ['quoteList', 'moduleType', 'pluginOutput'];
|
||||
const filterModuleTypeList: any[] = [
|
||||
FlowNodeTypeEnum.pluginModule,
|
||||
FlowNodeTypeEnum.datasetSearchNode,
|
||||
FlowNodeTypeEnum.tools
|
||||
FlowNodeTypeEnum.tools,
|
||||
FlowNodeTypeEnum.pluginOutput
|
||||
];
|
||||
|
||||
return flowResponses
|
||||
@@ -78,14 +90,22 @@ export const filterPublicNodeResponseData = ({
|
||||
});
|
||||
};
|
||||
|
||||
export const removeEmptyUserInput = (input: UserChatItemValueItemType[]) => {
|
||||
return input.filter((item) => {
|
||||
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
export const removeEmptyUserInput = (input?: UserChatItemValueItemType[]) => {
|
||||
return (
|
||||
input?.filter((item) => {
|
||||
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
export const getPluginOutputsFromChatResponses = (responses: ChatHistoryItemResType[]) => {
|
||||
const outputs =
|
||||
responses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput)?.pluginOutput ?? {};
|
||||
return outputs;
|
||||
};
|
||||
|
||||
2
packages/global/core/plugin/type.d.ts
vendored
2
packages/global/core/plugin/type.d.ts
vendored
@@ -32,7 +32,7 @@ export type PluginItemSchema = {
|
||||
export type PluginTemplateType = PluginRuntimeType & {
|
||||
author?: string;
|
||||
id: string;
|
||||
source: `${PluginSourceEnum}`;
|
||||
source: PluginSourceEnum;
|
||||
templateType: FlowNodeTemplateType['templateType'];
|
||||
intro: string;
|
||||
version: string;
|
||||
|
||||
@@ -4,6 +4,9 @@ export enum FlowNodeTemplateTypeEnum {
|
||||
function = 'function',
|
||||
tools = 'tools',
|
||||
|
||||
search = 'search',
|
||||
multimodal = 'multimodal',
|
||||
|
||||
other = 'other',
|
||||
teamApp = 'teamApp'
|
||||
}
|
||||
|
||||
@@ -67,11 +67,12 @@ export type RuntimeNodeItemType = {
|
||||
};
|
||||
|
||||
export type PluginRuntimeType = {
|
||||
id: string;
|
||||
teamId?: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
showStatus?: boolean;
|
||||
isTool?: boolean;
|
||||
currentCost?: number;
|
||||
nodes: StoreNodeItemType[];
|
||||
edges: StoreEdgeItemType[];
|
||||
};
|
||||
@@ -142,6 +143,9 @@ export type DispatchNodeResponseType = {
|
||||
|
||||
// code
|
||||
codeLog?: string;
|
||||
|
||||
// plugin
|
||||
pluginOutput?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type DispatchNodeResultType<T> = {
|
||||
|
||||
@@ -123,6 +123,7 @@ export const checkNodeRunStatus = ({
|
||||
(item) => item.target === node.nodeId
|
||||
);
|
||||
|
||||
// Entry
|
||||
if (workflowEdges.length === 0) {
|
||||
return 'run';
|
||||
}
|
||||
|
||||
@@ -8,5 +8,10 @@ export const Output_Template_AddOutput: FlowNodeOutputItemType = {
|
||||
key: NodeOutputKeyEnum.addOutputParam,
|
||||
type: FlowNodeOutputTypeEnum.dynamic,
|
||||
valueType: WorkflowIOValueTypeEnum.dynamic,
|
||||
label: ''
|
||||
label: '',
|
||||
customFieldConfig: {
|
||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||
showDescription: false,
|
||||
showDefaultValue: false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.chatNode,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/AI.png',
|
||||
avatar: 'core/workflow/template/aiChat',
|
||||
name: 'AI 对话',
|
||||
intro: 'AI 大模型对话',
|
||||
showStatus: true,
|
||||
|
||||
@@ -13,7 +13,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.answerNode,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/reply.png',
|
||||
avatar: 'core/workflow/template/reply',
|
||||
name: '指定回复',
|
||||
intro:
|
||||
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',
|
||||
|
||||
@@ -25,7 +25,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.classifyQuestion,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(true, false, true, true),
|
||||
avatar: '/imgs/workflow/cq.png',
|
||||
avatar: 'core/workflow/template/questionClassify',
|
||||
name: '问题分类',
|
||||
intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题`,
|
||||
showStatus: true,
|
||||
|
||||
@@ -20,7 +20,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.contentExtract,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/extract.png',
|
||||
avatar: 'core/workflow/template/extractJson',
|
||||
name: '文本内容提取',
|
||||
intro: '可从文本中提取指定的数据,例如:sql语句、搜索关键词、代码等',
|
||||
showStatus: true,
|
||||
|
||||
@@ -13,7 +13,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.customFeedback,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/customFeedback.svg',
|
||||
avatar: 'core/workflow/template/customFeedback',
|
||||
name: '自定义反馈',
|
||||
intro: '该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。',
|
||||
version: '486',
|
||||
|
||||
@@ -37,7 +37,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/concat.svg',
|
||||
avatar: 'core/workflow/template/datasetConcat',
|
||||
name: '知识库搜索引用合并',
|
||||
intro: '可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。',
|
||||
showStatus: false,
|
||||
|
||||
@@ -23,7 +23,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/db.png',
|
||||
avatar: 'core/workflow/template/datasetSearch',
|
||||
name: '知识库搜索',
|
||||
intro: Dataset_SEARCH_DESC,
|
||||
showStatus: true,
|
||||
|
||||
@@ -20,7 +20,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.httpRequest468,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/http.png',
|
||||
avatar: 'core/workflow/template/httpRequest',
|
||||
name: 'HTTP 请求',
|
||||
intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
|
||||
showStatus: true,
|
||||
@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
...Output_Template_AddOutput,
|
||||
customFieldConfig: {
|
||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||
showDescription: false,
|
||||
showDefaultValue: true
|
||||
}
|
||||
...Output_Template_AddOutput
|
||||
},
|
||||
{
|
||||
id: NodeOutputKeyEnum.error,
|
||||
|
||||
@@ -18,7 +18,7 @@ export const IfElseNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.ifElseNode,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(true, false, true, true),
|
||||
avatar: '/imgs/workflow/ifElse.svg',
|
||||
avatar: 'core/workflow/template/ifelse',
|
||||
name: '判断器',
|
||||
intro: '根据一定的条件,执行不同的分支。',
|
||||
showStatus: true,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const LafModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.lafModule,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/laf.png',
|
||||
avatar: 'core/workflow/template/lafDispatch',
|
||||
name: 'Laf 函数调用(测试)',
|
||||
intro: '可以调用Laf账号下的云函数。',
|
||||
showStatus: true,
|
||||
|
||||
@@ -11,7 +11,7 @@ export const PluginInputModule: FlowNodeTemplateType = {
|
||||
targetHandle: getHandleConfig(false, false, false, false),
|
||||
unique: true,
|
||||
forbidDelete: true,
|
||||
avatar: '/imgs/workflow/input.png',
|
||||
avatar: 'core/workflow/template/workflowStart',
|
||||
name: '插件输入',
|
||||
intro: '可以配置插件需要哪些输入,利用这些输入来运行插件',
|
||||
showStatus: false,
|
||||
|
||||
@@ -11,7 +11,7 @@ export const PluginOutputModule: FlowNodeTemplateType = {
|
||||
targetHandle: getHandleConfig(false, false, false, true),
|
||||
unique: true,
|
||||
forbidDelete: true,
|
||||
avatar: '/imgs/workflow/output.png',
|
||||
avatar: 'core/workflow/template/pluginOutput',
|
||||
name: '自定义插件输出',
|
||||
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
|
||||
showStatus: false,
|
||||
|
||||
@@ -19,12 +19,12 @@ import { LLMModelTypeEnum } from '../../../ai/constants';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const AiQueryExtension: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.chatNode,
|
||||
id: FlowNodeTypeEnum.queryExtension,
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
flowNodeType: FlowNodeTypeEnum.queryExtension,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/cfr.svg',
|
||||
avatar: 'core/workflow/template/queryExtension',
|
||||
name: '问题优化',
|
||||
intro:
|
||||
'使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。',
|
||||
|
||||
@@ -19,7 +19,7 @@ export const RunAppModule: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.runApp,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/app.png',
|
||||
avatar: 'core/workflow/template/runApp',
|
||||
name: '应用调用',
|
||||
intro: '可以选择一个其他应用进行调用',
|
||||
showStatus: true,
|
||||
|
||||
@@ -21,7 +21,7 @@ export const CodeNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.code,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/code.svg',
|
||||
avatar: 'core/workflow/template/codeRun',
|
||||
name: '代码运行',
|
||||
intro: '执行一段简单的脚本代码,通常用于进行复杂的数据处理。',
|
||||
showStatus: true,
|
||||
@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
|
||||
showDefaultValue: true
|
||||
}
|
||||
},
|
||||
{
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
canEdit: true,
|
||||
key: 'data1',
|
||||
label: 'data1',
|
||||
customInputConfig: {
|
||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||
showDescription: false,
|
||||
showDefaultValue: true
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
canEdit: true,
|
||||
key: 'data2',
|
||||
label: 'data2',
|
||||
customInputConfig: {
|
||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||
showDescription: false,
|
||||
showDefaultValue: true
|
||||
},
|
||||
required: true
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.codeType,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
|
||||
outputs: [
|
||||
{
|
||||
...Output_Template_AddOutput,
|
||||
description: '将代码中 return 的对象作为输出,传递给后续的节点'
|
||||
description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
|
||||
},
|
||||
{
|
||||
id: NodeOutputKeyEnum.rawResponse,
|
||||
|
||||
@@ -9,7 +9,7 @@ export const StopToolNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.stopTool,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/toolStop.svg',
|
||||
avatar: 'core/workflow/template/stopTool',
|
||||
name: '工具调用终止',
|
||||
intro:
|
||||
'该模块需配置工具调用使用。当该模块被执行时,本次工具调用将会强制结束,并且不再调用AI针对工具调用结果回答问题。',
|
||||
|
||||
@@ -9,7 +9,7 @@ export const SystemConfigNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.systemConfig,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(false, false, false, false),
|
||||
avatar: '/imgs/workflow/userGuide.png',
|
||||
avatar: 'core/workflow/template/systemConfig',
|
||||
name: '系统配置',
|
||||
intro: '可以配置应用的系统参数。',
|
||||
unique: true,
|
||||
|
||||
@@ -19,7 +19,7 @@ export const TextEditorNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.textEditor,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/textEditor.svg',
|
||||
avatar: 'core/workflow/template/textConcat',
|
||||
name: '文本拼接',
|
||||
intro: '可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。',
|
||||
version: '486',
|
||||
|
||||
@@ -26,8 +26,8 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
templateType: FlowNodeTemplateTypeEnum.ai,
|
||||
sourceHandle: getHandleConfig(true, true, false, true),
|
||||
targetHandle: getHandleConfig(true, true, false, true),
|
||||
avatar: '/imgs/workflow/tool.svg',
|
||||
name: '工具调用(实验)',
|
||||
avatar: 'core/workflow/template/toolCall',
|
||||
name: '工具调用',
|
||||
intro: '通过AI模型自动选择一个或多个功能块进行调用,也可以对插件进行调用。',
|
||||
showStatus: true,
|
||||
version: '481',
|
||||
|
||||
@@ -13,7 +13,7 @@ export const VariableUpdateNode: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.variableUpdate,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
avatar: '/imgs/workflow/variable.png',
|
||||
avatar: 'core/workflow/template/variableUpdate',
|
||||
name: '变量更新',
|
||||
intro: '可以更新指定节点的输出值或更新全局变量',
|
||||
showStatus: false,
|
||||
|
||||
@@ -14,7 +14,7 @@ export const WorkflowStart: FlowNodeTemplateType = {
|
||||
flowNodeType: FlowNodeTypeEnum.workflowStart,
|
||||
sourceHandle: getHandleConfig(false, true, false, false),
|
||||
targetHandle: getHandleConfig(false, false, false, false),
|
||||
avatar: '/imgs/workflow/userChatInput.svg',
|
||||
avatar: 'core/workflow/template/workflowStart',
|
||||
name: '流程开始',
|
||||
intro: '',
|
||||
forbidDelete: true,
|
||||
|
||||
12
packages/global/core/workflow/type/index.d.ts
vendored
12
packages/global/core/workflow/type/index.d.ts
vendored
@@ -42,17 +42,29 @@ export type WorkflowTemplateType = {
|
||||
|
||||
workflow: WorkflowTemplateBasicType;
|
||||
};
|
||||
|
||||
// template market
|
||||
export type TemplateMarketItemType = WorkflowTemplateType & {
|
||||
tags?: { id: string; label: string }[];
|
||||
};
|
||||
|
||||
// system plugin
|
||||
export type SystemPluginTemplateItemType = WorkflowTemplateType & {
|
||||
templateType: FlowNodeTemplateTypeEnum;
|
||||
isTool?: boolean;
|
||||
|
||||
// commercial plugin config
|
||||
originCost: number; // n points/one time
|
||||
currentCost: number;
|
||||
|
||||
isActive?: boolean;
|
||||
inputConfig?: {
|
||||
// Render config input form. Find the corresponding node and replace the variable directly
|
||||
key: string;
|
||||
label: string;
|
||||
description: string;
|
||||
value?: any;
|
||||
}[];
|
||||
|
||||
workflow: WorkflowTemplateBasicType;
|
||||
};
|
||||
|
||||
3
packages/global/core/workflow/type/node.d.ts
vendored
3
packages/global/core/workflow/type/node.d.ts
vendored
@@ -70,7 +70,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
|
||||
export type NodeTemplateListItemType = {
|
||||
id: string; // 系统节点-系统节点的 id, 系统插件-插件的 id,团队应用的 id
|
||||
flowNodeType: FlowNodeTypeEnum; // render node card
|
||||
parentId?: string;
|
||||
parentId?: ParentIdType;
|
||||
isFolder?: boolean;
|
||||
templateType: FlowNodeTemplateTypeEnum;
|
||||
avatar?: string;
|
||||
@@ -79,6 +79,7 @@ export type NodeTemplateListItemType = {
|
||||
isTool?: boolean;
|
||||
author?: string;
|
||||
unique?: boolean; // 唯一的
|
||||
currentCost?: number; // 当前积分消耗
|
||||
};
|
||||
|
||||
export type NodeTemplateListType = {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
defaultWhisperConfig
|
||||
} from '../app/constants';
|
||||
import { IfElseResultEnum } from './template/system/ifElse/constant';
|
||||
import { RuntimeNodeItemType } from './runtime/type';
|
||||
|
||||
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
|
||||
return `${nodeId}-${type}-${key}`;
|
||||
@@ -190,3 +191,38 @@ export const isReferenceValue = (value: any): boolean => {
|
||||
export const getElseIFLabel = (i: number) => {
|
||||
return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`;
|
||||
};
|
||||
|
||||
// add value to plugin input node when run plugin
|
||||
export const updatePluginInputByVariables = (
|
||||
nodes: RuntimeNodeItemType[],
|
||||
variables: Record<string, any>
|
||||
) => {
|
||||
return nodes.map((node) =>
|
||||
node.flowNodeType === FlowNodeTypeEnum.pluginInput
|
||||
? {
|
||||
...node,
|
||||
inputs: node.inputs.map((input) => {
|
||||
const parseValue = (() => {
|
||||
try {
|
||||
if (
|
||||
input.valueType === WorkflowIOValueTypeEnum.string ||
|
||||
input.valueType === WorkflowIOValueTypeEnum.number ||
|
||||
input.valueType === WorkflowIOValueTypeEnum.boolean
|
||||
)
|
||||
return variables[input.key];
|
||||
|
||||
return JSON.parse(variables[input.key]);
|
||||
} catch (e) {
|
||||
return variables[input.key];
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
...input,
|
||||
value: parseValue ?? input.value
|
||||
};
|
||||
})
|
||||
}
|
||||
: node
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
"js-yaml": "^4.1.0",
|
||||
"jschardet": "3.1.1",
|
||||
"nanoid": "^4.0.1",
|
||||
"next": "14.2.3",
|
||||
"next": "14.2.5",
|
||||
"openai": "4.28.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"timezones-list": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.14.2"
|
||||
"@types/node": "20.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends":"../../tsconfig.json",
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
},
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
"name": "@fastgpt/plugins",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"duck-duck-scrape": "^2.2.5",
|
||||
"lodash": "^4.17.21",
|
||||
"expr-eval": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fastgpt/global": "workspace:*",
|
||||
"@fastgpt/service": "workspace:*",
|
||||
"@types/node": "^20.14.2"
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/node": "20.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,126 @@
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { SystemPluginResponseType } from './type';
|
||||
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { isProduction } from '../service/common/system/constants';
|
||||
import { FastGPTProUrl, isProduction } from '../service/common/system/constants';
|
||||
import { GET, POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
let list = ['getTime', 'fetchUrl', 'mathExprVal'];
|
||||
let list = [
|
||||
'getTime',
|
||||
'fetchUrl',
|
||||
'mathExprVal',
|
||||
'duckduckgo',
|
||||
'duckduckgo/search',
|
||||
'duckduckgo/searchImg',
|
||||
'duckduckgo/searchNews',
|
||||
'duckduckgo/searchVideo'
|
||||
];
|
||||
|
||||
/* Get plugins */
|
||||
export const getCommunityPlugins = () => {
|
||||
if (isProduction && global.communitySystemPlugins) return global.communitySystemPlugins;
|
||||
return list.map<SystemPluginTemplateItemType>((name) => {
|
||||
const config = require(`./src/${name}/template.json`);
|
||||
|
||||
global.communitySystemPlugins = list.map((name) => ({
|
||||
...require(`./src/${name}/template.json`),
|
||||
id: `${PluginSourceEnum.community}-${name}`
|
||||
}));
|
||||
const isFolder = list.find((item) => item.startsWith(`${name}/`));
|
||||
|
||||
return global.communitySystemPlugins;
|
||||
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
|
||||
};
|
||||
});
|
||||
};
|
||||
const getCommercialPlugins = () => {
|
||||
return GET<SystemPluginTemplateItemType[]>('/core/app/plugin/getSystemPlugins');
|
||||
};
|
||||
export const getSystemPluginTemplates = async (refresh = false) => {
|
||||
if (isProduction && global.systemPlugins && !refresh) return cloneDeep(global.systemPlugins);
|
||||
|
||||
export const getCommunityPluginsTemplateList = () => {
|
||||
return getCommunityPlugins().map<NodeTemplateListItemType>((plugin) => ({
|
||||
id: plugin.id,
|
||||
templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other,
|
||||
flowNodeType: FlowNodeTypeEnum.pluginModule,
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
isTool: plugin.isTool
|
||||
}));
|
||||
try {
|
||||
if (!global.systemPlugins) {
|
||||
global.systemPlugins = [];
|
||||
}
|
||||
|
||||
global.systemPlugins = FastGPTProUrl ? await getCommercialPlugins() : getCommunityPlugins();
|
||||
|
||||
return cloneDeep(global.systemPlugins);
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
global.systemPlugins = undefined;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getCommunityCb = async () => {
|
||||
if (isProduction && global.communitySystemPluginCb) return global.communitySystemPluginCb;
|
||||
|
||||
// Do not modify the following code
|
||||
const loadModule = async (name: string) => {
|
||||
const module = await import(`./src/${name}/index`);
|
||||
return module.default;
|
||||
};
|
||||
|
||||
const result = await Promise.all(
|
||||
list.map(async (name) => ({
|
||||
name,
|
||||
cb: await loadModule(name)
|
||||
}))
|
||||
const result = (
|
||||
await Promise.all(
|
||||
list.map(async (name) => {
|
||||
try {
|
||||
return {
|
||||
name,
|
||||
cb: await loadModule(name)
|
||||
};
|
||||
} 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;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
global.communitySystemPluginCb = result.reduce<
|
||||
Record<string, (e: any) => SystemPluginResponseType>
|
||||
>((acc, { name, cb }) => {
|
||||
acc[name] = cb;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return global.communitySystemPluginCb;
|
||||
};
|
||||
const getCommercialCb = async () => {
|
||||
const plugins = await getSystemPluginTemplates();
|
||||
const result = plugins.map((plugin) => {
|
||||
const name = plugin.id.split('-')[1];
|
||||
|
||||
return {
|
||||
name,
|
||||
cb: (e: any) =>
|
||||
POST<Record<string, any>>('/core/app/plugin/run', {
|
||||
pluginName: name,
|
||||
data: e
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
|
||||
(acc, { name, cb }) => {
|
||||
acc[name] = cb;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
};
|
||||
export const getSystemPluginCb = async () => {
|
||||
if (isProduction && global.systemPluginCb) return global.systemPluginCb;
|
||||
|
||||
try {
|
||||
global.systemPluginCb = {};
|
||||
global.systemPluginCb = FastGPTProUrl ? await getCommercialCb() : await getCommunityCb();
|
||||
return global.systemPluginCb;
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
global.systemPluginCb = undefined;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
47
packages/plugins/src/duckduckgo/search/index.ts
Normal file
47
packages/plugins/src/duckduckgo/search/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { search, SafeSearchType } from 'duck-duck-scrape';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
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) {
|
||||
if (retry <= 0) {
|
||||
return {
|
||||
result: 'Failed to fetch data'
|
||||
};
|
||||
}
|
||||
|
||||
addLog.warn('DuckDuckGo error', { error });
|
||||
|
||||
await delay(Math.random() * 2000);
|
||||
return main(props, retry - 1);
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
260
packages/plugins/src/duckduckgo/search/template.json
Normal file
260
packages/plugins/src/duckduckgo/search/template.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "DuckDuckGo 网络搜索",
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "使用 DuckDuckGo 进行网络搜索",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 393.68844551739926,
|
||||
"y": -58.80666875994541
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["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": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1795.6509902691012,
|
||||
"y": -47.04550785550961
|
||||
},
|
||||
"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": "/imgs/workflow/http.png",
|
||||
"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": "core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "duckduckgo/search"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\n \"query\": \"{{query}}\"\n}",
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"required": true,
|
||||
"value": ["pluginInput", "query"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"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": "error",
|
||||
"key": "error",
|
||||
"label": "请求错误",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"label": "原始响应",
|
||||
"required": true,
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "lEyy5QqyIBrK",
|
||||
"valueType": "string",
|
||||
"type": "dynamic",
|
||||
"key": "result",
|
||||
"label": "result"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
46
packages/plugins/src/duckduckgo/searchImg/index.ts
Normal file
46
packages/plugins/src/duckduckgo/searchImg/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { searchImages, SafeSearchType } from 'duck-duck-scrape';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
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) {
|
||||
return {
|
||||
result: 'Failed to fetch data'
|
||||
};
|
||||
}
|
||||
|
||||
addLog.warn('DuckDuckGo error', { error });
|
||||
|
||||
await delay(Math.random() * 2000);
|
||||
return main(props, retry - 1);
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
260
packages/plugins/src/duckduckgo/searchImg/template.json
Normal file
260
packages/plugins/src/duckduckgo/searchImg/template.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "DuckDuckGo 图片搜索",
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "使用 DuckDuckGo 进行图片搜索",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 393.68844551739926,
|
||||
"y": -58.80666875994541
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["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": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1795.6509902691012,
|
||||
"y": -47.04550785550961
|
||||
},
|
||||
"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": "/imgs/workflow/http.png",
|
||||
"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": "core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "duckduckgo/searchImg"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\n \"query\": \"{{query}}\"\n}",
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"required": true,
|
||||
"value": ["pluginInput", "query"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"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": "error",
|
||||
"key": "error",
|
||||
"label": "请求错误",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"label": "原始响应",
|
||||
"required": true,
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "lEyy5QqyIBrK",
|
||||
"valueType": "string",
|
||||
"type": "dynamic",
|
||||
"key": "result",
|
||||
"label": "result"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
47
packages/plugins/src/duckduckgo/searchNews/index.ts
Normal file
47
packages/plugins/src/duckduckgo/searchNews/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { searchNews, SafeSearchType } from 'duck-duck-scrape';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
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) {
|
||||
return {
|
||||
result: 'Failed to fetch data'
|
||||
};
|
||||
}
|
||||
|
||||
addLog.warn('DuckDuckGo error', { error });
|
||||
|
||||
await delay(Math.random() * 2000);
|
||||
return main(props, retry - 1);
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
260
packages/plugins/src/duckduckgo/searchNews/template.json
Normal file
260
packages/plugins/src/duckduckgo/searchNews/template.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "DuckDuckGo 新闻检索",
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "使用 DuckDuckGo 进行新闻检索",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 393.68844551739926,
|
||||
"y": -58.80666875994541
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["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": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1795.6509902691012,
|
||||
"y": -47.04550785550961
|
||||
},
|
||||
"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": "/imgs/workflow/http.png",
|
||||
"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": "core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "duckduckgo/searchNews"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\n \"query\": \"{{query}}\"\n}",
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"required": true,
|
||||
"value": ["pluginInput", "query"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"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": "error",
|
||||
"key": "error",
|
||||
"label": "请求错误",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"label": "原始响应",
|
||||
"required": true,
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "lEyy5QqyIBrK",
|
||||
"valueType": "string",
|
||||
"type": "dynamic",
|
||||
"key": "result",
|
||||
"label": "result"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
47
packages/plugins/src/duckduckgo/searchVideo/index.ts
Normal file
47
packages/plugins/src/duckduckgo/searchVideo/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { searchVideos, SafeSearchType } from 'duck-duck-scrape';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
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 searchVideos(query, {
|
||||
safeSearch: SafeSearchType.STRICT
|
||||
});
|
||||
|
||||
const result = searchResults.results
|
||||
.map((item) => ({
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
url: item.url
|
||||
}))
|
||||
.slice(0, 10);
|
||||
|
||||
return {
|
||||
result: JSON.stringify(result)
|
||||
};
|
||||
} catch (error) {
|
||||
if (retry <= 0) {
|
||||
return {
|
||||
result: 'Failed to fetch data'
|
||||
};
|
||||
}
|
||||
|
||||
addLog.warn('DuckDuckGo error', { error });
|
||||
|
||||
await delay(Math.random() * 2000);
|
||||
return main(props, retry - 1);
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
260
packages/plugins/src/duckduckgo/searchVideo/template.json
Normal file
260
packages/plugins/src/duckduckgo/searchVideo/template.json
Normal file
@@ -0,0 +1,260 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "DuckDuckGo 视频搜索",
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "使用 DuckDuckGo 进行视频搜索",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "自定义插件输入",
|
||||
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
|
||||
"avatar": "/imgs/workflow/input.png",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 393.68844551739926,
|
||||
"y": -58.80666875994541
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["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": "/imgs/workflow/output.png",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1795.6509902691012,
|
||||
"y": -47.04550785550961
|
||||
},
|
||||
"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": "/imgs/workflow/http.png",
|
||||
"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": "core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "duckduckgo/searchVideo"
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": ["custom"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "core.module.input.description.Http Request Header",
|
||||
"placeholder": "core.module.input.description.Http Request Header",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\n \"query\": \"{{query}}\"\n}",
|
||||
"label": "",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"required": true,
|
||||
"value": ["pluginInput", "query"]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"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": "error",
|
||||
"key": "error",
|
||||
"label": "请求错误",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"label": "原始响应",
|
||||
"required": true,
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "lEyy5QqyIBrK",
|
||||
"valueType": "string",
|
||||
"type": "dynamic",
|
||||
"key": "result",
|
||||
"label": "result"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
17
packages/plugins/src/duckduckgo/template.json
Normal file
17
packages/plugins/src/duckduckgo/template.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "DuckDuckGo服务",
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "DuckDuckGo 服务,包含网络搜索、图片搜索、新闻搜索等。",
|
||||
"showStatus": false,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "tools",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [],
|
||||
"edges": []
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { SystemPluginResponseType } from '../../type';
|
||||
import { urlsFetch } from '../../../service/common/string/cheerio';
|
||||
import { urlsFetch } from '@fastgpt/service/common/string/cheerio';
|
||||
|
||||
type Props = {
|
||||
url: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"author": "FastGPT",
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "网页内容抓取",
|
||||
"avatar": "/imgs/workflow/fetchUrl.svg",
|
||||
"avatar": "core/workflow/template/fetchUrl",
|
||||
"intro": "可获取一个网页链接内容,并以 Markdown 格式输出,仅支持获取静态网站。",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
@@ -88,7 +88,7 @@
|
||||
"x": 1050.9890727421412,
|
||||
"y": -415.2085119990912
|
||||
},
|
||||
"version": "486",
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"author": "FastGPT Team",
|
||||
"author": "",
|
||||
"version": "481",
|
||||
"templateType": "tools",
|
||||
"name": "获取当前时间",
|
||||
"avatar": "/imgs/workflow/getCurrentTime.svg",
|
||||
"avatar": "core/workflow/template/getTime",
|
||||
"intro": "获取用户当前时区的时间。",
|
||||
"showStatus": false,
|
||||
"isTool": true,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"author": "FastGPT",
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"name": "数学公式执行",
|
||||
"avatar": "/imgs/workflow/mathExprEval.svg",
|
||||
"avatar": "core/workflow/template/mathCall",
|
||||
"intro": "用于执行数学表达式的工具,通过 js 的 expr-eval 库运行表达式并返回结果。",
|
||||
"showStatus": false,
|
||||
"weight": 10,
|
||||
|
||||
6
packages/plugins/type.d.ts
vendored
6
packages/plugins/type.d.ts
vendored
@@ -1,7 +1,9 @@
|
||||
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
|
||||
export type SystemPluginResponseType = Promise<Record<string, any>>;
|
||||
|
||||
declare global {
|
||||
var communitySystemPlugins: SystemPluginTemplateItemType[];
|
||||
var communitySystemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
|
||||
var systemPlugins: SystemPluginTemplateItemType[];
|
||||
var systemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ const instance = axios.create({
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
});
|
||||
export const serverRequestBaseUrl = `http://${SERVICE_LOCAL_HOST}`;
|
||||
|
||||
/* 请求拦截 */
|
||||
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
|
||||
@@ -79,7 +80,7 @@ export function request(url: string, data: any, config: ConfigType, method: Meth
|
||||
|
||||
return instance
|
||||
.request({
|
||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
||||
baseURL: serverRequestBaseUrl,
|
||||
url,
|
||||
method,
|
||||
data: ['POST', 'PUT'].includes(method) ? data : null,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { RawTextBufferSchemaType } from './type';
|
||||
|
||||
@@ -28,6 +28,7 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoRawTextBuffer: Model<RawTextBufferSchemaType> =
|
||||
models[collectionName] || model(collectionName, RawTextBufferSchema);
|
||||
MongoRawTextBuffer.syncIndexes();
|
||||
export const MongoRawTextBuffer = getMongoModel<RawTextBufferSchemaType>(
|
||||
collectionName,
|
||||
RawTextBufferSchema
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { TTSBufferSchemaType } from './type.d';
|
||||
|
||||
@@ -31,6 +31,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoTTSBuffer: Model<TTSBufferSchemaType> =
|
||||
models[collectionName] || model(collectionName, TTSBufferSchema);
|
||||
MongoTTSBuffer.syncIndexes();
|
||||
export const MongoTTSBuffer = getMongoModel<TTSBufferSchemaType>(collectionName, TTSBufferSchema);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
|
||||
const FileSchema = new Schema({});
|
||||
@@ -10,6 +10,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoFileSchema = models['dataset.files'] || model('dataset.files', FileSchema);
|
||||
|
||||
MongoFileSchema.syncIndexes();
|
||||
export const MongoFileSchema = getMongoModel('dataset.files', FileSchema);
|
||||
|
||||
@@ -8,6 +8,7 @@ export function getMongoImgUrl(id: string) {
|
||||
}
|
||||
|
||||
export const maxImgSize = 1024 * 1024 * 12;
|
||||
const base64MimeRegex = /data:image\/([^\)]+);base64/;
|
||||
export async function uploadMongoImg({
|
||||
type,
|
||||
base64Img,
|
||||
@@ -22,7 +23,8 @@ export async function uploadMongoImg({
|
||||
return Promise.reject('Image too large');
|
||||
}
|
||||
|
||||
const base64Data = base64Img.split(',')[1];
|
||||
const [base64Mime, base64Data] = base64Img.split(',')
|
||||
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'jpeg'}`
|
||||
const binary = Buffer.from(base64Data, 'base64');
|
||||
|
||||
const { _id } = await MongoImage.create({
|
||||
@@ -30,7 +32,7 @@ export async function uploadMongoImg({
|
||||
teamId,
|
||||
binary,
|
||||
expiredTime,
|
||||
metadata,
|
||||
metadata: Object.assign({ mime }, metadata),
|
||||
shareId
|
||||
});
|
||||
|
||||
@@ -42,7 +44,7 @@ export async function readMongoImg({ id }: { id: string }) {
|
||||
if (!data) {
|
||||
return Promise.reject('Image not found');
|
||||
}
|
||||
return data?.binary;
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function delImgByRelatedId({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
import { connectionMongo, type Model } from '../../mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
||||
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
|
||||
import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
@@ -41,7 +41,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoImage: Model<MongoImageSchemaType> =
|
||||
models['image'] || model('image', ImageSchema);
|
||||
|
||||
MongoImage.syncIndexes();
|
||||
export const MongoImage = getMongoModel<MongoImageSchemaType>('image', ImageSchema);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { addLog } from '../../common/system/log';
|
||||
import mongoose, { Model } from 'mongoose';
|
||||
|
||||
export default mongoose;
|
||||
export * from 'mongoose';
|
||||
@@ -11,4 +12,68 @@ export const connectionMongo = (() => {
|
||||
return global.mongodb;
|
||||
})();
|
||||
|
||||
export const ReadPreference = mongoose.mongo.ReadPreference;
|
||||
const addCommonMiddleware = (schema: mongoose.Schema) => {
|
||||
const operations = [
|
||||
/^find/,
|
||||
'save',
|
||||
'create',
|
||||
/^update/,
|
||||
/^delete/,
|
||||
'aggregate',
|
||||
'count',
|
||||
'countDocuments',
|
||||
'estimatedDocumentCount',
|
||||
'distinct',
|
||||
'insertMany'
|
||||
];
|
||||
|
||||
operations.forEach((op: any) => {
|
||||
schema.pre(op, function (this: any, next) {
|
||||
this._startTime = Date.now();
|
||||
this._query = this.getQuery ? this.getQuery() : null;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
schema.post(op, function (this: any, result: any, next) {
|
||||
if (this._startTime) {
|
||||
const duration = Date.now() - this._startTime;
|
||||
|
||||
const warnLogData = {
|
||||
query: this._query,
|
||||
op,
|
||||
duration
|
||||
};
|
||||
|
||||
if (duration > 1000) {
|
||||
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
|
||||
} else if (duration > 3000) {
|
||||
addLog.error(`Slow operation ${duration}ms`, warnLogData);
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
|
||||
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
|
||||
console.log('Load model======', name);
|
||||
addCommonMiddleware(schema);
|
||||
|
||||
const model = connectionMongo.model<T>(name, schema);
|
||||
|
||||
if (process.env.SYNC_INDEX !== '0') {
|
||||
try {
|
||||
model.syncIndexes({ background: true });
|
||||
} catch (error) {
|
||||
addLog.error('Create index error', error);
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
};
|
||||
|
||||
export const ReadPreference = connectionMongo.mongo.ReadPreference;
|
||||
|
||||
@@ -7,19 +7,12 @@ const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20));
|
||||
/**
|
||||
* connect MongoDB and init data
|
||||
*/
|
||||
export async function connectMongo({
|
||||
beforeHook,
|
||||
afterHook
|
||||
}: {
|
||||
beforeHook?: () => any;
|
||||
afterHook?: () => Promise<any>;
|
||||
}): Promise<Mongoose> {
|
||||
export async function connectMongo(): Promise<Mongoose> {
|
||||
/* Connecting, connected will return */
|
||||
if (connectionMongo.connection.readyState !== 0) {
|
||||
return connectionMongo;
|
||||
}
|
||||
|
||||
beforeHook && beforeHook();
|
||||
|
||||
console.log('mongo start connect');
|
||||
try {
|
||||
connectionMongo.set('strictQuery', true);
|
||||
@@ -55,11 +48,5 @@ export async function connectMongo({
|
||||
addLog.error('mongo connect error', error);
|
||||
}
|
||||
|
||||
try {
|
||||
afterHook && (await afterHook());
|
||||
} catch (error) {
|
||||
addLog.error('mongo connect after hook error', error);
|
||||
}
|
||||
|
||||
return connectionMongo;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SystemConfigsType } from '@fastgpt/global/common/system/config/type';
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
|
||||
import { SystemConfigsTypeMap } from '@fastgpt/global/common/system/config/constants';
|
||||
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
@@ -27,6 +27,7 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoSystemConfigs: Model<SystemConfigsType> =
|
||||
models[collectionName] || model(collectionName, systemConfigSchema);
|
||||
MongoSystemConfigs.syncIndexes();
|
||||
export const MongoSystemConfigs = getMongoModel<SystemConfigsType>(
|
||||
collectionName,
|
||||
systemConfigSchema
|
||||
);
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import dayjs from 'dayjs';
|
||||
import chalk from 'chalk';
|
||||
import { isProduction } from './constants';
|
||||
import { LogLevelEnum } from './log/constant';
|
||||
import { connectionMongo } from '../mongo/index';
|
||||
import { getMongoLog } from './log/schema';
|
||||
|
||||
enum LogLevelEnum {
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warn = 2,
|
||||
error = 3
|
||||
}
|
||||
const logMap = {
|
||||
[LogLevelEnum.debug]: {
|
||||
levelLog: chalk.green('[Debug]')
|
||||
@@ -23,23 +19,26 @@ const logMap = {
|
||||
}
|
||||
};
|
||||
const envLogLevelMap: Record<string, number> = {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
warn: 2,
|
||||
error: 3
|
||||
debug: LogLevelEnum.debug,
|
||||
info: LogLevelEnum.info,
|
||||
warn: LogLevelEnum.warn,
|
||||
error: LogLevelEnum.error
|
||||
};
|
||||
|
||||
const logLevel = (() => {
|
||||
if (!isProduction) return LogLevelEnum.debug;
|
||||
const envLogLevel = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
|
||||
if (!envLogLevel || envLogLevelMap[envLogLevel] === undefined) return LogLevelEnum.info;
|
||||
return envLogLevelMap[envLogLevel];
|
||||
const { LOG_LEVEL, STORE_LOG_LEVEL } = (() => {
|
||||
const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
|
||||
const STORE_LOG_LEVEL = (process.env.STORE_LOG_LEVEL || '').toLocaleLowerCase();
|
||||
|
||||
return {
|
||||
LOG_LEVEL: envLogLevelMap[LOG_LEVEL] || LogLevelEnum.info,
|
||||
STORE_LOG_LEVEL: envLogLevelMap[STORE_LOG_LEVEL] ?? 99
|
||||
};
|
||||
})();
|
||||
|
||||
/* add logger */
|
||||
export const addLog = {
|
||||
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
|
||||
if (level < logLevel) return;
|
||||
if (level < LOG_LEVEL) return;
|
||||
|
||||
const stringifyObj = JSON.stringify(obj);
|
||||
const isEmpty = Object.keys(obj).length === 0;
|
||||
@@ -52,35 +51,15 @@ export const addLog = {
|
||||
|
||||
level === LogLevelEnum.error && console.error(obj);
|
||||
|
||||
const lokiUrl = process.env.LOKI_LOG_URL as string;
|
||||
if (!lokiUrl) return;
|
||||
|
||||
try {
|
||||
fetch(lokiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
streams: [
|
||||
{
|
||||
stream: {
|
||||
level
|
||||
},
|
||||
values: [
|
||||
[
|
||||
`${Date.now() * 1000000}`,
|
||||
JSON.stringify({
|
||||
message: msg,
|
||||
...obj
|
||||
})
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
// store
|
||||
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
|
||||
// store log
|
||||
getMongoLog().create({
|
||||
text: msg,
|
||||
level,
|
||||
metadata: obj
|
||||
});
|
||||
} catch (error) {}
|
||||
}
|
||||
},
|
||||
debug(msg: string, obj?: Record<string, any>) {
|
||||
this.log(LogLevelEnum.debug, msg, obj);
|
||||
|
||||
10
packages/service/common/system/log/constant.ts
Normal file
10
packages/service/common/system/log/constant.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export enum LogLevelEnum {
|
||||
debug = 0,
|
||||
info = 1,
|
||||
warn = 2,
|
||||
error = 3
|
||||
}
|
||||
|
||||
export enum LogSignEnum {
|
||||
slowOperation = 'slowOperation'
|
||||
}
|
||||
29
packages/service/common/system/log/schema.ts
Normal file
29
packages/service/common/system/log/schema.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { getMongoModel, Schema } from '../../../common/mongo';
|
||||
import { SystemLogType } from './type';
|
||||
import { LogLevelEnum } from './constant';
|
||||
|
||||
export const LogCollectionName = 'system_logs';
|
||||
|
||||
export const getMongoLog = () => {
|
||||
const SystemLogSchema = new Schema({
|
||||
text: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
level: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: Object.values(LogLevelEnum)
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
metadata: Object
|
||||
});
|
||||
|
||||
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
|
||||
SystemLogSchema.index({ level: 1 });
|
||||
|
||||
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
|
||||
};
|
||||
9
packages/service/common/system/log/type.d.ts
vendored
Normal file
9
packages/service/common/system/log/type.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LogLevelEnum, LogSignEnum } from './constant';
|
||||
|
||||
export type SystemLogType = {
|
||||
_id: string;
|
||||
text: string;
|
||||
level: LogLevelEnum;
|
||||
time: Date;
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
||||
import { timerIdMap } from './constants';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { TimerLockSchemaType } from './type.d';
|
||||
@@ -24,6 +24,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoTimerLock: Model<TimerLockSchemaType> =
|
||||
models[collectionName] || model(collectionName, TimerLockSchema);
|
||||
MongoTimerLock.syncIndexes();
|
||||
export const MongoTimerLock = getMongoModel<TimerLockSchemaType>(collectionName, TimerLockSchema);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { MongoApp } from '../schema';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { getCommunityPlugins } from '@fastgpt/plugins/register';
|
||||
import { getSystemPluginTemplates } from '../../../../plugins/register';
|
||||
|
||||
/*
|
||||
plugin id rule:
|
||||
@@ -28,7 +28,7 @@ export async function splitCombinePluginId(id: string) {
|
||||
};
|
||||
}
|
||||
|
||||
const [source, pluginId] = id.split('-') as [`${PluginSourceEnum}`, string];
|
||||
const [source, pluginId] = id.split('-') as [PluginSourceEnum, string];
|
||||
if (!source || !pluginId) return Promise.reject('pluginId not found');
|
||||
|
||||
return { source, pluginId: id };
|
||||
@@ -39,14 +39,6 @@ const getPluginTemplateById = async (
|
||||
): Promise<SystemPluginTemplateItemType & { teamId?: string }> => {
|
||||
const { source, pluginId } = await splitCombinePluginId(id);
|
||||
|
||||
if (source === PluginSourceEnum.community) {
|
||||
const item = [...global.communityPlugins, ...getCommunityPlugins()].find(
|
||||
(plugin) => plugin.id === pluginId
|
||||
);
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
|
||||
return cloneDeep(item);
|
||||
}
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
const item = await MongoApp.findById(id).lean();
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
@@ -68,8 +60,14 @@ const getPluginTemplateById = async (
|
||||
originCost: 0,
|
||||
currentCost: 0
|
||||
};
|
||||
} else {
|
||||
const item = [...global.communityPlugins, ...(await getSystemPluginTemplates())].find(
|
||||
(plugin) => plugin.id === pluginId
|
||||
);
|
||||
if (!item) return Promise.reject('plugin not found');
|
||||
|
||||
return cloneDeep(item);
|
||||
}
|
||||
return Promise.reject('plugin not found');
|
||||
};
|
||||
|
||||
/* format plugin modules to plugin preview module */
|
||||
@@ -98,10 +96,12 @@ export async function getPluginRuntimeById(id: string): Promise<PluginRuntimeTyp
|
||||
const plugin = await getPluginTemplateById(id);
|
||||
|
||||
return {
|
||||
id: plugin.id,
|
||||
teamId: plugin.teamId,
|
||||
name: plugin.name,
|
||||
avatar: plugin.avatar,
|
||||
showStatus: plugin.showStatus,
|
||||
currentCost: plugin.currentCost,
|
||||
nodes: plugin.workflow.nodes,
|
||||
edges: plugin.workflow.edges
|
||||
};
|
||||
|
||||
35
packages/service/core/app/plugin/systemPluginSchema.ts
Normal file
35
packages/service/core/app/plugin/systemPluginSchema.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo/index';
|
||||
const { Schema } = connectionMongo;
|
||||
import type { SystemPluginConfigSchemaType } from './type';
|
||||
|
||||
export const collectionName = 'app_system_plugins';
|
||||
|
||||
const SystemPluginSchema = new Schema({
|
||||
pluginId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
inputConfig: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
originCost: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
currentCost: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
});
|
||||
|
||||
SystemPluginSchema.index({ pluginId: 1 });
|
||||
|
||||
export const MongoSystemPluginSchema = getMongoModel<SystemPluginConfigSchemaType>(
|
||||
collectionName,
|
||||
SystemPluginSchema
|
||||
);
|
||||
10
packages/service/core/app/plugin/type.d.ts
vendored
Normal file
10
packages/service/core/app/plugin/type.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
|
||||
export type SystemPluginConfigSchemaType = {
|
||||
pluginId: string;
|
||||
|
||||
originCost: number; // n points/one time
|
||||
currentCost: number;
|
||||
isActive: boolean;
|
||||
inputConfig: SystemPluginTemplateItemType['inputConfig'];
|
||||
};
|
||||
21
packages/service/core/app/plugin/utils.ts
Normal file
21
packages/service/core/app/plugin/utils.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { PluginRuntimeType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { splitCombinePluginId } from './controller';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
|
||||
/*
|
||||
1. Commercial plugin: n points per times
|
||||
2. Other plugin: sum of children points
|
||||
*/
|
||||
export const computedPluginUsage = async (
|
||||
plugin: PluginRuntimeType,
|
||||
childrenUsage: ChatNodeUsageType[]
|
||||
) => {
|
||||
const { source } = await splitCombinePluginId(plugin.id);
|
||||
|
||||
if (source === PluginSourceEnum.commercial) {
|
||||
return plugin.currentCost ?? 0;
|
||||
}
|
||||
|
||||
return childrenUsage.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { Schema, getMongoModel } from '../../common/mongo';
|
||||
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
@@ -21,6 +20,7 @@ export const chatConfigType = {
|
||||
chatInputGuide: Object
|
||||
};
|
||||
|
||||
// schema
|
||||
const AppSchema = new Schema({
|
||||
parentId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
@@ -58,6 +58,7 @@ const AppSchema = new Schema({
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
updateTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
@@ -112,15 +113,8 @@ const AppSchema = new Schema({
|
||||
...getPermissionSchema(AppDefaultPermissionVal)
|
||||
});
|
||||
|
||||
try {
|
||||
AppSchema.index({ updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
|
||||
|
||||
export const MongoApp: Model<AppType> =
|
||||
models[AppCollectionName] || model(AppCollectionName, AppSchema);
|
||||
|
||||
MongoApp.syncIndexes();
|
||||
export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
import { chatConfigType } from '../schema';
|
||||
@@ -34,7 +34,7 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoAppVersion: Model<AppVersionSchemaType> =
|
||||
models[AppVersionCollectionName] || model(AppVersionCollectionName, AppVersionSchema);
|
||||
|
||||
MongoAppVersion.syncIndexes();
|
||||
export const MongoAppVersion = getMongoModel<AppVersionSchemaType>(
|
||||
AppVersionCollectionName,
|
||||
AppVersionSchema
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { ChatItemSchema as ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { ChatRoleMap } from '@fastgpt/global/core/chat/constants';
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
import { AppCollectionName } from '../app/schema';
|
||||
import { userCollectionName } from '../../support/user/schema';
|
||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
|
||||
export const ChatItemCollectionName = 'chatitems';
|
||||
@@ -99,7 +98,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoChatItem: Model<ChatItemType> =
|
||||
models[ChatItemCollectionName] || model(ChatItemCollectionName, ChatItemSchema);
|
||||
|
||||
MongoChatItem.syncIndexes();
|
||||
export const MongoChatItem = getMongoModel<ChatItemType>(ChatItemCollectionName, ChatItemSchema);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
|
||||
@@ -98,6 +98,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoChat: Model<ChatType> =
|
||||
models[chatCollectionName] || model(chatCollectionName, ChatSchema);
|
||||
MongoChat.syncIndexes();
|
||||
export const MongoChat = getMongoModel<ChatType>(chatCollectionName, ChatSchema);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user