Compare commits

..

No commits in common. "main" and "v4.14.2" have entirely different histories.

794 changed files with 11249 additions and 33812 deletions

View File

@ -0,0 +1,384 @@
# Lexical Editor 文本解析一致性分析报告
## 执行摘要
通过对 `textToEditorState``editorStateToText` 函数的全面分析,发现了 **3 个确认的不一致性问题**,会导致用户保存后重新加载时看到与编辑器显示不同的内容。
### 严重问题总览
| 问题 | 严重性 | 影响 | 位置 |
|------|--------|------|------|
| 列表项尾部空格丢失 | 🔴 高 | 用户有意添加的空格被删除 | utils.ts:255 |
| 有序列表序号重置 | 🔴 高 | 自定义序号变成连续序号 | utils.ts:257 |
| 列表项内换行不对称 | 🟡 中 | 编辑器支持但无法往返 | processListItem |
---
## 问题 1: 列表项尾部空格丢失 🔴(已处理)
### 问题描述
`processListItem` 函数中使用了 `trim()` 处理列表项文本:
```typescript
// utils.ts:255
const itemTextString = itemText.join('').trim();
```
### 不一致性演示
**用户输入:**
```
- hello world
```
(注意 "world" 后面有 2 个空格)
**EditorState:**
```json
{
"type": "listitem",
"children": [
{ "type": "text", "text": "hello world " }
]
}
```
**输出文本:**
```
- hello world
```
(尾部空格被 trim 删除)
**重新加载:**
```
- hello world
```
(用户的空格永久丢失)
### 影响分析
- **用户体验**: 用户有意添加的尾部空格(可能用于格式对齐)会丢失
- **数据完整性**: 每次保存/加载循环都会丢失尾部空格
- **严重程度**: 高 - 直接影响用户输入的完整性
### 解决方案
**方案 1: 移除 trim()**
```typescript
const itemTextString = itemText.join(''); // 不使用 trim
```
**方案 2: 只移除前导空格**
```typescript
const itemTextString = itemText.join('').trimStart(); // 只移除开头空格
```
**推荐**: 方案 1,完全保留用户输入的空格
---
## 问题 2: 有序列表序号重置 🔴
### 问题描述
在输出有序列表时,使用 `index + 1` 而不是列表项自身的 `value`:
```typescript
// utils.ts:257
const prefix = listType === 'bullet' ? '- ' : `${index + 1}. `;
```
但在解析时,`numberValue` 被正确提取并存储到 `listItem.value`
### 不一致性演示
**用户输入:**
```
1. first
2. second
5. fifth
10. tenth
```
**解析 (textToEditorState):**
```javascript
items = [
{ numberValue: 1, text: "first" },
{ numberValue: 2, text: "second" },
{ numberValue: 5, text: "fifth" },
{ numberValue: 10, text: "tenth" }
]
```
**EditorState:**
```json
[
{ "value": 1, "text": "first" },
{ "value": 2, "text": "second" },
{ "value": 5, "text": "fifth" },
{ "value": 10, "text": "tenth" }
]
```
**输出文本 (editorStateToText):**
```
1. first (index=0, 0+1=1) ✓
2. second (index=1, 1+1=2) ✓
3. fifth (index=2, 2+1=3) ✗ 应该是 5
4. tenth (index=3, 3+1=4) ✗ 应该是 10
```
**重新加载:**
用户的自定义序号 5 和 10 永久丢失,变成连续的 3 和 4。
### 影响分析
- **用户体验**: 用户有意设置的序号被强制改为连续序号
- **数据完整性**: 有序列表的语义丢失(如章节编号 1.1, 1.2, 2.1)
- **严重程度**: 高 - 改变了用户的语义表达
### 解决方案
```typescript
// utils.ts:257
const prefix = listType === 'bullet'
? '- '
: `${listItem.value || index + 1}. `;
```
使用 `listItem.value` 而不是 `index + 1`,保留原始序号。
---
## 问题 3: 列表项内换行不对称 🟡
### 问题描述
Lexical 编辑器允许在列表项内插入换行符 (`linebreak` 节点),但 `textToEditorState` 无法将包含换行的文本重新解析为列表项内换行。
### 不一致性演示
**用户在编辑器中操作:**
```
1. 输入: "- item1"
2. 按 Shift+Enter (插入软换行)
3. 继续输入: "continued content"
```
**EditorState:**
```json
{
"type": "listitem",
"children": [
{ "type": "text", "text": "item1" },
{ "type": "linebreak" },
{ "type": "text", "text": "continued content" }
]
}
```
**输出文本 (editorStateToText):**
```
- item1
continued content
```
**重新加载 (textToEditorState):**
```
行1: "- item1" → 列表项
行2: "continued content" → 段落 (不再是列表项的一部分!)
```
**最终结构变化:**
```
原来: 1个列表项(包含换行)
现在: 1个列表项 + 1个段落
```
### 影响分析
- **结构完整性**: 列表项的内部结构在保存/加载后改变
- **语义丢失**: 原本属于列表项的内容变成了独立段落
- **严重程度**: 中 - 影响文档结构,但可能符合 Markdown 语义
### 解决方案
**方案 1: 在输出时将换行转为空格**
```typescript
if (child.type === 'linebreak') {
itemText.push(' '); // 使用空格而不是 \n
}
```
**方案 2: 在编辑器中禁止列表项内换行**
- 配置 Lexical 不允许在列表项内插入 linebreak
- 用户只能通过创建新列表项来换行
**方案 3: 支持 Markdown 风格的列表项多行**
```typescript
// 识别缩进的行为列表项的继续内容
parseTextLine:
if (line.startsWith(' ') && prevLine.wasListItem) {
// 作为列表项的继续内容
}
```
**推荐**: 方案 1 (最简单) 或方案 2 (最明确)
---
## 其他潜在问题
### 问题 4: 变量节点保存后变成普通文本 🟡
**现象**:
```
EditorState: { type: 'variableLabel', variableKey: '{{var1}}' }
输出文本: "{{var1}}"
重新加载: { type: 'text', text: "{{var1}}" }
```
**影响**: 变量节点的功能性丢失
**分析**: 这可能是设计决策 - 变量只在编辑会话中有效,保存到文本后变成普通占位符。如果需要保持变量功能,应该使用其他存储格式(如 JSON)而不是纯文本。
### 问题 5: 非连续缩进级别可能导致结构错误 🟡
**现象**:
```
输入:
- level 0
- level 2 (跳过 level 1)
- level 1
```
**问题**: `buildListStructure` 可能无法正确处理非连续的缩进级别
**影响**: 列表嵌套结构可能不符合预期
**建议**: 规范化缩进级别,或在文档中说明只支持连续缩进
---
## 正常运作的部分 ✅
经过分析,以下功能**正常运作**,不存在一致性问题:
1. **空行处理** - 空行被正确保留和还原
2. **段落前导空格** - 修复后完全保留
3. **列表和段落边界** - 正确识别和分离
4. **特殊字符在段落中** - 只有行首的 `- ``\d+. ` 被识别为列表
5. **混合列表类型** - bullet 和 number 列表正确分离
6. **列表缩进** - 使用 TabStr 统一为 2 个空格
---
## 测试用例建议
### 测试用例 1: 列表项尾部空格
```typescript
const input = "- hello world "; // 2个尾部空格
const state = textToEditorState(input, true);
const editor = createEditorWithState(state);
const output = editorStateToText(editor);
expect(output).toBe("- hello world "); // 应保留空格
```
### 测试用例 2: 自定义列表序号
```typescript
const input = "1. first\n5. fifth\n10. tenth";
const state = textToEditorState(input, true);
const editor = createEditorWithState(state);
const output = editorStateToText(editor);
expect(output).toBe("1. first\n5. fifth\n10. tenth"); // 应保留序号
```
### 测试用例 3: 列表项换行
```typescript
// 在编辑器中创建列表项并插入 linebreak
const editor = createEditor();
// ... 创建列表项
// ... 插入 linebreak
const output = editorStateToText(editor);
const reloadedState = textToEditorState(output, true);
const reloadedEditor = createEditorWithState(reloadedState);
// 验证结构是否一致
expect(getStructure(editor)).toEqual(getStructure(reloadedEditor));
```
### 测试用例 4: 往返对称性
```typescript
const testCases = [
"simple text",
" indented text",
"- bullet list\n - nested",
"1. first\n2. second\n5. fifth",
"text\n\n\nwith\n\nempty\n\nlines",
"- item ", // 尾部空格
];
testCases.forEach(input => {
const state = textToEditorState(input, true);
const editor = createEditorWithState(state);
const output = editorStateToText(editor);
expect(output).toBe(input); // 应完全一致
});
```
---
## 修复优先级建议
### P0 - 立即修复
1. ✅ **列表项尾部空格丢失** - 影响数据完整性
2. ✅ **有序列表序号重置** - 影响语义表达
### P1 - 高优先级
3. ⚠️ **列表项内换行不对称** - 影响结构一致性
### P2 - 按需修复
4. 📝 **变量节点** - 根据产品需求决定
5. 📝 **非连续缩进** - 文档说明或规范化处理
---
## 代码修改建议
### 修改 1: 保留列表项空格
```diff
// utils.ts:255
- const itemTextString = itemText.join('').trim();
+ const itemTextString = itemText.join('');
```
### 修改 2: 使用原始列表序号
```diff
// utils.ts:257
- const prefix = listType === 'bullet' ? '- ' : `${index + 1}. `;
+ const prefix = listType === 'bullet' ? '- ' : `${listItem.value || index + 1}. `;
```
### 修改 3: 处理列表项换行(方案1)
```diff
// utils.ts:242
if (child.type === 'linebreak') {
- itemText.push('\n');
+ itemText.push(' '); // 转为空格而不是换行
}
```
---
## 总结
通过全面分析,确认了 **3 个会导致编辑器显示与解析文本不一致的问题**:
1. 🔴 列表项尾部空格丢失 → 修复: 移除 trim()
2. 🔴 有序列表序号重置 → 修复: 使用 listItem.value
3. 🟡 列表项内换行不对称 → 修复: 转换为空格或禁止
其他方面(空行、前导空格、边界处理)都运作正常。
建议优先修复前两个 P0 问题,确保用户数据的完整性和语义准确性。

View File

@ -608,6 +608,136 @@ function App({ Component, pageProps }: AppPropsWithLayout) {
---
### 🔴 H9. instrumentation.ts 初始化失败未处理,导致静默失败
**位置**: `projects/app/src/instrumentation.ts:81-84`
**问题描述**:
```typescript
} catch (error) {
console.log('Init system error', error);
exit(1);
}
```
- 初始化失败直接退出进程
- 部分初始化错误被 `.catch()` 吞没
- 缺少初始化状态检查
**风险等级**: 🔴 高危
**影响**:
- 应用启动失败但无明确错误信息
- 部分服务未初始化导致运行时错误
- 调试困难
**建议方案**:
```typescript
// 1. 详细的初始化错误处理
export async function register() {
const initSteps: Array<{
name: string;
fn: () => Promise<void>;
required: boolean;
}> = [];
try {
if (process.env.NEXT_RUNTIME !== 'nodejs') {
return;
}
const results = {
success: [] as string[],
failed: [] as Array<{ name: string; error: any }>
};
// 阶段 1: 基础连接 (必需)
try {
console.log('Connecting to MongoDB...');
await connectMongo({ db: connectionMongo, url: MONGO_URL });
results.success.push('MongoDB Main');
} catch (error) {
console.error('Fatal: MongoDB connection failed', error);
throw error;
}
try {
await connectMongo({ db: connectionLogMongo, url: MONGO_LOG_URL });
results.success.push('MongoDB Log');
} catch (error) {
console.warn('Non-fatal: MongoDB Log connection failed', error);
results.failed.push({ name: 'MongoDB Log', error });
}
// 阶段 2: 系统初始化 (必需)
try {
console.log('Initializing system config...');
await Promise.all([
getInitConfig(),
initVectorStore(),
initRootUser(),
loadSystemModels()
]);
results.success.push('System Config');
} catch (error) {
console.error('Fatal: System initialization failed', error);
throw error;
}
// 阶段 3: 可选服务
await Promise.allSettled([
preLoadWorker().catch(e => {
console.warn('Worker preload failed (non-fatal)', e);
results.failed.push({ name: 'Worker Preload', error: e });
}),
getSystemTools().catch(e => {
console.warn('System tools init failed (non-fatal)', e);
results.failed.push({ name: 'System Tools', error: e });
}),
initSystemPluginGroups().catch(e => {
console.warn('Plugin groups init failed (non-fatal)', e);
results.failed.push({ name: 'Plugin Groups', error: e });
})
]);
// 阶段 4: 后台任务
startCron();
startTrainingQueue(true);
trackTimerProcess();
console.log('Init system success', {
success: results.success,
failed: results.failed.map(f => f.name)
});
} catch (error) {
console.error('Init system critical error', error);
console.error('Stack:', error.stack);
// 发送告警通知
if (process.env.ERROR_WEBHOOK_URL) {
try {
await fetch(process.env.ERROR_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'INIT_ERROR',
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
})
});
} catch (webhookError) {
console.error('Failed to send error webhook', webhookError);
}
}
exit(1);
}
}
```
---
## 二、中危问题 (Medium Priority)
### 🟡 M1. Next.js 未启用 SWC 编译优化完整特性

View File

@ -934,6 +934,28 @@ export const authCertWithCSRF = async (props: AuthModeType) => {
## 新增中危问题 (Additional Medium Priority)
### 🟡 M20. 向量查询缓存策略过于激进
**位置**: `packages/service/common/vectorDB/controller.ts:29-35`
**问题描述**:
```typescript
const onDelCache = throttle((teamId: string) => delRedisCache(getChcheKey(teamId)), 30000, {
leading: true,
trailing: true
});
```
- 删除操作使用 throttle,30 秒内只执行一次
- 可能导致缓存计数不准确
- 未考虑高频删除场景
**建议**:
- 删除操作直接更新缓存
- 定期全量同步缓存和数据库
- 添加缓存一致性校验
---
### 🟡 M21. 训练队列缺少优先级机制
**位置**: `packages/service/common/bullmq/index.ts:20-26`

View File

@ -1,672 +0,0 @@
---
name: workflow-stop-design
description: 工作流暂停逻辑设计方案
---
## 1. Redis 状态管理方案
### 1.1 状态键设计
**Redis Key 结构:**
```typescript
// Key 格式: agent_runtime_stopping:{appId}:{chatId}
const WORKFLOW_STATUS_PREFIX = 'agent_runtime_stopping';
type WorkflowStatusKey = `${typeof WORKFLOW_STATUS_PREFIX}:${string}:${string}`;
// 示例: agent_runtime_stopping:app_123456:chat_789012
```
**状态值设计:**
- **存在键 (值为 1)**: 工作流应该停止
- **不存在键**: 工作流正常运行
- **设计简化**: 不使用状态枚举,仅通过键的存在与否判断
**参数类型定义:**
```typescript
type WorkflowStatusParams = {
appId: string;
chatId: string;
};
```
### 1.2 状态生命周期管理
**状态转换流程:**
```
正常运行(无键) → 停止中(键存在) → 完成(删除键)
```
**TTL 设置:**
- **停止标志 TTL**: 60 秒
- 原因: 避免因意外情况导致的键泄漏
- 正常情况下会在工作流完成时主动删除
- **工作流完成后**: 直接删除 Redis 键
- 原因: 不需要保留终态,减少 Redis 内存占用
### 1.3 核心函数说明
**1. setAgentRuntimeStop**
- **功能**: 设置停止标志
- **参数**: `{ appId, chatId }`
- **实现**: 使用 `SETEX` 命令,设置键值为 1,TTL 60 秒
**2. shouldWorkflowStop**
- **功能**: 检查工作流是否应该停止
- **参数**: `{ appId, chatId }`
- **返回**: `Promise<boolean>` - true=应该停止, false=继续运行
- **实现**: GET 命令获取键值,存在则返回 true
**3. delAgentRuntimeStopSign**
- **功能**: 删除停止标志
- **参数**: `{ appId, chatId }`
- **实现**: DEL 命令删除键
**4. waitForWorkflowComplete**
- **功能**: 等待工作流完成(停止标志被删除)
- **参数**: `{ appId, chatId, timeout?, pollInterval? }`
- **实现**: 轮询检查停止标志是否被删除,超时返回
### 1.4 边界情况处理
**1. Redis 操作失败**
- **错误处理**: 所有 Redis 操作都包含 `.catch()` 错误处理
- **降级策略**:
- `shouldWorkflowStop`: 出错时返回 `false` (认为不需要停止,继续运行)
- `delAgentRuntimeStopSign`: 出错时记录错误日志,但不影响主流程
- **设计原因**: Redis 异常不应阻塞工作流运行,降级到继续执行策略
**2. TTL 自动清理**
- **TTL 设置**: 60 秒
- **清理时机**: Redis 自动清理过期键
- **设计原因**:
- 避免因异常情况导致的 Redis 键泄漏
- 自动清理减少手动维护成本
- 60 秒足够大多数工作流完成停止操作
**3. stop 接口等待超时**
- **超时时间**: 5 秒
- **超时策略**: `waitForWorkflowComplete` 在 5 秒内轮询检查停止标志是否被删除
- **超时处理**: 5 秒后直接返回,不影响工作流继续执行
- **设计原因**:
- 避免前端长时间等待
- 5 秒足够大多数节点完成当前操作
- 用户体验优先,超时后前端可选择重试或放弃
**4. 并发停止请求**
- **处理方式**: 多次调用 `setAgentRuntimeStop` 是安全的,Redis SETEX 是幂等操作
- **设计原因**: 避免用户多次点击停止按钮导致的问题
---
## 2. Redis 工具函数实现
**位置**: `packages/service/core/workflow/dispatch/workflowStatus.ts`
```typescript
import { addLog } from '../../../common/system/log';
import { getGlobalRedisConnection } from '../../../common/redis/index';
import { delay } from '@fastgpt/global/common/system/utils';
const WORKFLOW_STATUS_PREFIX = 'agent_runtime_stopping';
const TTL = 60; // 60秒
export const StopStatus = 'STOPPING';
export type WorkflowStatusParams = {
appId: string;
chatId: string;
};
// 获取工作流状态键
export const getRuntimeStatusKey = (params: WorkflowStatusParams): string => {
return `${WORKFLOW_STATUS_PREFIX}:${params.appId}:${params.chatId}`;
};
// 设置停止标志
export const setAgentRuntimeStop = async (params: WorkflowStatusParams): Promise<void> => {
const redis = getGlobalRedisConnection();
const key = getRuntimeStatusKey(params);
await redis.setex(key, TTL, 1);
};
// 删除停止标志
export const delAgentRuntimeStopSign = async (params: WorkflowStatusParams): Promise<void> => {
const redis = getGlobalRedisConnection();
const key = getRuntimeStatusKey(params);
await redis.del(key).catch((err) => {
addLog.error(`[Agent Runtime Stop] Delete stop sign error`, err);
});
};
// 检查工作流是否应该停止
export const shouldWorkflowStop = (params: WorkflowStatusParams): Promise<boolean> => {
const redis = getGlobalRedisConnection();
const key = getRuntimeStatusKey(params);
return redis
.get(key)
.then((res) => !!res)
.catch(() => false);
};
/**
* 等待工作流完成(停止标志被删除)
* @param params 工作流参数
* @param timeout 超时时间(毫秒),默认5秒
* @param pollInterval 轮询间隔(毫秒),默认50毫秒
*/
export const waitForWorkflowComplete = async ({
appId,
chatId,
timeout = 5000,
pollInterval = 50
}: {
appId: string;
chatId: string;
timeout?: number;
pollInterval?: number;
}) => {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const sign = await shouldWorkflowStop({ appId, chatId });
// 如果停止标志已被删除,说明工作流已完成
if (!sign) {
return;
}
// 等待下一次轮询
await delay(pollInterval);
}
// 超时后直接返回
return;
};
```
**测试用例位置**: `test/cases/service/core/app/workflow/workflowStatus.test.ts`
```typescript
import { describe, test, expect, beforeEach } from 'vitest';
import {
setAgentRuntimeStop,
delAgentRuntimeStopSign,
shouldWorkflowStop,
waitForWorkflowComplete
} from '@fastgpt/service/core/workflow/dispatch/workflowStatus';
describe('Workflow Status Redis Functions', () => {
const testAppId = 'test_app_123';
const testChatId = 'test_chat_456';
beforeEach(async () => {
// 清理测试数据
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
});
test('should set stopping sign', async () => {
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(true);
});
test('should return false for non-existent status', async () => {
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(false);
});
test('should return false after deleting stop sign', async () => {
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(false);
});
test('should wait for workflow completion', async () => {
// 设置初始停止标志
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
// 模拟异步完成(删除停止标志)
setTimeout(async () => {
await delAgentRuntimeStopSign({ appId: testAppId, chatId: testChatId });
}, 500);
// 等待完成
await waitForWorkflowComplete({
appId: testAppId,
chatId: testChatId,
timeout: 2000
});
// 验证停止标志已被删除
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(false);
});
test('should timeout when waiting too long', async () => {
await setAgentRuntimeStop({ appId: testAppId, chatId: testChatId });
// 等待超时(不删除标志)
await waitForWorkflowComplete({
appId: testAppId,
chatId: testChatId,
timeout: 100
});
// 验证停止标志仍然存在
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(true);
});
test('should handle concurrent stop sign operations', async () => {
// 并发设置停止标志
await Promise.all([
setAgentRuntimeStop({ appId: testAppId, chatId: testChatId }),
setAgentRuntimeStop({ appId: testAppId, chatId: testChatId })
]);
// 停止标志应该存在
const shouldStop = await shouldWorkflowStop({ appId: testAppId, chatId: testChatId });
expect(shouldStop).toBe(true);
});
});
```
## 3. 工作流停止检测机制改造
### 3.1 修改位置
**文件**: `packages/service/core/workflow/dispatch/index.ts`
### 3.2 工作流启动时的停止检测机制
**改造点 1: 停止检测逻辑 (行 196-216)**
使用内存变量 + 定时轮询 Redis 的方式:
```typescript
import { delAgentRuntimeStopSign, shouldWorkflowStop } from './workflowStatus';
// 初始化停止检测
let stopping = false;
const checkIsStopping = (): boolean => {
if (apiVersion === 'v2') {
return stopping;
}
if (apiVersion === 'v1') {
if (!res) return false;
return res.closed || !!res.errored;
}
return false;
};
// v2 版本: 启动定时器定期检查 Redis
const checkStoppingTimer =
apiVersion === 'v2'
? setInterval(async () => {
stopping = await shouldWorkflowStop({
appId: runningAppInfo.id,
chatId
});
}, 100)
: undefined;
```
**设计要点**:
- v2 版本使用内存变量 `stopping` + 100ms 定时器轮询 Redis
- v1 版本仍使用原有的 `res.closed/res.errored` 检测
- 轮询频率 100ms,平衡性能和响应速度
**改造点 2: 工作流完成后清理 (行 232-249)**
```typescript
return runWorkflow({
...data,
checkIsStopping, // 传递检测函数
query,
histories,
// ... 其他参数
}).finally(async () => {
// 清理定时器
if (streamCheckTimer) {
clearInterval(streamCheckTimer);
}
if (checkStoppingTimer) {
clearInterval(checkStoppingTimer);
}
// Close mcpClient connections
Object.values(mcpClientMemory).forEach((client) => {
client.closeConnection();
});
// 工作流完成后删除 Redis 记录
await delAgentRuntimeStopSign({
appId: runningAppInfo.id,
chatId
});
});
```
### 3.3 节点执行前的停止检测
**位置**: `packages/service/core/workflow/dispatch/index.ts:861-868`
`checkNodeCanRun` 方法中,每个节点执行前检查:
```typescript
private async checkNodeCanRun(
node: RuntimeNodeItemType,
skippedNodeIdList = new Set<string>()
) {
// ... 其他检查逻辑 ...
// Check queue status
if (data.maxRunTimes <= 0) {
addLog.error('Max run times is 0', {
appId: data.runningAppInfo.id
});
return;
}
// 停止检测
if (checkIsStopping()) {
addLog.warn('Workflow stopped', {
appId: data.runningAppInfo.id,
nodeId: node.nodeId,
nodeName: node.name
});
return;
}
// ... 执行节点逻辑 ...
}
```
**说明**:
- 直接调用 `checkIsStopping()` 同步方法
- 内部会检查内存变量 `stopping`
- 定时器每 100ms 更新一次该变量
- 检测到停止时记录日志并直接返回,不执行节点
## 4. v2/chat/stop 接口设计
### 4.1 接口规范
**接口路径**: `/api/v2/chat/stop`
**Schema 位置**: `packages/global/openapi/core/chat/api.ts`
**接口文档位置**: `packages/global/openapi/core/chat/index.ts`
**请求方法**: POST
**请求参数**:
```typescript
// packages/global/openapi/core/chat/api.ts
export const StopV2ChatSchema = z
.object({
appId: ObjectIdSchema.describe('应用ID'),
chatId: z.string().min(1).describe('对话ID'),
outLinkAuthData: OutLinkChatAuthSchema.optional().describe('外链鉴权数据')
});
export type StopV2ChatParams = z.infer<typeof StopV2ChatSchema>;
```
**响应格式**:
```typescript
export const StopV2ChatResponseSchema = z
.object({
success: z.boolean().describe('是否成功停止')
});
export type StopV2ChatResponse = z.infer<typeof StopV2ChatResponseSchema>;
```
### 4.2 接口实现
**文件位置**: `projects/app/src/pages/api/v2/chat/stop.ts`
```typescript
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry';
import { authChatCrud } from '@/service/support/permission/auth/chat';
import {
setAgentRuntimeStop,
waitForWorkflowComplete
} from '@fastgpt/service/core/workflow/dispatch/workflowStatus';
import { StopV2ChatSchema, type StopV2ChatResponse } from '@fastgpt/global/openapi/core/chat/controler/api';
async function handler(req: NextApiRequest, res: NextApiResponse): Promise<StopV2ChatResponse> {
const { appId, chatId, outLinkAuthData } = StopV2ChatSchema.parse(req.body);
// 鉴权 (复用聊天 CRUD 鉴权)
await authChatCrud({
req,
authToken: true,
authApiKey: true,
appId,
chatId,
...outLinkAuthData
});
// 设置停止标志
await setAgentRuntimeStop({
appId,
chatId
});
// 等待工作流完成 (最多等待 5 秒)
await waitForWorkflowComplete({ appId, chatId, timeout: 5000 });
return {
success: true
};
}
export default NextAPI(handler);
```
**接口文档** (`packages/global/openapi/core/chat/index.ts`):
```typescript
export const ChatPath: OpenAPIPath = {
// ... 其他路径
'/v2/chat/stop': {
post: {
summary: '停止 Agent 运行',
description: `优雅停止正在运行的 Agent, 会尝试等待当前节点结束后返回,最长 5s超过 5s 仍未结束,则会返回成功。
LLM 节点,流输出时会同时被终止,但 HTTP 请求节点这种可能长时间运行的,不会被终止。`,
tags: [TagsMap.chatPage],
requestBody: {
content: {
'application/json': {
schema: StopV2ChatSchema
}
}
},
responses: {
200: {
description: '成功停止工作流',
content: {
'application/json': {
schema: StopV2ChatResponseSchema
}
}
}
}
}
}
};
```
**说明**:
- 接口使用 `authChatCrud` 进行鉴权,支持 Token 和 API Key
- 支持分享链接和团队空间的鉴权数据
- 设置停止标志后等待最多 5 秒
- 无论是否超时,都返回 `success: true`
## 5. 前端改造
由于当前代码已经能够正常工作,且 v2 版本的后端已经实现了基于 Redis 的停止机制,前端可以保持现有的简单实现:
**保持现有实现的原因**:
1. 后端已经通过定时器轮询 Redis 实现了停止检测
2. 前端调用 `abort()` 后,后端会在下个检测周期(100ms内)发现停止标志
3. 简化前端逻辑,避免增加复杂性
4. 用户体验上,立即中断连接响应更快
**可选的增强方案**:
如果需要在前端显示更详细的停止状态,可以添加 API 客户端函数:
**文件位置**: `projects/app/src/web/core/chat/api.ts`
```typescript
import { POST } from '@/web/common/api/request';
import type { StopV2ChatParams, StopV2ChatResponse } from '@fastgpt/global/openapi/core/chat/controler/api';
/**
* 停止 v2 版本工作流运行
*/
export const stopV2Chat = (data: StopV2ChatParams) =>
POST<StopV2ChatResponse>('/api/v2/chat/stop', data);
```
**增强的 abortRequest 函数**:
```typescript
/* Abort chat completions, questionGuide */
const abortRequest = useMemoizedFn(async (reason: string = 'stop') => {
// 先调用 abort 中断连接
chatController.current?.abort(new Error(reason));
questionGuideController.current?.abort(new Error(reason));
pluginController.current?.abort(new Error(reason));
// v2 版本: 可选地通知后端优雅停止
if (chatBoxData?.app?.version === 'v2' && appId && chatId) {
try {
await stopV2Chat({
appId,
chatId,
outLinkAuthData
});
} catch (error) {
// 静默失败,不影响用户体验
console.warn('Failed to notify backend to stop workflow', error);
}
}
});
```
**建议**:
- **推荐**: 保持当前简单实现,后端已经足够健壮
- **可选**: 如果需要更精确的停止状态追踪,可以实现上述增强方案
## 6. 完整调用流程
### 6.1 正常停止流程
```
用户点击停止按钮
前端: abortRequest()
前端: chatController.abort() [立即中断 HTTP 连接]
[可选] 前端: POST /api/v2/chat/stop
后端: setAgentRuntimeStop(appId, chatId) [设置停止标志]
后端: 定时器检测到 Redis 停止标志,更新内存变量 stopping = true
后端: 下个节点执行前 checkIsStopping() 返回 true
后端: 停止处理新节点,记录日志
后端: 工作流 finally 块删除 Redis 停止标志
[可选] 后端: waitForWorkflowComplete() 检测到停止标志被删除
[可选] 前端: 显示停止成功提示
```
### 6.2 超时流程
```
[可选] 前端: POST /api/v2/chat/stop
后端: setAgentRuntimeStop(appId, chatId)
后端: waitForWorkflowComplete(timeout=5s)
后端: 5秒后停止标志仍存在
后端: 返回成功响应 (不区分超时)
[可选] 前端: 显示成功提示
后端: 工作流继续运行,最终完成后删除停止标志
```
### 6.3 工作流自然完成流程
```
工作流运行中
所有节点执行完成
dispatchWorkFlow.finally()
删除 Redis 停止标志
清理定时器
60秒 TTL 确保即使删除失败也会自动清理
```
### 6.4 时序说明
**关键时间点**:
- **100ms**: 后端定时器检查 Redis 停止标志的频率
- **5s**: stop 接口等待工作流完成的超时时间
- **60s**: Redis 键的 TTL,自动清理时间
**响应时间**:
- 用户点击停止 → HTTP 连接中断: **立即** (前端 abort)
- 停止标志写入 Redis: **< 50ms** (Redis SETEX 操作)
- 后端检测到停止: **< 100ms** (定时器轮询周期)
- 当前节点停止执行: **取决于节点类型**
- LLM 流式输出: **立即**中断流
- HTTP 请求节点: **等待请求完成**
- 其他节点: **等待当前操作完成**
## 7. 测试策略
### 7.1 单元测试
**Redis 工具函数测试**:
- `setAgentRuntimeStop` / `shouldWorkflowStop` 基本功能
- `delAgentRuntimeStopSign` 删除功能
- `waitForWorkflowComplete` 等待机制和超时
- 并发操作安全性
**文件位置**: `test/cases/service/core/app/workflow/workflowStatus.test.ts`
**测试用例**:
```typescript
describe('Workflow Status Redis Functions', () => {
test('should set stopping sign')
test('should return false for non-existent status')
test('should detect stopping status')
test('should return false after deleting stop sign')
test('should wait for workflow completion')
test('should timeout when waiting too long')
test('should delete workflow stop sign')
test('should handle concurrent stop sign operations')
});
```

View File

@ -73,6 +73,7 @@ jobs:
--label "org.opencontainers.image.description=${{ steps.config.outputs.DESCRIPTION }}" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${{ steps.config.outputs.DOCKER_REPO_TAGGED }} \
.

View File

@ -1,33 +1,35 @@
{
// Place your FastGPT 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Place your FastGPT 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
"Next api template": {
"scope": "javascript,typescript",
"prefix": "nextapi",
"body": [
"import { NextAPI } from '@/service/middleware/entry';",
"import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';",
"",
"async function handler(",
" req: ApiRequestProps,",
" res: ApiResponseType<${1}ResponseType>",
"): Promise<${1}ResponseType> {",
" const body = ${1}BodySchema.parse(req.body);",
" const query = ${1}QuerySchema.parse(req.query);",
"",
" ${2}",
"",
" return {};",
"}",
"",
"export default NextAPI(handler);"
"import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';",
"import { NextAPI } from '@/service/middleware/entry';",
"",
"export type ${TM_FILENAME_BASE}Query = {};",
"",
"export type ${TM_FILENAME_BASE}Body = {};",
"",
"export type ${TM_FILENAME_BASE}Response = {};",
"",
"async function handler(",
" req: ApiRequestProps<${TM_FILENAME_BASE}Body, ${TM_FILENAME_BASE}Query>,",
" res: ApiResponseType<any>",
"): Promise<${TM_FILENAME_BASE}Response> {",
" $1",
" return {}",
"}",
"",
"export default NextAPI(handler);"
],
"description": "FastGPT Next API template with Zod validation"
"description": "FastGPT Next API template"
},
"use context template": {
"scope": "typescriptreact",
@ -36,7 +38,7 @@
"import React, { ReactNode } from 'react';",
"import { createContext } from 'use-context-selector';",
"",
"type ContextType = {${1}};",
"type ContextType = {$1};",
"",
"export const Context = createContext<ContextType>({});",
"",
@ -63,4 +65,4 @@
"});"
]
}
}
}

View File

@ -5,7 +5,7 @@
如果您发现了 FastGPT 的安全漏洞,请按照以下步骤进行报告:
1. **报告方式**
发送邮件至:archer@fastgpt.io
发送邮件至:yujinlong@sealos.io
请备注版本以及您的 GitHub 账号
3. **响应时间**

View File

@ -1,9 +1,9 @@
{
"tags": {
"fastgpt": "v4.14.4",
"fastgpt-sandbox": "v4.14.4",
"fastgpt-mcp_server": "v4.14.4",
"fastgpt-plugin": "v0.3.4",
"fastgpt": "v4.14.1",
"fastgpt-sandbox": "v4.14.1",
"fastgpt-mcp_server": "v4.14.1",
"fastgpt-plugin": "v0.3.1",
"aiproxy": "v0.3.2",
"aiproxy-pg": "0.8.0-pg15",
"mongo": "5.0.18",

View File

@ -136,7 +136,7 @@ services:
retries: 3
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
ports:
- 3002:3000
networks:
@ -144,7 +144,7 @@ services:
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
ports:
- 3005:3000
networks:
@ -153,7 +153,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
ports:
@ -162,7 +162,6 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false

View File

@ -136,7 +136,7 @@ services:
retries: 3
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
ports:
- 3002:3000
networks:
@ -144,7 +144,7 @@ services:
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
ports:
- 3005:3000
networks:
@ -153,7 +153,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
ports:
@ -162,7 +162,6 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false

View File

@ -177,7 +177,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -227,13 +227,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -242,7 +242,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -154,7 +154,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -204,13 +204,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -219,7 +219,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -135,7 +135,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -185,13 +185,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -200,7 +200,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -118,7 +118,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -168,13 +168,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -183,7 +183,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -177,7 +177,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -227,13 +227,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -242,7 +242,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -154,7 +154,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -204,13 +204,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -219,7 +219,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -135,7 +135,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -185,13 +185,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -200,7 +200,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -118,7 +118,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -168,13 +168,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -183,7 +183,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -162,7 +162,6 @@ services:
- fastgpt
environment:
- AUTH_TOKEN=token
- S3_EXTERNAL_BASE_URL=http://127.0.0.1:9000 # TODO: 改为你 Minio 的实际的 ip 地址
- S3_ENDPOINT=fastgpt-minio
- S3_PORT=9000
- S3_USE_SSL=false

View File

@ -1,6 +1,6 @@
# FastGPT 文档
这是FastGPT的官方文档采用 fumadoc 框架。
这是FastGPT的官方文档采用fumadoc框架。
## 运行项目
要运行文档,首先需要进行环境变量配置,在文档的根目录下创建`.env.local`文件,填写以下环境变量:
@ -12,7 +12,7 @@ FASTGPT_HOME_DOMAIN = #要跳转的FastGPT项目的域名默认海外版
你可以在FastGPT项目根目录下执行以下命令来运行文档。
```bash
npm install # 只能 npm install不能 pnpm
npm install #只能npm install不能pnpm
npm run dev
```
项目会默认跑在`http:localhost:3000`端口

View File

@ -4,7 +4,7 @@ title: 报错
1. ### 当前分组上游负载已饱和,请稍后再试(request id:202407100753411462086782835521)
是oneapi渠道的问题可以换个模型用或者换一家中转站
是oneapi渠道的问题可以换个模型用or换一家中转站
1. ### 使用API时在日志中报错Connection Error

View File

@ -36,7 +36,7 @@ import { Alert } from '@/components/docs/Alert';
### 2. 配置介绍
<Alert icon="🤖" context="success">
注意: 1. 目前语音识别模型仅会生效一个,所以配置时候,只需要配置一个即可。 2.
注意: 1. 目前语音识别模型和重排模型仅会生效一个,所以配置时候,只需要配置一个即可。 2.
系统至少需要一个语言模型和一个索引模型才能正常使用。
</Alert>

View File

@ -616,7 +616,7 @@ event取值
<Tab value="请求示例">
```bash
curl --location --request POST 'http://localhost:3000/api/core/chat/history/getHistories' \
curl --location --request POST 'http://localhost:3000/api/core/chat/getHistories' \
--header 'Authorization: Bearer [apikey]' \
--header 'Content-Type: application/json' \
--data-raw '{
@ -679,7 +679,7 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/history/getH
<Tab value="请求示例">
```bash
curl --location --request POST 'http://localhost:3000/api/core/chat/history/updateHistory' \
curl --location --request POST 'http://localhost:3000/api/core/chat/updateHistory' \
--header 'Authorization: Bearer [apikey]' \
--header 'Content-Type: application/json' \
--data-raw '{
@ -721,7 +721,7 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/history/upda
<Tab value="请求示例">
```bash
curl --location --request POST 'http://localhost:3000/api/core/chat/history/updateHistory' \
curl --location --request POST 'http://localhost:3000/api/core/chat/updateHistory' \
--header 'Authorization: Bearer [apikey]' \
--header 'Content-Type: application/json' \
--data-raw '{
@ -763,7 +763,7 @@ curl --location --request POST 'http://localhost:3000/api/core/chat/history/upda
<Tab value="请求示例">
```bash
curl --location --request DELETE 'http://localhost:3000/api/core/chat/history/delHistory?chatId=[chatId]&appId=[appId]' \
curl --location --request DELETE 'http://localhost:3000/api/core/chat/delHistory?chatId=[chatId]&appId=[appId]' \
--header 'Authorization: Bearer [apikey]'
```
@ -800,7 +800,7 @@ curl --location --request DELETE 'http://localhost:3000/api/core/chat/history/de
<Tab value="请求示例">
```bash
curl --location --request DELETE 'http://localhost:3000/api/core/chat/history/clearHistories?appId=[appId]' \
curl --location --request DELETE 'http://localhost:3000/api/core/chat/clearHistories?appId=[appId]' \
--header 'Authorization: Bearer [apikey]'
```

View File

@ -15,20 +15,20 @@ description: FastGPT 分享链接身份鉴权
### 接口统一响应格式
```jsonc
```json
{
"success": true,
"message": "错误提示",
"msg": "同message, 错误提示",
"data": {
"uid": "用户唯一凭证" // 必须返回
"uid": "用户唯一凭证"
}
}
```
`FastGPT` 将会判断`success`是否为`true`决定是允许用户继续操作。`message`与`msg`是等同的,你可以选择返回其中一个,当`success`不为`true`时,将会提示这个错误。
`uid` 是用户的唯一凭证,必须返回该 ID 且 ID 的格式为不包含 "|"、"/“、"\\" 字符的、小于等于 255 **字节长度**的字符串,否则会返回 `Invalid UID` 的错误。`uid` 将会用于拉取对话记录以及保存对话记录可参考下方实践案例。
`uid`是用户的唯一凭证,将会用于拉取对话记录以及保存对话记录可参考下方实践案例。
### 触发流程

View File

@ -1,42 +0,0 @@
---
title: 配置自定义域名
description: 如何在 FastGPT 中配置自定义域名
---
FastGPT 云服务版自 v4.14.4 后支持配置自定义域名。
## 如何配置自定义域名
### 1. 打开“自定义域名”页面
在侧边栏选择“账号” -> “自定义域名”,打开自定义域名配置页。
如果您的套餐等级不支持配置,请根据页面的指引升级套餐。
![打开配置页面](/imgs/guide/team_permissions/customDomain/1.png)
### 2. 添加自定义域名
1. 准备好您的域名。您的域名必须先经过备案,目前支持“阿里云”、“腾讯云”、“火山引擎”三家服务商的备案域名。
2. 点击“编辑”按钮,进入编辑状态。
3. 填入您的域名,例如 www.example.com
4. 在域名服务商的域名解析处,添加界面中提示的 DNS 记录,注意记录类型为 CNAME。
5. 添加解析记录后,点击"保存"按钮。系统将自动检查 DNS 解析情况,一般情况下,在一分钟内就可以获取到解析记录。如果长时间没有获取到记录,可以重试一次。
6. 待状态提示显示为“已生效”后,点击“确认”按钮即可。
![配置自定义域名](/imgs/guide/team_permissions/customDomain/2.png)
现在您可以通过您自己的域名访问 fastgpt 服务、调用 fastgpt 的 API 了。
## 域名解析失效
系统会每天对 DNS 解析进行检查,如果发现 DNS 解析记录失效,则会停用该自定义域名,可以在"自定义域名"管理界面中点击"编辑"进行重新解析。
![编辑](/imgs/guide/team_permissions/customDomain/3.png)
如果您需要修改自定义域名、或修改服务商,则需要删除自定义域名配置后进行重新配置。
## 使用案例
- [接入企业微信智能机器人](/docs/use-cases/external-integration/wecom)

View File

@ -1,5 +1,5 @@
{
"title": "团队与权限",
"description": "团队管理、成员组与权限设置,确保团队协作中的数据安全和权限分配合理。",
"pages": ["team_roles_permissions","invitation_link", "customDomain"]
}
"pages": ["team_roles_permissions","invitation_link"]
}

View File

@ -58,5 +58,5 @@ Due to servers potentially being located in different countries/regions, you agr
**Contact Us**
1. For any questions, suggestions, or complaints about this policy, contact us at: archer@fastgpt.io.
1. For any questions, suggestions, or complaints about this policy, contact us at: yujinlong@sealos.io.
2. We will respond promptly and address your concerns.

View File

@ -58,5 +58,5 @@ description: ' FastGPT 隐私政策'
**联系我们**
1. 如您对本隐私政策有任何疑问、建议或投诉,请通过以下方式与我们联系:archer@fastgpt.io。
1. 如您对本隐私政策有任何疑问、建议或投诉,请通过以下方式与我们联系:yujinlong@sealos.io。
2. 我们将尽快回复并解决您提出的问题。

View File

@ -72,4 +72,4 @@ This FastGPT Service Agreement constitutes the terms and conditions agreed betwe
**Article 7 Additional Provisions**
1. If any clause is deemed unlawful or invalid, the remaining provisions shall remain enforceable.
2. Sealos retains final authority in interpreting this Agreement and privacy policies. For any inquiries, please contact us at archer@fastgpt.io.
2. Sealos retains final authority in interpreting this Agreement and privacy policies. For any inquiries, please contact us at yujinlong@sealos.io.

View File

@ -67,4 +67,4 @@ FastGPT 服务协议是您与珠海环界云计算有限公司(以下简称“
**第7条 其他条款**
1. 如本协议中部分条款因违反法律法规而被视为无效,不影响其他条款的效力。
2. 本公司保留对本协议及隐私政策的最终解释权。如您对本协议或隐私政策有任何疑问,请联系我们:archer@fastgpt.io。
2. 本公司保留对本协议及隐私政策的最终解释权。如您对本协议或隐私政策有任何疑问,请联系我们:yujinlong@sealos.io。

View File

@ -92,7 +92,6 @@ description: FastGPT 文档目录
- [/docs/introduction/guide/plugins/google_search_plugin_guide](/docs/introduction/guide/plugins/google_search_plugin_guide)
- [/docs/introduction/guide/plugins/searxng_plugin_guide](/docs/introduction/guide/plugins/searxng_plugin_guide)
- [/docs/introduction/guide/plugins/upload_system_tool](/docs/introduction/guide/plugins/upload_system_tool)
- [/docs/introduction/guide/team_permissions/customDomain](/docs/introduction/guide/team_permissions/customDomain)
- [/docs/introduction/guide/team_permissions/invitation_link](/docs/introduction/guide/team_permissions/invitation_link)
- [/docs/introduction/guide/team_permissions/team_roles_permissions](/docs/introduction/guide/team_permissions/team_roles_permissions)
- [/docs/introduction/index](/docs/introduction/index)
@ -114,9 +113,6 @@ description: FastGPT 文档目录
- [/docs/upgrading/4-14/4140](/docs/upgrading/4-14/4140)
- [/docs/upgrading/4-14/4141](/docs/upgrading/4-14/4141)
- [/docs/upgrading/4-14/4142](/docs/upgrading/4-14/4142)
- [/docs/upgrading/4-14/4143](/docs/upgrading/4-14/4143)
- [/docs/upgrading/4-14/4144](/docs/upgrading/4-14/4144)
- [/docs/upgrading/4-14/4145](/docs/upgrading/4-14/4145)
- [/docs/upgrading/4-8/40](/docs/upgrading/4-8/40)
- [/docs/upgrading/4-8/41](/docs/upgrading/4-8/41)
- [/docs/upgrading/4-8/42](/docs/upgrading/4-8/42)

View File

@ -17,8 +17,8 @@ description: 'FastGPT V4.13.2 更新说明'
### 2. 增加 FastGPT/FastGPT-pro 环境变量
```
S3_PUBLIC_BUCKET=fastgpt-public #(公开读公开桶名称,对应原来 plugin 项目的S3_TOOL_BUCKET
S3_PRIVATE_BUCKET=fastgpt-private #(私有读私有写桶名称,对应原来 plugin 项目的S3_PLUGIN_BUCKET
S3_PUBLIC_BUCKET=fastgpt_public #(公开读公开桶名称,对应原来 plugin 项目的S3_TOOL_BUCKET
S3_PRIVATE_BUCKET=fastgpt_private #(私有读私有写桶名称,对应原来 plugin 项目的S3_PLUGIN_BUCKET
```
### 3. 修复 fastgpt-plugin 环境变量

View File

@ -1,5 +1,5 @@
---
title: 'V4.14.1(包含升级脚本)'
title: 'V4.14.1'
description: 'FastGPT V4.14.1 更新说明'
---
@ -29,7 +29,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4141' \
## 🚀 新增内容
1. 新工作台交互。原插件,改名"工作流工具",并移动到我的工具分类下。
1. 新工作台交互。
2. 工作流运行欠费后提供继续运行按键,无需从头开始。
## ⚙️ 优化

View File

@ -1,22 +1,13 @@
---
title: 'V4.14.2'
title: 'V4.14.2(进行中)'
description: 'FastGPT V4.14.2 更新说明'
---
## 更新指南
### 1. 更新镜像:
- 更新 FastGPT 镜像tag: v4.14.2
- 更新 FastGPT 商业版镜像tag: v4.14.2
- 更新 fastgpt-plugin 镜像 tag: v0.3.2
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新
## 🚀 新增内容
1. 封装底层 Agent Call 方式,支持工具连续调用时上下文的压缩。
1. 封装底层 Agent Call 方式,支持工具连续调用时上下文的压缩,以及单个工具长响应的压缩。
2. 模板市场新 UI。
3. 支持 Agent 编辑页快速创建知识库。
@ -24,7 +15,6 @@ description: 'FastGPT V4.14.2 更新说明'
1. 30 分钟模板市场缓存时长。
2. 自定义分隔符块大小采用最大块大小。
3. 避免日志记录触发递归日志风暴,排除日志模型的性能监控中间件。
## 🐛 修复

View File

@ -1,55 +0,0 @@
---
title: 'V4.14.3'
description: 'FastGPT V4.14.3 更新说明'
---
## 更新指南
### 1. 更新镜像:
- 更新 FastGPT 镜像tag: v4.14.3
- 更新 FastGPT 商业版镜像tag: v4.14.3
- 更新 fastgpt-plugin 镜像 tag: v0.3.3
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新
### 2. 执行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 `{{rootkey}}` 替换成环境变量里的 `rootkey``{{host}}` 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4143' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
会将原系统 MongoDB 的 GridFS 中的所有知识库文件迁移到 S3 中,包含文本数据集和图片数据集,但不包括文档(如 .docx里解析出来的图片。
## 🚀 新增内容
1. 知识库文件迁移至 S3(全部使用文件的地方均已迁移)。
2. 全局变量支持文件上传。
3. 表单输入节点支持密码、开关、时间点、时间范围、文件上传、对话模型选择。
4. 插件输入支持多选、时间点、时间范围、内部变量。
5. 系统插件,插件市场中会提示是否有新版本,并提供更新按键。
6. 工作流运行 QPM 限制。
## ⚙️ 优化
1. 工作流工具,文件上传输入 UX 优化。
2. 添加权限表校验中间件,增强权限表鲁棒性。
## 🐛 修复
1. 工作流调试预览窗口,重新渲染导致输入丢失。
2. S3 服务与主服务相同 Origin 的域名会导致对 S3 的文件请求 URL 被错误替换,产生 404 报错。
## 插件
1. 工具更新逻辑,提供一个计算的 version 值来判断更新
2. 微信公众号工具集:允许同时上传多篇文档到草稿箱
3. 修复工具缓存没有被正确刷新
4. 修复开发模式下刷新缓存导致静态文件重新上传
5. 修复修复上传 pkg 后图片没有被正确上传的问题

View File

@ -1,88 +0,0 @@
---
title: 'V4.14.4'
description: 'FastGPT V4.14.4 更新说明'
---
## 更新指南
### 1. 更新镜像:
- 更新 FastGPT 镜像tag: v4.14.4
- 更新 FastGPT 商业版镜像tag: v4.14.4
- 更新 fastgpt-plugin 镜像 tag: v0.3.4
- mcp_server 无需更新
- Sandbox 无需更新
- AIProxy 无需更新
### 2. 执行升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 `{{rootkey}}` 替换成环境变量里的 `rootkey``{{host}}` 替换成**FastGPT 域名**。
```bash
curl --location --request POST 'https://{{host}}/api/admin/initv4144' \
--header 'rootkey: {{rootkey}}' \
--header 'Content-Type: application/json'
```
1. 将 4.14.3 中,遗留的 Dataset/local 接口上传的文件,也迁移到 S3 中。
2. 全量计算旧的 chat 中的反馈,增加 flags 值便于筛选。该函数执行较慢所以放到异步执行接口不会返回结果请关注日志中是否打印Migration feedback completed!
## 🚀 新增内容
1. 工具调用支持配置流输出
2. AI 积分告警通知。
3. 对话日志支持展示 IP 地址归属地。
4. 对话日志支持展示应用版本名(如果对话中途修改成最新版本,则会被修改成最新版本)
5. 对话日志支持按点赞点踩过滤,并在对话详情里可以快速定位到赞/踩的记录。
6. 通过 API 上传本地文件至知识库,保存至 S3。同时将旧版 Gridfs 代码全部移除。
7. 新版订阅套餐逻辑。
8. 支持配置对话文件白名单。
9. S3 支持 pathStyle 和 region 配置。
10. 支持通过 Sealos 来进行多租户自定义域名配置。
11. 工作流中引用工具时,文件输入支持手动填写(原本只支持变量引用)。
12. 支持网络代理(HTTP_PROXY,HTTPS_PROXY)
## ⚙️ 优化
1. 增加 S3 上传文件超时时长为 5 分钟。
2. 问题优化采用 JinaAI 的边际收益公式,获取最大边际收益的检索词。
3. 用户通知,支持中英文,以及优化模板。
4. 删除知识库采用队列异步删除模式。
5. LLM 请求时,图片无效报错提示。
6. completions 接口,非 stream 模式, detail=false 时,增加返回 reason_content。
7. 增加对于无效的 S3 key 检测。
8. 删除应用和知识库时,强制要求输入名称校验。
9. Mongo 慢操作日志,可以准确打印集合名和操作内容。
10. 分享链接,自定义鉴权返回的 uid强制要求长度小于 200太长会影响文件上传
## 🐛 修复
1. 循环节点数组,取消过滤空内容。
2. 工作流工具,未传递自定义 DataId导致测试运行时查看知识库提示无权限。
3. 对话 Agent 工具配置中,非必填的布尔和数字类型无法直接确认。
4. 工作台卡片在名字过长时错位。
5. 分享链接中url query 中携带全局变量时,前端 UI 不会加载该值。
6. window 下判断 CSV 文件异常。
7. 模型测试时,如果模型未启动,会导致无法被测试。
8. MCP header 中带特殊内容时,会抛错。
9. 工作流引用其他 Agent 时,切换版本号后未及时更新 UI。
10. http 节点使用值为空字符串的全局变量时,值会被替换为 null。
11. 判断器节点折叠时,连线断开。
12. 节点调试时,单选和多选类型的变量无法展示选项。
13. 发布渠道文档链接定位错误。
14. Checkbox 在禁用状态时hover 样式错误。
15. 模型头像缺失情况下,默认 huggingface.svg 图标显示错误。
16. 日志导出时,结束时间会多出一天。
17. 表单输入,前端默认值未传递到实体值。
18. 工具调用时,未传递 max_tokens 参数。
19. 工作流判断器 value 值,未结合 condition 来综合获取数据类型。
20. 非直接分块模式的知识库数据,引用阅读器导航顺序异常。引用阅读器只会加载同一页。
## 插件
1. 新增 - GLM4.6 与 DS3.2 系列模型预设。
2. 修复 - MinerU SaaS 插件模型版本不能选择 vlm 的问题
3. 修复 - 微信公众号插件批量上传 markdown 参数传递问题
4. 新增 - 获取微信公众号草稿箱列表工具
5. 优化 - markdown 转文件支持自定义文件名
6. 修复 - import cache 导致的插件无法被更新的问题

View File

@ -1,27 +0,0 @@
---
title: 'V4.14.5(进行中)'
description: 'FastGPT V4.14.5 更新说明'
---
## 🚀 新增内容
1. 对话记录使用侧改成软删除,增加从日志管理里删除对话记录。
2. 更新Agent/工具时,会更新其上层所有目录的更新时间,以便其会排在列表前面。
3. 门户页支持配置单个应用运行可见度配。
## ⚙️ 优化
1. 优化获取 redis 所有 key 的逻辑,避免大量获取时导致阻塞。
2. MongoDB, Redis 和 MQ 的重连逻辑优化。
## 🐛 修复
1. MCP 工具创建时,使用自定义鉴权头会报错。
2. 获取对话日志列表时,如果用户头像为空,会抛错。
3. chatAgent 未开启问题优化时,前端 UI 显示开启。
4. 加载默认模型时maxTokens 字段未赋值,导致模型最大响应值配置为空。
5. S3 文件清理队列因网络稳定问题出现阻塞,导致删除任务不再执行。
## 插件

View File

@ -1,5 +1,5 @@
{
"title": "4.14.x",
"description": "",
"pages": ["4145", "4144", "4143", "4142", "4141", "4140"]
"pages": ["4142", "4141", "4140"]
}

View File

@ -27,7 +27,7 @@ import { Alert } from '@/components/docs/Alert';
背景知识中,引导模型调用工具去执行不通的操作。
<Alert icon="🤗" context="success">
**Tips:** 这里需要增加适当的上下文,方便模型结合历史录进行判断和决策~
**Tips:** 这里需要增加适当的上下文,方便模型结合历史录进行判断和决策~
</Alert>
## 3. HTTP 模块

View File

@ -3,70 +3,119 @@ title: 接入企微机器人教程
description: FastGPT 接入企微机器人教程
---
- 从 4.12.4 版本起FastGPT 商业版支持直接接入企微机器人,无需额外的 API。
- 从 4.14.4 版本起FastGPT 云服务版支持通过配置自定义域名的方式接入企微智能机器人。
从 4.12.4 版本起FastGPT 商业版支持直接接入企微机器人,无需额外的 API。
## 1. (云服务版必须)配置自定义域名
## 1. 配置可信域名和可信IP
企微要求智能机器人消息推送地址必须使用企业主体域名,因此云服务版本用户必须先配置自定义域名才能使用企微机器人。
点击企微头像,打开管理企业
- [配置自定义域名](/docs/introduction/guide/team_permissions/customDomain)
![图片](/imgs/wecom-bot-1.png)
若您是商业版用户,请继续使用您企业的域名。
在应用管理中找到"自建"-"创建应用"
## 2. 创建智能机器人
![图片](/imgs/wecom-bot-2.png)
### 2.1 超级管理员登录
创建好应用后, 下拉, 依次配置"网页授权及JS-SDK"和"企业可信IP"
[点击打开企业微信管理后台](https://work.weixin.qq.com/)
![图片](/imgs/wecom-bot-3.png)
### 2.2 找到智能机器人入口
其中, 网页授权及JS-SDK要求按照企微指引,完成域名归属认证
在"安全与管理" - "管理工具"页面点击"智能机器人" ( 注意: 只有企业创建者或超级管理员才有权限看到这个入口 )
![图片](/imgs/wecom-bot-4.png)
![图片](/imgs/use-cases/external-integration/wecom/1.png)
企业可信IP要求为企业服务器IP, 后续企微的回调URL将请求到此IP
### 2.3 选择 “API模式创建” 智能机器人
![图片](/imgs/wecom-bot-5.png)
在创建机器人页面, 下拉, 点击 "API模式创建"
## 2. 创建企业自建应用
![图片](/imgs/use-cases/external-integration/wecom/2.png)
前往 FastGPT ,选择想要接入的应用,在 发布渠道 页面,新建一个接入企微智能机器人的发布渠道,填写好基础信息。
### 2.4 获取关键密钥
![图片](/imgs/wecom-bot-6.png)
随机生成或者手动输入 Token 和 Encoding-AESKey并且记录下来
现在回到企业微信平台,找到 Corp ID, Secret, Agent ID, Token, AES Key 信息并填写回 FastGPT 平台
![图片](/imgs/use-cases/external-integration/wecom/3.png)
![图片](/imgs/wecom-bot-7.png)
### 2.5 创建企微机器人发布渠道
在"我的企业"里找到企业 ID, 填写到 FastGPT 的 Corp ID 中
在 FastGPT 中,选择要使用 Agent在发布渠道页面选择“企业微信机器人”点击“创建”
![图片](/imgs/wecom-bot-8.png)
![图片](/imgs/use-cases/external-integration/wecom/4.png)
在应用中找到 Agent Id 和 Secret, 并填写回 FastGPT
### 2.6 配置发布渠道信息
![图片](/imgs/wecom-bot-9.png)
配置该发布渠道的信息,需要填入 Token 和 AESKey也就是第四步中记录下来的 Token 和 Encoding-AESKey
点击"消息接收"-"设置API接收"
![图片](/imgs/use-cases/external-integration/wecom/5.png)
![图片](/imgs/wecom-bot-10.png)
### 2.7 复制回调地址
随机生成或者手动输入 Token 和 Encoding-Key, 分别填写到 FastGPT 的 Token 和 AES Key 中
点击“确认”后,选择您配置的自定义域名,复制回调地址,填回企微智能机器人配置页中。
![图片](/imgs/wecom-bot-11.png)
![图片](/imgs/use-cases/external-integration/wecom/6.png)
填写完成后确认创建
## 3. 使用智能机器人
然后点击请求地址, 复制页面中的链接
![图片](/imgs/wecom-bot-12.png)
回到刚才的配置详情, 将刚才复制的链接填入 URL 框中, 并点击下方的保存 ,即可完成自建应用的创建
注意: 若复制的链接是以 "http://localhost" 开头, 需要将本地地址改为企业主体域名
因为企微会给填写的 URL 发送验证密文, 若 URL 为本地地址, 则本地接收不到企微的密文
若 URL 不是企业主体域名, 则验证会失败
## 3. 创建智能机器人
第二步创建企业自建应用是为了验证域名和IP的合规性, 并获取 secret 参数, 下面创建智能机器人才是正式的配置流程
在"安全与管理" - "管理工具"页面找到"智能机器人" ( 注意: 只有企业创建者或超级管理员才有权限看到这个入口 )
![图片](/imgs/wecom-bot-13.png)
创建机器人页面,下拉,找到,点击"API模式创建"
![图片](/imgs/wecom-bot-14.png)
与刚才配置自建应用同理, 在 FastGPT 平台再新增一个发布渠道, 并回到企业微信配置参数
![图片](/imgs/wecom-bot-19.png)
随机生成或者手动输入 Token 和 Encoding-AESKey, 分别填写到 FastGPT 的 Token 和 AES Key 中
![图片](/imgs/wecom-bot-15.png)
Corp ID 和 Secret 这两个参数和刚才的自建应用保持一致
Agent ID 和自建应用的不同, 需要先填写一个自定义值, 后续会根据企业微信提供的数据重新更改
在 FastGPT 将Corp ID, Secret, Agent ID, Token, AES Key 等参数都填写完毕后, 点击确认
然后点击请求地址, 复制页面中的链接
回到企业微信, 将链接粘贴到智能机器人的 URL 配置栏, 点击创建
创建完成后, 找到智能机器人的配置详情
![图片](/imgs/wecom-bot-16.png)
复制 Bot ID, 填写到 FastGPT 的 Agent ID 中, 即可完成智能机器人配置
![图片](/imgs/wecom-bot-17.png)
## 4. 使用智能机器人
在企业微信平台的"通讯录",即可找到创建的机器人,就可以发送消息了
![图片](/imgs/use-cases/external-integration/wecom/7.png)
![图片](/imgs/wecom-bot-18.png)
## FAQ
### 发送了消息,没响应
1. 检查可信域名是否配置正确。
2. 检查 Token 和 Encoding-AESKey 是否正确。
3. 查看 FastGPT 对话日志,是否有对应的提问记录。
4. 如果没记录,则可能是应用运行报错了,可以先试试最简单的机器人。
1. 检查可信域名和可信IP是否配置正确。
2. 检查自建应用的 Secret 参数是否与智能机器人一致
3. 查看 FastGPT 对话日志,是否有对应的提问记录
4. 如果没记录,则可能是应用运行报错了,可以先试试最简单的机器人.

View File

@ -2,7 +2,7 @@
"document/content/docs/faq/app.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/chat.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/dataset.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/error.mdx": "2025-12-10T20:07:05+08:00",
"document/content/docs/faq/error.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/external_channel_integration.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/index.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/faq/other.mdx": "2025-08-04T22:07:52+08:00",
@ -26,15 +26,15 @@
"document/content/docs/introduction/development/migration/docker_db.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/migration/docker_mongo.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/modelConfig/ai-proxy.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/introduction/development/modelConfig/intro.mdx": "2025-12-03T08:36:19+08:00",
"document/content/docs/introduction/development/modelConfig/intro.mdx": "2025-08-12T22:22:18+08:00",
"document/content/docs/introduction/development/modelConfig/one-api.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/modelConfig/ppio.mdx": "2025-09-29T11:52:39+08:00",
"document/content/docs/introduction/development/modelConfig/siliconCloud.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/introduction/development/openapi/app.mdx": "2025-09-26T13:18:51+08:00",
"document/content/docs/introduction/development/openapi/chat.mdx": "2025-12-18T13:49:45+08:00",
"document/content/docs/introduction/development/openapi/chat.mdx": "2025-11-14T13:21:17+08:00",
"document/content/docs/introduction/development/openapi/dataset.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/introduction/development/openapi/intro.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/introduction/development/openapi/share.mdx": "2025-12-09T23:33:32+08:00",
"document/content/docs/introduction/development/openapi/share.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/introduction/development/proxy/cloudflare.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/proxy/http_proxy.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/development/proxy/nginx.mdx": "2025-07-23T21:35:03+08:00",
@ -89,7 +89,6 @@
"document/content/docs/introduction/guide/plugins/google_search_plugin_guide.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/plugins/searxng_plugin_guide.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/plugins/upload_system_tool.mdx": "2025-11-04T16:58:12+08:00",
"document/content/docs/introduction/guide/team_permissions/customDomain.mdx": "2025-12-10T20:07:05+08:00",
"document/content/docs/introduction/guide/team_permissions/invitation_link.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/guide/team_permissions/team_roles_permissions.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/introduction/index.en.mdx": "2025-07-23T21:35:03+08:00",
@ -97,12 +96,12 @@
"document/content/docs/protocol/index.mdx": "2025-07-30T15:38:30+08:00",
"document/content/docs/protocol/open-source.en.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/protocol/open-source.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/protocol/privacy.en.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/protocol/privacy.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/protocol/terms.en.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/protocol/terms.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/protocol/privacy.en.mdx": "2025-08-03T22:37:45+08:00",
"document/content/docs/protocol/privacy.mdx": "2025-08-03T22:37:45+08:00",
"document/content/docs/protocol/terms.en.mdx": "2025-08-03T22:37:45+08:00",
"document/content/docs/protocol/terms.mdx": "2025-08-03T22:37:45+08:00",
"document/content/docs/toc.en.mdx": "2025-08-04T13:42:36+08:00",
"document/content/docs/toc.mdx": "2025-12-17T17:44:38+08:00",
"document/content/docs/toc.mdx": "2025-11-14T13:21:17+08:00",
"document/content/docs/upgrading/4-10/4100.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-10/4101.mdx": "2025-09-08T20:07:20+08:00",
"document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00",
@ -114,13 +113,10 @@
"document/content/docs/upgrading/4-12/4124.mdx": "2025-09-17T22:29:56+08:00",
"document/content/docs/upgrading/4-13/4130.mdx": "2025-11-04T15:06:39+08:00",
"document/content/docs/upgrading/4-13/4131.mdx": "2025-09-30T15:47:06+08:00",
"document/content/docs/upgrading/4-13/4132.mdx": "2025-12-15T11:50:00+08:00",
"document/content/docs/upgrading/4-13/4132.mdx": "2025-10-21T11:46:53+08:00",
"document/content/docs/upgrading/4-14/4140.mdx": "2025-11-06T15:43:00+08:00",
"document/content/docs/upgrading/4-14/4141.mdx": "2025-11-19T10:15:27+08:00",
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00",
"document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00",
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00",
"document/content/docs/upgrading/4-14/4145.mdx": "2025-12-21T23:28:19+08:00",
"document/content/docs/upgrading/4-14/4141.mdx": "2025-11-12T12:19:02+08:00",
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-17T21:02:39+08:00",
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",
@ -192,7 +188,7 @@
"document/content/docs/use-cases/app-cases/feishu_webhook.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/fixingEvidence.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/google_search.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/lab_appointment.mdx": "2025-12-10T20:07:05+08:00",
"document/content/docs/use-cases/app-cases/lab_appointment.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/multi_turn_translation_bot.mdx": "2025-07-23T21:35:03+08:00",
"document/content/docs/use-cases/app-cases/submit_application_template.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/use-cases/app-cases/translate-subtitle-using-gpt.mdx": "2025-07-23T21:35:03+08:00",
@ -200,6 +196,6 @@
"document/content/docs/use-cases/external-integration/feishu.mdx": "2025-07-24T14:23:04+08:00",
"document/content/docs/use-cases/external-integration/official_account.mdx": "2025-08-05T23:20:39+08:00",
"document/content/docs/use-cases/external-integration/openapi.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-12-10T20:07:05+08:00",
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-09-16T14:22:28+08:00",
"document/content/docs/use-cases/index.mdx": "2025-07-24T14:23:04+08:00"
}

View File

@ -18,7 +18,7 @@
"fumadocs-ui": "15.6.3",
"gray-matter": "^4.0.3",
"lucide-react": "^0.525.0",
"next": "15.5.9",
"next": "15.3.5",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-responsive": "^10.0.1",
@ -41,6 +41,212 @@
"zod": "^4.0.5"
}
},
"node_modules/@algolia/client-abtesting": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.34.0.tgz",
"integrity": "sha512-d6ardhDtQsnMpyr/rPrS3YuIE9NYpY4rftkC7Ap9tyuhZ/+V3E/LH+9uEewPguKzVqduApdwJzYq2k+vAXVEbQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-analytics": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.34.0.tgz",
"integrity": "sha512-WXIByjHNA106JO1Dj6b4viSX/yMN3oIB4qXr2MmyEmNq0MgfuPfPw8ayLRIZPa9Dp27hvM3G8MWJ4RG978HYFw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-common": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.34.0.tgz",
"integrity": "sha512-JeN1XJLZIkkv6yK0KT93CIXXk+cDPUGNg5xeH4fN9ZykYFDWYRyqgaDo+qvg4RXC3WWkdQ+hogQuuCk4Y3Eotw==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-insights": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.34.0.tgz",
"integrity": "sha512-gdFlcQa+TWXJUsihHDlreFWniKPFIQ15i5oynCY4m9K3DCex5g5cVj9VG4Hsquxf2t6Y0yv8w6MvVTGDO8oRLw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-personalization": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.34.0.tgz",
"integrity": "sha512-g91NHhIZDkh1IUeNtsUd8V/ZxuBc2ByOfDqhCkoQY3Z/mZszhpn3Czn6AR5pE81fx793vMaiOZvQVB5QttArkQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-query-suggestions": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.34.0.tgz",
"integrity": "sha512-cvRApDfFrlJ3Vcn37U4Nd/7S6T8cx7FW3mVLJPqkkzixv8DQ/yV+x4VLirxOtGDdq3KohcIbIGWbg1QuyOZRvQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/client-search": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.34.0.tgz",
"integrity": "sha512-m9tK4IqJmn+flEPRtuxuHgiHmrKV0su5fuVwVpq8/es4DMjWMgX1a7Lg1PktvO8AbKaTp9kTtBAPnwXpuCwmEg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/ingestion": {
"version": "1.34.0",
"resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.34.0.tgz",
"integrity": "sha512-2rxy4XoeRtIpzxEh5u5UgDC5HY4XbNdjzNgFx1eDrfFkSHpEVjirtLhISMy2N5uSFqYu1uUby5/NC1Soq8J7iw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/monitoring": {
"version": "1.34.0",
"resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.34.0.tgz",
"integrity": "sha512-OJiDhlJX8ZdWAndc50Z6aUEW/YmnhFK2ul3rahMw5/c9Damh7+oY9SufoK2LimJejy+65Qka06YPG29v2G/vww==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/recommend": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.34.0.tgz",
"integrity": "sha512-fzNQZAdVxu/Gnbavy8KW5gurApwdYcPW6+pjO7Pw8V5drCR3eSqnOxSvp79rhscDX8ezwqMqqK4F3Hsq+KpRzg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/requester-browser-xhr": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.34.0.tgz",
"integrity": "sha512-gEI0xjzA/xvMpEdYmgQnf6AQKllhgKRtnEWmwDrnct+YPIruEHlx1dd7nRJTy/33MiYcCxkB4khXpNrHuqgp3Q==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/requester-fetch": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.34.0.tgz",
"integrity": "sha512-5SwGOttpbACT4jXzfSJ3mnTcF46SVNSnZ1JjxC3qBa3qKi4U0CJGzuVVy3L798u8dG5H0SZ2MAB5v7180Gnqew==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@algolia/requester-node-http": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.34.0.tgz",
"integrity": "sha512-409XlyIyEXrxyGjWxd0q5RASizHSRVUU0AXPCEdqnbcGEzbCgL1n7oYI8YxzE/RqZLha+PNwWCcTVn7EE5tyyQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-common": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
@ -1104,15 +1310,15 @@
}
},
"node_modules/@next/env": {
"version": "15.5.9",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz",
"integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.5.tgz",
"integrity": "sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==",
"license": "MIT"
},
"node_modules/@next/swc-darwin-arm64": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz",
"integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.5.tgz",
"integrity": "sha512-lM/8tilIsqBq+2nq9kbTW19vfwFve0NR7MxfkuSUbRSgXlMQoJYg+31+++XwKVSXk4uT23G2eF/7BRIKdn8t8w==",
"cpu": [
"arm64"
],
@ -1126,9 +1332,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz",
"integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.5.tgz",
"integrity": "sha512-WhwegPQJ5IfoUNZUVsI9TRAlKpjGVK0tpJTL6KeiC4cux9774NYE9Wu/iCfIkL/5J8rPAkqZpG7n+EfiAfidXA==",
"cpu": [
"x64"
],
@ -1142,9 +1348,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz",
"integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.5.tgz",
"integrity": "sha512-LVD6uMOZ7XePg3KWYdGuzuvVboxujGjbcuP2jsPAN3MnLdLoZUXKRc6ixxfs03RH7qBdEHCZjyLP/jBdCJVRJQ==",
"cpu": [
"arm64"
],
@ -1158,9 +1364,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz",
"integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.5.tgz",
"integrity": "sha512-k8aVScYZ++BnS2P69ClK7v4nOu702jcF9AIHKu6llhHEtBSmM2zkPGl9yoqbSU/657IIIb0QHpdxEr0iW9z53A==",
"cpu": [
"arm64"
],
@ -1174,9 +1380,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz",
"integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.5.tgz",
"integrity": "sha512-2xYU0DI9DGN/bAHzVwADid22ba5d/xrbrQlr2U+/Q5WkFUzeL0TDR963BdrtLS/4bMmKZGptLeg6282H/S2i8A==",
"cpu": [
"x64"
],
@ -1190,9 +1396,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz",
"integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.5.tgz",
"integrity": "sha512-TRYIqAGf1KCbuAB0gjhdn5Ytd8fV+wJSM2Nh2is/xEqR8PZHxfQuaiNhoF50XfY90sNpaRMaGhF6E+qjV1b9Tg==",
"cpu": [
"x64"
],
@ -1206,9 +1412,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz",
"integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.5.tgz",
"integrity": "sha512-h04/7iMEUSMY6fDGCvdanKqlO1qYvzNxntZlCzfE8i5P0uqzVQWQquU1TIhlz0VqGQGXLrFDuTJVONpqGqjGKQ==",
"cpu": [
"arm64"
],
@ -1222,9 +1428,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "15.5.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz",
"integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.5.tgz",
"integrity": "sha512-5fhH6fccXxnX2KhllnGhkYMndhOiLOLEiVGYjP2nizqeGWkN10sA9taATlXwake2E2XMvYZjjz0Uj7T0y+z1yw==",
"cpu": [
"x64"
],
@ -2128,6 +2334,12 @@
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
"license": "Apache-2.0"
},
"node_modules/@swc/helpers": {
"version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@ -2543,7 +2755,6 @@
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@ -2554,7 +2765,6 @@
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
"devOptional": true,
"license": "MIT",
"peer": true,
"peerDependencies": {
"@types/react": "^19.0.0"
}
@ -2576,7 +2786,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -2593,6 +2802,32 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/algoliasearch": {
"version": "5.34.0",
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.34.0.tgz",
"integrity": "sha512-wioVnf/8uuG8Bmywhk5qKIQ3wzCCtmdvicPRb0fa3kKYGGoewfgDqLEaET1MV2NbTc3WGpPv+AgauLVBp1nB9A==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@algolia/client-abtesting": "5.34.0",
"@algolia/client-analytics": "5.34.0",
"@algolia/client-common": "5.34.0",
"@algolia/client-insights": "5.34.0",
"@algolia/client-personalization": "5.34.0",
"@algolia/client-query-suggestions": "5.34.0",
"@algolia/client-search": "5.34.0",
"@algolia/ingestion": "1.34.0",
"@algolia/monitoring": "1.34.0",
"@algolia/recommend": "5.34.0",
"@algolia/requester-browser-xhr": "5.34.0",
"@algolia/requester-fetch": "5.34.0",
"@algolia/requester-node-http": "5.34.0"
},
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -2642,6 +2877,17 @@
"node": ">=8"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/camelcase": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
@ -3244,7 +3490,6 @@
"resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-15.6.3.tgz",
"integrity": "sha512-71IPC6Y0ZLPHlavYormnF1r2uX/lNrTFTYCEh6Akll8hWxRNbKG9Hk4xpFJDTkU83c8eLtHk2iow/ccQkcV6Hw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@formatjs/intl-localematcher": "^0.6.1",
"@orama/orama": "^3.1.9",
@ -4295,9 +4540,9 @@
}
},
"node_modules/mdast-util-to-hast": {
"version": "13.2.1",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
"integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@ -5173,14 +5418,15 @@
}
},
"node_modules/next": {
"version": "15.5.9",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz",
"integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==",
"version": "15.3.5",
"resolved": "https://registry.npmjs.org/next/-/next-15.3.5.tgz",
"integrity": "sha512-RkazLBMMDJSJ4XZQ81kolSpwiCt907l0xcgcpF4xC2Vml6QVcPNXW0NQRwQ80FFtSn7UM52XN0anaw8TEJXaiw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@next/env": "15.5.9",
"@next/env": "15.3.5",
"@swc/counter": "0.1.3",
"@swc/helpers": "0.5.15",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
"styled-jsx": "5.1.6"
@ -5192,19 +5438,19 @@
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "15.5.7",
"@next/swc-darwin-x64": "15.5.7",
"@next/swc-linux-arm64-gnu": "15.5.7",
"@next/swc-linux-arm64-musl": "15.5.7",
"@next/swc-linux-x64-gnu": "15.5.7",
"@next/swc-linux-x64-musl": "15.5.7",
"@next/swc-win32-arm64-msvc": "15.5.7",
"@next/swc-win32-x64-msvc": "15.5.7",
"sharp": "^0.34.3"
"@next/swc-darwin-arm64": "15.3.5",
"@next/swc-darwin-x64": "15.3.5",
"@next/swc-linux-arm64-gnu": "15.3.5",
"@next/swc-linux-arm64-musl": "15.3.5",
"@next/swc-linux-x64-gnu": "15.3.5",
"@next/swc-linux-x64-musl": "15.3.5",
"@next/swc-win32-arm64-msvc": "15.3.5",
"@next/swc-win32-x64-msvc": "15.3.5",
"sharp": "^0.34.1"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
"@playwright/test": "^1.51.1",
"@playwright/test": "^1.41.2",
"babel-plugin-react-compiler": "*",
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
"react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
@ -5353,7 +5599,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -5469,7 +5714,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -5479,7 +5723,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@ -6022,6 +6265,14 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause"
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/stringify-entities": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@ -6101,8 +6352,7 @@
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
"integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==",
"devOptional": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/tapable": {
"version": "2.2.2",
@ -6198,7 +6448,6 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"

View File

@ -18,7 +18,7 @@
"fumadocs-ui": "15.6.3",
"gray-matter": "^4.0.3",
"lucide-react": "^0.525.0",
"next": "15.5.9",
"next": "15.3.5",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-responsive": "^10.0.1",

View File

@ -16,8 +16,6 @@
"url": "", // PDF
"key": "", // PDF
"doc2xKey": "", // doc2x
"textinAppId": "", // Textin App ID
"textinSecretCode": "", // Textin Secret Code
"price": 0 // PDF
}
}

View File

@ -177,7 +177,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -227,13 +227,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -242,7 +242,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -154,7 +154,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -204,13 +204,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -219,7 +219,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -135,7 +135,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -185,13 +185,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -200,7 +200,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -118,7 +118,7 @@ services:
fastgpt:
container_name: fastgpt
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.4 # git
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -168,13 +168,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -183,7 +183,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.4
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -177,7 +177,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -227,13 +227,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -242,7 +242,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -154,7 +154,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -204,13 +204,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -219,7 +219,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -135,7 +135,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -185,13 +185,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -200,7 +200,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

View File

@ -118,7 +118,7 @@ services:
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.14.4 # git
image: ghcr.io/labring/fastgpt:v4.14.1 # git
ports:
- 3000:3000
networks:
@ -168,13 +168,13 @@ services:
- ./config.json:/app/data/config.json
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.14.4
image: ghcr.io/labring/fastgpt-sandbox:v4.14.1
networks:
- fastgpt
restart: always
fastgpt-mcp-server:
container_name: fastgpt-mcp-server
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.4
image: ghcr.io/labring/fastgpt-mcp_server:v4.14.1
networks:
- fastgpt
ports:
@ -183,7 +183,7 @@ services:
environment:
- FASTGPT_ENDPOINT=http://fastgpt:3000
fastgpt-plugin:
image: ghcr.io/labring/fastgpt-plugin:v0.3.4
image: ghcr.io/labring/fastgpt-plugin:v0.3.1
container_name: fastgpt-plugin
restart: always
networks:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -11,10 +11,8 @@ export enum TeamErrEnum {
datasetAmountNotEnough = 'datasetAmountNotEnough',
appAmountNotEnough = 'appAmountNotEnough',
pluginAmountNotEnough = 'pluginAmountNotEnough',
appFolderAmountNotEnough = 'appFolderAmountNotEnough',
websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough',
ticketNotAvailable = 'ticketNotAvailable',
groupNameEmpty = 'groupNameEmpty',
groupNameDuplicate = 'groupNameDuplicate',
groupNotExist = 'groupNotExist',
@ -67,10 +65,6 @@ const teamErr = [
statusText: TeamErrEnum.pluginAmountNotEnough,
message: i18nT('common:code_error.team_error.plugin_amount_not_enough')
},
{
statusText: TeamErrEnum.appFolderAmountNotEnough,
message: i18nT('common:code_error.team_error.app_folder_amount_not_enough')
},
{
statusText: TeamErrEnum.websiteSyncNotEnough,
message: i18nT('common:code_error.team_error.website_sync_not_enough')
@ -79,10 +73,6 @@ const teamErr = [
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
},
{
statusText: TeamErrEnum.ticketNotAvailable,
message: i18nT('common:code_error.team_error.ticket_not_available')
},
{
statusText: TeamErrEnum.groupNameEmpty,
message: i18nT('common:code_error.team_error.group_name_empty')

View File

@ -1,4 +1,4 @@
import type { OutLinkChatAuthProps } from '../../support/permission/chat';
import type { OutLinkChatAuthProps } from '../../support/permission/chat.d';
export type preUploadImgProps = OutLinkChatAuthProps & {
// expiredTime?: Date;

View File

@ -1,8 +1,20 @@
import { i18nT } from '../../../web/i18n/utils';
/* mongo fs bucket */
export enum BucketNameEnum {
dataset = 'dataset',
chat = 'chat'
}
export const bucketNameMap = {
[BucketNameEnum.dataset]: {
label: i18nT('file:bucket_file'),
previewExpireMinutes: 30 // 30 minutes
},
[BucketNameEnum.chat]: {
label: i18nT('file:bucket_chat'),
previewExpireMinutes: 7 * 24 * 60 // 7 days
}
};
export const EndpointUrl = `${process.env.FILE_DOMAIN || process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}`;
export const ReadFileBaseUrl = `${EndpointUrl}/api/common/file/read`;

View File

@ -4,4 +4,3 @@ export const FolderIcon = 'file/fill/folder';
export const FolderImgUrl = '/imgs/files/folder.svg';
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
export const HttpImgUrl = '/imgs/workflow/http.png';
export const TempFileURL = '/api/file/temp';

View File

@ -1,5 +1,5 @@
import { detect } from 'jschardet';
import { imageFileType } from './constants';
import { documentFileType } from './constants';
import { ChatFileTypeEnum } from '../../core/chat/constants';
import { type UserChatItemFileItemType } from '../../core/chat/type';
import * as fs from 'fs';
@ -66,17 +66,18 @@ export const parseUrlToFileType = (url: string): UserChatItemFileItemType | unde
})();
const extension = filename?.split('.').pop()?.toLowerCase() || '';
if (extension && imageFileType.includes(extension)) {
// Default to file type for non-extension files
// If it's a document type, return as file, otherwise treat as image
if (extension && documentFileType.includes(extension)) {
return {
type: ChatFileTypeEnum.image,
type: ChatFileTypeEnum.file,
name: filename || 'null',
url
};
}
// If it's a document type, return as file, otherwise treat as image
// Default to image type for non-document files
return {
type: ChatFileTypeEnum.file,
type: ChatFileTypeEnum.image,
name: filename || 'null',
url
};

View File

@ -1,28 +0,0 @@
import path from 'path';
export const isCSVFile = (filename: string) => {
const extension = path.extname(filename).toLowerCase();
return extension === '.csv';
};
export function detectImageContentType(buffer: Buffer) {
if (!buffer || buffer.length < 12) return 'text/plain';
// JPEG
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return 'image/jpeg';
// PNG
const pngSig = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
if (pngSig.every((v, i) => buffer.readUInt8(i) === v)) return 'image/png';
// GIF
const gifSig = buffer.subarray(0, 6).toString('ascii');
if (gifSig === 'GIF87a' || gifSig === 'GIF89a') return 'image/gif';
// WEBP
const riff = buffer.subarray(0, 4).toString('ascii');
const webp = buffer.subarray(8, 12).toString('ascii');
if (riff === 'RIFF' && webp === 'WEBP') return 'image/webp';
return 'text/plain';
}

View File

@ -16,10 +16,3 @@ export const parseI18nString = (str: I18nStringType | string = '', lang = 'en')
// 最后回退到英文
return str['en'] || '';
};
export const formatI18nLocationToZhEn = (locale: localeType = 'zh-CN'): 'zh' | 'en' => {
if (locale.toLocaleLowerCase().startsWith('zh')) {
return 'zh';
}
return 'en';
};

View File

@ -9,15 +9,5 @@ export enum TrackEnum {
datasetSearch = 'datasetSearch',
readSystemAnnouncement = 'readSystemAnnouncement',
clickOperationalAd = 'clickOperationalAd',
closeOperationalAd = 'closeOperationalAd',
teamChatQPM = 'teamChatQPM',
// Admin cron job tracks
subscriptionDeleted = 'subscriptionDeleted',
freeAccountCleanup = 'freeAccountCleanup',
auditLogCleanup = 'auditLogCleanup',
chatHistoryCleanup = 'chatHistoryCleanup',
// web tracks
clientError = 'clientError'
closeOperationalAd = 'closeOperationalAd'
}

View File

@ -184,7 +184,7 @@ export const sliceStrStartEnd = (str: string, start: number, end: number) => {
return `${startContent}${overSize ? `\n\n...[hide ${str.length - start - end} chars]...\n\n` : ''}${endContent}`;
};
/*
/*
Parse file extension from url
Test
1. https://xxx.com/file.pdf?token=123
@ -201,32 +201,3 @@ export const parseFileExtensionFromUrl = (url = '') => {
const extension = fileName.split('.').pop();
return (extension || '').toLowerCase();
};
export const formatNumberWithUnit = (num: number, locale: string = 'zh-CN'): string => {
if (num === 0) return '0';
if (!num || isNaN(num)) return '-';
const absNum = Math.abs(num);
const isNegative = num < 0;
const prefix = isNegative ? '-' : '';
if (locale === 'zh-CN') {
if (absNum >= 10000) {
const value = absNum / 10000;
const formatted = Number(value.toFixed(2)).toString();
return `${prefix}${formatted}`;
}
return num.toLocaleString(locale);
} else {
if (absNum >= 1000000) {
const value = absNum / 1000000;
const formatted = Number(value.toFixed(2)).toString();
return `${prefix}${formatted}M`;
}
if (absNum >= 1000) {
const value = absNum / 1000;
const formatted = Number(value.toFixed(2)).toString();
return `${prefix}${formatted}K`;
}
return num.toLocaleString(locale);
}
};

View File

@ -3,8 +3,7 @@ export enum SystemConfigsTypeEnum {
fastgptPro = 'fastgptPro',
systemMsgModal = 'systemMsgModal',
license = 'license',
operationalAd = 'operationalAd',
activityAd = 'activityAd'
operationalAd = 'operationalAd'
}
export const SystemConfigsTypeMap = {
@ -22,8 +21,5 @@ export const SystemConfigsTypeMap = {
},
[SystemConfigsTypeEnum.operationalAd]: {
label: 'operationalAd'
},
[SystemConfigsTypeEnum.activityAd]: {
label: 'activityAd'
}
};

View File

@ -65,7 +65,6 @@ export type FastGPTFeConfigsType = {
show_compliance_copywriting?: boolean;
show_aiproxy?: boolean;
show_coupon?: boolean;
show_discount_coupon?: boolean;
concatMd?: string;
show_dataset_feishu?: boolean;
@ -128,18 +127,6 @@ export type FastGPTFeConfigsType = {
alipay?: boolean;
bank?: boolean;
};
payFormUrl?: string;
fileUrlWhitelist?: string[];
customDomain?: {
enable?: boolean;
domain?: {
aliyun?: string;
tencent?: string;
volcengine?: string;
};
};
ip_whitelist?: string;
};
export type SystemEnvType = {
@ -158,39 +145,12 @@ export type SystemEnvType = {
chatApiKey?: string;
customPdfParse?: customPdfParseType;
fileUrlWhitelist?: string[];
customDomain?: customDomainType;
};
export type customDomainType = {
kc?: {
aliyun?: string;
tencent?: string;
volcengine?: string;
};
domain?: {
aliyun?: string;
tencent?: string;
volcengine?: string;
};
issuerServiceName?: {
aliyun?: string;
tencent?: string;
volcengine?: string;
};
nginxServiceName?: {
aliyun?: string;
tencent?: string;
volcengine?: string;
};
};
export type customPdfParseType = {
url?: string;
key?: string;
doc2xKey?: string;
textinAppId?: string;
textinSecretCode?: string;
price?: number;
};

29
packages/global/core/app/logs/api.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
import type { AppLogTimespanEnum } from './constants';
import type { AppChatLogAppData, AppChatLogChatData, AppChatLogUserData } from './type';
export type getChartDataBody = {
appId: string;
dateStart: Date;
dateEnd: Date;
source?: ChatSourceEnum[];
offset: number;
userTimespan: AppLogTimespanEnum;
chatTimespan: AppLogTimespanEnum;
appTimespan: AppLogTimespanEnum;
};
export type getChartDataResponse = {
userData: AppChatLogUserData;
chatData: AppChatLogChatData;
appData: AppChatLogAppData;
};
export type getTotalDataQuery = {
appId: string;
};
export type getTotalDataResponse = {
totalUsers: number;
totalChats: number;
totalPoints: number;
};

View File

@ -13,9 +13,7 @@ export enum AppLogKeysEnum {
ANNOTATED_COUNT = 'annotatedCount',
POINTS = 'points',
RESPONSE_TIME = 'responseTime',
ERROR_COUNT = 'errorCount',
REGION = 'region',
VERSION_NAME = 'versionName'
ERROR_COUNT = 'errorCount'
}
export const AppLogKeysEnumMap = {
@ -31,13 +29,11 @@ export const AppLogKeysEnumMap = {
[AppLogKeysEnum.ANNOTATED_COUNT]: i18nT('app:logs_keys_annotatedCount'),
[AppLogKeysEnum.POINTS]: i18nT('app:logs_keys_points'),
[AppLogKeysEnum.RESPONSE_TIME]: i18nT('app:logs_keys_responseTime'),
[AppLogKeysEnum.ERROR_COUNT]: i18nT('app:logs_keys_errorCount'),
[AppLogKeysEnum.REGION]: i18nT('app:logs_keys_region'),
[AppLogKeysEnum.VERSION_NAME]: i18nT('app:logs_keys_versionName')
[AppLogKeysEnum.ERROR_COUNT]: i18nT('app:logs_keys_errorCount')
};
export const DefaultAppLogKeys = [
{ key: AppLogKeysEnum.SOURCE, enable: false },
{ key: AppLogKeysEnum.SOURCE, enable: true },
{ key: AppLogKeysEnum.USER, enable: true },
{ key: AppLogKeysEnum.TITLE, enable: true },
{ key: AppLogKeysEnum.SESSION_ID, enable: false },
@ -49,9 +45,7 @@ export const DefaultAppLogKeys = [
{ key: AppLogKeysEnum.ANNOTATED_COUNT, enable: false },
{ key: AppLogKeysEnum.POINTS, enable: false },
{ key: AppLogKeysEnum.RESPONSE_TIME, enable: false },
{ key: AppLogKeysEnum.ERROR_COUNT, enable: false },
{ key: AppLogKeysEnum.REGION, enable: true },
{ key: AppLogKeysEnum.VERSION_NAME, enable: false }
{ key: AppLogKeysEnum.ERROR_COUNT, enable: false }
];
export enum AppLogTimespanEnum {

65
packages/global/core/app/logs/type.d.ts vendored Normal file
View File

@ -0,0 +1,65 @@
import type { ChatSourceEnum } from '../../core/chat/constants';
import type { AppLogKeysEnum } from './constants';
export type AppLogKeysType = {
key: AppLogKeysEnum;
enable: boolean;
};
export type AppLogKeysSchemaType = {
teamId: string;
appId: string;
logKeys: AppLogKeysType[];
};
export type AppChatLogSchema = {
_id: string;
appId: string;
teamId: string;
chatId: string;
userId: string;
source: string;
sourceName?: string;
createTime: Date;
updateTime: Date;
chatItemCount: number;
errorCount: number;
totalPoints: number;
goodFeedbackCount: number;
badFeedbackCount: number;
totalResponseTime: number;
isFirstChat: boolean; // whether this is the user's first session in the app
};
export type AppChatLogUserData = {
timestamp: number;
summary: {
userCount: number;
newUserCount: number;
retentionUserCount: number;
points: number;
sourceCountMap: Record<ChatSourceEnum, number>;
};
}[];
export type AppChatLogChatData = {
timestamp: number;
summary: {
chatItemCount: number;
chatCount: number;
errorCount: number;
points: number;
};
}[];
export type AppChatLogAppData = {
timestamp: number;
summary: {
goodFeedBackCount: number;
badFeedBackCount: number;
chatCount: number;
totalResponseTime: number;
};
}[];

View File

@ -1,39 +0,0 @@
import { ObjectIdSchema } from '../../../common/type/mongo';
import { ChatSourceEnum } from '../../chat/constants';
import { AppLogKeysEnum } from './constants';
import { z } from 'zod';
export const AppLogKeysSchema = z.object({
key: z.enum(AppLogKeysEnum),
enable: z.boolean()
});
export type AppLogKeysType = z.infer<typeof AppLogKeysSchema>;
export const AppLogKeysSchemaType = z.object({
teamId: z.string(),
appId: z.string(),
logKeys: z.array(AppLogKeysSchema)
});
export type AppLogKeysSchemaType = z.infer<typeof AppLogKeysSchemaType>;
export const AppChatLogSchema = z.object({
_id: ObjectIdSchema,
appId: ObjectIdSchema,
teamId: ObjectIdSchema,
chatId: z.string(),
userId: z.string(),
source: z.enum(ChatSourceEnum),
sourceName: z.string().optional(),
createTime: z.date(),
updateTime: z.date(),
chatItemCount: z.number(),
errorCount: z.number(),
totalPoints: z.number(),
goodFeedbackCount: z.number(),
badFeedbackCount: z.number(),
totalResponseTime: z.number(),
isFirstChat: z.boolean() // whether this is the user's first session in the app
});
export type AppChatLogSchema = z.infer<typeof AppChatLogSchema>;

View File

@ -59,9 +59,6 @@ export type AppSchema = {
inited?: boolean;
/** @deprecated */
teamTags: string[];
// 软删除字段
deleteTime?: Date | null;
};
export type AppListItemType = {

View File

@ -0,0 +1,68 @@
import type { OutLinkChatAuthType } from '../../support/permission/chat/type';
import { OutLinkChatAuthSchema } from '../../support/permission/chat/type';
import { ObjectIdSchema } from '../../common/type/mongo';
import z from 'zod';
export const PresignChatFileGetUrlSchema = z
.object({
key: z.string().min(1),
appId: ObjectIdSchema,
outLinkAuthData: OutLinkChatAuthSchema.optional()
})
.meta({
description: '获取对话文件预览链接',
example: {
key: '1234567890',
appId: '1234567890',
outLinkAuthData: {
shareId: '1234567890',
outLinkUid: '1234567890'
}
}
});
export type PresignChatFileGetUrlParams = z.infer<typeof PresignChatFileGetUrlSchema> & {
outLinkAuthData?: OutLinkChatAuthType;
};
export const PresignChatFilePostUrlSchema = z
.object({
filename: z.string().min(1),
appId: ObjectIdSchema,
chatId: ObjectIdSchema,
outLinkAuthData: OutLinkChatAuthSchema.optional()
})
.meta({
description: '获取上传对话文件预签名 URL',
example: {
filename: '1234567890',
appId: '1234567890',
chatId: '1234567890',
outLinkAuthData: {
shareId: '1234567890',
outLinkUid: '1234567890'
}
}
});
export type PresignChatFilePostUrlParams = z.infer<typeof PresignChatFilePostUrlSchema> & {
outLinkAuthData?: OutLinkChatAuthType;
};
export const UpdateChatFeedbackSchema = z
.object({
appId: z.string().min(1),
chatId: z.string().min(1),
dataId: z.string().min(1),
userBadFeedback: z.string().optional(),
userGoodFeedback: z.string().optional()
})
.meta({
description: '更新对话反馈',
example: {
appId: '1234567890',
chatId: '1234567890',
dataId: '1234567890',
userBadFeedback: '1234567890',
userGoodFeedback: '1234567890'
}
});
export type UpdateChatFeedbackProps = z.infer<typeof UpdateChatFeedbackSchema>;

View File

@ -28,7 +28,6 @@ export type ChatSchemaType = {
teamId: string;
tmbId: string;
appId: string;
appVersionId?: string;
createTime: Date;
updateTime: Date;
title: string;
@ -45,14 +44,6 @@ export type ChatSchemaType = {
variables: Record<string, any>;
pluginInputs?: FlowNodeInputItemType[];
metadata?: Record<string, any>;
// Boolean flags for efficient filtering
hasGoodFeedback?: boolean;
hasBadFeedback?: boolean;
hasUnreadGoodFeedback?: boolean;
hasUnreadBadFeedback?: boolean;
deleteTime?: Date | null;
};
export type ChatWithAppSchema = Omit<ChatSchemaType, 'appId'> & {
@ -114,7 +105,6 @@ export type AIChatItemType = {
userBadFeedback?: string;
customFeedbacks?: string[];
adminFeedback?: AdminFbkType;
isFeedbackRead?: boolean;
durationSeconds?: number;
errorMsg?: string;
@ -162,7 +152,6 @@ export type ChatItemType = ChatItemMergeType & {
// Frontend type
export type ChatSiteItemType = ChatItemMergeType & {
_id?: string;
id: string;
dataId: string;
status: `${ChatStatusEnum}`;
moduleName?: string;
@ -199,7 +188,7 @@ export type HistoryItemType = {
};
export type ChatHistoryItemType = HistoryItemType & {
appId: string;
top?: boolean;
top: boolean;
};
/* ------- response data ------------ */

View File

@ -59,6 +59,7 @@ export const getHistoryPreview = (
return `![Input an image](${item.file.url.slice(0, 100)}...)`;
return '';
})
.filter(Boolean)
.join('\n') || ''
);
} else if (item.obj === ChatRoleEnum.AI) {
@ -69,8 +70,7 @@ export const getHistoryPreview = (
item.text?.content || item?.tools?.map((item) => item.toolName).join(',') || ''
);
})
.join('')
.trim() || ''
.join('') || ''
);
}
return '';

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