mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-07 22:14:03 -05:00
fix(frontend): Consider start task job error status for loading indicators (#11670)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { getStatusCode, getIndicatorColor, IndicatorColor } from "../status";
|
||||
import { getStatusCode, getIndicatorColor, IndicatorColor } from "#/utils/status";
|
||||
import { AgentState } from "#/types/agent-state";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
|
||||
@@ -87,6 +87,36 @@ describe("getStatusCode", () => {
|
||||
// Should return runtime status since no agent state
|
||||
expect(result).toBe("STATUS$STARTING_RUNTIME");
|
||||
});
|
||||
|
||||
it("should prioritize task ERROR status over websocket CONNECTING state", () => {
|
||||
// Test case: Task has errored but websocket is still trying to connect
|
||||
const result = getStatusCode(
|
||||
{ id: "", message: "", type: "info", status_update: true }, // statusMessage
|
||||
"CONNECTING", // webSocketStatus (stuck connecting)
|
||||
null, // conversationStatus
|
||||
null, // runtimeStatus
|
||||
AgentState.LOADING, // agentState
|
||||
"ERROR", // taskStatus (ERROR)
|
||||
);
|
||||
|
||||
// Should return error message, not "Connecting..."
|
||||
expect(result).toBe(I18nKey.AGENT_STATUS$ERROR_OCCURRED);
|
||||
});
|
||||
|
||||
it("should show Connecting when task is working and websocket is connecting", () => {
|
||||
// Test case: Task is in progress and websocket is connecting normally
|
||||
const result = getStatusCode(
|
||||
{ id: "", message: "", type: "info", status_update: true }, // statusMessage
|
||||
"CONNECTING", // webSocketStatus
|
||||
null, // conversationStatus
|
||||
null, // runtimeStatus
|
||||
AgentState.LOADING, // agentState
|
||||
"WORKING", // taskStatus (in progress)
|
||||
);
|
||||
|
||||
// Should show connecting message since task hasn't errored
|
||||
expect(result).toBe(I18nKey.CHAT_INTERFACE$CONNECTING);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getIndicatorColor", () => {
|
||||
@@ -13,6 +13,7 @@ import { useConversationStore } from "#/state/conversation-store";
|
||||
import CircleErrorIcon from "#/icons/circle-error.svg?react";
|
||||
import { useAgentState } from "#/hooks/use-agent-state";
|
||||
import { useUnifiedWebSocketStatus } from "#/hooks/use-unified-websocket-status";
|
||||
import { useTaskPolling } from "#/hooks/query/use-task-polling";
|
||||
|
||||
export interface AgentStatusProps {
|
||||
className?: string;
|
||||
@@ -35,6 +36,7 @@ export function AgentStatus({
|
||||
const { curStatusMessage } = useStatusStore();
|
||||
const webSocketStatus = useUnifiedWebSocketStatus();
|
||||
const { data: conversation } = useActiveConversation();
|
||||
const { taskStatus } = useTaskPolling();
|
||||
|
||||
const statusCode = getStatusCode(
|
||||
curStatusMessage,
|
||||
@@ -42,17 +44,24 @@ export function AgentStatus({
|
||||
conversation?.status || null,
|
||||
conversation?.runtime_status || null,
|
||||
curAgentState,
|
||||
taskStatus,
|
||||
);
|
||||
|
||||
const isTaskLoading =
|
||||
taskStatus && taskStatus !== "ERROR" && taskStatus !== "READY";
|
||||
|
||||
const shouldShownAgentLoading =
|
||||
isPausing ||
|
||||
curAgentState === AgentState.INIT ||
|
||||
curAgentState === AgentState.LOADING ||
|
||||
webSocketStatus === "CONNECTING";
|
||||
(webSocketStatus === "CONNECTING" && taskStatus !== "ERROR") ||
|
||||
isTaskLoading;
|
||||
|
||||
const shouldShownAgentError =
|
||||
curAgentState === AgentState.ERROR ||
|
||||
curAgentState === AgentState.RATE_LIMITED;
|
||||
curAgentState === AgentState.RATE_LIMITED ||
|
||||
webSocketStatus === "DISCONNECTED" ||
|
||||
taskStatus === "ERROR";
|
||||
|
||||
const shouldShownAgentStop = curAgentState === AgentState.RUNNING;
|
||||
|
||||
@@ -61,7 +70,8 @@ export function AgentStatus({
|
||||
|
||||
// Update global state when agent loading condition changes
|
||||
useEffect(() => {
|
||||
setShouldShownAgentLoading(shouldShownAgentLoading);
|
||||
if (shouldShownAgentLoading)
|
||||
setShouldShownAgentLoading(shouldShownAgentLoading);
|
||||
}, [shouldShownAgentLoading, setShouldShownAgentLoading]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AgentState } from "#/types/agent-state";
|
||||
import { ConversationStatus } from "#/types/conversation-status";
|
||||
import { StatusMessage } from "#/types/message";
|
||||
import { RuntimeStatus } from "#/types/runtime-status";
|
||||
import { V1AppConversationStartTaskStatus } from "#/api/conversation-service/v1-conversation-service.types";
|
||||
|
||||
export enum IndicatorColor {
|
||||
BLUE = "bg-blue-500",
|
||||
@@ -103,8 +104,15 @@ export function getStatusCode(
|
||||
conversationStatus: ConversationStatus | null,
|
||||
runtimeStatus: RuntimeStatus | null,
|
||||
agentState: AgentState | null,
|
||||
taskStatus?: V1AppConversationStartTaskStatus | null,
|
||||
) {
|
||||
// Handle conversation and runtime stopped states
|
||||
// PRIORITY 1: Handle task error state (when start-tasks API returns ERROR)
|
||||
// This must come first to prevent "Connecting..." from showing when task has errored
|
||||
if (taskStatus === "ERROR") {
|
||||
return I18nKey.AGENT_STATUS$ERROR_OCCURRED;
|
||||
}
|
||||
|
||||
// PRIORITY 2: Handle conversation and runtime stopped states
|
||||
if (conversationStatus === "STOPPED" || runtimeStatus === "STATUS$STOPPED") {
|
||||
return I18nKey.CHAT_INTERFACE$STOPPED;
|
||||
}
|
||||
@@ -134,7 +142,8 @@ export function getStatusCode(
|
||||
return runtimeStatus;
|
||||
}
|
||||
|
||||
// Handle WebSocket connection states
|
||||
// PRIORITY 3: Handle WebSocket connection states
|
||||
// Note: WebSocket may be stuck in CONNECTING when task errors, so we check taskStatus first
|
||||
if (webSocketStatus === "DISCONNECTED") {
|
||||
return I18nKey.CHAT_INTERFACE$DISCONNECTED;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user