Compare commits

..

5 Commits

Author SHA1 Message Date
Archer
e2ae571d15 4.8.6 fix (#1970)
* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import

* feat: mongo log store

* doc

* fix: sandbox outputs

* perf: desc color

* fix: node init

* perf code

* perf: chat header
2024-07-10 15:52:39 +08:00
Zong
f548e24e7d fix(env): typo template (#1990) 2024-07-08 15:05:23 +08:00
Archer
5605f1a892 4.8.6 fix (#1963)
* feat: log store

* fix: full text search match query

* perf: mongo schema import, Avoid duplicate import
2024-07-05 17:37:42 +08:00
Archer
88d10451c9 perf: collection created response (#1947)
* perf: collection created response

* update openapi doc

* remove default collection

* perf: chat ui

* fix: system prompt concat

* perf: published check

* perf: update app
2024-07-05 10:27:38 +08:00
Zong
8a7bd689ae fix(prompt): typo extra content (#1942)
* fix(prompt): typo extra content

* refactor(prompt): AIChat template
2024-07-04 19:08:26 +08:00
81 changed files with 563 additions and 465 deletions

View File

@@ -1,5 +0,0 @@
{
"recommendations": [
"inlang.vs-code-extension"
]
}

View File

@@ -2,7 +2,7 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.mouseWheelZoom": true, "editor.mouseWheelZoom": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.prettierPath": "../node_modules/prettier", "prettier.prettierPath": "node_modules/prettier",
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": [ "i18n-ally.localesPaths": [
"packages/web/i18n", "packages/web/i18n",

View File

@@ -13,7 +13,7 @@ weight: 853
## 创建训练订单(4.6.9地址发生改动) ## 创建训练订单
{{< tabs tabTotal="2" >}} {{< tabs tabTotal="2" >}}
{{< tab tabName="请求示例" >}} {{< tab tabName="请求示例" >}}
@@ -26,6 +26,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/
--header 'Authorization: Bearer {{apikey}}' \ --header 'Authorization: Bearer {{apikey}}' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--data-raw '{ --data-raw '{
"datasetId": "知识库 ID",
"name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx" "name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx"
}' }'
``` ```

View File

@@ -13,9 +13,9 @@ weight: 818
### 2. 修改镜像 ### 2. 修改镜像
- fastgpt 镜像 tag 修改成 v4.8.6-alpha - fastgpt 镜像 tag 修改成 v4.8.6-alpha2
- fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha - fastgpt-sandbox 镜像 tag 修改成 v4.8.6-alpha2
- 商业版镜像 tag 修改成 v4.8.6-alpha - 商业版镜像 tag 修改成 v4.8.6-alpha2
### 3. 执行初始化 ### 3. 执行初始化
@@ -38,5 +38,8 @@ curl --location --request POST 'https://{{host}}/api/admin/initv486' \
3. 新增 - 网页抓取和数学计算器系统插件 3. 新增 - 网页抓取和数学计算器系统插件
4. 新增 - 移动文本加工和自定义反馈到基础节点中 4. 新增 - 移动文本加工和自定义反馈到基础节点中
5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力 5. 优化 - Read file 默认选中从节点,实现 MongoDB 读写分离,减轻主节点压力
6. 修复 - 工作流中团队插件加载异常 6. 优化 - 知识库导入接口,返回值对齐
7. 修复 - 知识库集合目录导航失效 7. 优化 - Mongo model 重复加载
8. 修复 - 工作流中团队插件加载异常
9. 修复 - 知识库集合目录导航失效
10. 修复 - 通过 API 调用 chat 接口,传递 System 异常

View File

@@ -156,6 +156,7 @@ services:
- SANDBOX_URL=http://sandbox:3000 - SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error # 日志等级: debug, info, warn, error
- LOG_LEVEL=info - LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

View File

@@ -113,6 +113,7 @@ services:
- SANDBOX_URL=http://sandbox:3000 - SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error # 日志等级: debug, info, warn, error
- LOG_LEVEL=info - LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

View File

@@ -94,6 +94,7 @@ services:
- SANDBOX_URL=http://sandbox:3000 - SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error # 日志等级: debug, info, warn, error
- LOG_LEVEL=info - LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

View File

@@ -100,7 +100,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
<QA> <QA>
{{quote}} {{quote}}
</QA>} </QA>
思考流程: 思考流程:
1. 判断问题是否与 <QA></QA> 标记中的内容有关。 1. 判断问题是否与 <QA></QA> 标记中的内容有关。
@@ -109,7 +109,12 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
4. 如果有相同的问题,直接输出对应答案。 4. 如果有相同的问题,直接输出对应答案。
5. 如果只有相近的问题,请把相近的问题和答案一起输出。 5. 如果只有相近的问题,请把相近的问题和答案一起输出。
最后,避免提及你是从 QA 获取的知识,只需要回复答案。 回答要求:
- 如果没有相关的问答对,你需要澄清。
- 回答的内容应尽可能与 <QA></QA> 标记中的内容一致。
- 避免提及你是从 QA 获取的知识,只需要回复答案。
- 使用 Markdown 语法优化回答格式。
- 使用与问题相同的语言回答。
问题:"""{{question}}"""` 问题:"""{{question}}"""`
} }

View File

@@ -3,6 +3,17 @@ import { FlowNodeTypeEnum } from '../workflow/node/constant';
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
import { ChatHistoryItemResType, ChatItemType, UserChatItemValueItemType } from './type.d'; import { ChatHistoryItemResType, ChatItemType, UserChatItemValueItemType } from './type.d';
// Concat 2 -> 1, and sort by role
export const concatHistories = (histories1: ChatItemType[], histories2: ChatItemType[]) => {
const newHistories = [...histories1, ...histories2];
return newHistories.sort((a, b) => {
if (a.obj === ChatRoleEnum.System) {
return -1;
}
return 1;
});
};
export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => { export const getChatTitleFromChatMessage = (message?: ChatItemType, defaultValue = '新对话') => {
// @ts-ignore // @ts-ignore
const textMsg = message?.value.find((item) => item.type === ChatItemValueTypeEnum.text); const textMsg = message?.value.find((item) => item.type === ChatItemValueTypeEnum.text);

View File

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

View File

@@ -82,12 +82,7 @@ export const HttpNode468: FlowNodeTemplateType = {
], ],
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
}
}, },
{ {
id: NodeOutputKeyEnum.error, id: NodeOutputKeyEnum.error,

View File

@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
showDefaultValue: true showDefaultValue: true
} }
}, },
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data1',
label: 'data1',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{
renderTypeList: [FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: 'data2',
label: 'data2',
customInputConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
},
required: true
},
{ {
key: NodeInputKeyEnum.codeType, key: NodeInputKeyEnum.codeType,
renderTypeList: [FlowNodeInputTypeEnum.hidden], renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
outputs: [ outputs: [
{ {
...Output_Template_AddOutput, ...Output_Template_AddOutput,
description: '将代码中 return 的对象作为输出,传递给后续的节点' description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
}, },
{ {
id: NodeOutputKeyEnum.rawResponse, id: NodeOutputKeyEnum.rawResponse,

View File

@@ -1,5 +1,5 @@
{ {
"extends":"../../tsconfig.json", "extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": "." "baseUrl": "."
}, },

View File

@@ -88,7 +88,7 @@
"x": 1050.9890727421412, "x": 1050.9890727421412,
"y": -415.2085119990912 "y": -415.2085119990912
}, },
"version": "486", "version": "481",
"inputs": [ "inputs": [
{ {
"key": "system_addInputParam", "key": "system_addInputParam",

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo'; import { connectionMongo, getMongoModel, type Model } from '../../mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { RawTextBufferSchemaType } from './type'; import { RawTextBufferSchemaType } from './type';
@@ -28,6 +28,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoRawTextBuffer: Model<RawTextBufferSchemaType> = export const MongoRawTextBuffer = getMongoModel<RawTextBufferSchemaType>(
models[collectionName] || model(collectionName, RawTextBufferSchema); collectionName,
MongoRawTextBuffer.syncIndexes(); RawTextBufferSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TTSBufferSchemaType } from './type.d'; import { TTSBufferSchemaType } from './type.d';
@@ -31,6 +31,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoTTSBuffer: Model<TTSBufferSchemaType> = export const MongoTTSBuffer = getMongoModel<TTSBufferSchemaType>(collectionName, TTSBufferSchema);
models[collectionName] || model(collectionName, TTSBufferSchema);
MongoTTSBuffer.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo'; import { connectionMongo, getMongoModel, type Model } from '../../mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
const FileSchema = new Schema({}); const FileSchema = new Schema({});
@@ -10,6 +10,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoFileSchema = models['dataset.files'] || model('dataset.files', FileSchema); export const MongoFileSchema = getMongoModel('dataset.files', FileSchema);
MongoFileSchema.syncIndexes();

View File

@@ -1,5 +1,5 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { connectionMongo, type Model } from '../../mongo'; import { connectionMongo, getMongoModel, type Model } from '../../mongo';
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d'; import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants'; import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
@@ -41,7 +41,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoImage: Model<MongoImageSchemaType> = export const MongoImage = getMongoModel<MongoImageSchemaType>('image', ImageSchema);
models['image'] || model('image', ImageSchema);
MongoImage.syncIndexes();

View File

@@ -1,4 +1,5 @@
import mongoose from 'mongoose'; import { addLog } from '../../common/system/log';
import mongoose, { Model } from 'mongoose';
export default mongoose; export default mongoose;
export * from 'mongoose'; export * from 'mongoose';
@@ -11,4 +12,65 @@ export const connectionMongo = (() => {
return global.mongodb; return global.mongodb;
})(); })();
export const ReadPreference = mongoose.mongo.ReadPreference; const addCommonMiddleware = (schema: mongoose.Schema) => {
const operations = [
/^find/,
'save',
'create',
/^update/,
/^delete/,
'aggregate',
'count',
'countDocuments',
'estimatedDocumentCount',
'distinct',
'insertMany'
];
operations.forEach((op: any) => {
schema.pre(op, function (this: any, next) {
this._startTime = Date.now();
this._query = this.getQuery ? this.getQuery() : null;
next();
});
schema.post(op, function (this: any, result: any, next) {
if (this._startTime) {
const duration = Date.now() - this._startTime;
const warnLogData = {
query: this._query,
op,
duration
};
if (duration > 1000) {
addLog.warn(`Slow operation ${duration}ms`, warnLogData);
} else if (duration > 300) {
addLog.error(`Slow operation ${duration}ms`, warnLogData);
}
}
next();
});
});
return schema;
};
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
console.log('Load model======', name);
addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema);
try {
model.syncIndexes();
} catch (error) {
addLog.error('Create index error', error);
}
return model;
};
export const ReadPreference = connectionMongo.mongo.ReadPreference;

View File

@@ -1,5 +1,5 @@
import { SystemConfigsType } from '@fastgpt/global/common/system/config/type'; import { SystemConfigsType } from '@fastgpt/global/common/system/config/type';
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
import { SystemConfigsTypeMap } from '@fastgpt/global/common/system/config/constants'; import { SystemConfigsTypeMap } from '@fastgpt/global/common/system/config/constants';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
@@ -27,6 +27,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoSystemConfigs: Model<SystemConfigsType> = export const MongoSystemConfigs = getMongoModel<SystemConfigsType>(
models[collectionName] || model(collectionName, systemConfigSchema); collectionName,
MongoSystemConfigs.syncIndexes(); systemConfigSchema
);

View File

@@ -1,13 +1,9 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import chalk from 'chalk'; import chalk from 'chalk';
import { isProduction } from './constants'; import { LogLevelEnum } from './log/constant';
import { connectionMongo } from '../mongo/index';
import { getMongoLog } from './log/schema';
enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
const logMap = { const logMap = {
[LogLevelEnum.debug]: { [LogLevelEnum.debug]: {
levelLog: chalk.green('[Debug]') levelLog: chalk.green('[Debug]')
@@ -23,23 +19,26 @@ const logMap = {
} }
}; };
const envLogLevelMap: Record<string, number> = { const envLogLevelMap: Record<string, number> = {
debug: 0, debug: LogLevelEnum.debug,
info: 1, info: LogLevelEnum.info,
warn: 2, warn: LogLevelEnum.warn,
error: 3 error: LogLevelEnum.error
}; };
const logLevel = (() => { const { LOG_LEVEL, STORE_LOG_LEVEL } = (() => {
if (!isProduction) return LogLevelEnum.debug; const LOG_LEVEL = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase();
const envLogLevel = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase(); const STORE_LOG_LEVEL = (process.env.STORE_LOG_LEVEL || '').toLocaleLowerCase();
if (!envLogLevel || envLogLevelMap[envLogLevel] === undefined) return LogLevelEnum.info;
return envLogLevelMap[envLogLevel]; return {
LOG_LEVEL: envLogLevelMap[LOG_LEVEL] || LogLevelEnum.info,
STORE_LOG_LEVEL: envLogLevelMap[STORE_LOG_LEVEL] ?? 99
};
})(); })();
/* add logger */ /* add logger */
export const addLog = { export const addLog = {
log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) { log(level: LogLevelEnum, msg: string, obj: Record<string, any> = {}) {
if (level < logLevel) return; if (level < LOG_LEVEL) return;
const stringifyObj = JSON.stringify(obj); const stringifyObj = JSON.stringify(obj);
const isEmpty = Object.keys(obj).length === 0; const isEmpty = Object.keys(obj).length === 0;
@@ -52,35 +51,15 @@ export const addLog = {
level === LogLevelEnum.error && console.error(obj); level === LogLevelEnum.error && console.error(obj);
const lokiUrl = process.env.LOKI_LOG_URL as string; // store
if (!lokiUrl) return; if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log
try { getMongoLog().create({
fetch(lokiUrl, { text: msg,
method: 'POST', level,
headers: { metadata: obj
'Content-type': 'application/json'
},
body: JSON.stringify({
streams: [
{
stream: {
level
},
values: [
[
`${Date.now() * 1000000}`,
JSON.stringify({
message: msg,
...obj
})
]
]
}
]
})
}); });
} catch (error) {} }
}, },
debug(msg: string, obj?: Record<string, any>) { debug(msg: string, obj?: Record<string, any>) {
this.log(LogLevelEnum.debug, msg, obj); this.log(LogLevelEnum.debug, msg, obj);

View File

@@ -0,0 +1,10 @@
export enum LogLevelEnum {
debug = 0,
info = 1,
warn = 2,
error = 3
}
export enum LogSignEnum {
slowOperation = 'slowOperation'
}

View File

@@ -0,0 +1,29 @@
import { getMongoModel, Schema } from '../../../common/mongo';
import { SystemLogType } from './type';
import { LogLevelEnum } from './constant';
export const LogCollectionName = 'system_logs';
export const getMongoLog = () => {
const SystemLogSchema = new Schema({
text: {
type: String,
required: true
},
level: {
type: String,
required: true,
enum: Object.values(LogLevelEnum)
},
time: {
type: Date,
default: () => new Date()
},
metadata: Object
});
SystemLogSchema.index({ time: 1 }, { expires: '15d' });
SystemLogSchema.index({ level: 1 });
return getMongoModel<SystemLogType>(LogCollectionName, SystemLogSchema);
};

View File

@@ -0,0 +1,9 @@
import { LogLevelEnum, LogSignEnum } from './constant';
export type SystemLogType = {
_id: string;
text: string;
level: LogLevelEnum;
time: Date;
metadata?: Record<string, any>;
};

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../mongo'; import { connectionMongo, getMongoModel, type Model } from '../../mongo';
import { timerIdMap } from './constants'; import { timerIdMap } from './constants';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TimerLockSchemaType } from './type.d'; import { TimerLockSchemaType } from './type.d';
@@ -24,6 +24,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoTimerLock: Model<TimerLockSchemaType> = export const MongoTimerLock = getMongoModel<TimerLockSchemaType>(collectionName, TimerLockSchema);
models[collectionName] || model(collectionName, TimerLockSchema);
MongoTimerLock.syncIndexes();

View File

@@ -1,6 +1,5 @@
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { connectionMongo, type Model } from '../../common/mongo'; import { Schema, getMongoModel } from '../../common/mongo';
const { Schema, model, models } = connectionMongo;
import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
import { import {
TeamCollectionName, TeamCollectionName,
@@ -21,6 +20,7 @@ export const chatConfigType = {
chatInputGuide: Object chatInputGuide: Object
}; };
// schema
const AppSchema = new Schema({ const AppSchema = new Schema({
parentId: { parentId: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
@@ -112,15 +112,8 @@ const AppSchema = new Schema({
...getPermissionSchema(AppDefaultPermissionVal) ...getPermissionSchema(AppDefaultPermissionVal)
}); });
try { AppSchema.index({ updateTime: -1 });
AppSchema.index({ updateTime: -1 }); AppSchema.index({ teamId: 1, type: 1 });
AppSchema.index({ teamId: 1, type: 1 }); AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
AppSchema.index({ scheduledTriggerConfig: 1, intervalNextTime: -1 });
} catch (error) {
console.log(error);
}
export const MongoApp: Model<AppType> = export const MongoApp = getMongoModel<AppType>(AppCollectionName, AppSchema);
models[AppCollectionName] || model(AppCollectionName, AppSchema);
MongoApp.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { chatConfigType } from '../schema'; import { chatConfigType } from '../schema';
@@ -34,7 +34,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoAppVersion: Model<AppVersionSchemaType> = export const MongoAppVersion = getMongoModel<AppVersionSchemaType>(
models[AppVersionCollectionName] || model(AppVersionCollectionName, AppVersionSchema); AppVersionCollectionName,
AppVersionSchema
MongoAppVersion.syncIndexes(); );

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { ChatItemSchema as ChatItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemSchema as ChatItemType } from '@fastgpt/global/core/chat/type';
import { ChatRoleMap } from '@fastgpt/global/core/chat/constants'; import { ChatRoleMap } from '@fastgpt/global/core/chat/constants';
@@ -9,7 +9,6 @@ import {
} from '@fastgpt/global/support/user/team/constant'; } from '@fastgpt/global/support/user/team/constant';
import { AppCollectionName } from '../app/schema'; import { AppCollectionName } from '../app/schema';
import { userCollectionName } from '../../support/user/schema'; import { userCollectionName } from '../../support/user/schema';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
export const ChatItemCollectionName = 'chatitems'; export const ChatItemCollectionName = 'chatitems';
@@ -99,7 +98,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoChatItem: Model<ChatItemType> = export const MongoChatItem = getMongoModel<ChatItemType>(ChatItemCollectionName, ChatItemSchema);
models[ChatItemCollectionName] || model(ChatItemCollectionName, ChatItemSchema);
MongoChatItem.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d'; import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
import { ChatSourceMap } from '@fastgpt/global/core/chat/constants'; import { ChatSourceMap } from '@fastgpt/global/core/chat/constants';
@@ -98,6 +98,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoChat: Model<ChatType> = export const MongoChat = getMongoModel<ChatType>(chatCollectionName, ChatSchema);
models[chatCollectionName] || model(chatCollectionName, ChatSchema);
MongoChat.syncIndexes();

View File

@@ -13,24 +13,24 @@ export async function getChatItems({
chatId?: string; chatId?: string;
limit?: number; limit?: number;
field: string; field: string;
}): Promise<{ history: ChatItemType[] }> { }): Promise<{ histories: ChatItemType[] }> {
if (!chatId) { if (!chatId) {
return { history: [] }; return { histories: [] };
} }
const history = await MongoChatItem.find({ appId, chatId }, field) const histories = await MongoChatItem.find({ appId, chatId }, field)
.sort({ _id: -1 }) .sort({ _id: -1 })
.limit(limit) .limit(limit)
.lean(); .lean();
history.reverse(); histories.reverse();
history.forEach((item) => { histories.forEach((item) => {
// @ts-ignore // @ts-ignore
item.value = adaptStringValue(item.value); item.value = adaptStringValue(item.value);
}); });
return { history }; return { histories };
} }
/* 临时适配旧的对话记录 */ /* 临时适配旧的对话记录 */
export const adaptStringValue = (value: any): ChatItemValueItemType[] => { export const adaptStringValue = (value: any): ChatItemValueItemType[] => {

View File

@@ -1,5 +1,5 @@
import { AppCollectionName } from '../../app/schema'; import { AppCollectionName } from '../../app/schema';
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import type { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type.d'; import type { ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type.d';
@@ -23,7 +23,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoChatInputGuide: Model<ChatInputGuideSchemaType> = export const MongoChatInputGuide = getMongoModel<ChatInputGuideSchemaType>(
models[ChatInputGuideCollectionName] || model(ChatInputGuideCollectionName, ChatInputGuideSchema); ChatInputGuideCollectionName,
ChatInputGuideSchema
MongoChatInputGuide.syncIndexes(); );

View File

@@ -75,54 +75,9 @@ export async function createOneCollection({
{ session } { session }
); );
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: collection._id,
teamId,
tmbId,
session
});
}
return collection; return collection;
} }
// create default collection
export function createDefaultCollection({
name = '手动录入',
datasetId,
parentId,
teamId,
tmbId,
session
}: {
name?: '手动录入' | '手动标注';
datasetId: string;
parentId?: string;
teamId: string;
tmbId: string;
session?: ClientSession;
}) {
return MongoDatasetCollection.create(
[
{
name,
teamId,
tmbId,
datasetId,
parentId,
type: DatasetCollectionTypeEnum.virtual,
trainingType: TrainingModeEnum.chunk,
chunkSize: 0,
updateTime: new Date('2099')
}
],
{ session }
);
}
/* delete collection related images/files */ /* delete collection related images/files */
export const delCollectionRelatedSource = async ({ export const delCollectionRelatedSource = async ({
collections, collections,

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d'; import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { TrainingTypeMap, DatasetCollectionTypeMap } from '@fastgpt/global/core/dataset/constants'; import { TrainingTypeMap, DatasetCollectionTypeMap } from '@fastgpt/global/core/dataset/constants';
@@ -94,9 +94,6 @@ const DatasetCollectionSchema = new Schema({
} }
}); });
export const MongoDatasetCollection: Model<DatasetCollectionSchemaType> =
models[DatasetColCollectionName] || model(DatasetColCollectionName, DatasetCollectionSchema);
try { try {
// auth file // auth file
DatasetCollectionSchema.index({ teamId: 1, fileId: 1 }); DatasetCollectionSchema.index({ teamId: 1, fileId: 1 });
@@ -111,8 +108,11 @@ try {
// get forbid // get forbid
// DatasetCollectionSchema.index({ teamId: 1, datasetId: 1, forbid: 1 }); // DatasetCollectionSchema.index({ teamId: 1, datasetId: 1, forbid: 1 });
MongoDatasetCollection.syncIndexes({ background: true });
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
export const MongoDatasetCollection = getMongoModel<DatasetCollectionSchemaType>(
DatasetColCollectionName,
DatasetCollectionSchema
);

View File

@@ -10,6 +10,7 @@ import {
} from '@fastgpt/global/core/dataset/constants'; } from '@fastgpt/global/core/dataset/constants';
import { hashStr } from '@fastgpt/global/common/string/tools'; import { hashStr } from '@fastgpt/global/common/string/tools';
import { ClientSession } from '../../../common/mongo'; import { ClientSession } from '../../../common/mongo';
import { PushDatasetDataResponse } from '@fastgpt/global/core/dataset/api';
/** /**
* get all collection by top collectionId * get all collection by top collectionId
@@ -138,7 +139,7 @@ export const reloadCollectionChunks = async ({
billId?: string; billId?: string;
rawText?: string; rawText?: string;
session: ClientSession; session: ClientSession;
}) => { }): Promise<PushDatasetDataResponse> => {
const { const {
title, title,
rawText: newRawText, rawText: newRawText,
@@ -149,7 +150,10 @@ export const reloadCollectionChunks = async ({
newRawText: rawText newRawText: rawText
}); });
if (isSameRawText) return; if (isSameRawText)
return {
insertLen: 0
};
// split data // split data
const { chunks } = splitText2Chunks({ const { chunks } = splitText2Chunks({
@@ -164,7 +168,7 @@ export const reloadCollectionChunks = async ({
return Promise.reject('Training model error'); return Promise.reject('Training model error');
})(); })();
await MongoDatasetTraining.insertMany( const result = await MongoDatasetTraining.insertMany(
chunks.map((item, i) => ({ chunks.map((item, i) => ({
teamId: col.teamId, teamId: col.teamId,
tmbId, tmbId,
@@ -191,4 +195,8 @@ export const reloadCollectionChunks = async ({
}, },
{ session } { session }
); );
return {
insertLen: result.length
};
}; };

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d'; import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { import {
@@ -77,27 +77,23 @@ const DatasetDataSchema = new Schema({
rebuilding: Boolean rebuilding: Boolean
}); });
export const MongoDatasetData: Model<DatasetDataSchemaType> = // list collection and count data; list data; delete collection(relate data)
models[DatasetDataCollectionName] || model(DatasetDataCollectionName, DatasetDataSchema); DatasetDataSchema.index({
teamId: 1,
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
try { export const MongoDatasetData = getMongoModel<DatasetDataSchemaType>(
// list collection and count data; list data; delete collection(relate data) DatasetDataCollectionName,
DatasetDataSchema.index({ DatasetDataSchema
teamId: 1, );
datasetId: 1,
collectionId: 1,
chunkIndex: 1,
updateTime: -1
});
// full text index
DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
MongoDatasetData.syncIndexes({ background: true });
} catch (error) {
console.log(error);
}

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d'; import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
import { import {
@@ -11,7 +11,6 @@ import {
TeamCollectionName, TeamCollectionName,
TeamMemberCollectionName TeamMemberCollectionName
} from '@fastgpt/global/support/user/team/constant'; } from '@fastgpt/global/support/user/team/constant';
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant'; import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
export const DatasetCollectionName = 'datasets'; export const DatasetCollectionName = 'datasets';
@@ -99,6 +98,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoDataset: Model<DatasetSchemaType> = export const MongoDataset = getMongoModel<DatasetSchemaType>(DatasetCollectionName, DatasetSchema);
models[DatasetCollectionName] || model(DatasetCollectionName, DatasetSchema);
MongoDataset.syncIndexes();

View File

@@ -212,7 +212,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
{ {
$match: { $match: {
$expr: { $eq: ['$_id', '$$collectionId'] }, $expr: { $eq: ['$_id', '$$collectionId'] },
forbid: { $eq: false } // 直接在lookup阶段过滤 forbid: { $eq: true } // 匹配被禁用的数据
} }
}, },
{ {
@@ -226,7 +226,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
}, },
{ {
$match: { $match: {
collection: { $ne: [] } collection: { $eq: [] } // 没有 forbid=true 的数据
} }
}, },
{ {

View File

@@ -1,5 +1,5 @@
/* 模型的知识库 */ /* 模型的知识库 */
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type'; import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
@@ -103,7 +103,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoDatasetTraining: Model<DatasetTrainingSchemaType> = export const MongoDatasetTraining = getMongoModel<DatasetTrainingSchemaType>(
models[DatasetTrainingCollectionName] || model(DatasetTrainingCollectionName, TrainingDataSchema); DatasetTrainingCollectionName,
TrainingDataSchema
MongoDatasetTraining.syncIndexes(); );

View File

@@ -198,7 +198,7 @@ ${description ? `- ${description}` : ''}
required: [] required: []
} }
}; };
console.log(properties);
return { return {
filterMessages, filterMessages,
agentFunction agentFunction

View File

@@ -1,12 +1,11 @@
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { replaceSensitiveText } from '@fastgpt/global/common/string/tools'; import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { import {
WorkflowIOValueTypeEnum, WorkflowIOValueTypeEnum,
NodeOutputKeyEnum NodeOutputKeyEnum
} from '@fastgpt/global/core/workflow/constants'; } from '@fastgpt/global/core/workflow/constants';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type'; import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
export const filterToolNodeIdByEdges = ({ export const filterToolNodeIdByEdges = ({
nodeId, nodeId,
@@ -45,10 +44,16 @@ export const filterToolNodeIdByEdges = ({
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => { export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
if (!history) return []; if (!history) return [];
if (typeof history === 'number') return histories.slice(-(history * 2));
if (Array.isArray(history)) return history;
return []; const systemHistories = histories.filter((item) => item.obj === ChatRoleEnum.System);
const filterHistories = (() => {
if (typeof history === 'number') return histories.slice(-(history * 2));
if (Array.isArray(history)) return history;
return [];
})();
return [...systemHistories, ...filterHistories];
}; };
/* value type format */ /* value type format */

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { PromotionRecordSchema as PromotionRecordType } from '@fastgpt/global/support/activity/type.d'; import { PromotionRecordSchema as PromotionRecordType } from '@fastgpt/global/support/activity/type.d';
@@ -29,6 +29,7 @@ const PromotionRecordSchema = new Schema({
} }
}); });
export const MongoPromotionRecord: Model<PromotionRecordType> = export const MongoPromotionRecord = getMongoModel<PromotionRecordType>(
models['promotionRecord'] || model('promotionRecord', PromotionRecordSchema); 'promotionRecord',
MongoPromotionRecord.syncIndexes(); PromotionRecordSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type'; import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
import { import {
@@ -64,6 +64,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoOpenApi: Model<OpenApiSchema> = export const MongoOpenApi = getMongoModel<OpenApiSchema>('openapi', OpenApiSchema);
models['openapi'] || model('openapi', OpenApiSchema);
MongoOpenApi.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type'; import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type';
import { import {
@@ -90,7 +90,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoOutLink: Model<SchemaType> = export const MongoOutLink = getMongoModel<SchemaType>('outlinks', OutLinkSchema);
models['outlinks'] || model('outlinks', OutLinkSchema);
MongoOutLink.syncIndexes();

View File

@@ -2,7 +2,7 @@ import {
TeamCollectionName, TeamCollectionName,
TeamMemberCollectionName TeamMemberCollectionName
} from '@fastgpt/global/support/user/team/constant'; } from '@fastgpt/global/support/user/team/constant';
import { Model, connectionMongo } from '../../common/mongo'; import { Model, connectionMongo, getMongoModel } from '../../common/mongo';
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type'; import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
@@ -54,8 +54,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoResourcePermission: Model<ResourcePermissionType> = export const MongoResourcePermission = getMongoModel<ResourcePermissionType>(
models[ResourcePermissionCollectionName] || ResourcePermissionCollectionName,
model(ResourcePermissionCollectionName, ResourcePermissionSchema); ResourcePermissionSchema
);
MongoResourcePermission.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { hashStr } from '@fastgpt/global/common/string/tools'; import { hashStr } from '@fastgpt/global/common/string/tools';
import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import type { UserModelSchema } from '@fastgpt/global/support/user/type';
@@ -74,6 +74,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoUser: Model<UserModelSchema> = export const MongoUser = getMongoModel<UserModelSchema>(userCollectionName, UserSchema);
models[userCollectionName] || model(userCollectionName, UserSchema);
MongoUser.syncIndexes();

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d'; import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema'; import { userCollectionName } from '../../user/schema';
@@ -49,5 +49,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoTeamMember: Model<TeamMemberType> = export const MongoTeamMember = getMongoModel<TeamMemberType>(
models[TeamMemberCollectionName] || model(TeamMemberCollectionName, TeamMemberSchema); TeamMemberCollectionName,
TeamMemberSchema
);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d'; import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema'; import { userCollectionName } from '../../user/schema';
@@ -61,5 +61,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoTeam: Model<TeamType> = export const MongoTeam = getMongoModel<TeamType>(TeamCollectionName, TeamSchema);
models[TeamCollectionName] || model(TeamCollectionName, TeamSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d'; import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
import { import {
@@ -32,5 +32,7 @@ try {
console.log(error); console.log(error);
} }
export const MongoTeamTags: Model<TeamTagsSchemaType> = export const MongoTeamTags = getMongoModel<TeamTagsSchemaType>(
models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagSchema); TeamTagsCollectionName,
TeamTagSchema
);

View File

@@ -3,7 +3,7 @@
1. type=standard: There will only be 1, and each team will have one 1. type=standard: There will only be 1, and each team will have one
2. type=extraDatasetSize/extraPoints: Can buy multiple 2. type=extraDatasetSize/extraPoints: Can buy multiple
*/ */
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { import {
@@ -93,5 +93,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoTeamSub: Model<TeamSubSchema> = export const MongoTeamSub = getMongoModel<TeamSubSchema>(subCollectionName, SubSchema);
models[subCollectionName] || model(subCollectionName, SubSchema);

View File

@@ -1,4 +1,4 @@
import { connectionMongo, type Model } from '../../../common/mongo'; import { connectionMongo, getMongoModel, type Model } from '../../../common/mongo';
const { Schema, model, models } = connectionMongo; const { Schema, model, models } = connectionMongo;
import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type'; import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
@@ -70,6 +70,4 @@ try {
console.log(error); console.log(error);
} }
export const MongoUsage: Model<UsageSchemaType> = export const MongoUsage = getMongoModel<UsageSchemaType>(UsageCollectionName, UsageSchema);
models[UsageCollectionName] || model(UsageCollectionName, UsageSchema);
MongoUsage.syncIndexes();

View File

@@ -57,7 +57,7 @@
}, },
"module": { "module": {
"Combine Modules": "Combine Modules", "Combine Modules": "Combine Modules",
"Confirm Sync": "Using the latest template will overwrite the existing one and may result in the loss of some previous configuration information. Please confirm.", "Confirm Sync": "The template will be updated to the latest template configuration. Fields that do not exist in the template will be deleted (including all custom fields). You are advised to make a copy of the node and then update the original node version.",
"Custom Title Tip": "This title will be displayed during the conversation", "Custom Title Tip": "This title will be displayed during the conversation",
"My Modules": "My Modules", "My Modules": "My Modules",
"No Modules": "No plugins yet~", "No Modules": "No plugins yet~",

View File

@@ -1068,7 +1068,7 @@
"Debug": "Debug", "Debug": "Debug",
"Debug Node": "Debug mode", "Debug Node": "Debug mode",
"Failed": "Execution failed", "Failed": "Execution failed",
"Not intro": "This node has no introduction~\\", "Not intro": "This node has no introduction~",
"Run from here": "Run from here", "Run from here": "Run from here",
"Run result": "Run result", "Run result": "Run result",
"Running": "Running", "Running": "Running",

View File

@@ -16,7 +16,7 @@
"Tool input": "Tool", "Tool input": "Tool",
"code": { "code": {
"Reset template": "Reset template", "Reset template": "Reset template",
"Reset template confirm": "Are you sure to restore the code template? Be careful to save the current code." "Reset template confirm": "Are you sure to restore the code template? All input and output to template values will be reset, please be careful to save the current code."
}, },
"ifelse": { "ifelse": {
"Input value": "Input", "Input value": "Input",

View File

@@ -56,7 +56,7 @@
}, },
"module": { "module": {
"Combine Modules": "组合模块", "Combine Modules": "组合模块",
"Confirm Sync": "将会使用最新模板进行覆盖,可能会丢失一些旧的配置信息,请确认", "Confirm Sync": "将会更新至最新模板配置,不存在模板中的字段将会被删除(包括所有自定义字段),建议您先复制一份节点,再更新原来节点的版本。",
"Custom Title Tip": "该标题名字会展示在对话过程中", "Custom Title Tip": "该标题名字会展示在对话过程中",
"My Modules": "", "My Modules": "",
"No Modules": "没找到插件", "No Modules": "没找到插件",

View File

@@ -1077,7 +1077,7 @@
"Debug": "调试", "Debug": "调试",
"Debug Node": "Debug 模式", "Debug Node": "Debug 模式",
"Failed": "运行失败", "Failed": "运行失败",
"Not intro": "这个节点没有介绍~\\", "Not intro": "这个节点没有介绍~",
"Run from here": "从这里开始运行", "Run from here": "从这里开始运行",
"Run result": "", "Run result": "",
"Running": "运行中", "Running": "运行中",

View File

@@ -16,7 +16,7 @@
"Tool input": "工具参数", "Tool input": "工具参数",
"code": { "code": {
"Reset template": "还原模板", "Reset template": "还原模板",
"Reset template confirm": "确认还原代码模板?请注意保存当前代码。" "Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。"
}, },
"ifelse": { "ifelse": {
"Input value": "输入值", "Input value": "输入值",

View File

@@ -22,7 +22,7 @@ MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
# 向量库优先级: pg > milvus # 向量库优先级: pg > milvus
# PG 向量库连接参数 # PG 向量库连接参数
PG_URL=postgresql://username:password@host:port/postgres PG_URL=postgresql://username:password@host:port/postgres
# mivlus 向量库连接参数 # milvus 向量库连接参数
MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com
MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d
@@ -33,6 +33,6 @@ PRO_URL=
# 首页路径 # 首页路径
HOME_URL=/ HOME_URL=/
# 日志等级: debug, info, warn, error # 日志等级: debug, info, warn, error
LOG_LEVEL=info LOG_LEVEL=debug
STORE_LOG_LEVEL=warn
# Loki Log Path # Loki Log Path
# LOKI_LOG_URL=

View File

@@ -41,7 +41,6 @@ export const useChatBox = () => {
const map: Record<ExportChatType, () => void> = { const map: Record<ExportChatType, () => void> = {
md: () => { md: () => {
console.log(history);
fileDownload({ fileDownload({
text: history text: history
.map((item) => { .map((item) => {

View File

@@ -155,7 +155,7 @@ const SelectOneResource = ({
return loading ? ( return loading ? (
<Loading fixed={false} /> <Loading fixed={false} />
) : ( ) : (
<Box maxH={maxH} overflow={'auto'}> <Box maxH={maxH} h={'100%'} overflow={'auto'}>
<Render list={concatRoot} /> <Render list={concatRoot} />
</Box> </Box>
); );

View File

@@ -1,4 +1,7 @@
import { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api'; import {
PushDatasetDataChunkProps,
PushDatasetDataResponse
} from '@fastgpt/global/core/dataset/api';
import { import {
DatasetSearchModeEnum, DatasetSearchModeEnum,
DatasetSourceReadTypeEnum, DatasetSourceReadTypeEnum,
@@ -31,6 +34,10 @@ export type RebuildEmbeddingProps = {
}; };
/* ================= collection ===================== */ /* ================= collection ===================== */
export type CreateCollectionResponse = Promise<{
collectionId: string;
results: PushDatasetDataResponse;
}>;
/* ================= data ===================== */ /* ================= data ===================== */
export type InsertOneDatasetDataProps = PushDatasetDataChunkProps & { export type InsertOneDatasetDataProps = PushDatasetDataChunkProps & {

View File

@@ -154,10 +154,8 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
return onUpdate(session); return onUpdate(session);
}); });
} } else if (isDefaultPermissionChanged) {
// Update default permission
// Update default permission
if (isDefaultPermissionChanged) {
await mongoSessionRun(async (session) => { await mongoSessionRun(async (session) => {
if (isFolder) { if (isFolder) {
// Sync children default permission // Sync children default permission
@@ -193,6 +191,8 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
return onUpdate(session, defaultPermission); return onUpdate(session, defaultPermission);
}); });
} else {
return onUpdate();
} }
} }

View File

@@ -42,7 +42,7 @@ async function handler(
} }
// get app and history // get app and history
const [{ history }, { nodes }] = await Promise.all([ const [{ histories }, { nodes }] = await Promise.all([
getChatItems({ getChatItems({
appId, appId,
chatId, chatId,
@@ -60,7 +60,7 @@ async function handler(
title: chat?.title || '新对话', title: chat?.title || '新对话',
userAvatar: undefined, userAvatar: undefined,
variables: chat?.variables || {}, variables: chat?.variables || {},
history, history: histories,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig: app.chatConfig, chatConfig: app.chatConfig,

View File

@@ -41,7 +41,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
throw new Error(ChatErrEnum.unAuthChat); throw new Error(ChatErrEnum.unAuthChat);
} }
const [{ history }, { nodes }] = await Promise.all([ const [{ histories }, { nodes }] = await Promise.all([
getChatItems({ getChatItems({
appId: app._id, appId: app._id,
chatId, chatId,
@@ -56,7 +56,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]); ]);
// pick share response field // pick share response field
history.forEach((item) => { histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) { if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData }); item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
} }
@@ -70,7 +70,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
//@ts-ignore //@ts-ignore
userAvatar: tmb?.userId?.avatar, userAvatar: tmb?.userId?.avatar,
variables: chat?.variables || {}, variables: chat?.variables || {},
history, history: histories,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig: app.chatConfig, chatConfig: app.chatConfig,

View File

@@ -47,7 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
// get app and history // get app and history
const [{ history }, { nodes }] = await Promise.all([ const [{ histories }, { nodes }] = await Promise.all([
getChatItems({ getChatItems({
appId, appId,
chatId, chatId,
@@ -58,7 +58,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]); ]);
// pick share response field // pick share response field
history.forEach((item) => { histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) { if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData }); item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
} }
@@ -71,7 +71,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
title: chat?.title || '新对话', title: chat?.title || '新对话',
userAvatar: team?.avatar, userAvatar: team?.avatar,
variables: chat?.variables || {}, variables: chat?.variables || {},
history, history: histories,
app: { app: {
chatConfig: getAppChatConfig({ chatConfig: getAppChatConfig({
chatConfig: app.chatConfig, chatConfig: app.chatConfig,

View File

@@ -18,8 +18,9 @@ import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { rawText2Chunks } from '@fastgpt/service/core/dataset/read'; import { rawText2Chunks } from '@fastgpt/service/core/dataset/read';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { CreateCollectionResponse } from '@/global/core/dataset/api';
async function handler(req: NextApiRequest) { async function handler(req: NextApiRequest): CreateCollectionResponse {
const { datasetId, parentId, fileId } = req.body as FileIdCreateDatasetCollectionParams; const { datasetId, parentId, fileId } = req.body as FileIdCreateDatasetCollectionParams;
const trainingType = TrainingModeEnum.chunk; const trainingType = TrainingModeEnum.chunk;
const { teamId, tmbId, dataset } = await authDataset({ const { teamId, tmbId, dataset } = await authDataset({
@@ -50,7 +51,7 @@ async function handler(req: NextApiRequest) {
insertLen: predictDataLimitLength(trainingType, chunks) insertLen: predictDataLimitLength(trainingType, chunks)
}); });
await mongoSessionRun(async (session) => { return mongoSessionRun(async (session) => {
// 4. create collection // 4. create collection
const { _id: collectionId } = await createOneCollection({ const { _id: collectionId } = await createOneCollection({
teamId, teamId,
@@ -80,7 +81,7 @@ async function handler(req: NextApiRequest) {
}); });
// 6. insert to training queue // 6. insert to training queue
await pushDataListToTrainingQueue({ const insertResult = await pushDataListToTrainingQueue({
teamId, teamId,
tmbId, tmbId,
datasetId: dataset._id, datasetId: dataset._id,
@@ -97,7 +98,7 @@ async function handler(req: NextApiRequest) {
session session
}); });
return collectionId; return { collectionId, results: insertResult };
}); });
} }
export default NextAPI(handler); export default NextAPI(handler);

View File

@@ -15,8 +15,9 @@ import { reloadCollectionChunks } from '@fastgpt/service/core/dataset/collection
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CreateCollectionResponse } from '@/global/core/dataset/api';
async function handler(req: NextApiRequest) { async function handler(req: NextApiRequest): CreateCollectionResponse {
const { const {
link, link,
trainingType = TrainingModeEnum.chunk, trainingType = TrainingModeEnum.chunk,
@@ -40,7 +41,7 @@ async function handler(req: NextApiRequest) {
insertLen: predictDataLimitLength(trainingType, new Array(10)) insertLen: predictDataLimitLength(trainingType, new Array(10))
}); });
await mongoSessionRun(async (session) => { return mongoSessionRun(async (session) => {
// 2. create collection // 2. create collection
const collection = await createOneCollection({ const collection = await createOneCollection({
...body, ...body,
@@ -70,7 +71,7 @@ async function handler(req: NextApiRequest) {
}); });
// load // load
await reloadCollectionChunks({ const result = await reloadCollectionChunks({
collection: { collection: {
...collection.toObject(), ...collection.toObject(),
datasetId: dataset datasetId: dataset
@@ -80,7 +81,12 @@ async function handler(req: NextApiRequest) {
session session
}); });
return collection; return {
collectionId: collection._id,
results: {
insertLen: result.insertLen
}
};
}); });
} }

View File

@@ -23,8 +23,9 @@ import { MongoImage } from '@fastgpt/service/common/file/image/schema';
import { readRawTextByLocalFile } from '@fastgpt/service/common/file/read/utils'; import { readRawTextByLocalFile } from '@fastgpt/service/common/file/read/utils';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CreateCollectionResponse } from '@/global/core/dataset/api';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) { async function handler(req: NextApiRequest, res: NextApiResponse<any>): CreateCollectionResponse {
/** /**
* Creates the multer uploader * Creates the multer uploader
*/ */

View File

@@ -17,8 +17,9 @@ import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CreateCollectionResponse } from '@/global/core/dataset/api';
async function handler(req: NextApiRequest) { async function handler(req: NextApiRequest): CreateCollectionResponse {
const { const {
name, name,
text, text,

View File

@@ -1,7 +1,6 @@
import type { NextApiRequest } from 'next'; import type { NextApiRequest } from 'next';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import type { CreateDatasetParams } from '@/global/core/dataset/api.d'; import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
import { createDefaultCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { getLLMModel, getVectorModel, getDatasetModel } from '@fastgpt/service/core/ai/model'; import { getLLMModel, getVectorModel, getDatasetModel } from '@fastgpt/service/core/ai/model';
@@ -50,14 +49,6 @@ async function handler(req: NextApiRequest) {
defaultPermission defaultPermission
}); });
if (type === DatasetTypeEnum.dataset) {
await createDefaultCollection({
datasetId: _id,
teamId,
tmbId
});
}
return _id; return _id;
} }

View File

@@ -26,6 +26,7 @@ import requestIp from 'request-ip';
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
import { authTeamSpaceToken } from '@/service/support/permission/auth/team'; import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
import { import {
concatHistories,
filterPublicNodeResponseData, filterPublicNodeResponseData,
removeEmptyUserInput removeEmptyUserInput
} from '@fastgpt/global/core/chat/utils'; } from '@fastgpt/global/core/chat/utils';
@@ -119,6 +120,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
let startTime = Date.now(); let startTime = Date.now();
// Web chat params: [Human, AI]
const chatMessages = GPTMessages2Chats(messages); const chatMessages = GPTMessages2Chats(messages);
if (chatMessages[chatMessages.length - 1].obj !== ChatRoleEnum.Human) { if (chatMessages[chatMessages.length - 1].obj !== ChatRoleEnum.Human) {
chatMessages.pop(); chatMessages.pop();
@@ -170,7 +172,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// 1. get and concat history; 2. get app workflow // 1. get and concat history; 2. get app workflow
const limit = getMaxHistoryLimitFromNodes(app.modules); const limit = getMaxHistoryLimitFromNodes(app.modules);
const [{ history }, { nodes, edges, chatConfig }] = await Promise.all([ const [{ histories }, { nodes, edges, chatConfig }] = await Promise.all([
getChatItems({ getChatItems({
appId: app._id, appId: app._id,
chatId, chatId,
@@ -179,7 +181,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}), }),
getAppLatestVersion(app._id, app) getAppLatestVersion(app._id, app)
]); ]);
const concatHistories = history.concat(chatMessages); const newHistories = concatHistories(histories, chatMessages);
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId; const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
/* start flow controller */ /* start flow controller */
@@ -198,7 +200,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
runtimeEdges: initWorkflowEdgeStatus(edges), runtimeEdges: initWorkflowEdgeStatus(edges),
variables, variables,
query: removeEmptyUserInput(question.value), query: removeEmptyUserInput(question.value),
histories: concatHistories, histories: newHistories,
stream, stream,
detail, detail,
maxRunTimes: 200 maxRunTimes: 200
@@ -217,7 +219,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
modules: setEntryEntries(app.modules), modules: setEntryEntries(app.modules),
variables, variables,
inputFiles: files, inputFiles: files,
histories: concatHistories, histories: newHistories,
startParams: { startParams: {
userChatInput: text userChatInput: text
}, },

View File

@@ -63,8 +63,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
const avatar = getValues('avatar'); const avatar = getValues('avatar');
// submit config // submit config
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({ const { runAsync: saveSubmitSuccess, loading: btnLoading } = useRequest2(
mutationFn: async (data: AppSchema) => { async (data: AppSchema) => {
await updateAppDetail({ await updateAppDetail({
name: data.name, name: data.name,
avatar: data.avatar, avatar: data.avatar,
@@ -72,16 +72,17 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
defaultPermission: data.defaultPermission defaultPermission: data.defaultPermission
}); });
}, },
onSuccess() { {
onClose(); onSuccess() {
toast({ toast({
title: t('common.Update Success'), title: t('common.Update Success'),
status: 'success' status: 'success'
}); });
reloadApp(); reloadApp();
}, },
errorToast: t('common.Update Failed') errorToast: t('common.Update Failed')
}); }
);
const saveSubmitError = useCallback(() => { const saveSubmitError = useCallback(() => {
// deep search message // deep search message
@@ -101,8 +102,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
}, [errors, t, toast]); }, [errors, t, toast]);
const saveUpdateModel = useCallback( const saveUpdateModel = useCallback(
() => handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(), () => handleSubmit((data) => saveSubmitSuccess(data).then(onClose), saveSubmitError)(),
[handleSubmit, saveSubmitError, saveSubmitSuccess] [handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
); );
const onSelectFile = useCallback( const onSelectFile = useCallback(
@@ -210,7 +211,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
isInheritPermission={appDetail.inheritPermission} isInheritPermission={appDetail.inheritPermission}
onChange={(v) => { onChange={(v) => {
setValue('defaultPermission', v); setValue('defaultPermission', v);
handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)(); return handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
}} }}
hasParent={!!appDetail.parentId} hasParent={!!appDetail.parentId}
/> />

View File

@@ -10,7 +10,6 @@ import {
Checkbox, Checkbox,
ModalFooter ModalFooter
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { DragHandleIcon } from '@chakra-ui/icons';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { AppSchema } from '@fastgpt/global/core/app/type.d'; import { AppSchema } from '@fastgpt/global/core/app/type.d';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
@@ -28,11 +27,13 @@ import MyModal from '@fastgpt/web/components/common/MyModal';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { postTransition2Workflow } from '@/web/core/app/api/app'; import { postTransition2Workflow } from '@/web/core/app/api/app';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const AppCard = () => { const AppCard = () => {
const router = useRouter(); const router = useRouter();
const { t } = useTranslation(); const { t } = useTranslation();
const { appT } = useI18n(); const { appT } = useI18n();
const { isPc } = useSystem();
const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector( const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector(
AppContext, AppContext,
@@ -68,7 +69,7 @@ const AppCard = () => {
return ( return (
<> <>
{/* basic info */} {/* basic info */}
<Box px={6} py={4} position={'relative'}> <Box px={[4, 6]} py={4} position={'relative'}>
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} /> <Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
<Box ml={3} fontWeight={'bold'} fontSize={'md'} flex={'1 0 0'} color={'myGray.900'}> <Box ml={3} fontWeight={'bold'} fontSize={'md'} flex={'1 0 0'} color={'myGray.900'}>
@@ -149,13 +150,15 @@ const AppCard = () => {
/> />
)} )}
<Box flex={1} /> <Box flex={1} />
<MyTag {isPc && (
type="borderFill" <MyTag
colorSchema="gray" type="borderFill"
onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} colorSchema="gray"
> onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)}
<PermissionIconText defaultPermission={appDetail.defaultPermission} /> >
</MyTag> <PermissionIconText defaultPermission={appDetail.defaultPermission} />
</MyTag>
)}
</HStack> </HStack>
</Box> </Box>
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />} {TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}

View File

@@ -29,7 +29,6 @@ import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete'; import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete';
import { TTSTypeEnum } from '@/web/core/app/constants'; import { TTSTypeEnum } from '@/web/core/app/constants';
import { getSystemVariables } from '@/web/core/app/utils'; import { getSystemVariables } from '@/web/core/app/utils';
import { useUpdate } from 'ahooks';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context'; import { AppContext } from '@/pages/app/detail/components/context';
@@ -49,7 +48,7 @@ const ScheduledTriggerConfig = dynamic(
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig')); const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'));
const BoxStyles: BoxProps = { const BoxStyles: BoxProps = {
px: 5, px: [4, 6],
py: '16px', py: '16px',
borderBottomWidth: '1px', borderBottomWidth: '1px',
borderBottomColor: 'borderColor.low' borderBottomColor: 'borderColor.low'

View File

@@ -396,7 +396,9 @@ const RenderList = React.memo(function RenderList({
{t(template.name)} {t(template.name)}
</Box> </Box>
</Flex> </Flex>
<Box mt={2}>{t(template.intro || 'core.workflow.Not intro')}</Box> <Box mt={2} color={'myGray.500'}>
{t(template.intro) || t('core.workflow.Not intro')}
</Box>
</Box> </Box>
} }
> >

View File

@@ -16,7 +16,8 @@ import CodeEditor from '@fastgpt/web/components/common/Textarea/CodeEditor';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import { useI18n } from '@/web/context/I18n'; import { useI18n } from '@/web/context/I18n';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { JS_TEMPLATE } from '@fastgpt/global/core/workflow/template/system/sandbox/constants'; import { getLatestNodeTemplate } from '@/web/core/workflow/utils';
import { CodeNode } from '@fastgpt/global/core/workflow/template/system/sandbox';
const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -24,6 +25,8 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { nodeId, inputs, outputs } = data; const { nodeId, inputs, outputs } = data;
const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs); const splitToolInputs = useContextSelector(WorkflowContext, (ctx) => ctx.splitToolInputs);
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
const { isTool, commonInputs } = splitToolInputs(inputs, nodeId); const { isTool, commonInputs } = splitToolInputs(inputs, nodeId);
const { ConfirmModal, openConfirm } = useConfirm({ const { ConfirmModal, openConfirm } = useConfirm({
content: workflowT('code.Reset template confirm') content: workflowT('code.Reset template confirm')
@@ -41,14 +44,9 @@ const NodeCode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
color={'primary.500'} color={'primary.500'}
fontSize={'xs'} fontSize={'xs'}
onClick={openConfirm(() => { onClick={openConfirm(() => {
onChangeNode({ onResetNode({
nodeId, id: nodeId,
type: 'updateInput', node: getLatestNodeTemplate(data, CodeNode)
key: item.key,
value: {
...item,
value: JS_TEMPLATE
}
}); });
})} })}
> >

View File

@@ -37,6 +37,7 @@ export const defaultInput: FlowNodeInputItemType = {
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
selectedTypeIndex: 0, selectedTypeIndex: 0,
valueType: WorkflowIOValueTypeEnum.string, valueType: WorkflowIOValueTypeEnum.string,
canEdit: true,
key: '', key: '',
label: '' label: ''
}; };

View File

@@ -16,7 +16,7 @@ import { useDebug } from '../../hooks/useDebug';
import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal'; import { ResponseBox } from '@/components/ChatBox/components/WholeResponseModal';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { getPreviewPluginNode } from '@/web/core/app/api/plugin'; import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
import { storeNode2FlowNode, updateFlowNodeVersion } from '@/web/core/workflow/utils'; import { storeNode2FlowNode, getLatestNodeTemplate } from '@/web/core/workflow/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../context'; import { WorkflowContext } from '../../../context';
@@ -24,8 +24,6 @@ import { useI18n } from '@/web/context/I18n';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants'; import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import { QuestionOutlineIcon } from '@chakra-ui/icons'; import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useMount } from 'ahooks';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useWorkflowUtils } from '../../hooks/useUtils'; import { useWorkflowUtils } from '../../hooks/useUtils';
@@ -56,13 +54,11 @@ const NodeCard = (props: Props) => {
minW = '300px', minW = '300px',
maxW = '600px', maxW = '600px',
nodeId, nodeId,
flowNodeType,
selected, selected,
menuForbid, menuForbid,
isTool = false, isTool = false,
isError = false, isError = false,
debugResult, debugResult
pluginId
} = props; } = props;
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
@@ -106,11 +102,11 @@ const NodeCard = (props: Props) => {
); );
const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version; const hasNewVersion = newNodeVersion && newNodeVersion !== node?.version;
const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType); const { runAsync: onClickSyncVersion } = useRequest2(
async () => {
const onClickSyncVersion = useCallback(async () => { const template = moduleTemplatesFlat.find((item) => item.flowNodeType === node?.flowNodeType);
try {
if (!node || !template) return; if (!node || !template) return;
if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) { if (node?.flowNodeType === FlowNodeTypeEnum.pluginModule) {
if (!node.pluginId) return; if (!node.pluginId) return;
onResetNode({ onResetNode({
@@ -120,14 +116,15 @@ const NodeCard = (props: Props) => {
} else { } else {
onResetNode({ onResetNode({
id: nodeId, id: nodeId,
node: updateFlowNodeVersion(node, template) node: getLatestNodeTemplate(node, template)
}); });
} }
await getNodeVersion(); await getNodeVersion();
} catch (error) { },
console.error('Error fetching plugin module:', error); {
refreshDeps: [node, nodeId, onResetNode, getNodeVersion]
} }
}, [getNodeVersion, node, nodeId, onResetNode, template]); );
/* Node header */ /* Node header */
const Header = useMemo(() => { const Header = useMemo(() => {
@@ -197,12 +194,7 @@ const NodeCard = (props: Props) => {
</MyTooltip> </MyTooltip>
)} )}
</Flex> </Flex>
<MenuRender <MenuRender nodeId={nodeId} menuForbid={menuForbid} />
nodeId={nodeId}
pluginId={pluginId}
flowNodeType={flowNodeType}
menuForbid={menuForbid}
/>
<NodeIntro nodeId={nodeId} intro={intro} /> <NodeIntro nodeId={nodeId} intro={intro} />
</Box> </Box>
<ConfirmSyncModal /> <ConfirmSyncModal />
@@ -219,8 +211,6 @@ const NodeCard = (props: Props) => {
appT, appT,
onOpenConfirmSync, onOpenConfirmSync,
onClickSyncVersion, onClickSyncVersion,
pluginId,
flowNodeType,
intro, intro,
ConfirmSyncModal, ConfirmSyncModal,
onOpenCustomTitleModal, onOpenCustomTitleModal,
@@ -274,13 +264,9 @@ export default React.memo(NodeCard);
const MenuRender = React.memo(function MenuRender({ const MenuRender = React.memo(function MenuRender({
nodeId, nodeId,
pluginId,
flowNodeType,
menuForbid menuForbid
}: { }: {
nodeId: string; nodeId: string;
pluginId?: string;
flowNodeType: Props['flowNodeType'];
menuForbid?: Props['menuForbid']; menuForbid?: Props['menuForbid'];
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -140,7 +140,7 @@ const ChatHistorySlider = ({
fontSize={'sm'} fontSize={'sm'}
onClick={() => onClick={() =>
canRouteToDetail && canRouteToDetail &&
router.replace({ router.push({
pathname: '/app/detail', pathname: '/app/detail',
query: { appId } query: { appId }
}) })
@@ -156,27 +156,30 @@ const ChatHistorySlider = ({
{/* menu */} {/* menu */}
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}> <Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
<Box flex={'1 0 0'}> {!isPc && appId && (
{!isPc && appId && ( <LightRowTabs<TabEnum>
<LightRowTabs<TabEnum> flex={'1 0 0'}
mr={1} mr={1}
inlineStyles={{ inlineStyles={{
px: 1 px: 1
}} }}
list={[ list={[
{ label: t('core.chat.Recent use'), value: TabEnum.recently }, ...(isTeamChat
...(!isTeamChat ? [{ label: t('App'), value: TabEnum.app }] : []), ? [{ label: t('App'), value: TabEnum.recently }]
{ label: t('core.chat.History'), value: TabEnum.history } : [
]} { label: t('core.chat.Recent use'), value: TabEnum.recently },
value={currentTab} { label: t('App'), value: TabEnum.app }
onChange={setCurrentTab} ]),
/> { label: t('core.chat.History'), value: TabEnum.history }
)} ]}
</Box> value={currentTab}
onChange={setCurrentTab}
/>
)}
<Button <Button
variant={'whitePrimary'} variant={'whitePrimary'}
flex={['auto', 1]} flex={[appId ? '0 0 auto' : 1, 1]}
h={'100%'} h={'100%'}
color={'primary.600'} color={'primary.600'}
borderRadius={'xl'} borderRadius={'xl'}
@@ -186,8 +189,8 @@ const ChatHistorySlider = ({
> >
{t('core.chat.New Chat')} {t('core.chat.New Chat')}
</Button> </Button>
{/* Clear */}
{(isPc || !showApps) && ( {isPc && (
<IconButton <IconButton
ml={3} ml={3}
h={'100%'} h={'100%'}

View File

@@ -11,7 +11,7 @@ const ToolMenu = ({ history }: { history: ChatItemType[] }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { onExportChat } = useChatBox(); const { onExportChat } = useChatBox();
const router = useRouter(); const router = useRouter();
console.log(history);
return history.length > 0 ? ( return history.length > 0 ? (
<MyMenu <MyMenu
Button={ Button={

View File

@@ -34,7 +34,6 @@ export async function insertData2Dataset({
session?: ClientSession; session?: ClientSession;
}) { }) {
if (!q || !datasetId || !collectionId || !model) { if (!q || !datasetId || !collectionId || !model) {
console.log(q, a, datasetId, collectionId, model);
return Promise.reject('q, datasetId, collectionId, model is required'); return Promise.reject('q, datasetId, collectionId, model is required');
} }
if (String(teamId) === String(tmbId)) { if (String(teamId) === String(tmbId)) {
@@ -140,7 +139,7 @@ export async function updateData2Dataset({
formatIndexes.unshift(defaultIndex ? defaultIndex : getDefaultIndex({ q, a })); formatIndexes.unshift(defaultIndex ? defaultIndex : getDefaultIndex({ q, a }));
} }
formatIndexes = formatIndexes.slice(0, 6); formatIndexes = formatIndexes.slice(0, 6);
console.log(formatIndexes);
// patch indexes, create, update, delete // patch indexes, create, update, delete
const patchResult: PatchIndexesProps[] = []; const patchResult: PatchIndexesProps[] = [];

View File

@@ -8,6 +8,7 @@ import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/cons
import { import {
EDGE_TYPE, EDGE_TYPE,
FlowNodeInputTypeEnum, FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum, FlowNodeTypeEnum,
defaultNodeVersion defaultNodeVersion
} from '@fastgpt/global/core/workflow/node/constant'; } from '@fastgpt/global/core/workflow/node/constant';
@@ -34,6 +35,7 @@ import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/syste
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant'; import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
import { AppChatConfigType } from '@fastgpt/global/core/app/type'; import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { cloneDeep, isEqual } from 'lodash'; import { cloneDeep, isEqual } from 'lodash';
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
export const nodeTemplate2FlowNode = ({ export const nodeTemplate2FlowNode = ({
template, template,
@@ -70,13 +72,24 @@ export const storeNode2FlowNode = ({
moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) || moduleTemplatesFlat.find((template) => template.flowNodeType === storeNode.flowNodeType) ||
EmptyNode; EmptyNode;
const templateInputs = template.inputs.filter((input) => !input.canEdit);
const templateOutputs = template.outputs.filter(
(output) => output.type !== FlowNodeOutputTypeEnum.dynamic
);
const dynamicInput = template.inputs.find(
(input) => input.renderTypeList[0] === FlowNodeInputTypeEnum.addInputParam
);
// replace item data // replace item data
const nodeItem: FlowNodeItemType = { const nodeItem: FlowNodeItemType = {
...template, ...template,
...storeNode, ...storeNode,
version: storeNode.version ?? template.version ?? defaultNodeVersion, version: storeNode.version ?? template.version ?? defaultNodeVersion,
inputs: template.inputs /*
Inputs and outputs, New fields are added, not reduced
*/
inputs: templateInputs
.map<FlowNodeInputItemType>((templateInput) => { .map<FlowNodeInputItemType>((templateInput) => {
const storeInput = const storeInput =
storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput; storeNode.inputs.find((item) => item.key === templateInput.key) || templateInput;
@@ -91,27 +104,35 @@ export const storeNode2FlowNode = ({
}; };
}) })
.concat( .concat(
/* /* Concat dynamic inputs */
1. Plugin input storeNode.inputs
2. Old version adapt: Dynamic input will be added to the node inputs. .filter((item) => !templateInputs.find((input) => input.key === item.key))
*/ .map((item) => {
storeNode.inputs.filter((item) => !template.inputs.find((input) => input.key === item.key)) if (!dynamicInput) return item;
return {
...item,
...getInputComponentProps(dynamicInput)
};
})
), ),
outputs: template.outputs outputs: templateOutputs
.map<FlowNodeOutputItemType>((templateOutput) => { .map<FlowNodeOutputItemType>((templateOutput) => {
const storeOutput = const storeOutput =
template.outputs.find((item) => item.key === templateOutput.key) || templateOutput; template.outputs.find((item) => item.key === templateOutput.key) || templateOutput;
return { return {
...storeOutput, ...storeOutput,
...templateOutput, ...templateOutput,
id: storeOutput.id ?? templateOutput.id, id: storeOutput.id ?? templateOutput.id,
label: storeOutput.label ?? templateOutput.label,
value: storeOutput.value ?? templateOutput.value value: storeOutput.value ?? templateOutput.value
}; };
}) })
.concat( .concat(
storeNode.outputs.filter( storeNode.outputs.filter(
(item) => !template.outputs.find((output) => output.key === item.key) (item) => !templateOutputs.find((output) => output.key === item.key)
) )
) )
}; };
@@ -365,37 +386,42 @@ export const getWorkflowGlobalVariables = ({
export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>; export type CombinedItemType = Partial<FlowNodeInputItemType> & Partial<FlowNodeOutputItemType>;
export const updateFlowNodeVersion = ( /* Reset node to latest version */
export const getLatestNodeTemplate = (
node: FlowNodeItemType, node: FlowNodeItemType,
template: FlowNodeTemplateType template: FlowNodeTemplateType
): FlowNodeItemType => { ): FlowNodeItemType => {
function updateArrayBasedOnTemplate<T extends FlowNodeInputItemType | FlowNodeOutputItemType>(
nodeArray: T[],
templateArray: T[]
): T[] {
return templateArray.map((templateItem) => {
const nodeItem = nodeArray.find((item) => item.key === templateItem.key);
if (nodeItem) {
return { ...templateItem, ...nodeItem } as T;
}
return { ...templateItem };
});
}
const updatedNode: FlowNodeItemType = { const updatedNode: FlowNodeItemType = {
...node, ...node,
...template, ...template,
inputs: template.inputs.map((templateItem) => {
const nodeItem = node.inputs.find((item) => item.key === templateItem.key);
if (nodeItem) {
return {
...templateItem,
value: nodeItem.value,
selectedTypeIndex: nodeItem.selectedTypeIndex,
valueType: nodeItem.valueType
};
}
return { ...templateItem };
}),
outputs: template.outputs.map((templateItem) => {
const nodeItem = node.outputs.find((item) => item.key === templateItem.key);
if (nodeItem) {
return {
...templateItem,
id: nodeItem.id,
value: nodeItem.value,
valueType: nodeItem.valueType
};
}
return { ...templateItem };
}),
name: node.name, name: node.name,
intro: node.intro intro: node.intro
}; };
if (node.inputs && template.inputs) {
updatedNode.inputs = updateArrayBasedOnTemplate(node.inputs, template.inputs);
}
if (node.outputs && template.outputs) {
updatedNode.outputs = updateArrayBasedOnTemplate(node.outputs, template.outputs);
}
return updatedNode; return updatedNode;
}; };
@@ -441,41 +467,46 @@ export const compareWorkflow = (workflow1: WorkflowType, workflow2: WorkflowType
return false; return false;
} }
const node1 = clone1.nodes.filter(Boolean).map((node) => ({ const formatNodes = (nodes: StoreNodeItemType[]) => {
flowNodeType: node.flowNodeType, return nodes
inputs: node.inputs.map((input) => ({ .filter((node) => {
...input, if (!node) return;
value: input.value ?? undefined if ([FlowNodeTypeEnum.systemConfig].includes(node.flowNodeType)) return;
})),
outputs: node.outputs.map((input) => ({ return true;
...input, })
value: input.value ?? undefined .map((node) => ({
})), flowNodeType: node.flowNodeType,
name: node.name, inputs: node.inputs.map((input) => ({
intro: node.intro, key: input.key,
avatar: node.avatar, selectedTypeIndex: input.selectedTypeIndex ?? 0,
version: node.version, renderTypeLis: input.renderTypeList,
position: node.position valueType: input.valueType,
})); value: input.value ?? undefined
const node2 = clone2.nodes.filter(Boolean).map((node) => ({ })),
flowNodeType: node.flowNodeType, outputs: node.outputs.map((item) => ({
inputs: node.inputs.map((input) => ({ key: item.key,
...input, type: item.type,
value: input.value ?? undefined value: item.value ?? undefined
})), })),
outputs: node.outputs.map((input) => ({ name: node.name,
...input, intro: node.intro,
value: input.value ?? undefined avatar: node.avatar,
})), version: node.version,
name: node.name, position: node.position
intro: node.intro, }));
avatar: node.avatar, };
version: node.version, const node1 = formatNodes(clone1.nodes);
position: node.position const node2 = formatNodes(clone2.nodes);
}));
// console.log(node1); // console.log(node1);
// console.log(node2); // console.log(node2);
node1.forEach((node, i) => {
if (!isEqual(node, node2[i])) {
console.log('node not equal');
}
});
return isEqual(node1, node2); return isEqual(node1, node2);
}; };