Files
hermes-ui/packages/client/src/components/hermes/chat/groupRunMessages.ts
T
toor 10ce9e5c28
Build / build (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s
fix(chat): separate merged output segments
2026-11-05 15:52:53 +08:00

50 lines
1.9 KiB
TypeScript

import type { Message } from '@/stores/hermes/chat'
// Group only within an uninterrupted turn. Tool rows remain available after
// the combined answer; user, command and system messages are hard boundaries.
export function groupRunMessages(messages: Message[], runActive: boolean): Message[] {
const result: Message[] = []
let assistants: Message[] = []
let tools: Message[] = []
let marker: string | null = null
function flush(completed: boolean) {
if (assistants.length) {
const first = assistants[0]
result.push({
...first,
toolMessages: tools.length ? tools : undefined,
contentSegments: assistants.map(m => m.content).filter(text => text.trim()),
id: assistants[assistants.length - 1].id,
content: assistants.map(m => m.content).filter(text => text.trim()).join('\n\n'),
reasoning: assistants.map(m => m.reasoning).filter(Boolean).join('\n\n') || undefined,
attachments: assistants.flatMap(m => m.attachments || []),
isStreaming: assistants.some(m => m.isStreaming),
})
}
if (tools.length && !assistants.length) {
result.push({ id: `tool-group-${tools[0].id}`, role: 'system', content: '', timestamp: tools[0].timestamp, systemType: 'tool-group', toolMessages: tools })
}
if (completed && assistants.length) {
result.push({ id: `run-end-${assistants[0].id}`, role: 'system', content: '', timestamp: assistants[assistants.length - 1].timestamp, systemType: 'run-end' })
}
assistants = []
tools = []
marker = null
}
for (const message of messages) {
if (message.role !== 'assistant' && message.role !== 'tool') {
flush(true)
result.push(message)
continue
}
if (marker && message.runMarker && marker !== message.runMarker) flush(true)
marker ||= message.runMarker || null
if (message.role === 'assistant') assistants.push(message)
else tools.push(message)
}
flush(!runActive)
return result
}