feat(app): add a tool-call construction indicator #343

Merged
rwachters merged 1 commit from feat/app-tool-call-construction-indicator into master 2026-10-11 23:48:38 +00:00
rwachters commented 2026-10-11 23:48:05 +00:00 (Migrated from github.com)

Summary

Shows a "Writing tool calls…" label inside an assistant message while the model is still streaming a tool call, so the message no longer looks idle between the first delta and the arrival of the complete calls. The change is client-only and adds no server, worker, or protocol surface.

Changes

  • Added the tool-call slot in assistant messages: the construction label while a call is being written, the existing badges once the calls arrive, never both.
  • Added a rolling five-second freshness window measured from the most recent tool-call delta, so a stalled stream lets the label lapse on its own.
  • Added per-message delta instants to ChatState, cleared when the calls arrive complete, when the message or turn ends, when the stream errors, and on state reset.
  • Added the tool_call_construction_indicator string resource.
  • Documented why ToolCallDelta is deliberately not cached by the session repository, since only the complete call set is displayable.
  • Added tests for the freshness rule, the state bookkeeping and clearing, and the collector's note/forget behaviour.

Implementation notes

  • The indicator is derived from the recorded instant rather than accumulated, and the Compose producer stops as soon as the window lapses or no record exists, so no polling loop runs while nothing is fresh.
  • The record lives in memory only and holds one instant per message, overwritten by every later delta; leaving TurnExecutionState.RUNNING and resetState discard it so a reused session slot cannot show a stale label.
  • The label is intentionally static — no spinner, no animation, and no partial argument text, which would present a reconstruction of the call rather than the call.
  • The freshness window is a fixed compile-time constant; making it configurable was considered unnecessary.

Validation

  • ./gradlew :app:desktopMainClasses :app:desktopTestClasses — success
  • ./gradlew :app:desktopTest — 1071 tests, 0 failures, 2 pre-existing skips
  • ./gradlew :app:compileKotlinWasmJs — success

Risks and follow-up

  • The window and the re-evaluation tick are separate constants (5 s and 1 s); the label may linger for at most about one tick past the window, which is unnoticeable while the message is still streaming.
  • Three LOW-severity review findings were accepted as follow-ups and are not addressed in this pull request.
## Summary Shows a "Writing tool calls…" label inside an assistant message while the model is still streaming a tool call, so the message no longer looks idle between the first delta and the arrival of the complete calls. The change is client-only and adds no server, worker, or protocol surface. ## Changes - Added the tool-call slot in assistant messages: the construction label while a call is being written, the existing badges once the calls arrive, never both. - Added a rolling five-second freshness window measured from the most recent tool-call delta, so a stalled stream lets the label lapse on its own. - Added per-message delta instants to `ChatState`, cleared when the calls arrive complete, when the message or turn ends, when the stream errors, and on state reset. - Added the `tool_call_construction_indicator` string resource. - Documented why `ToolCallDelta` is deliberately not cached by the session repository, since only the complete call set is displayable. - Added tests for the freshness rule, the state bookkeeping and clearing, and the collector's note/forget behaviour. ## Implementation notes - The indicator is derived from the recorded instant rather than accumulated, and the Compose producer stops as soon as the window lapses or no record exists, so no polling loop runs while nothing is fresh. - The record lives in memory only and holds one instant per message, overwritten by every later delta; leaving `TurnExecutionState.RUNNING` and `resetState` discard it so a reused session slot cannot show a stale label. - The label is intentionally static — no spinner, no animation, and no partial argument text, which would present a reconstruction of the call rather than the call. - The freshness window is a fixed compile-time constant; making it configurable was considered unnecessary. ## Validation - `./gradlew :app:desktopMainClasses :app:desktopTestClasses` — success - `./gradlew :app:desktopTest` — 1071 tests, 0 failures, 2 pre-existing skips - `./gradlew :app:compileKotlinWasmJs` — success ## Risks and follow-up - The window and the re-evaluation tick are separate constants (5 s and 1 s); the label may linger for at most about one tick past the window, which is unnoticeable while the message is still streaming. - Three LOW-severity review findings were accepted as follow-ups and are not addressed in this pull request.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Torvian.eu/chatbot!343
No description provided.