Skip to content

feat(webview): add batchNearby utility for smarter tool ask batching#1005

Open
easonLiangWorldedtech wants to merge 3 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:fix/batch-nearby-tool-asks
Open

feat(webview): add batchNearby utility for smarter tool ask batching#1005
easonLiangWorldedtech wants to merge 3 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:fix/batch-nearby-tool-asks

Conversation

@easonLiangWorldedtech

@easonLiangWorldedtech easonLiangWorldedtech commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

UI tool ask batching fails when models insert API rows between tool calls

Problem

When using models like qwen, the UI shows multiple separate prompts for the same type of tool call:

Zoo wants to read this file (a.ts)
Zoo wants to read this file (b.ts)
Zoo wants to edit this file (c.ts)

Instead of a single batched prompt:

Zoo wants to read these files:
- a.ts
- b.ts

Root Cause

The existing batchConsecutive() utility only merges tool asks that are truly adjacent in the message array. However, models like qwen insert low-information messages between tool calls during streaming:

  • say: "api_req_started" — API request metadata row
  • say: "api_req_finished" — API request completion row
  • say: "text" with empty content — partial streaming rows
  • say: "reasoning" — hidden reasoning text

These messages break the consecutive chain, so batchConsecutive stops merging.

Current Code (ChatView.tsx ~line 1272)

const readFileBatched = batchConsecutive(filtered, isReadFileAsk, synthesizeReadFileBatch)
const listFilesBatched = batchConsecutive(readFileBatched, isListFilesAsk, synthesizeListFilesBatch)
const result = batchConsecutive(listFilesBatched, isEditFileAsk, synthesizeEditFileBatch)

Proposed Solution: batchNearby() utility

A new utility that merges same-type tool asks even when separated by ignorable messages. It uses three predicates:

  1. isTarget — matches the target tool type (readFile, listFiles, editFile)
  2. isIgnorableBetweenTargets — skips over low-info messages without breaking the batch
  3. isBoundary — stops merging when hitting a semantic boundary

Ignorable Messages (skipped during batching)

  • api_req_started / api_req_finished
  • Empty text rows (say: "text" with no content)
  • Reasoning rows (say: "reasoning")

Semantic Boundaries (stop merging)

  • User feedback (user_feedback, user_feedback_diff)
  • Visible assistant text (text with content)
  • Completion result (completion_result)
  • Checkpoint saved (checkpoint_saved)
  • Errors (error)
  • Context condensation (condense_context)
  • Codebase search results (codebase_search_result)

New Usage in ChatView.tsx

const readFileBatched = batchNearby(filtered, {
  isTarget: isReadFileAsk,
  isIgnorableBetweenTargets, // api_req_started/finished, empty text, reasoning
  isBoundary,                // user_feedback, visible text, completion_result, error, etc.
  synthesize: synthesizeReadFileBatch,
})

Files Changed

  • New: webview-ui/src/utils/batchNearby.ts — the batching utility function
  • New: webview-ui/src/utils/__tests__/batchNearby.spec.ts — 23 comprehensive tests
  • Modified: webview-ui/src/components/chat/ChatView.tsx — replace batchConsecutive with batchNearby

Testing

All 23 unit tests pass, covering:

  • Empty input / no matches / single match passthrough
  • Consecutive vs non-consecutive matching
  • Ignorable messages between targets (api_req_started/finished, empty text, reasoning)
  • Semantic boundaries stopping the merge (user_feedback, visible text, completion_result, checkpoint_saved, error)
  • Multiple batches separated by boundaries
  • Realistic qwen scenarios with API rows between tool calls

Long-term Improvement (separate issue)

Add toolCallGroupId metadata to backend messages so UI can use explicit grouping instead of heuristic-based merging. This would make batching work consistently across all model providers regardless of streaming format differences.

Fixes #1004

Screen Cap

image

Summary by CodeRabbit

  • Improvements
    • Improved chat message grouping during streaming to combine related tool requests more reliably, even when brief or low-information rows appear between them.
    • Ensures key content types (such as user feedback, visible assistant text, errors, and completion results) remain correctly separated.
    • Preserves important semantic boundaries during multi-step operations for clearer, more consistent chat history.

Replace batchConsecutive with batchNearby in ChatView.tsx to allow
merging same-type tool asks even when separated by ignorable messages
(api_req_started/finished, empty text rows, reasoning).

Semantic boundaries (user_feedback, visible text, completion_result,
checkpoint_saved, error) stop the merge, preserving correct ordering.

This fixes UX where models like qwen insert API request rows between
tool calls, causing UI to show multiple 'Zoo wants to read this file'
instead of a single batched prompt.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Nearby tool-ask batching

Layer / File(s) Summary
Nearby batching utility
webview-ui/src/utils/batchNearby.ts
Adds configurable target, ignorable-row, boundary, and synthesis callbacks, then groups nearby targets while preserving interrupted and boundary rows.
Chat tool-ask integration
webview-ui/src/components/chat/ChatView.tsx
Uses nearby batching for read, list, and edit tool asks, allowing low-information streaming rows between matching requests.
Batching behavior validation
webview-ui/src/utils/__tests__/batchNearby.spec.ts
Tests passthrough, synthesis, separators, boundaries, immutability, callback grouping, and realistic streamed tool-call sequences.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: taltas

Sequence Diagram(s)

sequenceDiagram
  participant ChatView
  participant batchNearby
  participant ToolBatchSynthesizer
  ChatView->>batchNearby: classify streamed tool-ask rows
  batchNearby->>batchNearby: skip ignorable rows and stop at boundaries
  batchNearby->>ToolBatchSynthesizer: synthesize grouped tool asks
  ToolBatchSynthesizer-->>batchNearby: return batched tool-ask row
  batchNearby-->>ChatView: return processed chat rows
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy #1004 by batching around ignorable rows, preserving boundaries, and adding comprehensive tests.
Out of Scope Changes check ✅ Passed The PR stays focused on tool-ask batching and related tests without introducing unrelated changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title is concise and accurately summarizes the main change: introducing batchNearby for smarter tool-ask batching.
Description check ✅ Passed The PR description covers the issue, root cause, solution, tests, and screenshot, though it doesn't follow the template headings exactly.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@easonLiangWorldedtech
easonLiangWorldedtech marked this pull request as draft July 24, 2026 15:29
@easonLiangWorldedtech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.68293% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
webview-ui/src/components/chat/ChatView.tsx 72.72% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@webview-ui/src/utils/batchNearby.ts`:
- Around line 42-67: Update batchNearby in webview-ui/src/utils/batchNearby.ts
(lines 42-67) to track skipped isIgnorableBetweenTargets items in
pendingIgnorable, clear them only when a subsequent target is found, and restore
remaining items to result after finalizing the batch. Make no code change in
webview-ui/src/components/chat/ChatView.tsx (lines 1311-1328). Update the
boundary-message test in webview-ui/src/utils/__tests__/batchNearby.spec.ts
(lines 135-152) to expect four items, including the restored api_req_started
item between match-1 and visible text.
🪄 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 Plus

Run ID: 421939d2-6c08-49b5-989a-af52340d01f0

📥 Commits

Reviewing files that changed from the base of the PR and between 78c1410 and bd3efaa.

📒 Files selected for processing (3)
  • webview-ui/src/components/chat/ChatView.tsx
  • webview-ui/src/utils/__tests__/batchNearby.spec.ts
  • webview-ui/src/utils/batchNearby.ts

Comment thread webview-ui/src/utils/batchNearby.ts
When a single target is followed by ignorable messages and then a
boundary (no second target found), the original code silently dropped
those ignorable items — contradicting the documented contract that all
items are preserved in-order.

Fix: track skipped ignorable items in pendingIgnorable, flush them after
the batch result so they're never lost. When bridge succeeds (second
target found), pending items are consumed into the synthesized batch as
expected.
@easonLiangWorldedtech
easonLiangWorldedtech marked this pull request as ready for review July 24, 2026 15:54
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UI tool ask batching fails when models insert API rows between tool calls

3 participants