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 ...@@ -42,6 +42,8 @@ The script only fills missing `appName` values. It does not overwrite existing v
## ⚙️ Improvements ## ⚙️ Improvements
1. Removed system field parameters when AgentV2 calls nested workflows.
## 🐛 Fixes ## 🐛 Fixes
1. Workflow tool debugging did not show run details. 1. Workflow tool debugging did not show run details.
......
...@@ -42,6 +42,8 @@ curl -X POST "{{host}}/api/admin/initv4151" \ ...@@ -42,6 +42,8 @@ curl -X POST "{{host}}/api/admin/initv4151" \
## ⚙️ 优化 ## ⚙️ 优化
1. AgentV2 调用嵌套工作流时候,去除系统字段参数。
## 🐛 修复 ## 🐛 修复
1. 工作流工具调试时,运行详情看不到。 1. 工作流工具调试时,运行详情看不到。
......
...@@ -79,10 +79,10 @@ ...@@ -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/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.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.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.en.mdx": "2026-07-03T21:53:07+08:00",
"content/guide/build/workflow/nodes/loop_run.mdx": "2026-07-03T18:33:25+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-03T18:33:25+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-03T18:33:25+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.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/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", "content/guide/build/workflow/nodes/reply.en.mdx": "2026-05-07T15:06:40+08:00",
...@@ -314,8 +314,8 @@ ...@@ -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/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.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/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.en.mdx": "2026-07-03T21:53:07+08:00",
"content/self-host/upgrading/4-15/4151.mdx": "2026-07-03T18:54:27+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.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/40.mdx": "2026-04-26T21:08:47+08:00",
"content/self-host/upgrading/outdated/41.en.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 ...@@ -165,6 +165,12 @@ export const replaceAgentFileIdsWithUrls = <T>(value: T, fileUrlMap: Record<stri
return replaceValue(value) as T; return replaceValue(value) as T;
}; };
const filterAgentWorkflowRuntimeParams = (params: Record<string, any>) => {
const runtimeParams = { ...params };
delete runtimeParams[NodeInputKeyEnum.forbidStream];
return runtimeParams;
};
/** /**
* 统一 Agent 的知识库配置来源。 * 统一 Agent 的知识库配置来源。
* *
...@@ -360,7 +366,7 @@ export const getExecuteTool = ({ ...@@ -360,7 +366,7 @@ export const getExecuteTool = ({
nodeResponse nodeResponse
}; };
} else if (tool.type === 'workflow') { } else if (tool.type === 'workflow') {
const { userChatInput, ...params } = requestParams; const { userChatInput, ...params } = filterAgentWorkflowRuntimeParams(requestParams);
const { response, usages, nodeResponse } = await dispatchApp({ const { response, usages, nodeResponse } = await dispatchApp({
app: { app: {
...@@ -411,6 +417,7 @@ export const getExecuteTool = ({ ...@@ -411,6 +417,7 @@ export const getExecuteTool = ({
return trueId; return trueId;
} }
})(); })();
const customAppVariables = filterAgentWorkflowRuntimeParams(requestParams);
const { response, usages, nodeResponse } = await dispatchPlugin({ const { response, usages, nodeResponse } = await dispatchPlugin({
app: { app: {
name: tool.name, name: tool.name,
...@@ -418,7 +425,7 @@ export const getExecuteTool = ({ ...@@ -418,7 +425,7 @@ export const getExecuteTool = ({
id id
}, },
userChatInput: '', userChatInput: '',
customAppVariables: requestParams, customAppVariables,
checkIsStopping, checkIsStopping,
lang, lang,
requestOrigin, requestOrigin,
......
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
import { SubAppIds } from '@fastgpt/global/core/workflow/node/agent/constants'; import { SubAppIds } from '@fastgpt/global/core/workflow/node/agent/constants';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { import {
getAgentDatasetParams, getAgentDatasetParams,
getSubapps, getSubapps,
...@@ -13,12 +14,14 @@ const { ...@@ -13,12 +14,14 @@ const {
dispatchAgentDatasetSearchMock, dispatchAgentDatasetSearchMock,
dispatchAppMock, dispatchAppMock,
dispatchFileReadMock, dispatchFileReadMock,
dispatchPluginMock,
dispatchToolMock, dispatchToolMock,
getAgentRuntimeToolsMock getAgentRuntimeToolsMock
} = vi.hoisted(() => ({ } = vi.hoisted(() => ({
dispatchAgentDatasetSearchMock: vi.fn(), dispatchAgentDatasetSearchMock: vi.fn(),
dispatchAppMock: vi.fn(), dispatchAppMock: vi.fn(),
dispatchFileReadMock: vi.fn(), dispatchFileReadMock: vi.fn(),
dispatchPluginMock: vi.fn(),
dispatchToolMock: vi.fn(), dispatchToolMock: vi.fn(),
getAgentRuntimeToolsMock: vi.fn(async () => []) getAgentRuntimeToolsMock: vi.fn(async () => [])
})); }));
...@@ -41,7 +44,7 @@ vi.mock('@fastgpt/service/core/workflow/dispatch/ai/agent/sub/dataset', () => ({ ...@@ -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', () => ({ vi.mock('@fastgpt/service/core/workflow/dispatch/ai/agent/sub/app', () => ({
dispatchApp: dispatchAppMock, dispatchApp: dispatchAppMock,
dispatchPlugin: vi.fn() dispatchPlugin: dispatchPluginMock
})); }));
describe('Agent read_files tool protocol', () => { describe('Agent read_files tool protocol', () => {
...@@ -584,4 +587,103 @@ 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( ...@@ -308,6 +308,9 @@ export function agentForm2AppWorkflow(
config: tool.inputs.reduce( config: tool.inputs.reduce(
(acc, input) => { (acc, input) => {
if (input.key === NodeInputKeyEnum.forbidStream) {
return acc;
}
// Special tool // Special tool
if ( if (
tool.flowNodeType === FlowNodeTypeEnum.appModule && tool.flowNodeType === FlowNodeTypeEnum.appModule &&
......
...@@ -294,7 +294,17 @@ const TrainingErrorList = ({ ...@@ -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 = []; trainingErrorDataRef.current = [];
setData([]); setData([]);
setTotal(0); setTotal(0);
...@@ -335,10 +345,8 @@ const TrainingErrorList = ({ ...@@ -335,10 +345,8 @@ const TrainingErrorList = ({
collectionId: updatedData.collectionId, collectionId: updatedData.collectionId,
dataId: updatedData.dataId dataId: updatedData.dataId
}); });
} else { onRefresh?.();
clearLocalErrorData();
} }
onRefresh?.();
setEditChunk(undefined); setEditChunk(undefined);
} }
} }
...@@ -440,12 +448,16 @@ const TrainingErrorList = ({ ...@@ -440,12 +448,16 @@ const TrainingErrorList = ({
await updateData({ await updateData({
collectionId: scope.collectionId collectionId: scope.collectionId
}); });
// 批量重试已把当前最终异常重新放回训练队列;本地先清空,避免立刻读 secondary 时回填旧异常。
resetErrorListState();
onRefresh?.();
return; return;
} }
await updateData({ await updateData({
datasetId: scope.datasetId datasetId: scope.datasetId
}); });
onRefresh?.();
onClose?.(); onClose?.();
}; };
const captureDatasetScrollTop = () => { const captureDatasetScrollTop = () => {
......
...@@ -450,4 +450,42 @@ describe('appWorkflow2AgentForm', () => { ...@@ -450,4 +450,42 @@ describe('appWorkflow2AgentForm', () => {
expect(restored.dataset.authTmbId).toBe(true); 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