Commit d41f3405 authored by David Yang's avatar David Yang

fix(chat): keep audit results on abort and stop blank success messages

- emit STOPPED terminal chunk on abort so partial text and pending
  auditResult/auditExport stay attached to the message
- fill '已停止生成。' when nothing was streamed, and skip local
  fallback scoring once the user has stopped
- treat aborted signal (including string abort reason) as terminal in
  the request catch
- drop stale stream/error updates in useChat when the target message
  was removed, e.g. after switching conversations
parent 48cccb9b
......@@ -1023,6 +1023,24 @@ export default forwardRef((props, ref) => {
};
}
if (chunk.data === 'STOPPED') {
// 停止/中断收尾:保留已流式输出的内容,并补挂本次已生成的审计结果
return {
...originMessage,
role: 'assistant',
content: originContent || '已停止生成。',
auditResult:
originMessage?.auditResult ||
pendingAuditResultRef.current ||
undefined,
auditExport:
originMessage?.auditExport ||
pendingAuditExportRef.current ||
undefined,
tool_calls: undefined,
};
}
try {
let messageData;
try {
......@@ -1124,6 +1142,15 @@ export default forwardRef((props, ref) => {
}${agentPrompt}`;
};
// 用户已停止时抛出 AbortError,跳过后续兜底分析,由请求层统一走 STOPPED 收尾
const throwIfAborted = () => {
if (abortController.current?.signal?.aborted) {
const abortError = new Error('Aborted');
abortError.name = 'AbortError';
throw abortError;
}
};
const getMessagePrompt = async (message) => {
const topic = conversationTopic;
......@@ -1339,6 +1366,7 @@ export default forwardRef((props, ref) => {
}
}
if (!result) {
throwIfAborted();
let queryExpansion = null;
if (!isRefine) {
try {
......@@ -1411,6 +1439,7 @@ export default forwardRef((props, ref) => {
}
}
if (!result) {
throwIfAborted();
result = buildAuditResult(
message.content,
records,
......@@ -1443,9 +1472,9 @@ export default forwardRef((props, ref) => {
{ onUpdate, onSuccess, onError, onStream },
) => {
const chunks = [];
const controller = new AbortController();
onStream(controller);
try {
const controller = new AbortController();
onStream(controller);
pendingAuditResultRef.current = null;
pendingAuditExportRef.current = null;
......@@ -1679,7 +1708,12 @@ export default forwardRef((props, ref) => {
onSuccess(chunks);
} catch (error) {
if (error.name === 'AbortError') {
if (error.name === 'AbortError' || controller.signal.aborted) {
// 停止/中断收尾:发 STOPPED 保留已输出内容并补挂审计结果,
// 避免结构化清单丢失;完全没有输出时由 transformMessage 填充停止提示
onUpdate({
data: 'STOPPED',
});
onSuccess(chunks);
} else {
onError(error);
......
......@@ -109,6 +109,8 @@ export function useChat({
(item) => item.id === updatingMessageId,
);
if (!current) {
// 消息已创建过但被移除(如切换会话重置消息列表)时,丢弃过期更新
if (updatingMessageId) return;
const nextMessage = createMessage(
transformMessage({ chunk, chunks, status }),
status,
......@@ -144,6 +146,13 @@ export function useChat({
onUpdate: (chunk) => updateMessage('loading', chunk, []),
onSuccess: (chunks) => updateMessage('success', undefined, chunks),
onError: async (error) => {
if (
updatingMessageId &&
!messagesRef.current.some((item) => item.id === updatingMessageId)
) {
// 消息已被移除(如切换会话重置消息列表)时,丢弃过期错误回退
return;
}
const fallback = await requestFallback(message, {
error,
messages: getRequestMessages(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment