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

@@ -56,12 +56,12 @@ const getNodeInputRenderTypeFromSchemaInputType = ({
}
if (type === 'string') {
return {
renderTypeList: [FlowNodeInputTypeEnum.input]
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference]
};
}
if (type === 'number') {
return {
renderTypeList: [FlowNodeInputTypeEnum.numberInput],
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
max: maximum,
min: minimum
};
@@ -71,7 +71,7 @@ const getNodeInputRenderTypeFromSchemaInputType = ({
renderTypeList: [FlowNodeInputTypeEnum.switch]
};
}
return { renderTypeList: [FlowNodeInputTypeEnum.JSONEditor] };
return { renderTypeList: [FlowNodeInputTypeEnum.JSONEditor, FlowNodeInputTypeEnum.reference] };
};
export const jsonSchema2NodeInput = (jsonSchema: JSONSchemaInputType): FlowNodeInputItemType[] => {
return Object.entries(jsonSchema?.properties || {}).map(([key, value]) => ({

View File

@@ -0,0 +1,49 @@
import { i18nT } from '../../../../web/i18n/utils';
export enum AppLogKeysEnum {
SOURCE = 'source',
USER = 'user',
TITLE = 'title',
SESSION_ID = 'sessionId',
CREATED_TIME = 'createdTime',
LAST_CONVERSATION_TIME = 'lastConversationTime',
MESSAGE_COUNT = 'messageCount',
FEEDBACK = 'feedback',
CUSTOM_FEEDBACK = 'customFeedback',
ANNOTATED_COUNT = 'annotatedCount',
POINTS = 'points',
RESPONSE_TIME = 'responseTime',
ERROR_COUNT = 'errorCount'
}
export const AppLogKeysEnumMap = {
[AppLogKeysEnum.SOURCE]: i18nT('app:logs_keys_source'),
[AppLogKeysEnum.USER]: i18nT('app:logs_keys_user'),
[AppLogKeysEnum.TITLE]: i18nT('app:logs_keys_title'),
[AppLogKeysEnum.SESSION_ID]: i18nT('app:logs_keys_sessionId'),
[AppLogKeysEnum.CREATED_TIME]: i18nT('app:logs_keys_createdTime'),
[AppLogKeysEnum.LAST_CONVERSATION_TIME]: i18nT('app:logs_keys_lastConversationTime'),
[AppLogKeysEnum.MESSAGE_COUNT]: i18nT('app:logs_keys_messageCount'),
[AppLogKeysEnum.FEEDBACK]: i18nT('app:logs_keys_feedback'),
[AppLogKeysEnum.CUSTOM_FEEDBACK]: i18nT('app:logs_keys_customFeedback'),
[AppLogKeysEnum.ANNOTATED_COUNT]: i18nT('app:logs_keys_annotatedCount'),
[AppLogKeysEnum.POINTS]: i18nT('app:logs_keys_points'),
[AppLogKeysEnum.RESPONSE_TIME]: i18nT('app:logs_keys_responseTime'),
[AppLogKeysEnum.ERROR_COUNT]: i18nT('app:logs_keys_errorCount')
};
export const DefaultAppLogKeys = [
{ key: AppLogKeysEnum.SOURCE, enable: true },
{ key: AppLogKeysEnum.USER, enable: true },
{ key: AppLogKeysEnum.TITLE, enable: true },
{ key: AppLogKeysEnum.SESSION_ID, enable: false },
{ key: AppLogKeysEnum.CREATED_TIME, enable: false },
{ key: AppLogKeysEnum.LAST_CONVERSATION_TIME, enable: true },
{ key: AppLogKeysEnum.MESSAGE_COUNT, enable: true },
{ key: AppLogKeysEnum.FEEDBACK, enable: true },
{ key: AppLogKeysEnum.CUSTOM_FEEDBACK, enable: false },
{ key: AppLogKeysEnum.ANNOTATED_COUNT, enable: false },
{ key: AppLogKeysEnum.POINTS, enable: false },
{ key: AppLogKeysEnum.RESPONSE_TIME, enable: false },
{ key: AppLogKeysEnum.ERROR_COUNT, enable: false }
];

12
packages/global/core/app/logs/type.d.ts vendored Normal file
View File

@@ -0,0 +1,12 @@
import type { AppLogKeysEnum } from './constants';
export type AppLogKeysType = {
key: AppLogKeysEnum;
enable: boolean;
};
export type AppLogKeysSchemaType = {
teamId: string;
appId: string;
logKeys: AppLogKeysType[];
};

View File

@@ -14,38 +14,37 @@ import { type RuntimeNodeItemType } from '../../workflow/runtime/type';
import { type StoreSecretValueType } from '../../../common/secret/type';
import { jsonSchema2NodeInput } from '../jsonschema';
import { getNanoid } from '../../../common/string/tools';
import { PluginSourceEnum } from '../plugin/constants';
export const getMCPToolSetRuntimeNode = ({
url,
toolList,
headerSecret,
name,
avatar
avatar,
toolId
}: {
url: string;
toolList: McpToolConfigType[];
headerSecret?: StoreSecretValueType;
name?: string;
avatar?: string;
toolId: string;
}): RuntimeNodeItemType => {
return {
nodeId: getNanoid(16),
flowNodeType: FlowNodeTypeEnum.toolSet,
avatar,
intro: '',
inputs: [
{
key: NodeInputKeyEnum.toolSetData,
label: '',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: {
url,
headerSecret,
toolList
}
intro: 'MCP Tools',
toolConfig: {
mcpToolSet: {
toolList,
headerSecret,
url,
toolId
}
],
},
inputs: [],
outputs: [],
name: name || '',
version: ''
@@ -54,34 +53,24 @@ export const getMCPToolSetRuntimeNode = ({
export const getMCPToolRuntimeNode = ({
tool,
url,
headerSecret,
avatar = 'core/app/type/mcpToolsFill'
avatar = 'core/app/type/mcpToolsFill',
parentId
}: {
tool: McpToolConfigType;
url: string;
headerSecret?: StoreSecretValueType;
avatar?: string;
parentId: string;
}): RuntimeNodeItemType => {
return {
nodeId: getNanoid(16),
flowNodeType: FlowNodeTypeEnum.tool,
avatar,
intro: tool.description,
inputs: [
{
key: NodeInputKeyEnum.toolData,
label: 'Tool Data',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: {
...tool,
url,
headerSecret
}
},
...jsonSchema2NodeInput(tool.inputSchema)
],
toolConfig: {
mcpTool: {
toolId: `${PluginSourceEnum.mcp}-${parentId}/${tool.name}`
}
},
inputs: jsonSchema2NodeInput(tool.inputSchema),
outputs: [
{
id: NodeOutputKeyEnum.rawResponse,
@@ -97,3 +86,12 @@ export const getMCPToolRuntimeNode = ({
version: ''
};
};
/**
* Get the parent id of the mcp toolset
* mcp-123123/toolName ==> 123123
* 123123/toolName ==> 123123
* @param id mcp-parentId/name or parentId/name
* @returns parentId
*/
export const getMCPParentId = (id: string) => id.split('-').pop()?.split('/')[0];

View File

@@ -2,6 +2,7 @@ export enum PluginSourceEnum {
personal = 'personal', // this is a app.
systemTool = 'systemTool', // FastGPT-plugin tools, pure code.
commercial = 'commercial', // configured in Pro, with associatedPluginId. Specially, commercial-dalle3 is a systemTool
mcp = 'mcp', // mcp
// @deprecated
community = 'community' // this is deprecated, will be replaced by systemTool
}

View File

@@ -1,6 +1,7 @@
import { type StoreNodeItemType } from '../../workflow/type/node';
import { type FlowNodeInputItemType } from '../../workflow/type/io';
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
import { PluginSourceEnum } from './constants';
export const getPluginInputsFromStoreNodes = (nodes: StoreNodeItemType[]) => {
return nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput)?.inputs || [];
@@ -22,3 +23,42 @@ export const getPluginRunContent = ({
});
return JSON.stringify(pluginInputsWithValue);
};
/**
plugin id rule:
- personal: ObjectId
- commercial: commercial-ObjectId
- systemtool: systemTool-id
- mcp tool: mcp-parentId/toolName
(deprecated) community: community-id
*/
export function splitCombinePluginId(id: string) {
const splitRes = id.split('-');
if (splitRes.length === 1) {
// app id
return {
source: PluginSourceEnum.personal,
pluginId: id
};
}
const [source, ...rest] = id.split('-') as [PluginSourceEnum, string | undefined];
const pluginId = rest.join('-');
if (!source || !pluginId) throw new Error('pluginId not found');
// 兼容4.10.0 之前的插件
if (source === 'community' || id === 'commercial-dalle3') {
return {
source: PluginSourceEnum.systemTool,
pluginId: `${PluginSourceEnum.systemTool}-${pluginId}`
};
}
if (source === 'mcp') {
return {
source: PluginSourceEnum.mcp,
pluginId
};
}
return { source, pluginId: id };
}

View File

@@ -146,7 +146,7 @@ export type SettingAIDataType = {
// variable
export type VariableItemType = {
id: string;
// id: string;
key: string;
label: string;
type: VariableInputEnum;
@@ -161,6 +161,8 @@ export type VariableItemType = {
max?: number;
min?: number;
// select
list?: { label: string; value: string }[];
// @deprecated
enums?: { value: string; label: string }[];
};
// tts

View File

@@ -105,6 +105,13 @@ export const toolValueTypeList: {
type: 'boolean'
}
}
},
{
label: 'object',
value: WorkflowIOValueTypeEnum.object,
jsonSchema: {
type: 'object'
}
}
];
export const valueTypeJsonSchemaMap: Record<string, JsonSchemaPropertiesItemType> =

View File

@@ -87,6 +87,7 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
return typeof value === 'object' ? JSON.stringify(value) : String(value);
}
if (type === WorkflowIOValueTypeEnum.number) {
if (value === '') return undefined;
return Number(value);
}
if (type === WorkflowIOValueTypeEnum.boolean) {

View File

@@ -23,14 +23,29 @@ import type { AppDetailType, AppSchema, McpToolConfigType } from '../../app/type
import type { ParentIdType } from 'common/parentFolder/type';
import { AppTypeEnum } from '../../app/constants';
import type { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
import type { StoreSecretValueType } from '../../../common/secret/type';
export type NodeToolConfigType = {
mcpTool?: McpToolConfigType & {
mcpToolSet?: {
toolId: string; // ObjectId of the MCP App
url: string;
headerSecret?: StoreSecretValueType;
toolList: McpToolConfigType[];
};
mcpTool?: {
toolId: string;
};
systemTool?: {
toolId: string;
};
systemToolSet?: {
toolId: string;
toolList: {
toolId: string;
name: string;
description: string;
}[];
};
};
export type FlowNodeCommonType = {

View File

@@ -18,6 +18,7 @@ import {
type ReferenceArrayValueType,
type ReferenceItemValueType
} from './type/io.d';
import type { NodeToolConfigType } from './type/node';
import { type StoreNodeItemType } from './type/node';
import type {
VariableItemType,
@@ -310,35 +311,25 @@ export const appData2FlowNodeIO = ({
};
};
export const toolData2FlowNodeIO = ({
nodes
}: {
nodes: StoreNodeItemType[];
}): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} => {
export const toolData2FlowNodeIO = ({ nodes }: { nodes: StoreNodeItemType[] }) => {
const toolNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.tool);
return {
inputs: toolNode?.inputs || [],
outputs: toolNode?.outputs || []
outputs: toolNode?.outputs || [],
toolConfig: toolNode?.toolConfig
};
};
export const toolSetData2FlowNodeIO = ({
nodes
}: {
nodes: StoreNodeItemType[];
}): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} => {
export const toolSetData2FlowNodeIO = ({ nodes }: { nodes: StoreNodeItemType[] }) => {
const toolSetNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);
return {
inputs: toolSetNode?.inputs || [],
outputs: toolSetNode?.outputs || []
outputs: toolSetNode?.outputs || [],
toolConfig: toolSetNode?.toolConfig,
showSourceHandle: false,
showTargetHandle: false
};
};