fix(config): 优雅处理 Provider 未配置错误

- 添加 ConfigurationError 类替代 process.exit(1)
- Server 端捕获配置错误并返回友好消息
- UI 端支持 config_error 类型的 WebSocket 消息
- 服务器不再因配置缺失而崩溃
This commit is contained in:
2025-12-14 22:24:51 +08:00
parent c307cd3a7c
commit 32064a3531
6 changed files with 107 additions and 15 deletions
+2
View File
@@ -122,6 +122,8 @@ export type {
CompressionStatus,
CompressionType,
CompressionResult,
// WebSocket error types
ConfigErrorPayload,
} from './types.js';
// API Configuration
+18
View File
@@ -756,3 +756,21 @@ export interface CompressionResult {
summaryTokens?: number;
}
// ============ WebSocket 错误相关 ============
/**
* 配置错误 Payload
*
* 当 Provider 未配置 API Key 时,通过 WebSocket 返回此错误
*/
export interface ConfigErrorPayload {
/** 错误类型标识 */
type: 'config_error';
/** 错误消息 */
message: string;
/** 缺失配置的 Provider */
provider?: string;
/** 建议的操作 */
action: 'open_providers_panel' | 'open_settings';
}
+12 -2
View File
@@ -7,12 +7,15 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { createWebSocket, getMessages, type Message } from '../api/client.js';
import type { PermissionRequest } from '../components/PermissionDialog.js';
import type { ConfigErrorPayload } from '../api/types.js';
interface UseChatOptions {
sessionId: string;
onError?: (error: Error) => void;
onSessionNotFound?: () => void;
onSessionUpdated?: (sessionId: string, name: string) => void;
/** 配置错误回调(如 API Key 未配置) */
onConfigError?: (error: ConfigErrorPayload) => void;
}
interface ChatState {
@@ -23,7 +26,7 @@ interface ChatState {
permissionRequest: PermissionRequest | null;
}
export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdated }: UseChatOptions) {
export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdated, onConfigError }: UseChatOptions) {
const [state, setState] = useState<ChatState>({
messages: [],
isConnected: false,
@@ -43,9 +46,11 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
const onErrorRef = useRef(onError);
const onSessionNotFoundRef = useRef(onSessionNotFound);
const onSessionUpdatedRef = useRef(onSessionUpdated);
const onConfigErrorRef = useRef(onConfigError);
onErrorRef.current = onError;
onSessionNotFoundRef.current = onSessionNotFound;
onSessionUpdatedRef.current = onSessionUpdated;
onConfigErrorRef.current = onConfigError;
// 加载历史消息
const loadMessages = useCallback(async () => {
@@ -137,7 +142,12 @@ export function useChat({ sessionId, onError, onSessionNotFound, onSessionUpdate
break;
case 'error':
onErrorRef.current?.(new Error(message.payload?.message || 'Unknown error'));
// 检查是否为配置错误
if (message.payload?.type === 'config_error') {
onConfigErrorRef.current?.(message.payload as ConfigErrorPayload);
} else {
onErrorRef.current?.(new Error(message.payload?.message || 'Unknown error'));
}
setState((prev) => ({ ...prev, isLoading: false, streamingContent: '' }));
break;