fix(frontend): display LLM configuration errors to the user (#11776)

This commit is contained in:
Hiep Le 2025-11-19 20:15:42 +07:00 committed by GitHub
parent 36cf4e161a
commit 28af600c16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 39 additions and 2 deletions

View File

@ -6,6 +6,7 @@ import { AgentState } from "#/types/agent-state";
import { useAgentState } from "#/hooks/use-agent-state";
import { useTaskPolling } from "#/hooks/query/use-task-polling";
import { getStatusColor } from "#/utils/utils";
import { useErrorMessageStore } from "#/stores/error-message-store";
export interface ServerStatusProps {
className?: string;
@ -21,6 +22,7 @@ export function ServerStatus({
const { curAgentState } = useAgentState();
const { t } = useTranslation();
const { isTask, taskStatus, taskDetail } = useTaskPolling();
const { errorMessage } = useErrorMessageStore();
const isStartingStatus =
curAgentState === AgentState.LOADING || curAgentState === AgentState.INIT;
@ -69,7 +71,7 @@ export function ServerStatus({
return t(I18nKey.COMMON$SERVER_STOPPED);
}
if (curAgentState === AgentState.ERROR) {
return t(I18nKey.COMMON$ERROR);
return errorMessage || t(I18nKey.COMMON$ERROR);
}
return t(I18nKey.COMMON$RUNNING);
};
@ -79,7 +81,7 @@ export function ServerStatus({
return (
<div className={className} data-testid="server-status">
<div className="flex items-center">
<DebugStackframeDot className="w-6 h-6" color={statusColor} />
<DebugStackframeDot className="w-6 h-6 shrink-0" color={statusColor} />
<span className="text-[13px] text-white font-normal">{statusText}</span>
</div>
</div>

View File

@ -24,6 +24,7 @@ import {
isAgentStatusConversationStateUpdateEvent,
isExecuteBashActionEvent,
isExecuteBashObservationEvent,
isConversationErrorEvent,
} from "#/types/v1/type-guards";
import { handleActionEventCacheInvalidation } from "#/utils/cache-utils";
import { buildWebSocketUrl } from "#/utils/websocket-url";
@ -132,6 +133,11 @@ export function ConversationWebSocketProvider({
if (isV1Event(event)) {
addEvent(event);
// Handle ConversationErrorEvent specifically
if (isConversationErrorEvent(event)) {
setErrorMessage(event.detail);
}
// Handle AgentErrorEvent specifically
if (isAgentErrorEvent(event)) {
setErrorMessage(event.error);

View File

@ -45,3 +45,21 @@ export interface ConversationStateUpdateEventAgentStatus
export type ConversationStateUpdateEvent =
| ConversationStateUpdateEventFullState
| ConversationStateUpdateEventAgentStatus;
// Conversation error event - contains error information
export interface ConversationErrorEvent extends BaseEvent {
/**
* The source is always "environment" for conversation error events
*/
source: "environment";
/**
* Error code (e.g., "AuthenticationError")
*/
code: string;
/**
* Detailed error message
*/
detail: string;
}

View File

@ -10,6 +10,7 @@ import {
CondensationRequestEvent,
CondensationSummaryEvent,
ConversationStateUpdateEvent,
ConversationErrorEvent,
PauseEvent,
} from "./events/index";
@ -30,5 +31,6 @@ export type OpenHandsEvent =
| CondensationRequestEvent
| CondensationSummaryEvent
| ConversationStateUpdateEvent
| ConversationErrorEvent
// Control events
| PauseEvent;

View File

@ -12,6 +12,7 @@ import {
ConversationStateUpdateEvent,
ConversationStateUpdateEventAgentStatus,
ConversationStateUpdateEventFullState,
ConversationErrorEvent,
} from "./core/events/conversation-state-event";
import { SystemPromptEvent } from "./core/events/system-event";
import type { OpenHandsParsedEvent } from "../core/index";
@ -138,6 +139,14 @@ export const isAgentStatusConversationStateUpdateEvent = (
): event is ConversationStateUpdateEventAgentStatus =>
event.key === "execution_status";
/**
* Type guard function to check if an event is a conversation error event
*/
export const isConversationErrorEvent = (
event: OpenHandsEvent,
): event is ConversationErrorEvent =>
"kind" in event && event.kind === "ConversationErrorEvent";
// =============================================================================
// TEMPORARY COMPATIBILITY TYPE GUARDS
// These will be removed once we fully migrate to V1 events