mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-21 11:43:56 +00:00
V4.9.6 feature (#4565)
* Dashboard submenu (#4545) * add app submenu (#4452) * add app submenu * fix * width & i18n * optimize submenu code (#4515) * optimize submenu code * fix * fix * fix * fix ts * perf: dashboard sub menu * doc --------- Co-authored-by: heheer <heheer@sealos.io> * feat: value format test * doc * Mcp export (#4555) * feat: mcp server * feat: mcp server * feat: mcp server build * update doc * perf: path selector (#4556) * perf: path selector * fix: docker file path * perf: add image endpoint to dataset search (#4557) * perf: add image endpoint to dataset search * fix: mcp_server url * human in loop (#4558) * Support interactive nodes for loops, and enhance the function of merging nested and loop node history messages. (#4552) * feat: add LoopInteractive definition * feat: Support LoopInteractive type and update related logic * fix: Refactor loop handling logic and improve output value initialization * feat: Add mergeSignId to dispatchLoop and dispatchRunAppNode responses * feat: Enhance mergeChatResponseData to recursively merge plugin details and improve response handling * refactor: Remove redundant comments in mergeChatResponseData for clarity * perf: loop interactive * perf: human in loop --------- Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com> * mcp server ui * integrate mcp (#4549) * integrate mcp * delete unused code * fix ts * bug fix * fix * support whole mcp tools * add try catch * fix * fix * fix ts * fix test * fix ts * fix: interactive in v1 completions * doc * fix: router path * fix mcp integrate (#4563) * fix mcp integrate * fix ui * fix: mcp ux * feat: mcp call title * remove repeat loading * fix mcp tools avatar (#4564) * fix * fix avatar * fix update version * update doc * fix: value format * close server and remove cache * perf: avatar --------- Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Theresa <63280168+sd0ric4@users.noreply.github.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { ErrType } from '../errorCode';
|
||||
const startCode = 507000;
|
||||
export enum CommonErrEnum {
|
||||
invalidParams = 'invalidParams',
|
||||
invalidResource = 'invalidResource',
|
||||
fileNotFound = 'fileNotFound',
|
||||
unAuthFile = 'unAuthFile',
|
||||
missingParams = 'missingParams',
|
||||
@@ -15,6 +16,10 @@ const datasetErr = [
|
||||
statusText: CommonErrEnum.fileNotFound,
|
||||
message: i18nT('common:error.invalid_params')
|
||||
},
|
||||
{
|
||||
statusText: CommonErrEnum.invalidResource,
|
||||
message: i18nT('common:error_invalid_resource')
|
||||
},
|
||||
{
|
||||
statusText: CommonErrEnum.fileNotFound,
|
||||
message: 'error.fileNotFound'
|
||||
|
@@ -27,7 +27,8 @@ export enum TeamErrEnum {
|
||||
userNotActive = 'userNotActive',
|
||||
invitationLinkInvalid = 'invitationLinkInvalid',
|
||||
youHaveBeenInTheTeam = 'youHaveBeenInTheTeam',
|
||||
tooManyInvitations = 'tooManyInvitations'
|
||||
tooManyInvitations = 'tooManyInvitations',
|
||||
unPermission = 'unPermission'
|
||||
}
|
||||
|
||||
const teamErr = [
|
||||
@@ -35,6 +36,10 @@ const teamErr = [
|
||||
statusText: TeamErrEnum.notUser,
|
||||
message: i18nT('common:code_error.team_error.not_user')
|
||||
},
|
||||
{
|
||||
statusText: TeamErrEnum.unPermission,
|
||||
message: i18nT('common:error_un_permission')
|
||||
},
|
||||
{
|
||||
statusText: TeamErrEnum.teamOverSize,
|
||||
message: i18nT('common:code_error.team_error.over_size')
|
||||
|
@@ -49,6 +49,7 @@ export type FastGPTFeConfigsType = {
|
||||
find_password_method?: ['email' | 'phone'];
|
||||
bind_notification_method?: ['email' | 'phone'];
|
||||
googleClientVerKey?: string;
|
||||
mcpServerProxyEndpoint?: string;
|
||||
|
||||
show_emptyChat?: boolean;
|
||||
show_appStore?: boolean;
|
||||
|
@@ -11,7 +11,9 @@ export enum AppTypeEnum {
|
||||
simple = 'simple',
|
||||
workflow = 'advanced',
|
||||
plugin = 'plugin',
|
||||
httpPlugin = 'httpPlugin'
|
||||
httpPlugin = 'httpPlugin',
|
||||
toolSet = 'toolSet',
|
||||
tool = 'tool'
|
||||
}
|
||||
|
||||
export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin];
|
||||
@@ -53,7 +55,10 @@ export enum AppTemplateTypeEnum {
|
||||
imageGeneration = 'image-generation',
|
||||
webSearch = 'web-search',
|
||||
roleplay = 'roleplay',
|
||||
officeServices = 'office-services'
|
||||
officeServices = 'office-services',
|
||||
|
||||
// special type
|
||||
contribute = 'contribute'
|
||||
}
|
||||
|
||||
export const defaultDatasetMaxTokens = 16000;
|
||||
|
97
packages/global/core/app/mcpTools/utils.ts
Normal file
97
packages/global/core/app/mcpTools/utils.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { NodeOutputKeyEnum, WorkflowIOValueTypeEnum } from '../../workflow/constants';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '../../workflow/node/constant';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { ToolType } from '../type';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { RuntimeNodeItemType } from '../../workflow/runtime/type';
|
||||
|
||||
export const getMCPToolSetRuntimeNode = ({
|
||||
url,
|
||||
toolList,
|
||||
name,
|
||||
avatar
|
||||
}: {
|
||||
url: string;
|
||||
toolList: ToolType[];
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
}): RuntimeNodeItemType => {
|
||||
return {
|
||||
nodeId: nanoid(16),
|
||||
flowNodeType: FlowNodeTypeEnum.toolSet,
|
||||
avatar,
|
||||
intro: 'MCP Tools',
|
||||
inputs: [
|
||||
{
|
||||
key: 'toolSetData',
|
||||
label: 'Tool Set Data',
|
||||
valueType: WorkflowIOValueTypeEnum.object,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
value: { url, toolList }
|
||||
}
|
||||
],
|
||||
outputs: [],
|
||||
name: name || '',
|
||||
version: ''
|
||||
};
|
||||
};
|
||||
|
||||
export const getMCPToolRuntimeNode = ({
|
||||
tool,
|
||||
url,
|
||||
avatar = 'core/app/type/mcpToolsFill'
|
||||
}: {
|
||||
tool: ToolType;
|
||||
url: string;
|
||||
avatar?: string;
|
||||
}): RuntimeNodeItemType => {
|
||||
return {
|
||||
nodeId: nanoid(16),
|
||||
flowNodeType: FlowNodeTypeEnum.tool,
|
||||
avatar,
|
||||
intro: tool.description,
|
||||
inputs: [
|
||||
{
|
||||
key: 'toolData',
|
||||
label: 'Tool Data',
|
||||
valueType: WorkflowIOValueTypeEnum.object,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
value: { ...tool, url }
|
||||
},
|
||||
...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({
|
||||
key,
|
||||
label: key,
|
||||
valueType: value.type as WorkflowIOValueTypeEnum,
|
||||
description: value.description,
|
||||
toolDescription: value.description || key,
|
||||
required: tool.inputSchema?.required?.includes(key) || false,
|
||||
renderTypeList: [
|
||||
value.type === 'string'
|
||||
? FlowNodeInputTypeEnum.input
|
||||
: value.type === 'number'
|
||||
? FlowNodeInputTypeEnum.numberInput
|
||||
: value.type === 'boolean'
|
||||
? FlowNodeInputTypeEnum.switch
|
||||
: FlowNodeInputTypeEnum.JSONEditor
|
||||
]
|
||||
}))
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
id: NodeOutputKeyEnum.rawResponse,
|
||||
key: NodeOutputKeyEnum.rawResponse,
|
||||
required: true,
|
||||
label: i18nT('workflow:raw_response'),
|
||||
description: i18nT('workflow:tool_raw_response_description'),
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
],
|
||||
name: tool.name,
|
||||
version: ''
|
||||
};
|
||||
};
|
10
packages/global/core/app/type.d.ts
vendored
10
packages/global/core/app/type.d.ts
vendored
@@ -16,6 +16,16 @@ import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
|
||||
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
|
||||
import { SourceMemberType } from '../../support/user/type';
|
||||
|
||||
export type ToolType = {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: {
|
||||
type: string;
|
||||
properties?: Record<string, { type: string; description?: string }>;
|
||||
required?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type AppSchema = {
|
||||
_id: string;
|
||||
parentId?: ParentIdType;
|
||||
|
@@ -140,7 +140,9 @@ export const appWorkflow2Form = ({
|
||||
);
|
||||
} else if (
|
||||
node.flowNodeType === FlowNodeTypeEnum.pluginModule ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.tool ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.toolSet
|
||||
) {
|
||||
if (!node.pluginId) return;
|
||||
|
||||
|
@@ -38,7 +38,8 @@ export enum ChatSourceEnum {
|
||||
team = 'team',
|
||||
feishu = 'feishu',
|
||||
official_account = 'official_account',
|
||||
wecom = 'wecom'
|
||||
wecom = 'wecom',
|
||||
mcp = 'mcp'
|
||||
}
|
||||
|
||||
export const ChatSourceMap = {
|
||||
@@ -68,6 +69,9 @@ export const ChatSourceMap = {
|
||||
},
|
||||
[ChatSourceEnum.wecom]: {
|
||||
name: i18nT('common:core.chat.logs.wecom')
|
||||
},
|
||||
[ChatSourceEnum.mcp]: {
|
||||
name: i18nT('common:core.chat.logs.mcp')
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -154,25 +154,55 @@ export const getChatSourceByPublishChannel = (publishChannel: PublishChannelEnum
|
||||
/*
|
||||
Merge chat responseData
|
||||
1. Same tool mergeSignId (Interactive tool node)
|
||||
2. Recursively merge plugin details with same mergeSignId
|
||||
*/
|
||||
export const mergeChatResponseData = (responseDataList: ChatHistoryItemResType[]) => {
|
||||
let lastResponse: ChatHistoryItemResType | undefined = undefined;
|
||||
|
||||
return responseDataList.reduce<ChatHistoryItemResType[]>((acc, curr) => {
|
||||
if (lastResponse && lastResponse.mergeSignId && curr.mergeSignId === lastResponse.mergeSignId) {
|
||||
// 替换 lastResponse
|
||||
const concatResponse: ChatHistoryItemResType = {
|
||||
...curr,
|
||||
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
|
||||
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
|
||||
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
|
||||
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
|
||||
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])]
|
||||
export const mergeChatResponseData = (
|
||||
responseDataList: ChatHistoryItemResType[]
|
||||
): ChatHistoryItemResType[] => {
|
||||
// Merge children reponse data(Children has interactive response)
|
||||
const responseWithMergedPlugins = responseDataList.map((item) => {
|
||||
if (item.pluginDetail && item.pluginDetail.length > 1) {
|
||||
return {
|
||||
...item,
|
||||
pluginDetail: mergeChatResponseData(item.pluginDetail)
|
||||
};
|
||||
return [...acc.slice(0, -1), concatResponse];
|
||||
} else {
|
||||
lastResponse = curr;
|
||||
return [...acc, curr];
|
||||
}
|
||||
}, []);
|
||||
return item;
|
||||
});
|
||||
|
||||
let lastResponse: ChatHistoryItemResType | undefined = undefined;
|
||||
let hasMerged = false;
|
||||
|
||||
const firstPassResult = responseWithMergedPlugins.reduce<ChatHistoryItemResType[]>(
|
||||
(acc, curr) => {
|
||||
if (
|
||||
lastResponse &&
|
||||
lastResponse.mergeSignId &&
|
||||
curr.mergeSignId === lastResponse.mergeSignId
|
||||
) {
|
||||
const concatResponse: ChatHistoryItemResType = {
|
||||
...curr,
|
||||
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
|
||||
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
|
||||
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
|
||||
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
|
||||
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])],
|
||||
loopDetail: [...(lastResponse.loopDetail || []), ...(curr.loopDetail || [])],
|
||||
pluginDetail: [...(lastResponse.pluginDetail || []), ...(curr.pluginDetail || [])]
|
||||
};
|
||||
hasMerged = true;
|
||||
return [...acc.slice(0, -1), concatResponse];
|
||||
} else {
|
||||
lastResponse = curr;
|
||||
return [...acc, curr];
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (hasMerged && firstPassResult.length > 1) {
|
||||
return mergeChatResponseData(firstPassResult);
|
||||
}
|
||||
|
||||
return firstPassResult;
|
||||
};
|
||||
|
@@ -140,7 +140,9 @@ export enum FlowNodeTypeEnum {
|
||||
loopStart = 'loopStart',
|
||||
loopEnd = 'loopEnd',
|
||||
formInput = 'formInput',
|
||||
comment = 'comment'
|
||||
comment = 'comment',
|
||||
tool = 'tool',
|
||||
toolSet = 'toolSet'
|
||||
}
|
||||
|
||||
// node IO value type
|
||||
|
@@ -217,6 +217,8 @@ export type DispatchNodeResponseType = {
|
||||
// tool params
|
||||
toolParamsResult?: Record<string, any>;
|
||||
|
||||
toolRes?: any;
|
||||
|
||||
// abandon
|
||||
extensionModel?: string;
|
||||
extensionResult?: string;
|
||||
|
@@ -10,6 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||
import json5 from 'json5';
|
||||
import {
|
||||
InteractiveNodeResponseType,
|
||||
WorkflowInteractiveResponseType
|
||||
@@ -18,7 +19,10 @@ import {
|
||||
export const extractDeepestInteractive = (
|
||||
interactive: WorkflowInteractiveResponseType
|
||||
): WorkflowInteractiveResponseType => {
|
||||
if (interactive?.type === 'childrenInteractive' && interactive.params?.childrenResponse) {
|
||||
if (
|
||||
(interactive?.type === 'childrenInteractive' || interactive?.type === 'loopInteractive') &&
|
||||
interactive.params?.childrenResponse
|
||||
) {
|
||||
return extractDeepestInteractive(interactive.params.childrenResponse);
|
||||
}
|
||||
return interactive;
|
||||
@@ -40,6 +44,113 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
|
||||
return limit * 2;
|
||||
};
|
||||
|
||||
/* value type format */
|
||||
export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
|
||||
const isObjectString = (value: any) => {
|
||||
if (typeof value === 'string' && value !== 'false' && value !== 'true') {
|
||||
const trimmedValue = value.trim();
|
||||
const isJsonString =
|
||||
(trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||
|
||||
(trimmedValue.startsWith('[') && trimmedValue.endsWith(']'));
|
||||
return isJsonString;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 1. any值,忽略格式化
|
||||
if (value === undefined || value === null) return value;
|
||||
if (!type || type === WorkflowIOValueTypeEnum.any) return value;
|
||||
|
||||
// 2. 如果值已经符合目标类型,直接返回
|
||||
if (
|
||||
(type === WorkflowIOValueTypeEnum.string && typeof value === 'string') ||
|
||||
(type === WorkflowIOValueTypeEnum.number && typeof value === 'number') ||
|
||||
(type === WorkflowIOValueTypeEnum.boolean && typeof value === 'boolean') ||
|
||||
(type.startsWith('array') && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.object && typeof value === 'object') ||
|
||||
(type === WorkflowIOValueTypeEnum.chatHistory &&
|
||||
(Array.isArray(value) || typeof value === 'number')) ||
|
||||
(type === WorkflowIOValueTypeEnum.datasetQuote && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.selectDataset && Array.isArray(value)) ||
|
||||
(type === WorkflowIOValueTypeEnum.selectApp && typeof value === 'object')
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 4. 按目标类型,进行格式转化
|
||||
// 4.1 基本类型转换
|
||||
if (type === WorkflowIOValueTypeEnum.string) {
|
||||
return typeof value === 'object' ? JSON.stringify(value) : String(value);
|
||||
}
|
||||
if (type === WorkflowIOValueTypeEnum.number) {
|
||||
return Number(value);
|
||||
}
|
||||
if (type === WorkflowIOValueTypeEnum.boolean) {
|
||||
if (typeof value === 'string') {
|
||||
return value.toLowerCase() === 'true';
|
||||
}
|
||||
return Boolean(value);
|
||||
}
|
||||
|
||||
// 4.3 字符串转对象
|
||||
if (
|
||||
(type === WorkflowIOValueTypeEnum.object || type.startsWith('array')) &&
|
||||
typeof value === 'string' &&
|
||||
value.trim()
|
||||
) {
|
||||
const trimmedValue = value.trim();
|
||||
const isJsonString = isObjectString(trimmedValue);
|
||||
|
||||
if (isJsonString) {
|
||||
try {
|
||||
const parsed = json5.parse(trimmedValue);
|
||||
// 检测解析结果与目标类型是否一致
|
||||
if (type.startsWith('array') && Array.isArray(parsed)) return parsed;
|
||||
if (type === WorkflowIOValueTypeEnum.object && typeof parsed === 'object') return parsed;
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
// 4.4 数组类型(这里 value 不是数组类型)(TODO: 嵌套数据类型转化)
|
||||
if (type.startsWith('array')) {
|
||||
return [value];
|
||||
}
|
||||
|
||||
// 4.5 特殊类型处理
|
||||
if (
|
||||
[WorkflowIOValueTypeEnum.datasetQuote, WorkflowIOValueTypeEnum.selectDataset].includes(type)
|
||||
) {
|
||||
if (isObjectString(value)) {
|
||||
try {
|
||||
return json5.parse(value);
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (
|
||||
[WorkflowIOValueTypeEnum.selectApp, WorkflowIOValueTypeEnum.object].includes(type) &&
|
||||
typeof value === 'string'
|
||||
) {
|
||||
if (isObjectString(value)) {
|
||||
try {
|
||||
return json5.parse(value);
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
// Invalid history type
|
||||
if (type === WorkflowIOValueTypeEnum.chatHistory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 5. 默认返回原值
|
||||
return value;
|
||||
};
|
||||
|
||||
/*
|
||||
Get interaction information (if any) from the last AI message.
|
||||
What can be done:
|
||||
@@ -62,7 +173,10 @@ export const getLastInteractiveValue = (
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastValue.interactive.type === 'childrenInteractive') {
|
||||
if (
|
||||
lastValue.interactive.type === 'childrenInteractive' ||
|
||||
lastValue.interactive.type === 'loopInteractive'
|
||||
) {
|
||||
return lastValue.interactive;
|
||||
}
|
||||
|
||||
@@ -83,7 +197,7 @@ export const getLastInteractiveValue = (
|
||||
return;
|
||||
};
|
||||
|
||||
export const initWorkflowEdgeStatus = (
|
||||
export const storeEdges2RuntimeEdges = (
|
||||
edges: StoreEdgeItemType[],
|
||||
lastInteractive?: WorkflowInteractiveResponseType
|
||||
): RuntimeEdgeItemType[] => {
|
||||
@@ -114,7 +228,12 @@ export const getWorkflowEntryNodeIds = (
|
||||
FlowNodeTypeEnum.pluginInput
|
||||
];
|
||||
return nodes
|
||||
.filter((node) => entryList.includes(node.flowNodeType as any))
|
||||
.filter(
|
||||
(node) =>
|
||||
entryList.includes(node.flowNodeType as any) ||
|
||||
(!nodes.some((item) => entryList.includes(item.flowNodeType as any)) &&
|
||||
node.flowNodeType === FlowNodeTypeEnum.tool)
|
||||
)
|
||||
.map((item) => item.nodeId);
|
||||
};
|
||||
|
||||
@@ -312,7 +431,6 @@ export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTyp
|
||||
if (
|
||||
[
|
||||
WorkflowIOValueTypeEnum.object,
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.selectDataset
|
||||
|
@@ -34,6 +34,8 @@ import { LoopStartNode } from './system/loop/loopStart';
|
||||
import { LoopEndNode } from './system/loop/loopEnd';
|
||||
import { FormInputNode } from './system/interactive/formInput';
|
||||
import { ToolParamsNode } from './system/toolParams';
|
||||
import { RunToolNode } from './system/runTool';
|
||||
import { RunToolSetNode } from './system/runToolSet';
|
||||
|
||||
const systemNodes: FlowNodeTemplateType[] = [
|
||||
AiChatModule,
|
||||
@@ -84,5 +86,7 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||
RunAppNode,
|
||||
RunAppModule,
|
||||
LoopStartNode,
|
||||
LoopEndNode
|
||||
LoopEndNode,
|
||||
RunToolNode,
|
||||
RunToolSetNode
|
||||
];
|
||||
|
@@ -8,7 +8,7 @@ import { i18nT } from '../../../../web/i18n/utils';
|
||||
export const Input_Template_History: FlowNodeInputItemType = {
|
||||
key: NodeInputKeyEnum.history,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||
valueType: WorkflowIOValueTypeEnum.chatHistory, // Array / Number
|
||||
label: i18nT('common:core.module.input.label.chat history'),
|
||||
description: i18nT('workflow:max_dialog_rounds'),
|
||||
|
||||
|
@@ -28,6 +28,15 @@ type ChildrenInteractive = InteractiveNodeType & {
|
||||
};
|
||||
};
|
||||
|
||||
type LoopInteractive = InteractiveNodeType & {
|
||||
type: 'loopInteractive';
|
||||
params: {
|
||||
loopResult: any[];
|
||||
childrenResponse: WorkflowInteractiveResponseType;
|
||||
currentIndex: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type UserSelectOptionItemType = {
|
||||
key: string;
|
||||
value: string;
|
||||
@@ -71,5 +80,7 @@ type UserInputInteractive = InteractiveNodeType & {
|
||||
export type InteractiveNodeResponseType =
|
||||
| UserSelectInteractive
|
||||
| UserInputInteractive
|
||||
| ChildrenInteractive;
|
||||
| ChildrenInteractive
|
||||
| LoopInteractive;
|
||||
|
||||
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;
|
||||
|
19
packages/global/core/workflow/template/system/runTool.ts
Normal file
19
packages/global/core/workflow/template/system/runTool.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../type/node';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const RunToolNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.tool,
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
flowNodeType: FlowNodeTypeEnum.tool,
|
||||
sourceHandle: getHandleConfig(true, true, true, true),
|
||||
targetHandle: getHandleConfig(true, true, true, true),
|
||||
intro: '',
|
||||
name: '',
|
||||
showStatus: false,
|
||||
isTool: true,
|
||||
version: '4.9.6',
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
19
packages/global/core/workflow/template/system/runToolSet.ts
Normal file
19
packages/global/core/workflow/template/system/runToolSet.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { FlowNodeTemplateTypeEnum } from '../../constants';
|
||||
import { FlowNodeTypeEnum } from '../../node/constant';
|
||||
import { FlowNodeTemplateType } from '../../type/node';
|
||||
import { getHandleConfig } from '../utils';
|
||||
|
||||
export const RunToolSetNode: FlowNodeTemplateType = {
|
||||
id: FlowNodeTypeEnum.toolSet,
|
||||
templateType: FlowNodeTemplateTypeEnum.other,
|
||||
flowNodeType: FlowNodeTypeEnum.toolSet,
|
||||
sourceHandle: getHandleConfig(false, false, false, false),
|
||||
targetHandle: getHandleConfig(false, false, false, false),
|
||||
intro: '',
|
||||
name: '',
|
||||
showStatus: false,
|
||||
isTool: true,
|
||||
version: '4.9.6',
|
||||
inputs: [],
|
||||
outputs: []
|
||||
};
|
@@ -311,6 +311,38 @@ export const appData2FlowNodeIO = ({
|
||||
};
|
||||
};
|
||||
|
||||
export const toolData2FlowNodeIO = ({
|
||||
nodes
|
||||
}: {
|
||||
nodes: StoreNodeItemType[];
|
||||
}): {
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
} => {
|
||||
const toolNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.tool);
|
||||
|
||||
return {
|
||||
inputs: toolNode?.inputs || [],
|
||||
outputs: toolNode?.outputs || []
|
||||
};
|
||||
};
|
||||
|
||||
export const toolSetData2FlowNodeIO = ({
|
||||
nodes
|
||||
}: {
|
||||
nodes: StoreNodeItemType[];
|
||||
}): {
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
} => {
|
||||
const toolSetNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);
|
||||
|
||||
return {
|
||||
inputs: toolSetNode?.inputs || [],
|
||||
outputs: toolSetNode?.outputs || []
|
||||
};
|
||||
};
|
||||
|
||||
export const formatEditorVariablePickerIcon = (
|
||||
variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[]
|
||||
): EditorVariablePickerType[] => {
|
||||
|
14
packages/global/support/mcp/type.d.ts
vendored
Normal file
14
packages/global/support/mcp/type.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
export type McpKeyType = {
|
||||
_id: string;
|
||||
key: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
apps: McpAppType[];
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type McpAppType = {
|
||||
appId: string;
|
||||
toolName: string;
|
||||
description: string;
|
||||
};
|
@@ -11,7 +11,8 @@ export enum UsageSourceEnum {
|
||||
feishu = 'feishu',
|
||||
dingtalk = 'dingtalk',
|
||||
official_account = 'official_account',
|
||||
pdfParse = 'pdfParse'
|
||||
pdfParse = 'pdfParse',
|
||||
mcp = 'mcp'
|
||||
}
|
||||
|
||||
export const UsageSourceMap = {
|
||||
@@ -47,5 +48,8 @@ export const UsageSourceMap = {
|
||||
},
|
||||
[UsageSourceEnum.pdfParse]: {
|
||||
label: i18nT('account_usage:pdf_parse')
|
||||
},
|
||||
[UsageSourceEnum.mcp]: {
|
||||
label: i18nT('account_usage:mcp')
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user