fix(workspace): 恢复先删后建并修复新会话初始化时序

- 新会话初始化改为 delete -> create

- 通过初始化就绪门控,确保 history 在创建完成后再加载

- 发送消息前等待初始化完成,避免与初始化并发
This commit is contained in:
肖应宇 2026-04-28 18:34:18 +08:00
parent d1cdb7eef7
commit 08e8de5e3e
1 changed files with 42 additions and 18 deletions

View File

@ -81,16 +81,23 @@ export default function ChatPage() {
() => isNewThread && !safeThreadId,
[isNewThread, safeThreadId],
);
const [isThreadInitReady, setIsThreadInitReady] = useState(false);
const streamThreadId = useMemo(() => {
if (isNewThread && createNewSession) {
if (!safeThreadId) {
return undefined;
}
// In /new flow, defer history loading until thread init is finished:
// delete -> create -> history.
if (isNewThread && !isThreadInitReady) {
return undefined;
}
return safeThreadId;
}, [createNewSession, isNewThread, safeThreadId]);
}, [isNewThread, isThreadInitReady, safeThreadId]);
const apiClient = useMemo(() => getAPIClient(isMock), [isMock]);
const warnedMissingThreadIdRef = useRef(false);
const initializedThreadRef = useRef<string | null>(null);
const threadInitPromiseRef = useRef<Promise<void> | null>(null);
const { showNotification } = useNotification();
const currentSlogan = motivationSlogans[
@ -130,6 +137,7 @@ export default function ChatPage() {
useEffect(() => {
if (!isNewThread) {
warnedMissingThreadIdRef.current = false;
setIsThreadInitReady(true);
return;
}
if (!safeThreadId) {
@ -137,29 +145,38 @@ export default function ChatPage() {
warnedMissingThreadIdRef.current = true;
toast.error(t.chatPage.missingThreadIdForCreate);
}
setIsThreadInitReady(false);
return;
}
warnedMissingThreadIdRef.current = false;
if (initializedThreadRef.current === safeThreadId) return;
initializedThreadRef.current = safeThreadId;
void apiClient.threads
// TODO: 先注释先删除再创建的逻辑
// .delete(safeThreadId)
// .catch(() => undefined)
// .then(() =>
// apiClient.threads.create({
// threadId: safeThreadId,
// ifExists: "raise",
// }),
// )
.create({
setIsThreadInitReady(false);
const initPromise = apiClient.threads
.delete(safeThreadId)
.catch(() => undefined)
.then(() =>
apiClient.threads.create({
threadId: safeThreadId,
ifExists: "do_nothing",
}),
)
.then(() => {
setIsThreadInitReady(true);
})
.catch(() => {
initializedThreadRef.current = null;
setIsThreadInitReady(false);
toast.error(t.chatPage.createSessionFailed);
});
threadInitPromiseRef.current = initPromise;
void initPromise.finally(() => {
if (threadInitPromiseRef.current === initPromise) {
threadInitPromiseRef.current = null;
}
});
}, [
apiClient,
isNewThread,
@ -291,7 +308,7 @@ export default function ChatPage() {
const [showExitDialog, setShowExitDialog] = useState(false);
const isStreaming = isUploading || thread.isLoading;
const handleSubmit = useCallback(
(message: Parameters<typeof sendMessage>[1]) => {
async (message: Parameters<typeof sendMessage>[1]) => {
if (isSelectedSkillBootstrapping) {
return;
}
@ -299,6 +316,12 @@ export default function ChatPage() {
toast.error(t.chatPage.missingThreadIdForSend);
return;
}
if (isNewThread && safeThreadId) {
await threadInitPromiseRef.current;
}
if (isNewThread && safeThreadId && !isThreadInitReady) {
return;
}
setHasSubmitted(true);
if (safeThreadId && (isNewThread || showWelcomeStyle)) {
router.replace(`/workspace/chats/${safeThreadId}?is_chatting=true`);
@ -307,6 +330,7 @@ export default function ChatPage() {
},
[
isNewThread,
isThreadInitReady,
isSelectedSkillBootstrapping,
router,
safeThreadId,
@ -633,14 +657,14 @@ export default function ChatPage() {
type: POST_MESSAGE_TYPES.IS_CHATTING,
isChatting: false,
});
resetNewSessionState();
// 始终复用 query 中的 thread_id。
const nextQuery = new URLSearchParams();
if (threadId && threadId !== "new") {
nextQuery.set("thread_id", threadId);
}
// /workspace/chats/${threadId}?is_chatting=false
router.replace(
`/workspace/chats/${threadId}?is_chatting=false`,
`/workspace/chats/new?thread_id=${threadId}`,
);
}}
>