Skip to content

feat(ui): add message timing metrics#357

Open
pascalandr wants to merge 4 commits into
NeuralNomadsAI:devfrom
Pagecran:feat/message-timing-metrics
Open

feat(ui): add message timing metrics#357
pascalandr wants to merge 4 commits into
NeuralNomadsAI:devfrom
Pagecran:feat/message-timing-metrics

Conversation

@pascalandr
Copy link
Copy Markdown
Contributor

@pascalandr pascalandr commented Apr 23, 2026

Fixes #297

Summary

  • show total assistant response duration next to the existing message timestamp using only explicit OpenCode message timing
  • show reasoning duration on reasoning cards only when OpenCode provides explicit part timing
  • remove all client-side timing inference from updated timestamps, next-part boundaries, and message-completion fallback

Why

Client-derived durations are not reliable indicators of actual model thinking or processing time.

This revision keeps the UI scoped to explicit server-side timing data that OpenCode already provides, which matches the maintainer feedback on this PR.

Validation

  • node --experimental-strip-types --test "packages/ui/src/lib/message-timing.test.ts"
  • npm run build --workspace @codenomad/ui

@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented Apr 23, 2026

Gatekeeper review for the latest PR state:

What I reviewed

  • Request understood as: do a ruthless PR gatekeeper pass on the current branch, focused on regressions and better implementation options for the new message timing metrics.
  • Reviewed the changed code paths in packages/ui/src/lib/message-timing.ts, packages/ui/src/lib/message-timing.test.ts, packages/ui/src/components/message-item.tsx, packages/ui/src/components/message-block.tsx, and packages/ui/src/styles/messaging/message-base.css.

Findings

  • No blocking regressions found in the current implementation.
  • No clearly better implementation jumped out that would justify changing this PR. The timing logic is kept in one helper module as requested, and the UI integration stays narrow.

What I checked closely

  • packages/ui/src/lib/message-timing.ts:17-95
    Centralized timing extraction is small and coherent. The completion fallback rules are conservative, and reasoning duration inference prefers explicit values before inferring from the next timed part or message completion.
  • packages/ui/src/components/message-item.tsx:142-145 and :565-569
    Assistant-only total duration display is scoped correctly and does not affect user messages.
  • packages/ui/src/components/message-block.tsx:760-779, :1398-1405, and :1600-1604
    Reasoning cards receive inferred durations without spreading timing logic into the component tree.

Validation

  • node --experimental-strip-types --test "packages/ui/src/lib/message-timing.test.ts" passed.
  • npm run build --workspace @codenomad/ui could not be completed in this workspace because the environment is missing the vite package, so I could not fully verify build-level regressions from here.

Residual risk

  • The helper tests cover the inference rules well, but component-level rendering is still implicitly covered rather than directly tested. If you want this even tighter, the next incremental improvement would be a UI-level test around assistant header timing and reasoning-card timing display.

Gatekeeper verdict

  • Approved from a code-quality and regression-review standpoint, with the only unresolved item being build verification blocked by missing local dependencies in this environment.

--
Yours,
CodeNomadBot

@shantur
Copy link
Copy Markdown
Collaborator

shantur commented Apr 26, 2026

@pascalandr - Are these times provided by OpenCode or its being calculated in CodeNomad based on request and response?
If it's latter, then these times have no value at all, as its not thinking time or anything, it can be the time for the request reaching, queuing, and processing.

@pascalandr
Copy link
Copy Markdown
Contributor Author

pascalandr commented Apr 26, 2026

@pascalandr - Are these times provided by OpenCode or its being calculated in CodeNomad based on request and response? If it's latter, then these times have no value at all, as its not thinking time or anything, it can be the time for the request reaching, queuing, and processing.

It does use an explicit part duration if one is present, but otherwise it is just derived from the timing metadata we have in the stream.
A better version would be to only show explicit server-side timings, right ?
OpenCode does already provide some server-side timing data for messages and parts, so a basic elapsed duration could be meaningful.

@shantur
Copy link
Copy Markdown
Collaborator

shantur commented Apr 26, 2026

Agreed, we can look at this PR once OC times are used

@shantur shantur added the needs-work PR needs more work label Apr 26, 2026
@pascalandr
Copy link
Copy Markdown
Contributor Author

Updated this PR to only use explicit OpenCode timing data.

What changed:

  • total assistant duration now only uses explicit message timing (duration / time.duration) or time.created -> time.end
  • reasoning duration now only uses explicit part timing (duration / time.duration) or time.start -> time.end
  • removed all client-side inference from updated timestamps, next-part boundaries, and message-completion fallback

Validation:

  • node --experimental-strip-types --test "packages/ui/src/lib/message-timing.test.ts"
  • npm run build --workspace @codenomad/ui

@pascalandr
Copy link
Copy Markdown
Contributor Author

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented Apr 30, 2026

Gatekeeper review for the latest PR state:

What I understood

  • Request: perform a ruthless gatekeeper pass on the revised PR, specifically checking regressions and whether the implementation now correctly avoids unreliable client-side timing inference.
  • Assumption: the intended behavior is exactly what the updated PR body/comment says: show durations only from explicit OpenCode timing data, not from updated, stream ordering, next-part boundaries, or message-completion fallback.

Findings

  • No blocking regressions found.
  • The revised implementation now matches the maintainer feedback: duration display is limited to explicit duration / time.duration, or explicit start/end timing on the same OpenCode message/part.

What I checked

  • packages/ui/src/lib/message-timing.ts:29-38
    Message duration no longer uses local fallback timestamps or time.updated. It uses explicit duration first, then time.created -> time.end only when both are present on the message.
  • packages/ui/src/lib/message-timing.ts:44-60
    Reasoning duration no longer infers from next timed part or message completion. It only uses explicit part duration or explicit part start/created -> end timing.
  • packages/ui/src/lib/message-timing.test.ts:18-43
    Tests now cover the important negative cases: no message duration from updated, and no reasoning duration from message completion.
  • packages/ui/src/components/message-item.tsx:142-144 and :565-569
    Total duration remains assistant-only and empty durations render nothing.
  • packages/ui/src/components/message-block.tsx:760-779 and :1398-1405
    Reasoning-card duration is passed through the helper and displayed only when formatted duration exists.

Better implementation notes

  • Non-blocking: inferReasoningDurationMs is now a compatibility wrapper around getPartDurationMs, so the name is a bit stale. I would not block this PR on renaming it, because keeping the call sites stable makes the fix smaller.
  • Non-blocking: getMessageCompletedAt is exported but currently unused by changed code. It is harmless, but could be removed if there are no expected near-term callers.

Validation

  • node --experimental-strip-types --test "packages/ui/src/lib/message-timing.test.ts" passed.
  • npm run build --workspace @codenomad/ui could not run to completion in this workspace because vite is missing from local dependencies here (ERR_MODULE_NOT_FOUND: Cannot find package 'vite'). This appears to be an environment/dependency-install issue, not a code failure from this PR.

Gatekeeper verdict

  • Approved from code-quality and regression-review standpoint.
  • The prior core concern about client-derived durations has been addressed in the latest PR state.

--
Yours,
CodeNomadBot

pascalandr added 2 commits May 1, 2026 23:04
Resolve the message-block import conflict between the message timing metrics and the session search updates from dev.

This keeps the OpenCode timing helpers wired into reasoning cards while preserving the new session search match typing from dev.

Validated with the message timing unit tests, UI typecheck, and UI build after resolving the merge.
@pascalandr pascalandr removed the needs-work PR needs more work label May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

display thinking time, total response time

2 participants