mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-07 16:19:09 +00:00
fix: revert dashboard stop run controls (#1461)
Revert the dashboard stop-button behavior from #1433 because it allows duplicate submissions while optimistic prompts are still pending.
This commit is contained in:
parent
11042175c6
commit
5430672edb
4 changed files with 19 additions and 67 deletions
|
|
@ -223,9 +223,7 @@ def _metadata_repo(metadata: dict[str, Any]) -> tuple[str, str, str]:
|
||||||
def _run_status_to_agent_status(thread_status: str | None, run_status: str | None) -> str:
|
def _run_status_to_agent_status(thread_status: str | None, run_status: str | None) -> str:
|
||||||
if thread_status == "busy" or run_status in {"pending", "running"}:
|
if thread_status == "busy" or run_status in {"pending", "running"}:
|
||||||
return "running"
|
return "running"
|
||||||
if run_status in {"interrupted", "cancelled"}:
|
if run_status in {"error", "failed", "timeout", "interrupted"}:
|
||||||
return "interrupted"
|
|
||||||
if run_status in {"error", "failed", "timeout"}:
|
|
||||||
return "error"
|
return "error"
|
||||||
if run_status == "success":
|
if run_status == "success":
|
||||||
return "finished"
|
return "finished"
|
||||||
|
|
@ -672,7 +670,7 @@ async def cancel_dashboard_thread(
|
||||||
run_id = metadata.get("latest_run_id")
|
run_id = metadata.get("latest_run_id")
|
||||||
if isinstance(run_id, str) and run_id:
|
if isinstance(run_id, str) and run_id:
|
||||||
try:
|
try:
|
||||||
await client.runs.cancel(thread_id, run_id, wait=False, action="interrupt")
|
await client.runs.cancel(thread_id, run_id, wait=False)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Could not cancel run %s for thread %s", run_id, thread_id, exc_info=True)
|
logger.debug("Could not cancel run %s for thread %s", run_id, thread_id, exc_info=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,6 @@ def test_thread_summary_keeps_repo_when_present() -> None:
|
||||||
assert summary["repoFullName"] == "octo/repo"
|
assert summary["repoFullName"] == "octo/repo"
|
||||||
|
|
||||||
|
|
||||||
def test_thread_summary_reports_interrupted_run_status() -> None:
|
|
||||||
summary = thread_api._thread_summary(
|
|
||||||
{"thread_id": "t3", "metadata": {"latest_run_status": "interrupted"}}
|
|
||||||
)
|
|
||||||
assert summary["status"] == "interrupted"
|
|
||||||
|
|
||||||
|
|
||||||
class _FakeThreadsClient:
|
class _FakeThreadsClient:
|
||||||
async def create(
|
async def create(
|
||||||
self, *, thread_id: str, metadata: dict[str, Any], if_exists: str
|
self, *, thread_id: str, metadata: dict[str, Any], if_exists: str
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,7 @@ import type { AgentThread, Message } from "@/lib/agents/types"
|
||||||
import type { ModelSelection } from "@/lib/agents/useModelOptions"
|
import type { ModelSelection } from "@/lib/agents/useModelOptions"
|
||||||
import { AgentPromptBar } from "@/components/agents/AgentPromptBar"
|
import { AgentPromptBar } from "@/components/agents/AgentPromptBar"
|
||||||
import { MessageView } from "@/components/agents/ported"
|
import { MessageView } from "@/components/agents/ported"
|
||||||
import {
|
import { agentThreadKeys, useSendAgentMessage } from "@/lib/agents/queries"
|
||||||
agentThreadKeys,
|
|
||||||
useCancelAgentThread,
|
|
||||||
useSendAgentMessage,
|
|
||||||
} from "@/lib/agents/queries"
|
|
||||||
import {
|
import {
|
||||||
dropPendingPrompts,
|
dropPendingPrompts,
|
||||||
getPendingPrompts,
|
getPendingPrompts,
|
||||||
|
|
@ -58,7 +54,6 @@ function isPendingPromptConfirmed(
|
||||||
export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const sendMessage = useSendAgentMessage(thread.id)
|
const sendMessage = useSendAgentMessage(thread.id)
|
||||||
const cancelThread = useCancelAgentThread(thread.id)
|
|
||||||
useAgentThreadStream(thread.id, thread.status === "running")
|
useAgentThreadStream(thread.id, thread.status === "running")
|
||||||
const [pendingPrompts, setPendingPrompts] = useState<Array<PendingPrompt>>(
|
const [pendingPrompts, setPendingPrompts] = useState<Array<PendingPrompt>>(
|
||||||
() => getPendingPrompts(thread.id)
|
() => getPendingPrompts(thread.id)
|
||||||
|
|
@ -139,7 +134,7 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
||||||
<AgentPromptBar
|
<AgentPromptBar
|
||||||
placeholder="Add a follow up"
|
placeholder="Add a follow up"
|
||||||
compact
|
compact
|
||||||
busy={hasActiveRun}
|
busy={isStreaming}
|
||||||
disabled={sendMessage.isPending}
|
disabled={sendMessage.isPending}
|
||||||
onSubmit={(content, images) =>
|
onSubmit={(content, images) =>
|
||||||
sendMessage.mutate({
|
sendMessage.mutate({
|
||||||
|
|
@ -149,8 +144,6 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
||||||
effort: activeSelection?.effort ?? null,
|
effort: activeSelection?.effort ?? null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onStop={() => cancelThread.mutate()}
|
|
||||||
stopping={cancelThread.isPending}
|
|
||||||
models={models}
|
models={models}
|
||||||
selection={activeSelection}
|
selection={activeSelection}
|
||||||
onSelectionChange={setSelection}
|
onSelectionChange={setSelection}
|
||||||
|
|
@ -167,7 +160,7 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
||||||
<AgentPromptBar
|
<AgentPromptBar
|
||||||
placeholder="Send the first message"
|
placeholder="Send the first message"
|
||||||
compact
|
compact
|
||||||
busy={hasActiveRun}
|
busy={isStreaming}
|
||||||
disabled={sendMessage.isPending}
|
disabled={sendMessage.isPending}
|
||||||
onSubmit={(content, images) =>
|
onSubmit={(content, images) =>
|
||||||
sendMessage.mutate({
|
sendMessage.mutate({
|
||||||
|
|
@ -177,8 +170,6 @@ export function AgentThreadView({ thread }: AgentThreadViewProps) {
|
||||||
effort: activeSelection?.effort ?? null,
|
effort: activeSelection?.effort ?? null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onStop={() => cancelThread.mutate()}
|
|
||||||
stopping={cancelThread.isPending}
|
|
||||||
models={models}
|
models={models}
|
||||||
selection={activeSelection}
|
selection={activeSelection}
|
||||||
onSelectionChange={setSelection}
|
onSelectionChange={setSelection}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
import {
|
import { ArrowUp, ChevronDown, ImagePlus, LoaderCircle, X } from "lucide-react"
|
||||||
ArrowUp,
|
|
||||||
ChevronDown,
|
|
||||||
ImagePlus,
|
|
||||||
LoaderCircle,
|
|
||||||
Square,
|
|
||||||
X,
|
|
||||||
} from "lucide-react"
|
|
||||||
import {
|
import {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
|
@ -39,9 +32,6 @@ export interface CloudPromptBarProps {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
busy?: boolean
|
busy?: boolean
|
||||||
onSubmit?: (value: string, images: Array<ImageChunk>) => void
|
onSubmit?: (value: string, images: Array<ImageChunk>) => void
|
||||||
/** Called to stop the running agent. When set, the send button becomes a stop button while busy and the input is empty. */
|
|
||||||
onStop?: () => void
|
|
||||||
stopping?: boolean
|
|
||||||
models?: Array<ModelOption>
|
models?: Array<ModelOption>
|
||||||
selection?: ModelSelection | null
|
selection?: ModelSelection | null
|
||||||
onSelectionChange?: (next: ModelSelection) => void
|
onSelectionChange?: (next: ModelSelection) => void
|
||||||
|
|
@ -84,8 +74,6 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
|
||||||
disabled = false,
|
disabled = false,
|
||||||
busy = false,
|
busy = false,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onStop,
|
|
||||||
stopping = false,
|
|
||||||
models = [],
|
models = [],
|
||||||
selection = null,
|
selection = null,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
|
|
@ -211,8 +199,6 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const pickerDisabled = combos.length === 0 || !onSelectionChange
|
const pickerDisabled = combos.length === 0 || !onSelectionChange
|
||||||
const showStop =
|
|
||||||
busy && !disabled && !value.trim() && pendingImages.length === 0 && !!onStop
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -360,35 +346,19 @@ export const CloudPromptBar = memo(function CloudPromptBarComponent({
|
||||||
<ImagePlus className="size-4" />
|
<ImagePlus className="size-4" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showStop ? (
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
onClick={handleSubmit}
|
||||||
onClick={onStop}
|
disabled={!canSubmit}
|
||||||
disabled={stopping}
|
aria-label="Send message"
|
||||||
aria-label="Stop run"
|
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
|
||||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
|
>
|
||||||
>
|
{disabled ? (
|
||||||
{stopping ? (
|
<LoaderCircle className="size-3.5 animate-spin" />
|
||||||
<LoaderCircle className="size-3.5 animate-spin" />
|
) : (
|
||||||
) : (
|
<ArrowUp className="size-3.5" strokeWidth={2.5} />
|
||||||
<Square className="size-3 fill-current" strokeWidth={0} />
|
)}
|
||||||
)}
|
</button>
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleSubmit}
|
|
||||||
disabled={!canSubmit}
|
|
||||||
aria-label="Send message"
|
|
||||||
className="flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--ui-accent)] text-white transition-opacity hover:opacity-90 disabled:cursor-default disabled:opacity-40"
|
|
||||||
>
|
|
||||||
{disabled ? (
|
|
||||||
<LoaderCircle className="size-3.5 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<ArrowUp className="size-3.5" strokeWidth={2.5} />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue