fix: harden generation task recovery
This commit is contained in:
@@ -37,7 +37,7 @@ import {
|
||||
import "../../styles/pages/workbench.css";
|
||||
import type { WebGenerationPreviewTask, WebUserSession } from "../../types";
|
||||
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
||||
import { claimGenerationSlot, getActiveGenerationTaskCount, getGenerationUserKey, releaseGenerationSlot } from "../../api/generationConcurrency";
|
||||
import { claimGenerationSlot, getActiveGenerationTaskCount, getEffectiveGenerationLimit, getGenerationUserKey, releaseGenerationSlot } from "../../api/generationConcurrency";
|
||||
import { preUploadReference, resolvePreUploadedUrl } from "../../api/referenceUploadService";
|
||||
import { assetClient } from "../../api/assetClient";
|
||||
import { communityClient } from "../../api/communityClient";
|
||||
@@ -1056,6 +1056,54 @@ function WorkbenchPage({
|
||||
persistKeepaliveTasks(rest);
|
||||
};
|
||||
|
||||
const releaseKeepaliveTaskLocally = useCallback((taskId: string, options?: { cancelServer?: boolean }) => {
|
||||
const task = keepaliveTasksRef.current[taskId];
|
||||
taskAbortControllersRef.current.get(taskId)?.abort();
|
||||
taskAbortControllersRef.current.delete(taskId);
|
||||
removeKeepaliveTask(taskId);
|
||||
if (task && options?.cancelServer) {
|
||||
aiGenerationClient.cancelTask(task.taskId).catch(() => {});
|
||||
}
|
||||
syncActiveGenerationUi();
|
||||
}, [syncActiveGenerationUi]);
|
||||
|
||||
const releaseKeepaliveTaskAfterNetworkLoss = useCallback((task: WorkbenchKeepaliveTask, progress: number) => {
|
||||
const latestTask = {
|
||||
...task,
|
||||
progress,
|
||||
statusLabel: "网络中断,已释放提交按钮",
|
||||
};
|
||||
void patchConversationMessage(task.conversationId, task.assistantMessageId, {
|
||||
status: "failed",
|
||||
taskProgress: Math.max(progress, 100),
|
||||
taskStatusLabel: "网络中断",
|
||||
body: "网络中断,当前任务已停止等待并释放提交按钮。请确认网络恢复后重新提交任务。",
|
||||
});
|
||||
upsertKeepaliveTask(latestTask);
|
||||
releaseKeepaliveTaskLocally(task.taskId, { cancelServer: true });
|
||||
if (activeConversationIdRef.current === task.conversationId) {
|
||||
setIsGenerating(false);
|
||||
setGenerationStatus("网络中断,已释放提交按钮");
|
||||
setGenerationProgress(0);
|
||||
}
|
||||
}, [patchConversationMessage, releaseKeepaliveTaskLocally]);
|
||||
|
||||
const cancelActiveKeepaliveTasksOnPageExit = useCallback(() => {
|
||||
const tasks = Object.values(keepaliveTasksRef.current);
|
||||
if (!tasks.length) return;
|
||||
tasks.forEach((task) => {
|
||||
taskAbortControllersRef.current.get(task.taskId)?.abort();
|
||||
taskAbortControllersRef.current.delete(task.taskId);
|
||||
releaseGenerationSlot(task.concurrencySlotId);
|
||||
aiGenerationClient.cancelTaskOnUnload(task.taskId);
|
||||
});
|
||||
keepaliveTasksRef.current = {};
|
||||
persistKeepaliveTasks({});
|
||||
setIsGenerating(false);
|
||||
setGenerationStatus("已释放未完成任务");
|
||||
setGenerationProgress(0);
|
||||
}, []);
|
||||
|
||||
const runKeepalivePoll = useCallback(
|
||||
(task: WorkbenchKeepaliveTask) => {
|
||||
if (taskAbortControllersRef.current.has(task.taskId)) return;
|
||||
@@ -1082,6 +1130,10 @@ function WorkbenchPage({
|
||||
if (abortController.signal.aborted) return;
|
||||
if (attempt > 0) await sleep(3000);
|
||||
if (abortController.signal.aborted) return;
|
||||
if (typeof navigator !== "undefined" && navigator.onLine === false) {
|
||||
releaseKeepaliveTaskAfterNetworkLoss(task, lastKnownProgress);
|
||||
return;
|
||||
}
|
||||
|
||||
let status;
|
||||
try {
|
||||
@@ -1095,7 +1147,8 @@ function WorkbenchPage({
|
||||
taskProgress: 100,
|
||||
taskStatusLabel: "任务异常",
|
||||
});
|
||||
removeKeepaliveTask(task.taskId);
|
||||
releaseKeepaliveTaskLocally(task.taskId, { cancelServer: true });
|
||||
onRefreshUsage?.();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
@@ -1323,6 +1376,24 @@ function WorkbenchPage({
|
||||
};
|
||||
}, [runKeepalivePoll]);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePageHide = () => {
|
||||
cancelActiveKeepaliveTasksOnPageExit();
|
||||
};
|
||||
const handleOnline = () => {
|
||||
Object.values(keepaliveTasksRef.current).forEach((task) => runKeepalivePoll(task));
|
||||
syncActiveGenerationUi();
|
||||
};
|
||||
|
||||
window.addEventListener("pagehide", handlePageHide);
|
||||
window.addEventListener("online", handleOnline);
|
||||
aiGenerationClient.flushPendingTaskCancellations();
|
||||
return () => {
|
||||
window.removeEventListener("pagehide", handlePageHide);
|
||||
window.removeEventListener("online", handleOnline);
|
||||
};
|
||||
}, [cancelActiveKeepaliveTasksOnPageExit, runKeepalivePoll, syncActiveGenerationUi]);
|
||||
|
||||
useEffect(() => {
|
||||
persistPromptHistory(promptHistory);
|
||||
}, [promptHistory]);
|
||||
@@ -1987,7 +2058,7 @@ function WorkbenchPage({
|
||||
const trimmedPrompt = (promptOverride ?? inputValue).trim();
|
||||
if (!trimmedPrompt) return;
|
||||
const userKey = getGenerationUserKey(session?.user.id);
|
||||
if (activeMode !== "chat" && getActiveGenerationTaskCount(userKey) >= 3) return;
|
||||
if (activeMode !== "chat" && getActiveGenerationTaskCount(userKey) >= getEffectiveGenerationLimit()) return;
|
||||
setReferencePreviewOpen(false);
|
||||
|
||||
let conversationId = activeConversationIdRef.current ?? activeConversationId;
|
||||
@@ -2466,8 +2537,11 @@ function WorkbenchPage({
|
||||
setProjectError("仅支持对视频结果进行超分");
|
||||
return;
|
||||
}
|
||||
if (getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id)) >= 3) {
|
||||
setProjectError(`当前已有 ${getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id))} 个任务进行中(上限3个),请等待任一任务完成后再提交新任务`);
|
||||
const userKey = getGenerationUserKey(session?.user.id);
|
||||
const activeCount = getActiveGenerationTaskCount(userKey);
|
||||
const limit = getEffectiveGenerationLimit();
|
||||
if (activeCount >= limit) {
|
||||
setProjectError(`当前已有 ${activeCount} 个任务进行中(上限${limit}个),请等待任一任务完成后再提交新任务`);
|
||||
return;
|
||||
}
|
||||
if (!isAuthenticated) {
|
||||
@@ -2588,8 +2662,11 @@ function WorkbenchPage({
|
||||
setProjectError("仅支持对图片结果进行超分");
|
||||
return;
|
||||
}
|
||||
if (getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id)) >= 3) {
|
||||
setProjectError(`当前已有 ${getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id))} 个任务进行中(上限3个),请等待任一任务完成后再提交新任务`);
|
||||
const userKey = getGenerationUserKey(session?.user.id);
|
||||
const activeCount = getActiveGenerationTaskCount(userKey);
|
||||
const limit = getEffectiveGenerationLimit();
|
||||
if (activeCount >= limit) {
|
||||
setProjectError(`当前已有 ${activeCount} 个任务进行中(上限${limit}个),请等待任一任务完成后再提交新任务`);
|
||||
return;
|
||||
}
|
||||
if (!isAuthenticated) {
|
||||
@@ -2762,13 +2839,14 @@ function WorkbenchPage({
|
||||
};
|
||||
|
||||
const activeGenerationCount = getActiveGenerationTaskCount(getGenerationUserKey(session?.user.id));
|
||||
const generationLimitReached = activeMode !== "chat" && activeGenerationCount >= 3;
|
||||
const activeGenerationLimit = getEffectiveGenerationLimit();
|
||||
const generationLimitReached = activeMode !== "chat" && activeGenerationCount >= activeGenerationLimit;
|
||||
const promptIsEmpty = !inputValue.trim();
|
||||
const sendDisabled = promptIsEmpty || generationLimitReached;
|
||||
const sendButtonTitle = promptIsEmpty
|
||||
? "输入内容后可发送"
|
||||
: generationLimitReached
|
||||
? `当前已有 ${activeGenerationCount} 个任务进行中,请等待任一任务完成`
|
||||
? `当前已有 ${activeGenerationCount} 个任务进行中(上限 ${activeGenerationLimit} 个),请等待任一任务完成`
|
||||
: billingEstimate.title;
|
||||
|
||||
const suggestedPrompts = [
|
||||
|
||||
Reference in New Issue
Block a user