Compare commits

..

13 Commits

Author SHA1 Message Date
archer
7c5657978d perf: doc 2025-01-16 15:53:49 +08:00
heheer
2d82db01a9 fix: api dataset reference tag preview (#3600) 2025-01-15 14:47:57 +08:00
Archer
80e670600b perf: load members;perf: yuque load;fix: workflow llm params cannot close (#3594)
* chat openapi doc

* feat: dataset openapi doc

* perf: load members

* perf: member load code

* perf: yuque load

* fix: workflow llm params cannot close
2025-01-15 14:47:56 +08:00
heheer
68f5afeba0 fix: yuque dataset file folder can enter (#3593) 2025-01-15 14:47:56 +08:00
Finley Ge
0d9f54cbf3 fix: scroll fetch (#3592) 2025-01-15 14:47:55 +08:00
Finley Ge
11cfe8a809 fix: collection list api old version (#3591)
* fix: collection list api format

* fix: type error of addSourceMemeber
2025-01-15 14:47:55 +08:00
Archer
6f8c6b6ad1 4.8.19 test (#3584)
* faet: dataset search filter

* fix: scroll page
2025-01-15 14:47:54 +08:00
Finley Ge
f468ba2f30 fix: pagination bug (#3577) 2025-01-15 14:47:54 +08:00
Finley Ge
19abfd1a3e docs: add custom uid docs (#3572) 2025-01-15 14:47:54 +08:00
Jiangween
ace304c619 docs:更新用户答疑 (#3576) 2025-01-15 14:47:54 +08:00
archer
923d0f85e9 perf: list data 2025-01-15 14:47:53 +08:00
archer
62bcff2ff0 perf: scroll page code 2025-01-15 14:47:53 +08:00
Finley Ge
ec0cef09a2 feat: sync org from wecom, pref: member list pagination (#3549)
* feat: sync org

* chore: fe

* chore: loading

* chore: type

* pref: team member list change to pagination. Edit a sort of list apis.

* feat: member update avatar

* chore: user avatar move to tmb

* chore: init scripts move user avatar

* chore: sourceMember

* fix: list api sourceMember

* fix: member sync

* fix: pagination

* chore: adjust code

* chore: move changeOwner to pro

* chore: init v4819 script

* chore: adjust code

* chore: UserBox
2025-01-15 14:47:50 +08:00
43 changed files with 183 additions and 634 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -29,43 +29,6 @@ images: []
如图 如图
![](/imgs/faq2.png) ![](/imgs/faq2.png)
### 数据库3306端口被占用了启动服务失败
![](/imgs/faq3.png)
mysql 只有 oneAPI 用到,外面一般不需要调用,所以可以
- 把 3306:3306 的映射去掉/或者直接改一个映射。
```yaml
# 在 docker-compose.yaml 文件内
# ...
mysql:
image: mysql:8.0.36
ports:
- 3306:3306 # 这个端口被占用了!
# - 3307:3306 # 直接改一个。。和外面的不冲突
# *empty* 或者直接删了,反正外面用不到
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
environment:
- SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi # 这不用改,容器内外网络是隔离的
```
- 另一种做法是可以直接连现有的 mysql 要改 oneAPI 的环境变量。
```yaml
# 在 docker-compose.yaml 文件内
# ...
# mysql: # 要连外面的,这个玩意用不到了
# image: mysql:8.0.36
# ports:
# - 3306:3306 # 这个端口被占用了!
oneapi:
container_name: oneapi
image: ghcr.io/songquanpeng/one-api:latest
environment:
- SQL_DSN=root:oneapimmysql@tcp(mysql:3306)/oneapi # 改成外面的链接字符串
```
### 本地部署的限制 ### 本地部署的限制
具体内容参考https://fael3z0zfze.feishu.cn/wiki/OFpAw8XzAi36Guk8dfucrCKUnjg。 具体内容参考https://fael3z0zfze.feishu.cn/wiki/OFpAw8XzAi36Guk8dfucrCKUnjg。

View File

@@ -12,11 +12,7 @@ weight: 806
1. 新增 - 工作流知识库检索支持按知识库权限进行过滤。 1. 新增 - 工作流知识库检索支持按知识库权限进行过滤。
2. 新增 - 飞书/语雀知识库查看原文。 2. 新增 - 飞书/语雀知识库查看原文。
3. 新增 - 流程等待插件,可以等待 n 毫秒后继续执行流程 3. 优化 - 成员列表分页加载
4. 优化 - 成员列表分页加载。 4. 优化 - 统一分页加载代码
5. 优化 - 统一分页加载代码 5. 修复 - 语雀文件库导入时,嵌套文件内容无法展开的问题
6. 优化 - 对话页面加载时,可配置是否为独立页面。 6. 修复 - 工作流编排中LLM 参数无法关闭问题。
7. 修复 - 语雀文件库导入时,嵌套文件内容无法展开的问题。
8. 修复 - 工作流编排中LLM 参数无法关闭问题。
9. 修复 - 工作流编排中,代码运行节点还原模板问题。
10. 修复 - HTTP 接口适配对象字符串解析。

View File

@@ -21,19 +21,6 @@ weight: 908
定时执行会在应用发布后生效,会在后台生效。 定时执行会在应用发布后生效,会在后台生效。
## V4.8.18-FIX2中提到“ 1. 修复 HTTP 节点, {{}} 格式引用变量兼容问题。建议尽快替换 / 模式取变量, {{}} 语法已弃用。”替换{{}}引用格式是仅仅只有在http节点还是所有节点的都会有影响
只有 http 节点用到这个语法。
## 工作流类型的应用在运行预览可以正常提问返回,但是发布免登录窗口之后有问题。
一般是没正确发布,在工作流右上角点击【保存并发布】。
## 如何解决猜你想问使用中文回答显示
注意需要更新到V4.8.17及以上,把猜你想问的提示词改成中文。
![](/imgs/quizApp2.png)
## AI对话回答要求中的Markdown语法取消 ## AI对话回答要求中的Markdown语法取消
修改知识库默认提示词, 默认用的是标准模板提示词,会要求按 Markdown 输出,可以去除该要求: 修改知识库默认提示词, 默认用的是标准模板提示词,会要求按 Markdown 输出,可以去除该要求:

View File

@@ -24,17 +24,6 @@ xlsx等都可以上传的不止支持CSV。
统一按gpt3.5标准。 统一按gpt3.5标准。
## 误删除重排模型后重排模型怎么加入到fastgpt
![](/imgs/dataset3.png)
config.json文件里面配置后就可以勾选重排模型
## 线上平台上创建了应用和知识库,到期之后如果短期内不续费,数据是否会被清理。
免费版是三十天不登录后清空知识库,应用不会动。其他付费套餐到期后自动切免费版。
![](/imgs/dataset4.png)
## 基于知识库的查询但是问题相关的答案过多。ai回答到一半就不继续回答。 ## 基于知识库的查询但是问题相关的答案过多。ai回答到一半就不继续回答。
FastGPT回复长度计算公式: FastGPT回复长度计算公式:

View File

@@ -8,14 +8,4 @@ weight: 918
## oneapi 官网是哪个 ## oneapi 官网是哪个
只有开源的 README没官网GitHub: https://github.com/songquanpeng/one-api 只有开源的 README没官网GitHub: https://github.com/songquanpeng/one-api
## 想做多用户和多chat key
![](/imgs/other1.png)
![](/imgs/other2.png)
多用户问题:只能采取二开或者商业版
多chat key问题1. 私有化部署情况下oneapi解决。2. Saas或者商业版中可以为每个团队设置单独的key。
![](/imgs/other3.png)

View File

@@ -1,6 +1,6 @@
--- ---
title: "批量运行" title: "循环执行"
description: "FastGPT 批量运行节点介绍和使用" description: "FastGPT 循环运行节点介绍和使用"
icon: "input" icon: "input"
draft: false draft: false
toc: true toc: true
@@ -9,15 +9,15 @@ weight: 260
## 节点概述 ## 节点概述
【**批量运行**】节点是 FastGPT V4.8.11 版本新增的一个重要功能模块。它允许工作流对数组类型的输入数据进行迭代处理,每次处理数组中的一个元素,并自动执行后续节点,直到完成整个数组的处理。 【**循环运行**】节点是 FastGPT V4.8.11 版本新增的一个重要功能模块。它允许工作流对数组类型的输入数据进行迭代处理,每次处理数组中的一个元素,并自动执行后续节点,直到完成整个数组的处理。
这个节点的设计灵感来自编程语言中的循环结构,但以可视化的方式呈现。 这个节点的设计灵感来自编程语言中的循环结构,但以可视化的方式呈现。
![批量运行节点](/imgs/fastgpt-loop-node.png) ![循环运行节点](/imgs/fastgpt-loop-node.png)
> 在程序中,节点可以理解为一个个 Function 或者接口。可以理解为它就是一个**步骤**。将多个节点一个个拼接起来,即可一步步的去实现最终的 AI 输出。 > 在程序中,节点可以理解为一个个 Function 或者接口。可以理解为它就是一个**步骤**。将多个节点一个个拼接起来,即可一步步的去实现最终的 AI 输出。
【**批量运行**】节点本质上也是一个 Function它的主要职责是自动化地重复执行特定的工作流程。 【**循环运行**】节点本质上也是一个 Function它的主要职责是自动化地重复执行特定的工作流程。
## 核心特性 ## 核心特性
@@ -41,9 +41,9 @@ weight: 260
## 应用场景 ## 应用场景
【**批量运行**】节点的主要作用是通过自动化的方式扩展工作流的处理能力,使 FastGPT 能够更好地处理批量任务和复杂的数据处理流程。特别是在处理大规模数据或需要多轮迭代的场景下,批量运行节点能显著提升工作流的效率和自动化程度。 【**循环运行**】节点的主要作用是通过自动化的方式扩展工作流的处理能力,使 FastGPT 能够更好地处理批量任务和复杂的数据处理流程。特别是在处理大规模数据或需要多轮迭代的场景下,循环运行节点能显著提升工作流的效率和自动化程度。
【**批量运行**】节点特别适合以下场景: 【**循环运行**】节点特别适合以下场景:
1. **批量数据处理** 1. **批量数据处理**
- 批量翻译文本 - 批量翻译文本
@@ -64,7 +64,7 @@ weight: 260
### 输入参数设置 ### 输入参数设置
【**批量运行**】节点需要配置两个核心输入参数: 【**循环运行**】节点需要配置两个核心输入参数:
1. **数组 (必填)**:接收一个数组类型的输入,可以是: 1. **数组 (必填)**:接收一个数组类型的输入,可以是:
- 字符串数组 (`Array<string>`) - 字符串数组 (`Array<string>`)
@@ -95,7 +95,7 @@ weight: 260
### 批量处理数组 ### 批量处理数组
假设我们有一个包含多个文本的数组,需要对每个文本进行 AI 处理。这是批量运行节点最基础也最常见的应用场景。 假设我们有一个包含多个文本的数组,需要对每个文本进行 AI 处理。这是循环运行节点最基础也最常见的应用场景。
#### 实现步骤 #### 实现步骤
@@ -114,9 +114,9 @@ weight: 260
return { textArray: texts }; return { textArray: texts };
``` ```
2. 配置批量运行节点 2. 配置循环运行节点
![配置批量运行节点](/imgs/fastgpt-loop-node-example-2.png) ![配置循环运行节点](/imgs/fastgpt-loop-node-example-2.png)
- 数组输入:选择上一步代码运行节点的输出变量 `textArray`。 - 数组输入:选择上一步代码运行节点的输出变量 `textArray`。
- 循环体内添加一个【AI 对话】节点,用于处理每个文本。这里我们输入的 prompt 为:`请将这段文本翻译成英文`。 - 循环体内添加一个【AI 对话】节点,用于处理每个文本。这里我们输入的 prompt 为:`请将这段文本翻译成英文`。
@@ -128,7 +128,7 @@ weight: 260
![运行流程](/imgs/fastgpt-loop-node-example-3.png) ![运行流程](/imgs/fastgpt-loop-node-example-3.png)
1. 【代码运行】节点执行,生成测试数组 1. 【代码运行】节点执行,生成测试数组
2. 【批量运行】节点接收数组,开始遍历 2. 【循环运行】节点接收数组,开始遍历
3. 对每个数组元素: 3. 对每个数组元素:
- 【AI 对话】节点处理当前元素 - 【AI 对话】节点处理当前元素
- 【指定回复】节点输出翻译后的文本 - 【指定回复】节点输出翻译后的文本
@@ -144,7 +144,7 @@ weight: 260
- 需要维护上下文的连贯性 - 需要维护上下文的连贯性
- 翻译质量需要多轮优化 - 翻译质量需要多轮优化
【**批量运行**】节点可以很好地解决这些问题。 【**循环运行**】节点可以很好地解决这些问题。
#### 实现步骤 #### 实现步骤
@@ -281,9 +281,9 @@ weight: 260
这里我们用到了 [Jina AI 开源的一个强大的正则表达式](https://x.com/JinaAI_/status/1823756993108304135),它能利用所有可能的边界线索和启发式方法来精确切分文本。 这里我们用到了 [Jina AI 开源的一个强大的正则表达式](https://x.com/JinaAI_/status/1823756993108304135),它能利用所有可能的边界线索和启发式方法来精确切分文本。
2. 配置批量运行节点 2. 配置循环运行节点
![配置批量运行节点](/imgs/fastgpt-loop-node-example-5.png) ![配置循环运行节点](/imgs/fastgpt-loop-node-example-5.png)
- 数组输入:选择上一步代码运行节点的输出变量 `chunks`。 - 数组输入:选择上一步代码运行节点的输出变量 `chunks`。
- 循环体内添加一个【代码运行】节点,对源文本进行格式化。 - 循环体内添加一个【代码运行】节点,对源文本进行格式化。

View File

@@ -212,7 +212,7 @@ export default async function (ctx: FunctionContext): Promise<IResponse>{
![](/imgs/translate13.png) ![](/imgs/translate13.png)
## 批量运 ## 循环执
长文反思翻译比较关键的一个部分,就是对多个文本块进行循环反思翻译 长文反思翻译比较关键的一个部分,就是对多个文本块进行循环反思翻译

View File

@@ -91,9 +91,9 @@ weight: 604
这个过程不仅提高了效率,还最大限度地减少了人为错误的可能性。 这个过程不仅提高了效率,还最大限度地减少了人为错误的可能性。
## 批量运 ## 循环执
为了处理整个长字幕文件,我们需要一个批量运行机制。这是通过一个简单但有效的判断模块实现的: 为了处理整个长字幕文件,我们需要一个循环执行机制。这是通过一个简单但有效的判断模块实现的:
1. 检查当前翻译的文本块是否为最后一个。 1. 检查当前翻译的文本块是否为最后一个。
2. 如果不是,则将工作流重定向到格式化原文本块节点。 2. 如果不是,则将工作流重定向到格式化原文本块节点。

View File

@@ -33,7 +33,7 @@ export const defaultWhisperConfig: AppWhisperConfigType = {
export const defaultQGConfig: AppQGConfigType = { export const defaultQGConfig: AppQGConfigType = {
open: false, open: false,
model: 'gpt-4o-mini', model: 'gpt-4o-mini',
customPrompt: '' customPrompt: PROMPT_QUESTION_GUIDE
}; };
export const defaultChatInputGuideConfig = { export const defaultChatInputGuideConfig = {

View File

@@ -12,8 +12,7 @@ const staticPluginList = [
'DingTalkWebhook', 'DingTalkWebhook',
'WeWorkWebhook', 'WeWorkWebhook',
'google', 'google',
'bing', 'bing'
'delay'
]; ];
// Run in worker thread (Have npm packages) // Run in worker thread (Have npm packages)
const packagePluginList = [ const packagePluginList = [

View File

@@ -1,18 +0,0 @@
import { delay } from '@fastgpt/global/common/system/utils';
type Props = {
ms: number;
};
type Response = Promise<Number>;
const main = async ({ ms }: Props): Response => {
if (typeof ms !== 'number' || ms <= 0 || ms > 300000) {
return ms;
}
await delay(ms);
return ms;
};
export default main;

View File

@@ -1,318 +0,0 @@
{
"author": "collin",
"version": "4817",
"name": "流程等待",
"avatar": "core/workflow/template/sleep",
"intro": "让工作流等待指定时间后运行",
"showStatus": true,
"weight": 1,
"isTool": true,
"templateType": "tools",
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "workflow:template.plugin_start",
"intro": "workflow:intro_plugin_input",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 627.6352390819724,
"y": -165.05298493910118
},
"version": "481",
"inputs": [
{
"renderTypeList": ["numberInput", "reference"],
"selectedTypeIndex": 0,
"valueType": "number",
"canEdit": true,
"key": "延迟时长",
"label": "延迟时长",
"description": "需要暂停的时间,单位毫秒",
"defaultValue": 1000,
"list": [
{
"label": "",
"value": ""
}
],
"maxFiles": 5,
"canSelectFile": true,
"canSelectImg": true,
"required": true,
"toolDescription": "需要暂停的时间,单位毫秒",
"max": 300000,
"min": 1
}
],
"outputs": [
{
"id": "ms",
"valueType": "number",
"key": "延迟时长",
"label": "延迟时长",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "common:core.module.template.self_output",
"intro": "workflow:intro_custom_plugin_output",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1921.839722563351,
"y": -160.05298493910115
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "any",
"canEdit": true,
"key": "result",
"label": "result",
"isToolOutput": true,
"description": "",
"required": true,
"value": ["zCJC6zw7c14i", "httpRawResponse"]
}
],
"outputs": []
},
{
"nodeId": "pluginConfig",
"name": "common:core.module.template.system_config",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": 184.66337662472682,
"y": -216.05298493910115
},
"version": "4811",
"inputs": [],
"outputs": []
},
{
"nodeId": "zCJC6zw7c14i",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1154.4041630064592,
"y": -455.0529849391012
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "delay",
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n\"ms\": {{$pluginInput.ms$}}\n}",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "输出字段提取",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "可以通过 JSONPath 语法来提取响应值中的指定字段",
"valueDesc": ""
}
]
}
],
"edges": [
{
"source": "pluginInput",
"target": "zCJC6zw7c14i",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "zCJC6zw7c14i-target-left"
},
{
"source": "zCJC6zw7c14i",
"target": "pluginOutput",
"sourceHandle": "zCJC6zw7c14i-source-right",
"targetHandle": "pluginOutput-target-left"
}
],
"chatConfig": {
"welcomeText": ""
}
}
}

View File

@@ -72,7 +72,6 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
showStatus: false showStatus: false
}; };
}); });
const runtimeVariables = { const runtimeVariables = {
...filterSystemVariables(props.variables), ...filterSystemVariables(props.variables),
appId: String(plugin.id) appId: String(plugin.id)

View File

@@ -127,16 +127,8 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
if (typeof val === 'object') return JSON.stringify(val); if (typeof val === 'object') return JSON.stringify(val);
if (typeof val === 'string') { if (typeof val === 'string') {
try { const str = JSON.stringify(val);
const parsed = JSON.parse(val); return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
if (typeof parsed === 'object') {
return JSON.stringify(parsed);
}
return val;
} catch (error) {
const str = JSON.stringify(val);
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
}
} }
return String(val); return String(val);
@@ -243,9 +235,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
} }
if (!httpJsonBody) return {}; if (!httpJsonBody) return {};
if (httpContentType === ContentTypes.json) { if (httpContentType === ContentTypes.json) {
httpJsonBody = replaceJsonBodyString(httpJsonBody); return json5.parse(replaceJsonBodyString(httpJsonBody));
console.log(httpJsonBody);
return json5.parse(httpJsonBody);
} }
// Raw text, xml // Raw text, xml

View File

@@ -270,7 +270,6 @@ export const iconPaths = {
import('./icons/core/workflow/template/datasource.svg'), import('./icons/core/workflow/template/datasource.svg'),
'core/workflow/template/duckduckgo': () => 'core/workflow/template/duckduckgo': () =>
import('./icons/core/workflow/template/duckduckgo.svg'), import('./icons/core/workflow/template/duckduckgo.svg'),
'core/workflow/template/sleep': () => import('./icons/core/workflow/template/sleep.svg'),
'core/workflow/template/extractJson': () => 'core/workflow/template/extractJson': () =>
import('./icons/core/workflow/template/extractJson.svg'), import('./icons/core/workflow/template/extractJson.svg'),
'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'), 'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'),

View File

@@ -1,7 +0,0 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
width="200" height="200">
<path d="M-70.54222222-70.54222222m582.54222222 0l0 0q582.54222222 0 582.54222222 582.54222222l0 0q0 582.54222222-582.54222222 582.54222222l0 0q-582.54222222 0-582.54222222-582.54222222l0 0q0-582.54222222 582.54222222-582.54222222Z"
fill="#1296db"/>
<path d="M422.64477924 243.86730561H243.86730561v536.26538878h178.77747363V243.86730561zM780.13269439 243.86730561h-178.77747363v536.26538878H780.13269439V243.86730561z"
fill="#ffffff"/>
</svg>

Before

Width:  |  Height:  |  Size: 593 B

View File

@@ -135,7 +135,7 @@ const MySelect = <T = any,>(
))} ))}
</> </>
); );
}, [list, value]); }, []);
return ( return (
<Box <Box

View File

@@ -29,6 +29,8 @@ export type ChatProviderProps = {
outLinkAuthData?: OutLinkChatAuthProps; outLinkAuthData?: OutLinkChatAuthProps;
chatType: 'log' | 'chat' | 'share' | 'team'; chatType: 'log' | 'chat' | 'share' | 'team';
showRawSource: boolean;
showNodeStatus: boolean;
}; };
type useChatStoreType = ChatProviderProps & { type useChatStoreType = ChatProviderProps & {
@@ -130,6 +132,8 @@ const Provider = ({
chatId, chatId,
outLinkAuthData = {}, outLinkAuthData = {},
chatType = 'chat', chatType = 'chat',
showRawSource,
showNodeStatus,
children, children,
...props ...props
}: ChatProviderProps & { }: ChatProviderProps & {
@@ -245,7 +249,9 @@ const Provider = ({
chatId, chatId,
outLinkAuthData, outLinkAuthData,
getHistoryResponseData, getHistoryResponseData,
chatType chatType,
showRawSource,
showNodeStatus
}; };
return <ChatBoxContext.Provider value={value}>{children}</ChatBoxContext.Provider>; return <ChatBoxContext.Provider value={value}>{children}</ChatBoxContext.Provider>;

View File

@@ -25,7 +25,6 @@ import { isEqual } from 'lodash';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time'; import { formatTimeToChatItemTime } from '@fastgpt/global/common/string/time';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
const colorMap = { const colorMap = {
[ChatStatusEnum.loading]: { [ChatStatusEnum.loading]: {
@@ -140,7 +139,7 @@ const ChatItem = (props: Props) => {
const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting); const isChatting = useContextSelector(ChatBoxContext, (v) => v.isChatting);
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const showNodeStatus = useContextSelector(ChatItemContext, (v) => v.showNodeStatus); const showNodeStatus = useContextSelector(ChatBoxContext, (v) => v.showNodeStatus);
const isChatLog = chatType === 'log'; const isChatLog = chatType === 'log';
const { copyData } = useCopyData(); const { copyData } = useCopyData();

View File

@@ -9,16 +9,19 @@ import RawSourceBox from '@/components/core/dataset/RawSourceBox';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils'; import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../Provider'; import { ChatBoxContext } from '../Provider';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
const QuoteModal = ({ const QuoteModal = ({
rawSearch = [], rawSearch = [],
onClose, onClose,
canEditDataset,
showRawSource,
chatItemId, chatItemId,
metadata metadata
}: { }: {
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
onClose: () => void; onClose: () => void;
canEditDataset: boolean;
showRawSource: boolean;
chatItemId: string; chatItemId: string;
metadata?: { metadata?: {
collectionId: string; collectionId: string;
@@ -44,11 +47,6 @@ const QuoteModal = ({
chatItemId, chatItemId,
...(v.outLinkAuthData || {}) ...(v.outLinkAuthData || {})
})); }));
const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
const showRouteToDatasetDetail = useContextSelector(
ChatItemContext,
(v) => v.showRouteToDatasetDetail
);
return ( return (
<> <>
@@ -73,7 +71,12 @@ const QuoteModal = ({
} }
> >
<ModalBody> <ModalBody>
<QuoteList rawSearch={filterResults} chatItemId={chatItemId} /> <QuoteList
rawSearch={filterResults}
canEditDataset={canEditDataset}
canViewSource={showRawSource}
chatItemId={chatItemId}
/>
</ModalBody> </ModalBody>
</MyModal> </MyModal>
</> </>
@@ -84,10 +87,14 @@ export default QuoteModal;
export const QuoteList = React.memo(function QuoteList({ export const QuoteList = React.memo(function QuoteList({
chatItemId, chatItemId,
rawSearch = [] rawSearch = [],
canEditDataset,
canViewSource
}: { }: {
chatItemId?: string; chatItemId?: string;
rawSearch: SearchDataResponseItemType[]; rawSearch: SearchDataResponseItemType[];
canEditDataset: boolean;
canViewSource: boolean;
}) { }) {
const theme = useTheme(); const theme = useTheme();
@@ -97,11 +104,6 @@ export const QuoteList = React.memo(function QuoteList({
chatId: v.chatId, chatId: v.chatId,
...(v.outLinkAuthData || {}) ...(v.outLinkAuthData || {})
})); }));
const showRawSource = useContextSelector(ChatItemContext, (v) => v.isShowReadRawSource);
const showRouteToDatasetDetail = useContextSelector(
ChatItemContext,
(v) => v.showRouteToDatasetDetail
);
return ( return (
<> <>
@@ -118,8 +120,8 @@ export const QuoteList = React.memo(function QuoteList({
> >
<QuoteItem <QuoteItem
quoteItem={item} quoteItem={item}
canViewSource={showRawSource} canViewSource={canViewSource}
canEditDataset={showRouteToDatasetDetail} canEditDataset={canEditDataset}
{...RawSourceBoxProps} {...RawSourceBoxProps}
/> />
</Box> </Box>

View File

@@ -49,6 +49,7 @@ const ResponseTags = ({
const [quoteFolded, setQuoteFolded] = useState<boolean>(true); const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType); const chatType = useContextSelector(ChatBoxContext, (v) => v.chatType);
const showRawSource = useContextSelector(ChatBoxContext, (v) => v.showRawSource);
const notSharePage = useMemo(() => chatType !== 'share', [chatType]); const notSharePage = useMemo(() => chatType !== 'share', [chatType]);
const { const {
@@ -250,6 +251,8 @@ const ResponseTags = ({
<QuoteModal <QuoteModal
{...quoteModalData} {...quoteModalData}
chatItemId={historyItem.dataId} chatItemId={historyItem.dataId}
canEditDataset={notSharePage}
showRawSource={showRawSource}
onClose={() => setQuoteModalData(undefined)} onClose={() => setQuoteModalData(undefined)}
/> />
)} )}

View File

@@ -18,6 +18,7 @@ import { Box, Checkbox } from '@chakra-ui/react';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus'; import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt'; import { chats2GPTMessages } from '@fastgpt/global/core/chat/adapt';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { import {

View File

@@ -249,7 +249,14 @@ export const WholeResponseContent = ({
{activeModule.quoteList && activeModule.quoteList.length > 0 && ( {activeModule.quoteList && activeModule.quoteList.length > 0 && (
<Row <Row
label={t('common:core.chat.response.module quoteList')} label={t('common:core.chat.response.module quoteList')}
rawDom={<QuoteList chatItemId={dataId} rawSearch={activeModule.quoteList} />} rawDom={
<QuoteList
canEditDataset
canViewSource
chatItemId={dataId}
rawSearch={activeModule.quoteList}
/>
}
/> />
)} )}
</> </>

View File

@@ -164,6 +164,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
showMarkIcon showMarkIcon
showVoiceIcon={false} showVoiceIcon={false}
chatType="log" chatType="log"
showRawSource
showNodeStatus
/> />
)} )}
</Box> </Box>
@@ -185,12 +187,7 @@ const Render = (props: Props) => {
}, [appId, chatId]); }, [appId, chatId]);
return ( return (
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={true}
showRouteToDatasetDetail={true}
isShowReadRawSource={true}
showNodeStatus
>
<ChatRecordContextProvider params={params}> <ChatRecordContextProvider params={params}>
<DetailLogsModal {...props} /> <DetailLogsModal {...props} />
</ChatRecordContextProvider> </ChatRecordContextProvider>

View File

@@ -91,12 +91,7 @@ const Render = ({ appForm }: Props) => {
); );
return ( return (
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={true}
showRouteToDatasetDetail={true}
isShowReadRawSource={true}
showNodeStatus
>
<ChatRecordContextProvider params={chatRecordProviderParams}> <ChatRecordContextProvider params={chatRecordProviderParams}>
<ChatTest appForm={appForm} /> <ChatTest appForm={appForm} />
</ChatRecordContextProvider> </ChatRecordContextProvider>

View File

@@ -158,12 +158,7 @@ const Render = (Props: Props) => {
); );
return ( return (
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={true}
showRouteToDatasetDetail={true}
isShowReadRawSource={true}
showNodeStatus
>
<ChatRecordContextProvider params={chatRecordProviderParams}> <ChatRecordContextProvider params={chatRecordProviderParams}>
<ChatTest {...Props} /> <ChatTest {...Props} />
</ChatRecordContextProvider> </ChatRecordContextProvider>

View File

@@ -14,18 +14,22 @@ import RenderToolInput from './render/RenderToolInput';
import RenderOutput from './render/RenderOutput'; import RenderOutput from './render/RenderOutput';
import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor'; import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants'; import { getLatestNodeTemplate } from '@/web/core/workflow/utils';
import { CodeNode } from '@fastgpt/global/core/workflow/template/system/sandbox';
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { workflowT } = useI18n();
const { nodeId, inputs, outputs } = data; const { nodeId, inputs, outputs } = data;
const { splitToolInputs, onChangeNode, onResetNode } = useContextSelector(
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs); WorkflowContext,
const onChangeNode = useContextSelector(WorkflowContext, (ctx) => ctx.onChangeNode); (ctx) => ctx
);
const { ConfirmModal, openConfirm } = useConfirm({ const { ConfirmModal, openConfirm } = useConfirm({
content: t('workflow:code.Reset template confirm') content: workflowT('code.Reset template confirm')
}); });
const CustomComponent = useMemo(() => { const CustomComponent = useMemo(() => {
@@ -34,24 +38,19 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
return ( return (
<Box mt={-3}> <Box mt={-3}>
<Flex mb={2} alignItems={'flex-end'}> <Flex mb={2} alignItems={'flex-end'}>
<Box flex={'1'}>{'Javascript ' + t('workflow:Code')}</Box> <Box flex={'1'}>{'Javascript ' + workflowT('Code')}</Box>
<Box <Box
cursor={'pointer'} cursor={'pointer'}
color={'primary.500'} color={'primary.500'}
fontSize={'xs'} fontSize={'xs'}
onClick={openConfirm(() => { onClick={openConfirm(() => {
onChangeNode({ onResetNode({
nodeId, id: nodeId,
type: 'updateInput', node: getLatestNodeTemplate(data, CodeNode)
key: item.key,
value: {
...item,
value: JS_TEMPLATE
}
}); });
})} })}
> >
{t('workflow:code.Reset template')} {workflowT('code.Reset template')}
</Box> </Box>
</Flex> </Flex>
<CodeEditor <CodeEditor
@@ -74,33 +73,37 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
); );
} }
}; };
}, [nodeId, onChangeNode, openConfirm, t]); }, [data, nodeId, onChangeNode, onResetNode, openConfirm, workflowT]);
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId); const Render = useMemo(() => {
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
return ( return (
<NodeCard minW={'400px'} selected={selected} {...data}> <NodeCard minW={'400px'} selected={selected} {...data}>
{isTool && ( {isTool && (
<> <>
<Container> <Container>
<RenderToolInput nodeId={nodeId} inputs={inputs} /> <RenderToolInput nodeId={nodeId} inputs={inputs} />
</Container> </Container>
</> </>
)} )}
<Container> <Container>
<IOTitle text={t('common:common.Input')} mb={-1} /> <IOTitle text={t('common:common.Input')} mb={-1} />
<RenderInput <RenderInput
nodeId={nodeId} nodeId={nodeId}
flowInputList={commonInputs} flowInputList={commonInputs}
CustomComponent={CustomComponent} CustomComponent={CustomComponent}
/> />
</Container> </Container>
<Container> <Container>
<IOTitle text={t('common:common.Output')} /> <IOTitle text={t('common:common.Output')} />
<RenderOutput nodeId={nodeId} flowOutputList={outputs} /> <RenderOutput nodeId={nodeId} flowOutputList={outputs} />
</Container> </Container>
<ConfirmModal /> <ConfirmModal />
</NodeCard> </NodeCard>
); );
}, [ConfirmModal, CustomComponent, data, inputs, nodeId, outputs, selected, splitToolInputs, t]);
return Render;
}; };
export default React.memo(NodeCode); export default React.memo(NodeCode);

View File

@@ -141,6 +141,8 @@ export const useChatTest = ({
chatId={chatId} chatId={chatId}
showMarkIcon showMarkIcon
chatType="chat" chatType="chat"
showRawSource
showNodeStatus
onStartChat={startChat} onStartChat={startChat}
/> />
) )

View File

@@ -27,11 +27,13 @@ const ChatHeader = ({
history, history,
showHistory, showHistory,
apps, apps,
onRouteToAppDetail,
totalRecordsCount totalRecordsCount
}: { }: {
history: ChatItemType[]; history: ChatItemType[];
showHistory?: boolean; showHistory?: boolean;
apps?: AppListItemType[]; apps?: AppListItemType[];
onRouteToAppDetail?: () => void;
totalRecordsCount: number; totalRecordsCount: number;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -69,7 +71,7 @@ const ChatHeader = ({
)} )}
{/* control */} {/* control */}
{!isPlugin && <ToolMenu history={history} />} {!isPlugin && <ToolMenu history={history} onRouteToAppDetail={onRouteToAppDetail} />}
</Flex> </Flex>
); );
}; };

View File

@@ -28,6 +28,7 @@ type HistoryItemType = {
const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) => { const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) => {
const theme = useTheme(); const theme = useTheme();
const router = useRouter(); const router = useRouter();
const isUserChatPage = router.pathname === '/chat';
const { t } = useTranslation(); const { t } = useTranslation();
@@ -45,7 +46,6 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name); const appName = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.name);
const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar); const appAvatar = useContextSelector(ChatItemContext, (v) => v.chatBoxData?.app.avatar);
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
const concatHistory = useMemo(() => { const concatHistory = useMemo(() => {
const formatHistories: HistoryItemType[] = histories.map((item) => { const formatHistories: HistoryItemType[] = histories.map((item) => {
@@ -77,8 +77,8 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
}); });
const canRouteToDetail = useMemo( const canRouteToDetail = useMemo(
() => appId && userInfo?.team.permission.hasWritePer && showRouteToAppDetail, () => appId && userInfo?.team.permission.hasWritePer,
[appId, userInfo?.team.permission.hasWritePer, showRouteToAppDetail] [appId, userInfo?.team.permission.hasWritePer]
); );
return ( return (
@@ -287,7 +287,7 @@ const ChatHistorySlider = ({ confirmClearText }: { confirmClearText: string }) =
</ScrollData> </ScrollData>
{/* exec */} {/* exec */}
{!isPc && !!canRouteToDetail && ( {!isPc && isUserChatPage && (
<Flex <Flex
mt={2} mt={2}
borderTop={theme.borders.base} borderTop={theme.borders.base}

View File

@@ -14,8 +14,6 @@ import {
} from '@fastgpt/global/common/parentFolder/type'; } from '@fastgpt/global/common/parentFolder/type';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { useContextSelector } from 'use-context-selector';
const SelectOneResource = dynamic(() => import('@/components/common/folder/SelectOneResource')); const SelectOneResource = dynamic(() => import('@/components/common/folder/SelectOneResource'));
@@ -24,8 +22,6 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
const router = useRouter(); const router = useRouter();
const isTeamChat = router.pathname === '/chat/team'; const isTeamChat = router.pathname === '/chat/team';
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => { const getAppList = useCallback(async ({ parentId }: GetResourceFolderListProps) => {
return getMyApps({ return getMyApps({
parentId, parentId,
@@ -54,36 +50,34 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
return ( return (
<Flex flexDirection={'column'} h={'100%'}> <Flex flexDirection={'column'} h={'100%'}>
{showRouteToAppDetail && ( <Box mt={4} px={4}>
<> {!isTeamChat && (
<Box mt={4} px={4}> <Flex
<Flex alignItems={'center'}
alignItems={'center'} cursor={'pointer'}
cursor={'pointer'} py={2}
py={2} px={3}
px={3} borderRadius={'md'}
borderRadius={'md'} _hover={{ bg: 'myGray.200' }}
_hover={{ bg: 'myGray.200' }} onClick={() => router.push('/app/list')}
onClick={() => router.push('/app/list')} >
> <IconButton
<IconButton mr={3}
mr={3} icon={<MyIcon name={'common/backFill'} w={'1rem'} color={'primary.500'} />}
icon={<MyIcon name={'common/backFill'} w={'1rem'} color={'primary.500'} />} bg={'white'}
bg={'white'} boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} size={'smSquare'}
size={'smSquare'} borderRadius={'50%'}
borderRadius={'50%'} aria-label={''}
aria-label={''} />
/> {t('common:core.chat.Exit Chat')}
{t('common:core.chat.Exit Chat')} </Flex>
</Flex> )}
</Box> </Box>
<MyDivider h={2} my={1} />
</>
)}
{!isTeamChat && ( {!isTeamChat && (
<> <>
<MyDivider h={2} my={1} />
<HStack <HStack
px={4} px={4}
my={2} my={2}

View File

@@ -7,19 +7,20 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import MyMenu from '@fastgpt/web/components/common/MyMenu'; import MyMenu from '@fastgpt/web/components/common/MyMenu';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { ChatContext } from '@/web/core/chat/context/chatContext'; import { ChatContext } from '@/web/core/chat/context/chatContext';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { useRouter } from 'next/router';
const ToolMenu = ({ history }: { history: ChatItemType[] }) => { const ToolMenu = ({
const router = useRouter(); history,
onRouteToAppDetail
}: {
history: ChatItemType[];
onRouteToAppDetail?: () => void;
}) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { onExportChat } = useChatBox(); const { onExportChat } = useChatBox();
const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId); const onChangeChatId = useContextSelector(ChatContext, (v) => v.onChangeChatId);
const chatData = useContextSelector(ChatItemContext, (v) => v.chatBoxData);
const showRouteToAppDetail = useContextSelector(ChatItemContext, (v) => v.showRouteToAppDetail);
return ( return history.length > 0 ? (
<MyMenu <MyMenu
Button={ Button={
<IconButton <IconButton
@@ -60,14 +61,14 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
// } // }
] ]
}, },
...(showRouteToAppDetail ...(onRouteToAppDetail
? [ ? [
{ {
children: [ children: [
{ {
icon: 'core/app/aiLight', icon: 'core/app/aiLight',
label: t('app:app_detail'), label: t('app:app_detail'),
onClick: () => router.push(`/app/detail?appId=${chatData.appId}`) onClick: onRouteToAppDetail
} }
] ]
} }
@@ -75,6 +76,8 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
: []) : [])
]} ]}
/> />
) : (
<Box w={'28px'} h={'28px'} />
); );
}; };

View File

@@ -44,7 +44,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const { userInfo } = useUserStore(); const { userInfo } = useUserStore();
const { setLastChatAppId, chatId, appId, outLinkAuthData } = useChatStore(); const { setLastChatAppId, chatId, appId, outLinkAuthData } = useChatStore();
@@ -187,6 +186,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
apps={myApps} apps={myApps}
history={chatRecords} history={chatRecords}
showHistory showHistory
onRouteToAppDetail={() => router.push(`/app/detail?appId=${appId}`)}
/> />
{/* chat box */} {/* chat box */}
@@ -208,6 +208,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
feedbackType={'user'} feedbackType={'user'}
onStartChat={onStartChat} onStartChat={onStartChat}
chatType={'chat'} chatType={'chat'}
showRawSource
showNodeStatus
isReady={!loading} isReady={!loading}
/> />
)} )}
@@ -219,8 +221,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
); );
}; };
const Render = (props: { appId: string; isStandalone?: string }) => { const Render = (props: { appId: string }) => {
const { appId, isStandalone } = props; const { appId } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const router = useRouter();
@@ -274,12 +276,7 @@ const Render = (props: { appId: string; isStandalone?: string }) => {
return source === ChatSourceEnum.online ? ( return source === ChatSourceEnum.online ? (
<ChatContextProvider params={chatHistoryProviderParams}> <ChatContextProvider params={chatHistoryProviderParams}>
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={isStandalone !== '1'}
showRouteToDatasetDetail={isStandalone !== '1'}
isShowReadRawSource={true}
showNodeStatus
>
<ChatRecordContextProvider params={chatRecordProviderParams}> <ChatRecordContextProvider params={chatRecordProviderParams}>
<Chat myApps={myApps} /> <Chat myApps={myApps} />
</ChatRecordContextProvider> </ChatRecordContextProvider>
@@ -292,7 +289,6 @@ export async function getServerSideProps(context: any) {
return { return {
props: { props: {
appId: context?.query?.appId || '', appId: context?.query?.appId || '',
isStandalone: context?.query?.isStandalone || '',
...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow'])) ...(await serviceSideProps(context, ['file', 'app', 'chat', 'workflow']))
} }
}; };

View File

@@ -55,6 +55,7 @@ type Props = {
const OutLink = (props: Props) => { const OutLink = (props: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
const { showRawSource, showNodeStatus } = props;
const { const {
shareId = '', shareId = '',
showHistory = '1', showHistory = '1',
@@ -286,6 +287,8 @@ const OutLink = (props: Props) => {
feedbackType={'user'} feedbackType={'user'}
onStartChat={startChat} onStartChat={startChat}
chatType="share" chatType="share"
showRawSource={showRawSource}
showNodeStatus={showNodeStatus}
/> />
)} )}
</Box> </Box>
@@ -337,12 +340,7 @@ const Render = (props: Props) => {
return source === ChatSourceEnum.share ? ( return source === ChatSourceEnum.share ? (
<ChatContextProvider params={chatHistoryProviderParams}> <ChatContextProvider params={chatHistoryProviderParams}>
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={false}
showRouteToDatasetDetail={false}
isShowReadRawSource={props.showRawSource}
showNodeStatus={props.showNodeStatus}
>
<ChatRecordContextProvider params={chatRecordProviderParams}> <ChatRecordContextProvider params={chatRecordProviderParams}>
<OutLink {...props} /> <OutLink {...props} />
</ChatRecordContextProvider> </ChatRecordContextProvider>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import NextHead from '@/components/common/NextHead'; import NextHead from '@/components/common/NextHead';
import { getTeamChatInfo } from '@/web/core/chat/api'; import { getTeamChatInfo } from '@/web/core/chat/api';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@@ -20,7 +20,8 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext'; import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
import { AppListItemType } from '@fastgpt/global/core/app/type'; import { AppListItemType } from '@fastgpt/global/core/app/type';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { GetChatTypeEnum } from '@/global/core/chat/constants'; import { InitChatResponse } from '@/global/core/chat/api';
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
@@ -225,6 +226,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
feedbackType={'user'} feedbackType={'user'}
onStartChat={startChat} onStartChat={startChat}
chatType="team" chatType="team"
showRawSource
showNodeStatus
/> />
)} )}
</Box> </Box>
@@ -296,12 +299,7 @@ const Render = (props: Props) => {
return source === ChatSourceEnum.team ? ( return source === ChatSourceEnum.team ? (
<ChatContextProvider params={contextParams}> <ChatContextProvider params={contextParams}>
<ChatItemContextProvider <ChatItemContextProvider>
showRouteToAppDetail={false}
showRouteToDatasetDetail={false}
isShowReadRawSource={true}
showNodeStatus
>
<ChatRecordContextProvider params={chatRecordProviderParams}> <ChatRecordContextProvider params={chatRecordProviderParams}>
<Chat {...props} myApps={myApps} /> <Chat {...props} myApps={myApps} />
</ChatRecordContextProvider> </ChatRecordContextProvider>

View File

@@ -9,12 +9,6 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
type ContextProps = {
showRouteToAppDetail: boolean;
showRouteToDatasetDetail: boolean;
isShowReadRawSource: boolean;
showNodeStatus: boolean;
};
type ChatBoxDataType = { type ChatBoxDataType = {
appId: string; appId: string;
title?: string; title?: string;
@@ -43,7 +37,7 @@ type ChatItemContextType = {
chatBoxData: ChatBoxDataType; chatBoxData: ChatBoxDataType;
setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>; setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>;
isPlugin: boolean; isPlugin: boolean;
} & ContextProps; };
export const ChatItemContext = createContext<ChatItemContextType>({ export const ChatItemContext = createContext<ChatItemContextType>({
ChatBoxRef: null, ChatBoxRef: null,
@@ -67,15 +61,7 @@ export const ChatItemContext = createContext<ChatItemContextType>({
/* /*
Chat 对象的上下文 Chat 对象的上下文
*/ */
const ChatItemContextProvider = ({ const ChatItemContextProvider = ({ children }: { children: ReactNode }) => {
children,
showRouteToAppDetail,
showRouteToDatasetDetail,
isShowReadRawSource,
showNodeStatus
}: {
children: ReactNode;
} & ContextProps) => {
const ChatBoxRef = useRef<ChatComponentRef>(null); const ChatBoxRef = useRef<ChatComponentRef>(null);
const variablesForm = useForm<ChatBoxInputFormType>(); const variablesForm = useForm<ChatBoxInputFormType>();
@@ -127,23 +113,16 @@ const ChatItemContextProvider = ({
pluginRunTab, pluginRunTab,
setPluginRunTab, setPluginRunTab,
resetVariables, resetVariables,
clearChatRecords, clearChatRecords
showRouteToAppDetail,
showRouteToDatasetDetail,
isShowReadRawSource,
showNodeStatus
}; };
}, [ }, [
chatBoxData, chatBoxData,
setChatBoxData,
clearChatRecords,
isPlugin, isPlugin,
variablesForm,
pluginRunTab, pluginRunTab,
resetVariables, resetVariables,
clearChatRecords, variablesForm
showRouteToAppDetail,
showRouteToDatasetDetail,
isShowReadRawSource,
showNodeStatus
]); ]);
return <ChatItemContext.Provider value={contextValue}>{children}</ChatItemContext.Provider>; return <ChatItemContext.Provider value={contextValue}>{children}</ChatItemContext.Provider>;