applyGrouping function
GroupingResult
applyGrouping({
- required List<
NormalizedMessage> messages, - required Set<
String> toolsWithGrouping, - bool verbose = false,
Groups tool uses by message.id (same API response) if the tool supports grouped rendering.
Only groups 2+ tools of the same type from the same message. Also collects
corresponding tool_results and attaches them to the grouped message.
When verbose is true, skips grouping so messages render at original
positions.
Implementation
GroupingResult applyGrouping({
required List<NormalizedMessage> messages,
required Set<String> toolsWithGrouping,
bool verbose = false,
}) {
if (verbose) {
return GroupingResult(messages: messages);
}
// First pass: group tool uses by message.id + tool name
final groups = <String, List<NormalizedMessage>>{};
for (final msg in messages) {
final info = getToolUseInfo(msg);
if (info != null && toolsWithGrouping.contains(info.toolName)) {
final key = '${info.messageId}:${info.toolName}';
groups.putIfAbsent(key, () => []).add(msg);
}
}
// Identify valid groups (2+ items) and collect their tool use IDs
final validGroups = <String, List<NormalizedMessage>>{};
final groupedToolUseIds = <String>{};
for (final entry in groups.entries) {
if (entry.value.length >= 2) {
validGroups[entry.key] = entry.value;
for (final msg in entry.value) {
final info = getToolUseInfo(msg);
if (info != null) {
groupedToolUseIds.add(info.toolUseId);
}
}
}
}
// Collect result messages for grouped tool_uses
final resultsByToolUseId = <String, NormalizedMessage>{};
for (final msg in messages) {
if (msg.type != 'user') continue;
final content = msg.message['content'];
if (content is! List) continue;
for (final block in content) {
if (block is Map<String, dynamic> &&
block['type'] == 'tool_result' &&
groupedToolUseIds.contains(block['tool_use_id'])) {
resultsByToolUseId[block['tool_use_id'] as String] = msg;
}
}
}
// Second pass: build output, emitting each group only once
final result = <dynamic>[];
final emittedGroups = <String>{};
for (final msg in messages) {
final info = getToolUseInfo(msg);
if (info != null) {
final key = '${info.messageId}:${info.toolName}';
final group = validGroups[key];
if (group != null) {
if (!emittedGroups.contains(key)) {
emittedGroups.add(key);
final firstMsg = group.first;
// Collect results for this group
final results = <NormalizedMessage>[];
for (final assistantMsg in group) {
final content = assistantMsg.message['content'];
if (content is List && content.isNotEmpty) {
final toolUseId =
(content[0] as Map<String, dynamic>)['id'] as String?;
if (toolUseId != null) {
final resultMsg = resultsByToolUseId[toolUseId];
if (resultMsg != null) results.add(resultMsg);
}
}
}
result.add(
GroupedToolUseMessage(
toolName: info.toolName,
messages: group,
results: results,
displayMessage: firstMsg,
uuid: 'grouped-${firstMsg.uuid}',
timestamp: firstMsg.timestamp,
messageId: info.messageId,
),
);
}
continue;
}
}
// Skip user messages whose tool_results are all grouped
if (msg.type == 'user') {
final content = msg.message['content'];
if (content is List) {
final toolResults = content
.whereType<Map<String, dynamic>>()
.where((c) => c['type'] == 'tool_result')
.toList();
if (toolResults.isNotEmpty) {
final allGrouped = toolResults.every(
(tr) => groupedToolUseIds.contains(tr['tool_use_id']),
);
if (allGrouped) continue;
}
}
}
result.add(msg);
}
return GroupingResult(messages: result);
}