show chat run errors as agent messages (#887)
This commit is contained in:
@@ -467,6 +467,18 @@ export function disconnectChatRun(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeSocketListener(socket: Socket, event: string, handler: (...args: any[]) => void): void {
|
||||||
|
const candidate = socket as Socket & {
|
||||||
|
off?: (event: string, handler: (...args: any[]) => void) => Socket
|
||||||
|
removeListener?: (event: string, handler: (...args: any[]) => void) => Socket
|
||||||
|
}
|
||||||
|
if (typeof candidate.off === 'function') {
|
||||||
|
candidate.off(event, handler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
candidate.removeListener?.(event, handler)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a chat run via Socket.IO and stream events back.
|
* Start a chat run via Socket.IO and stream events back.
|
||||||
* Returns an AbortController-compatible handle for cancellation.
|
* Returns an AbortController-compatible handle for cancellation.
|
||||||
@@ -500,6 +512,23 @@ export function startRunViaSocket(
|
|||||||
|
|
||||||
let closed = false
|
let closed = false
|
||||||
const socket = connectChatRun()
|
const socket = connectChatRun()
|
||||||
|
const handleSocketError = (err: Error) => {
|
||||||
|
if (closed) return
|
||||||
|
closed = true
|
||||||
|
sessionEventHandlers.delete(sid)
|
||||||
|
onError(err)
|
||||||
|
}
|
||||||
|
socket.once('connect_error', handleSocketError)
|
||||||
|
const handleSocketDisconnect = (reason: string) => {
|
||||||
|
if (closed || reason === 'io client disconnect') return
|
||||||
|
handleSocketError(new Error(`Socket disconnected: ${reason}`))
|
||||||
|
}
|
||||||
|
socket.once('disconnect', handleSocketDisconnect)
|
||||||
|
|
||||||
|
const removeTerminalSocketListeners = () => {
|
||||||
|
removeSocketListener(socket, 'connect_error', handleSocketError)
|
||||||
|
removeSocketListener(socket, 'disconnect', handleSocketDisconnect)
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionEventHandlers.has(sid)) {
|
if (sessionEventHandlers.has(sid)) {
|
||||||
socket.emit('run', body)
|
socket.emit('run', body)
|
||||||
@@ -548,6 +577,7 @@ export function startRunViaSocket(
|
|||||||
onEvent(evt)
|
onEvent(evt)
|
||||||
if ((evt as any).queue_remaining > 0) return
|
if ((evt as any).queue_remaining > 0) return
|
||||||
closed = true
|
closed = true
|
||||||
|
removeTerminalSocketListeners()
|
||||||
onDone()
|
onDone()
|
||||||
},
|
},
|
||||||
onRunFailed: (evt: RunEvent) => {
|
onRunFailed: (evt: RunEvent) => {
|
||||||
@@ -555,7 +585,8 @@ export function startRunViaSocket(
|
|||||||
onEvent(evt)
|
onEvent(evt)
|
||||||
if ((evt as any).queue_remaining > 0) return
|
if ((evt as any).queue_remaining > 0) return
|
||||||
closed = true
|
closed = true
|
||||||
onError(new Error(evt.error || 'Run failed'))
|
removeTerminalSocketListeners()
|
||||||
|
onDone()
|
||||||
},
|
},
|
||||||
onCompressionStarted: (evt: RunEvent) => {
|
onCompressionStarted: (evt: RunEvent) => {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
@@ -574,6 +605,7 @@ export function startRunViaSocket(
|
|||||||
onEvent(evt)
|
onEvent(evt)
|
||||||
if ((evt as any).queue_length > 0) return
|
if ((evt as any).queue_length > 0) return
|
||||||
closed = true
|
closed = true
|
||||||
|
removeTerminalSocketListeners()
|
||||||
onDone()
|
onDone()
|
||||||
},
|
},
|
||||||
onUsageUpdated: (evt: RunEvent) => {
|
onUsageUpdated: (evt: RunEvent) => {
|
||||||
@@ -585,6 +617,7 @@ export function startRunViaSocket(
|
|||||||
onEvent(evt)
|
onEvent(evt)
|
||||||
if ((evt as any).terminal === false) return
|
if ((evt as any).terminal === false) return
|
||||||
closed = true
|
closed = true
|
||||||
|
removeTerminalSocketListeners()
|
||||||
sessionEventHandlers.delete(sid)
|
sessionEventHandlers.delete(sid)
|
||||||
onDone()
|
onDone()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export interface ChatMessage {
|
|||||||
tool_call_id?: string | null
|
tool_call_id?: string | null
|
||||||
tool_calls?: any[] | null
|
tool_calls?: any[] | null
|
||||||
tool_name?: string | null
|
tool_name?: string | null
|
||||||
finish_reason?: string | null
|
finish_reason?: 'streaming' | 'tool_calls' | 'error' | string | null
|
||||||
reasoning?: string | null
|
reasoning?: string | null
|
||||||
reasoning_details?: string | null
|
reasoning_details?: string | null
|
||||||
reasoning_content?: string | null
|
reasoning_content?: string | null
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const { t } = useI18n();
|
|||||||
const toast = useMessage();
|
const toast = useMessage();
|
||||||
|
|
||||||
const isSystem = computed(() => props.message.role === "system");
|
const isSystem = computed(() => props.message.role === "system");
|
||||||
|
const isAgentError = computed(() => props.message.role === "assistant" && props.message.systemType === "error");
|
||||||
|
|
||||||
const effectiveHeadingIdPrefix = computed(() => props.headingIdPrefix || `msg-${props.message.id}`);
|
const effectiveHeadingIdPrefix = computed(() => props.headingIdPrefix || `msg-${props.message.id}`);
|
||||||
const isCommandMessage = computed(() => props.message.role === "command" || props.message.systemType === "command");
|
const isCommandMessage = computed(() => props.message.role === "command" || props.message.systemType === "command");
|
||||||
@@ -790,6 +791,7 @@ onBeforeUnmount(() => {
|
|||||||
class="message-bubble"
|
class="message-bubble"
|
||||||
:class="{
|
:class="{
|
||||||
system: isSystem,
|
system: isSystem,
|
||||||
|
'agent-error': isAgentError,
|
||||||
command: isCommandMessage,
|
command: isCommandMessage,
|
||||||
'command-error': isCommandError,
|
'command-error': isCommandError,
|
||||||
'speech-playing': isPlayingThisMessage && !isPausedThisMessage,
|
'speech-playing': isPlayingThisMessage && !isPausedThisMessage,
|
||||||
@@ -1043,6 +1045,12 @@ onBeforeUnmount(() => {
|
|||||||
background-color: $msg-assistant-bg;
|
background-color: $msg-assistant-bg;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-bubble.agent-error {
|
||||||
|
color: $error;
|
||||||
|
background-color: rgba(var(--error-rgb), 0.06);
|
||||||
|
border: 1px solid rgba(var(--error-rgb), 0.2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tool {
|
&.tool {
|
||||||
@@ -1120,6 +1128,20 @@ onBeforeUnmount(() => {
|
|||||||
background-color: rgba(var(--warning-rgb), 0.06);
|
background-color: rgba(var(--warning-rgb), 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.agent-error {
|
||||||
|
color: $error;
|
||||||
|
background-color: rgba(var(--error-rgb), 0.06);
|
||||||
|
border: 1px solid rgba(var(--error-rgb), 0.2);
|
||||||
|
|
||||||
|
:deep(.markdown-body),
|
||||||
|
:deep(.markdown-body p),
|
||||||
|
:deep(.markdown-body li),
|
||||||
|
:deep(.markdown-body strong),
|
||||||
|
:deep(.markdown-body code) {
|
||||||
|
color: $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.speech-playing {
|
&.speech-playing {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 0 2px #ff6b6b,
|
0 0 0 2px #ff6b6b,
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ const isAgent = computed(() => {
|
|||||||
return props.agents.some(a => a.agentId === props.message.senderId || a.name === props.message.senderName)
|
return props.agents.some(a => a.agentId === props.message.senderId || a.name === props.message.senderName)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const isAgentError = computed(() => {
|
||||||
|
if (props.message.role !== 'assistant') return false
|
||||||
|
if (props.message.finish_reason === 'error') return true
|
||||||
|
return /^Error:\s*/i.test(props.message.content || '')
|
||||||
|
})
|
||||||
|
|
||||||
const isSelf = computed(() => {
|
const isSelf = computed(() => {
|
||||||
return !!props.currentUserId && props.message.senderId === props.currentUserId
|
return !!props.currentUserId && props.message.senderId === props.currentUserId
|
||||||
})
|
})
|
||||||
@@ -443,6 +449,7 @@ onBeforeUnmount(() => {
|
|||||||
class="msg-content"
|
class="msg-content"
|
||||||
:class="{
|
:class="{
|
||||||
'agent-content': isAgent,
|
'agent-content': isAgent,
|
||||||
|
'agent-error': isAgentError,
|
||||||
'speech-playing': isPlayingThisMessage && !isPausedThisMessage,
|
'speech-playing': isPlayingThisMessage && !isPausedThisMessage,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -548,6 +555,20 @@ onBeforeUnmount(() => {
|
|||||||
background-color: rgba(var(--accent-primary-rgb), 0.06);
|
background-color: rgba(var(--accent-primary-rgb), 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.agent .msg-content.agent-error {
|
||||||
|
color: $error;
|
||||||
|
background-color: rgba(var(--error-rgb), 0.06);
|
||||||
|
border: 1px solid rgba(var(--error-rgb), 0.2);
|
||||||
|
|
||||||
|
:deep(.markdown-body),
|
||||||
|
:deep(.markdown-body p),
|
||||||
|
:deep(.markdown-body li),
|
||||||
|
:deep(.markdown-body strong),
|
||||||
|
:deep(.markdown-body code) {
|
||||||
|
color: $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.self .msg-content {
|
&.self .msg-content {
|
||||||
background-color: rgba(var(--accent-primary-rgb), 0.1);
|
background-color: rgba(var(--accent-primary-rgb), 0.1);
|
||||||
}
|
}
|
||||||
@@ -834,6 +855,20 @@ onBeforeUnmount(() => {
|
|||||||
animation: rainbow-glow 4s linear infinite;
|
animation: rainbow-glow 4s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.agent-error {
|
||||||
|
color: $error;
|
||||||
|
background-color: rgba(var(--error-rgb), 0.06);
|
||||||
|
border: 1px solid rgba(var(--error-rgb), 0.2);
|
||||||
|
|
||||||
|
:deep(.markdown-body),
|
||||||
|
:deep(.markdown-body p),
|
||||||
|
:deep(.markdown-body li),
|
||||||
|
:deep(.markdown-body strong),
|
||||||
|
:deep(.markdown-body code) {
|
||||||
|
color: $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.mention-highlight) {
|
:deep(.mention-highlight) {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -567,6 +567,10 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
setPendingApproval({ ...e, session_id: sessionId } as RunEvent)
|
setPendingApproval({ ...e, session_id: sessionId } as RunEvent)
|
||||||
} else if (e.event === 'approval.resolved') {
|
} else if (e.event === 'approval.resolved') {
|
||||||
clearPendingApproval({ ...e, session_id: sessionId } as RunEvent)
|
clearPendingApproval({ ...e, session_id: sessionId } as RunEvent)
|
||||||
|
} else if (e.event === 'run.failed') {
|
||||||
|
addAgentErrorMessage(sessionId, e.error)
|
||||||
|
serverWorking.value.delete(sessionId)
|
||||||
|
queueLengths.value.delete(sessionId)
|
||||||
} else if (e.event === 'tool.started') {
|
} else if (e.event === 'tool.started') {
|
||||||
const msgs = getSessionMsgs(sessionId)
|
const msgs = getSessionMsgs(sessionId)
|
||||||
const toolCallId = e.tool_call_id as string | undefined
|
const toolCallId = e.tool_call_id as string | undefined
|
||||||
@@ -692,6 +696,29 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addAgentErrorMessage(sessionId: string, error?: string | null) {
|
||||||
|
const content = error ? `Error: ${error}` : 'Run failed'
|
||||||
|
const msgs = getSessionMsgs(sessionId)
|
||||||
|
const last = msgs[msgs.length - 1]
|
||||||
|
if (last?.isStreaming) {
|
||||||
|
updateMessage(sessionId, last.id, {
|
||||||
|
role: 'assistant',
|
||||||
|
content,
|
||||||
|
isStreaming: false,
|
||||||
|
systemType: 'error',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (last?.role === 'assistant' && last.systemType === 'error' && last.content === content) return
|
||||||
|
addMessage(sessionId, {
|
||||||
|
id: uid(),
|
||||||
|
role: 'assistant',
|
||||||
|
content,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
systemType: 'error',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function handleSessionCommandEvent(evt: RunEvent) {
|
function handleSessionCommandEvent(evt: RunEvent) {
|
||||||
const sid = evt.session_id
|
const sid = evt.session_id
|
||||||
if (!sid) return
|
if (!sid) return
|
||||||
@@ -1319,22 +1346,8 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'run.failed': {
|
case 'run.failed': {
|
||||||
|
addAgentErrorMessage(sid, evt.error)
|
||||||
const msgs = getSessionMsgs(sid)
|
const msgs = getSessionMsgs(sid)
|
||||||
const lastErr = msgs[msgs.length - 1]
|
|
||||||
if (lastErr?.isStreaming) {
|
|
||||||
updateMessage(sid, lastErr.id, {
|
|
||||||
isStreaming: false,
|
|
||||||
content: evt.error ? `Error: ${evt.error}` : 'Run failed',
|
|
||||||
role: 'system',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
addMessage(sid, {
|
|
||||||
id: uid(),
|
|
||||||
role: 'system',
|
|
||||||
content: evt.error ? `Error: ${evt.error}` : 'Run failed',
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
msgs.forEach((m, i) => {
|
msgs.forEach((m, i) => {
|
||||||
if (m.role === 'tool' && m.toolStatus === 'running') {
|
if (m.role === 'tool' && m.toolStatus === 'running') {
|
||||||
msgs[i] = { ...m, toolStatus: 'error' }
|
msgs[i] = { ...m, toolStatus: 'error' }
|
||||||
@@ -1371,20 +1384,14 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
// onError
|
// onError
|
||||||
(err) => {
|
(err) => {
|
||||||
console.warn('Socket.IO run stream error:', err.message)
|
console.warn('Socket.IO run stream error:', err.message)
|
||||||
|
addAgentErrorMessage(sid, err.message)
|
||||||
const msgs = getSessionMsgs(sid)
|
const msgs = getSessionMsgs(sid)
|
||||||
const last = msgs[msgs.length - 1]
|
|
||||||
if (last?.isStreaming) {
|
|
||||||
updateMessage(sid, last.id, { isStreaming: false })
|
|
||||||
}
|
|
||||||
msgs.forEach((m, i) => {
|
msgs.forEach((m, i) => {
|
||||||
if (m.role === 'tool' && m.toolStatus === 'running') {
|
if (m.role === 'tool' && m.toolStatus === 'running') {
|
||||||
msgs[i] = { ...m, toolStatus: 'done' }
|
msgs[i] = { ...m, toolStatus: 'error' }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
cleanup()
|
cleanup()
|
||||||
if (sid === activeSessionId.value) {
|
|
||||||
void refreshActiveSession()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
)
|
)
|
||||||
@@ -1756,22 +1763,8 @@ export const useChatStore = defineStore('chat', () => {
|
|||||||
} else {
|
} else {
|
||||||
queueLengths.value.delete(sid)
|
queueLengths.value.delete(sid)
|
||||||
}
|
}
|
||||||
|
addAgentErrorMessage(sid, evt.error)
|
||||||
const msgs = getSessionMsgs(sid)
|
const msgs = getSessionMsgs(sid)
|
||||||
const lastErr = msgs[msgs.length - 1]
|
|
||||||
if (lastErr?.isStreaming) {
|
|
||||||
updateMessage(sid, lastErr.id, {
|
|
||||||
isStreaming: false,
|
|
||||||
content: evt.error ? `Error: ${evt.error}` : 'Run failed',
|
|
||||||
role: 'system',
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
addMessage(sid, {
|
|
||||||
id: uid(),
|
|
||||||
role: 'system',
|
|
||||||
content: evt.error ? `Error: ${evt.error}` : 'Run failed',
|
|
||||||
timestamp: Date.now(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
msgs.forEach((m, i) => {
|
msgs.forEach((m, i) => {
|
||||||
if (m.role === 'tool' && m.toolStatus === 'running') {
|
if (m.role === 'tool' && m.toolStatus === 'running') {
|
||||||
msgs[i] = { ...m, toolStatus: 'error' }
|
msgs[i] = { ...m, toolStatus: 'error' }
|
||||||
|
|||||||
@@ -264,6 +264,13 @@ class AgentClient {
|
|||||||
onStatus?: (status: 'compressing' | 'replying' | 'ready') => void,
|
onStatus?: (status: 'compressing' | 'replying' | 'ready') => void,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logger.debug(`[AgentClients] ${this.name} mentioned by ${msg.senderName}: "${msg.content.slice(0, 50)}"`)
|
logger.debug(`[AgentClients] ${this.name} mentioned by ${msg.senderName}: "${msg.content.slice(0, 50)}"`)
|
||||||
|
const runMessageId = groupMessageId(roomId, this.profile, this.name)
|
||||||
|
let partIndex = 0
|
||||||
|
let streamMessageId = groupMessagePartId(runMessageId, partIndex)
|
||||||
|
let currentContent = ''
|
||||||
|
let totalContent = ''
|
||||||
|
let reasoningContent = ''
|
||||||
|
let streamStarted = false
|
||||||
try {
|
try {
|
||||||
// Notify room that agent is typing
|
// Notify room that agent is typing
|
||||||
this.startTyping(roomId)
|
this.startTyping(roomId)
|
||||||
@@ -335,12 +342,6 @@ class AgentClient {
|
|||||||
const bridge = new AgentBridgeClient()
|
const bridge = new AgentBridgeClient()
|
||||||
const sessionSeed = String(this.storage?.getRoom?.(roomId)?.sessionSeed || '0')
|
const sessionSeed = String(this.storage?.getRoom?.(roomId)?.sessionSeed || '0')
|
||||||
const sessionId = groupBridgeSessionId(roomId, this.profile, this.name, sessionSeed)
|
const sessionId = groupBridgeSessionId(roomId, this.profile, this.name, sessionSeed)
|
||||||
const runMessageId = groupMessageId(roomId, this.profile, this.name)
|
|
||||||
let partIndex = 0
|
|
||||||
let streamMessageId = groupMessagePartId(runMessageId, partIndex)
|
|
||||||
let currentContent = ''
|
|
||||||
let totalContent = ''
|
|
||||||
let reasoningContent = ''
|
|
||||||
const flushedAssistantParts = new Set<string>()
|
const flushedAssistantParts = new Set<string>()
|
||||||
let lastChunk: AgentBridgeOutput | null = null
|
let lastChunk: AgentBridgeOutput | null = null
|
||||||
const started = await bridge.chat(
|
const started = await bridge.chat(
|
||||||
@@ -355,6 +356,7 @@ class AgentClient {
|
|||||||
)
|
)
|
||||||
|
|
||||||
this.emitMessageStreamStart(roomId, streamMessageId)
|
this.emitMessageStreamStart(roomId, streamMessageId)
|
||||||
|
streamStarted = true
|
||||||
for await (const chunk of bridge.streamOutput(started.run_id, { timeoutMs: 120000 })) {
|
for await (const chunk of bridge.streamOutput(started.run_id, { timeoutMs: 120000 })) {
|
||||||
lastChunk = chunk
|
lastChunk = chunk
|
||||||
reasoningContent += await this.recordBridgeEvents(roomId, chunk, () => streamMessageId, async () => {
|
reasoningContent += await this.recordBridgeEvents(roomId, chunk, () => streamMessageId, async () => {
|
||||||
@@ -373,6 +375,7 @@ class AgentClient {
|
|||||||
partIndex += 1
|
partIndex += 1
|
||||||
streamMessageId = groupMessagePartId(runMessageId, partIndex)
|
streamMessageId = groupMessagePartId(runMessageId, partIndex)
|
||||||
this.emitMessageStreamStart(roomId, streamMessageId)
|
this.emitMessageStreamStart(roomId, streamMessageId)
|
||||||
|
streamStarted = true
|
||||||
return toolBaseId
|
return toolBaseId
|
||||||
})
|
})
|
||||||
if (chunk.delta) {
|
if (chunk.delta) {
|
||||||
@@ -384,6 +387,7 @@ class AgentClient {
|
|||||||
|
|
||||||
if (lastChunk?.status === 'error') {
|
if (lastChunk?.status === 'error') {
|
||||||
logger.error(`[AgentClients] ${this.name}: bridge response failed: ${lastChunk.error || 'unknown error'}`)
|
logger.error(`[AgentClients] ${this.name}: bridge response failed: ${lastChunk.error || 'unknown error'}`)
|
||||||
|
await this.sendAgentErrorMessage(roomId, streamMessageId, lastChunk.error || 'Run failed', msg, reasoningContent)
|
||||||
this.emitMessageStreamEnd(roomId, streamMessageId)
|
this.emitMessageStreamEnd(roomId, streamMessageId)
|
||||||
this.stopTyping(roomId)
|
this.stopTyping(roomId)
|
||||||
onStatus?.('ready')
|
onStatus?.('ready')
|
||||||
@@ -414,11 +418,35 @@ class AgentClient {
|
|||||||
onStatus?.('ready')
|
onStatus?.('ready')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
logger.error(`[AgentClients] ${this.name}: error handling message: ${err.message}`)
|
logger.error(`[AgentClients] ${this.name}: error handling message: ${err.message}`)
|
||||||
|
try {
|
||||||
|
await this.sendAgentErrorMessage(roomId, streamMessageId, err, msg, reasoningContent)
|
||||||
|
if (streamStarted) this.emitMessageStreamEnd(roomId, streamMessageId)
|
||||||
|
} catch (sendErr: any) {
|
||||||
|
logger.warn(`[AgentClients] ${this.name}: failed to send error message: ${sendErr.message}`)
|
||||||
|
}
|
||||||
this.stopTyping(roomId)
|
this.stopTyping(roomId)
|
||||||
onStatus?.('ready')
|
onStatus?.('ready')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async sendAgentErrorMessage(
|
||||||
|
roomId: string,
|
||||||
|
messageId: string,
|
||||||
|
error: unknown,
|
||||||
|
sourceMsg: MentionMessage,
|
||||||
|
reasoningContent = '',
|
||||||
|
): Promise<void> {
|
||||||
|
const detail = error instanceof Error ? error.message : String(error || 'Run failed')
|
||||||
|
const content = detail.startsWith('Error:') ? detail : `Error: ${detail}`
|
||||||
|
await this.sendMessage(roomId, content, messageId, {
|
||||||
|
role: 'assistant',
|
||||||
|
mentionDepth: nextMentionDepth(sourceMsg),
|
||||||
|
finish_reason: 'error',
|
||||||
|
reasoning: reasoningContent || null,
|
||||||
|
reasoning_content: reasoningContent || null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private async recordBridgeEvents(
|
private async recordBridgeEvents(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
chunk: AgentBridgeOutput,
|
chunk: AgentBridgeOutput,
|
||||||
|
|||||||
Reference in New Issue
Block a user