feature: V4.11.1 (#5350)

* perf: system toolset & mcp (#5200)

* feat: support system toolset

* fix: type

* fix: system tool config

* chore: mcptool config migrate

* refactor: mcp toolset

* fix: fe type error

* fix: type error

* fix: show version

* chore: support extract tool's secretInputConfig out of inputs

* chore: compatible with old version mcp

* chore: adjust

* deps: update dependency @fastgpt-skd/plugin

* fix: version

* fix: some bug (#5316)

* chore: compatible with old version mcp

* fix: version

* fix: compatible bug

* fix: mcp object params

* fix: type error

* chore: update test cases

* chore: remove log

* fix: toolset node name

* optimize app logs sort (#5310)

* log keys config modal

* multiple select

* api

* fontsize

* code

* chatid

* fix build

* fix

* fix component

* change name

* log keys config

* fix

* delete unused

* fix

* perf: log code

* perf: send auth code modal enter press

* fix log (#5328)

* perf: mcp toolset comment

* perf: log ui

* remove log (#5347)

* doc

* fix: action

* remove log

* fix: Table Optimization (#5319)

* feat: table test: 1

* feat: table test: 2

* feat: table test: 3

* feat: table test: 4

* feat: table test : 5 把maxSize改回chunkSize

* feat: table test : 6 都删了,只看maxSize

* feat: table test : 7 恢复初始,接下来删除标签功能

* feat: table test : 8 删除标签功能

* feat: table test : 9 删除标签功能成功

* feat: table test : 10 继续调试,修改trainingStates

* feat: table test : 11 修改第一步

* feat: table test : 12 修改第二步

* feat: table test : 13 修改了HtmlTable2Md

* feat: table test : 14 修改表头分块规则

* feat: table test : 15 前面表格分的太细了

* feat: table test : 16 改着改着表头又不加了

* feat: table test : 17 用CUSTOM_SPLIT_SIGN不行,重新改

* feat: table test : 18 表头仍然还会多加,但现在分块搞的合理了终于

* feat: table test : 19 还是需要搞好表头问题,先保存一下调试情况

* feat: table test : 20 调试结束,看一下replace有没有问题,没问题就pr

* feat: table test : 21 先把注释删了

* feat: table test : 21 注释replace都改了,下面切main分支看看情况

* feat: table test : 22 修改旧文件

* feat: table test : 23 修改测试文件

* feat: table test : 24 xlsx表格处理

* feat: table test : 25 刚才没保存先com了

* feat: table test : 26 fix

* feat: table test : 27 先com一版调试

* feat: table test : 28 试试放format2csv里

* feat: table test : 29 xlsx解决

* feat: table test : 30 tablesplit解决

* feat: table test : 31

* feat: table test : 32

* perf: table split

* perf: mcp old version compatibility (#5342)

* fix: system-tool secret inputs

* fix: rewrite runtime node i18n for system tool

* perf: mcp old version compatibility

* fix: splitPluginId

* fix: old mcp toolId

* fix: filter secret key

* feat: support system toolset activation

* chore: remove log

* perf: mcp update

* perf: rewrite toolset

* fix:delete variable id (#5335)

* perf: variable update

* fix: multiple select ui

* perf: model config move to plugin

* fix: var conflit

* perf: variable checker

* Avoid empty number

* update doc time

* fix: test

* fix: mcp object

* update count app

* update count app

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: heheer <zhiyu44@qq.com>
Co-authored-by: colnii <1286949794@qq.com>
Co-authored-by: dreamer6680 <1468683855@qq.com>
This commit is contained in:
Archer
2025-08-01 16:08:20 +08:00
committed by GitHub
parent e0c21a949c
commit e25d7efb5b
143 changed files with 2596 additions and 4177 deletions

View File

@@ -361,7 +361,7 @@ const getMultiInput = async ({
};
};
/*
/*
Tool call auth add file prompt to question。
Guide the LLM to call tool.
*/

View File

@@ -10,14 +10,16 @@ import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { MCPClient } from '../../../app/mcp';
import { getSecretValue } from '../../../../common/secret/utils';
import type { McpToolDataType } from '@fastgpt/global/core/app/mcpTools/type';
import { runSystemTool } from '../../../app/tool/api';
import { APIRunSystemTool } from '../../../app/tool/api';
import { MongoSystemPlugin } from '../../../app/plugin/systemPluginSchema';
import { SystemToolInputTypeEnum } from '@fastgpt/global/core/app/systemTool/constants';
import type { StoreSecretValueType } from '@fastgpt/global/common/secret/type';
import { getSystemPluginById } from '../../../app/plugin/controller';
import { getSystemToolById } from '../../../app/plugin/controller';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
import { pushTrack } from '../../../../common/middle/tracks/utils';
import { getNodeErrResponse } from '../utils';
import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils';
import { getAppVersionById } from '../../../../core/app/version/controller';
type SystemInputConfigType = {
type: SystemToolInputTypeEnum;
@@ -52,8 +54,8 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
try {
// run system tool
if (systemToolId) {
const tool = await getSystemPluginById(systemToolId);
if (toolConfig?.systemTool?.toolId) {
const tool = await getSystemToolById(toolConfig.systemTool!.toolId);
const inputConfigParams = await (async () => {
switch (params.system_input_config?.type) {
@@ -82,7 +84,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
const formatToolId = tool.id.split('-')[1];
const res = await runSystemTool({
const res = await APIRunSystemTool({
toolId: formatToolId,
inputs,
systemVar: {
@@ -112,6 +114,7 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
}
}
});
let result = res.output || {};
if (res.error) {
@@ -175,8 +178,33 @@ export const dispatchRunTool = async (props: RunToolProps): Promise<RunToolRespo
}
]
};
} else if (toolConfig?.mcpTool?.toolId) {
const { pluginId } = splitCombinePluginId(toolConfig.mcpTool.toolId);
const [parentId, toolName] = pluginId.split('/');
const tool = await getAppVersionById({
appId: parentId,
versionId: version
});
const { headerSecret, url } =
tool.nodes[0].toolConfig?.mcpToolSet ?? tool.nodes[0].inputs[0].value;
const mcpClient = new MCPClient({
url,
headers: getSecretValue({
storeSecret: headerSecret
})
});
const result = await mcpClient.toolCall(toolName, params);
return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
toolRes: result,
moduleLogo: avatar
},
[DispatchNodeResponseKeyEnum.toolResponses]: result
};
} else {
// mcp tool
// mcp tool (old version compatible)
const { toolData, system_toolData, ...restParams } = params;
const { name: toolName, url, headerSecret } = toolData || system_toolData;

View File

@@ -152,7 +152,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
} = data;
const startTime = Date.now();
rewriteRuntimeWorkFlow(runtimeNodes, runtimeEdges);
await rewriteRuntimeWorkFlow({ nodes: runtimeNodes, edges: runtimeEdges });
// 初始化深度和自动增加深度,避免无限嵌套
if (!props.workflowDispatchDeep) {
@@ -212,11 +212,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
sendStreamTimerSign();
}
// Add system variables
// Get default variables
variables = {
...getSystemVariable(data),
...externalProvider.externalWorkflowVariables,
...variables
...getSystemVariables(data)
};
}
@@ -846,23 +845,35 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
}
/* get system variable */
const getSystemVariable = ({
const getSystemVariables = ({
timezone,
runningAppInfo,
chatId,
responseChatItemId,
histories = [],
uid,
chatConfig
chatConfig,
variables
}: Props): SystemVariablesType => {
const variables = chatConfig?.variables || [];
const variablesMap = variables.reduce<Record<string, any>>((acc, item) => {
acc[item.key] = valueTypeFormat(item.defaultValue, item.valueType);
// Get global variables(Label -> key; Key -> key)
const globalVariables = chatConfig?.variables || [];
const variablesMap = globalVariables.reduce<Record<string, any>>((acc, item) => {
// API
if (variables[item.label] !== undefined) {
acc[item.key] = valueTypeFormat(variables[item.label], item.valueType);
}
// Web
else if (variables[item.key] !== undefined) {
acc[item.key] = valueTypeFormat(variables[item.key], item.valueType);
} else {
acc[item.key] = valueTypeFormat(item.defaultValue, item.valueType);
}
return acc;
}, {});
return {
...variablesMap,
// System var:
userId: uid,
appId: String(runningAppInfo.id),
chatId,

View File

@@ -1,4 +1,7 @@
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
import {
getPluginInputsFromStoreNodes,
splitCombinePluginId
} from '@fastgpt/global/core/app/plugin/utils';
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
@@ -15,9 +18,8 @@ import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { computedPluginUsage } from '../../../app/plugin/utils';
import { filterSystemVariables, getNodeErrResponse } from '../utils';
import { getPluginRunUserQuery } from '@fastgpt/global/core/workflow/utils';
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { getChildAppRuntimeById, splitCombinePluginId } from '../../../app/plugin/controller';
import type { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { getChildAppRuntimeById } from '../../../app/plugin/controller';
import { dispatchWorkFlow } from '../index';
import { getUserChatInfoAndAuthTeamPoints } from '../../../../support/permission/auth/team';
import { dispatchRunTool } from '../child/runTool';
@@ -66,7 +68,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
});
}
/*
/*
1. Team app
2. Admin selected system tool
*/
@@ -79,7 +81,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
per: ReadPermissionVal
});
plugin = await getChildAppRuntimeById(pluginId, version);
plugin = await getChildAppRuntimeById({ id: pluginId, versionId: version });
const outputFilterMap =
plugin.nodes

View File

@@ -1,7 +1,7 @@
import { getErrText } from '@fastgpt/global/common/error/utils';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import {
type RuntimeEdgeItemType,
type RuntimeNodeItemType,
@@ -17,7 +17,12 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
import { type SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import { getMCPToolRuntimeNode } from '@fastgpt/global/core/app/mcpTools/utils';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import type { McpToolSetDataType } from '@fastgpt/global/core/app/mcpTools/type';
import {
getSystemPluginRuntimeNodeById,
getSystemTools
} from '../../../core/app/plugin/controller';
import { MongoApp } from '../../../core/app/schema';
import { getMCPChildren } from '../../../core/app/mcp';
export const getWorkflowResponseWrite = ({
res,
@@ -151,10 +156,19 @@ export const formatHttpError = (error: any) => {
};
};
export const rewriteRuntimeWorkFlow = (
nodes: RuntimeNodeItemType[],
edges: RuntimeEdgeItemType[]
) => {
/**
* ToolSet node will be replaced by Children Tool Nodes.
* @param nodes
* @param edges
* @returns
*/
export const rewriteRuntimeWorkFlow = async ({
nodes,
edges
}: {
nodes: RuntimeNodeItemType[];
edges: RuntimeEdgeItemType[];
}) => {
const toolSetNodes = nodes.filter((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);
if (toolSetNodes.length === 0) {
@@ -165,35 +179,63 @@ export const rewriteRuntimeWorkFlow = (
for (const toolSetNode of toolSetNodes) {
nodeIdsToRemove.add(toolSetNode.nodeId);
const toolSetValue = toolSetNode.inputs[0]?.value as McpToolSetDataType | undefined;
if (!toolSetValue) continue;
const toolList = toolSetValue.toolList;
const url = toolSetValue.url;
const headerSecret = toolSetValue.headerSecret;
const systemToolId = toolSetNode.toolConfig?.systemToolSet?.toolId;
const mcpToolsetVal = toolSetNode.toolConfig?.mcpToolSet ?? toolSetNode.inputs[0].value;
const incomingEdges = edges.filter((edge) => edge.target === toolSetNode.nodeId);
for (const tool of toolList) {
const newToolNode = getMCPToolRuntimeNode({
avatar: toolSetNode.avatar,
tool,
url,
headerSecret
});
nodes.push({ ...newToolNode, name: `${toolSetNode.name} / ${tool.name}` });
const pushEdges = (nodeId: string) => {
for (const inEdge of incomingEdges) {
edges.push({
source: inEdge.source,
target: newToolNode.nodeId,
target: nodeId,
sourceHandle: inEdge.sourceHandle,
targetHandle: 'selectedTools',
status: inEdge.status
});
}
};
// systemTool
if (systemToolId) {
const toolsetInputConfig = toolSetNode.inputs.find(
(item) => item.key === NodeInputKeyEnum.systemInputConfig
);
const tools = await getSystemTools();
const children = tools.filter((item) => item.parentId === systemToolId);
for (const child of children) {
const toolListItem = toolSetNode.toolConfig?.systemToolSet?.toolList.find(
(item) => item.toolId === child.id
)!;
const newNode = await getSystemPluginRuntimeNodeById({
pluginId: child.id,
name: toolListItem?.name,
intro: toolListItem?.description
});
const newNodeInputConfig = newNode.inputs.find(
(item) => item.key === NodeInputKeyEnum.systemInputConfig
);
if (newNodeInputConfig) {
newNodeInputConfig.value = toolsetInputConfig?.value;
}
nodes.push(newNode);
pushEdges(newNode.nodeId);
}
} else if (mcpToolsetVal) {
const app = await MongoApp.findOne({ _id: toolSetNode.pluginId }).lean();
if (!app) continue;
const toolList = await getMCPChildren(app);
for (const tool of toolList) {
const newToolNode = getMCPToolRuntimeNode({
avatar: toolSetNode.avatar,
tool,
// New ?? Old
parentId: mcpToolsetVal.toolId ?? toolSetNode.pluginId
});
nodes.push({ ...newToolNode, name: `${toolSetNode.name}/${tool.name}` });
pushEdges(newToolNode.nodeId);
}
}
}