fix: adapt systemTool (#6817)

This commit is contained in:
Archer
2026-04-25 21:37:55 +08:00
committed by archer
parent 32438be5ce
commit 9bbdaab0f7
14 changed files with 163 additions and 63 deletions
@@ -0,0 +1,21 @@
---
title: 'V4.14.14'
description: 'FastGPT V4.14.14 Release Notes'
---
## Upgrade Guide
### 1. Update image tags
- Update fastgpt-app (FastGPT main service) image tag to: v4.14.14
- Update fastgpt-pro (FastGPT commercial) image tag to: v4.14.14
## 🐛 Bug Fixes
## ⚙️ Optimizations
1. Personal WeChat publishing channel: optimized polling strategy by decoupling pull from reply, preventing blocking under high message volume.
2. Added environment variable `WECHAT_CHANNEL_CONCURRENCY` (default 1000) to control the WeChat channel poll worker concurrency. Recommended to set ≥ peak online channel count.
3. Improved internal network address detection.
4. Added compatibility for DeepSeek tool calling combined with thinking mode to avoid 400 errors from the API.
@@ -0,0 +1,18 @@
---
title: 'V4.14.14'
description: 'FastGPT V4.14.14 Release Notes'
---
## Upgrade Guide
### 1. Update image tags
- Update fastgpt-app (FastGPT main service) image tag to: v4.14.15
- Update fastgpt-pro (FastGPT commercial) image tag to: v4.14.15
## 🐛 Bug Fixes
1. Fixed compatibility for legacy system tools.
2. Fixed an issue where selecting a system component as a system tool caused errors.
## ⚙️ Optimizations
@@ -0,0 +1,20 @@
---
title: 'V4.14.15'
description: 'FastGPT V4.14.15 更新说明'
---
## 升级指南
建议 4.14.8 以后的用户直接升级到该版本使用。
### 1. 更新镜像 tag
- 更新 fastgpt-app(fastgpt 主服务) 镜像 tag: v4.14.15
- 更新 fastgpt-pro(fastgpt 商业版) 镜像 tag: v4.14.15
## 🐛 修复
1. 修复兼容旧版的系统工具。
2. 修复选中系统组件为系统工具异常。
## ⚙️ 优化
+2
View File
@@ -118,6 +118,8 @@ description: FastGPT Toc
- [/en/docs/self-host/upgrading/4-14/41411](/en/docs/self-host/upgrading/4-14/41411)
- [/en/docs/self-host/upgrading/4-14/41412](/en/docs/self-host/upgrading/4-14/41412)
- [/en/docs/self-host/upgrading/4-14/41413](/en/docs/self-host/upgrading/4-14/41413)
- [/en/docs/self-host/upgrading/4-14/41414](/en/docs/self-host/upgrading/4-14/41414)
- [/en/docs/self-host/upgrading/4-14/41415](/en/docs/self-host/upgrading/4-14/41415)
- [/en/docs/self-host/upgrading/4-14/4142](/en/docs/self-host/upgrading/4-14/4142)
- [/en/docs/self-host/upgrading/4-14/4143](/en/docs/self-host/upgrading/4-14/4143)
- [/en/docs/self-host/upgrading/4-14/4144](/en/docs/self-host/upgrading/4-14/4144)
+1
View File
@@ -118,6 +118,7 @@ description: FastGPT 文档目录
- [/docs/self-host/upgrading/4-14/41412](/docs/self-host/upgrading/4-14/41412)
- [/docs/self-host/upgrading/4-14/41413](/docs/self-host/upgrading/4-14/41413)
- [/docs/self-host/upgrading/4-14/41414](/docs/self-host/upgrading/4-14/41414)
- [/docs/self-host/upgrading/4-14/41415](/docs/self-host/upgrading/4-14/41415)
- [/docs/self-host/upgrading/4-14/4142](/docs/self-host/upgrading/4-14/4142)
- [/docs/self-host/upgrading/4-14/4143](/docs/self-host/upgrading/4-14/4143)
- [/docs/self-host/upgrading/4-14/4144](/docs/self-host/upgrading/4-14/4144)
+1 -1
View File
@@ -230,7 +230,7 @@
"document/content/docs/self-host/upgrading/4-14/41412.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41413.en.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41413.mdx": "2026-04-21T23:04:26+08:00",
"document/content/docs/self-host/upgrading/4-14/41414.mdx": "2026-04-22T23:35:11+08:00",
"document/content/docs/self-host/upgrading/4-14/41414.mdx": "2026-04-24T18:34:55+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
+6 -3
View File
@@ -446,7 +446,6 @@ export async function getChildAppPreviewNode({
}
// http tool
else if (source === AppToolSourceEnum.http) {
console.log('pluginId', pluginId);
const [parentId, ...rest] = pluginId.split('/');
const toolName = rest.join('/');
const toolset = await MongoApp.findById(parentId).lean();
@@ -718,10 +717,14 @@ export const refreshSystemTools = async (): Promise<AppToolTemplateItemType[]> =
return concatTools;
};
// toolId: systemTool-id, commercial-id
// toolId: systemTool-id, commercial-id, community-id(deprecated, 映射为 systemTool-id)
export const getSystemToolById = async (toolId: string): Promise<AppToolTemplateItemType> => {
const tools = await getSystemTools();
const tool = tools.find((item) => item.id === toolId);
// 兼容旧的 community- 前缀,统一归一化为 systemTool-
const normalizedId = toolId.startsWith(`${AppToolSourceEnum.community}-`)
? `${AppToolSourceEnum.systemTool}-${toolId.slice(AppToolSourceEnum.community.length + 1)}`
: toolId;
const tool = tools.find((item) => item.id === normalizedId);
if (tool) {
return cloneDeep(tool);
}
+1
View File
@@ -87,6 +87,7 @@ export async function rewriteAppWorkflowToDetail({
const result = await loadToolNode({ id: node.pluginId, versionId: node.version });
if (result.success) {
const preview = result.data!;
node.avatar = preview.avatar ?? node.avatar;
node.isFolder = preview.isFolder;
node.pluginData = {
name: preview.name,
@@ -50,7 +50,6 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
runningAppInfo,
variables,
workflowStreamResponse,
node: { name, avatar, toolConfig, version, catchError }
} = props;
@@ -30,6 +30,7 @@ import type { AppToolRuntimeType } from '@fastgpt/global/core/app/tool/type';
import { anyValueDecrypt } from '../../../../common/secret/utils';
import { getAppVersionById } from '../../../app/version/controller';
import { parseI18nString } from '@fastgpt/global/common/i18n/utils';
import { getSystemToolByIdAndVersionId } from '../../../app/tool/controller';
type RunPluginProps = ModuleDispatchProps<{
[NodeInputKeyEnum.forbidStream]?: boolean;
@@ -59,7 +60,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
try {
// Adapt <= 4.10 system tool
const { source, pluginId: formatPluginId } = splitCombineToolId(pluginId);
const { source, pluginId: appId } = splitCombineToolId(pluginId);
if (source === AppToolSourceEnum.systemTool) {
return await dispatchRunTool({
...props,
@@ -67,7 +68,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
...props.node,
toolConfig: {
systemTool: {
toolId: formatPluginId
toolId: pluginId
}
}
}
@@ -79,37 +80,59 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
}
/*
1. Team app
2. Admin selected system tool
1. Team app (personal): 走 team 权限校验
2. Admin selected system tool (commercial): 系统级工具,不做用户态权限校验
*/
const { files } = chatValue2RuntimePrompt(query);
// auth workflowTool
const toolData = await authWorkflowToolByTmbId({
appId: formatPluginId,
tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
});
// 仅在 personal 分支需要 toolData 来判断 pluginDetail 的可见性
const toolData =
source === AppToolSourceEnum.personal
? await authWorkflowToolByTmbId({
appId,
tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
})
: undefined;
const toolVersion = await getAppVersionById({
appId: toolData._id,
versionId: version,
app: toolData
});
if (source === AppToolSourceEnum.personal && toolData) {
const toolVersion = await getAppVersionById({
appId: toolData._id,
versionId: version,
app: toolData
});
workflowTool = {
id: String(toolData._id),
teamId: toolData.teamId,
tmbId: toolData.tmbId,
name: parseI18nString(toolData.name, props.lang),
avatar: toolData.avatar || '',
showStatus: true,
currentCost: 0,
systemKeyCost: 0,
nodes: toolVersion.nodes,
edges: toolVersion.edges,
hasTokenFee: false
};
workflowTool = {
id: String(toolData._id),
teamId: toolData.teamId,
tmbId: toolData.tmbId,
name: parseI18nString(toolData.name, props.lang),
avatar: toolData.avatar || '',
showStatus: true,
currentCost: 0,
systemKeyCost: 0,
nodes: toolVersion.nodes,
edges: toolVersion.edges,
hasTokenFee: false
};
} else {
// commercial: 通过系统工具加载(内部会解析 associatedPluginId 对应的 app 版本)
const systemTool = await getSystemToolByIdAndVersionId(pluginId, version);
workflowTool = {
id: systemTool.id,
teamId: systemTool.teamId,
tmbId: systemTool.tmbId,
name: parseI18nString(systemTool.name, props.lang),
avatar: systemTool.avatar || '',
showStatus: true,
currentCost: systemTool.currentCost ?? 0,
systemKeyCost: systemTool.systemKeyCost ?? 0,
nodes: systemTool.workflow.nodes,
edges: systemTool.workflow.edges,
hasTokenFee: !!systemTool.hasTokenFee
};
}
const outputFilterMap =
workflowTool.nodes
@@ -290,8 +290,7 @@ const MySelect = <T = any,>(
<MenuList
ref={MenuListRef}
className={props.className}
minW={0}
w={(() => {
minW={(() => {
const w = ButtonRef.current?.clientWidth;
if (w) {
return `${w}px !important`;
@@ -300,6 +299,7 @@ const MySelect = <T = any,>(
? width.map((item) => `${item} !important`)
: `${width} !important`;
})()}
w={'max-content'}
px={'6px'}
py={'6px'}
border={'1px solid #fff'}
@@ -655,6 +655,15 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
);
}, [node.isLatestVersion, node?.version, node?.versionLabel, t]);
const ScrollDataWrapper = useCallback(
(props: { children: React.ReactNode }) => (
<ScrollData minH={'100px'} maxH={'40vh'}>
{props.children}
</ScrollData>
),
[ScrollData]
);
return (
<MySelect
className="nowheel"
@@ -667,11 +676,7 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
variant={'whitePrimaryOutline'}
size={'sm'}
list={renderVersionList}
ScrollData={(props) => (
<ScrollData minH={'100px'} maxH={'40vh'}>
{props.children}
</ScrollData>
)}
ScrollData={ScrollDataWrapper}
valueLabel={valueLabel}
/>
);
@@ -150,30 +150,28 @@ export const useChatTest = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [variableList]);
const CustomChatContainer = useCallback(
() =>
appDetail.type === AppTypeEnum.workflowTool ? (
<Box p={5} pb={16}>
<PluginRunBox
appId={appId}
chatId={chatId}
onNewChat={restartChat}
onStartChat={startChat}
runtimeFileSelectConfig={chatConfig.fileSelectConfig}
/>
</Box>
) : (
<ChatBox
isReady={isReady}
const CustomChatContainer = useMemoizedFn(() =>
appDetail.type === AppTypeEnum.workflowTool ? (
<Box p={5} pb={16}>
<PluginRunBox
appId={appId}
chatId={chatId}
showMarkIcon
chatType={ChatTypeEnum.test}
enableAutoResume
onNewChat={restartChat}
onStartChat={startChat}
runtimeFileSelectConfig={chatConfig.fileSelectConfig}
/>
),
[appDetail.type, appId, chatId, isReady, restartChat, startChat, chatConfig]
</Box>
) : (
<ChatBox
isReady={isReady}
appId={appId}
chatId={chatId}
showMarkIcon
chatType={ChatTypeEnum.test}
enableAutoResume
onStartChat={startChat}
/>
)
);
return {
@@ -6,7 +6,10 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { getSystemToolByIdAndVersionId } from '@fastgpt/service/core/app/tool/controller';
import {
getSystemToolById,
getSystemToolByIdAndVersionId
} from '@fastgpt/service/core/app/tool/controller';
import { AppToolSourceEnum } from '@fastgpt/global/core/app/tool/constants';
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
import { Types } from '@fastgpt/service/common/mongo';
@@ -35,7 +38,7 @@ async function handler(
};
}
const { source, pluginId: formatPluginId, authAppId } = splitCombineToolId(pluginId);
const { source, authAppId } = splitCombineToolId(pluginId);
// System tool plugin
if (source === AppToolSourceEnum.systemTool) {
@@ -62,7 +65,13 @@ async function handler(
});
return app._id;
} else {
return formatPluginId;
// Get appId from pluginId
const tool = await getSystemToolById(pluginId);
if (!tool.associatedPluginId) {
return Promise.reject(PluginErrEnum.unExist);
}
return tool.associatedPluginId;
}
})();