Compare commits

...

10 Commits

Author SHA1 Message Date
Archer
a63467d751 优化建议模式;修复多索引训练失败问题 (#1043)
* doc

* Optimize possible null Pointers and parts of Ux

* fix: mulity index training error
2024-03-21 19:48:57 +08:00
Archer
9d27de154b 4.7-alpha2 (#1027)
* feat: stop toolCall and rename some field. (#46)

* perf: node delete tip;pay tip

* fix: toolCall cannot save child answer

* feat: stop tool

* fix: team modal

* fix feckbackMoal  auth bug (#47)

* 简单的支持提示词运行tool。优化workflow模板 (#49)

* remove templates

* fix: request body undefined

* feat: prompt tool run

* feat: workflow tamplates modal

* perf: plugin start

* 4.7 (#50)

* fix docker-compose download url (#994)

original code is a bad url with '404 NOT FOUND' return.
fix docker-compose download url, add 'v' before docker-compose version

* Update ai_settings.md (#1000)

* Update configuration.md

* Update configuration.md

* Fix history in classifyQuestion and extract modules (#1012)

* Fix history in classifyQuestion and extract modules

* Add chatValue2RuntimePrompt import and update text formatting

* flow controller to packages

* fix: rerank select

* modal ui

* perf: modal code path

* point not sufficient

* feat: http url support variable

* fix http key

* perf: prompt

* perf: ai setting modal

* simple edit ui

---------

Co-authored-by: entorick <entorick11@qq.com>
Co-authored-by: liujianglc <liujianglc@163.com>
Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com>

* fix team share redirect to login (#51)

* feat: support openapi import plugins (#48)

* feat: support openapi import plugins

* feat: import from url

* fix: add body params parse

* fix build

* fix

* fix

* fix

* tool box ui (#52)

* fix: training queue

* feat: simple edit tool select

* perf: simple edit dataset prompt

* fix: chatbox tool ux

* feat: quote prompt module

* perf: plugin tools sign

* perf: model avatar

* tool selector ui

* feat: max histories

* perf: http plugin import (#53)

* perf: plugin http import

* chatBox ui

* perf: name

* fix: Node template card (#54)

* fix: ts

* setting modal

* package

* package

* feat: add plugins search (#57)

* feat: add plugins search

* perf: change http plugin header input

* Yjl (#56)

* perf: prompt tool call

* perf: chat box ux

* doc

* doc

* price tip

* perf: tool selector

* ui'

* fix: vector queue

* fix: empty tool and empty response

* fix: empty msg

* perf: pg index

* perf: ui tip

* doc

* tool tip

---------

Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com>
Co-authored-by: entorick <entorick11@qq.com>
Co-authored-by: liujianglc <liujianglc@163.com>
Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com>
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-03-21 13:32:31 +08:00
Fengrui Liu
6d4b331db9 Fix history in classifyQuestion and extract modules (#1012)
* Fix history in classifyQuestion and extract modules

* Add chatValue2RuntimePrompt import and update text formatting
2024-03-18 19:47:40 +08:00
Archer
a6bb554fa4 Update configuration.md 2024-03-17 09:51:20 +08:00
Archer
f5b0d60ff1 Update configuration.md 2024-03-16 22:28:16 +08:00
liujianglc
d62f5b4f25 Update ai_settings.md (#1000) 2024-03-15 09:47:14 +08:00
entorick
029a2f8090 fix docker-compose download url (#994)
original code is a bad url with '404 NOT FOUND' return. 
fix docker-compose download url, add 'v' before docker-compose version
2024-03-14 14:43:27 +08:00
GooYoung
98834ece54 docs: Fixed misspelled key "usedInToolCall" (#992) 2024-03-14 12:52:15 +08:00
Archer
9501c3f3a1 V4.7-alpha (#985)
Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
2024-03-13 10:50:02 +08:00
Carson Yang
5bca15f12f Docs: fix HomePage (#977)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2024-03-12 13:51:30 +08:00
406 changed files with 15584 additions and 8973 deletions

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: 微信交流群
url: https://doc.fastgpt.in/wechat-fastgpt.webp
url: https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg
about: FastGPT 全是问题群

View File

@@ -58,7 +58,7 @@ fastgpt.run 域名会弃用。
- [x] 源文件引用追踪
- [x] 模块封装,实现多级复用
- [x] 混合检索 & 重排
- [ ] Tool 模块
- [x] Tool 模块
- [ ] 嵌入 [Laf](https://github.com/labring/laf),实现在线编写 HTTP 模块
- [ ] 插件封装功能
@@ -114,7 +114,7 @@ fastgpt.run 域名会弃用。
* [多模型配置](https://doc.fastgpt.in/docs/development/one-api/)
* [版本更新/升级介绍](https://doc.fastgpt.in/docs/development/upgrading)
* [OpenAPI API 文档](https://doc.fastgpt.in/docs/development/openapi/)
* [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/)
* [知识库结构详解](https://doc.fastgpt.in/docs/course/datasetengine/)
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
@@ -122,9 +122,9 @@ fastgpt.run 域名会弃用。
## 🏘️ 社区交流群
添加 wx 小助手加入:
wx 扫一下加入:
![](https://otnvvf-imgs.oss.laf.run/wx300.jpg)
![](https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg)
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">

View File

@@ -116,14 +116,12 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
- [Configuring Multiple Models](https://doc.fastgpt.in/docs/installation/reference/models)
- [Version Updates & Upgrades](https://doc.fastgpt.in/docs/installation/upgrading)
<!-- ## :point_right: RoadMap
- [FastGPT RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
<!-- ## 🏘️ Community
## 🏘️ Community
| Community Group | Assistant |
| ------------------------------------------------- | ---------------------------------------------- |
| ![](https://otnvvf-imgs.oss.laf.run/wxqun300.jpg) | ![](https://otnvvf-imgs.oss.laf.run/wx300.jpg) | -->
| Community Group |
| ------------------------------------------------- |
| ![](https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg) |
<a href="#readme">
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

View File

@@ -19,16 +19,17 @@ FastGPT 商业版是基于 FastGPT 开源版的增强版本,增加了一些独
| 应用管理与高级编排 | ✅ | ✅ | ✅ |
| 文档知识库 | ✅ | ✅ | ✅ |
| 外部使用 | ✅ | ✅ | ✅ |
| 自定义版权信息 | ❌ | ✅ | |
| 自定义版权信息 | ❌ | ✅ | 设计中 |
| 多租户与支付 | ❌ | ✅ | ✅ |
| 团队空间 | ❌ | ✅ | ✅ |
| 外部使用限制 | ❌ | ✅ | ✅ |
| 应用发布安全配置 | ❌ | ✅ | ✅ |
| 内容审核 | ❌ | ✅ | ✅ |
| web站点同步 | ❌ | ✅ | ✅ |
| 管理后台 | ❌ | ✅ | ✅ |
| Saas服务商业授权 | ❌ | ✅ | ✅ |
| 完整商业授权 | ❌ | ✅ | ✅ |
| 图片知识库 | ❌ | 设计中 | 设计中 |
| 自动规划召回 | ❌ | 设计中 | 设计中 |
| 对话日志运营分析 | ❌ | 设计中 | 设计中 |
{{< /table >}}
## 商业版软件价格

View File

@@ -4,7 +4,7 @@ description: "FastGPT AI 高级配置说明"
icon: "sign_language"
draft: false
toc: true
weight: 501
weight: 102
---
在 FastGPT 的 AI 对话模块中,有一个 AI 高级配置,里面包含了 AI 模型的参数配置,本文详细介绍这些配置的含义。
@@ -15,7 +15,7 @@ weight: 501
## 温度
可选范围0-10大代表生成的内容自由扩散越小代表约严谨。调节能力有限知识库问答场景通常设置为0。
可选范围0-10大代表生成的内容自由扩散越小代表约严谨。调节能力有限知识库问答场景通常设置为0。
## 回复上限
@@ -48,7 +48,7 @@ Tips: 可以通过点击上下文按键查看完整的上下文组成,便于
FastGPT 知识库采用 QA 对(不一定都是问答格式,仅代表两个变量)的格式存储,在转义成字符串时候会根据**引用模板**来进行格式化。知识库包含多个可用变量: q, a, sourceId数据的ID, index(第n个数据), source(数据的集合名、文件名)score(距离得分0-1) 可以通过 {{q}} {{a}} {{sourceId}} {{index}} {{source}} {{score}} 按需引入。下面一个模板例子:
可以通过 [知识库结构讲解](/docs/use-cases/datasetEngine/) 了解详细的知识库的结构。
可以通过 [知识库结构讲解](/docs/course/datasetEngine/) 了解详细的知识库的结构。
#### 引用模板

View File

@@ -4,7 +4,7 @@ description: "本节会详细介绍 FastGPT 知识库结构设计,理解其 QA
icon: "dataset"
draft: false
toc: true
weight: 502
weight: 102
---
## 理解向量
@@ -90,4 +90,4 @@ FastGPT 采用了 `PostgresSQL` 的 `PG Vector` 插件作为向量检索器,
## QA的组合与引用提示词构建
参考[引用模板与引用提示词示例](/docs/use-cases/ai_settings/#示例)
参考[引用模板与引用提示词示例](/docs/course/ai_settings/#示例)

View File

@@ -4,7 +4,7 @@ description: " 利用 FastGPT 打造高质量 AI 知识库"
icon: "school"
draft: false
toc: true
weight: 699
weight: 106
---
## 前言

View File

@@ -11,7 +11,7 @@ weight: 708
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
这个配置文件中包含了系统参数、AI 对话的模型、function 模型等……
这个配置文件中包含了系统参数和各个模型配置,`使用时务必去掉注释!!!!!!!!!!!!!!`
## 4.6.8+ 版本新配置文件
@@ -20,15 +20,15 @@ llm模型全部合并
```json
{
"systemEnv": {
"openapiPrefix": "fastgpt",
"vectorMaxProcess": 15,
"qaMaxProcess": 15,
"pgHNSWEfSearch": 100
"pgHNSWEfSearch": 100 // 向量搜索参数。越大搜索越精确但是速度越慢。设置为100有99%+精度。
},
"llmModels": [
{
"model": "gpt-3.5-turbo-1106", // 模型名
"model": "gpt-3.5-turbo", // 模型名
"name": "gpt-3.5-turbo", // 别名
"avatar": "/imgs/model/openai.svg", // 模型的logo
"maxContext": 16000, // 最大上下文
"maxResponse": 4000, // 最大回复
"quoteMaxToken": 13000, // 最大引用内容
@@ -36,35 +36,22 @@ llm模型全部合并
"charsPointsPrice": 0,
"censor": false,
"vision": false, // 是否支持图片输入
"datasetProcess": false, // 是否设置为知识库处理模型QA务必保证至少有一个为true否则知识库会报错
"toolChoice": true, // 是否支持工具选择
"functionCall": false, // 是否支持函数调
"datasetProcess": true, // 是否设置为知识库处理模型QA务必保证至少有一个为true否则知识库会报错
"usedInClassify": true, // 是否用于问题分类务必保证至少有一个为true
"usedInExtractFields": true, // 是否用于内容提取务必保证至少有一个为true
"usedInToolCall": true, // 是否用于工具调用务必保证至少有一个为true
"usedInQueryExtension": true, // 是否用于问题优化务必保证至少有一个为true
"toolChoice": true, // 是否支持工具选择分类内容提取工具调用会用到。目前只有gpt支持
"functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice如果为false则使用 functionCall如果仍为 false则使用提示词模式
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
"customExtractPrompt": "", // 自定义内容提取提示词
"defaultSystemChatPrompt": "", // 对话默认携带的系统提示词
"defaultConfig":{} // 对话默认配置(比如 GLM4 的 top_p
},
{
"model": "gpt-3.5-turbo-16k",
"name": "gpt-3.5-turbo-16k",
"maxContext": 16000,
"maxResponse": 16000,
"quoteMaxToken": 13000,
"maxTemperature": 1.2,
"charsPointsPrice": 0,
"censor": false,
"vision": false,
"datasetProcess": true,
"toolChoice": true,
"functionCall": false,
"customCQPrompt": "",
"customExtractPrompt": "",
"defaultSystemChatPrompt": "",
"defaultConfig":{}
"defaultConfig":{} // LLM默认配置,可以针对不同模型设置特殊值(比如 GLM4 的 top_p
},
{
"model": "gpt-4-0125-preview",
"name": "gpt-4-turbo",
"avatar": "/imgs/model/openai.svg",
"maxContext": 125000,
"maxResponse": 4000,
"quoteMaxToken": 100000,
@@ -73,6 +60,10 @@ llm模型全部合并
"censor": false,
"vision": false,
"datasetProcess": false,
"usedInClassify": true,
"usedInExtractFields": true,
"usedInToolCall": true,
"usedInQueryExtension": true,
"toolChoice": true,
"functionCall": false,
"customCQPrompt": "",
@@ -83,6 +74,7 @@ llm模型全部合并
{
"model": "gpt-4-vision-preview",
"name": "gpt-4-vision",
"avatar": "/imgs/model/openai.svg",
"maxContext": 128000,
"maxResponse": 4000,
"quoteMaxToken": 100000,
@@ -91,6 +83,10 @@ llm模型全部合并
"censor": false,
"vision": true,
"datasetProcess": false,
"usedInClassify": false,
"usedInExtractFields": false,
"usedInToolCall": false,
"usedInQueryExtension": false,
"toolChoice": true,
"functionCall": false,
"customCQPrompt": "",
@@ -103,6 +99,7 @@ llm模型全部合并
{
"model": "text-embedding-ada-002",
"name": "Embedding-2",
"avatar": "/imgs/model/openai.svg",
"charsPointsPrice": 0,
"defaultToken": 700,
"maxToken": 3000,
@@ -134,6 +131,20 @@ llm模型全部合并
}
```
## 关于模型 logo
统一放置在项目的`public/imgs/model/xxx`目录中目前内置了以下几种如果有需要可以PR增加。
- /imgs/model/baichuan.svg - 百川
- /imgs/model/chatglm.svg - 智谱
- /imgs/model/calude.svg - calude
- /imgs/model/ernie.svg - 文心一言
- /imgs/model/moonshot.svg - 月之暗面
- /imgs/model/openai.svg - OpenAI GPT
- /imgs/model/qwen.svg - 通义千问
- /imgs/model/yi.svg - 零一万物
-
## 特殊模型
### ReRank 接入

View File

@@ -45,7 +45,7 @@ FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、A
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
systemctl enable --now docker
# 安装 docker-compose
curl -L https://github.com/docker/compose/releases/download/2.20.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
curl -L https://github.com/docker/compose/releases/download/v2.20.3/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# 验证安装
docker -v
@@ -191,4 +191,4 @@ docker-compose down
docker-compose up -d
```
4. 进入容器执行副本集合初始化(看上方)
4. 进入容器执行副本集合初始化(看上方)

View File

@@ -1,13 +1,13 @@
---
weight: 749
title: "常见开发 & 部署问题"
description: "FastGPT 常见开发 & 部署问题"
title: "私有部署常见问题"
description: "FastGPT 私有部署常见问题"
icon: upgrade
draft: false
images: []
---
## 错误排查方式
## 一、错误排查方式
遇到问题先按下面方式排查。
@@ -17,7 +17,7 @@ images: []
4. 无法解决时,可以找找[Issue](https://github.com/labring/FastGPT/issues),或新提 Issue私有部署错误务必提供详细的日志否则很难排查。
## 通用问题
## 二、通用问题
### 能否纯本地运行
@@ -46,7 +46,7 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
### 页面崩溃
1. 关闭翻译
2. 检查配置文件是否正常加载如果没有正常加载会导致缺失系统信息在某些操作下会导致空指针。95%情况可以F12打开控制台看具体的空指针情况
2. 检查配置文件是否正常加载如果没有正常加载会导致缺失系统信息在某些操作下会导致空指针。95%情况是配置文件不对可以F12打开控制台看具体的空指针情况
3. 某些api不兼容问题较少
### 开启内容补全后,响应速度变慢
@@ -54,7 +54,11 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
1. 问题补全需要经过一轮AI生成。
2. 会进行3~5轮的查询如果数据库性能不足会有明显影响。
## 私有部署问题
### 模型响应为空
1. 检查 key 问题。
2. 如果是国内模型,可能是命中风控了。
3. 查看模型请求日志,检查出入参数是否异常。
### 知识库索引没有进度
@@ -64,11 +68,7 @@ OneAPI 的 API Key 配置错误,需要修改`OPENAI_API_KEY`环境变量,并
2. 不能对话也不能索引API调用失败。可能是没连上OneAPI或OpenAI
3. 有进度但是非常慢api key不行OpenAI的免费号一分钟只有3次还是60次。一天上限200次。
## Docker 部署常见问题
### 首次部署root用户提示未注册
没有启动 Mongo 副本集模式。
## 三、Docker 部署常见问题
### 如何更新?
@@ -133,14 +133,6 @@ mongo连接失败检查
2. 环境变量账号密码注意host和port
3. 副本集启动失败一直在重启没挂载mongo keykey没有权限
## 本地开发问题
### 首次部署root用户提示未注册
### TypeError: Cannot read properties of null (reading 'useMemo' )
删除所有的`node_modules`,用 Node18 重新 install 试试,可能最新的 Node 有问题。 本地开发流程:
1. 根目录: `pnpm i`
2. 复制 `config.json` -> `config.local.json`
3. 复制 `.env.template` -> `.env.local`
4. `cd projects/app`
5. `pnpm dev`
没有启动 Mongo 副本集模式。

View File

@@ -48,7 +48,7 @@ git clone git@github.com:<github_username>/FastGPT.git
第一次开发,需要先部署数据库,建议本地开发可以随便找一台 2C2G 的轻量小数据库实践。数据库部署教程:[Docker 快速部署](/docs/development/docker/)。部署完了,可以本地访问其数据库。
Mongo 数据库需要修改副本集的`host`,从原来的`mongo:27017`修改为`ip:27017`
Mongo 数据库需要修改副本集的`host`,从原来的`mongo:27017`修改为`ip:27017`(ip为对应的公网IP)
### 4. 初始配置
@@ -113,7 +113,22 @@ docker build -t dockername/fastgpt:tag --build-arg name=app --build-arg proxy=ta
FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI``Type`。如果没有权限,可以先执行`chmod -R +x ./scripts/`,再执行`pnpm i`
### 加入社区
### 长时间运行后崩溃
似乎是由于 tiktoken 库的开发环境问题,生产环境中未遇到,暂时可忽略。
### TypeError: Cannot read properties of null (reading 'useMemo' )
删除所有的`node_modules`,用 Node18 重新 install 试试,可能最新的 Node 有问题。 本地开发流程:
1. 根目录: `pnpm i`
2. 复制 `config.json` -> `config.local.json`
3. 复制 `.env.template` -> `.env.local`
4. `cd projects/app`
5. `pnpm dev`
## 加入社区
遇到困难了吗?有任何问题吗? 加入微信群与开发者和用户保持沟通。

View File

@@ -112,6 +112,7 @@ CHAT_API_KEY=sk-xxxxxx
{
"model": "ERNIE-Bot", // 这里的模型需要对应 One API 的模型
"name": "文心一言", // 对外展示的名称
"avatar": "/imgs/model/openai.svg", // 模型的logo
"maxContext": 16000, // 最大上下文
"maxResponse": 4000, // 最大回复
"quoteMaxToken": 13000, // 最大引用内容
@@ -120,6 +121,10 @@ CHAT_API_KEY=sk-xxxxxx
"censor": false,
"vision": false, // 是否支持图片输入
"datasetProcess": false, // 是否设置为知识库处理模型
"usedInClassify": true, // 是否用于问题分类
"usedInExtractFields": true, // 是否用于字段提取
"usedInToolCall": true, // 是否用于工具调用
"usedInQueryExtension": true, // 是否用于问题优化
"toolChoice": true, // 是否支持工具选择
"functionCall": false, // 是否支持函数调用
"customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型
@@ -131,4 +136,11 @@ CHAT_API_KEY=sk-xxxxxx
],
```
添加完后,重启 FastGPT 即可在选择文心一言模型进行对话。**添加向量模型也是类似操作,增加到 `vectorModels`里。**
### 3. 重启 FastGPT
```bash
docker-compose down
docker-compose up -d
```
重启 FastGPT 即可在选择文心一言模型进行对话。**添加向量模型也是类似操作,增加到 `vectorModels`里。**

View File

@@ -0,0 +1,39 @@
---
title: 'V4.7(进行中)'
description: 'FastGPT V4.7更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 826
---
## 修改配置文件
增加一些 Boolean 值,用于决定不同功能块可以使用哪些模型,同时增加了模型的 logo[点击查看最新的配置文件](/docs/development/configuration/)
## 初始化脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成自己域名
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv47' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
脚本功能:
1. 初始化插件的 parentId
## V4.7 更新说明
1. 新增 - 工具调用模块可以让LLM模型根据用户意图动态的选择其他模型或插件执行。
2. 新增 - 分类和内容提取支持 functionCall 模式。部分模型支持 functionCall 不支持 ToolCall也可以使用了。需要把 LLM 模型配置文件里的 `functionCall` 设置为 `true` `toolChoice`设置为 `false`。如果 `toolChoice` 为 true会走 tool 模式。
3. 新增 - HTTP插件可实现OpenAPI快速生成插件。
4. 优化 - 高级编排性能。
5. 优化 - 抽离 Flow controller 到 packages。
6. 优化 - AI模型选择。
7. 修复 - 开源版重排选不上。
8. 修复 - http 请求 body不使用时传入undefined。会造成部分GET请求失败
9. 新增 - 支持 http url 使用变量。
10. 修复 - 469 的提取的提示词容易造成幻觉。
11. 修复 - PG HNSW索引未实际生效问题本次更新后搜索速度大幅度提升(但是可能会出现精度损失如果出现精度损失需要参考PgVector文档对索引进行调整)。详细见https://github.com/pgvector/pgvector?tab=readme-ov-file#troubleshooting

View File

@@ -1,6 +1,6 @@
---
title: " 接入飞书 "
description: "FastGPT 接入飞书机器人 "
title: " 接入飞书(社区文章)"
description: "FastGPT 接入飞书机器人"
icon: "chat"
draft: false
toc: true

View File

@@ -0,0 +1,60 @@
---
title: "使用 Gapier 快速导入Agent工具"
description: "FastGPT 使用 Gapier 快速导入Agent工具"
icon: "build"
draft: false
toc: true
weight: 501
---
FastGPT V4.7版本加入了工具调用,可以兼容 GPTs 的 Actions。这意味着你可以直接导入兼容 GPTs 的 Agent 工具。
Gapier 是一个在线 GPTs Actions工具提供了50多种现成工具并且每天有免费额度进行测试方便用户试用官方地址为[https://gapier.com/](https://gapier.com/)。
![](/imgs/gapierToolResult1.png)
现在,我们开始把 Gapier 的工具导入到 FastGPT 中。
## 1. 创建插件
| Step1 | Step2 | Step3 |
| --- | --- | --- |
| ![](/imgs/gapierTool1.png) | ![](/imgs/gapierTool2.png) | 登录[Gapier](https://gapier.com/) 复制相关参数 <br> ![](/imgs/gapierTool3.png) |
| Step4 | Step5 | Step6 |
| 自定义请求头: Authorization<br>请求值: Bearer 复制的key <br> ![](/imgs/gapierTool4.png) | ![](/imgs/gapierTool5.png) | ![](/imgs/gapierTool6.png) |
创建完后,如果需要变更,无需重新创建,只需要修改对应参数即可,会自动做差值比较更新。
![](/imgs/gapierTool7.png)
## 2. 应用绑定工具
### 简易模式
| Step1 | Step2 |
| --- | --- | --- |
| ![](/imgs/gapierTool8.png) | ![](/imgs/gapierTool9.png) |
| Step3 | Step4 |
| ![](/imgs/gapierTool10.png) | ![](/imgs/gapierTool11.png) |
### 高级编排
| Step1 | Step2 |
| --- | --- | --- |
| ![](/imgs/gapierTool12.png) | ![](/imgs/gapierTool13.png) |
| Step3 | Step4 |
| ![](/imgs/gapierTool14.png) | ![](/imgs/gapierTool15.png) |
![](/imgs/gapierTool16.png)
## 3. 工具调用说明
### 不同模型的区别
不同模型调用工具采用不同的方法,有些模型支持 toolChoice 和 functionCall 效果会更好。不支持这两种方式的模型通过提示词调用但是效果不是很好并且为了保证顺利调用FastGPT内置的提示词仅支持每次调用一个工具。
具体哪些模型支持 functionCall 可以官网查看当然也需要OneAPI支持同时需要调整模型配置文件中的对应字段详细看配置字段说明
线上版用户,可以在模型选择时,看到是否支持函数调用的标识。
![](/imgs/gapierTool17.png)

View File

@@ -1,5 +1,5 @@
---
title: " 接入微信和企业微信 "
title: "接入微信和企业微信 "
description: "FastGPT 接入微信和企业微信 "
icon: "chat"
draft: false

View File

@@ -112,7 +112,7 @@ defaultContentLanguage = 'zh-cn'
# Link behaviour
intLinkTooltip = true # Enable a tooltip for internal links that displays info about the destination? default false
# extLinkNewTab = false # Open external links in a new Tab? default true
# logoLinkURL = "" # Set a custom URL destination for the top header logo link.
logoLinkURL = "https://fastgpt.in/" # Set a custom URL destination for the top header logo link.
[params.flexsearch] # Parameters for FlexSearch
# enabled = true

View File

@@ -6,7 +6,7 @@
"prepare": "husky install",
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
"format-doc": "zhlint --dir ./docSite *.md --fix",
"gen:theme-typings": "chakra-cli tokens projects/app/src/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",
"initIcon": "node ./scripts/icon/init.js",
"previewIcon": "node ./scripts/icon/index.js"

View File

@@ -15,7 +15,7 @@ export enum TeamErrEnum {
const teamErr = [
{ statusText: TeamErrEnum.teamOverSize, message: 'error.team.overSize' },
{ statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' },
{ statusText: TeamErrEnum.aiPointsNotEnough, message: 'AI积分已用完~' },
{ statusText: TeamErrEnum.aiPointsNotEnough, message: '' },
{ statusText: TeamErrEnum.datasetSizeNotEnough, message: '知识库容量不足,请先扩容~' },
{ statusText: TeamErrEnum.datasetAmountNotEnough, message: '知识库数量已达上限~' },
{ statusText: TeamErrEnum.appAmountNotEnough, message: '应用数量已达上限~' },

View File

@@ -1,7 +1,7 @@
import { replaceSensitiveText } from '../string/tools';
export const getErrText = (err: any, def = '') => {
const msg: string = typeof err === 'string' ? err : err?.message || def || '';
const msg: string = typeof err === 'string' ? err : err?.message ?? def;
msg && console.log('error =>', msg);
return replaceSensitiveText(msg);
};

View File

@@ -1,3 +1,4 @@
/* mongo fs bucket */
export enum BucketNameEnum {
dataset = 'dataset'
}
@@ -7,4 +8,4 @@ export const bucketNameMap = {
}
};
export const FileBaseUrl = '/api/common/file/read';
export const ReadFileBaseUrl = '/api/common/file/read';

View File

@@ -50,3 +50,7 @@ export const mongoImageTypeMap = {
export const uniqueImageTypeList = Object.entries(mongoImageTypeMap)
.filter(([key, value]) => value.unique)
.map(([key]) => key as `${MongoImageTypeEnum}`);
export const FolderIcon = 'file/fill/folder';
export const FolderImgUrl = '/imgs/files/folder.svg';
export const HttpImgUrl = '/imgs/module/http.png';

View File

@@ -1,10 +1,15 @@
/* Only the token of gpt-3.5-turbo is used */
import type { ChatItemType } from '../../../core/chat/type';
import { Tiktoken } from 'js-tiktoken/lite';
import { adaptChat2GptMessages } from '../../../core/chat/adapt';
import { ChatCompletionRequestMessageRoleEnum } from '../../../core/ai/constant';
import { chats2GPTMessages } from '../../../core/chat/adapt';
import encodingJson from './cl100k_base.json';
import { ChatMessageItemType } from '../../../core/ai/type';
import {
ChatCompletionMessageParam,
ChatCompletionContentPart,
ChatCompletionCreateParams,
ChatCompletionTool
} from '../../../core/ai/type';
import { ChatCompletionRequestMessageRoleEnum } from '../../../core/ai/constants';
/* init tikToken obj */
export function getTikTokenEnc() {
@@ -29,18 +34,25 @@ export function getTikTokenEnc() {
/* count one prompt tokens */
export function countPromptTokens(
prompt = '',
role: '' | `${ChatCompletionRequestMessageRoleEnum}` = '',
tools?: any
prompt: string | ChatCompletionContentPart[] | null | undefined = '',
role: '' | `${ChatCompletionRequestMessageRoleEnum}` = ''
) {
const enc = getTikTokenEnc();
const toolText = tools
? JSON.stringify(tools)
.replace('"', '')
.replace('\n', '')
.replace(/( ){2,}/g, ' ')
: '';
const text = `${role}\n${prompt}\n${toolText}`.trim();
const promptText = (() => {
if (!prompt) return '';
if (typeof prompt === 'string') return prompt;
let promptText = '';
prompt.forEach((item) => {
if (item.type === 'text') {
promptText += item.text;
} else if (item.type === 'image_url') {
promptText += item.image_url.url;
}
});
return promptText;
})();
const text = `${role}\n${promptText}`.trim();
try {
const encodeText = enc.encode(text);
@@ -50,15 +62,66 @@ export function countPromptTokens(
return text.length;
}
}
export const countToolsTokens = (
tools?: ChatCompletionTool[] | ChatCompletionCreateParams.Function[]
) => {
if (!tools || tools.length === 0) return 0;
const enc = getTikTokenEnc();
const toolText = tools
? JSON.stringify(tools)
.replace('"', '')
.replace('\n', '')
.replace(/( ){2,}/g, ' ')
: '';
return enc.encode(toolText).length;
};
/* count messages tokens */
export const countMessagesTokens = (messages: ChatItemType[], tools?: any) => {
const adaptMessages = adaptChat2GptMessages({ messages, reserveId: true });
export const countMessagesTokens = (messages: ChatItemType[]) => {
const adaptMessages = chats2GPTMessages({ messages, reserveId: true });
return countGptMessagesTokens(adaptMessages, tools);
return countGptMessagesTokens(adaptMessages);
};
export const countGptMessagesTokens = (messages: ChatMessageItemType[], tools?: any) =>
messages.reduce((sum, item) => sum + countPromptTokens(item.content, item.role, tools), 0);
export const countGptMessagesTokens = (
messages: ChatCompletionMessageParam[],
tools?: ChatCompletionTool[],
functionCall?: ChatCompletionCreateParams.Function[]
) =>
messages.reduce((sum, item) => {
// Evaluates the text of toolcall and functioncall
const functionCallPrompt = (() => {
let prompt = '';
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
const toolCalls = item.tool_calls;
prompt +=
toolCalls
?.map((item) => `${item?.function?.name} ${item?.function?.arguments}`.trim())
?.join('') || '';
const functionCall = item.function_call;
prompt += `${functionCall?.name} ${functionCall?.arguments}`.trim();
}
return prompt;
})();
const contentPrompt = (() => {
if (!item.content) return '';
if (typeof item.content === 'string') return item.content;
return item.content
.map((item) => {
if (item.type === 'text') return item.text;
return '';
})
.join('');
})();
return sum + countPromptTokens(`${contentPrompt}${functionCallPrompt}`, item.role);
}, 0) +
countToolsTokens(tools) +
countToolsTokens(functionCall);
/* slice messages from top to bottom by maxTokens */
export function sliceMessagesTB({
@@ -68,7 +131,7 @@ export function sliceMessagesTB({
messages: ChatItemType[];
maxTokens: number;
}) {
const adaptMessages = adaptChat2GptMessages({ messages, reserveId: true });
const adaptMessages = chats2GPTMessages({ messages, reserveId: true });
let reduceTokens = maxTokens;
let result: ChatItemType[] = [];

View File

@@ -1,2 +1,3 @@
export const HUMAN_ICON = `/icon/human.svg`;
export const LOGO_ICON = `/icon/logo.svg`;
export const HUGGING_FACE_ICON = `/imgs/model/huggingface.svg`;

View File

@@ -1,7 +0,0 @@
export enum ChatCompletionRequestMessageRoleEnum {
'System' = 'system',
'User' = 'user',
'Assistant' = 'assistant',
'Function' = 'function',
'Tool' = 'tool'
}

View File

@@ -0,0 +1,27 @@
export enum ChatCompletionRequestMessageRoleEnum {
'System' = 'system',
'User' = 'user',
'Assistant' = 'assistant',
'Function' = 'function',
'Tool' = 'tool'
}
export enum ChatMessageTypeEnum {
text = 'text',
image_url = 'image_url'
}
export enum LLMModelTypeEnum {
all = 'all',
classify = 'classify',
extractFields = 'extractFields',
toolCall = 'toolCall',
queryExtension = 'queryExtension'
}
export const llmModelTypeFilterMap = {
[LLMModelTypeEnum.all]: 'model',
[LLMModelTypeEnum.classify]: 'usedInClassify',
[LLMModelTypeEnum.extractFields]: 'usedInExtractFields',
[LLMModelTypeEnum.toolCall]: 'usedInToolCall',
[LLMModelTypeEnum.queryExtension]: 'usedInQueryExtension'
};

View File

@@ -1,6 +1,7 @@
export type LLMModelItemType = {
model: string;
name: string;
avatar?: string;
maxContext: number;
maxResponse: number;
quoteMaxToken: number;
@@ -10,7 +11,13 @@ export type LLMModelItemType = {
censor?: boolean;
vision?: boolean;
datasetProcess?: boolean;
// diff function model
datasetProcess?: boolean; // dataset
usedInClassify?: boolean; // classify
usedInExtractFields?: boolean; // extract fields
usedInToolCall?: boolean; // tool call
usedInQueryExtension?: boolean; // query extension
functionCall: boolean;
toolChoice: boolean;
@@ -25,6 +32,7 @@ export type LLMModelItemType = {
export type VectorModelItemType = {
model: string;
name: string;
avatar?: string;
defaultToken: number;
charsPointsPrice: number;
maxToken: number;

View File

@@ -1,4 +1,4 @@
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
import { PromptTemplateItem } from '../type.d';
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{

View File

@@ -58,5 +58,3 @@ Human"{{question}}"
ID=
`;
export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;

View File

@@ -1,20 +1,33 @@
import openai from 'openai';
import type {
ChatCompletion,
ChatCompletionCreateParams,
ChatCompletionMessageToolCall,
ChatCompletionChunk,
ChatCompletionMessageParam,
ChatCompletionContentPart
ChatCompletionToolMessageParam,
ChatCompletionAssistantMessageParam
} from 'openai/resources';
import { ChatMessageTypeEnum } from './constants';
export type ChatCompletionContentPart = ChatCompletionContentPart;
export type ChatCompletionCreateParams = ChatCompletionCreateParams;
export type ChatMessageItemType = Omit<ChatCompletionMessageParam, 'name'> & {
name?: any;
export * from 'openai/resources';
export type ChatCompletionMessageParam = ChatCompletionMessageParam & {
dataId?: string;
content: any;
} & any;
};
export type ChatCompletionToolMessageParam = ChatCompletionToolMessageParam & { name: string };
export type ChatCompletionAssistantToolParam = {
role: 'assistant';
tool_calls: ChatCompletionMessageToolCall[];
};
export type ChatCompletion = ChatCompletion;
export type ChatCompletionMessageToolCall = ChatCompletionMessageToolCall & {
toolName?: string;
toolAvatar?: string;
};
export type ChatCompletionMessageFunctionCall = ChatCompletionAssistantMessageParam.FunctionCall & {
id?: string;
toolName?: string;
toolAvatar?: string;
};
export type StreamChatType = Stream<ChatCompletionChunk>;
export type PromptTemplateItem = {
@@ -22,3 +35,6 @@ export type PromptTemplateItem = {
desc: string;
value: string;
};
export default openai;
export * from 'openai';

View File

@@ -12,16 +12,9 @@ export type CreateAppParams = {
export interface AppUpdateParams {
name?: string;
type?: `${AppTypeEnum}`;
simpleTemplateId?: string;
avatar?: string;
intro?: string;
modules?: AppSchema['modules'];
permission?: AppSchema['permission'];
teamTags?: AppSchema['teamTags'];
}
export type FormatForm2ModulesProps = {
formData: AppSimpleEditFormType;
chatModelMaxToken: number;
llmModelList: LLMModelItemType[];
};

View File

@@ -1,7 +1,12 @@
import type { AppTTSConfigType, ModuleItemType, VariableItemType } from '../module/type.d';
import type {
AppTTSConfigType,
FlowNodeTemplateType,
ModuleItemType,
VariableItemType
} from '../module/type.d';
import { AppTypeEnum } from './constants';
import { PermissionTypeEnum } from '../../support/permission/constant';
import type { AIChatModuleProps, DatasetModuleProps } from '../module/node/type.d';
import type { DatasetModuleProps } from '../module/node/type.d';
import { VariableInputEnum } from '../module/constants';
import { SelectedDatasetType } from '../module/api';
import { DatasetSearchModeEnum } from '../dataset/constants';
@@ -13,7 +18,6 @@ export interface AppSchema {
tmbId: string;
name: string;
type: `${AppTypeEnum}`;
simpleTemplateId: string;
avatar: string;
intro: string;
updateTime: number;
@@ -37,19 +41,6 @@ export type AppDetailType = AppSchema & {
canWrite: boolean;
};
// export type AppSimpleEditFormType = {
// aiSettings: AIChatModuleProps;
// dataset: DatasetModuleProps & {
// searchEmptyText: string;
// };
// userGuide: {
// welcomeText: string;
// variables: VariableItemType[];
// questionGuide: boolean;
// tts: AppTTSConfigType;
// };
// };
// Since useform cannot infer enumeration types, all enumeration keys can only be undone manually
export type AppSimpleEditFormType = {
// templateId: string;
aiSettings: {
@@ -58,8 +49,7 @@ export type AppSimpleEditFormType = {
temperature: number;
maxToken: number;
isResponseAnswerText: boolean;
quoteTemplate?: string | undefined;
quotePrompt?: string | undefined;
maxHistories: number;
};
dataset: {
datasets: SelectedDatasetType;
@@ -67,11 +57,11 @@ export type AppSimpleEditFormType = {
similarity?: number;
limit?: number;
usingReRank?: boolean;
searchEmptyText?: string;
datasetSearchUsingExtensionQuery?: boolean;
datasetSearchExtensionModel?: string;
datasetSearchExtensionBg?: string;
};
selectedTools: FlowNodeTemplateType[];
userGuide: {
welcomeText: string;
variables: {
@@ -94,34 +84,3 @@ export type AppSimpleEditFormType = {
};
};
};
/* simple mode template*/
export type AppSimpleEditConfigTemplateType = {
id: string;
name: string;
desc: string;
systemForm: {
aiSettings?: {
model?: boolean;
systemPrompt?: boolean;
temperature?: boolean;
maxToken?: boolean;
quoteTemplate?: boolean;
quotePrompt?: boolean;
};
dataset?: {
datasets?: boolean;
similarity?: boolean;
limit?: boolean;
searchMode: `${DatasetSearchModeEnum}`;
usingReRank: boolean;
searchEmptyText?: boolean;
};
userGuide?: {
welcomeText?: boolean;
variables?: boolean;
questionGuide?: boolean;
tts?: boolean;
};
};
};

View File

@@ -1,6 +1,10 @@
import type { AppSimpleEditFormType } from '../app/type';
import { FlowNodeTypeEnum } from '../module/node/constant';
import { ModuleOutputKeyEnum, ModuleInputKeyEnum } from '../module/constants';
import {
ModuleOutputKeyEnum,
ModuleInputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../module/constants';
import type { FlowNodeInputItemType } from '../module/node/type.d';
import { getGuideModule, splitGuideModule } from '../module/utils';
import { ModuleItemType } from '../module/type.d';
@@ -13,20 +17,19 @@ export const getDefaultAppForm = (): AppSimpleEditFormType => {
systemPrompt: '',
temperature: 0,
isResponseAnswerText: true,
quotePrompt: '',
quoteTemplate: '',
maxHistories: 6,
maxToken: 4000
},
dataset: {
datasets: [],
similarity: 0.4,
limit: 1500,
searchEmptyText: '',
searchMode: DatasetSearchModeEnum.embedding,
usingReRank: false,
datasetSearchUsingExtensionQuery: true,
datasetSearchExtensionBg: ''
},
selectedTools: [],
userGuide: {
welcomeText: '',
variables: [],
@@ -47,7 +50,10 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => {
};
modules.forEach((module) => {
if (module.flowType === FlowNodeTypeEnum.chatNode) {
if (
module.flowType === FlowNodeTypeEnum.chatNode ||
module.flowType === FlowNodeTypeEnum.tools
) {
defaultAppForm.aiSettings.model = findInputValueByKey(
module.inputs,
ModuleInputKeyEnum.aiModel
@@ -64,13 +70,9 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => {
module.inputs,
ModuleInputKeyEnum.aiChatMaxToken
);
defaultAppForm.aiSettings.quoteTemplate = findInputValueByKey(
defaultAppForm.aiSettings.maxHistories = findInputValueByKey(
module.inputs,
ModuleInputKeyEnum.aiChatQuoteTemplate
);
defaultAppForm.aiSettings.quotePrompt = findInputValueByKey(
module.inputs,
ModuleInputKeyEnum.aiChatQuotePrompt
ModuleInputKeyEnum.history
);
} else if (module.flowType === FlowNodeTypeEnum.datasetSearchNode) {
defaultAppForm.dataset.datasets = findInputValueByKey(
@@ -104,17 +106,6 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => {
module.inputs,
ModuleInputKeyEnum.datasetSearchExtensionBg
);
// empty text
const emptyOutputs =
module.outputs.find((item) => item.key === ModuleOutputKeyEnum.datasetIsEmpty)?.targets ||
[];
const emptyOutput = emptyOutputs[0];
if (emptyOutput) {
const target = modules.find((item) => item.moduleId === emptyOutput.moduleId);
defaultAppForm.dataset.searchEmptyText =
target?.inputs?.find((item) => item.key === ModuleInputKeyEnum.answerText)?.value || '';
}
} else if (module.flowType === FlowNodeTypeEnum.userGuide) {
const { welcomeText, variableModules, questionGuide, ttsConfig } = splitGuideModule(
getGuideModule(modules)
@@ -125,6 +116,18 @@ export const appModules2Form = ({ modules }: { modules: ModuleItemType[] }) => {
questionGuide: questionGuide,
tts: ttsConfig
};
} else if (module.flowType === FlowNodeTypeEnum.pluginModule) {
defaultAppForm.selectedTools.push({
id: module.inputs.find((input) => input.key === ModuleInputKeyEnum.pluginId)?.value || '',
name: module.name,
avatar: module.avatar,
intro: module.intro || '',
flowType: module.flowType,
showStatus: module.showStatus,
inputs: module.inputs,
outputs: module.outputs,
templateType: FlowNodeTemplateTypeEnum.other
});
}
});

View File

@@ -1,40 +1,299 @@
import type { ChatItemType } from '../../core/chat/type.d';
import { ChatRoleEnum } from '../../core/chat/constants';
import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constant';
import type { ChatMessageItemType } from '../../core/ai/type.d';
import type {
ChatItemType,
ChatItemValueItemType,
RuntimeUserPromptType,
UserChatItemType
} from '../../core/chat/type.d';
import { ChatFileTypeEnum, ChatItemValueTypeEnum, ChatRoleEnum } from '../../core/chat/constants';
import type {
ChatCompletionContentPart,
ChatCompletionFunctionMessageParam,
ChatCompletionMessageFunctionCall,
ChatCompletionMessageParam,
ChatCompletionMessageToolCall,
ChatCompletionToolMessageParam
} from '../../core/ai/type.d';
import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants';
const chat2Message = {
[ChatRoleEnum.AI]: ChatCompletionRequestMessageRoleEnum.Assistant,
[ChatRoleEnum.Human]: ChatCompletionRequestMessageRoleEnum.User,
[ChatRoleEnum.System]: ChatCompletionRequestMessageRoleEnum.System,
[ChatRoleEnum.Function]: ChatCompletionRequestMessageRoleEnum.Function,
[ChatRoleEnum.Tool]: ChatCompletionRequestMessageRoleEnum.Tool
};
const message2Chat = {
const GPT2Chat = {
[ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System,
[ChatCompletionRequestMessageRoleEnum.User]: ChatRoleEnum.Human,
[ChatCompletionRequestMessageRoleEnum.Assistant]: ChatRoleEnum.AI,
[ChatCompletionRequestMessageRoleEnum.Function]: ChatRoleEnum.Function,
[ChatCompletionRequestMessageRoleEnum.Tool]: ChatRoleEnum.Tool
[ChatCompletionRequestMessageRoleEnum.Function]: ChatRoleEnum.AI,
[ChatCompletionRequestMessageRoleEnum.Tool]: ChatRoleEnum.AI
};
export function adaptRole_Chat2Message(role: `${ChatRoleEnum}`) {
return chat2Message[role];
}
export function adaptRole_Message2Chat(role: `${ChatCompletionRequestMessageRoleEnum}`) {
return message2Chat[role];
return GPT2Chat[role];
}
export const adaptChat2GptMessages = ({
export const simpleUserContentPart = (content: ChatCompletionContentPart[]) => {
if (content.length === 1 && content[0].type === 'text') {
return content[0].text;
}
return content;
};
export const chats2GPTMessages = ({
messages,
reserveId
reserveId,
reserveTool = false
}: {
messages: ChatItemType[];
reserveId: boolean;
}): ChatMessageItemType[] => {
return messages.map((item) => ({
...(reserveId && { dataId: item.dataId }),
role: chat2Message[item.obj],
content: item.value || ''
}));
reserveTool?: boolean;
}): ChatCompletionMessageParam[] => {
let results: ChatCompletionMessageParam[] = [];
messages.forEach((item) => {
const dataId = reserveId ? item.dataId : undefined;
if (item.obj === ChatRoleEnum.Human) {
const value = item.value
.map((item) => {
if (item.type === ChatItemValueTypeEnum.text) {
return {
type: 'text',
text: item.text?.content || ''
};
}
if (item.type === 'file' && item.file?.type === ChatFileTypeEnum.image) {
return {
type: 'image_url',
image_url: {
url: item.file?.url || ''
}
};
}
return;
})
.filter(Boolean) as ChatCompletionContentPart[];
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.User,
content: simpleUserContentPart(value)
});
} else if (item.obj === ChatRoleEnum.System) {
const content = item.value?.[0]?.text?.content;
if (content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.System,
content
});
}
} else {
//AI
item.value.forEach((value) => {
if (value.type === ChatItemValueTypeEnum.tool && value.tools && reserveTool) {
const tool_calls: ChatCompletionMessageToolCall[] = [];
const toolResponse: ChatCompletionToolMessageParam[] = [];
value.tools.forEach((tool) => {
tool_calls.push({
id: tool.id,
type: 'function',
function: {
name: tool.functionName,
arguments: tool.params
}
});
toolResponse.push({
tool_call_id: tool.id,
role: ChatCompletionRequestMessageRoleEnum.Tool,
name: tool.functionName,
content: tool.response
});
});
results = results
.concat({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls
})
.concat(toolResponse);
} else if (value.text) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: value.text.content
});
}
});
}
});
return results;
};
export const GPTMessages2Chats = (
messages: ChatCompletionMessageParam[],
reserveTool = true
): ChatItemType[] => {
return messages
.map((item) => {
const value: ChatItemType['value'] = [];
const obj = GPT2Chat[item.role];
if (
obj === ChatRoleEnum.System &&
item.role === ChatCompletionRequestMessageRoleEnum.System
) {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
} else if (
obj === ChatRoleEnum.Human &&
item.role === ChatCompletionRequestMessageRoleEnum.User
) {
if (typeof item.content === 'string') {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
} else if (Array.isArray(item.content)) {
item.content.forEach((item) => {
if (item.type === 'text') {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.text
}
});
} else if (item.type === 'image_url') {
value.push({
//@ts-ignore
type: 'file',
file: {
type: ChatFileTypeEnum.image,
name: '',
url: item.image_url.url
}
});
}
});
// @ts-ignore
}
} else if (
obj === ChatRoleEnum.AI &&
item.role === ChatCompletionRequestMessageRoleEnum.Assistant
) {
if (item.content && typeof item.content === 'string') {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: item.content
}
});
} else if (item.tool_calls && reserveTool) {
// save tool calls
const toolCalls = item.tool_calls as ChatCompletionMessageToolCall[];
value.push({
//@ts-ignore
type: ChatItemValueTypeEnum.tool,
tools: toolCalls.map((tool) => {
let toolResponse =
messages.find(
(msg) =>
msg.role === ChatCompletionRequestMessageRoleEnum.Tool &&
msg.tool_call_id === tool.id
)?.content || '';
toolResponse =
typeof toolResponse === 'string' ? toolResponse : JSON.stringify(toolResponse);
return {
id: tool.id,
toolName: tool.toolName || '',
toolAvatar: tool.toolAvatar || '',
functionName: tool.function.name,
params: tool.function.arguments,
response: toolResponse as string
};
})
});
} else if (item.function_call && reserveTool) {
const functionCall = item.function_call as ChatCompletionMessageFunctionCall;
const functionResponse = messages.find(
(msg) =>
msg.role === ChatCompletionRequestMessageRoleEnum.Function &&
msg.name === item.function_call?.name
) as ChatCompletionFunctionMessageParam;
if (functionResponse) {
value.push({
//@ts-ignore
type: ChatItemValueTypeEnum.tool,
tools: [
{
id: functionCall.id || '',
toolName: functionCall.toolName || '',
toolAvatar: functionCall.toolAvatar || '',
functionName: functionCall.name,
params: functionCall.arguments,
response: functionResponse.content || ''
}
]
});
}
}
}
return {
dataId: item.dataId,
obj,
value
} as ChatItemType;
})
.filter((item) => item.value.length > 0);
};
export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): RuntimeUserPromptType => {
const prompt: RuntimeUserPromptType = {
files: [],
text: ''
};
value.forEach((item) => {
if (item.type === 'file' && item.file) {
prompt.files?.push(item.file);
} else if (item.text) {
prompt.text += item.text.content;
}
});
return prompt;
};
export const runtimePrompt2ChatsValue = (
prompt: RuntimeUserPromptType
): UserChatItemType['value'] => {
const value: UserChatItemType['value'] = [];
if (prompt.files) {
prompt.files.forEach((file) => {
value.push({
type: ChatItemValueTypeEnum.file,
file
});
});
}
if (prompt.text) {
value.push({
type: ChatItemValueTypeEnum.text,
text: {
content: prompt.text
}
});
}
return value;
};
export const getSystemPrompt = (prompt?: string): ChatItemType[] => {
if (!prompt) return [];
return [
{
obj: ChatRoleEnum.System,
value: [{ type: ChatItemValueTypeEnum.text, text: { content: prompt } }]
}
];
};

View File

@@ -3,6 +3,8 @@ export type UpdateChatFeedbackProps = {
chatId: string;
chatItemId: string;
shareId?: string;
teamId?: string;
teamToken?: string;
outLinkUid?: string;
userBadFeedback?: string;
userGoodFeedback?: string;

View File

@@ -1,28 +1,30 @@
export enum ChatRoleEnum {
System = 'System',
Human = 'Human',
AI = 'AI',
Function = 'Function',
Tool = 'Tool'
AI = 'AI'
}
export const ChatRoleMap = {
[ChatRoleEnum.System]: {
name: '系统提示词'
name: '系统'
},
[ChatRoleEnum.Human]: {
name: '用户'
},
[ChatRoleEnum.AI]: {
name: 'AI'
},
[ChatRoleEnum.Function]: {
name: 'Function'
},
[ChatRoleEnum.Tool]: {
name: 'Tool'
}
};
export enum ChatFileTypeEnum {
image = 'image',
file = 'file'
}
export enum ChatItemValueTypeEnum {
text = 'text',
file = 'file',
tool = 'tool'
}
export enum ChatSourceEnum {
test = 'test',
online = 'online',

View File

@@ -1,11 +1,20 @@
import { ClassifyQuestionAgentItemType } from '../module/type';
import { SearchDataResponseItemType } from '../dataset/type';
import { ChatRoleEnum, ChatSourceEnum, ChatStatusEnum } from './constants';
import {
ChatFileTypeEnum,
ChatItemValueTypeEnum,
ChatRoleEnum,
ChatSourceEnum,
ChatStatusEnum
} from './constants';
import { FlowNodeTypeEnum } from '../module/node/constant';
import { ModuleOutputKeyEnum } from '../module/constants';
import { DispatchNodeResponseKeyEnum } from '../module/runtime/constants';
import { AppSchema } from '../app/type';
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import { DatasetSearchModeEnum } from '../dataset/constants';
import { ChatBoxInputType } from '../../../../projects/app/src/components/ChatBox/type';
import { DispatchNodeResponseType } from '../module/runtime/type.d';
export type ChatSchema = {
_id: string;
@@ -30,7 +39,53 @@ export type ChatWithAppSchema = Omit<ChatSchema, 'appId'> & {
appId: AppSchema;
};
export type ChatItemSchema = {
export type UserChatItemValueItemType = {
type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.file;
text?: {
content: string;
};
file?: {
type: `${ChatFileTypeEnum}`;
name?: string;
url: string;
};
};
export type UserChatItemType = {
obj: ChatRoleEnum.Human;
value: UserChatItemValueItemType[];
};
export type SystemChatItemValueItemType = {
type: ChatItemValueTypeEnum.text;
text?: {
content: string;
};
};
export type SystemChatItemType = {
obj: ChatRoleEnum.System;
value: SystemChatItemValueItemType[];
};
export type AIChatItemValueItemType = {
type: ChatItemValueTypeEnum.text | ChatItemValueTypeEnum.tool;
text?: {
content: string;
};
tools?: ToolModuleResponseItemType[];
};
export type AIChatItemType = {
obj: ChatRoleEnum.AI;
value: AIChatItemValueItemType[];
userGoodFeedback?: string;
userBadFeedback?: string;
customFeedbacks?: string[];
adminFeedback?: AdminFbkType;
[DispatchNodeResponseKeyEnum.nodeResponse]?: ChatHistoryItemResType[];
};
export type ChatItemValueItemType =
| UserChatItemValueItemType
| SystemChatItemValueItemType
| AIChatItemValueItemType;
export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
dataId: string;
chatId: string;
userId: string;
@@ -38,13 +93,6 @@ export type ChatItemSchema = {
tmbId: string;
appId: string;
time: Date;
obj: `${ChatRoleEnum}`;
value: string;
userGoodFeedback?: string;
userBadFeedback?: string;
customFeedbacks?: string[];
adminFeedback?: AdminFbkType;
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
};
export type AdminFbkType = {
@@ -56,22 +104,16 @@ export type AdminFbkType = {
};
/* --------- chat item ---------- */
export type ChatItemType = {
export type ChatItemType = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
dataId?: string;
obj: ChatItemSchema['obj'];
value: any;
userGoodFeedback?: string;
userBadFeedback?: string;
customFeedbacks?: ChatItemSchema['customFeedbacks'];
adminFeedback?: ChatItemSchema['feedback'];
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
};
export type ChatSiteItemType = ChatItemType & {
export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
dataId?: string;
status: `${ChatStatusEnum}`;
moduleName?: string;
ttsBuffer?: Uint8Array;
};
} & ChatBoxInputType;
/* --------- team chat --------- */
export type ChatAppListSchema = {
@@ -93,60 +135,25 @@ export type ChatHistoryItemType = HistoryItemType & {
};
/* ------- response data ------------ */
export type moduleDispatchResType = {
// common
moduleLogo?: string;
runningTime?: number;
query?: string;
textOutput?: string;
// bill
tokens?: number;
model?: string;
contextTotalLen?: number;
totalPoints?: number;
// chat
temperature?: number;
maxToken?: number;
quoteList?: SearchDataResponseItemType[];
historyPreview?: ChatItemType[]; // completion context array. history will slice
// dataset search
similarity?: number;
limit?: number;
searchMode?: `${DatasetSearchModeEnum}`;
searchUsingReRank?: boolean;
extensionModel?: string;
extensionResult?: string;
extensionTokens?: number;
// cq
cqList?: ClassifyQuestionAgentItemType[];
cqResult?: string;
// content extract
extractDescription?: string;
extractResult?: Record<string, any>;
// http
params?: Record<string, any>;
body?: Record<string, any>;
headers?: Record<string, any>;
httpResult?: Record<string, any>;
// plugin output
pluginOutput?: Record<string, any>;
pluginDetail?: ChatHistoryItemResType[];
// tf switch
tfSwitchResult?: boolean;
// abandon
tokens?: number;
};
export type ChatHistoryItemResType = moduleDispatchResType & {
export type ChatHistoryItemResType = DispatchNodeResponseType & {
moduleType: `${FlowNodeTypeEnum}`;
moduleName: string;
};
/* One tool run response */
export type ToolRunResponseItemType = any;
/* tool module response */
export type ToolModuleResponseItemType = {
id: string;
toolName: string; // tool name
toolAvatar: string;
params: string; // tool params
response: string;
functionName: string;
};
/* dispatch run time */
export type RuntimeUserPromptType = {
files?: UserChatItemValueItemType['file'][];
text: string;
};

View File

@@ -1,6 +1,79 @@
import { IMG_BLOCK_KEY, FILE_BLOCK_KEY } from './constants';
import { DispatchNodeResponseType } from '../module/runtime/type';
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../module/node/constant';
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
import { ChatHistoryItemResType, ChatItemType } from './type.d';
export function chatContentReplaceBlock(content: string = '') {
const regex = new RegExp(`\`\`\`(${IMG_BLOCK_KEY})\\n([\\s\\S]*?)\`\`\``, 'g');
return content.replace(regex, '').trim();
}
export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => {
// @ts-ignore
const textMsg = message?.value.find((item) => item.type === ChatItemValueTypeEnum.text);
if (textMsg?.text?.content) {
return textMsg.text.content.slice(0, 20);
}
return defaultValue;
};
export const getHistoryPreview = (
completeMessages: ChatItemType[]
): {
obj: `${ChatRoleEnum}`;
value: string;
}[] => {
return completeMessages.map((item, i) => {
if (item.obj === ChatRoleEnum.System || i >= completeMessages.length - 2) {
return {
obj: item.obj,
value: item.value?.[0]?.text?.content || ''
};
}
const content = item.value
.map((item) => {
if (item.text?.content) {
const content =
item.text.content.length > 20
? `${item.text.content.slice(0, 20)}...`
: item.text.content;
return content;
}
return '';
})
.filter(Boolean)
.join('\n');
return {
obj: item.obj,
value: content
};
});
};
export const filterPublicNodeResponseData = ({
flowResponses = []
}: {
flowResponses?: ChatHistoryItemResType[];
}) => {
const filedList = ['quoteList', 'moduleType'];
const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools
];
return flowResponses
.filter((item) => filterModuleTypeList.includes(item.moduleType))
.map((item) => {
const obj: DispatchNodeResponseType = {};
for (let key in item) {
if (key === 'toolDetail' || key === 'pluginDetail') {
// @ts-ignore
obj[key] = filterPublicNodeResponseData({ flowResponses: item[key] });
} else if (filedList.includes(key)) {
// @ts-ignore
obj[key] = item[key];
}
}
return obj as ChatHistoryItemResType;
});
};

View File

@@ -83,17 +83,17 @@ export const TrainingTypeMap = {
[TrainingModeEnum.chunk]: {
label: 'core.dataset.training.Chunk mode',
tooltip: 'core.dataset.import.Chunk Split Tip',
isPlus: true
openSource: true
},
[TrainingModeEnum.auto]: {
label: 'core.dataset.training.Auto mode',
tooltip: 'core.dataset.training.Auto mode Tip',
isPlus: true
openSource: false
},
[TrainingModeEnum.qa]: {
label: 'core.dataset.training.QA mode',
tooltip: 'core.dataset.import.QA Import Tip',
isPlus: true
openSource: true
}
};
@@ -154,8 +154,5 @@ export const SearchScoreTypeMap = {
}
};
export const FolderIcon = 'file/fill/folder';
export const FolderImgUrl = '/imgs/files/folder.svg';
export const CustomCollectionIcon = 'common/linkBlue';
export const LinkCollectionIcon = 'common/linkBlue';

View File

@@ -13,3 +13,10 @@ export type HttpQueryType = {
variables: Record<string, any>;
[key: string]: any;
};
/* http node */
export type HttpParamAndHeaderItemType = {
key: string;
type: string;
value: string;
};

View File

@@ -1,4 +1,4 @@
export enum ModuleTemplateTypeEnum {
export enum FlowNodeTemplateTypeEnum {
userGuide = 'userGuide',
systemInput = 'systemInput',
tools = 'tools',
@@ -21,7 +21,10 @@ export enum ModuleIOValueTypeEnum {
// plugin special type
selectApp = 'selectApp',
selectDataset = 'selectDataset'
selectDataset = 'selectDataset',
// tool
tools = 'tools'
}
/* reg: modulename key */
@@ -84,17 +87,16 @@ export enum ModuleInputKeyEnum {
runAppSelectApp = 'app',
// plugin
pluginId = 'pluginId'
pluginId = 'pluginId',
pluginStart = 'pluginStart'
}
export enum ModuleOutputKeyEnum {
// common
responseData = 'responseData',
moduleDispatchBills = 'moduleDispatchBills',
userChatInput = 'userChatInput',
finish = 'finish',
history = 'history',
answerText = 'answerText', // answer module text key
answerText = 'answerText', // module answer. the value will be show and save to history
success = 'success',
failed = 'failed',
text = 'system_text',
@@ -110,7 +112,16 @@ export enum ModuleOutputKeyEnum {
// tf switch
resultTrue = 'system_resultTrue',
resultFalse = 'system_resultFalse'
resultFalse = 'system_resultFalse',
// tools
selectedTools = 'selectedTools',
// http
httpRawResponse = 'httpRawResponse',
// plugin
pluginStart = 'pluginStart'
}
export enum VariableInputEnum {

View File

@@ -21,10 +21,12 @@ export enum FlowNodeInputTypeEnum {
// ai model select
selectLLMModel = 'selectLLMModel',
settingLLMModel = 'settingLLMModel',
// dataset special input
selectDataset = 'selectDataset',
selectDatasetParamsModal = 'selectDatasetParamsModal',
settingDatasetQuotePrompt = 'settingDatasetQuotePrompt',
hidden = 'hidden',
custom = 'custom'
@@ -56,7 +58,9 @@ export enum FlowNodeTypeEnum {
pluginModule = 'pluginModule',
pluginInput = 'pluginInput',
pluginOutput = 'pluginOutput',
queryExtension = 'cfr'
queryExtension = 'cfr',
tools = 'tools',
stopTool = 'stopTool'
// abandon
}

View File

@@ -2,6 +2,7 @@ import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants';
import { SelectedDatasetType } from '../api';
import { EditInputFieldMap, EditOutputFieldMap } from './type';
import { LLMModelTypeEnum } from '../../ai/constants';
export type FlowNodeChangeProps = {
moduleId: string;
@@ -28,6 +29,7 @@ export type FlowNodeInputItemType = {
label: string;
description?: string;
required?: boolean;
toolDescription?: string; // If this field is not empty, it is entered as a tool
edit?: boolean; // Whether to allow editing
editField?: EditInputFieldMap;
@@ -49,6 +51,8 @@ export type FlowNodeInputItemType = {
step?: number; // slider
max?: number; // slider, number input
min?: number; // slider, number input
llmModelType?: `${LLMModelTypeEnum}`;
};
export type FlowNodeOutputTargetItemType = {
@@ -62,6 +66,8 @@ export type FlowNodeOutputItemType = {
label?: string;
description?: string;
required?: boolean;
defaultValue?: any;
edit?: boolean;
editField?: EditOutputFieldMap;
@@ -74,12 +80,14 @@ export type FlowNodeOutputItemType = {
export type EditInputFieldMap = EditOutputFieldMap & {
inputType?: boolean;
required?: boolean;
isToolInput?: boolean;
};
export type EditOutputFieldMap = {
name?: boolean;
key?: boolean;
description?: boolean;
dataType?: boolean;
defaultValue?: boolean;
};
export type EditNodeFieldType = {
inputType?: `${FlowNodeInputTypeEnum}`; // input type
@@ -89,9 +97,18 @@ export type EditNodeFieldType = {
label?: string;
description?: string;
valueType?: `${ModuleIOValueTypeEnum}`;
isToolInput?: boolean;
defaultValue?: string;
};
/* ------------- item type --------------- */
export type SettingAIDataType = {
model: string;
temperature: number;
maxToken: number;
isResponseAnswerText?: boolean;
maxHistories?: number;
};
/* ai chat modules props */
export type AIChatModuleProps = {
[ModuleInputKeyEnum.aiModel]: string;

View File

@@ -0,0 +1,19 @@
export enum SseResponseEventEnum {
error = 'error',
answer = 'answer', // animation stream
fastAnswer = 'fastAnswer', // direct answer text, not animation
flowNodeStatus = 'flowNodeStatus', // update node status
toolCall = 'toolCall', // tool start
toolParams = 'toolParams', // tool params return
toolResponse = 'toolResponse', // tool response return
flowResponses = 'flowResponses' // sse response request
}
export enum DispatchNodeResponseKeyEnum {
nodeResponse = 'responseData', // run node response
nodeDispatchUsages = 'nodeDispatchUsages', // the node bill.
childrenResponses = 'childrenResponses', // Some nodes make recursive calls that need to be returned
toolResponses = 'toolResponses', // The result is passed back to the tool node for use
assistantResponses = 'assistantResponses' // assistant response
}

View File

@@ -0,0 +1,102 @@
import { ChatNodeUsageType } from '../../../support/wallet/bill/type';
import { ChatItemValueItemType, ToolRunResponseItemType } from '../../chat/type';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../node/type';
import { ModuleItemType } from '../type';
import { DispatchNodeResponseKeyEnum } from './constants';
export type RunningModuleItemType = {
name: ModuleItemType['name'];
avatar: ModuleItemType['avatar'];
intro?: ModuleItemType['intro'];
moduleId: ModuleItemType['moduleId'];
flowType: ModuleItemType['flowType'];
showStatus?: ModuleItemType['showStatus'];
isEntry?: ModuleItemType['isEntry'];
inputs: {
key: string;
value?: any;
valueType?: FlowNodeInputItemType['valueType'];
required?: boolean;
toolDescription?: string;
}[];
outputs: {
key: string;
required?: boolean;
defaultValue?: any;
answer?: boolean;
response?: boolean;
value?: any;
valueType?: FlowNodeOutputItemType['valueType'];
targets: {
moduleId: string;
key: string;
}[];
}[];
};
export type DispatchNodeResponseType = {
// common
moduleLogo?: string;
runningTime?: number;
query?: string;
textOutput?: string;
// bill
tokens?: number;
model?: string;
contextTotalLen?: number;
totalPoints?: number;
// chat
temperature?: number;
maxToken?: number;
quoteList?: SearchDataResponseItemType[];
historyPreview?: {
obj: `${ChatRoleEnum}`;
value: string;
}[]; // completion context array. history will slice
// dataset search
similarity?: number;
limit?: number;
searchMode?: `${DatasetSearchModeEnum}`;
searchUsingReRank?: boolean;
extensionModel?: string;
extensionResult?: string;
extensionTokens?: number;
// cq
cqList?: ClassifyQuestionAgentItemType[];
cqResult?: string;
// content extract
extractDescription?: string;
extractResult?: Record<string, any>;
// http
params?: Record<string, any>;
body?: Record<string, any>;
headers?: Record<string, any>;
httpResult?: Record<string, any>;
// plugin output
pluginOutput?: Record<string, any>;
pluginDetail?: ChatHistoryItemResType[];
// tf switch
tfSwitchResult?: boolean;
// tool
toolCallTokens?: number;
toolDetail?: ChatHistoryItemResType[];
toolStop?: boolean;
};
export type DispatchNodeResultType<T> = {
[DispatchNodeResponseKeyEnum.nodeResponse]?: DispatchNodeResponseType; // The node response detail
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]?: ChatNodeUsageType[]; //
[DispatchNodeResponseKeyEnum.childrenResponses]?: DispatchNodeResultType[];
[DispatchNodeResponseKeyEnum.toolResponses]?: ToolRunResponseItemType;
[DispatchNodeResponseKeyEnum.assistantResponses]?: ChatItemValueItemType[];
} & T;

View File

@@ -0,0 +1,31 @@
import { ChatCompletionRequestMessageRoleEnum } from '../../ai/constants';
export const textAdaptGptResponse = ({
text,
model = '',
finish_reason = null,
extraData = {}
}: {
model?: string;
text: string | null;
finish_reason?: null | 'stop';
extraData?: Object;
}) => {
return JSON.stringify({
...extraData,
id: '',
object: '',
created: 0,
model,
choices: [
{
delta:
text === null
? {}
: { role: ChatCompletionRequestMessageRoleEnum.Assistant, content: text },
index: 0,
finish_reason
}
]
});
};

View File

@@ -0,0 +1,120 @@
import { UserGuideModule } from './system/userGuide';
import { UserInputModule } from './system/userInput';
import { AiChatModule } from './system/aiChat';
import { DatasetSearchModule } from './system/datasetSearch';
import { DatasetConcatModule } from './system/datasetConcat';
import { AssignedAnswerModule } from './system/assignedAnswer';
import { ClassifyQuestionModule } from './system/classifyQuestion';
import { ContextExtractModule } from './system/contextExtract';
import { HttpModule468 } from './system/http468';
import { HttpModule } from './system/abandon/http';
import { ToolModule } from './system/tools';
import { StopToolNode } from './system/stopTool';
import { RunAppModule } from './system/runApp';
import { PluginInputModule } from './system/pluginInput';
import { PluginOutputModule } from './system/pluginOutput';
import { RunPluginModule } from './system/runPlugin';
import { AiQueryExtension } from './system/queryExtension';
import type { FlowNodeTemplateType, moduleTemplateListType } from '../../module/type.d';
import { FlowNodeTemplateTypeEnum } from '../../module/constants';
/* app flow module templates */
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
UserGuideModule,
UserInputModule,
AiChatModule,
AssignedAnswerModule,
DatasetSearchModule,
DatasetConcatModule,
RunAppModule,
ToolModule,
StopToolNode,
ClassifyQuestionModule,
ContextExtractModule,
HttpModule468,
AiQueryExtension
];
/* plugin flow module templates */
export const pluginSystemModuleTemplates: FlowNodeTemplateType[] = [
PluginInputModule,
PluginOutputModule,
AiChatModule,
AssignedAnswerModule,
DatasetSearchModule,
DatasetConcatModule,
RunAppModule,
ToolModule,
StopToolNode,
ClassifyQuestionModule,
ContextExtractModule,
HttpModule468,
AiQueryExtension
];
/* all module */
export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
UserGuideModule,
UserInputModule,
AiChatModule,
DatasetSearchModule,
DatasetConcatModule,
AssignedAnswerModule,
ClassifyQuestionModule,
ContextExtractModule,
HttpModule468,
HttpModule,
ToolModule,
StopToolNode,
AiChatModule,
RunAppModule,
PluginInputModule,
PluginOutputModule,
RunPluginModule,
AiQueryExtension
];
export const moduleTemplatesList: moduleTemplateListType = [
{
type: FlowNodeTemplateTypeEnum.userGuide,
label: '',
list: []
},
{
type: FlowNodeTemplateTypeEnum.textAnswer,
label: 'core.module.template.Response module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.functionCall,
label: 'core.module.template.Function module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.tools,
label: 'core.module.template.Tool module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.externalCall,
label: 'core.module.template.External module',
list: []
},
{
type: FlowNodeTemplateTypeEnum.personalPlugin,
label: '',
list: []
},
{
type: FlowNodeTemplateTypeEnum.other,
label: '其他',
list: []
},
{
type: FlowNodeTemplateTypeEnum.systemInput,
label: 'core.module.template.System input module',
list: []
}
];

View File

@@ -2,6 +2,7 @@ import type { FlowNodeInputItemType } from '../node/type.d';
import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '../constants';
import { FlowNodeInputTypeEnum } from '../node/constant';
import { ModuleIOValueTypeEnum } from '../constants';
import { chatNodeSystemPromptTip } from './tip';
export const Input_Template_Switch: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.switch,
@@ -58,9 +59,40 @@ export const Input_Template_DynamicInput: FlowNodeInputItemType = {
hideInApp: true
};
export const Input_Template_SelectAIModel: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectLLMModel,
label: 'core.module.input.label.aiModel',
required: true,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
};
export const Input_Template_SettingAiModel: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.settingLLMModel,
label: 'core.module.input.label.aiModel',
required: true,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
};
export const Input_Template_System_Prompt: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.aiSystemPrompt,
type: FlowNodeInputTypeEnum.textarea,
max: 3000,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.ai.Prompt',
description: chatNodeSystemPromptTip,
placeholder: chatNodeSystemPromptTip,
showTargetInApp: true,
showTargetInPlugin: true
};
export const Input_Template_Dataset_Quote: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.aiChatDatasetQuote,
type: FlowNodeInputTypeEnum.target,
type: FlowNodeInputTypeEnum.settingDatasetQuotePrompt,
label: '知识库引用',
description: 'core.module.Dataset quote.Input description',
valueType: ModuleIOValueTypeEnum.datasetQuote,

View File

@@ -3,11 +3,11 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../../node/constant';
import { FlowModuleTemplateType } from '../../../type';
import { FlowNodeTemplateType } from '../../../type';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../../constants';
import {
Input_Template_AddInputParam,
@@ -16,9 +16,9 @@ import {
} from '../../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../../output';
export const HttpModule: FlowModuleTemplateType = {
export const HttpModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.httpRequest,
templateType: ModuleTemplateTypeEnum.externalCall,
templateType: FlowNodeTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.httpRequest,
avatar: '/imgs/module/http.png',
name: 'core.module.template.Http request',

View File

@@ -3,41 +3,36 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import {
Input_Template_SettingAiModel,
Input_Template_Dataset_Quote,
Input_Template_History,
Input_Template_Switch,
Input_Template_System_Prompt,
Input_Template_UserChatInput
} from '../input';
import { chatNodeSystemPromptTip } from '../tip';
import { Output_Template_Finish, Output_Template_UserChatInput } from '../output';
export const AiChatModule: FlowModuleTemplateType = {
export const AiChatModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.chatNode,
templateType: ModuleTemplateTypeEnum.textAnswer,
templateType: FlowNodeTemplateTypeEnum.textAnswer,
flowType: FlowNodeTypeEnum.chatNode,
avatar: '/imgs/module/AI.png',
name: 'core.module.template.Ai chat',
intro: 'core.module.template.Ai chat intro',
name: 'AI 对话',
intro: 'AI 大模型对话',
showStatus: true,
isTool: true,
inputs: [
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectLLMModel,
label: 'core.module.input.label.aiModel',
required: true,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
},
Input_Template_SettingAiModel,
// --- settings modal
{
key: ModuleInputKeyEnum.aiChatTemperature,
@@ -88,28 +83,15 @@ export const AiChatModule: FlowModuleTemplateType = {
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.aiChatSettingModal,
type: FlowNodeInputTypeEnum.aiSettings,
label: '',
valueType: ModuleIOValueTypeEnum.any,
showTargetInApp: false,
showTargetInPlugin: false
},
// settings modal ---
{
key: ModuleInputKeyEnum.aiSystemPrompt,
type: FlowNodeInputTypeEnum.textarea,
...Input_Template_System_Prompt,
label: 'core.ai.Prompt',
max: 300,
valueType: ModuleIOValueTypeEnum.string,
description: chatNodeSystemPromptTip,
placeholder: chatNodeSystemPromptTip,
showTargetInApp: true,
showTargetInPlugin: true
placeholder: chatNodeSystemPromptTip
},
Input_Template_History,
Input_Template_UserChatInput,
{ ...Input_Template_UserChatInput, toolDescription: '用户问题' },
Input_Template_Dataset_Quote
],
outputs: [

View File

@@ -1,16 +1,21 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
import { Input_Template_Switch } from '../input';
import { Output_Template_Finish } from '../output';
export const AssignedAnswerModule: FlowModuleTemplateType = {
export const AssignedAnswerModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.answerNode,
templateType: ModuleTemplateTypeEnum.textAnswer,
templateType: FlowNodeTemplateTypeEnum.textAnswer,
flowType: FlowNodeTypeEnum.answerNode,
avatar: '/imgs/module/reply.png',
name: 'core.module.template.Assigned reply',
intro: 'core.module.template.Assigned reply intro',
name: '指定回复',
intro:
'该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。',
inputs: [
Input_Template_Switch,
{

View File

@@ -3,43 +3,41 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
import {
Input_Template_SelectAIModel,
Input_Template_History,
Input_Template_Switch,
Input_Template_UserChatInput
} from '../input';
import { Output_Template_UserChatInput } from '../output';
import { Input_Template_System_Prompt } from '../input';
import { LLMModelTypeEnum } from '../../../ai/constants';
export const ClassifyQuestionModule: FlowModuleTemplateType = {
export const ClassifyQuestionModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.classifyQuestion,
templateType: ModuleTemplateTypeEnum.functionCall,
templateType: FlowNodeTemplateTypeEnum.functionCall,
flowType: FlowNodeTypeEnum.classifyQuestion,
avatar: '/imgs/module/cq.png',
name: 'core.module.template.Classify question',
intro: `core.module.template.Classify question intro`,
name: '问题分类',
intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品“使用”问题\n类型3: 关于商品“购买”问题\n类型4: 其他问题`,
showStatus: true,
inputs: [
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectLLMModel,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.module.input.label.Classify model',
required: true,
showTargetInApp: false,
showTargetInPlugin: false
...Input_Template_SelectAIModel,
llmModelType: LLMModelTypeEnum.classify
},
{
key: ModuleInputKeyEnum.aiSystemPrompt,
type: FlowNodeInputTypeEnum.textarea,
valueType: ModuleIOValueTypeEnum.string,
...Input_Template_System_Prompt,
label: 'core.module.input.label.Background',
description: 'core.module.input.description.Background',
placeholder: 'core.module.input.placeholder.Classify background',
showTargetInApp: true,
showTargetInPlugin: true
placeholder: 'core.module.input.placeholder.Classify background'
},
Input_Template_History,
Input_Template_UserChatInput,

View File

@@ -3,33 +3,34 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import { Input_Template_History, Input_Template_Switch } from '../input';
import {
Input_Template_SelectAIModel,
Input_Template_History,
Input_Template_Switch
} from '../input';
import { LLMModelTypeEnum } from '../../../ai/constants';
export const ContextExtractModule: FlowModuleTemplateType = {
export const ContextExtractModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.contentExtract,
templateType: ModuleTemplateTypeEnum.functionCall,
templateType: FlowNodeTemplateTypeEnum.functionCall,
flowType: FlowNodeTypeEnum.contentExtract,
avatar: '/imgs/module/extract.png',
name: 'core.module.template.Extract field',
intro: 'core.module.template.Extract field intro',
name: '文本内容提取',
intro: '可从文本中提取指定的数据例如sql语句、搜索关键词、代码等',
showStatus: true,
isTool: true,
inputs: [
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectLLMModel,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.module.input.label.LLM',
required: true,
showTargetInApp: false,
showTargetInPlugin: false
...Input_Template_SelectAIModel,
llmModelType: LLMModelTypeEnum.extractFields
},
{
key: ModuleInputKeyEnum.description,
@@ -38,7 +39,6 @@ export const ContextExtractModule: FlowModuleTemplateType = {
label: '提取要求描述',
description:
'给AI一些对应的背景知识或要求描述引导AI更好的完成任务。\n该输入框可使用全局变量。',
required: true,
placeholder:
'例如: \n1. 当前时间为: {{cTime}}。你是一个实验室预约助手,你的任务是帮助用户预约实验室,从文本中获取对应的预约信息。\n2. 你是谷歌搜索助手,需要从文本中提取出合适的搜索词。',
showTargetInApp: true,
@@ -52,7 +52,8 @@ export const ContextExtractModule: FlowModuleTemplateType = {
required: true,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: true,
showTargetInPlugin: true
showTargetInPlugin: true,
toolDescription: '需要检索的内容'
},
{
key: ModuleInputKeyEnum.extractKeys,

View File

@@ -3,12 +3,12 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import { Input_Template_Dataset_Quote, Input_Template_Switch } from '../input';
import { Output_Template_Finish } from '../output';
@@ -20,13 +20,13 @@ export const getOneQuoteInputTemplate = (key = getNanoid()) => ({
type: FlowNodeInputTypeEnum.hidden
});
export const DatasetConcatModule: FlowModuleTemplateType = {
export const DatasetConcatModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.datasetConcatNode,
flowType: FlowNodeTypeEnum.datasetConcatNode,
templateType: ModuleTemplateTypeEnum.tools,
templateType: FlowNodeTemplateTypeEnum.other,
avatar: '/imgs/module/concat.svg',
name: '知识库搜索引用合并',
intro: 'core.module.template.Dataset search result concat intro',
intro: '可以将多个知识库搜索结果进行合并输出。使用 RRF 的合并方式进行最终排序输出。',
showStatus: false,
inputs: [
Input_Template_Switch,

View File

@@ -3,25 +3,26 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import { Input_Template_Switch, Input_Template_UserChatInput } from '../input';
import { Output_Template_Finish, Output_Template_UserChatInput } from '../output';
import { DatasetSearchModeEnum } from '../../../dataset/constants';
export const DatasetSearchModule: FlowModuleTemplateType = {
export const DatasetSearchModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.datasetSearchNode,
templateType: ModuleTemplateTypeEnum.functionCall,
templateType: FlowNodeTemplateTypeEnum.functionCall,
flowType: FlowNodeTypeEnum.datasetSearchNode,
avatar: '/imgs/module/db.png',
name: 'core.module.template.Dataset search',
intro: 'core.module.template.Dataset search intro',
name: '知识库搜索',
intro: '调用知识库搜索能力,查找“有可能”与问题相关的内容',
showStatus: true,
isTool: true,
inputs: [
Input_Template_Switch,
{
@@ -97,7 +98,10 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
showTargetInPlugin: false,
value: ''
},
Input_Template_UserChatInput
{
...Input_Template_UserChatInput,
toolDescription: '需要检索的内容'
}
],
outputs: [
Output_Template_UserChatInput,

View File

@@ -3,12 +3,12 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type';
import { FlowNodeTemplateType } from '../../type';
import {
DYNAMIC_INPUT_KEY,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleTemplateTypeEnum
ModuleOutputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
import {
Input_Template_AddInputParam,
@@ -17,14 +17,15 @@ import {
} from '../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../output';
export const HttpModule468: FlowModuleTemplateType = {
export const HttpModule468: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.httpRequest468,
templateType: ModuleTemplateTypeEnum.externalCall,
templateType: FlowNodeTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.httpRequest468,
avatar: '/imgs/module/http.png',
name: 'core.module.template.Http request',
intro: 'core.module.template.Http request intro',
name: 'HTTP 请求',
intro: '可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
showStatus: true,
isTool: true,
inputs: [
Input_Template_Switch,
{
@@ -86,7 +87,6 @@ export const HttpModule468: FlowModuleTemplateType = {
editField: {
key: true,
description: true,
required: true,
dataType: true
},
defaultEditField: {
@@ -94,19 +94,27 @@ export const HttpModule468: FlowModuleTemplateType = {
key: '',
description: '',
inputType: FlowNodeInputTypeEnum.target,
valueType: ModuleIOValueTypeEnum.string,
required: true
valueType: ModuleIOValueTypeEnum.string
}
}
],
outputs: [
Output_Template_Finish,
{
key: ModuleOutputKeyEnum.httpRawResponse,
label: '原始响应',
description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。',
valueType: ModuleIOValueTypeEnum.any,
type: FlowNodeOutputTypeEnum.source,
targets: []
},
{
...Output_Template_AddOutput,
editField: {
key: true,
description: true,
dataType: true
dataType: true,
defaultValue: true
},
defaultEditField: {
label: '',

View File

@@ -1,15 +1,43 @@
import { ModuleTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
FlowNodeTemplateTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum
} from '../../constants';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowNodeTemplateType } from '../../type.d';
export const PluginInputModule: FlowModuleTemplateType = {
export const PluginInputModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginInput,
templateType: ModuleTemplateTypeEnum.systemInput,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowType: FlowNodeTypeEnum.pluginInput,
avatar: '/imgs/module/input.png',
name: '定义插件输入',
intro: '自定义配置外部输入,使用插件时,仅暴露自定义配置的输入',
showStatus: false,
inputs: [],
outputs: []
inputs: [
{
key: ModuleInputKeyEnum.pluginStart,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleIOValueTypeEnum.boolean,
label: '插件开始运行',
description:
'插件开始运行时,会输出一个 True 的标识。有时候,插件不会有额外的的输入,为了顺利的进入下一个阶段,你可以将该值连接到下一个节点的触发器中。',
showTargetInApp: true,
showTargetInPlugin: true
}
],
outputs: [
{
key: ModuleOutputKeyEnum.pluginStart,
label: '插件开始运行',
type: FlowNodeOutputTypeEnum.source,
valueType: ModuleIOValueTypeEnum.boolean,
targets: []
}
]
};

View File

@@ -1,10 +1,10 @@
import { ModuleTemplateTypeEnum } from '../../constants';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
export const PluginOutputModule: FlowModuleTemplateType = {
export const PluginOutputModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginOutput,
templateType: ModuleTemplateTypeEnum.systemInput,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowType: FlowNodeTypeEnum.pluginOutput,
avatar: '/imgs/module/output.png',
name: '定义插件输出',

View File

@@ -3,38 +3,36 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type';
import { FlowNodeTemplateType } from '../../type';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import {
Input_Template_History,
Input_Template_Switch,
Input_Template_UserChatInput
Input_Template_UserChatInput,
Input_Template_SelectAIModel
} from '../input';
import { Output_Template_UserChatInput } from '../output';
import { LLMModelTypeEnum } from '../../../ai/constants';
export const AiQueryExtension: FlowModuleTemplateType = {
export const AiQueryExtension: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.chatNode,
templateType: ModuleTemplateTypeEnum.other,
templateType: FlowNodeTemplateTypeEnum.other,
flowType: FlowNodeTypeEnum.queryExtension,
avatar: '/imgs/module/cfr.svg',
name: 'core.module.template.Query extension',
intro: 'core.module.template.Query extension intro',
name: '问题优化',
intro:
'使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。',
showStatus: true,
inputs: [
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectLLMModel,
label: 'core.module.input.label.aiModel',
required: true,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
...Input_Template_SelectAIModel,
llmModelType: LLMModelTypeEnum.queryExtension
},
{
key: ModuleInputKeyEnum.aiSystemPrompt,

View File

@@ -3,12 +3,12 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
import {
Input_Template_History,
@@ -17,13 +17,13 @@ import {
} from '../input';
import { Output_Template_Finish, Output_Template_UserChatInput } from '../output';
export const RunAppModule: FlowModuleTemplateType = {
export const RunAppModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.runApp,
templateType: ModuleTemplateTypeEnum.externalCall,
templateType: FlowNodeTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.runApp,
avatar: '/imgs/module/app.png',
name: 'core.module.template.Running app',
intro: 'core.module.template.Running app intro',
name: '应用调用',
intro: '可以选择一个其他应用进行调用',
showStatus: true,
inputs: [
Input_Template_Switch,
@@ -52,7 +52,7 @@ export const RunAppModule: FlowModuleTemplateType = {
},
{
key: ModuleOutputKeyEnum.answerText,
label: 'AI回复',
label: '回复的文本',
description: '将在应用完全结束后触发',
valueType: ModuleIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,

View File

@@ -1,14 +1,15 @@
import { ModuleTemplateTypeEnum } from '../../constants';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
export const RunPluginModule: FlowModuleTemplateType = {
export const RunPluginModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.pluginModule,
templateType: ModuleTemplateTypeEnum.externalCall,
templateType: FlowNodeTemplateTypeEnum.externalCall,
flowType: FlowNodeTypeEnum.pluginModule,
intro: '',
name: '',
showStatus: false,
isTool: true,
inputs: [], // [{key:'pluginId'},...]
outputs: []
};

View File

@@ -0,0 +1,16 @@
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowNodeTemplateType } from '../../type.d';
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { Input_Template_Switch } from '../input';
export const StopToolNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.stopTool,
templateType: FlowNodeTemplateTypeEnum.functionCall,
flowType: FlowNodeTypeEnum.stopTool,
avatar: '/imgs/module/toolStop.svg',
name: '工具调用终止',
intro:
'该模块需配置工具调用使用。当该模块被执行时本次工具调用将会强制结束并且不再调用AI针对工具调用结果回答问题。',
inputs: [Input_Template_Switch],
outputs: []
};

View File

@@ -0,0 +1,81 @@
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleOutputKeyEnum,
FlowNodeTemplateTypeEnum,
ModuleInputKeyEnum
} from '../../constants';
import {
Input_Template_SettingAiModel,
Input_Template_History,
Input_Template_Switch,
Input_Template_System_Prompt,
Input_Template_UserChatInput
} from '../input';
import { chatNodeSystemPromptTip } from '../tip';
import { Output_Template_Finish, Output_Template_UserChatInput } from '../output';
import { LLMModelTypeEnum } from '../../../ai/constants';
export const ToolModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.tools,
flowType: FlowNodeTypeEnum.tools,
templateType: FlowNodeTemplateTypeEnum.functionCall,
avatar: '/imgs/module/tool.svg',
name: '工具调用(实验)',
intro: '通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。',
showStatus: true,
inputs: [
Input_Template_Switch,
{
...Input_Template_SettingAiModel,
llmModelType: LLMModelTypeEnum.all
},
{
key: ModuleInputKeyEnum.aiChatTemperature,
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
label: '',
value: 0,
valueType: ModuleIOValueTypeEnum.number,
min: 0,
max: 10,
step: 1,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.aiChatMaxToken,
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
label: '',
value: 2000,
valueType: ModuleIOValueTypeEnum.number,
min: 100,
max: 4000,
step: 50,
showTargetInApp: false,
showTargetInPlugin: false
},
{
...Input_Template_System_Prompt,
label: 'core.ai.Prompt',
description: chatNodeSystemPromptTip,
placeholder: chatNodeSystemPromptTip
},
Input_Template_History,
Input_Template_UserChatInput
],
outputs: [
Output_Template_UserChatInput,
{
key: ModuleOutputKeyEnum.selectedTools,
valueType: ModuleIOValueTypeEnum.tools,
type: FlowNodeOutputTypeEnum.hidden,
targets: []
},
Output_Template_Finish
]
};

View File

@@ -1,14 +1,18 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import { userGuideTip } from '../tip';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
FlowNodeTemplateTypeEnum
} from '../../constants';
export const UserGuideModule: FlowModuleTemplateType = {
export const UserGuideModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.userGuide,
templateType: ModuleTemplateTypeEnum.userGuide,
templateType: FlowNodeTemplateTypeEnum.userGuide,
flowType: FlowNodeTypeEnum.userGuide,
avatar: '/imgs/module/userGuide.png',
name: 'core.module.template.User guide',
name: '全局配置',
intro: userGuideTip,
inputs: [
{

View File

@@ -3,21 +3,21 @@ import {
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { FlowNodeTemplateType } from '../../type.d';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
FlowNodeTemplateTypeEnum
} from '../../constants';
export const UserInputModule: FlowModuleTemplateType = {
export const UserInputModule: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.questionInput,
templateType: ModuleTemplateTypeEnum.systemInput,
templateType: FlowNodeTemplateTypeEnum.systemInput,
flowType: FlowNodeTypeEnum.questionInput,
avatar: '/imgs/module/userChatInput.svg',
name: 'core.module.template.Chat entrance',
intro: 'core.module.template.Chat entrance intro',
name: '对话入口',
intro: '当用户发送一个内容后,流程将会从这个模块开始执行。',
inputs: [
{
key: ModuleInputKeyEnum.userChatInput,

View File

@@ -2,32 +2,44 @@ import { FlowNodeTypeEnum } from './node/constant';
import {
ModuleIOValueTypeEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum,
FlowNodeTemplateTypeEnum,
VariableInputEnum
} from './constants';
import { DispatchNodeResponseKeyEnum } from './runtime/constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
import { UserModelSchema } from 'support/user/type';
import { moduleDispatchResType } from '..//chat/type';
import { ChatModuleUsageType } from '../../support/wallet/bill/type';
import {
ChatItemValueItemType,
ToolRunResponseItemType,
UserChatItemValueItemType
} from '../chat/type';
import { ChatNodeUsageType } from '../../support/wallet/bill/type';
import { RunningModuleItemType } from './runtime/type';
import { PluginTypeEnum } from 'core/plugin/constants';
export type FlowModuleTemplateType = {
export type FlowNodeTemplateType = {
id: string; // module id, unique
templateType: `${ModuleTemplateTypeEnum}`;
templateType: `${FlowNodeTemplateTypeEnum}`;
flowType: `${FlowNodeTypeEnum}`; // render node card
avatar?: string;
name: string;
intro: string; // template list intro
isTool?: boolean; // can be connected by tool
showStatus?: boolean; // chatting response step status
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
// plugin data
pluginType?: `${PluginTypeEnum}`;
parentId?: string;
};
export type FlowModuleItemType = FlowModuleTemplateType & {
export type FlowModuleItemType = FlowNodeTemplateType & {
moduleId: string;
};
export type moduleTemplateListType = {
type: `${ModuleTemplateTypeEnum}`;
type: `${FlowNodeTemplateTypeEnum}`;
label: string;
list: FlowModuleTemplateType[];
list: FlowNodeTemplateType[];
}[];
// store module type
@@ -44,6 +56,9 @@ export type ModuleItemType = {
showStatus?: boolean;
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
// runTime field
isEntry?: boolean;
};
/* --------------- function type -------------------- */
@@ -85,30 +100,6 @@ export type ContextExtractAgentItemType = {
};
/* -------------- running module -------------- */
export type RunningModuleItemType = {
name: ModuleItemType['name'];
moduleId: ModuleItemType['moduleId'];
flowType: ModuleItemType['flowType'];
showStatus?: ModuleItemType['showStatus'];
} & {
inputs: {
key: string;
value?: any;
valueType?: `${ModuleIOValueTypeEnum}`;
}[];
outputs: {
key: string;
answer?: boolean;
response?: boolean;
value?: any;
valueType?: `${ModuleIOValueTypeEnum}`;
targets: {
moduleId: string;
key: string;
}[];
}[];
};
export type ChatDispatchProps = {
res: NextApiResponse;
mode: 'test' | 'chat';
@@ -120,15 +111,13 @@ export type ChatDispatchProps = {
responseChatItemId?: string;
histories: ChatItemType[];
variables: Record<string, any>;
inputFiles?: UserChatItemValueItemType['file'][];
stream: boolean;
detail: boolean; // response detail
};
export type ModuleDispatchProps<T> = ChatDispatchProps & {
module: RunningModuleItemType;
runtimeModules: RunningModuleItemType[];
params: T;
};
export type ModuleDispatchResponse<T> = T & {
[ModuleOutputKeyEnum.responseData]?: moduleDispatchResType;
[ModuleOutputKeyEnum.moduleDispatchBills]?: ChatModuleUsageType[];
};

View File

@@ -9,7 +9,9 @@ import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
import { AppTTSConfigType, ModuleItemType, VariableItemType } from './type';
import { Input_Template_Switch } from './template/input';
import { EditorVariablePickerType } from '../../../web/components/common/Textarea/PromptEditor/type';
import { Output_Template_Finish } from './template/output';
/* module */
export const getGuideModule = (modules: ModuleItemType[]) =>
modules.find((item) => item.flowType === FlowNodeTypeEnum.userGuide);
@@ -57,13 +59,13 @@ export const getModuleInputUiField = (input: FlowNodeInputItemType) => {
return {};
};
export function plugin2ModuleIO(
export const plugin2ModuleIO = (
pluginId: string,
modules: ModuleItemType[]
): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} {
} => {
const pluginInput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginInput);
const pluginOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput);
@@ -91,15 +93,18 @@ export function plugin2ModuleIO(
connected: false
}))
]
: [],
: [Input_Template_Switch],
outputs: pluginOutput
? pluginOutput.outputs.map((item) => ({
...item,
edit: false
}))
: []
? [
...pluginOutput.outputs.map((item) => ({
...item,
edit: false
})),
Output_Template_Finish
]
: [Output_Template_Finish]
};
}
};
export const formatEditorVariablePickerIcon = (
variables: { key: string; label: string; type?: `${VariableInputEnum}` }[]

View File

@@ -27,6 +27,26 @@ export const defaultModules: ModuleItemType[] = [
}
];
export enum PluginTypeEnum {
folder = 'folder',
custom = 'custom',
http = 'http'
}
export const pluginTypeMap = {
[PluginTypeEnum.folder]: {
label: '文件夹',
icon: 'file/fill/folder'
},
[PluginTypeEnum.custom]: {
label: '自定义',
icon: 'common/custom'
},
[PluginTypeEnum.http]: {
label: 'HTTP',
icon: 'common/http'
}
};
export enum PluginSourceEnum {
personal = 'personal',
community = 'community',

View File

@@ -1,21 +1,40 @@
import type { ModuleItemType } from '../module/type.d';
import { PluginTypeEnum } from './constants';
import { HttpAuthMethodType } from './httpPlugin/type';
export type CreateOnePluginParams = {
name: string;
avatar: string;
intro: string;
modules?: ModuleItemType[];
modules: ModuleItemType[];
parentId: string | null;
type: `${PluginTypeEnum}`;
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};
export type UpdatePluginParams = {
id: string;
parentId?: string | null;
name?: string;
avatar?: string;
intro?: string;
modules?: ModuleItemType[];
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};
export type PluginListItemType = {
_id: string;
parentId: string;
type: `${PluginTypeEnum}`;
name: string;
avatar: string;
intro: string;
metadata?: {
apiSchemaStr?: string;
customHeaders?: string;
};
};

View File

@@ -0,0 +1,13 @@
export type PathDataType = {
name: string;
description: string;
method: string;
path: string;
params: any[];
request: any;
};
export type OpenApiJsonSchema = {
pathData: PathDataType[];
serverPath: string;
};

View File

@@ -0,0 +1,516 @@
import { getNanoid } from '../../../common/string/tools';
import { OpenApiJsonSchema } from './type';
import yaml from 'js-yaml';
import { OpenAPIV3 } from 'openapi-types';
import { PluginTypeEnum } from '../constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from '../../module/node/type';
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum } from '../../module/node/constant';
import { ModuleIOValueTypeEnum } from '../../module/constants';
import { PluginInputModule } from '../../module/template/system/pluginInput';
import { PluginOutputModule } from '../../module/template/system/pluginOutput';
import { HttpModule468 } from '../../module/template/system/http468';
import { HttpParamAndHeaderItemType } from '../../module/api';
import { CreateOnePluginParams } from '../controller';
import { ModuleItemType } from '../../module/type';
import { HttpImgUrl } from '../../../common/file/image/constants';
export const str2OpenApiSchema = (yamlStr = ''): OpenApiJsonSchema => {
try {
const data: OpenAPIV3.Document = (() => {
try {
return JSON.parse(yamlStr);
} catch (jsonError) {
return yaml.load(yamlStr, { schema: yaml.FAILSAFE_SCHEMA });
}
})();
const serverPath = data.servers?.[0].url || '';
const pathData = Object.keys(data.paths)
.map((path) => {
const methodData: any = data.paths[path];
return Object.keys(methodData)
.filter((method) =>
['get', 'post', 'put', 'delete', 'patch'].includes(method.toLocaleLowerCase())
)
.map((method) => {
const methodInfo = methodData[method];
if (methodInfo.deprecated) return;
const result = {
path,
method,
name: methodInfo.operationId || path,
description: methodInfo.description,
params: methodInfo.parameters,
request: methodInfo?.requestBody
};
return result;
});
})
.flat()
.filter(Boolean) as OpenApiJsonSchema['pathData'];
return { pathData, serverPath };
} catch (err) {
throw new Error('Invalid Schema');
}
};
export const httpApiSchema2Plugins = ({
parentId,
apiSchemaStr = '',
customHeader = ''
}: {
parentId: string;
apiSchemaStr?: string;
customHeader?: string;
}): CreateOnePluginParams[] => {
const jsonSchema = str2OpenApiSchema(apiSchemaStr);
const baseUrl = jsonSchema.serverPath;
return jsonSchema.pathData.map((item) => {
const pluginOutputId = getNanoid();
const httpId = getNanoid();
const pluginOutputKey = 'result';
const properties = item.request?.content?.['application/json']?.schema?.properties;
const propsKeys = properties ? Object.keys(properties) : [];
const pluginInputs: FlowNodeInputItemType[] = [
...(item.params?.map((param: any) => {
return {
key: param.name,
valueType: ModuleIOValueTypeEnum.string,
label: param.name,
type: FlowNodeInputTypeEnum.target,
required: param.required,
description: param.description,
edit: true,
editField: {
key: true,
name: true,
description: true,
required: true,
dataType: true,
inputType: true,
isToolInput: true
},
connected: true,
toolDescription: param.description
};
}) || []),
...(propsKeys?.map((key) => {
const prop = properties[key];
return {
key,
valueType: ModuleIOValueTypeEnum.string,
label: key,
type: FlowNodeInputTypeEnum.target,
required: false,
description: prop.description,
edit: true,
editField: {
key: true,
name: true,
description: true,
required: true,
dataType: true,
inputType: true,
isToolInput: true
},
connected: true,
toolDescription: prop.description
};
}) || [])
];
const pluginOutputs: FlowNodeOutputItemType[] = [
...(item.params?.map((param: any) => {
return {
key: param.name,
valueType: ModuleIOValueTypeEnum.string,
label: param.name,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: [
{
moduleId: httpId,
key: param.name
}
]
};
}) || []),
...(propsKeys?.map((key) => {
return {
key,
valueType: ModuleIOValueTypeEnum.string,
label: key,
type: FlowNodeOutputTypeEnum.source,
edit: true,
targets: [
{
moduleId: httpId,
key
}
]
};
}) || [])
];
const httpInputs: FlowNodeInputItemType[] = [
...(item.params?.map((param: any) => {
return {
key: param.name,
valueType: ModuleIOValueTypeEnum.string,
label: param.name,
type: FlowNodeInputTypeEnum.target,
description: param.description,
edit: true,
editField: {
key: true,
description: true,
dataType: true
},
connected: true
};
}) || []),
...(propsKeys?.map((key) => {
const prop = properties[key];
return {
key,
valueType: ModuleIOValueTypeEnum.string,
label: key,
type: FlowNodeInputTypeEnum.target,
description: prop.description,
edit: true,
editField: {
key: true,
description: true,
dataType: true
},
connected: true
};
}) || [])
];
/* http node setting */
const httpNodeParams: HttpParamAndHeaderItemType[] = [];
const httpNodeHeaders: HttpParamAndHeaderItemType[] = [];
let httpNodeBody = '{}';
const requestUrl = `${baseUrl}${item.path}`;
if (item.params && item.params.length > 0) {
for (const param of item.params) {
if (param.in === 'header') {
httpNodeHeaders.push({
key: param.name,
type: param.schema?.type || ModuleIOValueTypeEnum.string,
value: `{{${param.name}}}`
});
} else if (param.in === 'body') {
httpNodeBody = JSON.stringify(
{ ...JSON.parse(httpNodeBody), [param.name]: `{{${param.name}}}` },
null,
2
);
} else if (param.in === 'query') {
httpNodeParams.push({
key: param.name,
type: param.schema?.type || ModuleIOValueTypeEnum.string,
value: `{{${param.name}}}`
});
}
}
}
if (item.request) {
const properties = item.request?.content?.['application/json']?.schema?.properties;
const keys = Object.keys(properties);
if (keys.length > 0) {
httpNodeBody = JSON.stringify(
keys.reduce((acc: any, key) => {
acc[key] = `{{${key}}}`;
return acc;
}, {}),
null,
2
);
}
}
if (customHeader) {
const headersObj = (() => {
try {
return JSON.parse(customHeader) as Record<string, string>;
} catch (err) {
return {};
}
})();
for (const key in headersObj) {
httpNodeHeaders.push({
key,
type: 'string',
// @ts-ignore
value: headersObj[key]
});
}
}
/* Combine complete modules */
const modules: ModuleItemType[] = [
{
moduleId: getNanoid(),
name: PluginInputModule.name,
intro: PluginInputModule.intro,
avatar: PluginInputModule.avatar,
flowType: PluginInputModule.flowType,
showStatus: PluginInputModule.showStatus,
position: {
x: 616.4226348688949,
y: -165.05298493910115
},
inputs: [
{
key: 'pluginStart',
type: 'hidden',
valueType: 'boolean',
label: '插件开始运行',
description:
'插件开始运行时,会输出一个 True 的标识。有时候,插件不会有额外的的输入,为了顺利的进入下一个阶段,你可以将该值连接到下一个节点的触发器中。',
showTargetInApp: true,
showTargetInPlugin: true,
connected: true
},
...pluginInputs
],
outputs: [
{
key: 'pluginStart',
label: '插件开始运行',
type: 'source',
valueType: 'boolean',
targets:
pluginOutputs.length === 0
? [
{
moduleId: httpId,
key: 'switch'
}
]
: []
},
...pluginOutputs
]
},
{
moduleId: pluginOutputId,
name: PluginOutputModule.name,
intro: PluginOutputModule.intro,
avatar: PluginOutputModule.avatar,
flowType: PluginOutputModule.flowType,
showStatus: PluginOutputModule.showStatus,
position: {
x: 1607.7142331269126,
y: -151.8669210746189
},
inputs: [
{
key: pluginOutputKey,
valueType: 'string',
label: pluginOutputKey,
type: 'target',
required: true,
description: '',
edit: true,
editField: {
key: true,
name: true,
description: true,
required: false,
dataType: true,
inputType: false
},
connected: true
}
],
outputs: [
{
key: pluginOutputKey,
valueType: 'string',
label: pluginOutputKey,
type: 'source',
edit: true,
targets: []
}
]
},
{
moduleId: httpId,
name: HttpModule468.name,
intro: HttpModule468.intro,
avatar: HttpModule468.avatar,
flowType: HttpModule468.flowType,
showStatus: true,
position: {
x: 1042.549746602742,
y: -447.77496332641647
},
inputs: [
{
key: 'switch',
type: 'target',
label: 'core.module.input.label.switch',
description: 'core.module.input.description.Trigger',
valueType: 'any',
showTargetInApp: true,
showTargetInPlugin: true,
connected: false
},
{
key: 'system_httpMethod',
type: 'custom',
valueType: 'string',
label: '',
value: item.method.toUpperCase(),
required: true,
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'system_httpReqUrl',
type: 'hidden',
valueType: 'string',
label: '',
description: 'core.module.input.description.Http Request Url',
placeholder: 'https://api.ai.com/getInventory',
required: false,
showTargetInApp: false,
showTargetInPlugin: false,
value: requestUrl,
connected: false
},
{
key: 'system_httpHeader',
type: 'custom',
valueType: 'any',
value: httpNodeHeaders,
label: '',
description: 'core.module.input.description.Http Request Header',
placeholder: 'core.module.input.description.Http Request Header',
required: false,
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'system_httpParams',
type: 'hidden',
valueType: 'any',
value: httpNodeParams,
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'system_httpJsonBody',
type: 'hidden',
valueType: 'any',
value: httpNodeBody,
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false,
connected: false
},
{
key: 'DYNAMIC_INPUT_KEY',
type: 'target',
valueType: 'any',
label: 'core.module.inputType.dynamicTargetInput',
description: 'core.module.input.description.dynamic input',
required: false,
showTargetInApp: false,
showTargetInPlugin: true,
hideInApp: true,
connected: false
},
{
key: 'system_addInputParam',
type: 'addInputParam',
valueType: 'any',
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false,
editField: {
key: true,
description: true,
dataType: true
},
defaultEditField: {
label: '',
key: '',
description: '',
inputType: 'target',
valueType: 'string'
},
connected: false
},
...httpInputs
],
outputs: [
{
key: 'finish',
label: 'core.module.output.label.running done',
description: 'core.module.output.description.running done',
valueType: 'boolean',
type: 'source',
targets: []
},
{
key: 'httpRawResponse',
label: '原始响应',
description: 'HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。',
valueType: 'any',
type: 'source',
targets: [
{
moduleId: pluginOutputId,
key: pluginOutputKey
}
]
},
{
key: 'system_addOutputParam',
type: 'addOutputParam',
valueType: 'any',
label: '',
targets: [],
editField: {
key: true,
description: true,
dataType: true,
defaultValue: true
},
defaultEditField: {
label: '',
key: '',
description: '',
outputType: 'source',
valueType: 'string'
}
}
]
}
];
return {
name: item.name,
avatar: HttpImgUrl,
intro: item.description,
parentId,
type: PluginTypeEnum.http,
modules
};
});
};

View File

@@ -1,6 +1,7 @@
import { ModuleTemplateTypeEnum } from 'core/module/constants';
import type { FlowModuleTemplateType, ModuleItemType } from '../module/type.d';
import { PluginSourceEnum } from './constants';
import { PluginSourceEnum, PluginTypeEnum } from './constants';
import { MethodType } from './controller';
export type PluginItemSchema = {
_id: string;
@@ -12,6 +13,13 @@ export type PluginItemSchema = {
intro: string;
updateTime: Date;
modules: ModuleItemType[];
parentId: string;
type: `${PluginTypeEnum}`;
metadata?: {
pluginUid?: string;
apiSchemaStr?: string;
customHeaders?: string;
};
};
/* plugin template */
@@ -19,7 +27,7 @@ export type PluginTemplateType = PluginRuntimeType & {
author?: string;
id: string;
source: `${PluginSourceEnum}`;
templateType: FlowModuleTemplateType['templateType'];
templateType: FlowNodeTemplateType['templateType'];
intro: string;
modules: ModuleItemType[];
};
@@ -29,5 +37,6 @@ export type PluginRuntimeType = {
name: string;
avatar: string;
showStatus?: boolean;
isTool?: boolean;
modules: ModuleItemType[];
};

View File

@@ -6,11 +6,15 @@
"dayjs": "^1.11.7",
"encoding": "^0.1.13",
"js-tiktoken": "^1.0.7",
"openai": "4.23.0",
"openapi-types": "^12.1.3",
"openai": "4.28.0",
"nanoid": "^4.0.1",
"timezones-list": "^3.0.2"
"js-yaml": "^4.1.0",
"timezones-list": "^3.0.2",
"next": "13.5.2"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.8.5"
}
}

View File

@@ -1,4 +1,4 @@
import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d';
import type { HistoryItemType } from '../../core/chat/type.d';
import { OutLinkSchema } from './type.d';
export type AuthOutLinkInitProps = {

View File

@@ -22,7 +22,7 @@ export type BillSchemaType = {
username: string;
};
export type ChatModuleUsageType = {
export type ChatNodeUsageType = {
tokens?: number;
totalPoints: number;
moduleName: string;

View File

@@ -1,4 +1,4 @@
import { POST } from '@fastgpt/service/common/api/plusRequest';
import { POST } from './plusRequest';
export const postTextCensor = (data: { text: string }) =>
POST<{ code?: number; message: string }>('/common/censor/text_baidu', data)

View File

@@ -0,0 +1,114 @@
import { SERVICE_LOCAL_HOST } from '../system/tools';
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
interface ConfigType {
headers?: { [key: string]: string };
hold?: boolean;
timeout?: number;
}
interface ResponseDataType {
code: number;
message: string;
data: any;
}
/**
* 请求开始
*/
function requestStart(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
return config;
}
/**
* 请求成功,检查请求头
*/
function responseSuccess(response: AxiosResponse<ResponseDataType>) {
return response;
}
/**
* 响应数据检查
*/
function checkRes(data: ResponseDataType) {
if (data === undefined) {
console.log('error->', data, 'data is empty');
return Promise.reject('服务器异常');
} else if (data?.code && (data.code < 200 || data.code >= 400)) {
return Promise.reject(data);
}
return data.data;
}
/**
* 响应错误
*/
function responseError(err: any) {
if (!err) {
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject({ message: err });
}
if (err?.response?.data) {
return Promise.reject(err?.response?.data);
}
return Promise.reject(err);
}
/* 创建请求实例 */
const instance = axios.create({
timeout: 60000, // 超时时间
headers: {
'content-type': 'application/json',
'Cache-Control': 'no-cache'
}
});
/* 请求拦截 */
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
/* 响应拦截 */
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
export function request(url: string, data: any, config: ConfigType, method: Method): any {
/* 去空 */
for (const key in data) {
if (data[key] === null || data[key] === undefined) {
delete data[key];
}
}
return instance
.request({
baseURL: `http://${SERVICE_LOCAL_HOST}`,
url,
method,
data: ['POST', 'PUT'].includes(method) ? data : null,
params: !['POST', 'PUT'].includes(method) ? data : null,
...config // custom config
})
.then((res) => checkRes(res.data))
.catch((err) => responseError(err));
}
/**
* api请求方式
* @param {String} url
* @param {Any} params
* @param {Object} config
* @returns
*/
export function GET<T = undefined>(url: string, params = {}, config: ConfigType = {}): Promise<T> {
return request(url, params, config, 'GET');
}
export function POST<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'POST');
}
export function PUT<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'PUT');
}
export function DELETE<T = undefined>(url: string, data = {}, config: ConfigType = {}): Promise<T> {
return request(url, data, config, 'DELETE');
}

View File

@@ -1,7 +0,0 @@
export enum sseResponseEventEnum {
error = 'error',
answer = 'answer', // animation stream
response = 'response', // direct response, not animation
moduleStatus = 'moduleStatus',
appStreamResponse = 'appStreamResponse' // sse response request
}

View File

@@ -1,5 +1,5 @@
import type { NextApiResponse } from 'next';
import { sseResponseEventEnum } from './constant';
import { SseResponseEventEnum } from '@fastgpt/global/core/module/runtime/constants';
import { proxyError, ERROR_RESPONSE, ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
import { addLog } from '../system/log';
import { clearCookie } from '../../support/permission/controller';
@@ -70,7 +70,7 @@ export const sseErrRes = (res: NextApiResponse, error: any) => {
return responseWrite({
res,
event: sseResponseEventEnum.error,
event: SseResponseEventEnum.error,
data: JSON.stringify(ERROR_RESPONSE[errResponseKey])
});
}
@@ -90,7 +90,7 @@ export const sseErrRes = (res: NextApiResponse, error: any) => {
responseWrite({
res,
event: sseResponseEventEnum.error,
event: SseResponseEventEnum.error,
data: JSON.stringify({ message: replaceSensitiveText(msg) })
});
};
@@ -132,3 +132,22 @@ export function responseWrite({
event && Write(`event: ${event}\n`);
Write(`data: ${data}\n\n`);
}
export const responseWriteNodeStatus = ({
res,
status = 'running',
name
}: {
res?: NextApiResponse;
status?: 'running';
name: string;
}) => {
responseWrite({
res,
event: SseResponseEventEnum.flowNodeStatus,
data: JSON.stringify({
status,
name
})
});
};

View File

@@ -1,4 +1,6 @@
export const stopWords = new Set([
import { cut } from '@node-rs/jieba';
const stopWords = new Set([
'--',
'?',
'“',
@@ -1506,3 +1508,14 @@ export const stopWords = new Set([
'i'
]
]);
export function jiebaSplit({ text }: { text: string }) {
const tokens = cut(text, true);
return (
tokens
.map((item) => item.replace(/[\u3000-\u303f\uff00-\uffef]/g, '').trim())
.filter((item) => item && !stopWords.has(item))
.join(' ') || ''
);
}

View File

@@ -139,11 +139,11 @@ export const embeddingRecall = async (
const results: any = await PgClient.query(
`BEGIN;
SET LOCAL hnsw.ef_search = ${efSearch};
select id, collection_id, (vector <#> '[${vectors[0]}]') * -1 AS score
select id, collection_id, vector <#> '[${vectors[0]}]' AS score
from ${PgDatasetTableName}
where dataset_id IN (${datasetIds.map((id) => `'${String(id)}'`).join(',')})
AND vector <#> '[${vectors[0]}]' < -${similarity}
order by score desc limit ${limit};
order by score limit ${limit};
COMMIT;`
);
@@ -153,7 +153,7 @@ export const embeddingRecall = async (
results: rows.map((item) => ({
id: item.id,
collectionId: item.collection_id,
score: item.score
score: item.score * -1
}))
};
} catch (error) {

View File

@@ -1,17 +1,17 @@
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
import type { ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type.d';
import { getAIApi } from '../config';
import { countGptMessagesTokens } from '@fastgpt/global/common/string/tiktoken';
export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
export const Prompt_QuestionGuide = `你是一个AI智能助手可以回答和解决我的问题。请结合前面的对话记录帮我生成 3 个问题引导我继续提问。问题的长度应小于20个字符按 JSON 格式返回: ["问题1", "问题2", "问题3"]`;
export async function createQuestionGuide({
messages,
model
}: {
messages: ChatMessageItemType[];
messages: ChatCompletionMessageParam[];
model: string;
}) {
const concatMessages: ChatMessageItemType[] = [
const concatMessages: ChatCompletionMessageParam[] = [
...messages,
{
role: 'user',

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