| 1234567891011121314151617181920212223242526272829303132333435363738 |
- import type { UiMessage } from '@/components/messages/types'
- export type MessageFailure = { title: string; hint: string; detail: string; key: string; replacesContent: boolean }
- const failurePrefix = /^执行失败[::]\s*/
- /** Presentation only: retain the original transcript and diagnostics for inspection. */
- export function messageFailure(message: UiMessage): MessageFailure | undefined {
- if (message.communication || message.taskReference || message.role === 'tool') return
- if (message.status !== 'failed' && !message.error?.trim()) return
- const content = message.content.trim()
- const prefixed = message.role !== 'user' && message.status === 'failed' && failurePrefix.test(content)
- const error = message.error?.trim() || (prefixed ? content.replace(failurePrefix, '') : '')
- if (!error && message.status !== 'failed') return
- const providerNotice = message.role === 'assistant' && message.status === 'failed'
- && content.includes('Provider said:') && /didn[’']t respond in time on any of \d+ attempts/.test(content)
- const replacesContent = message.role !== 'user' && (prefixed || providerNotice || (!!error && content === error) || !content)
- const detail = [...new Set([replacesContent ? content.replace(failurePrefix, '') : '', error].filter(Boolean))]
- .filter((value, index, values) => !values.some((other, otherIndex) => otherIndex !== index && other.includes(value))).join('\n\n')
- const timeout = /timeout|timed?\s*out|no SSE events|didn[’']t respond in time|超时/i.test(error || detail)
- return {
- title: message.role === 'user' ? '消息未发送' : timeout ? '响应超时' : '回复暂时中断',
- hint: message.role === 'user' ? '请检查连接后重新发送。' : '请稍后重试,或切换模型。',
- detail, key: error || detail, replacesContent,
- }
- }
- /** Fold only the scheduler's immediately repeated system failure, never a user's message. */
- export function collapseDuplicateFailures(messages: UiMessage[]): UiMessage[] {
- return messages.filter((message, index) => {
- const previous = messages[index - 1]
- if (!previous || previous.role !== 'assistant' || message.role !== 'system' || !/^执行失败[::]/.test(message.content)) return true
- const failure = messageFailure(message), prior = messageFailure(previous)
- if (!failure?.key || failure.key !== prior?.key || message.attachments?.length || message.tools?.length || message.reasoning) return true
- if (message.runId && previous.runId) return message.runId !== previous.runId
- const at = new Date(message.createdAt ?? '').getTime(), before = new Date(previous.createdAt ?? '').getTime()
- return !Number.isFinite(at) || !Number.isFinite(before) || at < before || at - before > 2000
- })
- }
|