Compare commits

..

11 Commits

Author SHA1 Message Date
Archer
41115a96c0 perf: prompt textarea (#625) 2023-12-18 21:41:17 +08:00
Archer
b14a1db2f9 fix: worker un exit (#624) 2023-12-18 20:35:17 +08:00
Archer
703583fff7 v4.6.5 (#620) 2023-12-18 16:24:50 +08:00
左风
d33c99f564 Docs:fix sealos.md (#619)
* Docs:fix sealos.md

* Docs:fix sealos.md
2023-12-18 13:01:53 +08:00
Archer
05bf1b2265 V4.6.5-alpha (#609) 2023-12-15 15:57:39 +08:00
左风
dd7b4b98ae Docs:add judgement and string modules (#608)
* Docs:add judgement and string modules

* Docs:fix judgement modules
2023-12-15 15:55:31 +08:00
gaord
34656dfda0 解决上传大文件超时的问题 (#606) 2023-12-15 09:55:37 +08:00
左风
7b5c35018b Docs: add data search update OneAPI guide and wechat (#607)
* Docs:update OneAPI guide

* Docs:update use-cases weachat

* Docs:add data search
2023-12-15 09:55:21 +08:00
Carson Yang
7630417679 Update README (#603)
Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
2023-12-13 22:43:01 +08:00
aigcxiaoyi
63ce76413e Update README.md (#599) 2023-12-13 11:46:55 +08:00
左风
1c1305fcb6 Docs:update sealos guide (#592)
* Docs:update commercial doc

* Docs:update sealos guide
2023-12-12 19:50:44 +08:00
247 changed files with 7972 additions and 4960 deletions

View File

@@ -1,57 +1,81 @@
# Install dependencies only when needed
FROM node:18.15-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat && npm install -g pnpm
# --------- install dependence -----------
FROM node:18.17-alpine AS mainDeps
WORKDIR /app
ARG name
ARG proxy
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
# if proxy exists, set proxy
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npm.taobao.org
# copy packages and one project
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
COPY ./packages ./packages
COPY ./projects/$name/package.json ./projects/$name/package.json
RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)
RUN pnpm install
RUN pnpm i
# Rebuild the source code only when needed
FROM node:18.15-alpine AS builder
# --------- install dependence -----------
FROM node:18.17-alpine AS workerDeps
WORKDIR /app
ARG proxy
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
# if proxy exists, set proxy
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npm.taobao.org
COPY ./worker /app/worker
RUN cd /app/worker && pnpm i --production --ignore-workspace
# --------- builder -----------
FROM node:18.17-alpine AS builder
WORKDIR /app
ARG name
ARG proxy
# copy common node_modules and one project node_modules
COPY package.json pnpm-workspace.yaml ./
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/packages ./packages
COPY --from=mainDeps /app/node_modules ./node_modules
COPY --from=mainDeps /app/packages ./packages
COPY ./projects/$name ./projects/$name
COPY --from=deps /app/projects/$name/node_modules ./projects/$name/node_modules
COPY --from=mainDeps /app/projects/$name/node_modules ./projects/$name/node_modules
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm install -g pnpm
RUN pnpm --filter=$name run build
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
FROM node:18.15-alpine AS runner
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
RUN pnpm --filter=$name build
# --------- runner -----------
FROM node:18.17-alpine AS runner
WORKDIR /app
ARG name
ARG proxy
# create user and use it
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache curl ca-certificates \
&& update-ca-certificates
# copy running files
COPY --from=builder /app/projects/$name/public ./projects/$name/public
COPY --from=builder /app/projects/$name/next.config.js ./projects/$name/next.config.js
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/static ./projects/$name/.next/static
COPY --from=builder /app/projects/$name/public /app/projects/$name/public
COPY --from=builder /app/projects/$name/next.config.js /app/projects/$name/next.config.js
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/standalone /app/
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/static /app/projects/$name/.next/static
# copy package.json to version file
COPY --from=builder /app/projects/$name/package.json ./package.json
# copy woker
COPY --from=workerDeps /app/worker /app/worker
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

View File

@@ -50,19 +50,20 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
## 💡 功能
1. 强大的可视化编排,轻松构建 AI 应用
`1` 强大的可视化编排,轻松构建 AI 应用
- [x] 提供简易模式,无需操作编排
- [x] 用户对话前引导,全局字符串变量
- [x] 知识库搜索
- [x] 多 LLM 模型对话
- [x] 文本内容提取成结构化数据
- [x] HTTP 扩展
- [ ] 嵌入 Laf实现在线编写 HTTP 模块
- [ ] 嵌入 [Laf](https://github.com/labring/laf),实现在线编写 HTTP 模块
- [x] 对话下一步指引
- [ ] 对话多路线选择
- [x] 源文件引用追踪
- [x] 模块封装,实现多级复用
2. 丰富的知识库预处理
`2` 丰富的知识库预处理
- [x] 多库复用,混用
- [x] chunk 记录修改和删除
- [x] 支持手动输入直接分段QA 拆分导入
@@ -70,15 +71,18 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- [x] 支持知识库单独设置向量模型
- [x] 源文件存储
- [ ] 文件学习 Agent
3. 多种效果测试渠道
`3` 多种效果测试渠道
- [x] 知识库单点搜索测试
- [x] 对话时反馈引用并可修改与删除
- [x] 完整上下文呈现
- [x] 完整模块中间值呈现
4. OpenAPI
`4` OpenAPI
- [x] completions 接口 (对齐 GPT 接口)
- [ ] 知识库 CRUD
5. 运营功能
`5` 运营功能
- [x] 免登录分享窗口
- [x] Iframe 一键嵌入
- [x] 统一查阅对话记录,并对数据进行标注
@@ -93,7 +97,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- **⚡ 快速部署**
> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
> [Sealos](https://sealos.io) 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
[![](https://cdn.jsdelivr.us/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
@@ -142,7 +146,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
</a>
## 🤝 第三方生态
## 🌿 第三方生态
- [OnWeChat 个人微信/企微机器人](https://doc.fastgpt.in/docs/use-cases/onwechat/)
@@ -150,9 +154,50 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
</a>
## 🤝 参与贡献
我们非常欢迎各种形式的贡献。如果你对贡献代码感兴趣,可以查看我们的 GitHub [Issues](https://github.com/labring/FastGPT/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc),大展身手,向我们展示你的奇思妙想。
<a href="https://github.com/labring/FastGPT/graphs/contributors" target="_blank">
<table>
<tr>
<th colspan="2">
<br><img src="https://contrib.rocks/image?repo=labring/FastGPT"><br><br>
</th>
</tr>
<tr>
<td>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
<img alt="Active participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
</picture>
</td>
<td rowspan="2">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=dark">
<img alt="New trends of labring" src="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=light">
</picture>
</td>
</tr>
<tr>
<td>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
<img alt="New participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
</picture>
</td>
</tr>
</table>
</a>
## 🌟 Star History
[![Star History Chart](https://api.star-history.com/svg?repos=labring/FastGPT&type=Date)](https://star-history.com/#labring/FastGPT&Date)
<a href="https://github.com/labring/FastGPT/stargazers" target="_blank" style="display: block" align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/analyze-repo-stars-history/thumbnail.png?repo_id=605673387&image_size=auto&color_scheme=dark">
<img alt="Star History of labring/FastGPT" src="https://next.ossinsight.io/widgets/official/analyze-repo-stars-history/thumbnail.png?repo_id=605673387&image_size=auto&color_scheme=light">
</picture>
</a>
<a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
@@ -165,4 +210,4 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
4. 联系方式yujinlong@sealos.io[点击查看商业版定价策略](https://doc.fastgpt.in/docs/commercial)
4. 联系方式yujinlong@sealos.io[点击查看商业版定价策略](https://doc.fastgpt.in/docs/commercial)

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,40 @@
---
title: '知识库搜索参数'
description: '知识库搜索原理'
icon: 'language'
draft: false
toc: true
weight: 106
---
在知识库搜索的方式上FastGPT提供了三种方式分别为“语义检索”“增强语义检索”“混合检索”。
![](/imgs/data_search1.png)
## 语义检索
语义检索就是向量检索,同时把用户的问题和知识库内容向量化,然后通过“语义相关度匹配”的方式从知识库中查找到匹配的知识点。
优点:
- 相近语义理解
- 跨多语言理解(例如输入中文问题匹配英文知识点)
- 多模态理解(文本,图片,音视频等)
## 增强语义检索
在语义检索的基础上,增强“语义相关度匹配”并在搜索结束后进行 Rerank重排
Rerank重排把检索结果按“与用户问题语义”相关性从高到低排序简单的说就是把最匹配用户问题的检索结果排在前面。
## 混合检索(推荐)
在向量检索的同时进行全文检索,并把两项检索的结果混合一起重排,以便选中匹配用户问题的最佳结果。
全文检索:理解为全文关键词检索,通过关键词查询知识库,并返回包含关键词的文本片段。
优点:
- 精确匹配姓名编号ID等
- 少量关键词匹配(当用户问题字数过少时向量检索效果非常不好)
混合检索结合了向量检索和全文检索的优点,并且对查询结果进行了重排,大大提高了命中率,推荐使用。

View File

@@ -50,7 +50,7 @@ BATCH_UPDATE_ENABLED=true
BATCH_UPDATE_INTERVAL=60
```
## 使用步骤
## One API使用步骤
### 1. 登录 One API

View File

@@ -24,7 +24,9 @@ weight: 860
"success": true,
"message": "错误提示",
"msg": "同message, 错误提示",
"uid": "用户唯一凭证"
"data": {
"uid": "用户唯一凭证"
}
}
```
@@ -80,7 +82,9 @@ curl --location --request POST '{{host}}/shareAuth/init' \
```json
{
"success": true,
"uid": "username123",
"data": {
"uid": "用户唯一凭证"
}
}
```
@@ -129,7 +133,9 @@ curl --location --request POST '{{host}}/shareAuth/start' \
```json
{
"success": true,
"uid": "username123",
"data": {
"uid": "用户唯一凭证"
}
}
```

View File

@@ -41,4 +41,154 @@ Sealos 的服务器在国外,不需要额外处理网络问题,无需服务
## 部署架构图
![](/imgs/sealos-fastgpt.webp)
![](/imgs/sealos-fastgpt.webp)
## Sealos 使用
### 简介
FastGPT 商业版共包含了3个应用fastgpt, fastgpt-plus, fastgpt-admin和2个数据库使用多 Api Key 时候需要安装 OneAPI一个应用和一个数据库总计4个应用和3个数据库。
![](/imgs/onSealos1.png)
点击右侧的详情,可以查看对应应用的详细信息。
### 如何更新/升级 FastGPT
[升级脚本文档](https://doc.fastgpt.in/docs/development/upgrading/)先看下文档,看下需要升级哪个版本。注意,不要跨版本升级!!!!!
例如目前是4.5 版本要升级到4.5.1就先把镜像版本改成v4.5.1,执行一下升级脚本,等待完成后再继续升级。如果目标版本不需要执行初始化,则可以跳过。
升级步骤:
1. 打开sealos的应用管理
2. 有3个应用 fastgpt fastgpt-plugin 和 fastgpt-admin
3. 点击对应应用右边3个点变更。或者点详情后右上角的变更。
4. 修改镜像名栏
![](/imgs/onsealos2.png)
5. 点击变更/重启,会自动拉取最新镜像进行更新
6. 执行对应版本的初始化脚本
### 如何获取 FastGPT 访问链接
打开对应的应用,点击外网访问地址。
![](/imgs/onsealos3.png)
### 配置自定义域名
点击对应应用的变更->点击自定义域名->填写域名-> 操作域名 Cname -> 确认 -> 确认变。
![](/imgs/onsealos4.png)
### 如何修改配置文件
打开 Sealos 的应用管理 -> 找到对应的应用 -> 变更 -> 往下拉到高级配置,里面有个配置文件 -> 新增或点击对应的配置文件可以进行编辑 -> 点击右上角确认变。
![](/imgs/onsealos5.png)
[配置文件参考](https://doc.fastgpt.in/docs/development/configuration/)
FeConfig 参考下面(目前未做可视化)
```
"FeConfig": {
"show_emptyChat": false, // 是否展示聊天时空白的内容
"show_register": true, // 展示注册按键
"show_appStore": false, // 应用市场(暂时不可用)
"show_contact": false, // 联系方式目前不可配置直接false
"show_git": false, // 展示 github
"show_doc": false, // 展示文档
"show_pay": true, // 展示支付
"show_openai_account": false, // 用户可自定义 openai key
"show_promotion": false, // 邀请好友机制
"docUrl": "https://doc.fastgpt.in", // 文档基本地址
"systemTitle": "FastGPT", // 系统的 title
"googleClientVerKey": "", // 谷歌 v3 校验前端凭证
"isPlus": true, // 直接设置 true
"oauth": { // oauth登录
"github": "",
"google": ""
},
"limit": {
"exportLimitMinutes": 0 // 导出间隔限制
},
"scripts": [
]
}
```
### 修改站点名称以及 favicon
修改应用的环境变量,增加
```
SYSTEM_NAME=FastGPT
SYSTEM_FAVICON=/favicon.ico
HOME_URL=/app/list
```
SYSTEM_FAVICON 可以是一个网络地址
![](/imgs/onsealos6.png)
### 挂载logo
目前暂时无法 把浏览器上的logo替换。仅支持svg待后续可视化做了后可以全部替换。
新增一个挂载文件,文件名为:/app/projects/app/public/icon/logo.svg ,值为 svg 对应的值。
![](/imgs/onsealos7.png)
![](/imgs/onsealos8.png)
### 管理后台
![](/imgs/onsealos9.png)
### 商业版镜像配置文件
```
{
"license": "",
"system": {
"title": "" // 系统名称
},
"censor": {
"BAIDU_TEXT_CENSOR_CLIENTID": "", // 百度文本安全校验
"BAIDU_TEXT_CENSOR_CLIENTSECRET": "" // 百度文本安全校验
},
"auth": {
"googleServiceVerKey": "", // 谷歌 v3 校验
"github": { // github oauth
"clientId": "",
"secret": ""
},
"google": { // google oauth
"clientId": "",
"secret": ""
},
"email": { // 注册邮箱配置
"service": "qq",
"user": "",
"pass": ""
},
"phone": { // 阿里短信配置
"SNED_PHONE_ACCESSKEYID": "",
"SNED_PHONE_ACCESSSECRET": "",
"SNED_PHONE_SIGNNAME": "",
"SNED_PHONE_TEMPLATE": ""
}
},
"pay": { // 微信支付配置
"wx": {
"WX_APPID": "",
"WX_MCHID": "",
"WX_V3_CODE": "",
"WX_NOTIFY_URL": "",
"WX_SERIAL_NO": "",
"WX_PRIVATE_KEY": ""
}
}
}
```
### One API 使用
[参考 OneAPI 使用步骤](/docs/development/one-api/)

View File

@@ -32,7 +32,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv464' \
4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。
5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块
6. 调整鉴权顺序,提高 apikey 的优先级避免cookie抢占 apikey 的鉴权。
7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/course/webSync)
7. 链接读取支持多选择器。参考[Web 站点同步用法](/docs/course/websync)
8. 修复 - 分享链接图片上传鉴权问题
9. 修复 - Mongo 连接池未释放问题。
10. 修复 - Dataset Intro 无法更新

View File

@@ -43,7 +43,6 @@ weight: 506
AIBOTK_KEY=微秘书 APIKEY
AIBOTK_SECRET=微秘书 APISECRET
WORK_PRO_TOKEN=你申请的企微 token (企业微信需要填写,私人微信不需要)
WECHATY_PUPPET_SERVICE_AUTHORITY=token-service-discovery-test.juzibot.com企业微信需要填写私人微信不需要
```
这里最后两个变量只有部署企业微信才需要,私人微信只需要填写前两个即可。
@@ -56,7 +55,7 @@ WECHATY_PUPPET_SERVICE_AUTHORITY=token-service-discovery-test.juzibot.com
![](/imgs/wechat7.png)
`WORK_PRO_TOKEN` [点击这里](https://tss.juzibot.com?aff=aibotk)申请 token 然后填入即可。
`WORK_PRO_TOKEN` [点击这里](https://tss.rpachat.com/?aff=aibotk)申请 token 然后填入即可。
`WECHATY_PUPPET_SERVICE_AUTHORITY`的值复制过去就可以。

View File

@@ -0,0 +1,35 @@
---
title: "自定义反馈"
description: "自定义反馈模块介绍"
icon: "feedback"
draft: false
toc: true
weight: 354
---
该模块为临时模块,后续会针对该模块进行更全面的设计。
## 特点
- 可重复添加
- 无外部输入
- 自动执行
| | |
| --------------------- | --------------------- |
| ![](/imgs/customfeedback1.png) | ![](/imgs/customfeedback2.png) |
| ![](/imgs/customfeedback3.png) | ![](/imgs/customfeedback4.png) |
## 介绍
自定义反馈模块,可以为你的对话增加一个反馈标记,从而方便在后台更好的分析对话的数据。
在调试模式下,不会记录反馈内容,而是直接提示: `自动反馈测试: 反馈内容`
在对话模式(对话、分享窗口、带 chatId 的 API 调用会将反馈内容记录到对话日志中。会延迟60s记录
## 作用
自定义反馈模块的功能类似于程序开发的`埋点`,便于你观测的对话中的数据。

View File

@@ -1,19 +0,0 @@
---
title: "历史记录"
description: "FastGPT 历史记录模块介绍"
icon: "history"
draft: false
toc: true
weight: 354
---
# 特点
- 可重复添加(防止复杂编排时线太乱,重复添加可以更美观)
- 无外部输入
- 流程入口
- 自动执行
每次对话时,会从数据库取最多 n 条聊天记录作为上下文。注意,不是指本轮对话最多 n 条上下文,本轮对话还包括:提示词、限定词、引用内容和问题。
![](/imgs/history.png)

View File

@@ -1,5 +1,5 @@
---
title: "HTTP 模块"
title: "HTTP 模块"
description: "FastGPT HTTP 模块介绍"
icon: "http"
draft: false
@@ -19,86 +19,233 @@ weight: 355
## 介绍
HTTP 模块会向对应的地址发送一个 POST 请求Body 中携带 JSON 类型的参数,具体的参数可自定义),并接收一个 JSON 响应值,字段也是自定义。如上图中,我们定义了一个入参:「提取的字段」(定义的 key 为 appointment类型为 string和一个出参「提取结果」定义的 key 为 response类型为 string
HTTP 模块会向对应的地址发送一个 `POST/GET` 请求,携带部分`系统参数``自定义参数`,并接收一个 JSON 响应值,字段也是自定义
那么,这个请求的命令为:
- 你还可以通过 JSON 传入自定义的请求头。
- POST 请求中,数据会被放置在 `body` 中。
- GET 请求中,数据会被放置在 `query` 中。
- 在出入参数中,你都可以通过 xxx.xxx 来代表嵌套的对象。
```bash
curl --location --request POST 'https://xxxx.laf.dev/appointment-lab' \
--header 'Content-Type: application/json' \
--data-raw '{
"appointment":"{\"name\":\"小明\",\"time\":\"2023/08/16 15:00\",\"labname\":\"子良A323\"}"
}'
```
## 参数结构
响应为:
### 系统参数说明
- appId: 应用的ID
- chatId: 当前对话的ID测试模式下不存在。
- responseChatItemId: 当前对话中响应的消息ID测试模式下不存在。
- variables: 当前对话的全局变量。
- data: 自定义传递的参数。
### 嵌套对象使用
**入参**
假设我们设计了`3个`输入。
- user.name (string)
- user.age (number)
- type (string)
最终组成的对象为:
```json
{
"response": "您已经有一个预约记录了,每人仅能同时预约一个实验室:\n 姓名:小明\n 时间: 2023/08/15 15:00\n 实验室: 子良A323\n "
"user": {
"name": "",
"age": ""
},
"type": ""
}
```
**出参**
假设接口的输出结构为:
```json
{
"message": "测试",
"data":{
"name": "name",
"age": 10
}
}
```
那么,自定出参的`key`可以设置为:
- message (string)
- data.name (string)
- data.age (number)
## POST 示例
**自定义入参**
- user.name (string)
- user.age (number)
- type (string)
**自定义出参**
- message (string)
- data.name (string)
- data.age (number)
那么,这个模块发出的请求则是:
{{< tabs tabTotal="2" >}}
{{< tab tabName="POST 请求示例" >}}
{{< markdownify >}}
```bash
curl --location --request POST 'http://xxxx.com' \
--header 'Content-Type: application/json' \
--data-raw '{
"appId": "65782f7ffae5f7854ed4498b",
"chatId": "xxxx",
"responseChatItemId": "xxxx",
"variables": {
"cTime": "2023-12-18 13:45:46"
},
"data": {
"user": {
"name": "",
"age": ""
},
"type": ""
}
}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="POST响应" >}}
{{< markdownify >}}
```json
{
"message": "message",
"data": {
"name": "name",
"age": 10
}
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
## GET 示例
GET 中不推荐使用嵌套参数否则会出现奇怪的问题。此外GET 请求中FastGPT 会将参数扁平化,不会将自定义参单独抽到 data 中,同时全局变量也会扁平化,因此需要注意字段 key 是否冲突。
**自定义入参**
- name (string)
- age (number)
- type (string)
**自定义出参**
- message (string)
- name (string)
- age (number)
那么,这个模块发出的请求则是:
{{< tabs tabTotal="2" >}}
{{< tab tabName="GET 请求示例" >}}
{{< markdownify >}}
```bash
curl --location --request GET 'http://xxx.com/test?name&age&type&appId=65782f7ffae5f7854ed4498b&chatId=xxxx&responseChatItemId=xxxx&cTime=2023-12-18 13:45:46'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="GET 响应" >}}
{{< markdownify >}}
```json
{
"message": "message",
"data": {
"name": "name",
"age": 10
}
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
## laf 对接 HTTP 示例
{{% alert context="warning" %}}
如果你不想额外部署服务,可以使用 [Laf](https://laf.dev/) 来快速开发上线接口,即写即发,无需部署。
下面是在 Laf 上编写的一个请求示例:
{{% /alert %}}
下面是在 Laf 编写的 POST 请求示例:
```ts
import cloud from '@lafjs/cloud';
const db = cloud.database();
import cloud from '@lafjs/cloud'
const db = cloud.database()
type RequestType = {
appId: string;
data: {
appointment: string;
action: 'post' | 'delete' | 'put' | 'get'
}
}
export default async function (ctx: FunctionContext) {
const { appointment } = ctx.body;
const { name, time, labname } = JSON.parse(appointment);
try {
// 从 body 中获取参数
const { appId, data: { appointment, action } } = ctx.body as RequestType
const parseBody = JSON.parse(appointment)
if (action === 'get') {
return await getRecord(parseBody)
}
if (action === 'post') {
return await createRecord(parseBody)
}
if (action === 'put') {
return await putRecord(parseBody)
}
if (action === 'delete') {
return await removeRecord(parseBody)
}
const missData = [];
if (!name) missData.push('你的姓名');
if (!time) missData.push('需要预约的时间');
if (!labname) missData.push('实验室名称');
if (missData.length > 0) {
return {
response: `请提供: ${missData.join('、')}`
};
}
const { data: record } = await db
.collection('LabAppointment')
.where({
name,
status: 'unStart'
})
.getOne();
if (record) {
response: "异常"
}
} catch (err) {
return {
response: `您已经有一个预约记录了,每人仅能同时预约一个实验室:
姓名:${record.name}
时间: ${record.time}
实验室: ${record.labname}
`
};
response: "异常"
}
}
await db.collection('LabAppointment').add({
name,
time,
labname,
status: 'unStart'
});
return {
response: `预约成功。
姓名:${name}
时间: ${time}
实验室: ${labname}
`
};
}
```
## 作用
基于 HTTP 模块可以无限扩展,比如操作数据库、执行联网搜索、发送邮箱等等。如果你有有趣的案例,欢迎提交 PR 到 [编排案例](/docs/workflow/examples)
通过 HTTP 模块可以无限扩展,比如
- 操作数据库
- 调用外部数据源
- 执行联网搜索
- 发送邮箱
- ....
## 相关示例
- [谷歌搜索](/docs/workflow/examples/google_search/)
- [实验室预约(操作数据库)](/docs/workflow/examples/lab_appointment/)

View File

@@ -0,0 +1,28 @@
---
title: "判断器"
description: "FastGPT 判断器模块介绍"
icon: "input"
draft: false
toc: true
weight: 362
---
## 特点
- 可重复添加
- 有外部输入
- 触发执行
![](/imgs/judgement1.png)
## 功能
对任意输入内容进行 True False 输出,默认情况下,当传入的内容为 false, undefined, null,0,none 时,会输出 false。
也可以增加自定义规则来补充输出 false 的内容,每行代表一个匹配规则,支持正则表达式。
根据上方示例图的匹配规则,当我们输入`123` `hi` `你好` 和任意手机号码时(正则匹配)同样也会输出 False 。
## 作用
适用场景有:让大模型做判断后输出固定内容,根据大模型回复内容判断是否触发后续模块。

View File

@@ -0,0 +1,28 @@
---
title: "文本加工"
description: "FastGPT 文本加工模块介绍"
icon: "input"
draft: false
toc: true
weight: 363
---
## 特点
- 可重复添加
- 有外部输入
- 触发执行
- 手动配置
![](/imgs/string.png)
## 功能
对输入文本进行固定加工处理,入参仅支持字符串和数字格式,入参以变量形式使用在文本编辑区域。
根据上方示例图的处理方式,对任何输入都会在前面拼接“我的问题是:”。
## 作用
给任意模块输入自定格式文本,或处理 AI 模块系统提示词。

View File

@@ -5,27 +5,24 @@
"scripts": {
"prepare": "husky install",
"format-code": "prettier --config \"./.prettierrc.js\" --write \"./**/src/**/*.{ts,tsx,scss}\"",
"format-doc": "zhlint --dir ./docSite *.md --fix"
"format-doc": "zhlint --dir ./docSite *.md --fix",
"postinstall": "sh ./scripts/postinstall.sh"
},
"devDependencies": {
"@types/multer": "^1.4.10",
"husky": "^8.0.3",
"i18next": "^22.5.1",
"lint-staged": "^13.2.1",
"next-i18next": "^13.3.0",
"prettier": "^3.0.3",
"react-i18next": "^12.3.1",
"zhlint": "^0.7.1"
"zhlint": "^0.7.1",
"i18next": "^22.5.1",
"next-i18next": "^13.3.0",
"react-i18next": "^12.3.1"
},
"lint-staged": {
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
"./**/**/*.md": "npm run format-doc"
},
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"multer": "1.4.5-lts.1",
"openai": "4.16.1"
"node": ">=18.0.0",
"pnpm": ">=8.6.0"
}
}

View File

@@ -1,8 +1,3 @@
import axios from 'axios';
import { UrlFetchParams, UrlFetchResponse } from './api.d';
import { htmlToMarkdown } from '../string/markdown';
import * as cheerio from 'cheerio';
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
@@ -12,91 +7,3 @@ export const formatFileSize = (bytes: number): string => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
export const cheerioToHtml = ({
fetchUrl,
$,
selector
}: {
fetchUrl: string;
$: cheerio.CheerioAPI;
selector?: string;
}) => {
// get origin url
const originUrl = new URL(fetchUrl).origin;
// remove i element
$('i,script').remove();
// remove empty a element
$('a')
.filter((i, el) => {
return $(el).text().trim() === '' && $(el).children().length === 0;
})
.remove();
// if link,img startWith /, add origin url
$('a').each((i, el) => {
const href = $(el).attr('href');
if (href && href.startsWith('/')) {
$(el).attr('href', originUrl + href);
}
});
$('img').each((i, el) => {
const src = $(el).attr('src');
if (src && src.startsWith('/')) {
$(el).attr('src', originUrl + src);
}
});
const html = $(selector || 'body')
.map((item, dom) => {
return $(dom).html();
})
.get()
.join('\n');
return html;
};
export const urlsFetch = async ({
urlList,
selector
}: UrlFetchParams): Promise<UrlFetchResponse> => {
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));
const response = (
await Promise.all(
urlList.map(async (url) => {
try {
const fetchRes = await axios.get(url, {
timeout: 30000
});
const $ = cheerio.load(fetchRes.data);
const md = htmlToMarkdown(
cheerioToHtml({
fetchUrl: url,
$,
selector
})
);
return {
url,
content: md
};
} catch (error) {
console.log(error, 'fetch error');
return {
url,
content: ''
};
}
})
)
).filter((item) => item.content);
return response;
};

View File

@@ -1,5 +1,4 @@
import { simpleText } from './tools';
import { NodeHtmlMarkdown } from 'node-html-markdown';
/* Delete redundant text in markdown */
export const simpleMarkdownText = (rawText: string) => {
@@ -27,75 +26,11 @@ export const simpleMarkdownText = (rawText: string) => {
// Remove headings and code blocks front spaces
['####', '###', '##', '#', '```', '~~~'].forEach((item, i) => {
const isMarkdown = i <= 3;
const reg = new RegExp(`\\n\\s*${item}`, 'g');
if (reg.test(rawText)) {
rawText = rawText.replace(
new RegExp(`(\\n)\\s*(${item})`, 'g'),
isMarkdown ? '\n$1$2' : '$1$2'
);
rawText = rawText.replace(new RegExp(`(\\n)\\s*(${item})`, 'g'), '$1$2');
}
});
return rawText.trim();
};
/* html string to markdown */
export const htmlToMarkdown = (html?: string | null) => {
if (!html) return '';
const surround = (source: string, surroundStr: string) => `${surroundStr}${source}${surroundStr}`;
const nhm = new NodeHtmlMarkdown(
{
codeFence: '```',
codeBlockStyle: 'fenced',
ignore: ['i', 'script']
},
{
code: ({ node, parent, options: { codeFence, codeBlockStyle }, visitor }) => {
const isCodeBlock = ['PRE', 'WRAPPED-PRE'].includes(parent?.tagName!);
if (!isCodeBlock) {
return {
spaceIfRepeatingChar: true,
noEscape: true,
postprocess: ({ content }) => {
// Find longest occurring sequence of running backticks and add one more (so content is escaped)
const delimiter =
'`' + (content.match(/`+/g)?.sort((a, b) => b.length - a.length)?.[0] || '');
const padding = delimiter.length > 1 ? ' ' : '';
return surround(surround(content, padding), delimiter);
}
};
}
/* Handle code block */
if (codeBlockStyle === 'fenced') {
const language =
node.getAttribute('class')?.match(/language-(\S+)/)?.[1] ||
parent?.getAttribute('class')?.match(/language-(\S+)/)?.[1] ||
'';
return {
noEscape: true,
prefix: `${codeFence}${language}\n`,
postfix: `\n${codeFence}\n`,
childTranslators: visitor.instance.codeBlockTranslators
};
}
return {
noEscape: true,
postprocess: ({ content }) => content.replace(/^/gm, ' '),
childTranslators: visitor.instance.codeBlockTranslators
};
}
}
);
const markdown = nhm.translate(html).trim();
return simpleMarkdownText(markdown);
};

View File

@@ -13,12 +13,13 @@ export const splitText2Chunks = (props: {
chunkLen: number;
overlapRatio?: number;
customReg?: string[];
countTokens?: boolean;
}): {
chunks: string[];
tokens: number;
overlapRatio?: number;
} => {
let { text = '', chunkLen, overlapRatio = 0.2, customReg = [] } = props;
let { text = '', chunkLen, overlapRatio = 0.2, customReg = [], countTokens = true } = props;
const splitMarker = 'SPLIT_HERE_SPLIT_HERE';
const codeBlockMarker = 'CODE_BLOCK_LINE_MARKER';
const overlapLen = Math.round(chunkLen * overlapRatio);
@@ -231,9 +232,11 @@ export const splitText2Chunks = (props: {
step: 0,
lastText: '',
mdTitle: ''
}).map((chunk) => chunk.replaceAll(codeBlockMarker, '\n')); // restore code block
}).map((chunk) => chunk?.replaceAll(codeBlockMarker, '\n') || ''); // restore code block
const tokens = chunks.reduce((sum, chunk) => sum + countPromptTokens(chunk, 'system'), 0);
const tokens = countTokens
? chunks.reduce((sum, chunk) => sum + countPromptTokens(chunk, 'system'), 0)
: 0;
return {
chunks,

View File

@@ -40,7 +40,7 @@ export type ChatItemSchema = {
value: string;
userGoodFeedback?: string;
userBadFeedback?: string;
robotBadFeedback?: string;
customFeedbacks?: string[];
adminFeedback?: AdminFbkType;
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
};
@@ -60,6 +60,7 @@ export type ChatItemType = {
value: any;
userGoodFeedback?: string;
userBadFeedback?: string;
customFeedbacks?: ChatItemSchema['customFeedbacks'];
adminFeedback?: ChatItemSchema['feedback'];
[ModuleOutputKeyEnum.responseData]?: ChatHistoryItemResType[];
};
@@ -84,12 +85,14 @@ export type ChatHistoryItemType = HistoryItemType & {
/* ------- response data ------------ */
export type moduleDispatchResType = {
// common
moduleLogo?: string;
price?: number;
runningTime?: number;
tokens?: number;
model?: string;
query?: string;
contextTotalLen?: number;
// chat
temperature?: number;
@@ -116,6 +119,12 @@ export type moduleDispatchResType = {
// plugin output
pluginOutput?: Record<string, any>;
// text editor
textOutput?: string;
// tf switch
tfSwitchResult?: boolean;
};
export type ChatHistoryItemResType = moduleDispatchResType & {

View File

@@ -1,3 +1,18 @@
import { VectorModelItemType } from '../ai/model.d';
export type SelectedDatasetType = { datasetId: string; vectorModel: VectorModelItemType }[];
export type HttpBodyType<T = any> = {
appId: string;
chatId?: string;
responseChatItemId?: string;
variables: Record<string, any>;
data: T;
};
export type HttpQueryType = {
appId: string;
chatId?: string;
responseChatItemId?: string;
variables: Record<string, any>;
[key: string]: any;
};

View File

@@ -1,19 +1,17 @@
export enum ModuleTemplateTypeEnum {
userGuide = 'userGuide',
systemInput = 'systemInput',
tools = 'tools',
textAnswer = 'textAnswer',
dataset = 'dataset',
functionCall = 'functionCall',
externalCall = 'externalCall',
personalPlugin = 'personalPlugin',
communityPlugin = 'communityPlugin',
commercialPlugin = 'commercialPlugin',
other = 'other'
}
export enum ModuleDataTypeEnum {
export enum ModuleIOValueTypeEnum {
string = 'string',
number = 'number',
boolean = 'boolean',
@@ -44,6 +42,9 @@ export enum ModuleInputKeyEnum {
aiModel = 'model',
aiSystemPrompt = 'systemPrompt',
description = 'description',
anyInput = 'system_anyInput',
textareaInput = 'system_textareaInput',
addInputParam = 'system_addInputParam',
// history
historyMaxAmount = 'maxContext',
@@ -69,7 +70,10 @@ export enum ModuleInputKeyEnum {
extractKeys = 'extractKeys',
// http
httpUrl = 'url',
httpReqUrl = 'system_httpReqUrl',
httpHeader = 'system_httpHeader',
httpMethod = 'system_httpMethod',
abandon_httpUrl = 'url',
// app
runAppSelectApp = 'app',
@@ -87,6 +91,8 @@ export enum ModuleOutputKeyEnum {
answerText = 'answerText', // answer module text key
success = 'success',
failed = 'failed',
text = 'system_text',
addOutputParam = 'system_addOutputParam',
// dataset
datasetIsEmpty = 'isEmpty',
@@ -94,7 +100,11 @@ export enum ModuleOutputKeyEnum {
datasetQuoteQA = 'quoteQA',
// context extract
contextExtractFields = 'fields'
contextExtractFields = 'fields',
// tf switch
resultTrue = 'system_resultTrue',
resultFalse = 'system_resultFalse'
}
export enum VariableInputEnum {
@@ -102,3 +112,5 @@ export enum VariableInputEnum {
textarea = 'textarea',
select = 'select'
}
export const DYNAMIC_INPUT_KEY = 'DYNAMIC_INPUT_KEY';

View File

@@ -2,32 +2,39 @@ export enum FlowNodeInputTypeEnum {
systemInput = 'systemInput', // history, userChatInput, variableInput
input = 'input', // one line input
textarea = 'textarea',
numberInput = 'numberInput',
select = 'select',
slider = 'slider',
custom = 'custom',
target = 'target', // data input
switch = 'switch',
textarea = 'textarea',
addInputParam = 'addInputParam', // params input
selectApp = 'selectApp',
// chat special input
aiSettings = 'aiSettings',
// model select
// ai model select
selectChatModel = 'selectChatModel',
selectCQModel = 'selectCQModel',
selectExtractModel = 'selectExtractModel',
// dataset special input
selectDataset = 'selectDataset',
selectDatasetParamsModal = 'selectDatasetParamsModal',
hidden = 'hidden'
hidden = 'hidden',
custom = 'custom'
}
export enum FlowNodeOutputTypeEnum {
answer = 'answer',
source = 'source',
hidden = 'hidden'
hidden = 'hidden',
addOutputParam = 'addOutputParam'
}
export enum FlowNodeTypeEnum {
@@ -45,7 +52,10 @@ export enum FlowNodeTypeEnum {
pluginModule = 'pluginModule',
pluginInput = 'pluginInput',
pluginOutput = 'pluginOutput',
textEditor = 'textEditor',
// abandon
variable = 'variable'
}
export const EDGE_TYPE = 'smoothstep';

View File

@@ -1,6 +1,7 @@
import { FlowNodeInputTypeEnum, FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from './constant';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleOutputKeyEnum } from '../constants';
import { SelectedDatasetType } from '../api';
import { EditInputFieldMap, EditOutputFieldMap } from './type';
export type FlowNodeChangeProps = {
moduleId: string;
@@ -20,27 +21,34 @@ export type FlowNodeChangeProps = {
};
export type FlowNodeInputItemType = {
valueType?: `${ModuleIOValueTypeEnum}`; // data type
type: `${FlowNodeInputTypeEnum}`; // Node Type. Decide on a render style
key: `${ModuleInputKeyEnum}` | string;
type: `${FlowNodeInputTypeEnum}`; // Decide on a render style
value?: any;
valueType?: `${ModuleDataTypeEnum}`; // data type
label: string;
description?: string;
required?: boolean;
edit?: boolean; // Whether to allow editing
connected?: boolean; // unConnected field will be deleted
editField?: EditInputFieldMap;
defaultEditField?: EditNodeFieldType;
connected?: boolean; // There are incoming data
showTargetInApp?: boolean;
showTargetInPlugin?: boolean;
placeholder?: string; // input,textarea
list?: { label: string; value: any }[]; // select
step?: number; // slider max?: number;
max?: number;
min?: number;
markList?: { label: string; value: any }[]; // slider
hideInApp?: boolean;
hideInPlugin?: boolean;
plusField?: boolean; // plus system will show
placeholder?: string; // input,textarea
list?: { label: string; value: any }[]; // select
markList?: { label: string; value: any }[]; // slider
step?: number; // slider
max?: number; // slider, number input
min?: number; // slider, number input
};
export type FlowNodeOutputTargetItemType = {
@@ -48,15 +56,41 @@ export type FlowNodeOutputTargetItemType = {
key: string;
};
export type FlowNodeOutputItemType = {
key: `${ModuleOutputKeyEnum}` | string;
label?: string;
edit?: boolean;
description?: string;
valueType?: `${ModuleDataTypeEnum}`;
type?: `${FlowNodeOutputTypeEnum}`;
key: `${ModuleOutputKeyEnum}` | string;
valueType?: `${ModuleIOValueTypeEnum}`;
label?: string;
description?: string;
edit?: boolean;
editField?: EditOutputFieldMap;
defaultEditField?: EditNodeFieldType;
targets: FlowNodeOutputTargetItemType[];
};
/* --------------- edit field ------------------- */
export type EditInputFieldMap = EditOutputFieldMap & {
inputType?: boolean;
required?: boolean;
};
export type EditOutputFieldMap = {
name?: boolean;
key?: boolean;
description?: boolean;
dataType?: boolean;
};
export type EditNodeFieldType = {
inputType?: `${FlowNodeInputTypeEnum}`; // input type
outputType?: `${FlowNodeOutputTypeEnum}`;
required?: boolean;
key?: string;
label?: string;
description?: string;
valueType?: `${ModuleIOValueTypeEnum}`;
};
/* ------------- item type --------------- */
/* ai chat modules props */
export type AIChatModuleProps = {

View File

@@ -1,13 +1,13 @@
import type { FlowNodeInputItemType } from '../node/type.d';
import { ModuleInputKeyEnum } from '../constants';
import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '../constants';
import { FlowNodeInputTypeEnum } from '../node/constant';
import { ModuleDataTypeEnum } from '../constants';
import { ModuleIOValueTypeEnum } from '../constants';
export const Input_Template_TFSwitch: FlowNodeInputItemType = {
export const Input_Template_Switch: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.switch,
type: FlowNodeInputTypeEnum.target,
label: 'core.module.input.label.switch',
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
showTargetInApp: true,
showTargetInPlugin: true
};
@@ -19,7 +19,7 @@ export const Input_Template_History: FlowNodeInputItemType = {
required: true,
min: 0,
max: 30,
valueType: ModuleDataTypeEnum.chatHistory,
valueType: ModuleIOValueTypeEnum.chatHistory,
value: 6,
showTargetInApp: true,
showTargetInPlugin: true
@@ -30,7 +30,29 @@ export const Input_Template_UserChatInput: FlowNodeInputItemType = {
type: FlowNodeInputTypeEnum.target,
label: 'core.module.input.label.user question',
required: true,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: true,
showTargetInPlugin: true
};
export const Input_Template_AddInputParam: FlowNodeInputItemType = {
key: ModuleInputKeyEnum.addInputParam,
type: FlowNodeInputTypeEnum.addInputParam,
valueType: ModuleIOValueTypeEnum.any,
label: '',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
};
export const Input_Template_DynamicInput: FlowNodeInputItemType = {
key: DYNAMIC_INPUT_KEY,
type: FlowNodeInputTypeEnum.target,
valueType: ModuleIOValueTypeEnum.any,
label: 'core.module.inputType.dynamicTargetInput',
description: 'core.module.input.description.dynamic input',
required: false,
showTargetInApp: false,
showTargetInPlugin: true,
hideInApp: true
};

View File

@@ -1,13 +1,21 @@
import type { FlowNodeOutputItemType } from '../node/type';
import { ModuleOutputKeyEnum } from '../constants';
import { FlowNodeOutputTypeEnum } from '../node/constant';
import { ModuleDataTypeEnum } from '../constants';
import { ModuleIOValueTypeEnum } from '../constants';
export const Output_Template_Finish: FlowNodeOutputItemType = {
key: ModuleOutputKeyEnum.finish,
label: 'core.module.output.label.running done',
description: 'core.module.output.description.running done',
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
type: FlowNodeOutputTypeEnum.source,
targets: []
};
export const Output_Template_AddOutput: FlowNodeOutputItemType = {
key: ModuleOutputKeyEnum.addOutputParam,
type: FlowNodeOutputTypeEnum.addOutputParam,
valueType: ModuleIOValueTypeEnum.any,
label: '',
targets: []
};

View File

@@ -2,9 +2,13 @@ import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
} from '../../../node/constant';
import { FlowModuleTemplateType } from '../../../type';
import {
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleTemplateTypeEnum
} from '../../../constants';
export const HistoryModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.historyNode,
@@ -12,7 +16,7 @@ export const HistoryModule: FlowModuleTemplateType = {
flowType: FlowNodeTypeEnum.historyNode,
avatar: '/imgs/module/history.png',
name: '聊天记录(弃用)',
intro: '用户输入的内容。该模块通常作为应用的入口,用户在发送消息后会首先执行该模块。',
intro: '聊天记录,该模块已被弃用',
inputs: [
{
key: ModuleInputKeyEnum.historyMaxAmount,
@@ -21,7 +25,7 @@ export const HistoryModule: FlowModuleTemplateType = {
description:
'该记录数不代表模型可接收这么多的历史记录具体可接收多少历史记录取决于模型的能力通常建议不要超过20条。',
value: 6,
valueType: ModuleDataTypeEnum.number,
valueType: ModuleIOValueTypeEnum.number,
min: 0,
max: 100,
showTargetInApp: false,
@@ -30,7 +34,7 @@ export const HistoryModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.history,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleDataTypeEnum.chatHistory,
valueType: ModuleIOValueTypeEnum.chatHistory,
label: '聊天记录',
showTargetInApp: false,
showTargetInPlugin: false
@@ -40,7 +44,7 @@ export const HistoryModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.history,
label: '聊天记录',
valueType: ModuleDataTypeEnum.chatHistory,
valueType: ModuleIOValueTypeEnum.chatHistory,
type: FlowNodeOutputTypeEnum.source,
targets: []
}

View File

@@ -5,14 +5,14 @@ import {
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
ModuleDataTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
} from '../../constants';
import {
Input_Template_History,
Input_Template_TFSwitch,
Input_Template_Switch,
Input_Template_UserChatInput
} from '../input';
import { chatNodeSystemPromptTip } from '../tip';
@@ -27,13 +27,13 @@ export const AiChatModule: FlowModuleTemplateType = {
intro: 'AI 大模型对话',
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectChatModel,
label: '对话模型',
required: true,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -43,7 +43,7 @@ export const AiChatModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
label: '温度',
value: 0,
valueType: ModuleDataTypeEnum.number,
valueType: ModuleIOValueTypeEnum.number,
min: 0,
max: 10,
step: 1,
@@ -59,7 +59,7 @@ export const AiChatModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.hidden, // Set in the pop-up window
label: '回复上限',
value: 2000,
valueType: ModuleDataTypeEnum.number,
valueType: ModuleIOValueTypeEnum.number,
min: 100,
max: 4000,
step: 50,
@@ -78,7 +78,7 @@ export const AiChatModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.hidden,
label: '返回AI内容',
value: true,
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -86,7 +86,7 @@ export const AiChatModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.aiChatQuoteTemplate,
type: FlowNodeInputTypeEnum.hidden,
label: '引用内容模板',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -94,7 +94,7 @@ export const AiChatModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.aiChatQuotePrompt,
type: FlowNodeInputTypeEnum.hidden,
label: '引用内容提示词',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -102,7 +102,7 @@ export const AiChatModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.aiChatSettingModal,
type: FlowNodeInputTypeEnum.aiSettings,
label: '',
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -112,7 +112,7 @@ export const AiChatModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.textarea,
label: '系统提示词',
max: 300,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
description: chatNodeSystemPromptTip,
placeholder: chatNodeSystemPromptTip,
showTargetInApp: true,
@@ -124,7 +124,7 @@ export const AiChatModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.target,
label: '引用内容',
description: "对象数组格式,结构:\n [{q:'问题',a:'回答'}]",
valueType: ModuleDataTypeEnum.datasetQuote,
valueType: ModuleIOValueTypeEnum.datasetQuote,
showTargetInApp: true,
showTargetInPlugin: true
},
@@ -135,7 +135,7 @@ export const AiChatModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.history,
label: '新的上下文',
description: '将本次回复内容拼接上历史记录,作为新的上下文返回',
valueType: ModuleDataTypeEnum.chatHistory,
valueType: ModuleIOValueTypeEnum.chatHistory,
type: FlowNodeOutputTypeEnum.source,
targets: []
},
@@ -143,7 +143,7 @@ export const AiChatModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.answerText,
label: 'AI回复',
description: '将在 stream 回复完毕后触发',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
targets: []
},

View File

@@ -1,7 +1,7 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { Input_Template_TFSwitch } from '../input';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { Input_Template_Switch } from '../input';
import { Output_Template_Finish } from '../output';
export const AssignedAnswerModule: FlowModuleTemplateType = {
@@ -12,14 +12,16 @@ export const AssignedAnswerModule: FlowModuleTemplateType = {
name: '指定回复',
intro: '该模块可以直接回复一段指定的内容。常用于引导、提示',
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.answerText,
type: FlowNodeInputTypeEnum.textarea,
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
label: '回复的内容',
description:
'可以使用 \\n 来实现连续换行。\n\n可以通过外部模块输入实现回复,外部模块输入时会覆盖当前填写的内容。\n\n如传入非字符串类型数据将会自动转成字符串',
'可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串',
placeholder:
'可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串',
showTargetInApp: true,
showTargetInPlugin: true
}

View File

@@ -4,10 +4,10 @@ import {
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import {
Input_Template_History,
Input_Template_TFSwitch,
Input_Template_Switch,
Input_Template_UserChatInput
} from '../input';
@@ -24,11 +24,11 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
类型4: 其他问题`,
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectCQModel,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
label: '分类模型',
required: true,
showTargetInApp: false,
@@ -37,7 +37,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.aiSystemPrompt,
type: FlowNodeInputTypeEnum.textarea,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
label: '背景知识',
description:
'你可以添加一些特定内容的介绍,从而更好的识别用户的问题类型。这个内容通常是给模型介绍一个它不知道的内容。',
@@ -51,7 +51,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.agents,
type: FlowNodeInputTypeEnum.custom,
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
label: '',
value: [
{

View File

@@ -5,12 +5,12 @@ import {
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
ModuleDataTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
} from '../../constants';
import { Input_Template_History, Input_Template_TFSwitch } from '../input';
import { Input_Template_History, Input_Template_Switch } from '../input';
export const ContextExtractModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.contentExtract,
@@ -21,11 +21,20 @@ export const ContextExtractModule: FlowModuleTemplateType = {
intro: '可从文本中提取指定的数据例如sql语句、搜索关键词、代码等',
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.aiModel,
type: FlowNodeInputTypeEnum.selectExtractModel,
valueType: ModuleIOValueTypeEnum.string,
label: '提取模型',
required: true,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.description,
type: FlowNodeInputTypeEnum.textarea,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
label: '提取要求描述',
description: '给AI一些对应的背景知识或要求描述引导AI更好的完成任务',
required: true,
@@ -40,7 +49,7 @@ export const ContextExtractModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.target,
label: '需要提取的文本',
required: true,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: true,
showTargetInPlugin: true
},
@@ -48,9 +57,9 @@ export const ContextExtractModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.extractKeys,
type: FlowNodeInputTypeEnum.custom,
label: '目标字段',
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
description: "由 '描述' 和 'key' 组成一个目标字段,可提取多个目标字段",
value: [], // {desc: string; key: string; required: boolean;}[]
value: [], // {desc: string; key: string; required: boolean; enum: string[]}[]
showTargetInApp: false,
showTargetInPlugin: false
}
@@ -59,14 +68,14 @@ export const ContextExtractModule: FlowModuleTemplateType = {
{
key: ModuleOutputKeyEnum.success,
label: '字段完全提取',
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
type: FlowNodeOutputTypeEnum.source,
targets: []
},
{
key: ModuleOutputKeyEnum.failed,
label: '提取字段缺失',
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
type: FlowNodeOutputTypeEnum.source,
targets: []
},
@@ -74,7 +83,7 @@ export const ContextExtractModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.contextExtractFields,
label: '完整提取结果',
description: '一个 JSON 字符串,例如:{"name:":"YY","Time":"2023/7/2 18:00"}',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
targets: []
}

View File

@@ -5,31 +5,31 @@ import {
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
ModuleDataTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
} from '../../constants';
import { Input_Template_TFSwitch, Input_Template_UserChatInput } from '../input';
import { Input_Template_Switch, Input_Template_UserChatInput } from '../input';
import { Output_Template_Finish } from '../output';
import { DatasetSearchModeEnum } from '../../../dataset/constant';
export const DatasetSearchModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.datasetSearchNode,
templateType: ModuleTemplateTypeEnum.dataset,
templateType: ModuleTemplateTypeEnum.functionCall,
flowType: FlowNodeTypeEnum.datasetSearchNode,
avatar: '/imgs/module/db.png',
name: '知识库搜索',
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.datasetSelectList,
type: FlowNodeInputTypeEnum.selectDataset,
label: '关联的知识库',
value: [],
valueType: ModuleDataTypeEnum.selectDataset,
valueType: ModuleIOValueTypeEnum.selectDataset,
list: [],
required: true,
showTargetInApp: false,
@@ -40,7 +40,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
type: FlowNodeInputTypeEnum.hidden,
label: '最低相关性',
value: 0.4,
valueType: ModuleDataTypeEnum.number,
valueType: ModuleIOValueTypeEnum.number,
min: 0,
max: 1,
step: 0.01,
@@ -57,7 +57,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
label: '引用上限',
description: '单次搜索最大的 Tokens 数量中文约1字=1.7Tokens英文约1字=1Tokens',
value: 1500,
valueType: ModuleDataTypeEnum.number,
valueType: ModuleIOValueTypeEnum.number,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -65,7 +65,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.datasetSearchMode,
type: FlowNodeInputTypeEnum.hidden,
label: 'core.dataset.search.Mode',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
showTargetInApp: false,
showTargetInPlugin: false,
value: DatasetSearchModeEnum.embedding
@@ -74,7 +74,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
key: ModuleInputKeyEnum.datasetParamsModal,
type: FlowNodeInputTypeEnum.selectDatasetParamsModal,
label: '',
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
showTargetInApp: false,
showTargetInPlugin: false
},
@@ -85,14 +85,14 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.datasetIsEmpty,
label: '搜索结果为空',
type: FlowNodeOutputTypeEnum.source,
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
targets: []
},
{
key: ModuleOutputKeyEnum.datasetUnEmpty,
label: '搜索结果不为空',
type: FlowNodeOutputTypeEnum.source,
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
targets: []
},
{
@@ -101,7 +101,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = {
description:
'始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器',
type: FlowNodeOutputTypeEnum.source,
valueType: ModuleDataTypeEnum.datasetQuote,
valueType: ModuleIOValueTypeEnum.datasetQuote,
targets: []
},
Output_Template_Finish

View File

@@ -1,8 +1,16 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { Input_Template_TFSwitch } from '../input';
import { Output_Template_Finish } from '../output';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import {
Input_Template_AddInputParam,
Input_Template_DynamicInput,
Input_Template_Switch
} from '../input';
import { Output_Template_AddOutput, Output_Template_Finish } from '../output';
export const HttpModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.httpRequest,
@@ -13,18 +21,86 @@ export const HttpModule: FlowModuleTemplateType = {
intro: '可以发出一个 HTTP POST 请求,实现更为复杂的操作(联网搜索、数据库查询等)',
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.httpUrl,
type: FlowNodeInputTypeEnum.input,
valueType: ModuleDataTypeEnum.string,
label: '请求地址',
description: '请求目标地址',
placeholder: 'https://api.ai.com/getInventory',
key: ModuleInputKeyEnum.httpMethod,
type: FlowNodeInputTypeEnum.select,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.module.input.label.Http Request Method',
value: 'POST',
list: [
{
label: 'GET',
value: 'GET'
},
{
label: 'POST',
value: 'POST'
}
],
required: true,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpReqUrl,
type: FlowNodeInputTypeEnum.input,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.module.input.label.Http Request Url',
description: 'core.module.input.description.Http Request Url',
placeholder: 'https://api.ai.com/getInventory',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
{
key: ModuleInputKeyEnum.httpHeader,
type: FlowNodeInputTypeEnum.textarea,
valueType: ModuleIOValueTypeEnum.string,
label: 'core.module.input.label.Http Request Header',
description: 'core.module.input.description.Http Request Header',
placeholder: 'core.module.input.description.Http Request Header',
required: false,
showTargetInApp: false,
showTargetInPlugin: false
},
Input_Template_DynamicInput,
{
...Input_Template_AddInputParam,
editField: {
key: true,
name: true,
description: true,
required: true,
dataType: true
},
defaultEditField: {
label: '',
key: '',
description: '',
inputType: FlowNodeInputTypeEnum.target,
valueType: ModuleIOValueTypeEnum.string,
required: true
}
}
],
outputs: [Output_Template_Finish]
outputs: [
Output_Template_Finish,
{
...Output_Template_AddOutput,
editField: {
key: true,
name: true,
description: true,
dataType: true
},
defaultEditField: {
label: '',
key: '',
description: '',
outputType: FlowNodeOutputTypeEnum.source,
valueType: ModuleIOValueTypeEnum.string
}
}
]
};

View File

@@ -5,14 +5,14 @@ import {
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
ModuleDataTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
} from '../../constants';
import {
Input_Template_History,
Input_Template_TFSwitch,
Input_Template_Switch,
Input_Template_UserChatInput
} from '../input';
import { Output_Template_Finish } from '../output';
@@ -26,11 +26,11 @@ export const RunAppModule: FlowModuleTemplateType = {
intro: '可以选择一个其他应用进行调用',
showStatus: true,
inputs: [
Input_Template_TFSwitch,
Input_Template_Switch,
{
key: ModuleInputKeyEnum.runAppSelectApp,
type: FlowNodeInputTypeEnum.selectApp,
valueType: ModuleDataTypeEnum.selectApp,
valueType: ModuleIOValueTypeEnum.selectApp,
label: '选择一个应用',
description: '选择一个其他应用进行调用',
required: true,
@@ -45,7 +45,7 @@ export const RunAppModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.history,
label: '新的上下文',
description: '将该应用回复内容拼接到历史记录中,作为新的上下文返回',
valueType: ModuleDataTypeEnum.chatHistory,
valueType: ModuleIOValueTypeEnum.chatHistory,
type: FlowNodeOutputTypeEnum.source,
targets: []
},
@@ -53,7 +53,7 @@ export const RunAppModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.answerText,
label: 'AI回复',
description: '将在应用完全结束后触发',
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.source,
targets: []
},

View File

@@ -1,7 +1,7 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import { userGuideTip } from '../tip';
import { ModuleDataTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, ModuleTemplateTypeEnum } from '../../constants';
export const UserGuideModule: FlowModuleTemplateType = {
id: FlowNodeTypeEnum.userGuide,
@@ -14,7 +14,7 @@ export const UserGuideModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.welcomeText,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
label: '开场白',
showTargetInApp: false,
showTargetInPlugin: false
@@ -22,7 +22,7 @@ export const UserGuideModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.variables,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
label: '对话框变量',
value: [],
showTargetInApp: false,
@@ -30,7 +30,7 @@ export const UserGuideModule: FlowModuleTemplateType = {
},
{
key: ModuleInputKeyEnum.questionGuide,
valueType: ModuleDataTypeEnum.boolean,
valueType: ModuleIOValueTypeEnum.boolean,
type: FlowNodeInputTypeEnum.switch,
label: '问题引导',
showTargetInApp: false,
@@ -39,7 +39,7 @@ export const UserGuideModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.tts,
type: FlowNodeInputTypeEnum.hidden,
valueType: ModuleDataTypeEnum.any,
valueType: ModuleIOValueTypeEnum.any,
label: '语音播报',
showTargetInApp: false,
showTargetInPlugin: false

View File

@@ -5,7 +5,7 @@ import {
} from '../../node/constant';
import { FlowModuleTemplateType } from '../../type.d';
import {
ModuleDataTypeEnum,
ModuleIOValueTypeEnum,
ModuleInputKeyEnum,
ModuleOutputKeyEnum,
ModuleTemplateTypeEnum
@@ -22,7 +22,7 @@ export const UserInputModule: FlowModuleTemplateType = {
{
key: ModuleInputKeyEnum.userChatInput,
type: FlowNodeInputTypeEnum.systemInput,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
label: '用户问题',
showTargetInApp: false,
showTargetInPlugin: false
@@ -33,7 +33,7 @@ export const UserInputModule: FlowModuleTemplateType = {
key: ModuleOutputKeyEnum.userChatInput,
label: '用户问题',
type: FlowNodeOutputTypeEnum.source,
valueType: ModuleDataTypeEnum.string,
valueType: ModuleIOValueTypeEnum.string,
targets: []
}
]

View File

@@ -1,5 +1,5 @@
import { FlowNodeTypeEnum } from './node/constant';
import { ModuleDataTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants';
import { ModuleIOValueTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
export type FlowModuleTemplateType = {
@@ -72,4 +72,5 @@ export type ContextExtractAgentItemType = {
desc: string;
key: string;
required: boolean;
enum?: string;
};

View File

@@ -1,7 +1,8 @@
import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from './node/constant';
import { ModuleDataTypeEnum, ModuleInputKeyEnum } from './constants';
import { ModuleIOValueTypeEnum, ModuleInputKeyEnum } from './constants';
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type';
import { AppTTSConfigType, ModuleItemType, VariableItemType } from './type';
import { Input_Template_Switch } from './template/input';
export const getGuideModule = (modules: ModuleItemType[]) =>
modules.find((item) => item.flowType === FlowNodeTypeEnum.userGuide);
@@ -29,42 +30,64 @@ export const splitGuideModule = (guideModules?: ModuleItemType) => {
};
};
export function formatPluginToPreviewModule(
export const getOrInitModuleInputValue = (input: FlowNodeInputItemType) => {
if (input.value !== undefined || !input.valueType) return input.value;
const map: Record<string, any> = {
[ModuleIOValueTypeEnum.boolean]: false,
[ModuleIOValueTypeEnum.number]: 0,
[ModuleIOValueTypeEnum.string]: ''
};
return map[input.valueType];
};
export const getModuleInputUiField = (input: FlowNodeInputItemType) => {
if (input.type === FlowNodeInputTypeEnum.input || input.type === FlowNodeInputTypeEnum.textarea) {
return {
placeholder: input.placeholder || input.description
};
}
return {};
};
export function plugin2ModuleIO(
pluginId: string,
modules: ModuleItemType[]
): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} {
function getPluginTemplatePluginIdInput(pluginId: string): FlowNodeInputItemType {
return {
key: ModuleInputKeyEnum.pluginId,
type: FlowNodeInputTypeEnum.hidden,
label: 'pluginId',
value: pluginId,
valueType: ModuleDataTypeEnum.string,
connected: true,
showTargetInApp: false,
showTargetInPlugin: false
};
}
const pluginInput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginInput);
const customOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput);
const pluginOutput = modules.find((module) => module.flowType === FlowNodeTypeEnum.pluginOutput);
return {
inputs: pluginInput
? [
getPluginTemplatePluginIdInput(pluginId),
{
// plugin id
key: ModuleInputKeyEnum.pluginId,
type: FlowNodeInputTypeEnum.hidden,
label: 'pluginId',
value: pluginId,
valueType: ModuleIOValueTypeEnum.string,
connected: true,
showTargetInApp: false,
showTargetInPlugin: false
},
// switch
Input_Template_Switch,
...pluginInput.inputs.map((item) => ({
...item,
...getModuleInputUiField(item),
value: getOrInitModuleInputValue(item),
edit: false,
connected: false
}))
]
: [],
outputs: customOutput
? customOutput.outputs.map((item) => ({
outputs: pluginOutput
? pluginOutput.outputs.map((item) => ({
...item,
edit: false
}))

View File

@@ -1,4 +1,3 @@
import { ModuleTemplateTypeEnum } from '../module/constants';
import { ModuleItemType } from '../module/type';
export const defaultModules: ModuleItemType[] = [
@@ -28,13 +27,8 @@ export const defaultModules: ModuleItemType[] = [
}
];
export enum PluginTypeEnum {
export enum PluginSourceEnum {
personal = 'personal',
community = 'community',
commercial = 'commercial'
}
export const PluginType2TemplateTypeMap = {
[PluginTypeEnum.personal]: ModuleTemplateTypeEnum.personalPlugin,
[PluginTypeEnum.community]: ModuleTemplateTypeEnum.communityPlugin,
[PluginTypeEnum.commercial]: ModuleTemplateTypeEnum.commercialPlugin
};

View File

@@ -1,6 +1,6 @@
import { ModuleTemplateTypeEnum } from 'core/module/constants';
import type { ModuleItemType } from '../module/type.d';
import { PluginTypeEnum } from './constants';
import type { FlowModuleTemplateType, ModuleItemType } from '../module/type.d';
import { PluginSourceEnum } from './constants';
export type PluginItemSchema = {
_id: string;
@@ -16,11 +16,13 @@ export type PluginItemSchema = {
/* plugin template */
export type PluginTemplateType = {
author?: string;
id: string;
type: `${PluginTypeEnum}`;
source: `${PluginSourceEnum}`;
templateType: FlowModuleTemplateType['templateType'];
name: string;
avatar: string;
intro: string;
showStatus?: boolean;
modules: ModuleItemType[];
templateType?: `${ModuleTemplateTypeEnum}`;
};

View File

@@ -2,17 +2,14 @@
"name": "@fastgpt/global",
"version": "1.0.0",
"dependencies": {
"axios": "^1.5.1",
"cheerio": "1.0.0-rc.12",
"dayjs": "^1.11.7",
"openai": "4.23.0",
"encoding": "^0.1.13",
"js-tiktoken": "^1.0.7",
"node-html-markdown": "^1.3.0",
"openai": "^4.20.1",
"axios": "^1.5.1",
"timezones-list": "^3.0.2"
},
"devDependencies": {
"@types/node": "^20.8.5",
"@types/turndown": "^5.0.4"
"@types/node": "^20.8.5"
}
}

View File

@@ -0,0 +1,10 @@
{
"name": "@fastgpt/plugins",
"version": "1.0.0",
"dependencies": {},
"devDependencies": {
"@types/node": "^20.8.5",
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*"
}
}

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "es2015",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": "."
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../**/*.d.ts"],
"exclude": ["node_modules"]
}

View File

@@ -71,7 +71,7 @@ instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err)
export function request(url: string, data: any, config: ConfigType, method: Method): any {
if (!global.systemEnv || !global.systemEnv?.pluginBaseUrl) {
console.log('未部署商业版接口');
console.log('未部署商业版接口', url);
return Promise.reject('The The request was denied...');
}

View File

@@ -0,0 +1,95 @@
import { UrlFetchParams, UrlFetchResponse } from '@fastgpt/global/common/file/api';
import * as cheerio from 'cheerio';
import axios from 'axios';
import { htmlToMarkdown } from './markdown';
export const cheerioToHtml = ({
fetchUrl,
$,
selector
}: {
fetchUrl: string;
$: cheerio.CheerioAPI;
selector?: string;
}) => {
// get origin url
const originUrl = new URL(fetchUrl).origin;
const selectDom = $(selector || 'body');
// remove i element
selectDom.find('i,script').remove();
// remove empty a element
selectDom
.find('a')
.filter((i, el) => {
return $(el).text().trim() === '' && $(el).children().length === 0;
})
.remove();
// if link,img startWith /, add origin url
selectDom.find('a').each((i, el) => {
const href = $(el).attr('href');
if (href && href.startsWith('/')) {
$(el).attr('href', originUrl + href);
}
});
selectDom.find('img').each((i, el) => {
const src = $(el).attr('src');
if (src && src.startsWith('/')) {
$(el).attr('src', originUrl + src);
}
});
const html = selectDom
.map((item, dom) => {
return $(dom).html();
})
.get()
.join('\n');
return html;
};
export const urlsFetch = async ({
urlList,
selector
}: UrlFetchParams): Promise<UrlFetchResponse> => {
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));
const response = (
await Promise.all(
urlList.map(async (url) => {
try {
const fetchRes = await axios.get(url, {
timeout: 30000
});
const $ = cheerio.load(fetchRes.data);
const md = await htmlToMarkdown(
cheerioToHtml({
fetchUrl: url,
$,
selector
})
);
return {
url,
content: md
};
} catch (error) {
console.log(error, 'fetch error');
return {
url,
content: ''
};
}
})
)
).filter((item) => item.content);
return response;
};

View File

@@ -0,0 +1,26 @@
import { simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
import { Worker } from 'worker_threads';
import { getWorkerPath } from './utils';
/* html string to markdown */
export const htmlToMarkdown = (html?: string | null) =>
new Promise<string>((resolve, reject) => {
if (!html) return resolve('');
const start = Date.now();
// worker
const worker = new Worker(getWorkerPath('html2md'));
worker.on('message', (md: string) => {
worker.terminate();
resolve(simpleMarkdownText(md));
});
worker.on('error', (err) => {
worker.terminate();
reject(err);
});
worker.postMessage(html);
});

View File

@@ -0,0 +1,9 @@
export const getWorkerPath = (name: string) => {
// @ts-ignore
const isSubModule = !!global?.systemConfig;
const isProd = process.env.NODE_ENV === 'production';
return isProd
? `/app/worker/${name}.js`
: `../../${isSubModule ? 'FastGPT/' : ''}/worker/${name}.js`;
};

View File

@@ -10,7 +10,7 @@ export async function createQuestionGuide({
messages: ChatMessageItemType[];
model: string;
}) {
const ai = getAIApi(undefined, 48000);
const ai = getAIApi(undefined, 480000);
const data = await ai.chat.completions.create({
model: model,
temperature: 0,

View File

@@ -61,8 +61,8 @@ const ChatItemSchema = new Schema({
userBadFeedback: {
type: String
},
robotBadFeedback: {
type: String
customFeedbacks: {
type: [String]
},
adminFeedback: {
type: {
@@ -86,7 +86,7 @@ try {
ChatItemSchema.index({ chatId: 1 });
ChatItemSchema.index({ userGoodFeedback: 1 });
ChatItemSchema.index({ userBadFeedback: 1 });
ChatItemSchema.index({ robotBadFeedback: 1 });
ChatItemSchema.index({ customFeedbacks: 1 });
ChatItemSchema.index({ adminFeedback: 1 });
} catch (error) {
console.log(error);

View File

@@ -1,5 +1,6 @@
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { MongoChatItem } from './chatItemSchema';
import { addLog } from '../../common/system/log';
export async function getChatItems({
chatId,
@@ -20,3 +21,29 @@ export async function getChatItems({
return { history };
}
export const addCustomFeedbacks = async ({
chatId,
chatItemId,
feedbacks
}: {
chatId?: string;
chatItemId?: string;
feedbacks: string[];
}) => {
if (!chatId || !chatItemId) return;
try {
await MongoChatItem.findOneAndUpdate(
{
chatId,
dataId: chatItemId
},
{
$push: { customFeedbacks: { $each: feedbacks } }
}
);
} catch (error) {
addLog.error('addCustomFeedbacks error', error);
}
};

View File

@@ -4,7 +4,7 @@ import type { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter';
import { MongoDatasetTraining } from '../training/schema';
import { urlsFetch } from '@fastgpt/global/common/file/tools';
import { urlsFetch } from '../../../common/string/cheerio';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
/**
@@ -105,7 +105,8 @@ export const loadingOneChunkCollection = async ({
// split data
const { chunks } = splitText2Chunks({
text: newRawText,
chunkLen: collection.chunkSize || 512
chunkLen: collection.chunkSize || 512,
countTokens: false
});
// insert to training queue

View File

@@ -1,9 +1,10 @@
import { MongoPlugin } from './schema';
import { FlowModuleTemplateType } from '@fastgpt/global/core/module/type';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
import { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils';
import { PluginType2TemplateTypeMap, PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
import { plugin2ModuleIO } from '@fastgpt/global/core/module/utils';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
import { ModuleTemplateTypeEnum } from '@fastgpt/global/core/module/constants';
/*
plugin id rule:
@@ -14,84 +15,72 @@ import type { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
export async function splitCombinePluginId(id: string) {
const splitRes = id.split('-');
if (splitRes.length === 1 && id.length === 24) {
if (splitRes.length === 1) {
return {
type: PluginTypeEnum.personal,
source: PluginSourceEnum.personal,
pluginId: id
};
}
const [type, pluginId] = id.split('-') as [`${PluginTypeEnum}`, string];
if (!type || !pluginId) return Promise.reject('pluginId not found');
const [source, pluginId] = id.split('-') as [`${PluginSourceEnum}`, string];
if (!source || !pluginId) return Promise.reject('pluginId not found');
return { type, pluginId: id };
return { source, pluginId: id };
}
const getPluginTemplateById = async (id: string): Promise<PluginTemplateType> => {
const { source, pluginId } = await splitCombinePluginId(id);
if (source === PluginSourceEnum.community) {
const item = global.communityPlugins?.find((plugin) => plugin.id === pluginId);
if (!item) return Promise.reject('plugin not found');
return item;
}
if (source === PluginSourceEnum.personal) {
const item = await MongoPlugin.findById(id).lean();
if (!item) return Promise.reject('plugin not found');
return {
id: String(item._id),
name: item.name,
avatar: item.avatar,
intro: item.intro,
showStatus: true,
source: PluginSourceEnum.personal,
modules: item.modules,
templateType: ModuleTemplateTypeEnum.personalPlugin
};
}
return Promise.reject('plugin not found');
};
/* format plugin modules to plugin preview module */
export async function getPluginPreviewModule({
id
}: {
id: string;
}): Promise<FlowModuleTemplateType> {
// classify
const { type, pluginId } = await splitCombinePluginId(id);
const plugin = await getPluginTemplateById(id);
const plugin = await (async () => {
if (type === PluginTypeEnum.community) {
return global.communityPlugins?.find((plugin) => plugin.id === pluginId);
}
if (type === PluginTypeEnum.personal) {
const item = await MongoPlugin.findById(id);
if (!item) return undefined;
return {
id: String(item._id),
name: item.name,
avatar: item.avatar,
intro: item.intro,
type: PluginTypeEnum.personal,
modules: item.modules
};
}
})();
if (!plugin) return Promise.reject('plugin not found');
return {
id: plugin.id,
templateType: PluginType2TemplateTypeMap[plugin.type],
templateType: plugin.templateType,
flowType: FlowNodeTypeEnum.pluginModule,
avatar: plugin.avatar,
name: plugin.name,
intro: plugin.intro,
showStatus: true,
...formatPluginToPreviewModule(plugin.id, plugin.modules)
showStatus: plugin.showStatus,
...plugin2ModuleIO(plugin.id, plugin.modules)
};
}
/* run plugin time */
export async function getPluginRuntimeById(id: string): Promise<PluginTemplateType> {
const { type, pluginId } = await splitCombinePluginId(id);
const plugin = await (async () => {
if (type === PluginTypeEnum.community) {
return global.communityPlugins?.find((plugin) => plugin.id === pluginId);
}
if (type === PluginTypeEnum.personal) {
const item = await MongoPlugin.findById(id);
if (!item) return undefined;
return {
id: String(item._id),
name: item.name,
avatar: item.avatar,
intro: item.intro,
type: PluginTypeEnum.personal,
modules: item.modules
};
}
})();
if (!plugin) return Promise.reject('plugin not found');
const plugin = await getPluginTemplateById(id);
return {
id: plugin.id,
type: plugin.type,
source: plugin.source,
templateType: plugin.templateType,
name: plugin.name,
avatar: plugin.avatar,
intro: plugin.intro,

View File

@@ -3,22 +3,24 @@
"version": "1.0.0",
"dependencies": {
"@fastgpt/global": "workspace:*",
"axios": "^1.5.1",
"cookie": "^0.5.0",
"encoding": "^0.1.13",
"jsonwebtoken": "^9.0.2",
"mongoose": "^7.0.2",
"nanoid": "^4.0.1",
"dayjs": "^1.11.7",
"next": "13.5.2",
"multer": "1.4.5-lts.1",
"axios": "^1.5.1",
"cheerio": "1.0.0-rc.12",
"nextjs-cors": "^2.1.2",
"pg": "^8.10.0",
"tunnel": "^0.0.6",
"dayjs": "^1.11.7"
"tunnel": "^0.0.6"
},
"devDependencies": {
"@types/cookie": "^0.5.2",
"@types/jsonwebtoken": "^9.0.3",
"@types/node": "^20.8.5",
"@types/multer": "^1.4.10",
"@types/pg": "^8.6.6",
"@types/tunnel": "^0.0.4"
}

View File

@@ -31,3 +31,11 @@ export async function authCertOrShareId({
canWrite: false
};
}
/* auth the request from local service */
export const authRequestFromLocal = ({ req }: AuthModeType) => {
const host = `${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000}`;
if (host !== req.headers.host) {
return Promise.reject('Invalid request');
}
};

View File

@@ -7,7 +7,7 @@ import { MongoPlugin } from '../../../core/plugin/schema';
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
import { splitCombinePluginId } from '../../../core/plugin/controller';
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
export async function authPluginCrud({
id,
@@ -66,13 +66,13 @@ export async function authPluginCanUse({
teamId: string;
tmbId: string;
}) {
const { type, pluginId } = await splitCombinePluginId(id);
const { source, pluginId } = await splitCombinePluginId(id);
if (type === PluginTypeEnum.community) {
if (source === PluginSourceEnum.community) {
return true;
}
if (type === PluginTypeEnum.personal) {
if (source === PluginSourceEnum.personal) {
const { role } = await getTeamInfoByTmbId({ tmbId });
const plugin = await MongoPlugin.findOne({ _id: pluginId, teamId });
if (!plugin) {

View File

@@ -1,8 +1,6 @@
{
"name": "@fastgpt/web",
"version": "1.0.0",
"dependencies": {
"axios": "^1.5.1"
},
"dependencies": {},
"devDependencies": {}
}

3791
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,10 @@
module.exports = {
i18n: {
defaultLocale: 'zh',
locales: ['en', 'zh', 'zh-Hans', 'zh-CN'],
localeDetection: false
locales: ['en', 'zh'],
localeDetection: true
},
localePath:
typeof window === 'undefined' ? require('path').resolve('./public/locales') : '/locales',
typeof window === 'undefined' ? require('path').resolve('./public/locales') : '/public/locales',
reloadOnPrerender: process.env.NODE_ENV === 'development'
};

View File

@@ -45,7 +45,7 @@ const nextConfig = {
},
transpilePackages: ['@fastgpt/*'],
experimental: {
serverComponentsExternalPackages: ['mongoose', 'winston', 'winston-mongodb', 'pg'],
serverComponentsExternalPackages: ['mongoose', 'pg'],
outputFileTracingRoot: path.join(__dirname, '../../')
}
};

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.6.4",
"version": "4.6.5",
"private": false,
"scripts": {
"dev": "next dev",
@@ -10,45 +10,39 @@
},
"dependencies": {
"@chakra-ui/anatomy": "^2.2.1",
"@chakra-ui/icons": "^2.0.17",
"@chakra-ui/react": "^2.7.0",
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/next-js": "^2.1.5",
"@chakra-ui/react": "^2.8.1",
"@chakra-ui/styled-system": "^2.9.1",
"@chakra-ui/system": "^2.5.8",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@chakra-ui/system": "^2.6.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fastgpt/plugins": "workspace:*",
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*",
"@fastgpt/web": "workspace:*",
"@node-rs/jieba": "^1.7.2",
"@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0",
"axios": "^1.5.1",
"date-fns": "^2.30.0",
"dayjs": "^1.11.7",
"downloadjs": "^1.4.7",
"echarts": "^5.4.1",
"next": "13.5.2",
"echarts-gl": "^2.0.9",
"formidable": "^2.1.1",
"framer-motion": "^9.0.6",
"hyperdown": "^2.4.29",
"i18next": "^22.5.1",
"immer": "^9.0.19",
"jschardet": "^3.0.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"mammoth": "^1.6.0",
"mermaid": "^10.2.3",
"multer": "1.4.5-lts.1",
"nanoid": "^4.0.1",
"next": "13.5.2",
"next-i18next": "^13.3.0",
"nprogress": "^0.2.0",
"papaparse": "^5.4.1",
"react": "18.2.0",
"react-day-picker": "^8.7.1",
"react-dom": "18.2.0",
"react-hook-form": "^7.43.1",
"react-i18next": "^12.3.1",
"react-markdown": "^8.0.7",
"react-syntax-highlighter": "^15.5.0",
"reactflow": "^11.7.4",
@@ -58,28 +52,28 @@
"remark-math": "^5.1.1",
"request-ip": "^3.3.0",
"sass": "^1.58.3",
"zustand": "^4.3.5"
"zustand": "^4.3.5",
"i18next": "^22.5.1",
"next-i18next": "^13.3.0",
"react-i18next": "^12.3.1",
"axios": "^1.5.1",
"nanoid": "^4.0.1",
"dayjs": "^1.11.7"
},
"devDependencies": {
"@svgr/webpack": "^6.5.1",
"@types/downloadjs": "^1.4.3",
"@types/formidable": "^2.0.5",
"@types/js-cookie": "^3.0.3",
"@types/jsonwebtoken": "^9.0.3",
"@types/lodash": "^4.14.191",
"@types/multer": "^1.4.10",
"@types/node": "^20.8.5",
"@types/papaparse": "^5.3.7",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"@types/react": "18.2.0",
"@types/react-dom": "18.2.0",
"@types/react-syntax-highlighter": "^15.5.6",
"@types/request-ip": "^0.0.37",
"eslint": "8.34.0",
"eslint-config-next": "13.1.6",
"typescript": "4.9.5"
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.6.0"
}
}

View File

@@ -5,7 +5,7 @@
3. 新增 - 分享链接更多嵌入方式提示更多DIY方式。
4. 优化 - 历史记录模块。弃用旧的历史记录模块,直接在对应地方填写数值即可。
5. 调整 - 知识库搜索模块 topk 逻辑,采用 MaxToken 计算,兼容不同长度的文本块
6. 链接读取支持多选择器。参考[Web 站点同步用法](https://doc.fastgpt.in/docs/course/webSync)
6. 链接读取支持多选择器。参考[Web 站点同步用法](https://doc.fastgpt.in/docs/course/websync)
7. [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/)
8. [知识库提示词详解](https://doc.fastgpt.in/docs/use-cases/ai_settings/#引用模板--引用提示词)
9. [使用文档](https://doc.fastgpt.in/docs/intro/)

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702637232008" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6146" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M261.688889 194.218667A67.470222 67.470222 0 1 1 194.218667 261.688889 67.584 67.584 0 0 1 261.688889 194.218667M261.688889 136.533333a125.155556 125.155556 0 1 0 125.155555 125.155556 125.155556 125.155556 0 0 0-125.155555-125.155556zM614.4 444.529778A67.470222 67.470222 0 1 1 547.271111 512a67.584 67.584 0 0 1 67.128889-67.470222M614.4 386.844444a125.155556 125.155556 0 1 0 125.155556 125.155556 125.155556 125.155556 0 0 0-125.155556-125.155556zM408.120889 694.840889A67.470222 67.470222 0 1 1 340.650667 762.311111a67.470222 67.470222 0 0 1 67.470222-67.470222m0-57.685333a125.155556 125.155556 0 1 0 125.155555 125.155555 125.155556 125.155556 0 0 0-125.155555-125.155555z" fill="#1DCCA1" p-id="6147"></path><path d="M489.016889 736.142222h375.011555v52.337778H489.016889zM325.632 788.48h-42.894222C171.804444 788.48 113.777778 711.452444 113.777778 635.335111a143.36 143.36 0 0 1 43.235555-103.651555c30.833778-30.037333 74.296889-45.511111 125.724445-45.511112h242.119111v52.337778H282.737778c-80.554667 0-116.622222 48.810667-116.622222 97.166222s36.522667 100.807111 116.622222 100.807112h42.894222zM743.537778 538.168889h-40.618667v-52.337778h40.618667c78.620444 0 114.346667-51.541333 114.346666-99.555555s-35.384889-98.417778-114.346666-98.417778H337.123556v-52.337778h406.414222C853.333333 235.52 910.222222 311.409778 910.222222 386.844444a147.911111 147.911111 0 0 1-42.780444 104.903112 168.732444 168.732444 0 0 1-123.904 46.421333z" fill="#1DCCA1" p-id="6148"></path><path d="M910.222222 762.311111l-136.533333 102.4V659.911111l136.533333 102.4z" fill="#1DCCA1" p-id="6149"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702446643259" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4270" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M247.0912 996.1472 100.5568 996.1472c-39.936 0-72.4992-32.5632-72.4992-72.4992L28.0576 81.92c0-39.936 32.5632-72.4992 72.4992-72.4992l766.0544 0c39.936 0 72.4992 32.5632 72.4992 72.4992l0 210.5344c0 12.3904-10.0352 22.528-22.528 22.528s-22.528-10.0352-22.528-22.528L894.0544 81.92c0-15.1552-12.288-27.5456-27.5456-27.5456L100.5568 54.3744c-15.1552 0-27.5456 12.288-27.5456 27.5456L73.0112 923.648c0 15.1552 12.288 27.5456 27.5456 27.5456l146.5344 0c12.3904 0 22.528 10.0352 22.528 22.528S259.4816 996.1472 247.0912 996.1472z" fill="#FF9000" p-id="4271"></path><path d="M745.2672 192.1024 174.6944 192.1024c-12.3904 0-22.528-10.0352-22.528-22.528s10.0352-22.528 22.528-22.528l570.5728 0c12.3904 0 22.528 10.0352 22.528 22.528S757.6576 192.1024 745.2672 192.1024z" fill="#FF9000" p-id="4272"></path><path d="M437.6576 429.6704 174.6944 429.6704c-12.3904 0-22.528-10.0352-22.528-22.528s10.0352-22.528 22.528-22.528l262.9632 0c12.3904 0 22.528 10.0352 22.528 22.528S450.1504 429.6704 437.6576 429.6704z" fill="#FF9000" p-id="4273"></path><path d="M620.6464 310.8864 174.6944 310.8864c-12.3904 0-22.528-10.0352-22.528-22.528s10.0352-22.528 22.528-22.528l445.952 0c12.3904 0 22.528 10.0352 22.528 22.528S633.1392 310.8864 620.6464 310.8864z" fill="#FF9000" p-id="4274"></path><path d="M399.6672 1009.8688c-6.2464 0-12.288-2.56-16.5888-7.2704-5.2224-5.7344-7.168-13.7216-5.12-21.2992l40.8576-146.6368c1.024-3.6864 3.072-7.168 5.7344-9.8304l408.9856-408.9856c14.1312-14.0288 36.9664-14.0288 51.0976 0l97.792 97.792c6.8608 6.8608 10.5472 15.872 10.5472 25.4976s-3.7888 18.7392-10.5472 25.4976L928.8704 618.496c-4.1984 4.1984-9.9328 6.5536-15.872 6.5536s-11.6736-2.3552-15.872-6.5536l-66.048-66.048c-8.8064-8.8064-8.8064-23.04 0-31.8464s23.04-8.8064 31.8464 0l50.176 50.176 31.4368-31.4368L859.136 454.0416 460.6976 852.48 431.104 958.6688 546.7136 936.96l231.7312-231.7312c5.0176-5.4272 50.7904-52.6336 107.2128-56.7296 12.3904-0.9216 23.1424 8.3968 24.064 20.7872 0.9216 12.3904-8.3968 23.1424-20.7872 24.064-40.3456 2.9696-77.4144 42.2912-77.824 42.7008-0.2048 0.2048-0.4096 0.512-0.7168 0.7168L573.5424 973.7216c-3.1744 3.1744-7.2704 5.3248-11.776 6.2464l-158.0032 29.5936C402.432 1009.7664 401.1008 1009.8688 399.6672 1009.8688z" fill="#FF9000" p-id="4275"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702452532573" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5265" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M28.444444 142.222222v739.555556c0 62.577778 51.2 113.777778 113.777778 113.777778h739.555556c62.577778 0 113.777778-51.2 113.777778-113.777778V142.222222c0-62.577778-51.2-113.777778-113.777778-113.777778H142.222222C79.644444 28.444444 28.444444 79.644444 28.444444 142.222222z m830.577778 773.688889H164.977778c-31.288889 0-56.888889-25.6-56.888889-56.888889V164.977778c0-31.288889 25.6-56.888889 56.888889-56.888889h691.2c31.288889 0 56.888889 25.6 56.888889 56.888889v691.2c2.844444 34.133333-22.755556 59.733333-54.044445 59.733333z" fill="#48C9B0" p-id="5266"></path><path d="M765.155556 756.622222l-8.533334-8.533333-22.755555-22.755556 31.288889-31.288889c17.066667-17.066667 17.066667-39.822222 0-56.888888-17.066667-17.066667-42.666667-17.066667-56.888889 0l-31.288889 31.288888-36.977778-36.977777c-17.066667-17.066667-39.822222-17.066667-56.888889 0-17.066667 17.066667-17.066667 42.666667 0 56.888889l36.977778 36.977777-34.133333 34.133334c-17.066667 17.066667-17.066667 42.666667 0 56.888889 17.066667 17.066667 39.822222 17.066667 56.888888 0l34.133334-34.133334 22.755555 22.755556 8.533334 8.533333c17.066667 17.066667 39.822222 17.066667 56.888889 0s14.222222-42.666667 0-56.888889zM355.555556 426.666667l136.533333-136.533334c17.066667-17.066667 17.066667-42.666667 0-56.888889-17.066667-17.066667-42.666667-17.066667-56.888889 0l-99.555556 99.555556-48.355555-48.355556c-17.066667-17.066667-42.666667-17.066667-56.888889 0-17.066667 17.066667-17.066667 39.822222 0 56.888889l85.333333 85.333334c11.377778 11.377778 28.444444 11.377778 39.822223 0zM753.777778 270.222222c-17.066667-17.066667-42.666667-17.066667-56.888889 0l-426.666667 426.666667c-17.066667 17.066667-17.066667 42.666667 0 56.888889 17.066667 17.066667 42.666667 17.066667 56.888889 0l426.666667-426.666667c17.066667-14.222222 17.066667-39.822222 0-56.888889z" fill="#48C9B0" p-id="5267"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -22,7 +22,6 @@
"Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations",
"Chat logs": "Chat Logs",
"Confirm Del App Tip": "Confirm to delete the app and all its chats",
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
"Connection is invalid": "Connecting is invalid",
"Connection type is different": "Connection type is different",
"Copy Module Config": "Copy config",
@@ -52,7 +51,7 @@
"My Modules": "My Custom Modules",
"No Modules": "No module",
"System Module": "System Module",
"type": "{{type}}\n{{example}}"
"type": "{{type}}\n{{description}}"
},
"modules": {
"Title is required": "Title is required"
@@ -89,32 +88,6 @@
"share": "Share",
"test": "Test Chat "
},
"response": {
"module cq": "Question classification list",
"module cq result": "Classification Result",
"module extract description": "Extract Description",
"module extract result": "Extract Result",
"module historyPreview": "Messages",
"module http body": "Body",
"module http result": "Response",
"module http url": "Request Url",
"module limit": "Count Limit",
"module maxToken": "MaxTokens",
"module model": "Model",
"module name": "Name",
"module price": "Price",
"module query": "Question/Query",
"module question": "Question",
"module quoteList": "Quotes",
"module runningTime": "Time",
"module search query": "Query",
"module search response": "Search Result",
"module similarity": "Similarity",
"module temperature": "Temperature",
"module time": "Running Time",
"module tokens": "Tokens",
"plugin output": "Plugin Output"
},
"retry": "Retry"
},
"common": {
@@ -239,6 +212,11 @@
"system": {
"Help Chatbot": "Chatbot Helper",
"Use Helper": "UsingHelp"
},
"ui": {
"textarea": {
"Magnifying": "Magnifying"
}
}
},
"core": {
@@ -262,6 +240,17 @@
"TTS Tip": "After this function is enabled, the voice playback function can be used after each conversation. Use of this feature may incur additional charges.",
"Welcome Text": "Welcome Text",
"create app": "Create App",
"edit": {
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
"Out Ad Edit": "You are about to exit the Advanced orchestration page, please confirm",
"Prompt Editor": "Prompt Editor",
"Save and out": "Save out",
"UnSave": "UnSave"
},
"feedback": {
"Custom feedback": "Custom feedback",
"close custom feedback": "Close Feedback"
},
"logs": {
"Source And Time": "Source & Time"
},
@@ -325,6 +314,34 @@
"Read Quote": "Read Quote",
"Read Source": "Read Source"
},
"response": {
"context total length": "Context Length",
"module cq": "Question classification list",
"module cq result": "Classification Result",
"module extract description": "Extract Description",
"module extract result": "Extract Result",
"module historyPreview": "Messages",
"module http body": "Body",
"module http result": "Response",
"module http url": "Request Url",
"module limit": "Count Limit",
"module maxToken": "MaxTokens",
"module model": "Model",
"module name": "Name",
"module price": "Price",
"module query": "Question/Query",
"module question": "Question",
"module quoteList": "Quotes",
"module runningTime": "Time",
"module search query": "Query",
"module search response": "Search Result",
"module similarity": "Similarity",
"module temperature": "Temperature",
"module time": "Running Time",
"module tokens": "Tokens",
"plugin output": "Plugin Output",
"text output": "Text Output"
},
"tts": {
"Stop Speech": "Stop"
}
@@ -506,14 +523,42 @@
"Plugin output must connect": "Custom outputs must all be connected",
"Variable": "Variable",
"Variable Setting": "Variable Setting",
"edit": {
"Field Already Exist": "Key already exist",
"Field Edit": "Field Edit"
},
"extract": {
"Enum Description": "Lists the possible values for the field, one per row",
"Enum Value": "Enum",
"Field Description Placeholder": "Name/age /sql statement......",
"Field Setting Title": "Extract field configuration"
},
"input": {
"Add Input": "Add Input",
"Input Number": "Input: {{length}}",
"description": {
"Http Request Header": "",
"Http Request Url": "",
"TFSwitch textarea": "",
"anyInput": "",
"dynamic input": "",
"textEditor textarea": "The passed variable can be referenced by {{key}}."
},
"label": {
"chat history": "",
"switch": "",
"user question": ""
"Http Request Header": "",
"Http Request Method": "",
"Http Request Url": "",
"TFSwitch textarea": "",
"anyInput": "",
"chat history": "chat history",
"switch": "Switch",
"textEditor textarea": "Text Edit",
"user question": "User question"
}
},
"inputType": {
"chat history": "History",
"dynamicTargetInput": "dynamic Target Input",
"input": "Input",
"selectApp": "App Selector",
"selectChatModel": "Select Chat Model",
@@ -523,18 +568,34 @@
"textarea": "Textarea"
},
"output": {
"Add Output": "Add Output",
"Output Number": "Output: {{length}}",
"description": {
"running done": "running done"
"running done": "Triggered when the module call ends"
},
"label": {
"running done": "running done"
"result false": "",
"result true": "",
"running done": "End of module call ",
"text": "Text output"
}
},
"template": {
"TFSwitch": "",
"TFSwitch intro": "",
"UnKnow Module": "UnKnow Module",
"textEditor": "Text Editor",
"textEditor intro": "Output of fixed or incoming text after edit"
},
"textEditor": {
"Text Edit": "Text Edit"
},
"valueType": {
"any": "Any",
"boolean": "Boolean",
"chatHistory": "History",
"datasetQuote": "Dataset Quote",
"dynamicTargetInput": "Dynamic Input",
"number": "Number",
"selectApp": "Select App",
"selectDataset": "Select Dataset",

View File

@@ -22,7 +22,6 @@
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
"Chat logs": "对话日志",
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
"Connection is invalid": "连接无效",
"Connection type is different": "连接的类型不一致",
"Copy Module Config": "复制配置",
@@ -52,7 +51,7 @@
"My Modules": "",
"No Modules": "还没有模块~",
"System Module": "系统模块",
"type": "\"{{type}}\"类型\n{{example}}"
"type": "\"{{type}}\"类型\n{{description}}"
},
"modules": {
"Title is required": "模块名不能为空"
@@ -89,32 +88,6 @@
"share": "外部链接调用",
"test": "测试"
},
"response": {
"module cq": "问题分类列表",
"module cq result": "分类结果",
"module extract description": "提取要求描述",
"module extract result": "提取结果",
"module historyPreview": "完整记录",
"module http body": "请求体",
"module http result": "响应体",
"module http url": "请求地址",
"module limit": "单次搜索上限",
"module maxToken": "最大 Tokens",
"module model": "模型",
"module name": "模型名",
"module price": "计费",
"module query": "问题/检索词",
"module question": "问题",
"module quoteList": "引用内容",
"module runningTime": "运行时长",
"module search query": "检索词",
"module search response": "搜索结果",
"module similarity": "相似度",
"module temperature": "温度",
"module time": "运行时长",
"module tokens": "Tokens",
"plugin output": "插件输出值"
},
"retry": "重新生成"
},
"common": {
@@ -239,6 +212,11 @@
"system": {
"Help Chatbot": "机器人助手",
"Use Helper": "使用帮助"
},
"ui": {
"textarea": {
"Magnifying": "放大"
}
}
},
"core": {
@@ -262,6 +240,17 @@
"TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。",
"Welcome Text": "对话开场白",
"create app": "创建属于你的 AI 应用",
"edit": {
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
"Out Ad Edit": "您即将退出高级编排页面,请确认",
"Prompt Editor": "提示词编辑",
"Save and out": "保存并退出",
"UnSave": "不保存"
},
"feedback": {
"Custom feedback": "自定义反馈",
"close custom feedback": "关闭反馈"
},
"logs": {
"Source And Time": "来源 & 时间"
},
@@ -325,6 +314,34 @@
"Read Quote": "查看引用",
"Read Source": "查看来源"
},
"response": {
"context total length": "上下文总长度",
"module cq": "问题分类列表",
"module cq result": "分类结果",
"module extract description": "提取要求描述",
"module extract result": "提取结果",
"module historyPreview": "完整记录",
"module http body": "请求体",
"module http result": "响应体",
"module http url": "请求地址",
"module limit": "单次搜索上限",
"module maxToken": "最大 Tokens",
"module model": "模型",
"module name": "模型名",
"module price": "计费",
"module query": "问题/检索词",
"module question": "问题",
"module quoteList": "引用内容",
"module runningTime": "运行时长",
"module search query": "检索词",
"module search response": "搜索结果",
"module similarity": "相似度",
"module temperature": "温度",
"module time": "运行时长",
"module tokens": "Tokens",
"plugin output": "插件输出值",
"text output": "文本输出"
},
"tts": {
"Stop Speech": "停止"
}
@@ -506,14 +523,42 @@
"Plugin output must connect": "自定义输出必须全部连接",
"Variable": "参数变量",
"Variable Setting": "变量设置",
"edit": {
"Field Already Exist": "key 重复",
"Field Edit": "字段编辑"
},
"extract": {
"Enum Description": "列举出该字段可能的值,每行一个",
"Enum Value": "枚举值",
"Field Description Placeholder": "姓名/年龄/sql语句……",
"Field Setting Title": "提取字段配置"
},
"input": {
"Add Input": "添加入参",
"Input Number": "入参: {{length}}",
"description": {
"Http Request Header": "自定义请求头请严格填入JSON字符串。\n1. 确保最后一个属性没有逗号\n2. 确保 key 包含双引号\n例如: {\"Authorization\":\"Bearer xxx\"}",
"Http Request Url": "新的HTTP请求地址。如果出现两个“请求地址”可以删除该模块重新加入会拉取最新的模块配置。",
"TFSwitch textarea": "允许定义一些字符串来实现 false 匹配,每行一个,支持正则表达式。",
"anyInput": "可传入任意内容",
"dynamic input": "接收用户动态添加的参数,会在运行时将这些参数平铺传入",
"textEditor textarea": "可以通过 {{key}} 的方式引用传入的变量。变量仅支持字符串或数字。"
},
"label": {
"Http Request Header": "请求头",
"Http Request Method": "请求方式",
"Http Request Url": "请求地址",
"TFSwitch textarea": "自定义 False 匹配规则",
"anyInput": "任意内容输入",
"chat history": "聊天记录",
"switch": "触发器",
"textEditor textarea": "文本编辑",
"user question": "用户问题"
}
},
"inputType": {
"chat history": "历史记录",
"dynamicTargetInput": "动态外部数据",
"input": "输入框",
"selectApp": "应用选择",
"selectChatModel": "对话模型选择",
@@ -523,18 +568,34 @@
"textarea": "段落输入"
},
"output": {
"Add Output": "添加出参",
"Output Number": "出参: {{length}}",
"description": {
"running done": "模块调用结束时触发"
},
"label": {
"running done": "模块调用结束"
"result false": "False",
"result true": "True",
"running done": "模块调用结束",
"text": "文本输出"
}
},
"template": {
"TFSwitch": "判断器",
"TFSwitch intro": "根据传入的内容进行 True False 输出。默认情况下,当传入的内容为 false, undefined, null, 0, none 时,会输出 false。你也可以增加一些自定义的字符串来补充输出 false 的内容。",
"UnKnow Module": "未知模块",
"textEditor": "文本加工",
"textEditor intro": "可对固定或传入的文本进行加工后输出"
},
"textEditor": {
"Text Edit": "文本加工"
},
"valueType": {
"any": "任意",
"boolean": "布尔",
"chatHistory": "聊天记录",
"datasetQuote": "引用内容",
"dynamicTargetInput": "动态字段输入",
"number": "数字",
"selectApp": "应用选择",
"selectDataset": "知识库选择",

View File

@@ -0,0 +1,32 @@
## 插件类型
xxx.json 文件
```ts
type TemplateType =
| 'userGuide'
| 'systemInput'
| 'tools'
| 'textAnswer'
| 'functionCall'
| 'externalCall'
| 'other';
type pluginType = {
author: string; // 填写作者信息
templateType: FlowModuleTemplateType['templateType'];
name: string;
avatar: string;
intro: string;
showStatus?: boolean; // 是否需要展示组件运行状态
modules: []; //直接从高级编排导出配置复制过来;
};
```
## 额外代码怎么写?
参考 `TFSwitch``TextEditor`,通过 HTTP 模块将数据转到一个接口中实现。提交到社区的插件,务必将所有代码都放置在 FastGPT 仓库中,可以在 `projects/app/src/pages/api/plugins` 下新建一个与**插件文件名相同**的子目录进行接口编辑。
## 需要装包怎么办?
可以在 `packages/plugins` 下创建一个与**插件文件名相同**的子目录进行编写,可在 plugins 目录下安装相关依赖。然后在 FastGPT 主项目的接口中通过 `@fastgpt/plugins/xxx` 引入。

View File

@@ -0,0 +1,319 @@
{
"author": "FastGPT Team",
"templateType": "other",
"name": "自定义反馈",
"avatar": "/imgs/module/customFeedback.svg",
"intro": "该模块被触发时,会给当前的对话记录增加一条反馈。可用于自动记录对话效果等。",
"showStatus": false,
"modules": [
{
"moduleId": "w90mfp",
"name": "定义插件输入",
"avatar": "/imgs/module/input.png",
"flowType": "pluginInput",
"showStatus": false,
"position": {
"x": 515.1887815471657,
"y": -169.04905809653783
},
"inputs": [
{
"key": "defaultFeedback",
"valueType": "string",
"label": "默认反馈内容",
"type": "textarea",
"required": false,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true,
"inputType": true
},
"connected": true
},
{
"key": "customFeedback",
"valueType": "string",
"label": "自定义反馈内容",
"type": "target",
"required": false,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true,
"inputType": true
},
"connected": true
}
],
"outputs": [
{
"key": "defaultFeedback",
"valueType": "string",
"label": "默认反馈内容",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "49de3g",
"key": "defaultFeedback"
}
]
},
{
"key": "customFeedback",
"valueType": "string",
"label": "自定义反馈内容",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "49de3g",
"key": "customFeedback"
}
]
}
]
},
{
"moduleId": "49de3g",
"name": "HTTP模块",
"avatar": "/imgs/module/http.png",
"flowType": "httpRequest",
"showStatus": true,
"position": {
"x": 1086.8929621216014,
"y": -451.7550009773506
},
"inputs": [
{
"key": "switch",
"type": "target",
"label": "core.module.input.label.switch",
"valueType": "any",
"showTargetInApp": true,
"showTargetInPlugin": true,
"connected": false
},
{
"key": "system_httpMethod",
"type": "select",
"valueType": "string",
"label": "core.module.input.label.Http Request Method",
"value": "POST",
"list": [
{
"label": "GET",
"value": "GET"
},
{
"label": "POST",
"value": "POST"
}
],
"required": true,
"showTargetInApp": false,
"showTargetInPlugin": false,
"connected": false
},
{
"key": "system_httpReqUrl",
"type": "input",
"valueType": "string",
"label": "core.module.input.label.Http Request Url",
"description": "core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"value": "/api/plugins/customFeedback",
"connected": false
},
{
"key": "system_httpHeader",
"type": "textarea",
"valueType": "string",
"label": "core.module.input.label.Http Request Header",
"description": "core.module.input.description.Http Request Header",
"placeholder": "core.module.input.description.Http Request Header",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"value": "",
"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
},
{
"valueType": "string",
"label": "defaultFeedback",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"connected": true,
"key": "defaultFeedback"
},
{
"key": "customFeedback",
"valueType": "string",
"label": "customFeedback",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"connected": true
},
{
"key": "system_addInputParam",
"type": "addInputParam",
"valueType": "any",
"label": "",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"inputType": "target",
"valueType": "string",
"required": true
},
"connected": false
}
],
"outputs": [
{
"key": "finish",
"label": "core.module.output.label.running done",
"description": "core.module.output.description.running done",
"valueType": "boolean",
"type": "source",
"targets": []
},
{
"key": "system_addOutputParam",
"type": "addOutputParam",
"valueType": "any",
"label": "",
"targets": [],
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"outputType": "source",
"valueType": "string"
}
},
{
"type": "source",
"valueType": "string",
"label": "response",
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"targets": [
{
"moduleId": "s15f3v",
"key": "text"
}
],
"key": "response"
}
]
},
{
"moduleId": "s15f3v",
"name": "指定回复",
"avatar": "/imgs/module/reply.png",
"flowType": "answerNode",
"position": {
"x": 1705.6337348182756,
"y": -37.53826066726282
},
"inputs": [
{
"key": "switch",
"type": "target",
"label": "core.module.input.label.switch",
"valueType": "any",
"showTargetInApp": true,
"showTargetInPlugin": true,
"connected": false
},
{
"key": "text",
"type": "textarea",
"valueType": "any",
"label": "回复的内容",
"description": "可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串",
"placeholder": "可以使用 \\n 来实现连续换行。\n可以通过外部模块输入实现回复外部模块输入时会覆盖当前填写的内容。\n如传入非字符串类型数据将会自动转成字符串",
"showTargetInApp": true,
"showTargetInPlugin": true,
"connected": true
}
],
"outputs": [
{
"key": "finish",
"label": "core.module.output.label.running done",
"description": "core.module.output.description.running done",
"valueType": "boolean",
"type": "source",
"targets": []
}
]
}
]
}

View File

@@ -0,0 +1,309 @@
{
"author": "FastGPT Team",
"templateType": "tools",
"name": "core.module.template.textEditor",
"avatar": "/imgs/module/textEditor.svg",
"intro": "core.module.template.textEditor intro",
"showStatus": false,
"modules": [
{
"moduleId": "w90mfp",
"name": "定义插件输入",
"avatar": "/imgs/module/input.png",
"flowType": "pluginInput",
"showStatus": false,
"position": {
"x": 616.4226348688949,
"y": -165.05298493910115
},
"inputs": [
{
"key": "textarea",
"valueType": "string",
"label": "core.module.input.label.textEditor textarea",
"type": "textarea",
"required": true,
"description": "core.module.input.description.textEditor textarea",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true,
"inputType": true
},
"connected": true
},
{
"key": "DYNAMIC_INPUT_KEY",
"valueType": "any",
"label": "字符串变量",
"type": "addInputParam",
"required": false,
"description": "可动态的添加字符串类型变量,在文本编辑中通过 {{key}} 使用变量。",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true,
"inputType": false
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"inputType": "target",
"valueType": "string",
"required": true
},
"connected": true
}
],
"outputs": [
{
"key": "textarea",
"valueType": "string",
"label": "core.module.input.label.textEditor textarea",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "49de3g",
"key": "text"
}
]
},
{
"key": "DYNAMIC_INPUT_KEY",
"valueType": "any",
"label": "字符串变量",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "49de3g",
"key": "DYNAMIC_INPUT_KEY"
}
]
}
]
},
{
"moduleId": "tze1ju",
"name": "定义插件输出",
"avatar": "/imgs/module/output.png",
"flowType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1607.7142331269126,
"y": -145.93201540017395
},
"inputs": [
{
"key": "text",
"valueType": "string",
"label": "core.module.output.label.text",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": false,
"dataType": true,
"inputType": false
},
"connected": true
}
],
"outputs": [
{
"key": "text",
"valueType": "string",
"label": "core.module.output.label.text",
"type": "source",
"edit": true,
"targets": []
}
]
},
{
"moduleId": "49de3g",
"name": "HTTP模块",
"avatar": "/imgs/module/http.png",
"flowType": "httpRequest",
"showStatus": true,
"position": {
"x": 1086.8929621216014,
"y": -451.7550009773506
},
"inputs": [
{
"key": "switch",
"type": "target",
"label": "core.module.input.label.switch",
"valueType": "any",
"showTargetInApp": true,
"showTargetInPlugin": true,
"connected": false
},
{
"key": "system_httpMethod",
"type": "select",
"valueType": "string",
"label": "core.module.input.label.Http Request Method",
"value": "POST",
"list": [
{
"label": "GET",
"value": "GET"
},
{
"label": "POST",
"value": "POST"
}
],
"required": true,
"showTargetInApp": false,
"showTargetInPlugin": false,
"connected": false
},
{
"key": "system_httpReqUrl",
"type": "input",
"valueType": "string",
"label": "core.module.input.label.Http Request Url",
"description": "core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"value": "/api/plugins/textEditor",
"connected": false
},
{
"key": "system_httpHeader",
"type": "textarea",
"valueType": "string",
"label": "core.module.input.label.Http Request Header",
"description": "core.module.input.description.Http Request Header",
"placeholder": "core.module.input.description.Http Request Header",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"value": "",
"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": true
},
{
"key": "text",
"valueType": "string",
"label": "text",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"connected": true
},
{
"key": "system_addInputParam",
"type": "addInputParam",
"valueType": "any",
"label": "",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"inputType": "target",
"valueType": "string",
"required": true
},
"connected": false
}
],
"outputs": [
{
"key": "finish",
"label": "core.module.output.label.running done",
"description": "core.module.output.description.running done",
"valueType": "boolean",
"type": "source",
"targets": []
},
{
"key": "system_addOutputParam",
"type": "addOutputParam",
"valueType": "any",
"label": "",
"targets": [],
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"outputType": "source",
"valueType": "string"
}
},
{
"type": "source",
"valueType": "string",
"key": "text",
"label": "core.module.output.label.text",
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"targets": [
{
"moduleId": "tze1ju",
"key": "text"
}
]
}
]
}
]
}

View File

@@ -0,0 +1,335 @@
{
"author": "FastGPT Team",
"templateType": "tools",
"name": "core.module.template.TFSwitch",
"avatar": "/imgs/module/tfSwitch.svg",
"intro": "core.module.template.TFSwitch intro",
"showStatus": false,
"modules": [
{
"moduleId": "w90mfp",
"name": "定义插件输入",
"avatar": "/imgs/module/input.png",
"flowType": "pluginInput",
"showStatus": false,
"position": {
"x": 616.4226348688949,
"y": -165.05298493910115
},
"inputs": [
{
"key": "input",
"valueType": "any",
"type": "target",
"label": "core.module.input.label.anyInput",
"required": true,
"edit": true,
"connected": true,
"description": "core.module.input.description.anyInput"
},
{
"key": "rule",
"valueType": "string",
"label": "core.module.input.label.TFSwitch textarea",
"type": "textarea",
"required": false,
"description": "core.module.input.description.TFSwitch textarea",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true,
"inputType": true
},
"connected": true
}
],
"outputs": [
{
"key": "input",
"valueType": "any",
"label": "core.module.input.label.anyInput",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "8kld99",
"key": "input"
}
]
},
{
"key": "rule",
"valueType": "string",
"label": "core.module.input.label.TFSwitch textarea",
"type": "source",
"edit": true,
"targets": [
{
"moduleId": "8kld99",
"key": "rule"
}
]
}
]
},
{
"moduleId": "tze1ju",
"name": "定义插件输出",
"avatar": "/imgs/module/output.png",
"flowType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1985.3791673445353,
"y": -144.90535546692078
},
"inputs": [
{
"key": "true",
"type": "target",
"valueType": "boolean",
"label": "True",
"required": true,
"edit": true,
"connected": true,
"description": ""
},
{
"key": "false",
"valueType": "boolean",
"label": "False",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": false,
"dataType": true,
"inputType": false
},
"connected": true
}
],
"outputs": [
{
"key": "true",
"valueType": "boolean",
"label": "True",
"type": "source",
"edit": true,
"targets": []
},
{
"key": "false",
"valueType": "boolean",
"label": "False",
"type": "source",
"edit": true,
"targets": []
}
]
},
{
"moduleId": "8kld99",
"name": "HTTP模块",
"avatar": "/imgs/module/http.png",
"flowType": "httpRequest",
"showStatus": true,
"position": {
"x": 1210.560012858087,
"y": -387.62433050951756
},
"inputs": [
{
"key": "switch",
"type": "target",
"label": "core.module.input.label.switch",
"valueType": "any",
"showTargetInApp": true,
"showTargetInPlugin": true,
"connected": false
},
{
"key": "system_httpMethod",
"type": "select",
"valueType": "string",
"label": "core.module.input.label.Http Request Method",
"value": "POST",
"list": [
{
"label": "GET",
"value": "GET"
},
{
"label": "POST",
"value": "POST"
}
],
"required": true,
"showTargetInApp": false,
"showTargetInPlugin": false,
"connected": false
},
{
"key": "system_httpReqUrl",
"type": "input",
"valueType": "string",
"label": "core.module.input.label.Http Request Url",
"description": "core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"value": "/api/plugins/TFSwitch",
"connected": false
},
{
"key": "system_httpHeader",
"type": "textarea",
"valueType": "string",
"label": "core.module.input.label.Http Request Header",
"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": "input",
"valueType": "any",
"label": "input",
"type": "target",
"required": true,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"connected": true
},
{
"key": "rule",
"valueType": "string",
"label": "rule",
"type": "target",
"required": false,
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"connected": true
},
{
"key": "system_addInputParam",
"type": "addInputParam",
"valueType": "any",
"label": "",
"required": false,
"showTargetInApp": false,
"showTargetInPlugin": false,
"editField": {
"key": true,
"name": true,
"description": true,
"required": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"inputType": "target",
"valueType": "string",
"required": true
},
"connected": false
}
],
"outputs": [
{
"key": "finish",
"label": "core.module.output.label.running done",
"description": "core.module.output.description.running done",
"valueType": "boolean",
"type": "source",
"targets": []
},
{
"key": "system_addOutputParam",
"type": "addOutputParam",
"valueType": "any",
"label": "",
"targets": [],
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"defaultEditField": {
"label": "",
"key": "",
"description": "",
"outputType": "source",
"valueType": "string"
}
},
{
"type": "source",
"valueType": "boolean",
"key": "true",
"label": "true",
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"targets": [
{
"moduleId": "tze1ju",
"key": "true"
}
]
},
{
"type": "source",
"valueType": "boolean",
"key": "false",
"label": "false",
"description": "",
"edit": true,
"editField": {
"key": true,
"name": true,
"description": true,
"dataType": true
},
"targets": [
{
"moduleId": "tze1ju",
"key": "false"
}
]
}
]
}
]
}

View File

@@ -2,7 +2,7 @@ import { useSpeech } from '@/web/common/hooks/useSpeech';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, Image, Spinner, Textarea } from '@chakra-ui/react';
import React, { useRef, useEffect, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useTranslation } from 'next-i18next';
import MyTooltip from '../MyTooltip';
import MyIcon from '../Icon';
import styles from './index.module.scss';
@@ -216,7 +216,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
pl={5}
alignItems={'center'}
bg={'white'}
color={'myBlue.600'}
color={'blue.500'}
visibility={isSpeaking && isTransCription ? 'visible' : 'hidden'}
>
<Spinner size={'sm'} mr={4} />
@@ -244,7 +244,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
alignItems={'center'}
justifyContent={'center'}
rounded={'md'}
color={'myBlue.600'}
color={'blue.500'}
top={0}
left={0}
bottom={0}
@@ -260,7 +260,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
h={'16px'}
color={'myGray.700'}
cursor={'pointer'}
_hover={{ color: 'myBlue.600' }}
_hover={{ color: 'blue.500' }}
position={'absolute'}
bg={'white'}
right={'-8px'}
@@ -396,7 +396,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
name={isSpeaking ? 'core/chat/stopSpeechFill' : 'core/chat/recordFill'}
width={['20px', '22px']}
height={['20px', '22px']}
color={'myBlue.600'}
color={'blue.500'}
/>
</MyTooltip>
</Flex>
@@ -415,7 +415,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
h={['28px', '32px']}
w={['28px', '32px']}
borderRadius={'md'}
bg={isSpeaking || isChatting ? '' : !havInput ? '#E5E5E5' : 'myBlue.600'}
bg={isSpeaking || isChatting ? '' : !havInput ? '#E5E5E5' : 'blue.500'}
cursor={havInput ? 'pointer' : 'not-allowed'}
lineHeight={1}
onClick={() => {

View File

@@ -105,7 +105,7 @@ const QuoteModal = ({
className="hover-data"
display={'none'}
alignItems={'center'}
color={'myBlue.600'}
color={'blue.500'}
href={`/dataset/detail?datasetId=${item.datasetId}&currentTab=dataCard&collectionId=${item.collectionId}`}
>
{t('core.dataset.Go Dataset')}
@@ -164,7 +164,7 @@ const QuoteModal = ({
cursor={'pointer'}
color={'myGray.600'}
_hover={{
color: 'myBlue.700'
color: 'blue.600'
}}
onClick={() => onclickEdit(item)}
/>

View File

@@ -147,7 +147,7 @@ const ResponseTags = ({
name="common/routePushLight"
w={'14px'}
cursor={'pointer'}
_hover={{ color: 'myBlue.600' }}
_hover={{ color: 'blue.500' }}
onClick={async (e) => {
e.stopPropagation();

View File

@@ -35,7 +35,7 @@ const SelectMarkCollection = ({
const theme = useTheme();
const [selectedDatasetId, setSelectedDatasetId] = useState<string>();
const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState<string[]>([]);
const { paths, parentId, setParentId, datasets, isFetching } = useDatasetSelect();
const { paths, setParentId, datasets, isFetching } = useDatasetSelect();
return (
<>
@@ -70,7 +70,7 @@ const SelectMarkCollection = ({
}}
{...(selected
? {
bg: 'myBlue.300'
bg: 'blue.200'
}
: {})}
onClick={() => {

View File

@@ -21,6 +21,7 @@ function Row({
value?: string | number;
rawDom?: React.ReactNode;
}) {
const { t } = useTranslation();
const theme = useTheme();
const val = value || rawDom;
const strValue = `${value}`;
@@ -29,7 +30,7 @@ function Row({
return val !== undefined && val !== '' && val !== 'undefined' ? (
<Box mb={3}>
<Box fontSize={['sm', 'md']} mb={isCodeBlock ? 0 : 1} flex={'0 0 90px'}>
{label}:
{t(label)}:
</Box>
<Box
borderRadius={'md'}
@@ -69,12 +70,12 @@ const WholeResponseModal = ({
alt={''}
w={['14px', '16px']}
/>
{item.moduleName}
{t(item.moduleName)}
</Flex>
),
id: `${i}`
})),
[response]
[response, t]
);
const [currentTab, setCurrentTab] = useState(`0`);
@@ -103,26 +104,33 @@ const WholeResponseModal = ({
<Tabs list={list} activeId={currentTab} onChange={setCurrentTab} />
</Box>
<Box py={2} px={4} flex={'1 0 0'} overflow={'auto'}>
<Row label={t('chat.response.module name')} value={activeModule?.moduleName} />
<Row label={t('core.chat.response.module name')} value={t(activeModule.moduleName)} />
{activeModule?.price !== undefined && (
<Row
label={t('chat.response.module price')}
label={t('core.chat.response.module price')}
value={`${formatPrice(activeModule?.price)}`}
/>
)}
<Row
label={t('chat.response.module time')}
label={t('core.chat.response.module time')}
value={`${activeModule?.runningTime || 0}s`}
/>
<Row label={t('chat.response.module tokens')} value={`${activeModule?.tokens}`} />
<Row label={t('chat.response.module model')} value={activeModule?.model} />
<Row label={t('chat.response.module query')} value={activeModule?.query} />
<Row label={t('core.chat.response.module tokens')} value={`${activeModule?.tokens}`} />
<Row label={t('core.chat.response.module model')} value={activeModule?.model} />
<Row label={t('core.chat.response.module query')} value={activeModule?.query} />
<Row
label={t('core.chat.response.context total length')}
value={activeModule?.contextTotalLen}
/>
{/* ai chat */}
<Row label={t('chat.response.module temperature')} value={activeModule?.temperature} />
<Row label={t('chat.response.module maxToken')} value={activeModule?.maxToken} />
<Row
label={t('chat.response.module historyPreview')}
label={t('core.chat.response.module temperature')}
value={activeModule?.temperature}
/>
<Row label={t('core.chat.response.module maxToken')} value={activeModule?.maxToken} />
<Row
label={t('core.chat.response.module historyPreview')}
rawDom={
activeModule.historyPreview ? (
<>
@@ -148,7 +156,7 @@ const WholeResponseModal = ({
/>
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
<Row
label={t('chat.response.module quoteList')}
label={t('core.chat.response.module quoteList')}
value={`~~~json\n${JSON.stringify(activeModule.quoteList, null, 2)}`}
/>
)}
@@ -161,27 +169,27 @@ const WholeResponseModal = ({
value={t(DatasetSearchModeMap[activeModule.searchMode]?.title)}
/>
)}
<Row label={t('chat.response.module similarity')} value={activeModule?.similarity} />
<Row label={t('chat.response.module limit')} value={activeModule?.limit} />
<Row label={t('core.chat.response.module similarity')} value={activeModule?.similarity} />
<Row label={t('core.chat.response.module limit')} value={activeModule?.limit} />
{/* classify question */}
<Row
label={t('chat.response.module cq')}
label={t('core.chat.response.module cq')}
value={(() => {
if (!activeModule?.cqList) return '';
return activeModule.cqList.map((item) => `* ${item.value}`).join('\n');
})()}
/>
<Row label={t('chat.response.module cq result')} value={activeModule?.cqResult} />
<Row label={t('core.chat.response.module cq result')} value={activeModule?.cqResult} />
{/* extract */}
<Row
label={t('chat.response.module extract description')}
label={t('core.chat.response.module extract description')}
value={activeModule?.extractDescription}
/>
{activeModule?.extractResult && (
<Row
label={t('chat.response.module extract result')}
label={t('core.chat.response.module extract result')}
value={`~~~json\n${JSON.stringify(activeModule?.extractResult, null, 2)}`}
/>
)}
@@ -189,13 +197,13 @@ const WholeResponseModal = ({
{/* http */}
{activeModule?.body && (
<Row
label={t('chat.response.module http body')}
label={t('core.chat.response.module http body')}
value={`~~~json\n${JSON.stringify(activeModule?.body, null, 2)}`}
/>
)}
{activeModule?.httpResult && (
<Row
label={t('chat.response.module http result')}
label={t('core.chat.response.module http result')}
value={`~~~json\n${JSON.stringify(activeModule?.httpResult, null, 2)}`}
/>
)}
@@ -203,10 +211,13 @@ const WholeResponseModal = ({
{/* plugin */}
{activeModule?.pluginOutput && (
<Row
label={t('chat.response.plugin output')}
label={t('core.chat.response.plugin output')}
value={`~~~json\n${JSON.stringify(activeModule?.pluginOutput, null, 2)}`}
/>
)}
{/* text editor */}
<Row label={t('core.chat.response.text output')} value={activeModule?.textOutput} />
</Box>
</Flex>
</MyModal>

View File

@@ -27,7 +27,8 @@ import {
BoxProps,
FlexProps,
Image,
Textarea
Textarea,
Checkbox
} from '@chakra-ui/react';
import { feConfigs } from '@/web/common/system/staticData';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
@@ -43,7 +44,11 @@ import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import { customAlphabet } from 'nanoid';
import { updateChatAdminFeedback, updateChatUserFeedback } from '@/web/core/chat/api';
import {
closeCustomFeedback,
updateChatAdminFeedback,
updateChatUserFeedback
} from '@/web/core/chat/api';
import type { AdminMarkType } from './SelectMarkCollection';
import MyIcon from '@/components/Icon';
@@ -63,6 +68,7 @@ import { splitGuideModule } from '@fastgpt/global/core/module/utils';
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
import MessageInput from './MessageInput';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import ChatBoxDivider from '../core/chat/Divider';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
@@ -492,7 +498,7 @@ const ChatBox = (
const colorMap = {
loading: 'myGray.700',
running: '#67c13b',
finish: 'myBlue.600'
finish: 'blue.500'
};
if (!isChatting) return;
const chatContent = chatHistory[chatHistory.length - 1];
@@ -500,7 +506,7 @@ const ChatBox = (
return {
bg: colorMap[chatContent.status] || colorMap.loading,
name: chatContent.moduleName || t('common.Loading')
name: t(chatContent.moduleName || '') || t('common.Loading')
};
}, [chatHistory, isChatting, t]);
/* style end */
@@ -517,7 +523,7 @@ const ChatBox = (
};
}, [router.query]);
// add guide text listener
// add listener
useEffect(() => {
const windowMessage = ({ data }: MessageEvent<{ type: 'sendPrompt'; text: string }>) => {
if (data?.type === 'sendPrompt' && data?.text) {
@@ -536,9 +542,9 @@ const ChatBox = (
});
return () => {
window.removeEventListener('message', windowMessage);
eventBus.off(EventNameEnum.sendQuestion);
eventBus.off(EventNameEnum.editQuestion);
window.removeEventListener('message', windowMessage);
};
}, [handleSubmit, resetInputVal, sendPrompt]);
@@ -660,7 +666,7 @@ const ChatBox = (
<Card
className="markdown"
{...MessageCardStyle}
bg={'myBlue.300'}
bg={'blue.200'}
borderRadius={'8px 0 8px 8px'}
textAlign={'left'}
>
@@ -853,16 +859,56 @@ const ChatBox = (
<ResponseTags responseData={item.responseData} isShare={!!shareId} />
{/* custom feedback */}
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
<Box>
<ChatBoxDivider
icon={'core/app/customFeedback'}
text={t('core.app.feedback.Custom feedback')}
/>
{item.customFeedbacks.map((text, i) => (
<Box key={`${text}${i}`}>
<MyTooltip label={t('core.app.feedback.close custom feedback')}>
<Checkbox
onChange={(e) => {
if (e.target.checked && appId && chatId && item.dataId) {
closeCustomFeedback({
appId,
chatId,
chatItemId: item.dataId,
index: i
});
// update dom
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === item.dataId
? {
...chatItem,
customFeedbacks: chatItem.customFeedbacks?.filter(
(item, index) => index !== i
)
}
: chatItem
)
);
}
console.log(e);
}}
>
{text}
</Checkbox>
</MyTooltip>
</Box>
))}
</Box>
)}
{/* admin mark content */}
{showMarkIcon && item.adminFeedback && (
<Box>
<Flex alignItems={'center'} py={2}>
<MyIcon name={'core/app/markLight'} w={'14px'} color={'myGray.900'} />
<Box ml={2} color={'myGray.500'}>
{t('chat.Admin Mark Content')}
</Box>
<Box h={'1px'} bg={'myGray.300'} flex={'1'} />
</Flex>
<ChatBoxDivider
icon="core/app/markLight"
text={t('chat.Admin Mark Content')}
/>
<Box whiteSpace={'pre'}>{`${item.adminFeedback.q || ''}${
item.adminFeedback.a ? `\n${item.adminFeedback.a}` : ''
}`}</Box>
@@ -942,7 +988,10 @@ const ChatBox = (
setAdminMarkData={(e) => setAdminMarkData({ ...e, chatItemId: adminMarkData.chatItemId })}
onClose={() => setAdminMarkData(undefined)}
onSuccess={(adminFeedback) => {
if (!appId || !chatId || !adminMarkData.chatItemId) return;
updateChatAdminFeedback({
appId,
chatId,
chatItemId: adminMarkData.chatItemId,
...adminFeedback
});
@@ -1089,7 +1138,7 @@ function ChatAvatar({ src, type }: { src?: string; type: 'Human' | 'AI' }) {
borderRadius={'lg'}
border={theme.borders.base}
boxShadow={'0 0 5px rgba(0,0,0,0.1)'}
bg={type === 'Human' ? 'white' : 'myBlue.100'}
bg={type === 'Human' ? 'white' : 'blue.50'}
>
<Avatar src={src} w={'100%'} h={'100%'} />
</Box>
@@ -1170,7 +1219,7 @@ function ChatController({
<MyIcon
{...controlIconStyle}
name={'copy'}
_hover={{ color: 'myBlue.700' }}
_hover={{ color: 'blue.600' }}
onClick={() => copyData(chat.value)}
/>
</MyTooltip>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702637232008"
class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6146"
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
<path
d="M261.688889 194.218667A67.470222 67.470222 0 1 1 194.218667 261.688889 67.584 67.584 0 0 1 261.688889 194.218667M261.688889 136.533333a125.155556 125.155556 0 1 0 125.155555 125.155556 125.155556 125.155556 0 0 0-125.155555-125.155556zM614.4 444.529778A67.470222 67.470222 0 1 1 547.271111 512a67.584 67.584 0 0 1 67.128889-67.470222M614.4 386.844444a125.155556 125.155556 0 1 0 125.155556 125.155556 125.155556 125.155556 0 0 0-125.155556-125.155556zM408.120889 694.840889A67.470222 67.470222 0 1 1 340.650667 762.311111a67.470222 67.470222 0 0 1 67.470222-67.470222m0-57.685333a125.155556 125.155556 0 1 0 125.155555 125.155555 125.155556 125.155556 0 0 0-125.155555-125.155555z"
fill="#1DCCA1" p-id="6147"></path>
<path
d="M489.016889 736.142222h375.011555v52.337778H489.016889zM325.632 788.48h-42.894222C171.804444 788.48 113.777778 711.452444 113.777778 635.335111a143.36 143.36 0 0 1 43.235555-103.651555c30.833778-30.037333 74.296889-45.511111 125.724445-45.511112h242.119111v52.337778H282.737778c-80.554667 0-116.622222 48.810667-116.622222 97.166222s36.522667 100.807111 116.622222 100.807112h42.894222zM743.537778 538.168889h-40.618667v-52.337778h40.618667c78.620444 0 114.346667-51.541333 114.346666-99.555555s-35.384889-98.417778-114.346666-98.417778H337.123556v-52.337778h406.414222C853.333333 235.52 910.222222 311.409778 910.222222 386.844444a147.911111 147.911111 0 0 1-42.780444 104.903112 168.732444 168.732444 0 0 1-123.904 46.421333z"
fill="#1DCCA1" p-id="6148"></path>
<path d="M910.222222 762.311111l-136.533333 102.4V659.911111l136.533333 102.4z" fill="#1DCCA1" p-id="6149"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -124,7 +124,8 @@ const iconPaths = {
'common/confirm/deleteTip': () => import('./icons/common/confirm/deleteTip.svg'),
'common/confirm/commonTip': () => import('./icons/common/confirm/commonTip.svg'),
'common/routePushLight': () => import('./icons/common/routePushLight.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg')
'common/viewLight': () => import('./icons/common/viewLight.svg'),
'core/app/customFeedback': () => import('./icons/core/app/customFeedback.svg')
};
export type IconName = keyof typeof iconPaths;

View File

@@ -129,7 +129,7 @@ const Navbar = ({ unread }: { unread: number }) => {
{...itemStyles}
{...(item.activeLink.includes(router.pathname)
? {
color: 'myBlue.700',
color: 'blue.600',
bg: 'white !important',
boxShadow: '1px 1px 10px rgba(0,0,0,0.2)'
}

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