Show cancel button for backend active runs
This commit is contained in:
@@ -58,6 +58,7 @@ import {
|
|||||||
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
||||||
import { Reasoning } from "@/components/assistant-ui/reasoning";
|
import { Reasoning } from "@/components/assistant-ui/reasoning";
|
||||||
import {
|
import {
|
||||||
|
type ClawRunStatus,
|
||||||
fetchLatestRunStatus,
|
fetchLatestRunStatus,
|
||||||
toReplayRepository,
|
toReplayRepository,
|
||||||
} from "@/components/assistant-ui/thread-list";
|
} from "@/components/assistant-ui/thread-list";
|
||||||
@@ -925,6 +926,8 @@ const ComposerAction: FC = () => {
|
|||||||
});
|
});
|
||||||
const [runtimeCancelling, setRuntimeCancelling] = useState(false);
|
const [runtimeCancelling, setRuntimeCancelling] = useState(false);
|
||||||
const cancelSessionId = replayedRun.sessionId ?? currentSessionId;
|
const cancelSessionId = replayedRun.sessionId ?? currentSessionId;
|
||||||
|
const backendRunStatus = useBackendActiveRunStatus(cancelSessionId);
|
||||||
|
const cancelRunId = replayedRun.runId ?? backendRunStatus?.run_id ?? null;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!runtimeRunning) setRuntimeCancelling(false);
|
if (!runtimeRunning) setRuntimeCancelling(false);
|
||||||
}, [runtimeRunning]);
|
}, [runtimeRunning]);
|
||||||
@@ -935,7 +938,7 @@ const ComposerAction: FC = () => {
|
|||||||
<ComposerAssistButtons />
|
<ComposerAssistButtons />
|
||||||
</div>
|
</div>
|
||||||
<ComposerContextStatus />
|
<ComposerContextStatus />
|
||||||
{!runtimeRunning && !replayedRun.active ? (
|
{!runtimeRunning && !replayedRun.active && !backendRunStatus ? (
|
||||||
<ComposerPrimitive.Send asChild>
|
<ComposerPrimitive.Send asChild>
|
||||||
<TooltipIconButton
|
<TooltipIconButton
|
||||||
tooltip="Send message"
|
tooltip="Send message"
|
||||||
@@ -961,11 +964,12 @@ const ComposerAction: FC = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!cancelSessionId) return;
|
if (!cancelSessionId) return;
|
||||||
setRuntimeCancelling(true);
|
setRuntimeCancelling(true);
|
||||||
void cancelLatestRun(cancelSessionId, replayedRun.runId)
|
void cancelLatestRun(cancelSessionId, cancelRunId)
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
// 后端可能已经 idle;无论取消接口是否真的命中 run,都清理本地残留运行态。
|
// 后端可能已经 idle;无论取消接口是否真的命中 run,都清理本地残留运行态。
|
||||||
aui.thread().cancelRun();
|
aui.thread().cancelRun();
|
||||||
|
window.dispatchEvent(new Event("claw-sessions-changed"));
|
||||||
window.setTimeout(() => setRuntimeCancelling(false), 1200);
|
window.setTimeout(() => setRuntimeCancelling(false), 1200);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -973,16 +977,48 @@ const ComposerAction: FC = () => {
|
|||||||
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
|
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
|
||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
{!runtimeRunning && replayedRun.active ? (
|
{!runtimeRunning && (replayedRun.active || backendRunStatus) ? (
|
||||||
<ReplayedRunCancelButton
|
<ReplayedRunCancelButton
|
||||||
sessionId={replayedRun.sessionId}
|
sessionId={cancelSessionId}
|
||||||
runId={replayedRun.runId}
|
runId={cancelRunId}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function useBackendActiveRunStatus(sessionId: string | null) {
|
||||||
|
const [status, setStatus] = useState<ClawRunStatus | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!sessionId) {
|
||||||
|
setStatus(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const safeSessionId = sessionId;
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
async function refresh() {
|
||||||
|
const nextStatus = await fetchLatestRunStatus(safeSessionId);
|
||||||
|
if (cancelled) return;
|
||||||
|
setStatus(isActiveRunStatus(nextStatus) ? nextStatus : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh();
|
||||||
|
const interval = window.setInterval(refresh, 1000);
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
window.clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, [sessionId]);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isActiveRunStatus(status?: ClawRunStatus | null) {
|
||||||
|
return status?.status === "queued" || status?.status === "running";
|
||||||
|
}
|
||||||
|
|
||||||
const ReplayedRunCancelButton: FC<{
|
const ReplayedRunCancelButton: FC<{
|
||||||
sessionId: string | null;
|
sessionId: string | null;
|
||||||
runId: string | null;
|
runId: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user