Files
deer-flow/frontend/tests/unit/core/tasks/lifecycle.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

43 lines
1.0 KiB
TypeScript

import { describe, expect, it } from "@rstest/core";
import { taskEventToSubtaskUpdate } from "@/core/tasks/lifecycle";
describe("taskEventToSubtaskUpdate", () => {
it("maps a task-start event to the effective model for that task", () => {
expect(
taskEventToSubtaskUpdate({
type: "task_started",
task_id: "call-1",
description: "Research auth",
model_name: "claude-3-7-sonnet",
}),
).toEqual({
id: "call-1",
modelName: "claude-3-7-sonnet",
});
});
it("maps a running event to its cumulative token snapshot", () => {
expect(
taskEventToSubtaskUpdate({
type: "task_running",
task_id: "call-1",
model_name: "claude-3-7-sonnet",
usage: {
input_tokens: 100,
output_tokens: 20,
total_tokens: 120,
},
}),
).toEqual({
id: "call-1",
modelName: "claude-3-7-sonnet",
usage: {
inputTokens: 100,
outputTokens: 20,
totalTokens: 120,
},
});
});
});