mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-22 16:06:50 +00:00
fix(frontend): drop dead identity ternary and add opt-in export tests
Address review feedback on the previous export commit: 1. Removed the no-op `typeof msg.content === "string" ? msg.content : msg.content` expression in `formatThreadAsJSON`. Both branches returned the same value; the message content now flows through unchanged whether it is a string or the rich `MessageContent[]` shape (LangChain JSON-serialises the array structure correctly already). 2. Expanded the JSDoc on `ExportOptions` to make it clearer that the four flags are not currently wired to any UI control — callers wanting a debug export must build the options object explicitly. The default behaviour continues to match the explicit prescription in bytedance/deer-flow#3107 BUG-006. 3. Added opt-in coverage. The previous tests only exercised the `options = {}` default path; the new cases verify each flag flips the corresponding payload back into the export so a future debug-export surface does not silently break the contract. Refs: bytedance/deer-flow#3107 (BUG-006)
This commit is contained in:
@@ -15,12 +15,13 @@ import { titleOfThread } from "./utils";
|
||||
/**
|
||||
* Optional debug switches for advanced exports.
|
||||
*
|
||||
* Bytedance/deer-flow issue #3107 BUG-006: by default, the user-facing chat
|
||||
* export must include only the visible transcript. Internal payloads —
|
||||
* `hide_from_ui` messages, reasoning content, tool calls, and tool result
|
||||
* messages — stay out unless the caller explicitly opts in. There is no UI
|
||||
* surface for this today; the flags exist so a future "debug export" can
|
||||
* reuse the same formatter instead of forking it.
|
||||
* Bytedance/deer-flow issue #3107 BUG-006 explicitly prescribes that the
|
||||
* default export includes only the user-visible transcript and excludes
|
||||
* thinking/reasoning content, tool calls, tool results, hidden messages,
|
||||
* memory injection, and `<system-reminder>` payloads. These options let a
|
||||
* future "debug export" surface re-include any of those categories without
|
||||
* forking the formatter. They are not currently wired to any UI control —
|
||||
* callers that want them must construct the options object explicitly.
|
||||
*/
|
||||
export interface ExportOptions {
|
||||
includeReasoning?: boolean;
|
||||
@@ -134,7 +135,7 @@ export function formatThreadAsJSON(
|
||||
messages: visibleMessages(messages, options).map((msg) => ({
|
||||
type: msg.type,
|
||||
id: msg.id,
|
||||
content: typeof msg.content === "string" ? msg.content : msg.content,
|
||||
content: msg.content,
|
||||
...(options.includeToolCalls &&
|
||||
msg.type === "ai" &&
|
||||
msg.tool_calls?.length
|
||||
|
||||
@@ -104,6 +104,66 @@ describe("formatThreadAsMarkdown", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatThreadAsMarkdown opt-in flags", () => {
|
||||
it("emits reasoning when includeReasoning is true", () => {
|
||||
const message = ai("final answer", {
|
||||
additional_kwargs: {
|
||||
reasoning_content: "step-by-step chain of thought",
|
||||
},
|
||||
} as Partial<Message>);
|
||||
const md = formatThreadAsMarkdown(makeThread(), [message], {
|
||||
includeReasoning: true,
|
||||
});
|
||||
expect(md).toContain("step-by-step chain of thought");
|
||||
expect(md).toContain("Thinking");
|
||||
});
|
||||
|
||||
it("emits tool call rows when includeToolCalls is true", () => {
|
||||
const message = ai("ok", {
|
||||
tool_calls: [{ id: "1", name: "task", args: { description: "do work" } }],
|
||||
} as Partial<Message>);
|
||||
const md = formatThreadAsMarkdown(makeThread(), [message], {
|
||||
includeToolCalls: true,
|
||||
});
|
||||
expect(md).toContain("**Tool:**");
|
||||
expect(md).toContain("`task`");
|
||||
});
|
||||
|
||||
it("keeps hidden messages when includeHidden is true", () => {
|
||||
const hidden = human("internal reminder", {
|
||||
additional_kwargs: { hide_from_ui: true },
|
||||
} as Partial<Message>);
|
||||
const md = formatThreadAsMarkdown(makeThread(), [hidden], {
|
||||
includeHidden: true,
|
||||
});
|
||||
expect(md).toContain("internal reminder");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatThreadAsJSON opt-in flags", () => {
|
||||
it("emits tool_calls field when includeToolCalls is true", () => {
|
||||
const message = ai("ok", {
|
||||
tool_calls: [{ id: "1", name: "task", args: { description: "x" } }],
|
||||
} as Partial<Message>);
|
||||
const raw = formatThreadAsJSON(makeThread(), [message], {
|
||||
includeToolCalls: true,
|
||||
});
|
||||
expect(raw).toContain("tool_calls");
|
||||
expect(raw).toContain('"task"');
|
||||
});
|
||||
|
||||
it("keeps tool messages when includeToolMessages is true", () => {
|
||||
const raw = formatThreadAsJSON(
|
||||
makeThread(),
|
||||
[toolMsg("Task Succeeded. Result: keep me")],
|
||||
{ includeToolMessages: true },
|
||||
);
|
||||
const parsed = JSON.parse(raw) as { messages: { type: string }[] };
|
||||
expect(parsed.messages.some((m) => m.type === "tool")).toBe(true);
|
||||
expect(raw).toContain("keep me");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatThreadAsJSON", () => {
|
||||
it("strips hidden messages, tool messages, reasoning, and tool calls", () => {
|
||||
const messages = [
|
||||
|
||||
Reference in New Issue
Block a user