Skip to content

perf: lazy-load tool data on demand, fix reasoning state & action button visibility#4354

Merged
SivanCola merged 12 commits into
esengine:main-v2from
CVEngineer66:feat/lazy-tool-data
Jun 14, 2026
Merged

perf: lazy-load tool data on demand, fix reasoning state & action button visibility#4354
SivanCola merged 12 commits into
esengine:main-v2from
CVEngineer66:feat/lazy-tool-data

Conversation

@CVEngineer66

@CVEngineer66 CVEngineer66 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR addresses memory growth and UX issues in long-running Reasonix sessions across three areas: tool card memory optimization (99% string reduction), reasoning state tracking (eliminating the "still thinking" false state), and action button visibility during streaming. It also merges complementary reasoning display improvements from #4318.


1. Tool Memory Optimization

  • Archive on tool_result: Every completed tool call is archived immediately — args trimmed to 200 chars (command/subject preview), output dropped entirely. Collapsed cards only hold tool name + command in memory.
  • Lazy loading via Wails binding: New Controller.ToolResult() walks Session().Messages by ToolCallID. App.ToolResultForTab exposes it to the frontend. When a card is expanded, full data is fetched on demand.
  • History archived on restore: Session history loaded via history action is also archived, not just new turns.
  • ToolCard fullData reset on rewind: useEffect cleanup clears cached data when item identity changes.

Tested: 18 unit tests confirm archiving behavior, null-safety, and 99% reduction in tool string data (500-tool session: 7.5M → 100K chars).

Files: internal/control/controller.go, desktop/app.go, useController.ts, ToolCard.tsx, bridge.ts, tool-data-archive.test.ts


2. Reasoning State & Display

Files: Message.tsx, Transcript.tsx, useController.ts, reasoningDisplay.ts, reasoning-display.test.ts


3. Action Button Visibility

Copy/fork/summarize/rewind buttons below each assistant message are hidden while the model is actively generating (state.running === true). Previous completed turns' buttons remain visible with copy enabled. A running prop gates the hot zone's final pushTurnActions().

Files: Transcript.tsx, App.tsx


4. Rendering Performance

Three root-cause fixes for the streaming memory spike (600MB->2GB->600MB):

  • LiveAssistantMessage shown in useMemo -> AssistantMessage.memo no longer defeated by new object identity
  • Markdown: removed useDeferredValue (caused code-block flicker); normalizeMath in useMemo
  • HljsCode: wrapped in React.memo + useMemo — code blocks not re-highlighted when value unchanged
  • scrollVersion simplified: {id}:{streaming} / {id}:{status} instead of full length fields
  • Auto-scroll coalesced into one requestAnimationFrame per frame (from Fix streaming reasoning UI jank / 修复流式思考过程导致的界面卡顿 #4318)

Files: Transcript.tsx, Markdown.tsx, editors/HljsCode.tsx, render-optimization.test.ts


5. Display Mode Default

Removed "minimal" from DisplayMode. normalizeDisplayMode fallback changed to "standard" to match getDisplayMode.

Files: SettingsPanel.tsx, displayMode.ts, types.ts, config.go


Files Changed

16 files changed, 648 insertions(+), 59 deletions(-)

internal/control/controller.go          +43     # ToolResult() query
desktop/app.go                           +17     # ToolResultForTab binding
desktop/frontend/src/lib/useController.ts +36    # archive + reasoningComplete
desktop/frontend/src/lib/bridge.ts        +4     # AppBindings
desktop/frontend/src/components/ToolCard.tsx +47 # lazy load + fullData reset
desktop/frontend/src/components/Message.tsx +54   # reasoningComplete + #4318 merge
desktop/frontend/src/components/Transcript.tsx +75 # useMemo + running guard + #4318
desktop/frontend/src/components/Markdown.tsx +6   # remove useDeferredValue
desktop/frontend/src/components/editors/HljsCode.tsx +9  # React.memo + useMemo
desktop/frontend/src/components/SettingsPanel.tsx +2    # fix default
desktop/frontend/src/App.tsx              +1     # running prop
desktop/frontend/src/lib/reasoningDisplay.ts +39  # reasoning truncation (#4318)
desktop/frontend/src/__tests__/*          +370   # 3 test files
desktop/frontend/package.json             +4     # test suite

Prefix Cache Invariance

Zero impact. System prompt, tool schemas, session message serialization, and agent Run/Compose paths are untouched. Controller.ToolResult() is a read-only query over Session().Messages — it never mutates history.

wufengfan added 4 commits June 14, 2026 11:25
…uring streaming

- Add reasoningComplete flag to LiveStream to track when reasoning finishes
- Auto-close reasoning panel and update label when reasoningComplete becomes true
- Suppress current-turn action buttons (copy/fork/summarize/rewind) while model is running
- Keep previous completed turns' action buttons visible with copy enabled

useController.ts: reasoningComplete logic in reducer (turn_started/reasoning/text)
Message.tsx: reasoningComplete-driven auto-close, label toggle, data-running control
Transcript.tsx: running-guarded final pushTurnActions for hot zone
App.tsx: pass running={state.running} to Transcript
Add backend ToolResult endpoint (Controller + Wails binding App.ToolResultForTab)
to retrieve full tool args/output from Session.Messages by tool ID.
Frontend truncates oldest tool items (>100) to 200-char previews on turn_done,
loading full data via the backend when the card is expanded.

Prefix cache invariance: untouched — session messages, system prompt, and tool
schemas are never modified by this change.
12 tests verify: archiving triggers at threshold, truncates args to ≤200
chars, drops output to undefined, sets dataArchived flag, does not crash
on undefined output, does not archive running tools, and reduces total
string size by 33% in a 150-tool session.
Tool cards now only keep tool name + command (200-char args preview) in
items[] after tool_result. Full output is dropped immediately; full data
is loaded on demand via app.ToolResultForTab when the card is expanded.

Running tools still keep full args for subject/UI display; archiving only
triggers on tool_result (completion or error).

Test: 18 tests confirm every completed tool is archived, running tools
keep full data, undefined output doesn't crash, and a 500-tool session
sees 99% reduction in tool string data (7.5M chars → 100K chars).
@github-actions github-actions Bot added desktop Wails desktop app (desktop/**) agent Core agent loop (internal/agent, internal/control) v2 Go rewrite (1.x) — main-v2 branch, active development labels Jun 14, 2026
@CVEngineer66 CVEngineer66 changed the title feat: lazy-load tool data on demand + reasoningComplete state tracking perf: lazy-load tool data on demand, fix reasoning state & action button visibility Jun 14, 2026
wufengfan added 2 commits June 14, 2026 12:48
Three root-cause fixes for the 600MB→2GB→600MB memory spike during
streaming model output:

1. LiveAssistantMessage (Transcript.tsx): wrap  object in useMemo
   so React.memo on AssistantMessage is not defeated by new object
   identity on every render → stops the cascade through Markdown when
   text hasn't changed.

2. Markdown (Markdown.tsx): wrap normalizeMath(deferred) in useMemo so
   the full-text math normalization is skipped when deferred value is
   stable.

3. HljsCode (editors/HljsCode.tsx): add React.memo + useMemo for
   highlightToHtml so code blocks are not re-highlighted when value
   and language are unchanged.
14 tests proving the streaming markdown rendering fixes work:

- LiveAssistantMessage shown computation: stable output for stable input,
  updates only when live.text/reasoning change (Test 1)
- normalizeMath: deterministic, fast (100 calls in 0.5ms) (Test 2)
- highlightToHtml LRU cache: cached call (0.01ms) vs first call (5.67ms),
  streaming-adjacent calls under 1ms (Test 3)
- Streaming growth simulation: pattern confirmed correct (Test 4)
@CVEngineer66

Copy link
Copy Markdown
Contributor Author

Temporarily closing to merge complementary changes from #4318 and test locally

wufengfan added 4 commits June 14, 2026 13:34
historyMessagesToItems creates items with full args/output, bypassing the
tool_result reducer. Added archiving in the 'history' action handler so
history-loaded tools also get args truncated and output dropped.
useDeferredValue caused the rendered markdown to jump from the stale
(shorter) deferred text to the current (longer) value, creating a
visible flash when streaming long code blocks. Using text directly
ensures incremental rendering with no jumps.

The rAF batch already provides sufficient coalescing (~16ms per flush)
for smooth streaming without useDeferredValue.
- E2: normalizeDisplayMode default changed from 'compact' to 'standard'
  to match getDisplayMode (fixes inconsistent fallback)
- A4: history archive skips items with empty output string (avoids
  unnecessary object allocation for already-small data)
- A5: ToolCard resets fullData cache when item reference changes
  (prevents stale tool data after rewind)
- D3: confirmed MAX=30 cap already exists in breadcrumbs.ts (no fix
  needed — review was a false alarm)
@CVEngineer66 CVEngineer66 reopened this Jun 14, 2026
Combined test suites from both branches: reasoning-display.test.ts,
crash-reporting.test.ts (upstream), tool-data-archive.test.ts,
render-optimization.test.ts (ours).
Use a session snapshot when resolving archived tool data so the desktop query does not race live session writes. Also make archived tool cards expandable when only lazy-loaded output is available, restore shell previews from fetched output, and keep the new frontend tests typecheck-safe and deterministic.

Co-authored-by: SivanCola <32437197+SivanCola@users.noreply.github.com>
@SivanCola

Copy link
Copy Markdown
Collaborator

@codex review

@SivanCola SivanCola left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approve

@SivanCola SivanCola merged commit a05c193 into esengine:main-v2 Jun 14, 2026
13 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: cd4376bcea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) desktop Wails desktop app (desktop/**) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants