Compare commits

..

49 Commits

Author SHA1 Message Date
Archer
834d36981f fix: response modal (#2228) 2024-07-31 16:11:04 +08:00
Archer
2fdca621e6 perf: remove unuse code (#2227) 2024-07-31 15:43:58 +08:00
heheer
8c7f4a3a30 fix: add check for reset (#2226) 2024-07-31 15:42:59 +08:00
Leo Liu Junyi
41da52d6ed Fix Switch state inconsistency for Scheduled Trigger (#2221)
* 修复定时关闭时Switch显示不同步

* 优化写法
2024-07-31 12:07:31 +08:00
Archer
446d9cb1b9 fix: oneapi image (#2222) 2024-07-31 12:04:11 +08:00
Rick
48a5853550 fix: the helm release failed due to version handle (#2199)
Co-authored-by: rick <LinuxSuRen@users.noreply.github.com>
2024-07-31 10:19:42 +08:00
Archer
c129874d59 fix: dataset params default error (#2210) 2024-07-30 17:58:40 +08:00
papapatrick
cc6fa4207a style: 优化样式 (#2203) 2024-07-30 11:47:26 +08:00
papapatrick
e27abe1f6b perf: 运行详情弹窗优化 (#2192)
* perf: 运行详情弹窗优化

* style: 调整样式

* style: 弹窗样式优化&&应用切换圆角添加

* fix: 修复编译错误
2024-07-29 18:36:13 +08:00
Zhedong Cen
5cecef5836 Update README.md (#2197) 2024-07-29 17:28:50 +08:00
Archer
ee89d0d7a4 Update package.json 2024-07-29 16:00:20 +08:00
Archer
6eeb1b4da1 perf: dataset upload process render (#2191) 2024-07-29 15:58:59 +08:00
Carson Yang
b6c9bb4a65 docs: add subtitle translation example (#2195)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2024-07-29 15:09:12 +08:00
Archer
20aad33850 fix: plugin revert (#2190) 2024-07-29 11:01:12 +08:00
Archer
02b51317b4 feat: 4.8.8 doc (#2188) 2024-07-29 10:56:29 +08:00
Archer
ba933c7450 perf: markdown code css (#2187) 2024-07-29 10:01:14 +08:00
Archer
856d7ce48f fix: app type (#2185) 2024-07-29 09:47:47 +08:00
Archer
23f22cda18 Add mongo forward read. Fix markdown code block. (#2174)
* perf: read from mongo forward node

* fix: code block ui

* perf: markdown code css
2024-07-29 09:47:30 +08:00
Archer
0a9a7691b4 Update 488.md 2024-07-26 13:50:42 +08:00
Archer
8d25a1d3ec Add img extension (#2172)
* perf: mongo log

* perf: img read

* doc
2024-07-26 12:46:07 +08:00
heheer
2d1e53c3b5 fix: plugin input default value & validate (#2171)
* fix: plugin input default value & validate

* delete console
2024-07-26 12:41:18 +08:00
Finley Ge
71d0093768 fix: app get readable team plugin error (#2169)
In the former code. User who access the plugin should have Write
Permission which should be Read.
2024-07-26 11:43:46 +08:00
Archer
cd554f573e 4.8.8 test fix (#2149)
* perf: code comment

* feat: system plugin input guide

* perf: variable avatar

* feat: feishu webhook

* perf: select tool config tip

* perf: rename variable

* fix: per inherit error

* perf: docker-compose oneapi version and i18n

* perf: ui tip bug

* fix: ts

* perf: pg log

* perf: editor color

* perf: update init
2024-07-26 10:23:44 +08:00
heheer
2d016b7462 fix: editor focus & picker highlight & ui (#2166)
* fix ui

* fix editor focus & picker highlight
2024-07-26 00:03:07 +08:00
Finley Ge
65515e7952 dataset inheritance permission (#2151)
* refactor: dataset create and update api

* chore: defaultpermission & resume fe

* refactor: database auth

* fix(ts): add inheritPermission into default data types

* chore: adjust the code

* fix: list api type filter

* fix: query condition
2024-07-25 19:03:24 +08:00
heheer
5906daff9f fix: plugin run & setting quote variables (#2150)
* fix

* fix

* change variables & variablelabels show condition

* fix type

* fix
2024-07-25 18:01:43 +08:00
Finley Ge
55cefccad1 fix: enable http request (#2159) 2024-07-25 14:39:22 +08:00
Finley Ge
87dac54f1e fix: outlink manage can delete/update others (#2158)
* fix: outlink manage be able to delete/update others

* fix: remove enum validation for teamMemberSchema.
because the old data has the role property, which may cause unknown bug

* perf: change findAndRemove to deleteOne
2024-07-25 13:52:16 +08:00
Archer
45b8d7e8de 4.8.8 test fix (#2143)
* perf: transcriptions api

* perf: variable picker tip

* perf: variable picker tip

* perf: chat select app

* feat: router to app detail

* perf: variable avoid space

* perf: variable picker

* perf: doc2x icon and params

* perf: sandbox support countToken

* feat: sandbox support delay and countToken
2024-07-24 16:02:53 +08:00
Finley Ge
a478621730 refactor openapikey and outlink apis (#2134)
* refactor: OpenAPIKey refactor

* refactor: outlink api refactor

fix: list return wrong data

* chore: remove deprecated type definition

* chore: remove throw Error. instead of Promise.reject

* fix: auth openapikey's owner

* fix: manager could read all keys
2024-07-24 11:11:36 +08:00
heheer
a233ab9584 fix build (#2146) 2024-07-24 11:10:48 +08:00
Menghuan1918
8d2a192515 添加插件:Doc2X 图片/文档识别 (#2097)
* Init the requrest part

* Finish the img deal part

* For test only one plugin

* Finish img2text part

* Finish pdf ocr part

* Change vscode setting back

* Change `for` into` for await` type
2024-07-24 09:59:20 +08:00
heheer
dcaf972767 fix: variable dynamic refresh & space match (#2142)
* fix: variable dynamic refresh & space match

* fix variable picker filter
2024-07-23 22:02:36 +08:00
Zong
f9d43ac009 feat(textSplitter): typo (#2138) 2024-07-23 21:54:55 +08:00
papapatrick
abcf48d5ec perf: switch perf (#2131)
* feat:  新增对话框底部复制按钮

* fix: 对话框底部复制按钮定位问题

* perf: 优化应用切换逻辑

* perf: 删除不必要代码

* fix: 修复编译失败bug&&修复冲突错误

* feat:  新增对话框底部复制按钮

* fix: 对话框底部复制按钮定位问题

* feat: 过长引用折叠功能

* merge: 合并主仓库代码

* refactor:  删除不必要代码

* perf: 优化应用切换逻辑

* perf: 删除不必要代码

* fix: 修复编译失败bug&&修复冲突错误

* merge

* merge: 处理冲突

* refactor: 重构代码删除不必要代码

* perf: 删除不必要代码
2024-07-23 18:53:48 +08:00
heheer
bf5145e632 perf: variable label picker scroll & focus disappear (#2135)
* fix: variable label picker scroll & focus disappear

* fix

* fix filter

* revert
2024-07-23 18:03:53 +08:00
Finley Ge
f37cdabb15 purge old permission (#2118)
* chore: purge old permission
- remove useless role of teamMember
- Cleanup auth apis' Props and Return type Definitions

* chore: a better way of RequireAtLeastOne

Signed-off-by: Finley Ge <m13203533462@163.com>

---------

Signed-off-by: Finley Ge <m13203533462@163.com>
2024-07-23 14:55:54 +08:00
Archer
e99c91aaa6 Perf system plugin and worker (#2126)
* perf: worker pool

* perf: worker register

* perf: worker controller

* perf: system plugin worker

* perf: system plugin worker

* perf: worker

* perf: worker

* worker timeout

* perf: copy icon
2024-07-23 11:23:42 +08:00
heheer
a4787bce5c feat: get node variables in prompt editor (#2087)
* feat: get node variables in prompt editor

* fix

* fix build

* merge

* fix build

* delete default parent

* fix

* fix
2024-07-23 11:20:23 +08:00
jingyang
f24e41f5ec update: i18n common.json keys (#2124) 2024-07-22 20:31:27 +08:00
papapatrick
85de3c1d64 Chat-perf (#2117)
* feat:  新增对话框底部复制按钮

* fix: 对话框底部复制按钮定位问题

* feat: 过长引用折叠功能

* merge: 合并主仓库代码

* refactor: 删除不必要代码

* doc: 文档补充的部分修改成英文
2024-07-22 18:25:25 +08:00
Finley Ge
c6f682310c fix resource permission schema (#2116)
* fix: resource permission schema

* fix: ResorceType
2024-07-22 17:27:48 +08:00
jingyang
991398b8d2 scripts: add delete-unused-keys (#2115)
* scripts: add delete-unused-keys

* update script

* fix keys

* fix ci

* update keys

* add i18n

* update
2024-07-22 15:03:35 +08:00
Archer
f452554663 fix: markdown echarts (#2101)
* fix: markdown echarts

* perf: packages
2024-07-22 10:10:32 +08:00
jingyang
57ff38e16f feat: Optimize i18n keys and remove unused keys, supporting static files in I18nT (#2092)
* feat: packages web i18n

* delete file

* feat:i18n add i18nT

* handle keys

* delete unusedKeys

* fix build

* update dev.md
2024-07-19 16:28:07 +08:00
Archer
f7b55b501f Update 488.md 2024-07-19 14:23:11 +08:00
Archer
cf7145ab54 System plugin (#2091)
* System template (#2082)

* feat: system plugin (#2024)

* add plugin cost & change plugin avatar (#2030)

* add plugin cost & change plugin avatar

* add author

* feat: duckduckgo plugin

* duckduck search

* perf: templates select system plugin

* perf: system plugin avatar

* feat: duckduck plugins

* doc

* perf: plugin classify

* perf: icon avatar component

* perf: system template avatar

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>

* feat: system plugin search

* perf: plugin packages important

* perf: source avatar

* nextconfig

* perf: i18n

* perf: default model

* perf: system plugin author

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-07-19 14:15:01 +08:00
Carson Yang
1eedb9caba Update workflow to change baseurl for cn site (#2081)
* Docs: fix gitinfo

Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>

* Update workflow to change baseurl for cn site

Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>

---------

Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2024-07-18 12:15:03 +08:00
heheer
6390d64417 fix: delete plugin output node check filter (#2080) 2024-07-18 10:44:13 +08:00
423 changed files with 11887 additions and 4219 deletions

View File

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

View File

@@ -24,11 +24,6 @@ jobs:
export APP_VERSION=${{ steps.vars.outputs.tag }}
export HELM_VERSION=${{ steps.vars.outputs.tag }}
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
if [[ ! "$line" =~ ^v ]]
then
unset APP_VERSION
unset HELM_VERSION
fi
helm dependency update files/helm/fastgpt
helm package files/helm/fastgpt --version ${HELM_VERSION}-helm --app-version ${APP_VERSION} -d bin
helm push bin/fastgpt-${HELM_VERSION}-helm.tgz oci://${HELM_REPO}

View File

@@ -7,7 +7,7 @@ on:
workflow_dispatch:
jobs:
build-fastgpt-images:
preview-fastgpt-images:
runs-on: ubuntu-20.04
steps:
- name: Checkout

View File

@@ -3,4 +3,6 @@ dist
**/.DS_Store
node_modules
docSite/
*.md
*.md
cl100l_base.ts

46
.vscode/i18n-ally-custom-framework.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
# .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})['\"`]"
- "[^\\w\\d]i18nT\\(['\"`]({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: "([^:]+):"
# 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: false

View File

@@ -20,5 +20,8 @@
"i18n-ally.displayLanguage": "zh", // 显示语言
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key"
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

View File

@@ -106,7 +106,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。相关使用教程可查看:[Sealos 部署 FastGPT](https://doc.fastgpt.in/docs/development/sealos/)
* [快开始本地开发](https://doc.fastgpt.in/docs/development/intro/)
* [开始本地开发](https://doc.fastgpt.in/docs/development/intro/)
* [部署 FastGPT](https://doc.fastgpt.in/docs/development/sealos)
* [系统配置文件说明](https://doc.fastgpt.in/docs/development/configuration/)
* [多模型配置](https://doc.fastgpt.in/docs/development/one-api/)

BIN
bin/fastgpt-v1.0.0-helm.tgz Normal file

Binary file not shown.

72
dev.md
View File

@@ -23,6 +23,77 @@ pnpm dev
make dev name=app
```
Note: If the Node version is >= 20, you need to pass the `--no-node-snapshot` parameter to Node when running `pnpm i`
```sh
NODE_OPTIONS=--no-node-snapshot pnpm i
```
## I18N
### Install i18n-ally Plugin
1. Open the Extensions Marketplace in VSCode, search for and install the `i18n Ally` plugin.
### Code Optimization Examples
#### Fetch Specific Namespace Translations in `getServerSideProps`
```typescript
// pages/yourPage.tsx
export async function getServerSideProps(context: any) {
return {
props: {
currentTab: context?.query?.currentTab || TabEnum.info,
...(await serverSideTranslations(context.locale, ['publish', 'user']))
}
};
}
```
#### Use useTranslation Hook in Page
```typescript
// pages/yourPage.tsx
import { useTranslation } from 'next-i18next';
const YourComponent = () => {
const { t } = useTranslation();
return (
<Button
variant="outline"
size="sm"
mr={2}
onClick={() => setShowSelected(false)}
>
{t('common:close')}
</Button>
);
};
export default YourComponent;
```
#### Handle Static File Translations
```typescript
// utils/i18n.ts
import { i18nT } from '@fastgpt/web/i18n/utils';
const staticContent = {
id: 'simpleChat',
avatar: 'core/workflow/template/aiChat',
name: i18nT('app:template.simple_robot'),
};
export default staticContent;
```
### Standardize Translation Format
- Use the t(namespace:key) format to ensure consistent naming.
- Translation keys should use lowercase letters and underscores, e.g., common.close.
## Build
@@ -37,4 +108,3 @@ docker build -f ./projects/app/Dockerfile -t registry.cn-hangzhou.aliyuncs.com/f
# Make cmd: Build image with proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

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

View File

@@ -1,5 +1,5 @@
---
title: 'V4.8.7(进行中)'
title: 'V4.8.7'
description: 'FastGPT V4.8.7 更新说明'
icon: 'upgrade'
draft: false
@@ -13,8 +13,8 @@ weight: 817
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.7-alpha
- 商业版镜像 tag 修改成 v4.8.7-alpha
- fastgpt 镜像 tag 修改成 v4.8.7
- 商业版镜像 tag 修改成 v4.8.7
-------

View File

@@ -0,0 +1,51 @@
---
title: 'V4.8.8'
description: 'FastGPT V4.8.8 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 816
---
## 升级指南
### 1. 做好数据库备份
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.8
- 商业版镜像 tag 修改成 v4.8.8
### 3. 执行初始化
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv488' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
会初始化知识库的继承权限
-------
## V4.8.8 更新说明
[点击查看完整更新](https://github.com/labring/FastGPT/releases/tag/v4.8.8)
1. 新增 - 重构系统插件的结构。允许向开源社区 PR 系统插件,具体可见: [如何向 FastGPT 社区提交系统插件](https://fael3z0zfze.feishu.cn/wiki/ERZnw9R26iRRG0kXZRec6WL9nwh)。
2. 新增 - DuckDuckGo 系统插件。
3. 新增 - 飞书 webhook 系统插件。
4. 新增 - 修改变量填写方式。提示词输入框以以及工作流中所有 Textarea 输入框,支持输入 / 唤起变量选择,可直接选择所有上游输出值,无需动态引入。
5. 商业版新增 - 知识库权限继承。
6. 优化 - 移动端快速切换应用交互。
7. 优化 - 节点图标。
8. 优化 - 对话框引用增加额外复制案件,便于复制。增加引用内容折叠。
9. 优化 - OpenAI sdk 升级,并自定义了 whisper 模型接口(未仔细查看 sdk 实现,但 sdk 中 whisper 接口,似乎无法适配一般 fastapi 接口)
10. 修复 - Permission 表声明问题。
11. 修复 - 并行执行节点,运行时间未正确记录。
12. 修复 - 运行详情未正确展示嵌套节点信息。
13. 修复 - 简易模式,首次进入,无法正确获取知识库配置。
14. 修复 - Log debug level 配置无效。
15. 修复 - 插件独立运行时,会将插件输入的值进行变量替换,可能导致后续节点变量异常。

View File

@@ -4,7 +4,7 @@ description: '使用 HTTP 模块绘制图片'
icon: 'image'
draft: false
toc: true
weight: 404
weight: 408
---
| | |

View File

@@ -4,7 +4,7 @@ description: '利用工具调用模块发送一个飞书webhook通知'
icon: 'image'
draft: false
toc: true
weight: 404
weight: 409
---
该文章展示如何发送一个简单的飞书webhook通知以此类推发送其他类型的通知也可以这么操作。

View File

@@ -4,7 +4,7 @@ description: '利用指定回复,创建固定的开头和结尾'
icon: 'healing'
draft: false
toc: true
weight: 401
weight: 405
---
![](/imgs/demo-fix-evidence1.jpg)

View File

@@ -4,7 +4,7 @@ description: '将 FastGPT 接入谷歌搜索'
icon: 'search'
draft: false
toc: true
weight: 402
weight: 406
---
| | |

View File

@@ -4,7 +4,7 @@ description: '展示高级编排操作数据库的能力'
icon: 'database'
draft: false
toc: true
weight: 403
weight: 407
---
| | |

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -121,8 +121,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.8 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.8 # 阿里云
ports:
- 3000:3000
networks:
@@ -179,7 +179,7 @@ services:
- ./mysql:/var/lib/mysql
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
image: ghcr.io/songquanpeng/one-api:v0.6.7
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports:
- 3001:3000

View File

@@ -79,8 +79,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.8 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.8 # 阿里云
ports:
- 3000:3000
networks:
@@ -136,7 +136,7 @@ services:
- ./mysql:/var/lib/mysql
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
image: ghcr.io/songquanpeng/one-api:v0.6.7
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports:
- 3001:3000

View File

@@ -60,8 +60,8 @@ services:
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.6 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.6 # 阿里云
image: ghcr.io/labring/fastgpt:v4.8.8 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.8 # 阿里云
ports:
- 3000:3000
networks:
@@ -117,7 +117,7 @@ services:
- ./mysql:/var/lib/mysql
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
image: ghcr.io/songquanpeng/one-api:v0.6.7
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/one-api:v0.6.6 # 阿里云
ports:
- 3001:3000

View File

@@ -9,7 +9,8 @@
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
"postinstall": "sh ./scripts/postinstall.sh",
"initIcon": "node ./scripts/icon/init.js",
"previewIcon": "node ./scripts/icon/index.js"
"previewIcon": "node ./scripts/icon/index.js",
"checkI18n": "node ./scripts/i18n/delete-unused-keys.js"
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",

View File

@@ -8,8 +8,8 @@ export enum DatasetErrEnum {
unAuthDatasetCollection = 'unAuthDatasetCollection',
unAuthDatasetData = 'unAuthDatasetData',
unAuthDatasetFile = 'unAuthDatasetFile',
unLinkCollection = 'unLinkCollection'
unLinkCollection = 'unLinkCollection',
invalidVectorModelOrQAModel = 'invalidVectorModelOrQAModel'
}
const datasetErr = [
{
@@ -39,6 +39,10 @@ const datasetErr = [
{
statusText: DatasetErrEnum.unLinkCollection,
message: 'core.dataset.error.unLinkCollection'
},
{
statusText: DatasetErrEnum.invalidVectorModelOrQAModel,
message: 'core.dataset.error.invalidVectorModelOrQAModel'
}
];
export default datasetErr.reduce((acc, cur, index) => {

View File

@@ -3,7 +3,8 @@ import { ErrType } from '../errorCode';
/* dataset: 506000 */
export enum OpenApiErrEnum {
unExist = 'openapiUnExist',
unAuth = 'openapiUnAuth'
unAuth = 'openapiUnAuth',
exceedLimit = 'openapiExceedLimit'
}
const errList = [
{
@@ -13,6 +14,10 @@ const errList = [
{
statusText: OpenApiErrEnum.unAuth,
message: '无权操作该 Api Key'
},
{
statusText: OpenApiErrEnum.exceedLimit,
message: '最多 10 组 API 密钥'
}
];
export default errList.reduce((acc, cur, index) => {

View File

@@ -144,7 +144,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
];
}
const isCustomSteep = checkIsCustomStep(step);
const isCustomStep = checkIsCustomStep(step);
const isMarkdownSplit = checkIsMarkdownSplit(step);
const independentChunk = checkIndependentChunk(step);
@@ -154,7 +154,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
.replace(
reg,
(() => {
if (isCustomSteep) return splitMarker;
if (isCustomStep) return splitMarker;
if (independentChunk) return `${splitMarker}$1`;
return `$1${splitMarker}`;
})()

View File

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

View File

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

View File

@@ -6,27 +6,29 @@ import { getAppChatConfig } from '../workflow/utils';
import { StoreNodeItemType } from '../workflow/type/node';
import { DatasetSearchModeEnum } from '../dataset/constants';
export const getDefaultAppForm = (): AppSimpleEditFormType => ({
aiSettings: {
model: 'gpt-3.5-turbo',
systemPrompt: '',
temperature: 0,
isResponseAnswerText: true,
maxHistories: 6,
maxToken: 4000
},
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
datasetSearchUsingExtensionQuery: false,
datasetSearchExtensionBg: ''
},
selectedTools: [],
chatConfig: {}
});
export const getDefaultAppForm = (): AppSimpleEditFormType => {
return {
aiSettings: {
model: 'gpt-4o-mini',
systemPrompt: '',
temperature: 0,
isResponseAnswerText: true,
maxHistories: 6,
maxToken: 4000
},
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
datasetSearchUsingExtensionQuery: true,
datasetSearchExtensionBg: ''
},
selectedTools: [],
chatConfig: {}
};
};
/* format app nodes to edit form */
export const appWorkflow2Form = ({

View File

@@ -10,7 +10,6 @@ export type DatasetUpdateBody = {
name?: string;
avatar?: string;
intro?: string;
permission?: DatasetSchemaType['permission']; // TODO: Should be deleted.
agentModel?: LLMModelItemType;
status?: DatasetSchemaType['status'];

View File

@@ -8,22 +8,22 @@ export enum DatasetTypeEnum {
export const DatasetTypeMap = {
[DatasetTypeEnum.folder]: {
icon: 'common/folderFill',
label: 'Folder Dataset',
label: 'folder_dataset',
collectionLabel: 'common.Folder'
},
[DatasetTypeEnum.dataset]: {
icon: 'core/dataset/commonDataset',
label: 'Common Dataset',
label: 'common_dataset',
collectionLabel: 'common.File'
},
[DatasetTypeEnum.websiteDataset]: {
icon: 'core/dataset/websiteDataset',
label: 'Website Dataset',
label: 'website_dataset',
collectionLabel: 'common.Website'
},
[DatasetTypeEnum.externalFile]: {
icon: 'core/dataset/externalDataset',
label: 'External File',
label: 'external_file',
collectionLabel: 'common.File'
}
};

View File

@@ -1,4 +1,4 @@
import { PermissionValueType } from 'support/permission/type';
import { PermissionSchemaType } from '../../support/permission/type';
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
import { PermissionTypeEnum } from '../../support/permission/constant';
import { PushDatasetDataChunkProps } from './api';
@@ -12,31 +12,28 @@ import {
import { DatasetPermission } from '../../support/permission/dataset/controller';
import { Permission } from '../../support/permission/controller';
/* schema */
export type DatasetSchemaType = {
_id: string;
parentId: string;
parentId?: string;
userId: string;
teamId: string;
tmbId: string;
updateTime: Date;
avatar: string;
name: string;
vectorModel: string;
agentModel: string;
intro: string;
type: DatasetTypeEnum;
type: `${DatasetTypeEnum}`;
status: `${DatasetStatusEnum}`;
// permission: DatasetPermission;
// metadata
websiteConfig?: {
url: string;
selector: string;
};
externalReadUrl?: string;
defaultPermission: PermissionValueType;
};
} & PermissionSchemaType;
// } & PermissionSchemaType;
export type DatasetCollectionSchemaType = {
_id: string;
@@ -133,15 +130,13 @@ export type DatasetSimpleItemType = {
};
export type DatasetListItemType = {
_id: string;
parentId: string;
avatar: string;
name: string;
intro: string;
type: DatasetTypeEnum;
type: `${DatasetTypeEnum}`;
permission: DatasetPermission;
vectorModel: VectorModelItemType;
defaultPermission: PermissionValueType;
};
} & PermissionSchemaType;
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
vectorModel: VectorModelItemType;

View File

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

View File

@@ -4,6 +4,10 @@ export enum FlowNodeTemplateTypeEnum {
function = 'function',
tools = 'tools',
search = 'search',
multimodal = 'multimodal',
communication = 'communication',
other = 'other',
teamApp = 'teamApp'
}

View File

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

View File

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

View File

@@ -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:
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ export const AiQueryExtension: FlowNodeTemplateType = {
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 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。',

View File

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

View File

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

View File

@@ -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针对工具调用结果回答问题。',

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ export const ToolModule: FlowNodeTemplateType = {
templateType: FlowNodeTemplateTypeEnum.ai,
sourceHandle: getHandleConfig(true, true, false, true),
targetHandle: getHandleConfig(true, true, false, true),
avatar: '/imgs/workflow/tool.svg',
avatar: 'core/workflow/template/toolCall',
name: '工具调用',
intro: '通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。',
showStatus: true,

View File

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

View File

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

View File

@@ -35,6 +35,7 @@ export type WorkflowTemplateType = {
avatar: string;
intro?: string;
author?: string;
inputExplanationUrl?: string;
version: string;
showStatus?: boolean;
@@ -42,17 +43,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;
};

View File

@@ -31,6 +31,7 @@ export type FlowNodeCommonType = {
avatar?: string;
name: string;
intro?: string; // template list intro
inputExplanationUrl?: string;
showStatus?: boolean; // chatting response step status
version: string;
@@ -70,7 +71,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 +80,7 @@ export type NodeTemplateListItemType = {
isTool?: boolean;
author?: string;
unique?: boolean; // 唯一的
currentCost?: number; // 当前积分消耗
};
export type NodeTemplateListType = {

View File

@@ -3,9 +3,10 @@ import {
WorkflowIOValueTypeEnum,
NodeInputKeyEnum,
VariableInputEnum,
variableMap
variableMap,
VARIABLE_NODE_ID
} from './constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './type/io.d';
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d';
import { StoreNodeItemType } from './type/node';
import type {
VariableItemType,
@@ -23,6 +24,7 @@ import {
} from '../app/constants';
import { IfElseResultEnum } from './template/system/ifElse/constant';
import { RuntimeNodeItemType } from './runtime/type';
import { getReferenceVariableValue } from './runtime/utils';
export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
return `${nodeId}-${type}-${key}`;
@@ -226,3 +228,90 @@ export const updatePluginInputByVariables = (
: node
);
};
export const removePluginInputVariables = (
variables: Record<string, any>,
nodes: RuntimeNodeItemType[]
) => {
const pluginInputNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput);
if (!pluginInputNode) return variables;
return Object.keys(variables).reduce(
(acc, key) => {
if (!pluginInputNode.inputs.find((input) => input.key === key)) {
acc[key] = variables[key];
}
return acc;
},
{} as Record<string, any>
);
};
export function replaceVariableLabel({
text,
nodes,
variables,
runningNode
}: {
text: any;
nodes: RuntimeNodeItemType[];
variables: Record<string, string | number>;
runningNode: RuntimeNodeItemType;
}) {
if (typeof text !== 'string') return text;
const globalVariables = Object.keys(variables).map((key) => {
return {
nodeId: VARIABLE_NODE_ID,
id: key,
value: variables[key]
};
});
// Upstream node outputs
const nodeVariables = nodes
.map((node) => {
return node.outputs.map((output) => {
return {
nodeId: node.nodeId,
id: output.id,
value: output.value
};
});
})
.flat();
// Get runningNode inputs(Will be replaced with reference)
const customInputs = runningNode.inputs.flatMap((item) => {
if (Array.isArray(item.value)) {
return [
{
id: item.key,
value: getReferenceVariableValue({
value: item.value as ReferenceValueProps,
nodes,
variables
}),
nodeId: runningNode.nodeId
}
];
}
return [];
});
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
// Replace {{$xxx.xxx$}} to value
for (const key in allVariables) {
const val = allVariables[key];
const regex = new RegExp(`\\{\\{\\$(${val.nodeId}\\.${val.id})\\$\\}\\}`, 'g');
if (['string', 'number'].includes(typeof val.value)) {
text = text.replace(regex, String(val.value));
} else if (['object'].includes(typeof val.value)) {
text = text.replace(regex, JSON.stringify(val.value));
} else {
continue;
}
}
return text || '';
}

View File

@@ -11,7 +11,7 @@
"jschardet": "3.1.1",
"nanoid": "^4.0.1",
"next": "14.2.5",
"openai": "4.28.0",
"openai": "4.53.0",
"openapi-types": "^12.1.3",
"timezones-list": "^3.0.2"
},

View File

@@ -1,11 +1,13 @@
import { TeamMemberWithUserSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum } from './constant';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
// It is spired by the permission system in Linux.
// The lowest 3 bits present the permission of reading, writing and managing.
// The higher bits are advanced permissions or extended permissions, which could be customized.
export type PermissionValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`;
export type PermissionListType<T = {}> = Record<
T | PermissionKeyEnum,
{
@@ -16,16 +18,6 @@ export type PermissionListType<T = {}> = Record<
}
>;
export type AuthResponseType = {
teamId: string;
tmbId: string;
isOwner: boolean;
canWrite: boolean;
authType?: `${AuthUserTypeEnum}`;
appId?: string;
apikey?: string;
};
export type ResourcePermissionType = {
teamId: string;
tmbId: string;

View File

@@ -3,22 +3,13 @@ export const TeamMemberCollectionName = 'team_members';
export const TeamTagsCollectionName = 'team_tags';
export enum TeamMemberRoleEnum {
owner = 'owner',
admin = 'admin',
visitor = 'visitor'
owner = 'owner'
}
export const TeamMemberRoleMap = {
[TeamMemberRoleEnum.owner]: {
value: TeamMemberRoleEnum.owner,
label: 'user.team.role.Owner'
},
[TeamMemberRoleEnum.admin]: {
value: TeamMemberRoleEnum.admin,
label: 'user.team.role.Admin'
},
[TeamMemberRoleEnum.visitor]: {
value: TeamMemberRoleEnum.visitor,
label: 'user.team.role.Visitor'
}
};
@@ -28,6 +19,7 @@ export enum TeamMemberStatusEnum {
reject = 'reject',
leave = 'leave'
}
export const TeamMemberStatusMap = {
[TeamMemberStatusEnum.waiting]: {
label: 'user.team.member.waiting',
@@ -46,4 +38,5 @@ export const TeamMemberStatusMap = {
color: 'red.600'
}
};
export const notLeaveStatus = { $ne: TeamMemberStatusEnum.leave };

View File

@@ -2,11 +2,15 @@
"name": "@fastgpt/plugins",
"version": "1.0.0",
"dependencies": {
"duck-duck-scrape": "^2.2.5",
"lodash": "^4.17.21",
"axios": "^1.5.1",
"expr-eval": "^2.0.2"
},
"devDependencies": {
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*",
"@types/lodash": "^4.14.191",
"@types/node": "20.14.0"
}
}

View File

@@ -1,57 +1,143 @@
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';
import { WorkerNameEnum, runWorker } from '@fastgpt/service/worker/utils';
let list = ['getTime', 'fetchUrl', 'mathExprVal'];
// Run in main thread
const staticPluginList = [
'getTime',
'fetchUrl',
'Doc2X',
'Doc2X/URLPDF2text',
'Doc2X/URLImg2text',
'feishu'
];
// Run in worker thread (Have npm packages)
const packagePluginList = [
'mathExprVal',
'duckduckgo',
'duckduckgo/search',
'duckduckgo/searchImg',
'duckduckgo/searchNews',
'duckduckgo/searchVideo'
];
const list = [...staticPluginList, ...packagePluginList];
/* 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 loadCommunityModule = 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: staticPluginList.includes(name)
? await loadCommunityModule(name)
: (e: any) => {
return runWorker(WorkerNameEnum.systemPluginRun, {
pluginName: name,
data: e
});
}
};
} catch (error) {}
})
)
).filter(Boolean) as {
name: string;
cb: any;
}[];
return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
(acc, { name, cb }) => {
acc[name] = cb;
return acc;
},
{}
);
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);
}
};

View File

@@ -0,0 +1,24 @@
import { SystemPluginResponseType } from '../type';
import { parentPort } from 'worker_threads';
const loadModule = async (name: string): Promise<(e: any) => SystemPluginResponseType> => {
const module = await import(`../src/${name}/index`);
return module.default;
};
parentPort?.on('message', async ({ pluginName, data }: { pluginName: string; data: any }) => {
try {
const cb = await loadModule(pluginName);
parentPort?.postMessage({
type: 'success',
data: await cb(data)
});
} catch (error) {
parentPort?.postMessage({
type: 'error',
data: error
});
}
process.exit();
});

View File

@@ -0,0 +1,158 @@
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
type Props = {
apikey: string;
url: string;
img_correction: boolean;
formula: boolean;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
success: boolean;
}>;
const main = async ({ apikey, url, img_correction, formula }: Props): Response => {
// Check the apikey
if (!apikey) {
return {
result: `API key is required`,
success: false
};
}
let real_api_key = apikey;
if (!apikey.startsWith('sk-')) {
const response = await fetch('https://api.doc2x.noedgeai.com/api/token/refresh', {
method: 'POST',
headers: {
Authorization: `Bearer ${apikey}`
}
});
if (response.status !== 200) {
return {
result: `Get token failed: ${await response.text()}`,
success: false
};
}
const data = await response.json();
real_api_key = data.data.token;
}
//Get the image binary from the URL
const extension = url.split('.').pop()?.toLowerCase();
const name = url.split('/').pop()?.split('.').shift();
let mini = '';
switch (extension) {
case 'jpg':
case 'jpeg':
mini = 'image/jpeg';
break;
case 'png':
mini = 'image/png';
break;
default:
return {
result: `Not supported image format, only support jpg/jpeg/png`,
success: false
};
}
const response = await fetch(url);
if (!response.ok) {
return {
result: `Failed to fetch image from URL: ${url}`,
success: false
};
}
const blob = await response.blob();
const formData = new FormData();
formData.append('file', new Blob([blob], { type: mini }), name + '.' + extension);
formData.append('img_correction', img_correction ? '1' : '0');
formData.append('equation', formula ? '1' : '0');
let upload_url = 'https://api.doc2x.noedgeai.com/api/platform/async/img';
if (real_api_key.startsWith('sk-')) {
upload_url = 'https://api.doc2x.noedgeai.com/api/v1/async/img';
}
let uuid;
const uploadAttempts = [1, 2, 3];
for await (const attempt of uploadAttempts) {
const upload_response = await fetch(upload_url, {
method: 'POST',
headers: {
Authorization: `Bearer ${real_api_key}`
},
body: formData
});
if (!upload_response.ok) {
// Rate limit, wait for 10s and retry at most 3 times
if (upload_response.status === 429 && attempt < 3) {
await delay(10000);
continue;
}
return {
result: `Failed to upload image: ${await upload_response.text()}`,
success: false
};
}
const upload_data = await upload_response.json();
uuid = upload_data.data.uuid;
break;
}
// Get the result by uuid
let result_url = 'https://api.doc2x.noedgeai.com/api/platform/async/status?uuid=' + uuid;
if (real_api_key.startsWith('sk-')) {
result_url = 'https://api.doc2x.noedgeai.com/api/v1/async/status?uuid=' + uuid;
}
const maxAttempts = 100;
// Wait for the result, at most 100s
for await (const _ of Array(maxAttempts).keys()) {
const result_response = await fetch(result_url, {
headers: {
Authorization: `Bearer ${real_api_key}`
}
});
if (!result_response.ok) {
return {
result: `Failed to get result: ${await result_response.text()}`,
success: false
};
}
const result_data = await result_response.json();
if (['ready', 'processing'].includes(result_data.data.status)) {
await delay(1000);
} else if (result_data.data.status === 'pages limit exceeded') {
return {
result: 'Doc2X Pages limit exceeded',
success: false
};
} else if (result_data.data.status === 'success') {
let result = result_data.data.result.pages[0].md;
result = result.replace(/\\[\(\)]/g, '$').replace(/\\[\[\]]/g, '$$');
return {
result: result,
success: true
};
} else {
return {
result: `Failed to get result: ${await result_data.text()}`,
success: false
};
}
}
return {
result: 'Timeout waiting for result',
success: false
};
};
export default main;

View File

@@ -0,0 +1,421 @@
{
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X 图像(URL)识别",
"avatar": "plugins/doc2x",
"intro": "将传入的图片(URL)发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本",
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "自定义插件输入",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 388.243055058894,
"y": -75.09744210499466
},
"version": "481",
"inputs": [
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "apikey",
"label": "apikey",
"description": "Doc2X的验证密匙对于个人用户可以从Doc2X官网 - 个人信息 - 身份令牌获得",
"required": true,
"toolDescription": "",
"defaultValue": ""
},
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"description": "待处理图片的URL",
"required": true,
"toolDescription": "待处理图片的URL"
},
{
"renderTypeList": ["switch"],
"selectedTypeIndex": 0,
"valueType": "boolean",
"canEdit": true,
"key": "img_correction",
"label": "img_correction",
"description": "是否启用图形矫正功能",
"required": true,
"toolDescription": "",
"defaultValue": false
},
{
"renderTypeList": ["switch"],
"selectedTypeIndex": 0,
"valueType": "boolean",
"canEdit": true,
"key": "formula",
"label": "formula",
"description": "是否开启纯公式识别(仅适用于图片内容仅有公式时)",
"required": true,
"toolDescription": "",
"defaultValue": false
}
],
"outputs": [
{
"id": "apikey",
"valueType": "string",
"key": "apikey",
"label": "apikey",
"type": "hidden"
},
{
"id": "url",
"valueType": "string",
"key": "url",
"label": "url",
"type": "hidden"
},
{
"id": "img_correction",
"valueType": "boolean",
"key": "img_correction",
"label": "img_correction",
"type": "hidden"
},
{
"id": "formula",
"valueType": "boolean",
"key": "formula",
"label": "formula",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "自定义插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1654.8021754314786,
"y": -22.243376051504086
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "result",
"label": "result",
"description": "处理结果(或者是报错信息)",
"value": ["zHG5jJBkXmjB", "xWQuEf50F3mr"]
},
{
"renderTypeList": ["reference"],
"valueType": "boolean",
"canEdit": true,
"key": "success",
"label": "success",
"description": "是否处理成功",
"value": ["zHG5jJBkXmjB", "m6CJJj7GFud5"]
}
],
"outputs": []
},
{
"nodeId": "zHG5jJBkXmjB",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1081.967607938733,
"y": -426.08028677656125
},
"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": "Doc2X/URLImg2text"
},
{
"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 \"apikey\": \"{{apikey}}\",\n \"url\": \"{{url}}\",\n \"img_correction\": \"{{img_correction}}\",\n \"formula\": \"{{img_correction}}\"\n}",
"label": "",
"required": false
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "apikey",
"label": "apikey",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "apikey"]
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "url"]
},
{
"renderTypeList": ["reference"],
"valueType": "boolean",
"canEdit": true,
"key": "img_correction",
"label": "img_correction",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "img_correction"]
},
{
"renderTypeList": ["reference"],
"valueType": "boolean",
"canEdit": true,
"key": "formula",
"label": "formula",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "formula"]
}
],
"outputs": [
{
"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": "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": "xWQuEf50F3mr",
"valueType": "string",
"type": "dynamic",
"key": "result",
"label": "result"
},
{
"id": "m6CJJj7GFud5",
"valueType": "boolean",
"type": "dynamic",
"key": "success",
"label": "success"
}
]
}
],
"edges": [
{
"source": "pluginInput",
"target": "zHG5jJBkXmjB",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "zHG5jJBkXmjB-target-left"
},
{
"source": "zHG5jJBkXmjB",
"target": "pluginOutput",
"sourceHandle": "zHG5jJBkXmjB-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}

View File

@@ -0,0 +1,130 @@
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
type Props = {
apikey: string;
url: string;
ocr: boolean;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
success: boolean;
}>;
const main = async ({ apikey, url, ocr }: Props): Response => {
// Check the apikey
if (!apikey) {
return {
result: `API key is required`,
success: false
};
}
let real_api_key = apikey;
if (!apikey.startsWith('sk-')) {
const response = await fetch('https://api.doc2x.noedgeai.com/api/token/refresh', {
method: 'POST',
headers: {
Authorization: `Bearer ${apikey}`
}
});
if (response.status !== 200) {
return {
result: `Get token failed: ${await response.text()}`,
success: false
};
}
const data = await response.json();
real_api_key = data.data.token;
}
//Get the image binary from the URL
const formData = new FormData();
formData.append('pdf_url', url);
formData.append('ocr', ocr ? '1' : '0');
let upload_url = 'https://api.doc2x.noedgeai.com/api/platform/async/pdf';
if (real_api_key.startsWith('sk-')) {
upload_url = 'https://api.doc2x.noedgeai.com/api/v1/async/pdf';
}
let uuid;
const uploadAttempts = [1, 2, 3];
for await (const attempt of uploadAttempts) {
const upload_response = await fetch(upload_url, {
method: 'POST',
headers: {
Authorization: `Bearer ${real_api_key}`
},
body: formData
});
if (!upload_response.ok) {
if (upload_response.status === 429 && attempt < 3) {
await delay(10000);
continue;
}
return {
result: `Failed to upload file: ${await upload_response.text()}`,
success: false
};
}
const upload_data = await upload_response.json();
uuid = upload_data.data.uuid;
break;
}
// Get the result by uuid
let result_url = 'https://api.doc2x.noedgeai.com/api/platform/async/status?uuid=' + uuid;
if (real_api_key.startsWith('sk-')) {
result_url = 'https://api.doc2x.noedgeai.com/api/v1/async/status?uuid=' + uuid;
}
let result = '';
// Wait for the result, at most 100s
const maxAttempts = 100;
for await (const _ of Array(maxAttempts).keys()) {
const result_response = await fetch(result_url, {
headers: {
Authorization: `Bearer ${real_api_key}`
}
});
if (!result_response.ok) {
return {
result: `Failed to get result: ${await result_response.text()}`,
success: false
};
}
const result_data = await result_response.json();
if (['ready', 'processing'].includes(result_data.data.status)) {
await delay(1000);
} else if (result_data.data.status === 'pages limit exceeded') {
return {
result: 'Doc2X Pages limit exceeded',
success: false
};
} else if (result_data.data.status === 'success') {
result = await Promise.all(
result_data.data.result.pages.map((page: { md: any }) => page.md)
).then((pages) => pages.join('\n'));
result = result.replace(/\\[\(\)]/g, '$').replace(/\\[\[\]]/g, '$$');
return {
result: result,
success: true
};
} else {
return {
result: `Failed to get result: ${await result_data.text()}`,
success: false
};
}
}
return {
result: 'Timeout waiting for result',
success: false
};
};
export default main;

View File

@@ -0,0 +1,373 @@
{
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X PDF文件(URL)识别",
"avatar": "plugins/doc2x",
"intro": "将传入的PDF文件(URL)发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本",
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "自定义插件输入",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 388.243055058894,
"y": -75.09744210499466
},
"version": "481",
"inputs": [
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "apikey",
"label": "apikey",
"description": "Doc2X的验证密匙对于个人用户可以从Doc2X官网 - 个人信息 - 身份令牌获得",
"required": true,
"toolDescription": "",
"defaultValue": ""
},
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"description": "待处理PDF文件的URL",
"required": true,
"toolDescription": "待处理PDF文件的URL"
},
{
"renderTypeList": ["switch"],
"selectedTypeIndex": 0,
"valueType": "boolean",
"canEdit": true,
"key": "ocr",
"label": "ocr",
"description": "是否开启对PDF文件内图片的OCR识别建议开启",
"required": true,
"toolDescription": "",
"defaultValue": true
}
],
"outputs": [
{
"id": "apikey",
"valueType": "string",
"key": "apikey",
"label": "apikey",
"type": "hidden"
},
{
"id": "url",
"valueType": "string",
"key": "url",
"label": "url",
"type": "hidden"
},
{
"id": "formula",
"valueType": "boolean",
"key": "ocr",
"label": "ocr",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "自定义插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1654.8021754314786,
"y": -22.243376051504086
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "result",
"label": "result",
"description": "处理结果(或者是报错信息)",
"value": ["zHG5jJBkXmjB", "xWQuEf50F3mr"]
},
{
"renderTypeList": ["reference"],
"valueType": "boolean",
"canEdit": true,
"key": "success",
"label": "success",
"description": "是否处理成功",
"value": ["zHG5jJBkXmjB", "m6CJJj7GFud5"]
}
],
"outputs": []
},
{
"nodeId": "zHG5jJBkXmjB",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1081.967607938733,
"y": -426.08028677656125
},
"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": "Doc2X/URLPDF2text"
},
{
"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 \"apikey\": \"{{apikey}}\",\n \"url\": \"{{url}}\",\n \"ocr\": \"{{ocr}}\"\n}",
"label": "",
"required": false
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "apikey",
"label": "apikey",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "apikey"]
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "url"]
},
{
"renderTypeList": ["reference"],
"valueType": "boolean",
"canEdit": true,
"key": "ocr",
"label": "ocr",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "formula"]
}
],
"outputs": [
{
"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": "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": "xWQuEf50F3mr",
"valueType": "string",
"type": "dynamic",
"key": "result",
"label": "result"
},
{
"id": "m6CJJj7GFud5",
"valueType": "boolean",
"type": "dynamic",
"key": "success",
"label": "success"
}
]
}
],
"edges": [
{
"source": "pluginInput",
"target": "zHG5jJBkXmjB",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "zHG5jJBkXmjB-target-left"
},
{
"source": "zHG5jJBkXmjB",
"target": "pluginOutput",
"sourceHandle": "zHG5jJBkXmjB-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}

View File

@@ -0,0 +1,17 @@
{
"author": "Menghuan1918",
"version": "488",
"name": "Doc2X服务",
"avatar": "plugins/doc2x",
"intro": "传入的URL形式的图片或PDF文件发送至Doc2X进行解析返回带LaTeX公式的markdown格式的文本。",
"showStatus": true,
"weight": 10,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [],
"edges": []
}
}

View 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) {
console.log(error);
if (retry <= 0) {
addLog.warn('DuckDuckGo error', { error });
return {
result: 'Failed to fetch data'
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;

View 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"
}
]
}
}

View File

@@ -0,0 +1,45 @@
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) {
addLog.warn('DuckDuckGo error', { error });
return {
result: 'Failed to fetch data'
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;

View 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"
}
]
}
}

View File

@@ -0,0 +1,46 @@
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) {
addLog.warn('DuckDuckGo error', { error });
return {
result: 'Failed to fetch data'
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;

View 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"
}
]
}
}

View File

@@ -0,0 +1,46 @@
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) {
addLog.warn('DuckDuckGo error', { error });
return {
result: 'Failed to fetch data'
};
}
await delay(Math.random() * 5000);
return main(props, retry - 1);
}
};
export default main;

View 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"
}
]
}
}

View File

@@ -0,0 +1,17 @@
{
"author": "",
"version": "486",
"name": "DuckDuckGo服务",
"avatar": "core/workflow/template/duckduckgo",
"intro": "DuckDuckGo 服务,包含网络搜索、图片搜索、新闻搜索等。",
"showStatus": false,
"weight": 100,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [],
"edges": []
}
}

View File

@@ -0,0 +1,443 @@
{
"author": "",
"version": "488",
"name": "飞书机器人 webhook",
"avatar": "/imgs/app/templates/feishu.svg",
"intro": "向飞书机器人发起 webhook 请求。",
"inputExplanationUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
"showStatus": false,
"weight": 10,
"isTool": true,
"templateType": "communication",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "自定义插件输入",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 156.37657136084977,
"y": 90.73380846709256
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "content",
"label": "content",
"description": "需要发送的消息",
"required": true,
"toolDescription": "需要发送的消息"
},
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "hook_url",
"label": "hook_url",
"description": "飞书机器人地址",
"required": true,
"defaultValue": ""
}
],
"outputs": [
{
"id": "query",
"valueType": "string",
"key": "content",
"label": "content",
"type": "hidden"
},
{
"id": "hook_url",
"valueType": "string",
"key": "hook_url",
"label": "hook_url",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "自定义插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 2110.7223589692912,
"y": 120.17602722162474
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "result",
"label": "result",
"description": "",
"value": ["vzreK6vHrPvZ", "httpRawResponse"]
}
],
"outputs": []
},
{
"nodeId": "vzreK6vHrPvZ",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1363.4233257919495,
"y": -182.3490463845037
},
"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": "{{url}}"
},
{
"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": "{{content}}",
"label": "",
"required": false
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "url",
"label": "url",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "hook_url"]
},
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "content",
"label": "content",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["qcJpBBVtXsGd", "system_rawResponse"]
}
],
"outputs": [
{
"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": "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
}
}
]
},
{
"nodeId": "qcJpBBVtXsGd",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 805.8169457909617,
"y": -159.52218926716316
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "这些变量会作为代码的运行的输入参数",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js"
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({data1}){\n try{\n const parseData = JSON.parse(data1)\n if(typeof parseData === 'object') {\n return parseData\n }\n return {\n \"msg_type\": \"text\",\n content: {\n \"text\": data1\n }\n }\n } catch(err) {\n return {\n \"msg_type\": \"text\",\n content: {\n \"text\": data1\n }\n }\n }\n}"
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "data1",
"label": "data1",
"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_rawResponse",
"key": "system_rawResponse",
"label": "完整响应数据",
"valueType": "object",
"type": "static"
},
{
"id": "error",
"key": "error",
"label": "运行错误",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key"
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "content",
"valueType": "object",
"label": "content"
}
]
}
],
"edges": [
{
"source": "vzreK6vHrPvZ",
"target": "pluginOutput",
"sourceHandle": "vzreK6vHrPvZ-source-right",
"targetHandle": "pluginOutput-target-left"
},
{
"source": "pluginInput",
"target": "qcJpBBVtXsGd",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "qcJpBBVtXsGd-target-left"
},
{
"source": "qcJpBBVtXsGd",
"target": "vzreK6vHrPvZ",
"sourceHandle": "qcJpBBVtXsGd-source-right",
"targetHandle": "vzreK6vHrPvZ-target-left"
}
]
}
}

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { Parser } from 'expr-eval';
type Props = {

View File

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

View File

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

View File

@@ -163,6 +163,7 @@ export const readFileContentFromMongo = async ({
const encoding = file?.metadata?.encoding || detectFileEncoding(fileBuffers);
// Get raw text
const { rawText } = await readRawContentByFileBuffer({
extension,
isQAImport,

View File

@@ -2,9 +2,11 @@ import { UploadImgProps } from '@fastgpt/global/common/file/api';
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
import { MongoImage } from './schema';
import { ClientSession } from '../../../common/mongo';
import { guessBase64ImageType } from '../utils';
import { readFromSecondary } from '../../mongo/utils';
export function getMongoImgUrl(id: string) {
return `${imageBaseUrl}${id}`;
export function getMongoImgUrl(id: string, extension: string) {
return `${imageBaseUrl}${id}.${extension}`;
}
export const maxImgSize = 1024 * 1024 * 12;
@@ -23,9 +25,10 @@ export async function uploadMongoImg({
return Promise.reject('Image too large');
}
const [base64Mime, base64Data] = base64Img.split(',')
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'jpeg'}`
const [base64Mime, base64Data] = base64Img.split(',');
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
const binary = Buffer.from(base64Data, 'base64');
const extension = mime.split('/')[1];
const { _id } = await MongoImage.create({
type,
@@ -36,15 +39,23 @@ export async function uploadMongoImg({
shareId
});
return getMongoImgUrl(String(_id));
return getMongoImgUrl(String(_id), extension);
}
export async function readMongoImg({ id }: { id: string }) {
const data = await MongoImage.findById(id);
const formatId = id.replace(/\.[^/.]+$/, '');
const data = await MongoImage.findById(formatId, undefined, {
...readFromSecondary
});
if (!data) {
return Promise.reject('Image not found');
}
return data;
return {
binary: data.binary,
mime: data.metadata?.mime ?? guessBase64ImageType(data.binary.toString('base64'))
};
}
export async function delImgByRelatedId({

View File

@@ -6,9 +6,10 @@ import { addHours } from 'date-fns';
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
import fs from 'fs';
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
import { ReadFileResponse } from '../../../worker/file/type';
import type { ReadFileResponse } from '../../../worker/readFile/type';
export const initMarkdownText = ({
// match md img text and upload to db
export const matchMdImgTextAndUpload = ({
teamId,
md,
metadata
@@ -79,7 +80,7 @@ export const readRawContentByFileBuffer = async ({
// markdown data format
if (['md', 'html', 'docx'].includes(extension)) {
rawText = await initMarkdownText({
rawText = await matchMdImgTextAndUpload({
teamId: teamId,
md: rawText,
metadata: metadata

View File

@@ -47,8 +47,6 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
} else if (duration > 3000) {
addLog.error(`Slow operation ${duration}ms`, warnLogData);
}
}
next();

View File

@@ -1,3 +1,4 @@
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '../system/log';
import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose';
@@ -17,9 +18,11 @@ export async function connectMongo(): Promise<Mongoose> {
try {
connectionMongo.set('strictQuery', true);
connectionMongo.connection.on('error', (error) => {
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);
connectionMongo.disconnect();
await connectionMongo.disconnect();
await delay(1000);
connectMongo();
});
connectionMongo.connection.on('disconnected', () => {
console.log('mongo disconnected');
@@ -43,10 +46,11 @@ export async function connectMongo(): Promise<Mongoose> {
});
console.log('mongo connected');
return connectionMongo;
} catch (error) {
connectionMongo.disconnect();
addLog.error('mongo connect error', error);
await connectionMongo.disconnect();
await delay(1000);
return connectMongo();
}
return connectionMongo;
}

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