mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-18 09:24:03 +00:00
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:
@@ -361,7 +361,7 @@ const getMultiInput = async ({
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
Tool call, auth add file prompt to question。
|
||||
Guide the LLM to call tool.
|
||||
*/
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user