mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-17 01:56:18 +00:00
* feat(frontend): add expandable generic tool details in debug mode * fix: address tool call details review feedback * fix: preserve existing ellipsis keys in tool previews * fix(frontend): preserve tool preview property names * fix(frontend): keep bounded tool previews structurally complete * fix(frontend): avoid repeated array preview truncation markers * fix(frontend): preserve precision in tool result previews * fix(frontend): coalesce generated array tail markers
136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
||
|
||
import { mockLangGraphAPI } from "./utils/mock-api";
|
||
|
||
const threadId = "00000000-0000-0000-0000-000000004389";
|
||
|
||
for (const { debug, content, status, label } of [
|
||
{
|
||
debug: false,
|
||
content: "denied: " + "x".repeat(20000),
|
||
status: "error",
|
||
label: "Debug disabled",
|
||
},
|
||
{
|
||
debug: true,
|
||
content: "denied: " + "x".repeat(20000),
|
||
status: "error",
|
||
label: "bounded error",
|
||
},
|
||
{
|
||
debug: true,
|
||
content: '{"run_id":9223372036854775807,"ratio":0.1234567890123456789}',
|
||
status: "success",
|
||
label: "nested numeric precision",
|
||
},
|
||
{
|
||
debug: true,
|
||
content: "9223372036854775807",
|
||
status: "success",
|
||
label: "top-level numeric precision",
|
||
},
|
||
] as const) {
|
||
test(`generic tool details: ${label}`, async ({ page }) => {
|
||
await page.addInitScript((enabled) => {
|
||
localStorage.setItem(
|
||
"deerflow.local-settings",
|
||
JSON.stringify({
|
||
tokenUsage: {
|
||
headerTotal: true,
|
||
inlineMode: enabled ? "step_debug" : "per_turn",
|
||
},
|
||
}),
|
||
);
|
||
}, debug);
|
||
mockLangGraphAPI(page, {
|
||
threads: [
|
||
{
|
||
thread_id: threadId,
|
||
title: "Tool details",
|
||
messages: [
|
||
{ type: "human", id: "human", content: "Look up the run" },
|
||
{
|
||
type: "ai",
|
||
id: "ai-tool",
|
||
content: "",
|
||
tool_calls: [
|
||
{
|
||
id: "call-4389",
|
||
name: "mcp_lookup",
|
||
args: { run_id: "run-4389" },
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "tool",
|
||
id: "tool-result",
|
||
tool_call_id: "call-4389",
|
||
status,
|
||
content,
|
||
},
|
||
{ type: "ai", id: "answer", content: "The lookup failed." },
|
||
],
|
||
},
|
||
],
|
||
});
|
||
// 默认 mock 关闭了 Token Usage;这里启用入口,但不提供任何调用统计。
|
||
await page.route("**/api/models", (route) =>
|
||
route.fulfill({ json: { models: [], token_usage: { enabled: true } } }),
|
||
);
|
||
await page.goto(`/workspace/chats/${threadId}`);
|
||
await expect(
|
||
page.getByText("The lookup failed.", { exact: true }),
|
||
).toBeVisible();
|
||
const trigger = page.getByRole("button", {
|
||
name: "Tool details: mcp_lookup (call-4389)",
|
||
exact: true,
|
||
});
|
||
if (!debug) {
|
||
await expect(trigger).toHaveCount(0);
|
||
return;
|
||
}
|
||
await expect(trigger).toHaveAttribute("aria-expanded", "false");
|
||
await expect(
|
||
page.getByRole("region", { name: "Input", exact: true }),
|
||
).toHaveCount(0);
|
||
await trigger.click();
|
||
await expect(
|
||
page.getByRole("region", { name: "Input", exact: true }),
|
||
).toContainText("run-4389");
|
||
await expect(
|
||
page.getByRole("region", { name: "Call ID", exact: true }),
|
||
).toContainText("call-4389");
|
||
const error = page.getByRole("region", {
|
||
name: status === "error" ? "Error" : "Result",
|
||
exact: true,
|
||
});
|
||
if (status === "success") {
|
||
expect(await error.locator("pre").textContent()).toBe(content);
|
||
await expect(
|
||
error.getByText("Preview truncated", { exact: false }),
|
||
).toHaveCount(0);
|
||
await page
|
||
.context()
|
||
.grantPermissions(["clipboard-read", "clipboard-write"]);
|
||
await error
|
||
.getByRole("button", { name: "Copy to clipboard: Result", exact: true })
|
||
.click();
|
||
await expect
|
||
.poll(() => page.evaluate(() => navigator.clipboard.readText()))
|
||
.toBe(content);
|
||
return;
|
||
}
|
||
await expect(error).toContainText("denied:");
|
||
await expect(error).toContainText("Preview truncated");
|
||
expect(
|
||
(await error.locator("pre").textContent())?.length,
|
||
).toBeLessThanOrEqual(12000);
|
||
await page.screenshot({
|
||
path: test.info().outputPath("tool-details.png"),
|
||
fullPage: true,
|
||
});
|
||
await trigger.click();
|
||
await expect(error).toHaveCount(0);
|
||
});
|
||
}
|