Skip to content

Commit 47cea3b

Browse files
taltaseasonliang28
authored andcommitted
feat(webview): add batchNearby utility for smarter tool ask batching
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.
1 parent dcaa3cb commit 47cea3b

3 files changed

Lines changed: 531 additions & 5 deletions

File tree

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { LRUCache } from "lru-cache"
88
import { useDebounceEffect } from "@src/utils/useDebounceEffect"
99
import { appendImages } from "@src/utils/imageUtils"
1010
import { getCostBreakdownIfNeeded } from "@src/utils/costFormatting"
11-
import { batchConsecutive } from "@src/utils/batchConsecutive"
11+
import { batchNearby } from "@src/utils/batchNearby"
1212

1313
import type { ClineAsk, ClineSayTool, ClineMessage, ExtensionMessage, AudioType, SuggestionItem } from "@roo-code/types"
1414
import { getCompletionCheckpoint, getSuggestionMode, isRetiredProvider } from "@roo-code/types"
@@ -1279,10 +1279,64 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
12791279
}
12801280
}
12811281

1282-
// Consolidate consecutive ask messages into batches
1283-
const readFileBatched = batchConsecutive(filtered, isReadFileAsk, synthesizeReadFileBatch)
1284-
const listFilesBatched = batchConsecutive(readFileBatched, isListFilesAsk, synthesizeListFilesBatch)
1285-
const result = batchConsecutive(listFilesBatched, isEditFileAsk, synthesizeEditFileBatch)
1282+
// Messages that can be safely skipped over when batching tool asks.
1283+
// These are low-information or invisible messages that don't affect semantics:
1284+
// - api_req_started / api_req_finished (API request metadata rows)
1285+
// - empty text rows (partial streaming with no visible content)
1286+
// - reasoning rows (hidden from user by default)
1287+
const isIgnorableBetweenTargets = (msg: ClineMessage): boolean => {
1288+
if (msg.type !== "say") return false
1289+
return (
1290+
msg.say === "api_req_started" ||
1291+
msg.say === "api_req_finished" ||
1292+
(msg.say === "text" && !msg.text?.trim()) ||
1293+
msg.say === "reasoning"
1294+
)
1295+
}
1296+
1297+
// Semantic boundaries that stop batching. When we hit one of these,
1298+
// any current batch is finalized and the boundary message is preserved as-is:
1299+
// - user feedback / new user messages
1300+
// - visible assistant text (the model spoke to the user)
1301+
// - completion result (turn ended)
1302+
// - checkpoint saved
1303+
// - errors
1304+
const isBoundary = (msg: ClineMessage): boolean => {
1305+
if (msg.type !== "say") return false
1306+
return (
1307+
msg.say === "user_feedback" ||
1308+
msg.say === "user_feedback_diff" ||
1309+
(msg.say === "text" && !!msg.text?.trim()) ||
1310+
msg.say === "completion_result" ||
1311+
msg.say === "checkpoint_saved" ||
1312+
msg.say === "error" ||
1313+
msg.say === "condense_context" ||
1314+
msg.say === "codebase_search_result"
1315+
)
1316+
}
1317+
1318+
// Consolidate tool asks into batches, allowing ignorable messages between targets.
1319+
// Unlike batchConsecutive which only merges truly adjacent items, batchNearby
1320+
// skips over api_req_started/finished, empty text rows, and reasoning rows that
1321+
// models like qwen insert between tool calls during streaming.
1322+
const readFileBatched = batchNearby(filtered, {
1323+
isTarget: isReadFileAsk,
1324+
isIgnorableBetweenTargets,
1325+
isBoundary,
1326+
synthesize: synthesizeReadFileBatch,
1327+
})
1328+
const listFilesBatched = batchNearby(readFileBatched, {
1329+
isTarget: isListFilesAsk,
1330+
isIgnorableBetweenTargets,
1331+
isBoundary,
1332+
synthesize: synthesizeListFilesBatch,
1333+
})
1334+
const result = batchNearby(listFilesBatched, {
1335+
isTarget: isEditFileAsk,
1336+
isIgnorableBetweenTargets,
1337+
isBoundary,
1338+
synthesize: synthesizeEditFileBatch,
1339+
})
12861340

12871341
if (isCondensing) {
12881342
result.push({

0 commit comments

Comments
 (0)