Commit a0aec83f by light5980 Committed by GitHub

fix(error-list): retry refresh (#7244)

* fix(error-list): retry refresh

* perf: tranining error reset

* fix: agentv2 remove system params when call tool

* remove log

---------

Co-authored-by: archer <545436317@qq.com>
parent 6c7ea32d
......@@ -42,6 +42,8 @@ The script only fills missing `appName` values. It does not overwrite existing v
## ⚙️ Improvements
1. Removed system field parameters when AgentV2 calls nested workflows.
## 🐛 Fixes
1. Workflow tool debugging did not show run details.
......
......@@ -42,6 +42,8 @@ curl -X POST "{{host}}/api/admin/initv4151" \
## ⚙️ 优化
1. AgentV2 调用嵌套工作流时候,去除系统字段参数。
## 🐛 修复
1. 工作流工具调试时,运行详情看不到。
......
......@@ -79,10 +79,10 @@
"content/guide/build/workflow/nodes/knowledge_base_search_merge.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/workflow/nodes/loop.en.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/workflow/nodes/loop.mdx": "2026-05-07T15:06:40+08:00",
"content/guide/build/workflow/nodes/loop_run.en.mdx": "2026-07-03T18:33:25+08:00",
"content/guide/build/workflow/nodes/loop_run.mdx": "2026-07-03T18:33:25+08:00",
"content/guide/build/workflow/nodes/parallel_run.en.mdx": "2026-07-03T18:33:25+08:00",
"content/guide/build/workflow/nodes/parallel_run.mdx": "2026-07-03T18:33:25+08:00",
"content/guide/build/workflow/nodes/loop_run.en.mdx": "2026-07-03T21:53:07+08:00",
"content/guide/build/workflow/nodes/loop_run.mdx": "2026-07-03T21:53:07+08:00",
"content/guide/build/workflow/nodes/parallel_run.en.mdx": "2026-07-03T21:53:07+08:00",
"content/guide/build/workflow/nodes/parallel_run.mdx": "2026-07-03T21:53:07+08:00",
"content/guide/build/workflow/nodes/question_classify.en.mdx": "2026-06-22T11:01:59+08:00",
"content/guide/build/workflow/nodes/question_classify.mdx": "2026-06-22T11:01:59+08:00",
"content/guide/build/workflow/nodes/reply.en.mdx": "2026-05-07T15:06:40+08:00",
......@@ -314,8 +314,8 @@
"content/self-host/upgrading/4-15/41506.mdx": "2026-07-01T12:13:58+08:00",
"content/self-host/upgrading/4-15/41507.en.mdx": "2026-06-30T17:31:43+08:00",
"content/self-host/upgrading/4-15/41507.mdx": "2026-06-30T17:31:43+08:00",
"content/self-host/upgrading/4-15/4151.en.mdx": "2026-07-02T15:38:53+08:00",
"content/self-host/upgrading/4-15/4151.mdx": "2026-07-03T18:54:27+08:00",
"content/self-host/upgrading/4-15/4151.en.mdx": "2026-07-03T21:53:07+08:00",
"content/self-host/upgrading/4-15/4151.mdx": "2026-07-03T21:53:07+08:00",
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
......
......@@ -165,6 +165,12 @@ export const replaceAgentFileIdsWithUrls = <T>(value: T, fileUrlMap: Record<stri
return replaceValue(value) as T;
};
const filterAgentWorkflowRuntimeParams = (params: Record<string, any>) => {
const runtimeParams = { ...params };
delete runtimeParams[NodeInputKeyEnum.forbidStream];
return runtimeParams;
};
/**
* 统一 Agent 的知识库配置来源。
*
......@@ -360,7 +366,7 @@ export const getExecuteTool = ({
nodeResponse
};
} else if (tool.type === 'workflow') {
const { userChatInput, ...params } = requestParams;
const { userChatInput, ...params } = filterAgentWorkflowRuntimeParams(requestParams);
const { response, usages, nodeResponse } = await dispatchApp({
app: {
......@@ -411,6 +417,7 @@ export const getExecuteTool = ({
return trueId;
}
})();
const customAppVariables = filterAgentWorkflowRuntimeParams(requestParams);
const { response, usages, nodeResponse } = await dispatchPlugin({
app: {
name: tool.name,
......@@ -418,7 +425,7 @@ export const getExecuteTool = ({
id
},
userChatInput: '',
customAppVariables: requestParams,
customAppVariables,
checkIsStopping,
lang,
requestOrigin,
......
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { SubAppIds } from '@fastgpt/global/core/workflow/node/agent/constants';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import {
getAgentDatasetParams,
getSubapps,
......@@ -13,12 +14,14 @@ const {
dispatchAgentDatasetSearchMock,
dispatchAppMock,
dispatchFileReadMock,
dispatchPluginMock,
dispatchToolMock,
getAgentRuntimeToolsMock
} = vi.hoisted(() => ({
dispatchAgentDatasetSearchMock: vi.fn(),
dispatchAppMock: vi.fn(),
dispatchFileReadMock: vi.fn(),
dispatchPluginMock: vi.fn(),
dispatchToolMock: vi.fn(),
getAgentRuntimeToolsMock: vi.fn(async () => [])
}));
......@@ -41,7 +44,7 @@ vi.mock('@fastgpt/service/core/workflow/dispatch/ai/agent/sub/dataset', () => ({
vi.mock('@fastgpt/service/core/workflow/dispatch/ai/agent/sub/app', () => ({
dispatchApp: dispatchAppMock,
dispatchPlugin: vi.fn()
dispatchPlugin: dispatchPluginMock
}));
describe('Agent read_files tool protocol', () => {
......@@ -584,4 +587,103 @@ describe('Agent read_files tool protocol', () => {
})
);
});
it('filters forbid stream params before dispatching agent workflow tools', async () => {
dispatchAppMock.mockResolvedValue({
response: 'workflow result',
usages: [],
nodeResponse: {
moduleName: 'Sub Workflow'
}
});
dispatchPluginMock.mockResolvedValue({
response: 'workflow tool result',
usages: [],
nodeResponse: {
moduleName: 'Workflow Tool'
}
});
const createExecuteTool = (type: 'workflow' | 'toolWorkflow') =>
getExecuteTool({
checkIsStopping: vi.fn(),
chatConfig: {},
runningUserInfo: {
teamId: 'team_1',
tmbId: 'tmb_1'
},
runningAppInfo: {
id: 'app_1'
},
chatId: 'chat_1',
uid: 'user_1',
variableState: {} as any,
externalProvider: {
openaiAccount: undefined
} as any,
lang: 'zh-CN',
requestOrigin: '',
mode: 'chat',
timezone: 'Asia/Shanghai',
retainDatasetCite: false,
maxRunTimes: 10,
workflowDispatchDeep: 1,
params: {
model: 'gpt-4'
},
stream: false,
getSubAppInfo: () => ({
name: 'Workflow Tool',
avatar: '',
toolDescription: ''
}),
getSubApp: () => ({
type,
id: 'workflow-tool',
name: 'Workflow Tool',
avatar: '',
params: {
[NodeInputKeyEnum.forbidStream]: false,
fixed: 'from-config'
}
}),
completionTools: [],
filesMap: {}
} as any);
await createExecuteTool('workflow')({
callId: 'call_workflow',
toolId: 'workflow-tool',
args: JSON.stringify({
userChatInput: 'hello',
[NodeInputKeyEnum.forbidStream]: true,
dynamic: 'from-call'
})
});
await createExecuteTool('toolWorkflow')({
callId: 'call_plugin',
toolId: 'workflow-tool',
args: JSON.stringify({
[NodeInputKeyEnum.forbidStream]: true,
dynamic: 'from-call'
})
});
expect(dispatchAppMock).toHaveBeenCalledWith(
expect.objectContaining({
customAppVariables: {
fixed: 'from-config',
dynamic: 'from-call'
}
})
);
expect(dispatchPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
customAppVariables: {
fixed: 'from-config',
dynamic: 'from-call'
}
})
);
});
});
......@@ -308,6 +308,9 @@ export function agentForm2AppWorkflow(
config: tool.inputs.reduce(
(acc, input) => {
if (input.key === NodeInputKeyEnum.forbidStream) {
return acc;
}
// Special tool
if (
tool.flowNodeType === FlowNodeTypeEnum.appModule &&
......
......@@ -294,7 +294,17 @@ const TrainingErrorList = ({
}
}
);
const clearLocalErrorData = useMemoizedFn(() => {
const resetErrorListState = useMemoizedFn(() => {
collectionAutoFillOffsetRef.current = undefined;
datasetAutoFillOffsetRef.current = undefined;
pendingDatasetScrollTopRef.current = undefined;
const scrollContainer =
scope.type === 'collection' ? collectionScrollRef.current : datasetScrollRef.current;
if (scrollContainer) {
scrollContainer.scrollTop = 0;
}
trainingErrorDataRef.current = [];
setData([]);
setTotal(0);
......@@ -335,10 +345,8 @@ const TrainingErrorList = ({
collectionId: updatedData.collectionId,
dataId: updatedData.dataId
});
} else {
clearLocalErrorData();
onRefresh?.();
}
onRefresh?.();
setEditChunk(undefined);
}
}
......@@ -440,12 +448,16 @@ const TrainingErrorList = ({
await updateData({
collectionId: scope.collectionId
});
// 批量重试已把当前最终异常重新放回训练队列;本地先清空,避免立刻读 secondary 时回填旧异常。
resetErrorListState();
onRefresh?.();
return;
}
await updateData({
datasetId: scope.datasetId
});
onRefresh?.();
onClose?.();
};
const captureDatasetScrollTop = () => {
......
......@@ -450,4 +450,42 @@ describe('appWorkflow2AgentForm', () => {
expect(restored.dataset.authTmbId).toBe(true);
});
it('should omit forbid stream from agent selected tool config', () => {
const form = getDefaultAppForm();
form.aiSettings = {
[NodeInputKeyEnum.aiModel]: 'qwen-3.6-flash',
[NodeInputKeyEnum.aiSystemPrompt]: 'You are a helpful agent.',
maxHistories: 6,
[NodeInputKeyEnum.aiChatIsResponseText]: true
};
form.selectedTools = [
{
id: 'workflow-tool',
pluginId: 'workflow-tool',
flowNodeType: FlowNodeTypeEnum.pluginModule,
inputs: [
{
key: NodeInputKeyEnum.forbidStream,
value: false
},
{
key: 'query',
value: 'hello'
}
],
outputs: []
} as any
];
const workflow = agentForm2AppWorkflow(form, mockT);
const agentNode = workflow.nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.agent);
const selectedTools = agentNode?.inputs.find(
(input) => input.key === NodeInputKeyEnum.selectedTools
)?.value as Array<{ config: Record<string, any> }>;
expect(selectedTools[0].config).toEqual({
query: 'hello'
});
});
});
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