mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-08 13:58:38 +00:00
* fix(runs): show run duration once per run and label it as elapsed work turn_duration is the run's wall-clock lifetime (updated_at - created_at), but both message endpoints stamped it onto every AI message of the run and the frontend rendered each stamp as 'Thought for X seconds' — the same number repeated per message, and tool-wait time presented as model thinking latency (#4152). - share one stamping helper between list_messages and list_run_messages: only the run's final non-middleware AI message carries turn_duration (list_run_messages already tried to do this but iterated reversed() without stopping at the first match) - completed-state trigger copy becomes 'Worked for X seconds' since the number includes tool execution, not just thinking Fixes #4152 * fix(threads): stamp turn_duration once per run in /history replay too get_thread_history stamped turn_duration on every AI message of a run instead of only the last one, so a run with several AI messages (e.g. a tool call followed by a final answer) showed the same duration badge more than once on reload. Rebasing onto main picked up #4118, which replaced the /history duration path with a checkpoint-metadata fast path plus an event-store/run-manager fallback for legacy checkpoints - both of which stamped every AI message per run_id, reintroducing this same bug on both paths. This commit folds the once-per-run stamping (stamp_turn_duration_on_last_ai) into both paths instead of only the legacy one, and narrows the fast-path/fallback boundary to a per-run completeness check (turn_run_ids - checkpoint_run_durations) instead of a per-message one, so a run whose duration is already in checkpoint metadata never re-triggers the event-store correlation fallback just because its non-final AI messages correctly have no turn_duration. * fix(frontend): stop caching a client-measured turn_duration per message A run can produce several standalone content-only AI messages (e.g. a subagent handoff), each briefly becoming the newest message and each mounting its own Reasoning timer via the shared turnStartTime. Wiring onTurnDurationChange let that per-message timer cache and display a "Worked for X seconds" badge the moment a later message superseded it - with a different, premature number, since the timer only measured that message's own streaming window, not the run's total elapsed time. That reintroduced the #4152 duplicate-badge bug through a path the backend fix (once-per-run stamping) doesn't cover. cachedDuration now only ever mirrors a backend-confirmed turn_duration (already handled by the other effect here), so a superseded message can no longer display a stale or wrong duration. * fix(frontend): use 'Worked for' framing even without a persisted duration The completed-state copy diverged depending on whether turn_duration had landed yet: "Thought for a few seconds" (no duration) vs. "Worked for X seconds" (duration present). Both describe the same completed run, just with or without a persisted number, so the framing shouldn't disagree on what happened. * docs(runs): note why the middleware skip is inert on /history, add test stamp_turn_duration_on_last_ai's middleware-caller skip only has an effect on the event-store message shape (metadata.caller); checkpoint messages replayed on /history never carry that field. Document why that's not a gap - middleware writes go to thread metadata, not the messages channel, so no middleware message ever reaches a checkpoint's messages list. Also lock the middleware-skip contract on list_thread_messages with its own regression test, mirroring the existing one for list_run_messages - the thread feed only gained this skip through the same shared helper, and previously stamped every AI message, middleware included. * chore(frontend): retain current run duration UI The frontend behavior was superseded by #4348; keep the latest main implementation while retaining this PR's backend fix. * test(frontend): retain current reasoning coverage Align the old PR test with the frontend implementation already merged in #4348. --------- Co-authored-by: fancyboi999 <fancyboi999@users.noreply.github.com>