mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-22 16:06:50 +00:00
50e2c257bf
Address @ShenAC-SAC's BUG-007 review on #3131. `task_tool.py` actually emits five terminal strings: - `Task Succeeded. Result: …` - `Task failed. …` - `Task timed out. …` - `Task cancelled by user.` ← previously matched none - `Task polling timed out after N minutes …` ← previously matched none The previous cut handled three; the last two fell through to the "unknown content" branch and pushed the subtask card back to `in_progress` even though the backend had already reached a terminal state. Add explicit matches plus regression tests for both. The `in_progress` fallback is now reserved for genuinely unrecognised output (i.e. contract drift), as documented. Refs: bytedance/deer-flow#3107 (BUG-007), bytedance/deer-flow#3131 review
77 lines
3.2 KiB
TypeScript
77 lines
3.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { parseSubtaskResult } from "@/core/tasks/subtask-result";
|
|
|
|
describe("parseSubtaskResult", () => {
|
|
it("recognises the standard success prefix", () => {
|
|
const parsed = parseSubtaskResult(
|
|
"Task Succeeded. Result: investigated and produced a 3-page report",
|
|
);
|
|
expect(parsed.status).toBe("completed");
|
|
expect(parsed.result).toBe("investigated and produced a 3-page report");
|
|
});
|
|
|
|
it("recognises the standard failure prefix", () => {
|
|
const parsed = parseSubtaskResult(
|
|
"Task failed. underlying tool raised RuntimeError",
|
|
);
|
|
expect(parsed.status).toBe("failed");
|
|
expect(parsed.error).toBe("underlying tool raised RuntimeError");
|
|
});
|
|
|
|
it("recognises the standard timeout prefix", () => {
|
|
const parsed = parseSubtaskResult("Task timed out after 900s");
|
|
expect(parsed.status).toBe("failed");
|
|
expect(parsed.error).toBe("Task timed out after 900s");
|
|
});
|
|
|
|
it("recognises the cancelled-by-user prefix", () => {
|
|
// bytedance/deer-flow#3131 review: this is one of the five terminal
|
|
// strings task_tool.py actually emits — the previous cut treated it as
|
|
// unrecognised content and pushed the card back to in_progress.
|
|
const parsed = parseSubtaskResult("Task cancelled by user.");
|
|
expect(parsed.status).toBe("failed");
|
|
expect(parsed.error).toBe("Task cancelled by user.");
|
|
});
|
|
|
|
it("recognises the polling-timed-out prefix", () => {
|
|
// Emitted by task_tool when the background polling loop runs out of
|
|
// budget waiting for the subagent to reach a terminal state.
|
|
const parsed = parseSubtaskResult(
|
|
"Task polling timed out after 15 minutes. This may indicate the background task is stuck. Status: RUNNING",
|
|
);
|
|
expect(parsed.status).toBe("failed");
|
|
expect(parsed.error).toContain("polling timed out");
|
|
});
|
|
|
|
it("treats middleware-wrapped tool errors as terminal failures", () => {
|
|
// bytedance/deer-flow issue #3107 BUG-007: the parent-visible ToolMessage
|
|
// produced by ToolErrorHandlingMiddleware never matches the three legacy
|
|
// prefixes, so subtask cards stay stuck on "in_progress".
|
|
const parsed = parseSubtaskResult(
|
|
"Error: Tool 'task' failed with TypeError: 'AsyncCallbackManager' object is not iterable. Continue with available context, or choose an alternative tool.",
|
|
);
|
|
expect(parsed.status).toBe("failed");
|
|
expect(parsed.error).toContain("AsyncCallbackManager");
|
|
});
|
|
|
|
it("treats any other Error: prefix as a terminal failure", () => {
|
|
const parsed = parseSubtaskResult("Error: subagent worker pool exhausted");
|
|
expect(parsed.status).toBe("failed");
|
|
});
|
|
|
|
it("keeps unrecognised non-error output as in_progress", () => {
|
|
// Streaming partial chunks should not flip the card to terminal early.
|
|
const parsed = parseSubtaskResult("Investigating ...");
|
|
expect(parsed.status).toBe("in_progress");
|
|
expect(parsed.error).toBeUndefined();
|
|
expect(parsed.result).toBeUndefined();
|
|
});
|
|
|
|
it("trims surrounding whitespace before matching prefixes", () => {
|
|
const parsed = parseSubtaskResult(" Task Succeeded. Result: ok ");
|
|
expect(parsed.status).toBe("completed");
|
|
expect(parsed.result).toBe("ok");
|
|
});
|
|
});
|