Files
deer-flow/frontend/tests/unit/core/tasks/presentation.test.ts
T
Nan GaoandGitHub aafd5077b2 feat(subagents): show effective model and token usage on task cards (#4049)
* feat(subagents): show runtime metadata on task cards

* fix(subagents): stop task-card render loop and dedupe model fetches

Address code review on the runtime-metadata cards:

- P1 render loop: the terminal ToolMessage is re-parsed on every
  MessageList render and always carries modelName/usage, so the
  presence-based setTasks condition fired a fresh state object each
  render -> "Maximum update depth exceeded". computeNextSubtask now
  returns a value-compared `changed` flag and a pure subtaskNotification()
  routes terminal transitions through the deferred after-render path
  while skipping no-op re-parses.

- Per-card useModels refetch: add staleTime: Infinity to the ["models"]
  query so every subtask card shares one /api/models fetch instead of
  refetching on each mount.

* make format

* refactor(subagents): dedupe token-usage validators + tidy event narrowing

Address PR review follow-ups:

- DRY: extract one shared token-usage validator per side. Backend
  status_contract.normalize_token_usage() now backs both the terminal
  ToolMessage metadata and the subagent.step/.end run events
  (step_events.py), and frontend messages/usage.normalizeTokenUsage()
  backs both the live task_running event (lifecycle.ts) and the terminal
  ToolMessage metadata (subtask-result.ts). Prevents the input/output/
  total_tokens validation from drifting across the four former copies.

- Nit: onCustomEvent narrows event.type once instead of re-checking the
  object shape per branch; the redundant task_started early-return
  (already validated by taskEventToSubtaskUpdate) is dropped.
2026-07-11 15:41:57 +08:00

37 lines
976 B
TypeScript

import { describe, expect, it } from "@rstest/core";
import {
formatSubtaskTokenUsage,
resolveSubtaskModelLabel,
} from "@/core/tasks/presentation";
describe("resolveSubtaskModelLabel", () => {
it("prefers the configured display name and falls back to the model identifier", () => {
expect(
resolveSubtaskModelLabel("claude-3-7-sonnet", [
{
id: "model-1",
name: "claude-3-7-sonnet",
model: "claude-3-7-sonnet@20250219",
display_name: "Claude 3.7 Sonnet",
},
]),
).toBe("Claude 3.7 Sonnet");
expect(resolveSubtaskModelLabel("unlisted-model", [])).toBe(
"unlisted-model",
);
});
it("formats only reported cumulative token usage", () => {
expect(formatSubtaskTokenUsage(undefined)).toBeUndefined();
expect(
formatSubtaskTokenUsage({
inputTokens: 10_000,
outputTokens: 2_345,
totalTokens: 12_345,
}),
).toBe("12.3K");
});
});