Commit 1c8a47cf by Archer Committed by GitHub

fix: adapt systemTool (#6817)

parent 0c39c7ba
---
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.
---
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
---
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. 修复选中系统组件为系统工具异常。
## ⚙️ 优化
...@@ -118,6 +118,8 @@ description: FastGPT Toc ...@@ -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/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/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/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/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/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) - [/en/docs/self-host/upgrading/4-14/4144](/en/docs/self-host/upgrading/4-14/4144)
......
...@@ -118,6 +118,7 @@ description: FastGPT 文档目录 ...@@ -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/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/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/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/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/4143](/docs/self-host/upgrading/4-14/4143)
- [/docs/self-host/upgrading/4-14/4144](/docs/self-host/upgrading/4-14/4144) - [/docs/self-host/upgrading/4-14/4144](/docs/self-host/upgrading/4-14/4144)
......
...@@ -251,7 +251,7 @@ ...@@ -251,7 +251,7 @@
"document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00", "document/content/docs/self-host/upgrading/4-14/41481.mdx": "2026-03-09T17:39:53+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00", "document/content/docs/self-host/upgrading/4-14/4149.en.mdx": "2026-03-23T12:17:04+08:00",
"document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-04-07T21:01:52+08:00", "document/content/docs/self-host/upgrading/4-14/4149.mdx": "2026-04-07T21:01:52+08:00",
"document/content/docs/self-host/upgrading/4-15/4150.mdx": "2026-04-23T18:02:28+08:00", "document/content/docs/self-host/upgrading/4-15/4150.mdx": "2026-04-24T22:01:29+08:00",
"document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.en.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/40.mdx": "2026-03-03T17:39:47+08:00",
"document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00", "document/content/docs/self-host/upgrading/outdated/41.en.mdx": "2026-03-03T17:39:47+08:00",
......
...@@ -446,7 +446,6 @@ export async function getChildAppPreviewNode({ ...@@ -446,7 +446,6 @@ export async function getChildAppPreviewNode({
} }
// http tool // http tool
else if (source === AppToolSourceEnum.http) { else if (source === AppToolSourceEnum.http) {
console.log('pluginId', pluginId);
const [parentId, ...rest] = pluginId.split('/'); const [parentId, ...rest] = pluginId.split('/');
const toolName = rest.join('/'); const toolName = rest.join('/');
const toolset = await MongoApp.findById(parentId).lean(); const toolset = await MongoApp.findById(parentId).lean();
...@@ -718,10 +717,14 @@ export const refreshSystemTools = async (): Promise<AppToolTemplateItemType[]> = ...@@ -718,10 +717,14 @@ export const refreshSystemTools = async (): Promise<AppToolTemplateItemType[]> =
return concatTools; 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> => { export const getSystemToolById = async (toolId: string): Promise<AppToolTemplateItemType> => {
const tools = await getSystemTools(); 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) { if (tool) {
return cloneDeep(tool); return cloneDeep(tool);
} }
......
...@@ -87,6 +87,7 @@ export async function rewriteAppWorkflowToDetail({ ...@@ -87,6 +87,7 @@ export async function rewriteAppWorkflowToDetail({
const result = await loadToolNode({ id: node.pluginId, versionId: node.version }); const result = await loadToolNode({ id: node.pluginId, versionId: node.version });
if (result.success) { if (result.success) {
const preview = result.data!; const preview = result.data!;
node.avatar = preview.avatar ?? node.avatar;
node.isFolder = preview.isFolder; node.isFolder = preview.isFolder;
node.pluginData = { node.pluginData = {
name: preview.name, name: preview.name,
......
...@@ -50,7 +50,6 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo ...@@ -50,7 +50,6 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
runningAppInfo, runningAppInfo,
variables, variables,
workflowStreamResponse, workflowStreamResponse,
node: { name, avatar, toolConfig, version, catchError } node: { name, avatar, toolConfig, version, catchError }
} = props; } = props;
......
...@@ -30,6 +30,7 @@ import type { AppToolRuntimeType } from '@fastgpt/global/core/app/tool/type'; ...@@ -30,6 +30,7 @@ import type { AppToolRuntimeType } from '@fastgpt/global/core/app/tool/type';
import { anyValueDecrypt } from '../../../../common/secret/utils'; import { anyValueDecrypt } from '../../../../common/secret/utils';
import { getAppVersionById } from '../../../app/version/controller'; import { getAppVersionById } from '../../../app/version/controller';
import { parseI18nString } from '@fastgpt/global/common/i18n/utils'; import { parseI18nString } from '@fastgpt/global/common/i18n/utils';
import { getSystemToolByIdAndVersionId } from '../../../app/tool/controller';
type RunPluginProps = ModuleDispatchProps<{ type RunPluginProps = ModuleDispatchProps<{
[NodeInputKeyEnum.forbidStream]?: boolean; [NodeInputKeyEnum.forbidStream]?: boolean;
...@@ -59,7 +60,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi ...@@ -59,7 +60,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
try { try {
// Adapt <= 4.10 system tool // Adapt <= 4.10 system tool
const { source, pluginId: formatPluginId } = splitCombineToolId(pluginId); const { source, pluginId: appId } = splitCombineToolId(pluginId);
if (source === AppToolSourceEnum.systemTool) { if (source === AppToolSourceEnum.systemTool) {
return await dispatchRunTool({ return await dispatchRunTool({
...props, ...props,
...@@ -67,7 +68,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi ...@@ -67,7 +68,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
...props.node, ...props.node,
toolConfig: { toolConfig: {
systemTool: { systemTool: {
toolId: formatPluginId toolId: pluginId
} }
} }
} }
...@@ -79,37 +80,59 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi ...@@ -79,37 +80,59 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
} }
/* /*
1. Team app 1. Team app (personal): 走 team 权限校验
2. Admin selected system tool 2. Admin selected system tool (commercial): 系统级工具,不做用户态权限校验
*/ */
const { files } = chatValue2RuntimePrompt(query); const { files } = chatValue2RuntimePrompt(query);
// auth workflowTool // 仅在 personal 分支需要 toolData 来判断 pluginDetail 的可见性
const toolData = await authWorkflowToolByTmbId({ const toolData =
appId: formatPluginId, source === AppToolSourceEnum.personal
tmbId: runningAppInfo.tmbId, ? await authWorkflowToolByTmbId({
per: ReadPermissionVal appId,
}); tmbId: runningAppInfo.tmbId,
per: ReadPermissionVal
})
: undefined;
const toolVersion = await getAppVersionById({ if (source === AppToolSourceEnum.personal && toolData) {
appId: toolData._id, const toolVersion = await getAppVersionById({
versionId: version, appId: toolData._id,
app: toolData versionId: version,
}); app: toolData
});
workflowTool = { workflowTool = {
id: String(toolData._id), id: String(toolData._id),
teamId: toolData.teamId, teamId: toolData.teamId,
tmbId: toolData.tmbId, tmbId: toolData.tmbId,
name: parseI18nString(toolData.name, props.lang), name: parseI18nString(toolData.name, props.lang),
avatar: toolData.avatar || '', avatar: toolData.avatar || '',
showStatus: true, showStatus: true,
currentCost: 0, currentCost: 0,
systemKeyCost: 0, systemKeyCost: 0,
nodes: toolVersion.nodes, nodes: toolVersion.nodes,
edges: toolVersion.edges, edges: toolVersion.edges,
hasTokenFee: false 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 = const outputFilterMap =
workflowTool.nodes workflowTool.nodes
......
...@@ -293,8 +293,7 @@ const MySelect = <T = any,>( ...@@ -293,8 +293,7 @@ const MySelect = <T = any,>(
<MenuList <MenuList
ref={MenuListRef} ref={MenuListRef}
className={props.className} className={props.className}
minW={0} minW={(() => {
w={(() => {
const w = ButtonRef.current?.clientWidth; const w = ButtonRef.current?.clientWidth;
if (w) { if (w) {
return `${w}px !important`; return `${w}px !important`;
...@@ -303,6 +302,7 @@ const MySelect = <T = any,>( ...@@ -303,6 +302,7 @@ const MySelect = <T = any,>(
? width.map((item) => `${item} !important`) ? width.map((item) => `${item} !important`)
: `${width} !important`; : `${width} !important`;
})()} })()}
w={'max-content'}
px={'6px'} px={'6px'}
py={'6px'} py={'6px'}
border={'1px solid #fff'} border={'1px solid #fff'}
......
...@@ -655,6 +655,15 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt ...@@ -655,6 +655,15 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
); );
}, [node.isLatestVersion, node?.version, node?.versionLabel, t]); }, [node.isLatestVersion, node?.version, node?.versionLabel, t]);
const ScrollDataWrapper = useCallback(
(props: { children: React.ReactNode }) => (
<ScrollData minH={'100px'} maxH={'40vh'}>
{props.children}
</ScrollData>
),
[ScrollData]
);
return ( return (
<MySelect <MySelect
className="nowheel" className="nowheel"
...@@ -667,11 +676,7 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt ...@@ -667,11 +676,7 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
variant={'whitePrimaryOutline'} variant={'whitePrimaryOutline'}
size={'sm'} size={'sm'}
list={renderVersionList} list={renderVersionList}
ScrollData={(props) => ( ScrollData={ScrollDataWrapper}
<ScrollData minH={'100px'} maxH={'40vh'}>
{props.children}
</ScrollData>
)}
valueLabel={valueLabel} valueLabel={valueLabel}
/> />
); );
......
...@@ -150,30 +150,28 @@ export const useChatTest = ({ ...@@ -150,30 +150,28 @@ export const useChatTest = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [variableList]); }, [variableList]);
const CustomChatContainer = useCallback( const CustomChatContainer = useMemoizedFn(() =>
() => appDetail.type === AppTypeEnum.workflowTool ? (
appDetail.type === AppTypeEnum.workflowTool ? ( <Box p={5} pb={16}>
<Box p={5} pb={16}> <PluginRunBox
<PluginRunBox
appId={appId}
chatId={chatId}
onNewChat={restartChat}
onStartChat={startChat}
runtimeFileSelectConfig={chatConfig.fileSelectConfig}
/>
</Box>
) : (
<ChatBox
isReady={isReady}
appId={appId} appId={appId}
chatId={chatId} chatId={chatId}
showMarkIcon onNewChat={restartChat}
chatType={ChatTypeEnum.test}
enableAutoResume
onStartChat={startChat} onStartChat={startChat}
runtimeFileSelectConfig={chatConfig.fileSelectConfig}
/> />
), </Box>
[appDetail.type, appId, chatId, isReady, restartChat, startChat, chatConfig] ) : (
<ChatBox
isReady={isReady}
appId={appId}
chatId={chatId}
showMarkIcon
chatType={ChatTypeEnum.test}
enableAutoResume
onStartChat={startChat}
/>
)
); );
return { return {
......
...@@ -6,7 +6,10 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next'; ...@@ -6,7 +6,10 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; 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 { AppToolSourceEnum } from '@fastgpt/global/core/app/tool/constants';
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin'; import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
import { Types } from '@fastgpt/service/common/mongo'; import { Types } from '@fastgpt/service/common/mongo';
...@@ -35,7 +38,7 @@ async function handler( ...@@ -35,7 +38,7 @@ async function handler(
}; };
} }
const { source, pluginId: formatPluginId, authAppId } = splitCombineToolId(pluginId); const { source, authAppId } = splitCombineToolId(pluginId);
// System tool plugin // System tool plugin
if (source === AppToolSourceEnum.systemTool) { if (source === AppToolSourceEnum.systemTool) {
...@@ -62,7 +65,13 @@ async function handler( ...@@ -62,7 +65,13 @@ async function handler(
}); });
return app._id; return app._id;
} else { } else {
return formatPluginId; // Get appId from pluginId
const tool = await getSystemToolById(pluginId);
if (!tool.associatedPluginId) {
return Promise.reject(PluginErrEnum.unExist);
}
return tool.associatedPluginId;
} }
})(); })();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment