fix(chat): 修复 abort 后 progress 仍显示工作中的问题
This commit is contained in:
@@ -353,6 +353,66 @@ describe("useAgentChatSession", () => {
|
|||||||
expect(abortAgentChat).toHaveBeenCalledWith("session-loaded");
|
expect(abortAgentChat).toHaveBeenCalledWith("session-loaded");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("finalizes running progress when aborting an active prompt", async () => {
|
||||||
|
listChatSessions.mockResolvedValue([]);
|
||||||
|
jest.mocked(streamAgentChat).mockImplementationOnce(
|
||||||
|
({ onEvent, signal }) =>
|
||||||
|
new Promise<void>((_, reject) => {
|
||||||
|
onEvent({
|
||||||
|
type: "progress",
|
||||||
|
sessionId: "session-1",
|
||||||
|
id: "request-received",
|
||||||
|
phase: "start",
|
||||||
|
status: "running",
|
||||||
|
title: "开始分析",
|
||||||
|
startedAt: 1000,
|
||||||
|
} satisfies StreamEvent);
|
||||||
|
|
||||||
|
signal.addEventListener("abort", () => {
|
||||||
|
reject(new Error("aborted"));
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useAgentChatSession({
|
||||||
|
projectId: "project-1",
|
||||||
|
onToolCall: jest.fn(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isHydrating).toBe(false));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
void result.current.sendPrompt("测试中断");
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isStreaming).toBe(true));
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.abort();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => expect(result.current.isStreaming).toBe(false));
|
||||||
|
|
||||||
|
expect(result.current.messages.at(-1)).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
role: "assistant",
|
||||||
|
content: "⚠️ **请求已中断**",
|
||||||
|
isError: true,
|
||||||
|
progress: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "request-received",
|
||||||
|
status: "completed",
|
||||||
|
durationMs: expect.any(Number),
|
||||||
|
endedAt: expect.any(Number),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(abortAgentChat).toHaveBeenCalledWith("session-1");
|
||||||
|
});
|
||||||
|
|
||||||
it("ignores generated session titles after the title was edited manually", async () => {
|
it("ignores generated session titles after the title was edited manually", async () => {
|
||||||
listChatSessions.mockResolvedValue([]);
|
listChatSessions.mockResolvedValue([]);
|
||||||
jest.mocked(streamAgentChat).mockImplementationOnce(async ({ onEvent }) => {
|
jest.mocked(streamAgentChat).mockImplementationOnce(async ({ onEvent }) => {
|
||||||
|
|||||||
@@ -130,6 +130,25 @@ const completeRunningProgress = (progress: ChatProgress[] | undefined) =>
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const finalizeAssistantMessageAfterAbort = (message: Message): Message => {
|
||||||
|
const completedProgress = completeRunningProgress(message.progress);
|
||||||
|
const hasVisibleOutput =
|
||||||
|
message.content.trim().length > 0 ||
|
||||||
|
Boolean(message.artifacts?.length) ||
|
||||||
|
Boolean(completedProgress?.length);
|
||||||
|
|
||||||
|
if (!hasVisibleOutput) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...message,
|
||||||
|
content: message.content || "⚠️ **请求已中断**",
|
||||||
|
isError: true,
|
||||||
|
progress: completedProgress,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const createUserMessage = (content: string, branchRootId?: string): Message => {
|
const createUserMessage = (content: string, branchRootId?: string): Message => {
|
||||||
const id = createId();
|
const id = createId();
|
||||||
return {
|
return {
|
||||||
@@ -605,6 +624,17 @@ export const useAgentChatSession = ({
|
|||||||
const controller = abortRef.current;
|
const controller = abortRef.current;
|
||||||
controller?.abort();
|
controller?.abort();
|
||||||
setIsStreaming(false);
|
setIsStreaming(false);
|
||||||
|
const assistantMessageId = getLastAssistantMessageId();
|
||||||
|
|
||||||
|
if (assistantMessageId) {
|
||||||
|
setMessages((prev) =>
|
||||||
|
prev.map((message) =>
|
||||||
|
message.id === assistantMessageId
|
||||||
|
? finalizeAssistantMessageAfterAbort(message)
|
||||||
|
: message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const cancelPromise = abortAgentChat(sessionIdRef.current).catch((error) => {
|
const cancelPromise = abortAgentChat(sessionIdRef.current).catch((error) => {
|
||||||
console.error("[GlobalChatbox] Failed to abort agent session:", error);
|
console.error("[GlobalChatbox] Failed to abort agent session:", error);
|
||||||
@@ -615,7 +645,7 @@ export const useAgentChatSession = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
cancelPromiseRef.current = trackedCancelPromise;
|
cancelPromiseRef.current = trackedCancelPromise;
|
||||||
}, []);
|
}, [getLastAssistantMessageId]);
|
||||||
|
|
||||||
const createSession = useCallback(() => {
|
const createSession = useCallback(() => {
|
||||||
if (isHydrating || isStreaming) return;
|
if (isHydrating || isStreaming) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user