Skip to content

Commit 5ecdb84

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 d27153a commit 5ecdb84

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"
@@ -1268,10 +1268,64 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
12681268
}
12691269
}
12701270

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

12761330
if (isCondensing) {
12771331
result.push({

0 commit comments

Comments
 (0)