Files
FastGPT/packages/service/core/app/controller.ts
Archer 830eb19055 feature: V4.12.2 (#5525)
* feat: favorite apps & quick apps with their own configuration (#5515)

* chore: extract chat history and drawer; fix model selector

* feat: display favourite apps and make it configurable

* feat: favorite apps & quick apps with their own configuration

* fix: fix tab title and add loading state for searching

* fix: cascade delete favorite app and quick app while deleting relative app

* chore: make improvements

* fix: favourite apps ui

* fix: add permission for quick apps

* chore: fix permission & clear redundant code

* perf: chat home page code

* chatbox ui

* fix: 4.12.2-dev (#5520)

* fix: add empty placeholder; fix app quick status; fix tag and layout

* chore: add tab query for the setting tabs

* chore: use `useConfirm` hook instead of `MyModal`

* remove log

* fix: fix modal padding (#5521)

* perf: manage app

* feat: enhance model provider handling and update icon references (#5493)

* perf: model provider

* sdk package

* refactor: create llm response (#5499)

* feat: add LLM response processing functions, including the creation of stream-based and complete responses

* feat: add volta configuration for node and pnpm versions

* refactor: update LLM response handling and event structure in tool choice logic

* feat: update LLM response structure and integrate with tool choice logic

* refactor: clean up imports and remove unused streamResponse function in chat and toolChoice modules

* refactor: rename answer variable to answerBuffer for clarity in LLM response handling

* feat: enhance LLM response handling with tool options and integrate tools into chat and tool choice logic

* refactor: remove volta configuration from package.json

* refactor: reorganize LLM response types and ensure default values for token counts

* refactor: streamline LLM response handling by consolidating response structure and removing redundant checks

* refactor: enhance LLM response handling by consolidating tool options and streamlining event callbacks

* fix: build error

* refactor: update tool type definitions for consistency in tool handling

* feat: llm request function

* fix: ts

* fix: ts

* fix: ahook ts

* fix: variable name

* update lock

* ts version

* doc

* remove log

* fix: translation type

* perf: workflow status check

* fix: ts

* fix: prompt tool call

* fix: fix missing plugin interact window & make tag draggable (#5527)

* fix: incorrect select quick apps state; filter apps type (#5528)

* fix: usesafe translation

* perf: add quickapp modal

---------

Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com>
Co-authored-by: Ctrlz <143257420+ctrlz526@users.noreply.github.com>
Co-authored-by: francis <zhichengfan18@gmail.com>
2025-08-25 19:19:43 +08:00

238 lines
6.6 KiB
TypeScript

import { type AppSchema } from '@fastgpt/global/core/app/type';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { MongoApp } from './schema';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { encryptSecretValue, storeSecretValue } from '../../common/secret/utils';
import { SystemToolInputTypeEnum } from '@fastgpt/global/core/app/systemTool/constants';
import { type ClientSession } from '../../common/mongo';
import { MongoEvaluation } from './evaluation/evalSchema';
import { removeEvaluationJob } from './evaluation/mq';
import { deleteChatFiles } from '../chat/controller';
import { MongoChatItem } from '../chat/chatItemSchema';
import { MongoChat } from '../chat/chatSchema';
import { MongoOutLink } from '../../support/outLink/schema';
import { MongoOpenApi } from '../../support/openapi/schema';
import { MongoAppVersion } from './version/schema';
import { MongoChatInputGuide } from '../chat/inputGuide/schema';
import { MongoChatFavouriteApp } from '../chat/favouriteApp/schema';
import { MongoChatSetting } from '../chat/setting/schema';
import { MongoResourcePermission } from '../../support/permission/schema';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { removeImageByPath } from '../../common/file/image/controller';
import { mongoSessionRun } from '../../common/mongo/sessionRun';
import { MongoAppLogKeys } from './logs/logkeysSchema';
export const beforeUpdateAppFormat = ({ nodes }: { nodes?: StoreNodeItemType[] }) => {
if (!nodes) return;
nodes.forEach((node) => {
// Format header secret
node.inputs.forEach((input) => {
if (input.key === NodeInputKeyEnum.headerSecret && typeof input.value === 'object') {
input.value = storeSecretValue(input.value);
}
if (input.key === NodeInputKeyEnum.systemInputConfig && typeof input.value === 'object') {
input.inputList?.forEach((inputItem) => {
if (
inputItem.inputType === 'secret' &&
input.value?.type === SystemToolInputTypeEnum.manual &&
input.value?.value
) {
input.value.value[inputItem.key] = encryptSecretValue(input.value.value[inputItem.key]);
}
});
}
});
// Format dataset search
if (node.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
node.inputs.forEach((input) => {
if (input.key === NodeInputKeyEnum.datasetSelectList) {
const val = input.value as undefined | { datasetId: string }[] | { datasetId: string };
if (!val) {
input.value = [];
} else if (Array.isArray(val)) {
// Not rewrite reference value
if (val.length === 2 && val.every((item) => typeof item === 'string')) {
return;
}
input.value = val
.map((dataset: { datasetId: string }) => ({
datasetId: dataset.datasetId
}))
.filter((item) => !!item.datasetId);
} else if (typeof val === 'object' && val !== null) {
input.value = [
{
datasetId: val.datasetId
}
];
}
}
});
}
});
};
/* Get apps */
export async function findAppAndAllChildren({
teamId,
appId,
fields
}: {
teamId: string;
appId: string;
fields?: string;
}): Promise<AppSchema[]> {
const find = async (id: string) => {
const children = await MongoApp.find(
{
teamId,
parentId: id
},
fields
).lean();
let apps = children;
for (const child of children) {
const grandChildrenIds = await find(child._id);
apps = apps.concat(grandChildrenIds);
}
return apps;
};
const [app, childDatasets] = await Promise.all([MongoApp.findById(appId, fields), find(appId)]);
if (!app) {
return Promise.reject('Dataset not found');
}
return [app, ...childDatasets];
}
export const getAppBasicInfoByIds = async ({ teamId, ids }: { teamId: string; ids: string[] }) => {
const apps = await MongoApp.find(
{
teamId,
_id: { $in: ids }
},
'_id name avatar'
).lean();
return apps.map((item) => ({
id: item._id,
name: item.name,
avatar: item.avatar
}));
};
export const onDelOneApp = async ({
teamId,
appId,
session
}: {
teamId: string;
appId: string;
session?: ClientSession;
}) => {
const apps = await findAppAndAllChildren({
teamId,
appId,
fields: '_id avatar'
});
const deletedAppIds = apps
.filter((app) => app.type !== AppTypeEnum.folder)
.map((app) => String(app._id));
// Remove eval job
const evalJobs = await MongoEvaluation.find(
{
appId: { $in: apps.map((app) => app._id) }
},
'_id'
).lean();
await Promise.all(evalJobs.map((evalJob) => removeEvaluationJob(evalJob._id)));
const del = async (session: ClientSession) => {
for await (const app of apps) {
const appId = app._id;
// Chats
await deleteChatFiles({ appId });
await MongoChatItem.deleteMany(
{
appId
},
{ session }
);
await MongoChat.deleteMany(
{
appId
},
{ session }
);
// 删除分享链接
await MongoOutLink.deleteMany({
appId
}).session(session);
// Openapi
await MongoOpenApi.deleteMany({
appId
}).session(session);
// delete version
await MongoAppVersion.deleteMany({
appId
}).session(session);
await MongoChatInputGuide.deleteMany({
appId
}).session(session);
// 删除精选应用记录
await MongoChatFavouriteApp.deleteMany({
teamId,
appId
}).session(session);
// 从快捷应用中移除对应应用
await MongoChatSetting.updateMany(
{ teamId },
{ $pull: { quickAppIds: { id: String(appId) } } }
).session(session);
await MongoResourcePermission.deleteMany({
resourceType: PerResourceTypeEnum.app,
teamId,
resourceId: appId
}).session(session);
await MongoAppLogKeys.deleteMany({
appId
}).session(session);
// delete app
await MongoApp.deleteOne(
{
_id: appId
},
{ session }
);
await removeImageByPath(app.avatar, session);
}
};
if (session) {
await del(session);
return deletedAppIds;
}
await mongoSessionRun(del);
return deletedAppIds;
};