From 95c0eb1c7e0db7974dcfe491602124b8d7dc4bfa Mon Sep 17 00:00:00 2001 From: digua Date: Tue, 9 Dec 2025 22:16:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20AI=E9=AA=8C=E8=AF=81=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/main/ai/llm/deepseek.ts | 23 +++++++++++++--- electron/main/ai/llm/openai-compatible.ts | 27 ++++++++++++++----- electron/main/ai/llm/qwen.ts | 23 +++++++++++++--- electron/main/ai/llm/types.ts | 3 ++- electron/main/ipc/ai.ts | 7 +++-- electron/preload/index.d.ts | 7 ++++- .../common/settings/AIConfigEditModal.vue | 22 +++++++-------- 7 files changed, 81 insertions(+), 31 deletions(-) diff --git a/electron/main/ai/llm/deepseek.ts b/electron/main/ai/llm/deepseek.ts index 84664b81..2494873a 100644 --- a/electron/main/ai/llm/deepseek.ts +++ b/electron/main/ai/llm/deepseek.ts @@ -298,7 +298,7 @@ export class DeepSeekService implements ILLMService { } } - async validateApiKey(): Promise { + async validateApiKey(): Promise<{ success: boolean; error?: string }> { try { // 发送一个简单请求验证 API Key const response = await fetch(`${this.baseUrl}/v1/models`, { @@ -307,9 +307,24 @@ export class DeepSeekService implements ILLMService { Authorization: `Bearer ${this.apiKey}`, }, }) - return response.ok - } catch { - return false + if (response.ok) { + return { success: true } + } + // 尝试获取错误详情 + const errorText = await response.text() + let errorMessage = `HTTP ${response.status}` + try { + const errorJson = JSON.parse(errorText) + errorMessage = errorJson.error?.message || errorJson.message || errorMessage + } catch { + if (errorText) { + errorMessage = errorText.slice(0, 200) + } + } + return { success: false, error: errorMessage } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { success: false, error: errorMessage } } } } diff --git a/electron/main/ai/llm/openai-compatible.ts b/electron/main/ai/llm/openai-compatible.ts index e976e35a..31e9eec2 100644 --- a/electron/main/ai/llm/openai-compatible.ts +++ b/electron/main/ai/llm/openai-compatible.ts @@ -248,7 +248,7 @@ export class OpenAICompatibleService implements ILLMService { // 调试:如果有 delta 但没有 content,记录其他可能的内容字段 if (delta && !delta.content && !delta.tool_calls && !finishReason) { const deltaKeys = Object.keys(delta) - if (deltaKeys.length > 0 && !deltaKeys.every(k => ['role', 'name', 'audio_content'].includes(k))) { + if (deltaKeys.length > 0 && !deltaKeys.every((k) => ['role', 'name', 'audio_content'].includes(k))) { aiLogger.warn('OpenAI-Compatible', '检测到未处理的 delta 字段', { deltaKeys, delta }) } } @@ -348,7 +348,7 @@ export class OpenAICompatibleService implements ILLMService { } } - async validateApiKey(): Promise { + async validateApiKey(): Promise<{ success: boolean; error?: string }> { try { const headers: Record = { 'Content-Type': 'application/json', @@ -370,19 +370,32 @@ export class OpenAICompatibleService implements ILLMService { // 200 表示成功,401/403 表示认证失败,其他状态可能是参数问题但服务可达 if (response.ok) { - return true + return { success: true } + } + + // 尝试获取错误详情 + const errorText = await response.text() + let errorMessage = `HTTP ${response.status}` + try { + const errorJson = JSON.parse(errorText) + errorMessage = errorJson.error?.message || errorJson.message || errorMessage + } catch { + if (errorText) { + errorMessage = errorText.slice(0, 200) + } } // 认证失败 if (response.status === 401 || response.status === 403) { - return false + return { success: false, error: errorMessage } } // 其他错误(如 400 参数错误)但服务可达,认为验证通过 // 因为这说明认证成功了,只是请求参数有问题 - return true - } catch { - return false + return { success: true } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { success: false, error: errorMessage } } } } diff --git a/electron/main/ai/llm/qwen.ts b/electron/main/ai/llm/qwen.ts index b5799aac..5309244a 100644 --- a/electron/main/ai/llm/qwen.ts +++ b/electron/main/ai/llm/qwen.ts @@ -276,7 +276,7 @@ export class QwenService implements ILLMService { } } - async validateApiKey(): Promise { + async validateApiKey(): Promise<{ success: boolean; error?: string }> { try { // 发送一个简单请求验证 API Key const response = await fetch(`${this.baseUrl}/models`, { @@ -285,9 +285,24 @@ export class QwenService implements ILLMService { Authorization: `Bearer ${this.apiKey}`, }, }) - return response.ok - } catch { - return false + if (response.ok) { + return { success: true } + } + // 尝试获取错误详情 + const errorText = await response.text() + let errorMessage = `HTTP ${response.status}` + try { + const errorJson = JSON.parse(errorText) + errorMessage = errorJson.error?.message || errorJson.message || errorMessage + } catch { + if (errorText) { + errorMessage = errorText.slice(0, 200) + } + } + return { success: false, error: errorMessage } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { success: false, error: errorMessage } } } } diff --git a/electron/main/ai/llm/types.ts b/electron/main/ai/llm/types.ts index 96bb63f6..9245527a 100644 --- a/electron/main/ai/llm/types.ts +++ b/electron/main/ai/llm/types.ts @@ -150,8 +150,9 @@ export interface ILLMService { /** * 验证 API Key 是否有效 + * @returns 验证结果和可能的错误信息 */ - validateApiKey(): Promise + validateApiKey(): Promise<{ success: boolean; error?: string }> } /** diff --git a/electron/main/ipc/ai.ts b/electron/main/ipc/ai.ts index b08c955a..f0cd2704 100644 --- a/electron/main/ipc/ai.ts +++ b/electron/main/ipc/ai.ts @@ -265,6 +265,7 @@ export function registerAIHandlers({ win }: IpcContext): void { /** * 验证 API Key(支持自定义 baseUrl 和 model) + * 返回对象格式:{ success: boolean, error?: string } */ ipcMain.handle( 'llm:validateApiKey', @@ -274,10 +275,12 @@ export function registerAIHandlers({ win }: IpcContext): void { const service = llm.createLLMService({ provider, apiKey, baseUrl, model }) const result = await service.validateApiKey() console.log('[LLM:validateApiKey] 验证结果:', result) - return result + return { success: result.success, error: result.error } } catch (error) { console.error('[LLM:validateApiKey] 验证失败:', error) - return false + // 提取有意义的错误信息 + const errorMessage = error instanceof Error ? error.message : String(error) + return { success: false, error: errorMessage } } } ) diff --git a/electron/preload/index.d.ts b/electron/preload/index.d.ts index 0f8dee9f..e68f1ee2 100644 --- a/electron/preload/index.d.ts +++ b/electron/preload/index.d.ts @@ -225,7 +225,12 @@ interface LlmApi { setActiveConfig: (id: string) => Promise<{ success: boolean; error?: string }> // 验证和检查 - validateApiKey: (provider: string, apiKey: string, baseUrl?: string, model?: string) => Promise + validateApiKey: ( + provider: string, + apiKey: string, + baseUrl?: string, + model?: string + ) => Promise<{ success: boolean; error?: string }> hasConfig: () => Promise // 聊天功能 diff --git a/src/components/common/settings/AIConfigEditModal.vue b/src/components/common/settings/AIConfigEditModal.vue index ee414d0d..869d592e 100644 --- a/src/components/common/settings/AIConfigEditModal.vue +++ b/src/components/common/settings/AIConfigEditModal.vue @@ -197,14 +197,19 @@ async function validateKey() { try { const testApiKey = apiKey || 'sk-no-key-required' - const isValid = await window.llmApi.validateApiKey( + const result = await window.llmApi.validateApiKey( provider || 'openai-compatible', testApiKey, baseUrl || undefined, formData.value.model || undefined ) - validationResult.value = isValid ? 'valid' : 'invalid' - validationMessage.value = isValid ? '连接验证成功' : '连接验证失败,但仍可保存' + validationResult.value = result.success ? 'valid' : 'invalid' + if (result.success) { + validationMessage.value = '连接验证成功' + } else { + // 显示详细的错误信息 + validationMessage.value = result.error || '连接验证失败' + } } catch (error) { validationResult.value = 'invalid' validationMessage.value = '验证失败:' + String(error) @@ -482,13 +487,7 @@ watch( :placeholder="mode === 'edit' ? '输入新的 API Key(留空保持原有)' : '输入你的 API Key'" class="flex-1" /> - + 验证 @@ -526,7 +525,7 @@ watch(
- 测试 + 验证
@@ -616,7 +615,6 @@ watch(