mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-06 06:32:12 +00:00
feat: Wait on cancel, better UX when canceling (#578)
This commit is contained in:
parent
88d1b8e734
commit
d61965ce6b
4 changed files with 33 additions and 12 deletions
|
|
@ -49,7 +49,7 @@
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"langgraph-nextjs-api-passthrough": "^0.1.4",
|
"langgraph-nextjs-api-passthrough": "^0.1.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.476.0",
|
"lucide-react": "^0.532.0",
|
||||||
"next-themes": "^0.4.4",
|
"next-themes": "^0.4.4",
|
||||||
"nuqs": "^2.4.1",
|
"nuqs": "^2.4.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2, SquareStop } from "lucide-react";
|
||||||
import { UseStream } from "@langchain/langgraph-sdk/react";
|
import { UseStream } from "@langchain/langgraph-sdk/react";
|
||||||
import { useCancelStream } from "@/hooks/useCancelStream";
|
import { useCancelStream } from "@/hooks/useCancelStream";
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ export function CancelStreamButton({
|
||||||
runId,
|
runId,
|
||||||
streamName,
|
streamName,
|
||||||
}: CancelStreamButtonProps) {
|
}: CancelStreamButtonProps) {
|
||||||
const { cancelRun } = useCancelStream({
|
const { cancelRun, cancelLoading } = useCancelStream({
|
||||||
stream,
|
stream,
|
||||||
threadId,
|
threadId,
|
||||||
runId,
|
runId,
|
||||||
|
|
@ -35,9 +35,19 @@ export function CancelStreamButton({
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="h-8 px-3 text-xs"
|
className="h-8 px-3 text-xs"
|
||||||
|
disabled={cancelLoading}
|
||||||
>
|
>
|
||||||
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
|
{cancelLoading ? (
|
||||||
Stop {streamName}
|
<>
|
||||||
|
<SquareStop className="mr-1 h-3 w-3 animate-pulse" />
|
||||||
|
<p className="animate-pulse">Stopping {streamName}</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-1 h-3 w-3 animate-spin" />
|
||||||
|
Stop {streamName}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { UseStream } from "@langchain/langgraph-sdk/react";
|
import { UseStream } from "@langchain/langgraph-sdk/react";
|
||||||
import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types";
|
import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types";
|
||||||
import { GraphState } from "@open-swe/shared/open-swe/types";
|
import { GraphState } from "@open-swe/shared/open-swe/types";
|
||||||
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
interface UseCancelStreamProps<State extends PlannerGraphState | GraphState> {
|
interface UseCancelStreamProps<State extends PlannerGraphState | GraphState> {
|
||||||
|
|
@ -16,6 +17,7 @@ export function useCancelStream<State extends PlannerGraphState | GraphState>({
|
||||||
runId,
|
runId,
|
||||||
streamName,
|
streamName,
|
||||||
}: UseCancelStreamProps<State>) {
|
}: UseCancelStreamProps<State>) {
|
||||||
|
const [cancelLoading, setCancelLoading] = useState(false);
|
||||||
const cancelRun = async () => {
|
const cancelRun = async () => {
|
||||||
if (!threadId || !runId) {
|
if (!threadId || !runId) {
|
||||||
toast.error(`Cannot cancel ${streamName}: Missing thread or run ID`);
|
toast.error(`Cannot cancel ${streamName}: Missing thread or run ID`);
|
||||||
|
|
@ -23,9 +25,12 @@ export function useCancelStream<State extends PlannerGraphState | GraphState>({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await stream.client.runs.cancel(threadId, runId);
|
setCancelLoading(true);
|
||||||
|
await stream.client.runs.cancel(threadId, runId, true);
|
||||||
toast.success(`${streamName} cancelled successfully`, {
|
toast.success(`${streamName} cancelled successfully`, {
|
||||||
description: "The running operation has been stopped",
|
description: "The running operation has been stopped",
|
||||||
|
duration: 5000,
|
||||||
|
richColors: true,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorStr = String(error);
|
const errorStr = String(error);
|
||||||
|
|
@ -34,15 +39,21 @@ export function useCancelStream<State extends PlannerGraphState | GraphState>({
|
||||||
if (isAbortError) {
|
if (isAbortError) {
|
||||||
toast.info(`${streamName} operation cancelled`, {
|
toast.info(`${streamName} operation cancelled`, {
|
||||||
description: "The stream was successfully stopped",
|
description: "The stream was successfully stopped",
|
||||||
|
duration: 5000,
|
||||||
|
richColors: true,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error cancelling ${streamName} run:`, error);
|
console.error(`Error cancelling ${streamName} run:`, error);
|
||||||
toast.error(`Failed to cancel ${streamName}`, {
|
toast.error(`Failed to cancel ${streamName}`, {
|
||||||
description: errorStr || "Unknown error occurred",
|
description: errorStr || "Unknown error occurred",
|
||||||
|
duration: 5000,
|
||||||
|
richColors: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
setCancelLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { cancelRun };
|
return { cancelRun, cancelLoading };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
yarn.lock
10
yarn.lock
|
|
@ -4257,7 +4257,7 @@ __metadata:
|
||||||
katex: ^0.16.21
|
katex: ^0.16.21
|
||||||
langgraph-nextjs-api-passthrough: ^0.1.4
|
langgraph-nextjs-api-passthrough: ^0.1.4
|
||||||
lodash: ^4.17.21
|
lodash: ^4.17.21
|
||||||
lucide-react: ^0.476.0
|
lucide-react: ^0.532.0
|
||||||
next: ^15.2.3
|
next: ^15.2.3
|
||||||
next-themes: ^0.4.4
|
next-themes: ^0.4.4
|
||||||
nuqs: ^2.4.1
|
nuqs: ^2.4.1
|
||||||
|
|
@ -14594,12 +14594,12 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"lucide-react@npm:^0.476.0":
|
"lucide-react@npm:^0.532.0":
|
||||||
version: 0.476.0
|
version: 0.532.0
|
||||||
resolution: "lucide-react@npm:0.476.0"
|
resolution: "lucide-react@npm:0.532.0"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
checksum: fcfb5a4ce7be4d6184cca9cd1641bc9c967b70e8a29d2704d3a71a4c08d58b89aaabfbc436f323e7a93b2d2ef5f46782e99a88118267ed6f0670df70d35805d8
|
checksum: 5d2312b8836f8502f45bf6dbc355c7ab980b9f1f87ad8d12b81bc2f2de51d6176326aecf5bef09f56f65a50bef4d183b6df875a9d0b50777994f1effc27036e8
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue