Coalesce duplicate widget tool requests - #606
Conversation
📝 WalkthroughWalkthroughThe tool bridge now coalesces identical concurrent calls using canonical request keys, while the UI event tracker suppresses repeated normalized events within 250 milliseconds. Integration tests cover canonicalization, sequential behavior, duplicate events, and bounded cache eviction. ChangesDeduplication controls
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant WidgetCall
participant createToolBridge
participant callToolOnce
participant HostCallTool
WidgetCall->>createToolBridge: callTool(name, normalized args)
createToolBridge->>callToolOnce: start one in-flight request
callToolOnce->>HostCallTool: execute tool call
HostCallTool-->>callToolOnce: return result
callToolOnce-->>createToolBridge: resolve shared promise
createToolBridge-->>WidgetCall: return shared result
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/ui/shared/tool-bridge.ts`:
- Around line 73-80: The deduplication keys use inconsistent serialization and
collapse distinct values such as NaN and null. In src/ui/shared/tool-bridge.ts
lines 73-80, update stableStringify to provide the shared canonical sorted-key
encoding, preserving distinct type-safe representations for non-finite numbers
and invalid runtime values or rejecting them; in
src/ui/shared/ui-event-tracker.ts line 38, replace raw normalizedParams
serialization with the same shared encoder so equivalent objects with different
insertion order produce identical keys. Ensure both utilities use this single
canonical implementation.
In `@src/ui/shared/ui-event-tracker.ts`:
- Around line 45-49: Update the recentEvents maintenance logic to enforce a hard
maximum of 100 entries: after removing stale entries in the existing sweep,
evict the oldest remaining entries until recentEvents.size is at most 100,
preserving newer timestamps and the duplicate-window behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b7018710-920b-44b8-8b57-681bb9a009e3
📒 Files selected for processing (3)
src/ui/shared/tool-bridge.tssrc/ui/shared/ui-event-tracker.tstest/test-ui-event-tracking.js
There was a problem hiding this comment.
🧹 Nitpick comments (1)
test/test-ui-event-tracking.js (1)
136-150: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd a regression test for refreshed cache entries.
The loop at Line [144] only tests eviction of a never-revisited key. In the supplied tracker implementation, updating an existing
Mapentry does not move it to the end; after a key is accepted again beyond 250 ms, adding 100 unique events can evict that freshly refreshed key and allow a duplicate within the suppression window. Add a deterministic-clock or focused timing case that refreshesindex: 0, adds 100 unique events, then repeats it immediately.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/test-ui-event-tracking.js` around lines 136 - 150, Extend testUiEventCacheStaysBounded with a deterministic-clock or focused timing case that waits past the 250 ms suppression interval, tracks index: 0 to refresh it, adds 100 unique events, then repeats index: 0 immediately. Assert the refreshed key remains suppressed, preventing an additional call and covering cache recency after an existing entry is accepted again.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@test/test-ui-event-tracking.js`:
- Around line 136-150: Extend testUiEventCacheStaysBounded with a
deterministic-clock or focused timing case that waits past the 250 ms
suppression interval, tracks index: 0 to refresh it, adds 100 unique events,
then repeats index: 0 immediately. Assert the refreshed key remains suppressed,
preventing an additional call and covering cache recency after an existing entry
is accepted again.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8fe582dd-7808-408c-b0a6-82ced118c5fc
📒 Files selected for processing (4)
src/ui/shared/canonical-key.tssrc/ui/shared/tool-bridge.tssrc/ui/shared/ui-event-tracker.tstest/test-ui-event-tracking.js
🚧 Files skipped from review as they are similar to previous changes (1)
- src/ui/shared/ui-event-tracker.ts
insane66613
left a comment
There was a problem hiding this comment.
Current head 2e5db87896d25c081f01d6032251c51390116ac5 addresses the prior canonicalization and bounded-cache findings. The PR is ready for maintainer re-review. One automated nit remains requesting a focused regression test for refreshed cache-entry recency; it is non-blocking to the core implementation but should be considered before merge.
Summary
Verification
Summary by CodeRabbit
Performance
Bug Fixes
New Features
Tests