Compare commits

...

45 Commits

Author SHA1 Message Date
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
Archer
3d80eb288b lock (#2079) 2024-07-18 10:13:47 +08:00
Archer
5cb196535f 4.8.7 fix (#2076) 2024-07-17 19:06:37 +08:00
jingyang
982325d066 fix i18next.d.ts (#2064)
* fix i18next.d.ts

* feat: packages web i18n

* delete file
2024-07-17 15:27:51 +08:00
Archer
36f8755d09 lock (#2063)
* lock

* perf: init data

* perf: vision model url

* fix: chat index
2024-07-17 00:16:57 +08:00
jingyang
fc96bb99cc feat: optimize i18n implementation for better localization (#2062)
* feat: optimize i18n implementation for better localization

* delete i18n-ally-custom-framework.yml

* update common key
2024-07-16 17:56:27 +08:00
papapatrick
1e4ffc2481 docs: 完善本地开发教程 (#2061) 2024-07-16 17:38:11 +08:00
Archer
ee7496467b 4.8.7 test (#2058)
* fix: query extension id;App page change type invalid

* prettier
2024-07-16 16:55:12 +08:00
Archer
b5c98a4f63 Plugin runtime (#2050)
* feat: plugin run (#1950)

* feat: plugin run

* fix

* ui

* fix

* change user input type

* fix

* fix

* temp

* split out plugin chat

* perf: chatbox

* perf: chatbox

* fix: plugin runtime (#2032)

* fix: plugin runtime

* fix

* fix build

* fix build

* perf: chat send prompt

* perf: chat log ux

* perf: chatbox context and share page plugin runtime

* perf: plugin run time config

* fix: ts

* feat: doc

* perf: isPc check

* perf: variable input render

* feat: app search

* fix: response box height

* fix: phone ui

* perf: lock

* perf: plugin route

* fix: chat (#2049)

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-07-15 22:50:48 +08:00
Zong
090c880860 feat(image): metadata support image mime type (#2026) 2024-07-14 23:25:16 +08:00
Archer
dd2a9bdee5 Update 486.md 2024-07-11 14:43:34 +08:00
Archer
8d60ef505f Update 486.md 2024-07-11 14:41:48 +08:00
Archer
b14514c105 4.8.6 sandbox (#2020)
* fix: openapi doc

* update doc
2024-07-11 14:36:27 +08:00
Archer
11ffaaf2c5 4.8.6 sandbox (#2017)
* fix: openapi doc

* update doc
2024-07-11 10:34:59 +08:00
Archer
e2ae571d15 4.8.6 fix (#1970)
* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import

* feat: mongo log store

* doc

* fix: sandbox outputs

* perf: desc color

* fix: node init

* perf code

* perf: chat header
2024-07-10 15:52:39 +08:00
Zong
f548e24e7d fix(env): typo template (#1990) 2024-07-08 15:05:23 +08:00
584 changed files with 16054 additions and 10273 deletions

View File

@@ -16,6 +16,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 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 - name: Get current date and time
id: datetime id: datetime
run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" run: echo "datetime=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT"

View File

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

1
.npmrc
View File

@@ -1,2 +1,3 @@
public-hoist-pattern[]=*tiktoken* public-hoist-pattern[]=*tiktoken*
public-hoist-pattern[]=*@zilliz/milvus2-sdk-node* public-hoist-pattern[]=*@zilliz/milvus2-sdk-node*
registry=https://registry.npmjs.org/

View File

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

View File

@@ -26,13 +26,14 @@ usageMatchRegex:
- "[^\\w\\d]workflowT\\(['\"`]({key})['\"`]" - "[^\\w\\d]workflowT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]userT\\(['\"`]({key})['\"`]" - "[^\\w\\d]userT\\(['\"`]({key})['\"`]"
- "[^\\w\\d]chatT\\(['\"`]({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 # 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 # and works like how the i18next framework identifies the namespace scope from the
# useTranslation() hook. # useTranslation() hook.
# You should unescape RegEx strings in order to fit in the YAML file # 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 # To help with this, you can use https://www.freeformatter.com/json-escape.html
scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]" scopeRangeRegex: "([^:]+):"
# An array of strings containing refactor templates. # An array of strings containing refactor templates.
# The "$1" will be replaced by the keypath specified. # The "$1" will be replaced by the keypath specified.
@@ -41,6 +42,5 @@ scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
# refactorTemplates: # refactorTemplates:
# - i18n.get("$1") # - i18n.get("$1")
# If set to true, only enables this custom framework (will disable all built-in frameworks) # If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true monopoly: false

14
.vscode/settings.json vendored
View File

@@ -7,11 +7,21 @@
"i18n-ally.localesPaths": [ "i18n-ally.localesPaths": [
"packages/web/i18n", "packages/web/i18n",
], ],
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"], "i18n-ally.enabledParsers": [
"json",
"yaml",
"js",
"ts"
],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true, "i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": false, "i18n-ally.keepFulfilled": false,
"i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容 "i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容
"i18n-ally.displayLanguage": "zh", // 显示语言 "i18n-ally.displayLanguage": "zh", // 显示语言
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key" "i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
"i18n-ally.extract.targetPickingStrategy": "most-similar-by-key",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
} }

72
dev.md
View File

@@ -23,6 +23,77 @@ pnpm dev
make dev name=app 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 ## 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 cmd: Build image with proxy
make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.1 proxy=taobao
``` ```

View File

@@ -11,7 +11,6 @@ weight: 707
![](/imgs/sealos-fastgpt.webp) ![](/imgs/sealos-fastgpt.webp)
{{% alert icon="🤖" context="success" %}} {{% alert icon="🤖" context="success" %}}
- MongoDB用于存储除了向量外的各类数据 - MongoDB用于存储除了向量外的各类数据
@@ -105,13 +104,11 @@ brew install orbstack
{{< /tab >}} {{< /tab >}}
{{< /tabs >}} {{< /tabs >}}
## 开始部署 ## 开始部署
### 1. 下载 docker-compose.yml ### 1. 下载 docker-compose.yml
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`在这个文件夹中依据下载的配置文件运行docker若作为本地开发使用推荐`docker-compose-pgvector`版本,并且自行拉取并运行`sandbox``fastgpt`并在docker配置文件中注释掉`sandbox``fastgpt`的部分
非 Linux 环境或无法访问外网环境,可手动创建一个目录,并下载配置文件和对应版本的`docker-compose.yml`
- [config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json) - [config.json](https://github.com/labring/FastGPT/blob/main/projects/app/data/config.json)
- [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样) - [docker-compose.yml](https://github.com/labring/FastGPT/blob/main/files/docker) (注意,不同向量库版本的文件不一样)
@@ -271,7 +268,6 @@ rs.status()
默认是写了OneAPi的连接地址和密钥可以通过修改`docker-compose.yml`fastgpt容器的环境变量实现。 默认是写了OneAPi的连接地址和密钥可以通过修改`docker-compose.yml`fastgpt容器的环境变量实现。
`OPENAI_BASE_URL`API 接口的地址,需要加/v1 `OPENAI_BASE_URL`API 接口的地址,需要加/v1
`CHAT_API_KEY`API 接口的凭证)。 `CHAT_API_KEY`API 接口的凭证)。
@@ -315,8 +311,7 @@ docker-compose up -d
1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。 1. `docker exec -it fastgpt sh` 进入 FastGPT 容器。
2. 直接输入`env`命令查看所有环境变量。 2. 直接输入`env`命令查看所有环境变量。
### 为什么无法连接`本地模型`镜像
### 为什么无法连接`本地模型`镜像。
`docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。 `docker-compose.yml`中使用了桥接的模式建立了`fastgpt`网络如想通过0.0.0.0或镜像名访问其它镜像,需将其它镜像也加入到网络中。
@@ -368,8 +363,8 @@ mongo连接失败查看mongo的运行状态**对应日志**。
由于服务初始化错误,系统重启导致。 由于服务初始化错误,系统重启导致。
* 90%是由于配置文件写不对,导致 JSON 解析报错 - 90%是由于配置文件写不对,导致 JSON 解析报错
* 剩下的基本是因为向量数据库连不上 - 剩下的基本是因为向量数据库连不上
### 如何修改密码 ### 如何修改密码

View File

@@ -7,8 +7,7 @@ toc: true
weight: 705 weight: 705
--- ---
本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in)。 本文档介绍了如何设置开发环境以构建和测试 [FastGPT](https://fastgpt.in)
## 前置依赖项 ## 前置依赖项
@@ -16,13 +15,14 @@ weight: 705
- [Git](http://git-scm.com/) - [Git](http://git-scm.com/)
- [Docker](https://www.docker.com/)(构建镜像) - [Docker](https://www.docker.com/)(构建镜像)
- [Node.js v18.17 / v20.x](http://nodejs.org) - [Node.js v18.17 / v20.x](http://nodejs.org)版本尽量一样可以使用nvm管理node版本
- [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境) - [pnpm](https://pnpm.io/) 版本 8.6.0 (目前官方的开发环境)
- make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3) - make命令: 根据不同平台,百度安装 (官方是GNU Make 4.3)
## 开始本地开发 ## 开始本地开发
{{% alert context="success" %}} {{% alert context="success" %}}
1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC+0 1. 用户默认的时区为 `Asia/Shanghai`,非 linux 环境时候,获取系统时间会异常,本地开发时候,可以将用户的时区调整成 UTC+0
2. 建议先服务器装好**数据库**,再进行本地开发。 2. 建议先服务器装好**数据库**,再进行本地开发。
{{% /alert %}} {{% /alert %}}
@@ -47,9 +47,10 @@ git clone git@github.com:<github_username>/FastGPT.git
### 3. 安装数据库 ### 3. 安装数据库
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。 第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践或者新建文件夹并配置相关文件用以运行docker。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
{{% alert context="warning" %}}
Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。 Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnection=true` 参数,才能连接上副本集的数据库。
{{% /alert %}}
### 4. 初始配置 ### 4. 初始配置
@@ -57,7 +58,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
**1. 环境变量** **1. 环境变量**
复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template 复制`.env.template`文件,在同级目录下生成一个`.env.local` 文件,修改`.env.local` 里内容才是有效的变量。变量说明见 .env.template,主要需要修改`API_KEY`和数据库的地址与端口以及数据库账号的用户名和密码具体配置需要和docker配置文件相同其中用户名和密码如需修改需要修改docker配置文件、数据库和`.env.local`文件,不能只改一处。
**2. config 配置文件** **2. config 配置文件**
@@ -73,7 +74,7 @@ Mongo 数据库需要注意,需要注意在连接地址中增加 `directConnec
### 5. 运行 ### 5. 运行
可参考项目根目录下的 `dev.md` 可参考项目根目录下的 `dev.md`,第一次编译运行可能会有点慢,需要点耐心哦
```bash ```bash
# 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容) # 给自动化脚本代码执行权限(非 linux 系统, 可以手动执行里面的 postinstall.sh 文件内容)
@@ -114,7 +115,6 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。 如果遇到问题,比如合并冲突或不知道如何打开拉取请求,请查看 GitHub 的[拉取请求教程](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests),了解如何解决合并冲突和其他问题。一旦您的 PR 被合并,您将自豪地被列为[贡献者表](https://github.com/labring/FastGPT/graphs/contributors)中的一员。
## QA ## QA
### 本地数据库无法连接 ### 本地数据库无法连接
@@ -130,6 +130,7 @@ make build name=app image=registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8
FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI``Type`。如果没有权限,可以先执行`chmod -R +x ./scripts/`,再执行`pnpm i` FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI``Type`。如果没有权限,可以先执行`chmod -R +x ./scripts/`,再执行`pnpm i`
仍不可行的话,可以手动执行`./scripts/postinstall.sh`里的内容。 仍不可行的话,可以手动执行`./scripts/postinstall.sh`里的内容。
*如果是Windows下的话可以使用git bash给`postinstall`脚本添加执行权限并执行sh脚本*
### TypeError: Cannot read properties of null (reading 'useMemo' ) ### TypeError: Cannot read properties of null (reading 'useMemo' )
@@ -141,6 +142,9 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
4. `cd projects/app` 4. `cd projects/app`
5. `pnpm dev` 5. `pnpm dev`
### Error response from daemon: error while creating mount source path 'XXX': mkdir XXX: file exists
这个错误可能是之前停止容器时有文件残留导致的首先需要确认相关镜像都全部关闭然后手动删除相关文件或者重启docker即可
## 加入社区 ## 加入社区
@@ -155,6 +159,7 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
FastGPT 使用了 nextjs 的 page route 作为框架。为了区分好前后端代码,在目录分配上会分成 global, service, web 3个自目录分别对应着 `前后端共用``后端专用``前端专用`的代码。 FastGPT 使用了 nextjs 的 page route 作为框架。为了区分好前后端代码,在目录分配上会分成 global, service, web 3个自目录分别对应着 `前后端共用``后端专用``前端专用`的代码。
### monorepo ### monorepo
FastGPT 采用 pnpm workspace 方式构建 monorepo 项目,主要分为两个部分: FastGPT 采用 pnpm workspace 方式构建 monorepo 项目,主要分为两个部分:
- projects/app - FastGPT 主项目 - projects/app - FastGPT 主项目
@@ -173,6 +178,7 @@ support - 支撑功能(用户体系,计费,鉴权等)
common - 基础功能(日志管理,文件读写等) common - 基础功能(日志管理,文件读写等)
{{% details title="代码结构说明" closed="true" %}} {{% details title="代码结构说明" closed="true" %}}
``` ```
. .
├── .github // github 相关配置 ├── .github // github 相关配置
@@ -200,4 +206,5 @@ common - 基础功能(日志管理,文件读写等)
├── README_ja.md ├── README_ja.md
├── dev.md ├── dev.md
``` ```
{{% /details %}} {{% /details %}}

View File

@@ -7,18 +7,22 @@ toc: true
weight: 852 weight: 852
--- ---
## 发起对话
{{% alert icon="🤖 " context="success" %}} {{% alert icon="🤖 " context="success" %}}
该接口的 API Key 需使用`应用特定的 key`,否则会报错。 该接口的 API Key 需使用`应用特定的 key`,否则会报错。
有些包调用时,`BaseUrl`需要添加`v1`路径有些不需要如果出现404情况可补充`v1`重试。 有些包调用时,`BaseUrl`需要添加`v1`路径有些不需要如果出现404情况可补充`v1`重试。
{{% /alert %}} {{% /alert %}}
## 发起对话(简易应用和工作流)
**对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用** 对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl``Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
## 请求 {{% alert icon="🤖 " context="success" %}}
* 传入的`model``temperature`等参数字段均无效,这些字段由编排决定。
* 不会返回实际消耗`Token`值,如果需要,可以设置`detail=true`,并手动计算 `responseData` 里的`tokens`值。
{{% /alert %}}
### 请求
{{< tabs tabTotal="2" >}} {{< tabs tabTotal="2" >}}
{{< tab tabName="请求示例" >}} {{< tab tabName="请求示例" >}}
@@ -67,7 +71,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions'
{{< /tab >}} {{< /tab >}}
{{< /tabs >}} {{< /tabs >}}
## 响应 ### 响应
{{< tabs tabTotal="5" >}} {{< tabs tabTotal="5" >}}
{{< tab tabName="detail=false,stream=false 响应" >}} {{< tab tabName="detail=false,stream=false 响应" >}}
@@ -245,7 +249,7 @@ data: [{"moduleName":"知识库搜索","moduleType":"datasetSearchNode","running
{{< /markdownify >}} {{< /markdownify >}}
{{< /tab >}} {{< /tab >}}
{{< tab tabName="detail=true,stream=true 时,event值" >}} {{< tab tabName="event值" >}}
{{< markdownify >}} {{< markdownify >}}
event取值 event取值
@@ -265,6 +269,192 @@ event取值
{{< /tabs >}} {{< /tabs >}}
## 请求插件
插件的接口与对话接口一致,仅请求参数略有区别,有以下规定:
* 调用插件类型的应用时,接口默认为`detail`模式。
* 无需传入 `chatId`,因为插件只能运行一轮。
* 无需传入`messages`
* 通过传递`variables`来代表插件的输入。
* 通过获取`pluginData`来获取插件输出。
### 请求示例
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer test-xxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"stream": false,
"chatId": "test",
"variables": {
"query":"你好" # 我的插件输入有一个参数,变量名叫 query
}
}'
```
### 响应示例
{{< tabs tabTotal="3" >}}
{{< tab tabName="detail=true,stream=false 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过查找`responseData`中, `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍可以通过`choices`进行获取。
```json
{
"responseData": [
{
"nodeId": "fdDgXQ6SYn8v",
"moduleName": "AI 对话",
"moduleType": "chatNode",
"totalPoints": 0.685,
"model": "FastAI-3.5",
"tokens": 685,
"query": "你好",
"maxToken": 2000,
"historyPreview": [
{
"obj": "Human",
"value": "你好"
},
{
"obj": "AI",
"value": "你好!有什么可以帮助你的吗?欢迎向我提问。"
}
],
"contextTotalLen": 14,
"runningTime": 1.73
},
{
"nodeId": "pluginOutput",
"moduleName": "自定义插件输出",
"moduleType": "pluginOutput",
"totalPoints": 0,
"pluginOutput": {
"result": "你好!有什么可以帮助你的吗?欢迎向我提问。"
},
"runningTime": 0
}
],
"newVariables": {
"query": "你好"
},
"id": "safsafsa",
"model": "",
"usage": {
"prompt_tokens": 1,
"completion_tokens": 1,
"total_tokens": 1
},
"choices": [
{
"message": {
"role": "assistant",
"content": "你好!有什么可以帮助你的吗?欢迎向我提问。"
},
"finish_reason": "stop",
"index": 0
}
]
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="detail=true,stream=true 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过获取`event=flowResponses`中的字符串,并将其反序列化后得到一个数组。同样的,查找 `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍和对话接口一样获取。
```bash
event: flowNodeStatus
data: {"status":"running","name":"AI 对话"}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"好"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"有"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"什"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"么"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"可以"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"帮"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"助"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"的"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"吗"},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":""},"index":0,"finish_reason":null}]}
event: answer
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
event: answer
data: [DONE]
event: flowResponses
data: [{"nodeId":"fdDgXQ6SYn8v","moduleName":"AI 对话","moduleType":"chatNode","totalPoints":0.033,"model":"FastAI-3.5","tokens":33,"query":"你好","maxToken":2000,"historyPreview":[{"obj":"Human","value":"你好"},{"obj":"AI","value":"你好!有什么可以帮助你的吗?"}],"contextTotalLen":2,"runningTime":1.42},{"nodeId":"pluginOutput","moduleName":"自定义插件输出","moduleType":"pluginOutput","totalPoints":0,"pluginOutput":{"result":"你好!有什么可以帮助你的吗?"},"runningTime":0}]
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="输出获取" >}}
{{< markdownify >}}
event取值
- answer: 返回给客户端的文本(最终会算作回答)
- fastAnswer: 指定回复返回给客户端的文本(最终会算作回答)
- toolCall: 执行工具
- toolParams: 工具参数
- toolResponse: 工具返回
- flowNodeStatus: 运行到的节点状态
- flowResponses: 节点完整响应
- updateVariables: 更新变量
- error: 报错
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
## 使用案例 ## 使用案例
- [接入 NextWeb/ChatGPT web 等应用](/docs/use-cases/openapi) - [接入 NextWeb/ChatGPT web 等应用](/docs/use-cases/openapi)

View File

@@ -128,8 +128,12 @@ curl --location --request POST 'http://localhost:3000/api/core/dataset/create' \
{{< markdownify >}} {{< markdownify >}}
```bash ```bash
curl --location --request GET 'http://localhost:3000/api/core/dataset/list?parentId=' \ curl --location --request POST 'http://localhost:3000/api/core/dataset/list?parentId=' \
--header 'Authorization: Bearer {{authorization}}' \ --header 'Authorization: Bearer xxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"parentId":""
}'
``` ```
{{< /markdownify >}} {{< /markdownify >}}
@@ -139,7 +143,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren
{{< markdownify >}} {{< markdownify >}}
{{% alert icon=" " context="success" %}} {{% alert icon=" " context="success" %}}
- parentId - 父级ID不传或为空,代表获取根目录下的知识库 - parentId - 父级ID传空字符串或者null,代表获取根目录下的知识库
{{% /alert %}} {{% /alert %}}
{{< /markdownify >}} {{< /markdownify >}}

View File

@@ -77,7 +77,7 @@ docker-compose up -d
商业版用户需要执行一个初始化,格式化团队信息。 商业版用户需要执行一个初始化,格式化团队信息。
发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成自己域名) 发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成商业版域名)
```bash ```bash
curl --location --request POST 'https://{{host}}/api/init/v468' \ curl --location --request POST 'https://{{host}}/api/init/v468' \

View File

@@ -1,5 +1,5 @@
--- ---
title: 'V4.8.6(进行中)' title: 'V4.8.6(需要初始化)'
description: 'FastGPT V4.8.6 更新说明' description: 'FastGPT V4.8.6 更新说明'
icon: 'upgrade' icon: 'upgrade'
draft: false draft: false
@@ -13,9 +13,9 @@ weight: 818
### 2. 修改镜像 ### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.6-alpha - fastgpt 镜像 tag 修改成 v4.8.6
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha - fastgpt-sandbox 镜像 tag 修改成 v4.8.6
- 商业版镜像 tag 修改成 v4.8.6-alpha - 商业版镜像 tag 修改成 v4.8.6
### 3. 执行初始化 ### 3. 执行初始化
@@ -33,12 +33,19 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
## V4.8.6 更新说明 ## V4.8.6 更新说明
1. 新增 - 知识库支持单个集合禁用功能 1. 新增 - 应用权限继承
2. 新增 - 文件夹权限继承 2. 新增 - 知识库支持单个集合禁用功能
3. 新增 - 网页抓取和数学计算器系统插件 3. 新增 - 系统插件模式变更,新增链接读取和数学计算器插件,正式版会更新如何自定义系统插件
4. 新增 - 移动文本加工和自定义反馈到基础节点中 4. 新增 - 代码沙盒运行参数
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力 5. 新增 - AI对话时隐藏头部的功能主要是适配移动端
6. 优化 - 知识库导入接口,返回值对齐 6. 优化 - 文件读取Mongo 默认使用从节点,减轻主节点压力
7. 修复 - 工作流中团队插件加载异常 7. 优化 - 提示词模板
8. 修复 - 知识库集合目录导航失效 8. 优化 - Mongo model 重复加载
9. 修复 - 通过 API 调用 chat 接口,传递 System 异常 9. 修复 - 创建链接集合未返回 id
10. 修复 - 文档接口说明
11. 修复 - api system 提示合并
12. 修复 - 团队插件目录内的内容无法加载
13. 修复 - 知识库集合目录面包屑无法加载
14. 修复 - Markdown 导出对话异常
15. 修复 - 提示模板结束标签错误
16. 修复 - 文档描述

View File

@@ -0,0 +1,29 @@
---
title: 'V4.8.7'
description: 'FastGPT V4.8.7 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 817
---
## 升级指南
### 1. 做好数据库备份
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.7
- 商业版镜像 tag 修改成 v4.8.7
-------
## V4.8.7 更新说明
1. 新增 - 插件支持独立运行,发布和日志查看
2. 新增 - 应用搜索
3. 优化 - 对话框代码
4. 优化 - 升级 Dockerfile node 和 pnpm 版本
5. 优化 - local 域名部署,也可以正常使用 vision 模式
6. 修复 - 简易模式无法变更全局变量
7. 修复 - gpt4o 无法同时使用工具和图片

View File

@@ -0,0 +1,49 @@
---
title: 'V4.8.8(进行中)'
description: 'FastGPT V4.8.8 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 816
---
## 升级指南
### 1. 做好数据库备份
### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.8-alpha
- 商业版镜像 tag 修改成 v4.8.8-alpha
### 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 更新说明
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

@@ -78,7 +78,7 @@ weight: 404
}, },
{ {
"nodeId": "u6IAOEssxoZT", "nodeId": "u6IAOEssxoZT",
"name": "工具调用(实验)", "name": "工具调用",
"intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。", "intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。",
"avatar": "/imgs/workflow/tool.svg", "avatar": "/imgs/workflow/tool.svg",
"flowNodeType": "tools", "flowNodeType": "tools",

View File

@@ -5,6 +5,7 @@ enableEmoji = true
enableGitInfo = false # N.B. .GitInfo does not currently function with git submodule content directories enableGitInfo = false # N.B. .GitInfo does not currently function with git submodule content directories
defaultContentLanguage = 'zh-cn' defaultContentLanguage = 'zh-cn'
defaultContentLanguageInSubdir = false
[languages] [languages]
[languages.zh-cn] [languages.zh-cn]
title = "FastGPT" title = "FastGPT"

View File

@@ -15,7 +15,7 @@
{{ end }} {{ end }}
<!-- change --> <!-- change -->
{{ $repoURL = $repoURL | append "docSite/content" .Site.LanguagePrefix $filePath }} {{ $repoURL = $repoURL | append "docSite/content" .Site.Language.Lang $filePath }}
{{ $repoURL = delimit $repoURL "/" }} {{ $repoURL = delimit $repoURL "/" }}
{{ $editPageURL := replaceRE "(https?://)|(/)+" "$1$2" $repoURL }} {{ $editPageURL := replaceRE "(https?://)|(/)+" "$1$2" $repoURL }}

View File

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

View File

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

View File

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

View File

@@ -9,16 +9,17 @@
"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", "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", "postinstall": "sh ./scripts/postinstall.sh",
"initIcon": "node ./scripts/icon/init.js", "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": { "devDependencies": {
"@chakra-ui/cli": "^2.4.1", "@chakra-ui/cli": "^2.4.1",
"husky": "^8.0.3", "husky": "^8.0.3",
"i18next": "23.10.0",
"lint-staged": "^13.3.0", "lint-staged": "^13.3.0",
"next-i18next": "15.2.0", "i18next": "23.11.5",
"next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"prettier": "3.2.4", "prettier": "3.2.4",
"react-i18next": "13.5.0",
"zhlint": "^0.7.4" "zhlint": "^0.7.4"
}, },
"lint-staged": { "lint-staged": {
@@ -26,7 +27,7 @@
"./docSite/**/**/*.md": "npm run format-doc" "./docSite/**/**/*.md": "npm run format-doc"
}, },
"engines": { "engines": {
"node": ">=18.0.0", "node": ">=18.16.0",
"pnpm": ">=8.6.0" "pnpm": ">=9.0.0"
} }
} }

View File

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

View File

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

View File

@@ -9,6 +9,7 @@ export type MongoImageSchemaType = {
type: `${MongoImageTypeEnum}`; type: `${MongoImageTypeEnum}`;
metadata?: { metadata?: {
mime?: string; // image mime type.
relatedId?: string; // This id is associated with a set of images relatedId?: string; // This id is associated with a set of images
}; };
}; };

View File

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

View File

@@ -32,9 +32,12 @@ export type FastGPTFeConfigsType = {
show_promotion?: boolean; show_promotion?: boolean;
show_team_chat?: boolean; show_team_chat?: boolean;
concatMd?: string; concatMd?: string;
docUrl?: string; docUrl?: string;
chatbotUrl?: string; chatbotUrl?: string;
openAPIDocUrl?: string; openAPIDocUrl?: string;
systemPluginCourseUrl?: string;
systemTitle?: string; systemTitle?: string;
systemDescription?: string; systemDescription?: string;
googleClientVerKey?: string; googleClientVerKey?: string;
@@ -70,9 +73,3 @@ export type SystemEnvType = {
oneapiUrl?: string; oneapiUrl?: string;
chatApiKey?: string; chatApiKey?: string;
}; };
// declare global {
// var feConfigs: FastGPTFeConfigsType;
// var systemEnv: SystemEnvType;
// var systemInitd: boolean;
// }

View File

@@ -2,8 +2,8 @@ import type { LLMModelItemType, VectorModelItemType } from './model.d';
export const defaultQAModels: LLMModelItemType[] = [ export const defaultQAModels: LLMModelItemType[] = [
{ {
model: 'gpt-3.5-turbo', model: 'gpt-4o-mini',
name: 'gpt-3.5-turbo', name: 'gpt-4o-mini',
maxContext: 16000, maxContext: 16000,
maxResponse: 16000, maxResponse: 16000,
quoteMaxToken: 13000, quoteMaxToken: 13000,

View File

@@ -0,0 +1,10 @@
import { StoreNodeItemType } from '../../workflow/type/node';
import { FlowNodeInputItemType } from '../../workflow/type/io';
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
};
export const getPluginRunContent = (e: { pluginInputs: FlowNodeInputItemType[] }) => {
return JSON.stringify(e);
};

View File

@@ -21,6 +21,7 @@ export type AppSchema = {
name: string; name: string;
avatar: string; avatar: string;
intro: string; intro: string;
updateTime: Date; updateTime: Date;
modules: StoreNodeItemType[]; modules: StoreNodeItemType[];

View File

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

View File

@@ -56,7 +56,10 @@ export const chats2GPTMessages = ({
text: item.text?.content || '' text: item.text?.content || ''
}; };
} }
if (item.type === 'file' && item.file?.type === ChatFileTypeEnum.image) { if (
item.type === ChatItemValueTypeEnum.file &&
item.file?.type === ChatFileTypeEnum.image
) {
return { return {
type: 'image_url', type: 'image_url',
image_url: { image_url: {
@@ -64,7 +67,6 @@ export const chats2GPTMessages = ({
} }
}; };
} }
return;
}) })
.filter(Boolean) as ChatCompletionContentPart[]; .filter(Boolean) as ChatCompletionContentPart[];
@@ -166,7 +168,7 @@ export const GPTMessages2Chats = (
} else if (item.type === 'image_url') { } else if (item.type === 'image_url') {
value.push({ value.push({
//@ts-ignore //@ts-ignore
type: 'file', type: ChatItemValueTypeEnum.file,
file: { file: {
type: ChatFileTypeEnum.image, type: ChatFileTypeEnum.image,
name: '', name: '',
@@ -175,7 +177,6 @@ export const GPTMessages2Chats = (
}); });
} }
}); });
// @ts-ignore
} }
} else if ( } else if (
obj === ChatRoleEnum.AI && obj === ChatRoleEnum.AI &&

View File

@@ -56,7 +56,4 @@ export enum ChatStatusEnum {
finish = 'finish' finish = 'finish'
} }
export const IMG_BLOCK_KEY = 'img-block';
export const FILE_BLOCK_KEY = 'file-block';
export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN'; export const MARKDOWN_QUOTE_SIGN = 'QUOTE SIGN';

View File

@@ -13,8 +13,8 @@ import { DispatchNodeResponseKeyEnum } from '../workflow/runtime/constants';
import { AppChatConfigType, AppSchema, VariableItemType } from '../app/type'; import { AppChatConfigType, AppSchema, VariableItemType } from '../app/type';
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import { DatasetSearchModeEnum } from '../dataset/constants'; import { DatasetSearchModeEnum } from '../dataset/constants';
import { ChatBoxInputType } from '../../../../projects/app/src/components/ChatBox/type';
import { DispatchNodeResponseType } from '../workflow/runtime/type.d'; import { DispatchNodeResponseType } from '../workflow/runtime/type.d';
import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type';
export type ChatSchema = { export type ChatSchema = {
_id: string; _id: string;
@@ -115,6 +115,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
status: `${ChatStatusEnum}`; status: `${ChatStatusEnum}`;
moduleName?: string; moduleName?: string;
ttsBuffer?: Uint8Array; ttsBuffer?: Uint8Array;
responseData?: ChatHistoryItemResType[];
} & ChatBoxInputType; } & ChatBoxInputType;
/* --------- team chat --------- */ /* --------- team chat --------- */

View File

@@ -65,11 +65,12 @@ export const filterPublicNodeResponseData = ({
}: { }: {
flowResponses?: ChatHistoryItemResType[]; flowResponses?: ChatHistoryItemResType[];
}) => { }) => {
const filedList = ['quoteList', 'moduleType']; const filedList = ['quoteList', 'moduleType', 'pluginOutput'];
const filterModuleTypeList: any[] = [ const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule, FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode, FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools FlowNodeTypeEnum.tools,
FlowNodeTypeEnum.pluginOutput
]; ];
return flowResponses return flowResponses
@@ -89,14 +90,22 @@ export const filterPublicNodeResponseData = ({
}); });
}; };
export const removeEmptyUserInput = (input: UserChatItemValueItemType[]) => { export const removeEmptyUserInput = (input?: UserChatItemValueItemType[]) => {
return input.filter((item) => { return (
if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) { input?.filter((item) => {
return false; if (item.type === ChatItemValueTypeEnum.text && !item.text?.content?.trim()) {
} return false;
if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) { }
return false; if (item.type === ChatItemValueTypeEnum.file && !item.file?.url) {
} return false;
return true; }
}); return true;
}) || []
);
};
export const getPluginOutputsFromChatResponses = (responses: ChatHistoryItemResType[]) => {
const outputs =
responses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput)?.pluginOutput ?? {};
return outputs;
}; };

View File

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

View File

@@ -8,22 +8,22 @@ export enum DatasetTypeEnum {
export const DatasetTypeMap = { export const DatasetTypeMap = {
[DatasetTypeEnum.folder]: { [DatasetTypeEnum.folder]: {
icon: 'common/folderFill', icon: 'common/folderFill',
label: 'Folder Dataset', label: 'folder_dataset',
collectionLabel: 'common.Folder' collectionLabel: 'common.Folder'
}, },
[DatasetTypeEnum.dataset]: { [DatasetTypeEnum.dataset]: {
icon: 'core/dataset/commonDataset', icon: 'core/dataset/commonDataset',
label: 'Common Dataset', label: 'common_dataset',
collectionLabel: 'common.File' collectionLabel: 'common.File'
}, },
[DatasetTypeEnum.websiteDataset]: { [DatasetTypeEnum.websiteDataset]: {
icon: 'core/dataset/websiteDataset', icon: 'core/dataset/websiteDataset',
label: 'Website Dataset', label: 'website_dataset',
collectionLabel: 'common.Website' collectionLabel: 'common.Website'
}, },
[DatasetTypeEnum.externalFile]: { [DatasetTypeEnum.externalFile]: {
icon: 'core/dataset/externalDataset', icon: 'core/dataset/externalDataset',
label: 'External File', label: 'external_file',
collectionLabel: 'common.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 type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
import { PermissionTypeEnum } from '../../support/permission/constant'; import { PermissionTypeEnum } from '../../support/permission/constant';
import { PushDatasetDataChunkProps } from './api'; import { PushDatasetDataChunkProps } from './api';
@@ -12,31 +12,28 @@ import {
import { DatasetPermission } from '../../support/permission/dataset/controller'; import { DatasetPermission } from '../../support/permission/dataset/controller';
import { Permission } from '../../support/permission/controller'; import { Permission } from '../../support/permission/controller';
/* schema */
export type DatasetSchemaType = { export type DatasetSchemaType = {
_id: string; _id: string;
parentId: string; parentId?: string;
userId: string; userId: string;
teamId: string; teamId: string;
tmbId: string; tmbId: string;
updateTime: Date; updateTime: Date;
avatar: string; avatar: string;
name: string; name: string;
vectorModel: string; vectorModel: string;
agentModel: string; agentModel: string;
intro: string; intro: string;
type: DatasetTypeEnum; type: `${DatasetTypeEnum}`;
status: `${DatasetStatusEnum}`; status: `${DatasetStatusEnum}`;
// permission: DatasetPermission;
// metadata
websiteConfig?: { websiteConfig?: {
url: string; url: string;
selector: string; selector: string;
}; };
externalReadUrl?: string; externalReadUrl?: string;
defaultPermission: PermissionValueType; } & PermissionSchemaType;
}; // } & PermissionSchemaType;
export type DatasetCollectionSchemaType = { export type DatasetCollectionSchemaType = {
_id: string; _id: string;
@@ -133,15 +130,13 @@ export type DatasetSimpleItemType = {
}; };
export type DatasetListItemType = { export type DatasetListItemType = {
_id: string; _id: string;
parentId: string;
avatar: string; avatar: string;
name: string; name: string;
intro: string; intro: string;
type: DatasetTypeEnum; type: `${DatasetTypeEnum}`;
permission: DatasetPermission; permission: DatasetPermission;
vectorModel: VectorModelItemType; vectorModel: VectorModelItemType;
defaultPermission: PermissionValueType; } & PermissionSchemaType;
};
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & { export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
vectorModel: VectorModelItemType; vectorModel: VectorModelItemType;

View File

@@ -32,7 +32,7 @@ export type PluginItemSchema = {
export type PluginTemplateType = PluginRuntimeType & { export type PluginTemplateType = PluginRuntimeType & {
author?: string; author?: string;
id: string; id: string;
source: `${PluginSourceEnum}`; source: PluginSourceEnum;
templateType: FlowNodeTemplateType['templateType']; templateType: FlowNodeTemplateType['templateType'];
intro: string; intro: string;
version: string; version: string;

View File

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

View File

@@ -67,11 +67,12 @@ export type RuntimeNodeItemType = {
}; };
export type PluginRuntimeType = { export type PluginRuntimeType = {
id: string;
teamId?: string; teamId?: string;
name: string; name: string;
avatar: string; avatar: string;
showStatus?: boolean; showStatus?: boolean;
isTool?: boolean; currentCost?: number;
nodes: StoreNodeItemType[]; nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[]; edges: StoreEdgeItemType[];
}; };
@@ -142,6 +143,9 @@ export type DispatchNodeResponseType = {
// code // code
codeLog?: string; codeLog?: string;
// plugin
pluginOutput?: Record<string, any>;
}; };
export type DispatchNodeResultType<T> = { export type DispatchNodeResultType<T> = {

View File

@@ -123,6 +123,7 @@ export const checkNodeRunStatus = ({
(item) => item.target === node.nodeId (item) => item.target === node.nodeId
); );
// Entry
if (workflowEdges.length === 0) { if (workflowEdges.length === 0) {
return 'run'; return 'run';
} }

View File

@@ -8,5 +8,10 @@ export const Output_Template_AddOutput: FlowNodeOutputItemType = {
key: NodeOutputKeyEnum.addOutputParam, key: NodeOutputKeyEnum.addOutputParam,
type: FlowNodeOutputTypeEnum.dynamic, type: FlowNodeOutputTypeEnum.dynamic,
valueType: WorkflowIOValueTypeEnum.dynamic, valueType: WorkflowIOValueTypeEnum.dynamic,
label: '' label: '',
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: false
}
}; };

View File

@@ -26,7 +26,7 @@ export const AiChatModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.chatNode, flowNodeType: FlowNodeTypeEnum.chatNode,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/AI.png', avatar: 'core/workflow/template/aiChat',
name: 'AI 对话', name: 'AI 对话',
intro: 'AI 大模型对话', intro: 'AI 大模型对话',
showStatus: true, showStatus: true,

View File

@@ -13,7 +13,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.answerNode, flowNodeType: FlowNodeTypeEnum.answerNode,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/reply.png', avatar: 'core/workflow/template/reply',
name: '指定回复', name: '指定回复',
intro: intro:
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。', '该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',

View File

@@ -25,7 +25,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.classifyQuestion, flowNodeType: FlowNodeTypeEnum.classifyQuestion,
sourceHandle: getHandleConfig(false, false, false, false), sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true), targetHandle: getHandleConfig(true, false, true, true),
avatar: '/imgs/workflow/cq.png', avatar: 'core/workflow/template/questionClassify',
name: '问题分类', name: '问题分类',
intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题`, intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题`,
showStatus: true, showStatus: true,

View File

@@ -20,7 +20,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.contentExtract, flowNodeType: FlowNodeTypeEnum.contentExtract,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/extract.png', avatar: 'core/workflow/template/extractJson',
name: '文本内容提取', name: '文本内容提取',
intro: '可从文本中提取指定的数据例如sql语句、搜索关键词、代码等', intro: '可从文本中提取指定的数据例如sql语句、搜索关键词、代码等',
showStatus: true, showStatus: true,

View File

@@ -13,7 +13,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.customFeedback, flowNodeType: FlowNodeTypeEnum.customFeedback,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/customFeedback.svg', avatar: 'core/workflow/template/customFeedback',
name: '自定义反馈', name: '自定义反馈',
intro: '该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。', intro: '该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。',
version: '486', version: '486',

View File

@@ -37,7 +37,7 @@ export const DatasetConcatModule: FlowNodeTemplateType = {
templateType: FlowNodeTemplateTypeEnum.other, templateType: FlowNodeTemplateTypeEnum.other,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/concat.svg', avatar: 'core/workflow/template/datasetConcat',
name: '知识库搜索引用合并', name: '知识库搜索引用合并',
intro: '可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。', intro: '可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。',
showStatus: false, showStatus: false,

View File

@@ -23,7 +23,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.datasetSearchNode, flowNodeType: FlowNodeTypeEnum.datasetSearchNode,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/db.png', avatar: 'core/workflow/template/datasetSearch',
name: '知识库搜索', name: '知识库搜索',
intro: Dataset_SEARCH_DESC, intro: Dataset_SEARCH_DESC,
showStatus: true, showStatus: true,

View File

@@ -20,7 +20,7 @@ export const HttpNode468: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.httpRequest468, flowNodeType: FlowNodeTypeEnum.httpRequest468,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/http.png', avatar: 'core/workflow/template/httpRequest',
name: 'HTTP 请求', name: 'HTTP 请求',
intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)', intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
showStatus: true, showStatus: true,
@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
], ],
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
}
}, },
{ {
id: NodeOutputKeyEnum.error, id: NodeOutputKeyEnum.error,

View File

@@ -18,7 +18,7 @@ export const IfElseNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.ifElseNode, flowNodeType: FlowNodeTypeEnum.ifElseNode,
sourceHandle: getHandleConfig(false, false, false, false), sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, false, true, true), targetHandle: getHandleConfig(true, false, true, true),
avatar: '/imgs/workflow/ifElse.svg', avatar: 'core/workflow/template/ifelse',
name: '判断器', name: '判断器',
intro: '根据一定的条件,执行不同的分支。', intro: '根据一定的条件,执行不同的分支。',
showStatus: true, showStatus: true,

View File

@@ -26,7 +26,7 @@ export const LafModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.lafModule, flowNodeType: FlowNodeTypeEnum.lafModule,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/laf.png', avatar: 'core/workflow/template/lafDispatch',
name: 'Laf 函数调用(测试)', name: 'Laf 函数调用(测试)',
intro: '可以调用Laf账号下的云函数。', intro: '可以调用Laf账号下的云函数。',
showStatus: true, showStatus: true,

View File

@@ -11,7 +11,7 @@ export const PluginInputModule: FlowNodeTemplateType = {
targetHandle: getHandleConfig(false, false, false, false), targetHandle: getHandleConfig(false, false, false, false),
unique: true, unique: true,
forbidDelete: true, forbidDelete: true,
avatar: '/imgs/workflow/input.png', avatar: 'core/workflow/template/workflowStart',
name: '插件输入', name: '插件输入',
intro: '可以配置插件需要哪些输入,利用这些输入来运行插件', intro: '可以配置插件需要哪些输入,利用这些输入来运行插件',
showStatus: false, showStatus: false,

View File

@@ -11,7 +11,7 @@ export const PluginOutputModule: FlowNodeTemplateType = {
targetHandle: getHandleConfig(false, false, false, true), targetHandle: getHandleConfig(false, false, false, true),
unique: true, unique: true,
forbidDelete: true, forbidDelete: true,
avatar: '/imgs/workflow/output.png', avatar: 'core/workflow/template/pluginOutput',
name: '自定义插件输出', name: '自定义插件输出',
intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出', intro: '自定义配置外部输出,使用插件时,仅暴露自定义配置的输出',
showStatus: false, showStatus: false,

View File

@@ -19,12 +19,12 @@ import { LLMModelTypeEnum } from '../../../ai/constants';
import { getHandleConfig } from '../utils'; import { getHandleConfig } from '../utils';
export const AiQueryExtension: FlowNodeTemplateType = { export const AiQueryExtension: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.chatNode, id: FlowNodeTypeEnum.queryExtension,
templateType: FlowNodeTemplateTypeEnum.other, templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.queryExtension, flowNodeType: FlowNodeTypeEnum.queryExtension,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/cfr.svg', avatar: 'core/workflow/template/queryExtension',
name: '问题优化', name: '问题优化',
intro: intro:
'使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。', '使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。',

View File

@@ -19,7 +19,7 @@ export const RunAppModule: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.runApp, flowNodeType: FlowNodeTypeEnum.runApp,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/app.png', avatar: 'core/workflow/template/runApp',
name: '应用调用', name: '应用调用',
intro: '可以选择一个其他应用进行调用', intro: '可以选择一个其他应用进行调用',
showStatus: true, showStatus: true,

View File

@@ -21,7 +21,7 @@ export const CodeNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.code, flowNodeType: FlowNodeTypeEnum.code,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/code.svg', avatar: 'core/workflow/template/codeRun',
name: '代码运行', name: '代码运行',
intro: '执行一段简单的脚本代码,通常用于进行复杂的数据处理。', intro: '执行一段简单的脚本代码,通常用于进行复杂的数据处理。',
showStatus: true, showStatus: true,
@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
showDefaultValue: true showDefaultValue: true
} }
}, },
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data1',
label: 'data1',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data2',
label: 'data2',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{ {
key: NodeInputKeyEnum.codeType, key: NodeInputKeyEnum.codeType,
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput,
description: '将代码中 return 的对象作为输出,传递给后续的节点' description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
}, },
{ {
id: NodeOutputKeyEnum.rawResponse, id: NodeOutputKeyEnum.rawResponse,

View File

@@ -9,7 +9,7 @@ export const StopToolNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.stopTool, flowNodeType: FlowNodeTypeEnum.stopTool,
sourceHandle: getHandleConfig(false, false, false, false), sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/toolStop.svg', avatar: 'core/workflow/template/stopTool',
name: '工具调用终止', name: '工具调用终止',
intro: intro:
'该模块需配置工具调用使用。当该模块被执行时本次工具调用将会强制结束并且不再调用AI针对工具调用结果回答问题。', '该模块需配置工具调用使用。当该模块被执行时本次工具调用将会强制结束并且不再调用AI针对工具调用结果回答问题。',

View File

@@ -9,7 +9,7 @@ export const SystemConfigNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.systemConfig, flowNodeType: FlowNodeTypeEnum.systemConfig,
sourceHandle: getHandleConfig(false, false, false, false), sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false), targetHandle: getHandleConfig(false, false, false, false),
avatar: '/imgs/workflow/userGuide.png', avatar: 'core/workflow/template/systemConfig',
name: '系统配置', name: '系统配置',
intro: '可以配置应用的系统参数。', intro: '可以配置应用的系统参数。',
unique: true, unique: true,

View File

@@ -19,7 +19,7 @@ export const TextEditorNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.textEditor, flowNodeType: FlowNodeTypeEnum.textEditor,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/textEditor.svg', avatar: 'core/workflow/template/textConcat',
name: '文本拼接', name: '文本拼接',
intro: '可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。', intro: '可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。',
version: '486', version: '486',

View File

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

View File

@@ -13,7 +13,7 @@ export const VariableUpdateNode: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.variableUpdate, flowNodeType: FlowNodeTypeEnum.variableUpdate,
sourceHandle: getHandleConfig(true, true, true, true), sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true), targetHandle: getHandleConfig(true, true, true, true),
avatar: '/imgs/workflow/variable.png', avatar: 'core/workflow/template/variableUpdate',
name: '变量更新', name: '变量更新',
intro: '可以更新指定节点的输出值或更新全局变量', intro: '可以更新指定节点的输出值或更新全局变量',
showStatus: false, showStatus: false,

View File

@@ -14,7 +14,7 @@ export const WorkflowStart: FlowNodeTemplateType = {
flowNodeType: FlowNodeTypeEnum.workflowStart, flowNodeType: FlowNodeTypeEnum.workflowStart,
sourceHandle: getHandleConfig(false, true, false, false), sourceHandle: getHandleConfig(false, true, false, false),
targetHandle: getHandleConfig(false, false, false, false), targetHandle: getHandleConfig(false, false, false, false),
avatar: '/imgs/workflow/userChatInput.svg', avatar: 'core/workflow/template/workflowStart',
name: '流程开始', name: '流程开始',
intro: '', intro: '',
forbidDelete: true, forbidDelete: true,

View File

@@ -35,6 +35,7 @@ export type WorkflowTemplateType = {
avatar: string; avatar: string;
intro?: string; intro?: string;
author?: string; author?: string;
inputExplanationUrl?: string;
version: string; version: string;
showStatus?: boolean; showStatus?: boolean;
@@ -42,17 +43,29 @@ export type WorkflowTemplateType = {
workflow: WorkflowTemplateBasicType; workflow: WorkflowTemplateBasicType;
}; };
// template market // template market
export type TemplateMarketItemType = WorkflowTemplateType & { export type TemplateMarketItemType = WorkflowTemplateType & {
tags?: { id: string; label: string }[]; tags?: { id: string; label: string }[];
}; };
// system plugin // system plugin
export type SystemPluginTemplateItemType = WorkflowTemplateType & { export type SystemPluginTemplateItemType = WorkflowTemplateType & {
templateType: FlowNodeTemplateTypeEnum; templateType: FlowNodeTemplateTypeEnum;
isTool?: boolean; isTool?: boolean;
// commercial plugin config
originCost: number; // n points/one time originCost: number; // n points/one time
currentCost: number; 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; workflow: WorkflowTemplateBasicType;
}; };

View File

@@ -31,6 +31,7 @@ export type FlowNodeCommonType = {
avatar?: string; avatar?: string;
name: string; name: string;
intro?: string; // template list intro intro?: string; // template list intro
inputExplanationUrl?: string;
showStatus?: boolean; // chatting response step status showStatus?: boolean; // chatting response step status
version: string; version: string;
@@ -70,7 +71,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
export type NodeTemplateListItemType = { export type NodeTemplateListItemType = {
id: string; // 系统节点-系统节点的 id 系统插件-插件的 id团队应用的 id id: string; // 系统节点-系统节点的 id 系统插件-插件的 id团队应用的 id
flowNodeType: FlowNodeTypeEnum; // render node card flowNodeType: FlowNodeTypeEnum; // render node card
parentId?: string; parentId?: ParentIdType;
isFolder?: boolean; isFolder?: boolean;
templateType: FlowNodeTemplateTypeEnum; templateType: FlowNodeTemplateTypeEnum;
avatar?: string; avatar?: string;
@@ -79,6 +80,7 @@ export type NodeTemplateListItemType = {
isTool?: boolean; isTool?: boolean;
author?: string; author?: string;
unique?: boolean; // 唯一的 unique?: boolean; // 唯一的
currentCost?: number; // 当前积分消耗
}; };
export type NodeTemplateListType = { export type NodeTemplateListType = {

View File

@@ -3,9 +3,10 @@ import {
WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum,
NodeInputKeyEnum, NodeInputKeyEnum,
VariableInputEnum, VariableInputEnum,
variableMap variableMap,
VARIABLE_NODE_ID
} from './constants'; } from './constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './type/io.d'; import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d';
import { StoreNodeItemType } from './type/node'; import { StoreNodeItemType } from './type/node';
import type { import type {
VariableItemType, VariableItemType,
@@ -22,6 +23,8 @@ import {
defaultWhisperConfig defaultWhisperConfig
} from '../app/constants'; } from '../app/constants';
import { IfElseResultEnum } from './template/system/ifElse/constant'; 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) => { export const getHandleId = (nodeId: string, type: 'source' | 'target', key: string) => {
return `${nodeId}-${type}-${key}`; return `${nodeId}-${type}-${key}`;
@@ -190,3 +193,125 @@ export const isReferenceValue = (value: any): boolean => {
export const getElseIFLabel = (i: number) => { export const getElseIFLabel = (i: number) => {
return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`; return i === 0 ? IfElseResultEnum.IF : `${IfElseResultEnum.ELSE_IF} ${i}`;
}; };
// add value to plugin input node when run plugin
export const updatePluginInputByVariables = (
nodes: RuntimeNodeItemType[],
variables: Record<string, any>
) => {
return nodes.map((node) =>
node.flowNodeType === FlowNodeTypeEnum.pluginInput
? {
...node,
inputs: node.inputs.map((input) => {
const parseValue = (() => {
try {
if (
input.valueType === WorkflowIOValueTypeEnum.string ||
input.valueType === WorkflowIOValueTypeEnum.number ||
input.valueType === WorkflowIOValueTypeEnum.boolean
)
return variables[input.key];
return JSON.parse(variables[input.key]);
} catch (e) {
return variables[input.key];
}
})();
return {
...input,
value: parseValue ?? input.value
};
})
}
: node
);
};
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

@@ -10,13 +10,13 @@
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jschardet": "3.1.1", "jschardet": "3.1.1",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"next": "14.2.3", "next": "14.2.5",
"openai": "4.28.0", "openai": "4.53.0",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",
"timezones-list": "^3.0.2" "timezones-list": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/node": "^20.14.2" "@types/node": "20.14.0"
} }
} }

View File

@@ -1,11 +1,13 @@
import { TeamMemberWithUserSchema } from '../user/team/type'; 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. // 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. // It is spired by the permission system in Linux.
// The lowest 3 bits present the permission of reading, writing and managing. // 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. // The higher bits are advanced permissions or extended permissions, which could be customized.
export type PermissionValueType = number; export type PermissionValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`;
export type PermissionListType<T = {}> = Record< export type PermissionListType<T = {}> = Record<
T | PermissionKeyEnum, 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 = { export type ResourcePermissionType = {
teamId: string; teamId: string;
tmbId: string; tmbId: string;

View File

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

View File

@@ -2,11 +2,15 @@
"name": "@fastgpt/plugins", "name": "@fastgpt/plugins",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"duck-duck-scrape": "^2.2.5",
"lodash": "^4.17.21",
"axios": "^1.5.1",
"expr-eval": "^2.0.2" "expr-eval": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@fastgpt/global": "workspace:*", "@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*", "@fastgpt/service": "workspace:*",
"@types/node": "^20.14.2" "@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 { 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 { SystemPluginResponseType } from './type';
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node'; import { FastGPTProUrl, isProduction } from '../service/common/system/constants';
import { 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 = () => { 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) => ({ const isFolder = list.find((item) => item.startsWith(`${name}/`));
...require(`./src/${name}/template.json`),
id: `${PluginSourceEnum.community}-${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 = () => { try {
return getCommunityPlugins().map<NodeTemplateListItemType>((plugin) => ({ if (!global.systemPlugins) {
id: plugin.id, global.systemPlugins = [];
templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other, }
flowNodeType: FlowNodeTypeEnum.pluginModule,
avatar: plugin.avatar, global.systemPlugins = FastGPTProUrl ? await getCommercialPlugins() : getCommunityPlugins();
name: plugin.name,
intro: plugin.intro, return cloneDeep(global.systemPlugins);
isTool: plugin.isTool } catch (error) {
})); //@ts-ignore
global.systemPlugins = undefined;
return Promise.reject(error);
}
}; };
export const getCommunityCb = async () => { export const getCommunityCb = async () => {
if (isProduction && global.communitySystemPluginCb) return global.communitySystemPluginCb; const loadCommunityModule = async (name: string) => {
// Do not modify the following code
const loadModule = async (name: string) => {
const module = await import(`./src/${name}/index`); const module = await import(`./src/${name}/index`);
return module.default; return module.default;
}; };
const result = await Promise.all( const result = (
list.map(async (name) => ({ await Promise.all(
name, list.map(async (name) => {
cb: await loadModule(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< const getCommercialCb = async () => {
Record<string, (e: any) => SystemPluginResponseType> const plugins = await getSystemPluginTemplates();
>((acc, { name, cb }) => { const result = plugins.map((plugin) => {
acc[name] = cb; const name = plugin.id.split('-')[1];
return acc;
}, {}); return {
name,
return global.communitySystemPluginCb; 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,46 @@
import { search, SafeSearchType } from 'duck-duck-scrape';
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '@fastgpt/service/common/system/log';
type Props = {
query: string;
};
// Response type same as HTTP outputs
type Response = Promise<{
result: string;
}>;
const main = async (props: Props, retry = 3): Response => {
const { query } = props;
try {
const searchResults = await search(query, {
safeSearch: SafeSearchType.STRICT,
time: 'y'
});
const result = searchResults.results
.map((item) => ({
title: item.title,
link: item.url,
snippet: item.description
}))
.slice(0, 10);
return {
result: JSON.stringify(result)
};
} catch (error) {
if (retry <= 0) {
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,5 +1,4 @@
import { SystemPluginResponseType } from '../../type'; import { urlsFetch } from '@fastgpt/service/common/string/cheerio';
import { urlsFetch } from '../../../service/common/string/cheerio';
type Props = { type Props = {
url: string; url: string;

View File

@@ -1,8 +1,8 @@
{ {
"author": "FastGPT", "author": "",
"version": "486", "version": "486",
"name": "网页内容抓取", "name": "网页内容抓取",
"avatar": "/imgs/workflow/fetchUrl.svg", "avatar": "core/workflow/template/fetchUrl",
"intro": "可获取一个网页链接内容,并以 Markdown 格式输出,仅支持获取静态网站。", "intro": "可获取一个网页链接内容,并以 Markdown 格式输出,仅支持获取静态网站。",
"showStatus": true, "showStatus": true,
"weight": 10, "weight": 10,

View File

@@ -1,9 +1,9 @@
{ {
"author": "FastGPT Team", "author": "",
"version": "481", "version": "481",
"templateType": "tools", "templateType": "tools",
"name": "获取当前时间", "name": "获取当前时间",
"avatar": "/imgs/workflow/getCurrentTime.svg", "avatar": "core/workflow/template/getTime",
"intro": "获取用户当前时区的时间。", "intro": "获取用户当前时区的时间。",
"showStatus": false, "showStatus": false,
"isTool": true, "isTool": true,

View File

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

View File

@@ -1,8 +1,8 @@
{ {
"author": "FastGPT", "author": "",
"version": "486", "version": "486",
"name": "数学公式执行", "name": "数学公式执行",
"avatar": "/imgs/workflow/mathExprEval.svg", "avatar": "core/workflow/template/mathCall",
"intro": "用于执行数学表达式的工具,通过 js 的 expr-eval 库运行表达式并返回结果。", "intro": "用于执行数学表达式的工具,通过 js 的 expr-eval 库运行表达式并返回结果。",
"showStatus": false, "showStatus": false,
"weight": 10, "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'; import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
export type SystemPluginResponseType = Promise<Record<string, any>>; export type SystemPluginResponseType = Promise<Record<string, any>>;
declare global { declare global {
var communitySystemPlugins: SystemPluginTemplateItemType[]; var systemPlugins: SystemPluginTemplateItemType[];
var communitySystemPluginCb: Record<string, (e: any) => SystemPluginResponseType>; var systemPluginCb: Record<string, (e: any) => SystemPluginResponseType>;
} }

View File

@@ -63,6 +63,7 @@ const instance = axios.create({
'Cache-Control': 'no-cache' 'Cache-Control': 'no-cache'
} }
}); });
export const serverRequestBaseUrl = `http://${SERVICE_LOCAL_HOST}`;
/* 请求拦截 */ /* 请求拦截 */
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err)); instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
@@ -79,7 +80,7 @@ export function request(url: string, data: any, config: ConfigType, method: Meth
return instance return instance
.request({ .request({
baseURL: `http://${SERVICE_LOCAL_HOST}`, baseURL: serverRequestBaseUrl,
url, url,
method, method,
data: ['POST', 'PUT'].includes(method) ? data : null, data: ['POST', 'PUT'].includes(method) ? data : null,

View File

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

View File

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

View File

@@ -6,9 +6,10 @@ import { addHours } from 'date-fns';
import { WorkerNameEnum, runWorker } from '../../../worker/utils'; import { WorkerNameEnum, runWorker } from '../../../worker/utils';
import fs from 'fs'; import fs from 'fs';
import { detectFileEncoding } from '@fastgpt/global/common/file/tools'; 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, teamId,
md, md,
metadata metadata
@@ -79,7 +80,7 @@ export const readRawContentByFileBuffer = async ({
// markdown data format // markdown data format
if (['md', 'html', 'docx'].includes(extension)) { if (['md', 'html', 'docx'].includes(extension)) {
rawText = await initMarkdownText({ rawText = await matchMdImgTextAndUpload({
teamId: teamId, teamId: teamId,
md: rawText, md: rawText,
metadata: metadata metadata: metadata

View File

@@ -1,6 +1,9 @@
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose'; import mongoose, { Model } from 'mongoose';
export default mongoose;
export * from 'mongoose';
export const connectionMongo = (() => { export const connectionMongo = (() => {
if (!global.mongodb) { if (!global.mongodb) {
global.mongodb = mongoose; global.mongodb = mongoose;
@@ -9,9 +12,6 @@ export const connectionMongo = (() => {
return global.mongodb; return global.mongodb;
})(); })();
export default mongoose;
export * from 'mongoose';
const addCommonMiddleware = (schema: mongoose.Schema) => { const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [ const operations = [
/^find/, /^find/,
@@ -47,8 +47,6 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
if (duration > 1000) { if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData); addLog.warn(`Slow operation ${duration}ms`, warnLogData);
} else if (duration > 300) {
addLog.error(`Slow operation ${duration}ms`, warnLogData);
} }
} }
next(); next();
@@ -64,10 +62,13 @@ export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
addCommonMiddleware(schema); addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema); const model = connectionMongo.model<T>(name, schema);
try {
model.syncIndexes(); if (process.env.SYNC_INDEX !== '0') {
} catch (error) { try {
addLog.error('Create index error', error); model.syncIndexes({ background: true });
} catch (error) {
addLog.error('Create index error', error);
}
} }
return model; return model;

View File

@@ -1,3 +1,4 @@
import { delay } from '@fastgpt/global/common/system/utils';
import { addLog } from '../system/log'; import { addLog } from '../system/log';
import { connectionMongo } from './index'; import { connectionMongo } from './index';
import type { Mongoose } from 'mongoose'; import type { Mongoose } from 'mongoose';
@@ -7,26 +8,21 @@ const maxConnecting = Math.max(30, Number(process.env.DB_MAX_LINK || 20));
/** /**
* connect MongoDB and init data * connect MongoDB and init data
*/ */
export async function connectMongo({ export async function connectMongo(): Promise<Mongoose> {
beforeHook, /* Connecting, connected will return */
afterHook
}: {
beforeHook?: () => any;
afterHook?: () => Promise<any>;
}): Promise<Mongoose> {
if (connectionMongo.connection.readyState !== 0) { if (connectionMongo.connection.readyState !== 0) {
return connectionMongo; return connectionMongo;
} }
beforeHook && beforeHook();
console.log('mongo start connect'); console.log('mongo start connect');
try { try {
connectionMongo.set('strictQuery', true); connectionMongo.set('strictQuery', true);
connectionMongo.connection.on('error', (error) => { connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error); console.log('mongo error', error);
connectionMongo.disconnect(); await connectionMongo.disconnect();
await delay(1000);
connectMongo();
}); });
connectionMongo.connection.on('disconnected', () => { connectionMongo.connection.on('disconnected', () => {
console.log('mongo disconnected'); console.log('mongo disconnected');
@@ -50,16 +46,11 @@ export async function connectMongo({
}); });
console.log('mongo connected'); console.log('mongo connected');
return connectionMongo;
} catch (error) { } catch (error) {
connectionMongo.disconnect();
addLog.error('mongo connect error', error); addLog.error('mongo connect error', error);
await connectionMongo.disconnect();
await delay(1000);
return connectMongo();
} }
try {
afterHook && (await afterHook());
} catch (error) {
addLog.error('mongo connect after hook error', error);
}
return connectionMongo;
} }

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