Commit 8751665d by Finley Ge Committed by GitHub

fix(workflow): preserve explicit tool input render types (#7437)

* fix(workflow): preserve explicit tool input render types

* fix(tool-runtime): exclude referenced inputs from model schema
parent b60be546
...@@ -163,15 +163,12 @@ export const normalizeFlowNodeInputType = <T extends FlowNodeInputItemType>( ...@@ -163,15 +163,12 @@ export const normalizeFlowNodeInputType = <T extends FlowNodeInputItemType>(
: isLegacyDefaultSelection : isLegacyDefaultSelection
? undefined ? undefined
: legacySelectedType; : legacySelectedType;
const mustUseAgentGenerated =
canUseAgentGenerated && shouldUseAgentGeneratedOnly({ ...input, renderTypeList });
const shouldDefaultToAgentGenerated = canUseAgentGenerated && recommendsAgentGenerated; const shouldDefaultToAgentGenerated = canUseAgentGenerated && recommendsAgentGenerated;
const defaultManualType = renderTypeList.find( const defaultManualType = renderTypeList.find(
(type) => type !== FlowNodeInputTypeEnum.agentGenerated (type) => type !== FlowNodeInputTypeEnum.agentGenerated
); );
const selectedType = mustUseAgentGenerated const selectedType =
? FlowNodeInputTypeEnum.agentGenerated deferDefaultSelection && recommendsAgentGenerated && !savedSelectedType
: deferDefaultSelection && recommendsAgentGenerated && !savedSelectedType
? undefined ? undefined
: savedSelectedType && : savedSelectedType &&
renderTypeList.includes(savedSelectedType) && renderTypeList.includes(savedSelectedType) &&
...@@ -307,12 +304,11 @@ export const getToolInputDisplayRenderTypeList = ({ ...@@ -307,12 +304,11 @@ export const getToolInputDisplayRenderTypeList = ({
} }
const manualRenderType = getToolInputManualRenderType(input); const manualRenderType = getToolInputManualRenderType(input);
if (!manualRenderType) return [FlowNodeInputTypeEnum.agentGenerated];
return Array.from( return Array.from(
new Set([ new Set([
FlowNodeInputTypeEnum.agentGenerated, FlowNodeInputTypeEnum.agentGenerated,
manualRenderType, ...(manualRenderType ? [manualRenderType] : []),
...input.renderTypeList.filter( ...input.renderTypeList.filter(
(type) => type !== FlowNodeInputTypeEnum.agentGenerated && !manualInputRenderTypes.has(type) (type) => type !== FlowNodeInputTypeEnum.agentGenerated && !manualInputRenderTypes.has(type)
) )
...@@ -527,8 +523,17 @@ export const initToolInputTypeByDefaultMode = <T extends FlowNodeInputItemType>( ...@@ -527,8 +523,17 @@ export const initToolInputTypeByDefaultMode = <T extends FlowNodeInputItemType>(
}: ToolInputDefaultModeOptions = {} }: ToolInputDefaultModeOptions = {}
): T => { ): T => {
const isTool = allowUserChatInputAgentGenerated || input.key !== NodeInputKeyEnum.userChatInput; const isTool = allowUserChatInputAgentGenerated || input.key !== NodeInputKeyEnum.userChatInput;
const normalizedInput = normalizeFlowNodeInputType(input, { isTool, forceDefaultMode });
// Agent 配置不展示 reference 等工作流专用类型;没有手动控件时只能由 Agent 生成。
if (isTool && shouldUseAgentGeneratedOnly(normalizedInput)) {
return {
...normalizedInput,
selectedType: FlowNodeInputTypeEnum.agentGenerated
};
}
return normalizeFlowNodeInputType(input, { isTool, forceDefaultMode }); return normalizedInput;
}; };
export const initToolInputsTypeByDefaultMode = <T extends FlowNodeInputItemType>( export const initToolInputsTypeByDefaultMode = <T extends FlowNodeInputItemType>(
......
...@@ -2,6 +2,7 @@ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv'; ...@@ -2,6 +2,7 @@ import Ajv, { type ErrorObject, type ValidateFunction } from 'ajv';
import Ajv2019 from 'ajv/dist/2019'; import Ajv2019 from 'ajv/dist/2019';
import Ajv2020 from 'ajv/dist/2020'; import Ajv2020 from 'ajv/dist/2020';
import type { ChatCompletionTool } from '../../ai/llm/type'; import type { ChatCompletionTool } from '../../ai/llm/type';
import { FlowNodeInputTypeEnum } from '../../workflow/node/constant';
import type { FlowNodeInputItemType } from '../../workflow/type/io'; import type { FlowNodeInputItemType } from '../../workflow/type/io';
import { AgentToolInputModeEnum } from './constants'; import { AgentToolInputModeEnum } from './constants';
import { import {
...@@ -9,6 +10,7 @@ import { ...@@ -9,6 +10,7 @@ import {
canInputBeManuallyConfigured, canInputBeManuallyConfigured,
isAgentGeneratedToolInput isAgentGeneratedToolInput
} from '../formEdit/utils'; } from '../formEdit/utils';
import { getSelectedInputRenderType } from '../../workflow/utils';
import { import {
buildModelVisibleToolJsonSchema, buildModelVisibleToolJsonSchema,
type JSONSchemaInputType, type JSONSchemaInputType,
...@@ -47,8 +49,10 @@ const createToolInputDefinitions = ({ ...@@ -47,8 +49,10 @@ const createToolInputDefinitions = ({
}): ToolInputDefinition[] => }): ToolInputDefinition[] =>
inputs.map((input) => { inputs.map((input) => {
const canAgentGenerate = canInputBeAgentGenerated(input); const canAgentGenerate = canInputBeAgentGenerated(input);
const canConfigureManually = // reference 在工作流执行前已解析为固定值,不受 Agent 配置页手动控件范围限制。
const canUseFixedBinding =
canInputBeManuallyConfigured({ renderTypeList: input.renderTypeList ?? [] }) || canInputBeManuallyConfigured({ renderTypeList: input.renderTypeList ?? [] }) ||
getSelectedInputRenderType(input) === FlowNodeInputTypeEnum.reference ||
!canAgentGenerate; !canAgentGenerate;
return { return {
...@@ -57,7 +61,7 @@ const createToolInputDefinitions = ({ ...@@ -57,7 +61,7 @@ const createToolInputDefinitions = ({
nodeInput: input, nodeInput: input,
allowedModes: [ allowedModes: [
...(canAgentGenerate ? [AgentToolInputModeEnum.agentGenerated] : []), ...(canAgentGenerate ? [AgentToolInputModeEnum.agentGenerated] : []),
...(canConfigureManually ? [AgentToolInputModeEnum.manual] : []) ...(canUseFixedBinding ? [AgentToolInputModeEnum.manual] : [])
] ]
}; };
}); });
......
...@@ -1017,6 +1017,22 @@ describe('agent generated tool input helpers', () => { ...@@ -1017,6 +1017,22 @@ describe('agent generated tool input helpers', () => {
expect(getToolInputManualRenderType(input)).toBeUndefined(); expect(getToolInputManualRenderType(input)).toBeUndefined();
}); });
it('should preserve an explicit reference-only selection in workflow tool context', () => {
const input = normalizeFlowNodeInputType(
createMockInput({
renderTypeList: [FlowNodeInputTypeEnum.reference],
selectedType: FlowNodeInputTypeEnum.reference
}),
{ isTool: true }
);
expect(input.renderTypeList).toEqual([
FlowNodeInputTypeEnum.agentGenerated,
FlowNodeInputTypeEnum.reference
]);
expect(input.selectedType).toBe(FlowNodeInputTypeEnum.reference);
});
it('should normalize reference-only inputs to agent generated mode', () => { it('should normalize reference-only inputs to agent generated mode', () => {
const input = initToolInputTypeByDefaultMode( const input = initToolInputTypeByDefaultMode(
createMockInput({ createMockInput({
...@@ -1485,6 +1501,20 @@ describe('agent generated tool input helpers', () => { ...@@ -1485,6 +1501,20 @@ describe('agent generated tool input helpers', () => {
]); ]);
}); });
it('should keep reference available for reference-only workflow tool inputs', () => {
const renderTypeList = getToolInputDisplayRenderTypeList({
input: createMockInput({
renderTypeList: [FlowNodeInputTypeEnum.agentGenerated, FlowNodeInputTypeEnum.reference]
}),
showAgentGenerated: true
});
expect(renderTypeList).toEqual([
FlowNodeInputTypeEnum.agentGenerated,
FlowNodeInputTypeEnum.reference
]);
});
it.each([ it.each([
{ {
valueType: WorkflowIOValueTypeEnum.number, valueType: WorkflowIOValueTypeEnum.number,
......
...@@ -65,6 +65,41 @@ describe('compileToolRuntime', () => { ...@@ -65,6 +65,41 @@ describe('compileToolRuntime', () => {
}); });
}); });
it('keeps a selected workflow reference out of the model schema', () => {
const compiled = compileToolRuntime({
toolId: 'workflow-tool',
name: 'Workflow tool',
inputs: [
{
key: 'var_ref',
label: 'var_ref',
valueType: WorkflowIOValueTypeEnum.string,
renderTypeList: [FlowNodeInputTypeEnum.agentGenerated, FlowNodeInputTypeEnum.reference],
selectedType: FlowNodeInputTypeEnum.reference,
value: ['workflowStart', 'userChatInput']
},
{
key: 'var_ref2',
label: 'var_ref2',
valueType: WorkflowIOValueTypeEnum.string,
renderTypeList: [FlowNodeInputTypeEnum.agentGenerated, FlowNodeInputTypeEnum.reference],
selectedType: FlowNodeInputTypeEnum.agentGenerated
}
]
});
expect(compiled.modelTool.function.parameters).toEqual({
type: 'object',
properties: {
var_ref2: { type: 'string', description: '' }
}
});
expect(compiled.agentGeneratedKeys).toEqual(['var_ref2']);
expect(compiled.fixedInputBindings).toEqual({
var_ref: ['workflowStart', 'userChatInput']
});
});
it('normalizes persisted modes to each input allowed modes', () => { it('normalizes persisted modes to each input allowed modes', () => {
const compiled = compileToolRuntime({ const compiled = compileToolRuntime({
toolId: 'guarded-tool', toolId: 'guarded-tool',
......
...@@ -14,11 +14,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; ...@@ -14,11 +14,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { WorkflowActionsContext } from '../../../../context/workflowActionsContext'; import { WorkflowActionsContext } from '../../../../context/workflowActionsContext';
import { import { getToolInputDisplayRenderTypeList } from '@fastgpt/global/core/app/formEdit/utils';
canInputBeAgentGenerated,
canInputBeManuallyConfigured,
getToolInputDisplayRenderTypeList
} from '@fastgpt/global/core/app/formEdit/utils';
import { getSelectedInputRenderType } from '@fastgpt/global/core/workflow/utils'; import { getSelectedInputRenderType } from '@fastgpt/global/core/workflow/utils';
type Props = { type Props = {
...@@ -34,11 +30,8 @@ const InputLabel = ({ nodeId, input, RightComponent, isTool }: Props) => { ...@@ -34,11 +30,8 @@ const InputLabel = ({ nodeId, input, RightComponent, isTool }: Props) => {
const onChangeNode = useContextSelector(WorkflowActionsContext, (v) => v.onChangeNode); const onChangeNode = useContextSelector(WorkflowActionsContext, (v) => v.onChangeNode);
const { description, required, label, renderTypeList, valueType, valueDesc } = input; const { description, required, label, renderTypeList, valueType, valueDesc } = input;
const canManuallyConfigure = canInputBeManuallyConfigured(input);
const renderType = const renderType =
isTool && canInputBeAgentGenerated(input) && !canManuallyConfigure getSelectedInputRenderType(input) ?? renderTypeList?.[0] ?? FlowNodeInputTypeEnum.input;
? FlowNodeInputTypeEnum.agentGenerated
: (getSelectedInputRenderType(input) ?? renderTypeList?.[0] ?? FlowNodeInputTypeEnum.input);
const displayRenderTypeList = useMemo( const displayRenderTypeList = useMemo(
() => () =>
getToolInputDisplayRenderTypeList({ getToolInputDisplayRenderTypeList({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment