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.mouseWheelZoom": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.prettierPath": "../node_modules/prettier",
"prettier.prettierPath": "node_modules/prettier",
"typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": [
"packages/web/i18n",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,17 @@ import { FlowNodeTypeEnum } from '../workflow/node/constant';
import { ChatItemValueTypeEnum, ChatRoleEnum } from './constants';
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 = '新对话') => {
// @ts-ignore
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,
type: FlowNodeOutputTypeEnum.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: [
{
...Output_Template_AddOutput,
customFieldConfig: {
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
showDescription: false,
showDefaultValue: true
}
...Output_Template_AddOutput
},
{
id: NodeOutputKeyEnum.error,

View File

@@ -36,6 +36,32 @@ export const CodeNode: FlowNodeTemplateType = {
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,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
@@ -52,7 +78,7 @@ export const CodeNode: FlowNodeTemplateType = {
outputs: [
{
...Output_Template_AddOutput,
description: '将代码中 return 的对象作为输出,传递给后续的节点'
description: '将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key'
},
{
id: NodeOutputKeyEnum.rawResponse,

View File

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

View File

@@ -88,7 +88,7 @@
"x": 1050.9890727421412,
"y": -415.2085119990912
},
"version": "486",
"version": "481",
"inputs": [
{
"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;
import { RawTextBufferSchemaType } from './type';
@@ -28,6 +28,7 @@ try {
console.log(error);
}
export const MongoRawTextBuffer: Model<RawTextBufferSchemaType> =
models[collectionName] || model(collectionName, RawTextBufferSchema);
MongoRawTextBuffer.syncIndexes();
export const MongoRawTextBuffer = getMongoModel<RawTextBufferSchemaType>(
collectionName,
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;
import { TTSBufferSchemaType } from './type.d';
@@ -31,6 +31,4 @@ try {
console.log(error);
}
export const MongoTTSBuffer: Model<TTSBufferSchemaType> =
models[collectionName] || model(collectionName, TTSBufferSchema);
MongoTTSBuffer.syncIndexes();
export const MongoTTSBuffer = getMongoModel<TTSBufferSchemaType>(collectionName, TTSBufferSchema);

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 FileSchema = new Schema({});
@@ -10,6 +10,4 @@ try {
console.log(error);
}
export const MongoFileSchema = models['dataset.files'] || model('dataset.files', FileSchema);
MongoFileSchema.syncIndexes();
export const MongoFileSchema = getMongoModel('dataset.files', FileSchema);

View File

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

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

@@ -75,54 +75,9 @@ export async function createOneCollection({
{ session }
);
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: collection._id,
teamId,
tmbId,
session
});
}
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 */
export const delCollectionRelatedSource = async ({
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;
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
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 {
// auth file
DatasetCollectionSchema.index({ teamId: 1, fileId: 1 });
@@ -111,8 +108,11 @@ try {
// get forbid
// DatasetCollectionSchema.index({ teamId: 1, datasetId: 1, forbid: 1 });
MongoDatasetCollection.syncIndexes({ background: true });
} catch (error) {
console.log(error);
}
export const MongoDatasetCollection = getMongoModel<DatasetCollectionSchemaType>(
DatasetColCollectionName,
DatasetCollectionSchema
);

View File

@@ -10,6 +10,7 @@ import {
} from '@fastgpt/global/core/dataset/constants';
import { hashStr } from '@fastgpt/global/common/string/tools';
import { ClientSession } from '../../../common/mongo';
import { PushDatasetDataResponse } from '@fastgpt/global/core/dataset/api';
/**
* get all collection by top collectionId
@@ -138,7 +139,7 @@ export const reloadCollectionChunks = async ({
billId?: string;
rawText?: string;
session: ClientSession;
}) => {
}): Promise<PushDatasetDataResponse> => {
const {
title,
rawText: newRawText,
@@ -149,7 +150,10 @@ export const reloadCollectionChunks = async ({
newRawText: rawText
});
if (isSameRawText) return;
if (isSameRawText)
return {
insertLen: 0
};
// split data
const { chunks } = splitText2Chunks({
@@ -164,7 +168,7 @@ export const reloadCollectionChunks = async ({
return Promise.reject('Training model error');
})();
await MongoDatasetTraining.insertMany(
const result = await MongoDatasetTraining.insertMany(
chunks.map((item, i) => ({
teamId: col.teamId,
tmbId,
@@ -191,4 +195,8 @@ export const reloadCollectionChunks = async ({
},
{ 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;
import { DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type.d';
import {
@@ -77,27 +77,23 @@ const DatasetDataSchema = new Schema({
rebuilding: Boolean
});
export const MongoDatasetData: Model<DatasetDataSchemaType> =
models[DatasetDataCollectionName] || model(DatasetDataCollectionName, DatasetDataSchema);
// list collection and count data; list data; delete collection(relate data)
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 {
// list collection and count data; list data; delete collection(relate data)
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 });
MongoDatasetData.syncIndexes({ background: true });
} catch (error) {
console.log(error);
}
export const MongoDatasetData = getMongoModel<DatasetDataSchemaType>(
DatasetDataCollectionName,
DatasetDataSchema
);

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;
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
import {
@@ -11,7 +11,6 @@ import {
TeamCollectionName,
TeamMemberCollectionName
} 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';
export const DatasetCollectionName = 'datasets';
@@ -99,6 +98,4 @@ try {
console.log(error);
}
export const MongoDataset: Model<DatasetSchemaType> =
models[DatasetCollectionName] || model(DatasetCollectionName, DatasetSchema);
MongoDataset.syncIndexes();
export const MongoDataset = getMongoModel<DatasetSchemaType>(DatasetCollectionName, DatasetSchema);

View File

@@ -212,7 +212,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
{
$match: {
$expr: { $eq: ['$_id', '$$collectionId'] },
forbid: { $eq: false } // 直接在lookup阶段过滤
forbid: { $eq: true } // 匹配被禁用的数据
}
},
{
@@ -226,7 +226,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
},
{
$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;
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
@@ -103,7 +103,7 @@ try {
console.log(error);
}
export const MongoDatasetTraining: Model<DatasetTrainingSchemaType> =
models[DatasetTrainingCollectionName] || model(DatasetTrainingCollectionName, TrainingDataSchema);
MongoDatasetTraining.syncIndexes();
export const MongoDatasetTraining = getMongoModel<DatasetTrainingSchemaType>(
DatasetTrainingCollectionName,
TrainingDataSchema
);

View File

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

View File

@@ -1,12 +1,11 @@
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 {
WorkflowIOValueTypeEnum,
NodeOutputKeyEnum
} from '@fastgpt/global/core/workflow/constants';
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/runtime/type';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
export const filterToolNodeIdByEdges = ({
nodeId,
@@ -45,10 +44,16 @@ export const filterToolNodeIdByEdges = ({
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
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 */

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;
import { PromotionRecordSchema as PromotionRecordType } from '@fastgpt/global/support/activity/type.d';
@@ -29,6 +29,7 @@ const PromotionRecordSchema = new Schema({
}
});
export const MongoPromotionRecord: Model<PromotionRecordType> =
models['promotionRecord'] || model('promotionRecord', PromotionRecordSchema);
MongoPromotionRecord.syncIndexes();
export const MongoPromotionRecord = getMongoModel<PromotionRecordType>(
'promotionRecord',
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;
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
import {
@@ -64,6 +64,4 @@ try {
console.log(error);
}
export const MongoOpenApi: Model<OpenApiSchema> =
models['openapi'] || model('openapi', OpenApiSchema);
MongoOpenApi.syncIndexes();
export const MongoOpenApi = getMongoModel<OpenApiSchema>('openapi', OpenApiSchema);

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

View File

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

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

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;
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
@@ -49,5 +49,7 @@ try {
console.log(error);
}
export const MongoTeamMember: Model<TeamMemberType> =
models[TeamMemberCollectionName] || model(TeamMemberCollectionName, TeamMemberSchema);
export const MongoTeamMember = getMongoModel<TeamMemberType>(
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;
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
import { userCollectionName } from '../../user/schema';
@@ -61,5 +61,4 @@ try {
console.log(error);
}
export const MongoTeam: Model<TeamType> =
models[TeamCollectionName] || model(TeamCollectionName, TeamSchema);
export const MongoTeam = getMongoModel<TeamType>(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;
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
import {
@@ -32,5 +32,7 @@ try {
console.log(error);
}
export const MongoTeamTags: Model<TeamTagsSchemaType> =
models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagSchema);
export const MongoTeamTags = getMongoModel<TeamTagsSchemaType>(
TeamTagsCollectionName,
TeamTagSchema
);

View File

@@ -3,7 +3,7 @@
1. type=standard: There will only be 1, and each team will have one
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;
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import {
@@ -93,5 +93,4 @@ try {
console.log(error);
}
export const MongoTeamSub: Model<TeamSubSchema> =
models[subCollectionName] || model(subCollectionName, SubSchema);
export const MongoTeamSub = getMongoModel<TeamSubSchema>(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;
import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type';
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
@@ -70,6 +70,4 @@ try {
console.log(error);
}
export const MongoUsage: Model<UsageSchemaType> =
models[UsageCollectionName] || model(UsageCollectionName, UsageSchema);
MongoUsage.syncIndexes();
export const MongoUsage = getMongoModel<UsageSchemaType>(UsageCollectionName, UsageSchema);

View File

@@ -57,7 +57,7 @@
},
"module": {
"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",
"My Modules": "My Modules",
"No Modules": "No plugins yet~",

View File

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

View File

@@ -16,7 +16,7 @@
"Tool input": "Tool",
"code": {
"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": {
"Input value": "Input",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -155,7 +155,7 @@ const SelectOneResource = ({
return loading ? (
<Loading fixed={false} />
) : (
<Box maxH={maxH} overflow={'auto'}>
<Box maxH={maxH} h={'100%'} overflow={'auto'}>
<Render list={concatRoot} />
</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 {
DatasetSearchModeEnum,
DatasetSourceReadTypeEnum,
@@ -31,6 +34,10 @@ export type RebuildEmbeddingProps = {
};
/* ================= collection ===================== */
export type CreateCollectionResponse = Promise<{
collectionId: string;
results: PushDatasetDataResponse;
}>;
/* ================= data ===================== */
export type InsertOneDatasetDataProps = PushDatasetDataChunkProps & {

View File

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

View File

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

View File

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

View File

@@ -47,7 +47,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
// get app and history
const [{ history }, { nodes }] = await Promise.all([
const [{ histories }, { nodes }] = await Promise.all([
getChatItems({
appId,
chatId,
@@ -58,7 +58,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
// pick share response field
history.forEach((item) => {
histories.forEach((item) => {
if (item.obj === ChatRoleEnum.AI) {
item.responseData = filterPublicNodeResponseData({ flowResponses: item.responseData });
}
@@ -71,7 +71,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
title: chat?.title || '新对话',
userAvatar: team?.avatar,
variables: chat?.variables || {},
history,
history: histories,
app: {
chatConfig: getAppChatConfig({
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 { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
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 trainingType = TrainingModeEnum.chunk;
const { teamId, tmbId, dataset } = await authDataset({
@@ -50,7 +51,7 @@ async function handler(req: NextApiRequest) {
insertLen: predictDataLimitLength(trainingType, chunks)
});
await mongoSessionRun(async (session) => {
return mongoSessionRun(async (session) => {
// 4. create collection
const { _id: collectionId } = await createOneCollection({
teamId,
@@ -80,7 +81,7 @@ async function handler(req: NextApiRequest) {
});
// 6. insert to training queue
await pushDataListToTrainingQueue({
const insertResult = await pushDataListToTrainingQueue({
teamId,
tmbId,
datasetId: dataset._id,
@@ -97,7 +98,7 @@ async function handler(req: NextApiRequest) {
session
});
return collectionId;
return { collectionId, results: insertResult };
});
}
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 { NextAPI } from '@/service/middleware/entry';
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 {
link,
trainingType = TrainingModeEnum.chunk,
@@ -40,7 +41,7 @@ async function handler(req: NextApiRequest) {
insertLen: predictDataLimitLength(trainingType, new Array(10))
});
await mongoSessionRun(async (session) => {
return mongoSessionRun(async (session) => {
// 2. create collection
const collection = await createOneCollection({
...body,
@@ -70,7 +71,7 @@ async function handler(req: NextApiRequest) {
});
// load
await reloadCollectionChunks({
const result = await reloadCollectionChunks({
collection: {
...collection.toObject(),
datasetId: dataset
@@ -80,7 +81,12 @@ async function handler(req: NextApiRequest) {
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 { NextAPI } from '@/service/middleware/entry';
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
*/

View File

@@ -17,8 +17,9 @@ import { getLLMModel, getVectorModel } from '@fastgpt/service/core/ai/model';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { NextAPI } from '@/service/middleware/entry';
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 {
name,
text,

View File

@@ -1,7 +1,6 @@
import type { NextApiRequest } from 'next';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
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 { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { getLLMModel, getVectorModel, getDatasetModel } from '@fastgpt/service/core/ai/model';
@@ -50,14 +49,6 @@ async function handler(req: NextApiRequest) {
defaultPermission
});
if (type === DatasetTypeEnum.dataset) {
await createDefaultCollection({
datasetId: _id,
teamId,
tmbId
});
}
return _id;
}

View File

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

View File

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

View File

@@ -10,7 +10,6 @@ import {
Checkbox,
ModalFooter
} from '@chakra-ui/react';
import { DragHandleIcon } from '@chakra-ui/icons';
import { useRouter } from 'next/router';
import { AppSchema } from '@fastgpt/global/core/app/type.d';
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 { postTransition2Workflow } from '@/web/core/app/api/app';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
const AppCard = () => {
const router = useRouter();
const { t } = useTranslation();
const { appT } = useI18n();
const { isPc } = useSystem();
const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector(
AppContext,
@@ -68,7 +69,7 @@ const AppCard = () => {
return (
<>
{/* basic info */}
<Box px={6} py={4} position={'relative'}>
<Box px={[4, 6]} py={4} position={'relative'}>
<Flex alignItems={'center'}>
<Avatar src={appDetail.avatar} borderRadius={'md'} w={'28px'} />
<Box ml={3} fontWeight={'bold'} fontSize={'md'} flex={'1 0 0'} color={'myGray.900'}>
@@ -149,13 +150,15 @@ const AppCard = () => {
/>
)}
<Box flex={1} />
<MyTag
type="borderFill"
colorSchema="gray"
onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)}
>
<PermissionIconText defaultPermission={appDetail.defaultPermission} />
</MyTag>
{isPc && (
<MyTag
type="borderFill"
colorSchema="gray"
onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)}
>
<PermissionIconText defaultPermission={appDetail.defaultPermission} />
</MyTag>
)}
</HStack>
</Box>
{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 { TTSTypeEnum } from '@/web/core/app/constants';
import { getSystemVariables } from '@/web/core/app/utils';
import { useUpdate } from 'ahooks';
import { useI18n } from '@/web/context/I18n';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
@@ -49,7 +48,7 @@ const ScheduledTriggerConfig = dynamic(
const WelcomeTextConfig = dynamic(() => import('@/components/core/app/WelcomeTextConfig'));
const BoxStyles: BoxProps = {
px: 5,
px: [4, 6],
py: '16px',
borderBottomWidth: '1px',
borderBottomColor: 'borderColor.low'

View File

@@ -396,7 +396,9 @@ const RenderList = React.memo(function RenderList({
{t(template.name)}
</Box>
</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>
}
>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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