mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -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 { 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 { AgentState } from "#/types/agent-state";
|
||||||
import { I18nKey } from "#/i18n/declaration";
|
import { I18nKey } from "#/i18n/declaration";
|
||||||
|
|
||||||
@@ -87,6 +87,36 @@ describe("getStatusCode", () => {
|
|||||||
// Should return runtime status since no agent state
|
// Should return runtime status since no agent state
|
||||||
expect(result).toBe("STATUS$STARTING_RUNTIME");
|
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", () => {
|
describe("getIndicatorColor", () => {
|
||||||
@@ -13,6 +13,7 @@ import { useConversationStore } from "#/state/conversation-store";
|
|||||||
import CircleErrorIcon from "#/icons/circle-error.svg?react";
|
import CircleErrorIcon from "#/icons/circle-error.svg?react";
|
||||||
import { useAgentState } from "#/hooks/use-agent-state";
|
import { useAgentState } from "#/hooks/use-agent-state";
|
||||||
import { useUnifiedWebSocketStatus } from "#/hooks/use-unified-websocket-status";
|
import { useUnifiedWebSocketStatus } from "#/hooks/use-unified-websocket-status";
|
||||||
|
import { useTaskPolling } from "#/hooks/query/use-task-polling";
|
||||||
|
|
||||||
export interface AgentStatusProps {
|
export interface AgentStatusProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -35,6 +36,7 @@ export function AgentStatus({
|
|||||||
const { curStatusMessage } = useStatusStore();
|
const { curStatusMessage } = useStatusStore();
|
||||||
const webSocketStatus = useUnifiedWebSocketStatus();
|
const webSocketStatus = useUnifiedWebSocketStatus();
|
||||||
const { data: conversation } = useActiveConversation();
|
const { data: conversation } = useActiveConversation();
|
||||||
|
const { taskStatus } = useTaskPolling();
|
||||||
|
|
||||||
const statusCode = getStatusCode(
|
const statusCode = getStatusCode(
|
||||||
curStatusMessage,
|
curStatusMessage,
|
||||||
@@ -42,17 +44,24 @@ export function AgentStatus({
|
|||||||
conversation?.status || null,
|
conversation?.status || null,
|
||||||
conversation?.runtime_status || null,
|
conversation?.runtime_status || null,
|
||||||
curAgentState,
|
curAgentState,
|
||||||
|
taskStatus,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isTaskLoading =
|
||||||
|
taskStatus && taskStatus !== "ERROR" && taskStatus !== "READY";
|
||||||
|
|
||||||
const shouldShownAgentLoading =
|
const shouldShownAgentLoading =
|
||||||
isPausing ||
|
isPausing ||
|
||||||
curAgentState === AgentState.INIT ||
|
curAgentState === AgentState.INIT ||
|
||||||
curAgentState === AgentState.LOADING ||
|
curAgentState === AgentState.LOADING ||
|
||||||
webSocketStatus === "CONNECTING";
|
(webSocketStatus === "CONNECTING" && taskStatus !== "ERROR") ||
|
||||||
|
isTaskLoading;
|
||||||
|
|
||||||
const shouldShownAgentError =
|
const shouldShownAgentError =
|
||||||
curAgentState === AgentState.ERROR ||
|
curAgentState === AgentState.ERROR ||
|
||||||
curAgentState === AgentState.RATE_LIMITED;
|
curAgentState === AgentState.RATE_LIMITED ||
|
||||||
|
webSocketStatus === "DISCONNECTED" ||
|
||||||
|
taskStatus === "ERROR";
|
||||||
|
|
||||||
const shouldShownAgentStop = curAgentState === AgentState.RUNNING;
|
const shouldShownAgentStop = curAgentState === AgentState.RUNNING;
|
||||||
|
|
||||||
@@ -61,7 +70,8 @@ export function AgentStatus({
|
|||||||
|
|
||||||
// Update global state when agent loading condition changes
|
// Update global state when agent loading condition changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShouldShownAgentLoading(shouldShownAgentLoading);
|
if (shouldShownAgentLoading)
|
||||||
|
setShouldShownAgentLoading(shouldShownAgentLoading);
|
||||||
}, [shouldShownAgentLoading, setShouldShownAgentLoading]);
|
}, [shouldShownAgentLoading, setShouldShownAgentLoading]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AgentState } from "#/types/agent-state";
|
|||||||
import { ConversationStatus } from "#/types/conversation-status";
|
import { ConversationStatus } from "#/types/conversation-status";
|
||||||
import { StatusMessage } from "#/types/message";
|
import { StatusMessage } from "#/types/message";
|
||||||
import { RuntimeStatus } from "#/types/runtime-status";
|
import { RuntimeStatus } from "#/types/runtime-status";
|
||||||
|
import { V1AppConversationStartTaskStatus } from "#/api/conversation-service/v1-conversation-service.types";
|
||||||
|
|
||||||
export enum IndicatorColor {
|
export enum IndicatorColor {
|
||||||
BLUE = "bg-blue-500",
|
BLUE = "bg-blue-500",
|
||||||
@@ -103,8 +104,15 @@ export function getStatusCode(
|
|||||||
conversationStatus: ConversationStatus | null,
|
conversationStatus: ConversationStatus | null,
|
||||||
runtimeStatus: RuntimeStatus | null,
|
runtimeStatus: RuntimeStatus | null,
|
||||||
agentState: AgentState | 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") {
|
if (conversationStatus === "STOPPED" || runtimeStatus === "STATUS$STOPPED") {
|
||||||
return I18nKey.CHAT_INTERFACE$STOPPED;
|
return I18nKey.CHAT_INTERFACE$STOPPED;
|
||||||
}
|
}
|
||||||
@@ -134,7 +142,8 @@ export function getStatusCode(
|
|||||||
return runtimeStatus;
|
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") {
|
if (webSocketStatus === "DISCONNECTED") {
|
||||||
return I18nKey.CHAT_INTERFACE$DISCONNECTED;
|
return I18nKey.CHAT_INTERFACE$DISCONNECTED;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user