Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions packages/global/core/app/tool/systemTool/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,7 @@ export const SystemToolCodec = {
},

fromDBTypeToListItemType(item: SystemPluginToolCollectionType): SystemToolListItemType {
const {
name,
avatar,
intro,
toolDescription,
version,
userGuide,
author = '',
tags
} = item.customConfig!;
const { name, avatar, intro, version, userGuide, author = '', tags } = item.customConfig!;

return {
id: item.pluginId,
Expand All @@ -74,7 +65,6 @@ export const SystemToolCodec = {
systemKeyCost: 0,
// 数据库里面取出来的一定不是 toolset
isToolSet: false,
toolDescription: toolDescription ?? intro ?? '',
hideTags: item.hideTags ?? [],
promoteTags: item.promoteTags ?? []
// TODO: 不知道谁做落了,之后再补吧
Expand Down Expand Up @@ -118,7 +108,6 @@ export const SystemToolCodec = {
status: config?.status ?? PluginStatusEnum.Normal,
systemKeyCost: config?.systemKeyCost ?? 0,
tags: config?.customConfig?.tags ?? tool.tags ?? [],
toolDescription: config?.customConfig?.toolDescription ?? tool.toolDescription ?? '',
version: tool.version,
courseUrl: tool.tutorialUrl,
hideTags: config?.hideTags ?? [],
Expand Down
12 changes: 10 additions & 2 deletions packages/global/core/app/tool/systemTool/type/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ export const SystemToolListItemSchema = z.object({
intro: z.string().meta({ description: '工具的简介' }),
author: z.string().meta({ description: '工具的作者' }),
tags: z.array(z.string()).meta({ description: '工具的标签' }),
toolDescription: z.string().meta({ description: '给工具调用使用的工具的描述' }),
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),

userGuide: z.string().nullish().meta({ description: '工具的使用指南(markdown 纯文本)' }),
readmeUrl: z.string().optional().meta({ description: '工具的 README 地址' }),
Expand Down Expand Up @@ -79,7 +83,11 @@ export const SystemToolChildDetailSchema = z.object({
name: z.string(),
status: PluginStatusSchema.meta({ description: '工具的状态' }),
description: z.string().optional(),
toolDescription: z.string().optional(),
Comment thread
Nixieboluo marked this conversation as resolved.
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),
icon: z.string().optional(),
currentCost: z.number().meta({ description: '当前使用的费用' }),
systemKeyCost: z.number().meta({ description: '系统密钥的费用' }),
Expand Down
84 changes: 83 additions & 1 deletion packages/global/core/app/tool/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppToolSourceEnum } from '../tool/constants';
import { NodeInputKeyEnum } from '../../workflow/constants';
import { FlowNodeTypeEnum } from '../../workflow/node/constant';
import type { StoreNodeItemType } from '../../workflow/type/node';
import type { NodeToolConfigType, StoreNodeItemType } from '../../workflow/type/node';
import type { SelectedToolItemType } from '../formEdit/type';

/**
Expand Down Expand Up @@ -225,3 +225,85 @@ export const getToolNameCandidates = (toolName?: string) => {

return candidates;
};

/** 返回工具集子工具的有效描述;主动保存空字符串时保留空值。 */
export const getToolSetChildDescription = (
savedDescription?: string | null | false | 0,
definitionDescription = ''
) => (savedDescription === '' ? savedDescription : savedDescription || definitionDescription);

/**
* 将当前节点保存的子工具描述合并到新模板配置。
*
* 系统工具集用 `toolId` 匹配,MCP/HTTP 工具集用 `name` 匹配;保留用户保存的描述,
* 以便新增工具和历史缺失值继续使用新模板的工具定义描述。
*/
export const mergeToolSetChildDescriptions = ({
savedToolConfig,
templateToolConfig
}: {
savedToolConfig?: NodeToolConfigType;
templateToolConfig?: NodeToolConfigType;
}) => {
if (!templateToolConfig) return templateToolConfig;

const mergeToolList = <T extends { description: string }>({
templateList,
savedList,
getKey
}: {
templateList: T[];
savedList: T[] | undefined;
getKey: (tool: T) => string;
}) => {
const savedDescriptionMap = new Map(savedList?.map((tool) => [getKey(tool), tool.description]));

return templateList.map((tool) => {
const description = getToolSetChildDescription(
savedDescriptionMap.get(getKey(tool)),
tool.description
);
return description === tool.description ? tool : { ...tool, description };
});
};

return {
...templateToolConfig,
...(templateToolConfig.systemToolSet
? {
systemToolSet: {
...templateToolConfig.systemToolSet,
toolList: mergeToolList({
templateList: templateToolConfig.systemToolSet.toolList,
savedList: savedToolConfig?.systemToolSet?.toolList,
getKey: (tool) => tool.toolId
})
}
}
: {}),
...(templateToolConfig.mcpToolSet
? {
mcpToolSet: {
...templateToolConfig.mcpToolSet,
toolList: mergeToolList({
templateList: templateToolConfig.mcpToolSet.toolList ?? [],
savedList: savedToolConfig?.mcpToolSet?.toolList,
getKey: (tool) => tool.name
})
}
}
: {}),
...(templateToolConfig.httpToolSet
? {
httpToolSet: {
...templateToolConfig.httpToolSet,
toolList: mergeToolList({
templateList: templateToolConfig.httpToolSet.toolList ?? [],
savedList: savedToolConfig?.httpToolSet?.toolList,
getKey: (tool) => tool.name
})
}
}
: {})
} as NodeToolConfigType;
};
6 changes: 5 additions & 1 deletion packages/global/core/plugin/tool/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ export const SystemPluginToolCollectionSchema = SystemToolBasicConfigSchema.exte
name: z.string(),
avatar: z.string().optional(),
intro: z.string().optional(),
toolDescription: z.string().optional(),
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),
version: z.string(),
tags: z.array(z.string()).nullish(),
associatedPluginId: z.string().optional(),
Expand Down
1 change: 1 addition & 0 deletions packages/global/core/workflow/runtime/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type RuntimeNodeItemType = {
name: StoreNodeItemType['name'];
avatar?: StoreNodeItemType['avatar'];
intro?: StoreNodeItemType['intro'];
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription?: StoreNodeItemType['toolDescription'];
flowNodeType: StoreNodeItemType['flowNodeType'];
showStatus?: StoreNodeItemType['showStatus'];
Expand Down
1 change: 0 additions & 1 deletion packages/global/core/workflow/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ export const storeNodes2RuntimeNodes = (
name: node.name,
avatar: node.avatar,
intro: node.intro,
toolDescription: node.toolDescription,
flowNodeType: node.flowNodeType,
showStatus: node.showStatus,
isEntry: entryNodeIds.includes(node.nodeId),
Expand Down
6 changes: 5 additions & 1 deletion packages/global/core/workflow/type/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ export const WorkflowTemplateTypeSchema = z.object({
avatar: z.string().optional(),
name: z.union([I18nStringSchema, z.string()]),
intro: z.union([I18nStringSchema, z.string()]).optional(),
toolDescription: z.string().optional(),
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),

author: z.string().optional(),
courseUrl: z.string().optional(),
Expand Down
11 changes: 10 additions & 1 deletion packages/global/core/workflow/type/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ export const FlowNodeCommonTypeSchema = z.object({
colorSchema: z.enum(NodeColorSchemaEnum).optional(), // color schema
name: z.string(), // name
intro: z.string().optional(), // template list intro
toolDescription: z.string().optional(), // tool description
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),
showStatus: BoolSchema.optional(), // chatting response step status

version: z.string().optional(), // version
Expand Down Expand Up @@ -282,6 +286,11 @@ export const NodeTemplateListItemTypeSchema = z.object({
avatar: z.string().optional(),
name: z.string(),
intro: z.string().optional(), // template list intro
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: z.string().optional().meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),
isTool: BoolSchema.optional(),
hasToolInput: BoolSchema.optional(),

Expand Down
6 changes: 4 additions & 2 deletions packages/global/openapi/core/app/tool/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ export const GetToolSetChildrenResponseSchema = z.object({
export type GetToolSetChildrenResponseType = z.infer<typeof GetToolSetChildrenResponseSchema>;

const ToolNodeTemplateListItemSchema = NodeTemplateListItemTypeSchema.extend({
toolDescription: z.string().optional().meta({
description: '工具调用描述'
/** @deprecated Unused now in favor of `intro` in node data. */
toolDescription: NodeTemplateListItemTypeSchema.shape.toolDescription.meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
})
}).catchall(z.any());

Expand Down
5 changes: 3 additions & 2 deletions packages/global/openapi/core/workflow/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,9 @@ export const OpenAPIStoreNodeItemTypeSchema = StoreNodeItemTypeSchema.omit({
intro: z.string().optional().meta({
description: '节点简介'
}),
toolDescription: z.string().optional().meta({
description: '节点作为工具被调用时的能力说明'
toolDescription: StoreNodeItemTypeSchema.shape.toolDescription.meta({
description: '已废弃:节点作为工具被调用时的能力说明',
deprecated: true
}),
showStatus: BoolSchema.optional().meta({
description: '对话运行时是否展示该节点执行状态'
Expand Down
1 change: 0 additions & 1 deletion packages/global/test/core/app/tool/systemTool/type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const createAdminToolDetail = () => ({
intro: 'Tool intro',
author: 'FastGPT',
tags: [],
toolDescription: 'Tool description',
currentCost: 0,
systemKeyCost: 0,
hasTokenFee: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import { SystemToolCodec } from '@fastgpt/global/core/app/tool/systemTool/codec';
import { UpdateSystemToolBodySchema } from '@fastgpt/global/openapi/core/plugin/admin/tool/api';
import type { SystemPluginToolCollectionType } from '@fastgpt/global/core/plugin/tool/type';
import { type SystemPluginToolCollectionType } from '@fastgpt/global/core/plugin/tool/type';

describe('system tool config', () => {
it('allows null secretsVal to explicitly disable system secret', () => {
Expand Down
5 changes: 2 additions & 3 deletions packages/global/test/core/app/tool/type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@ describe('AgentToolSchema', () => {
expect(result.inputs).toEqual([{ key: 'query', mode: AgentToolInputModeEnum.agentGenerated }]);
});

it('rejects a historical workflow input snapshot', () => {
it('rejects a non-sparse workflow input snapshot', () => {
expect(() =>
AgentToolSchema.parse({
id: 'systemTool-search',
inputs: [
{
key: 'query',
renderTypeList: ['input', 'agentGenerated'],
selectedType: 'agentGenerated',
toolDescription: 'Search query'
selectedType: 'agentGenerated'
}
],
config: {}
Expand Down
94 changes: 94 additions & 0 deletions packages/global/test/core/app/tool/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
getToolIdentityKey,
getToolNameCandidates,
getToolRawId,
getToolSetChildDescription,
hasDebugToolInNodes,
hasDebugToolInSelectedTools,
isTeamPluginSource,
mergeToolSetChildDescriptions,
parseDebugToolSource,
parseTeamPluginSource,
parseToolsetToolId,
Expand All @@ -17,6 +19,98 @@ import {
import { AppToolSourceEnum } from '@fastgpt/global/core/app/tool/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';

describe('tool set child descriptions', () => {
it('uses definitions for falsy values except an explicitly empty description', () => {
expect(getToolSetChildDescription(undefined, 'Definition description')).toBe(
'Definition description'
);
expect(getToolSetChildDescription(null, 'Definition description')).toBe(
'Definition description'
);
expect(getToolSetChildDescription(false, 'Definition description')).toBe(
'Definition description'
);
expect(getToolSetChildDescription(0, 'Definition description')).toBe('Definition description');
expect(getToolSetChildDescription('', 'Definition description')).toBe('');
expect(getToolSetChildDescription(' ', 'Definition description')).toBe(' ');
expect(getToolSetChildDescription(' Custom description ', 'Definition description')).toBe(
' Custom description '
);
});

it('preserves saved system descriptions by tool id and external descriptions by name', () => {
const savedToolConfig = {
systemToolSet: {
toolId: 'system-tool-set',
toolList: [
{ toolId: 'search', name: 'Old search', description: 'Custom system description' },
{ toolId: 'removed', name: 'Removed', description: 'Removed description' }
]
},
mcpToolSet: {
url: 'https://example.com/mcp',
toolList: [
{ name: 'search', description: 'Custom MCP description' },
{ name: 'blank', description: ' ' }
]
},
httpToolSet: {
toolList: [
{ name: 'search', description: 'Custom HTTP description' },
{ name: 'blank', description: '' }
]
}
} as any;
const templateToolConfig = {
systemToolSet: {
toolId: 'system-tool-set',
toolList: [
{ toolId: 'search', name: 'New search', description: 'System default' },
{ toolId: 'new', name: 'New', description: 'New system default' }
]
},
mcpToolSet: {
url: 'https://example.com/mcp',
toolList: [
{ name: 'search', description: 'MCP default' },
{ name: 'blank', description: 'Blank MCP default' },
{ name: 'new', description: 'New MCP default' }
]
},
httpToolSet: {
toolList: [
{ name: 'search', description: 'HTTP default' },
{ name: 'blank', description: 'Blank HTTP default' },
{ name: 'new', description: 'New HTTP default' }
]
}
} as any;

expect(mergeToolSetChildDescriptions({ savedToolConfig, templateToolConfig })).toMatchObject({
systemToolSet: {
toolList: [
{ toolId: 'search', description: 'Custom system description' },
{ toolId: 'new', description: 'New system default' }
]
},
mcpToolSet: {
toolList: [
{ name: 'search', description: 'Custom MCP description' },
{ name: 'blank', description: ' ' },
{ name: 'new', description: 'New MCP default' }
]
},
httpToolSet: {
toolList: [
{ name: 'search', description: 'Custom HTTP description' },
{ name: 'blank', description: '' },
{ name: 'new', description: 'New HTTP default' }
]
}
});
});
});

describe('shouldUseLegacyToolDescriptionFallback', () => {
it('only enables fallback for workflow, system and commercial tools', () => {
expect(
Expand Down
Loading
Loading