diff --git a/.gemini/settings.json b/.gemini/settings.json index 7c1f59f1..e82592c1 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -9,6 +9,7 @@ ".gemini/**/*", "skills/**/*", "src/**/*", // 别忘了加上你的源代码目录! + ".gemini-clipboard/**/*", "README.md" ] }, diff --git a/.gitignore b/.gitignore index 9eadf06f..636af295 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ # vscode -.vscode +.vscode + +#gemini +.gemini-clipboard +.gemini # Intellij *.iml diff --git a/README.md b/README.md index b37b842c..0748b29a 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ Access plugin settings via: - **Maximum Retries**: (Visible only when enabled) Maximum number of retry attempts (0-10). Default: 3. - **API Error Debugging Mode**: * **Disabled (Default)**: Uses standard, concise error reporting. - * **Enabled**: Activates detailed error logging (similar to DeepSeek's verbose output) for all providers and tasks (including Translate and Search). This includes HTTP status codes and raw response text, which is crucial for troubleshooting API connection issues. + * **Enabled**: Activates detailed error logging (similar to DeepSeek's verbose output) for all providers and tasks (including Translate, Search, and Connection Tests). This includes HTTP status codes and raw response text, which is crucial for troubleshooting API connection issues. stable API calls ### General Settings diff --git a/README_zh.md b/README_zh.md index c077bb00..88fc43c9 100644 --- a/README_zh.md +++ b/README_zh.md @@ -218,7 +218,7 @@ Notemd 通过与各种大型语言模型 (LLM) 集成来增强您的 Obsidian - **最大重试次数 (Maximum Retries)**: (仅在启用时可见) 最大重试尝试次数(0-10)。默认值:3。 - **API 错误调试模式 (API Error Debugging Mode)**: * **禁用 (默认)**: 使用标准的简洁错误报告。 - * **启用**: 为所有提供商和任务(包括翻译和搜索)激活详细的错误日志记录(类似于 DeepSeek 的详细输出)。这包括 HTTP 状态代码和原始响应文本,对于排查 API 连接问题至关重要。 + * **启用**: 为所有提供商和任务(包括翻译、搜索和连接测试)激活详细的错误日志记录(类似于 DeepSeek 的详细输出)。这包括 HTTP 状态代码和原始响应文本,对于排查 API 连接问题至关重要。 stable API calls diff --git a/change.md b/change.md index eb7d5a06..820d98d1 100644 --- a/change.md +++ b/change.md @@ -8,11 +8,11 @@ This document summarizes the major functional and architectural changes implemen ### English * **Modularized API Error Handling**: Refactored error handling logic to ensure consistency across all tasks. -* **Enhanced Debugging**: The "API Error Debugging Mode" now fully supports "Translate" and "Search" tasks, providing detailed logs (HTTP status codes, raw responses) for deeper troubleshooting. +* **Enhanced Debugging**: The "API Error Debugging Mode" now fully supports "Translate", "Search", and "Connection Test" tasks, providing detailed logs (HTTP status codes, raw responses) for deeper troubleshooting. ### Chinese (中文) * **模块化API错误处理**: 重构了错误处理逻辑,确保所有任务的一致性。 -* **增强调试**: “API错误调试模式”现在完全支持“翻译”和“搜索”任务,提供详细的日志(HTTP状态代码、原始响应)以便进行更深入的故障排除。 +* **增强调试**: “API错误调试模式”现在完全支持“翻译”、“搜索”和“连接测试”任务,提供详细的日志(HTTP状态代码、原始响应)以便进行更深入的故障排除。 --- diff --git a/src/llmUtils.ts b/src/llmUtils.ts index 0bc8ce68..bd979d75 100644 --- a/src/llmUtils.ts +++ b/src/llmUtils.ts @@ -8,9 +8,10 @@ import { ErrorModal } from './ui/ErrorModal'; // Import ErrorModal /** * Tests the connection to a given LLM provider. * @param provider The provider configuration to test. + * @param debugMode Whether to include detailed debug info in failure messages. * @returns A promise resolving to an object indicating success and a message. */ -export async function testAPI(provider: LLMProviderConfig): Promise<{ success: boolean; message: string }> { +export async function testAPI(provider: LLMProviderConfig, debugMode: boolean = false): Promise<{ success: boolean; message: string }> { try { let response; let url: string; @@ -273,7 +274,15 @@ export async function testAPI(provider: LLMProviderConfig): Promise<{ success: b } catch (error: unknown) { // Changed to unknown const message = error instanceof Error ? error.message : String(error); console.error(`Connection test failed for ${provider.name}:`, error); - return { success: false, message: `Connection failed: ${message}` }; + + let finalMessage = `Connection failed: ${message}`; + if (debugMode) { + const debugInfo = getDebugInfo(error); + if (debugInfo) { + finalMessage += `\n\n[DEBUG MODE ENABLED]\n${debugInfo}`; + } + } + return { success: false, message: finalMessage }; } } @@ -363,7 +372,7 @@ async function callApiWithRetry( // --- Provider-Specific API Call Implementations --- -// Helper function to safely parse error details from API responses +// Helper function to safe-parse error details from API responses function getErrorDetails(errorText: string): string { try { const errorJson = JSON.parse(errorText); @@ -383,6 +392,20 @@ function getErrorDetails(errorText: string): string { } } +/** + * Extracts detailed debug information from an error object. + * @param error The error object. + * @returns A formatted string with stack trace and raw response if available. + */ +export function getDebugInfo(error: any): string { + const stack = error instanceof Error ? error.stack : ''; + const rawText = (error as any).text || (error as any).response?.text || ''; + let info = ''; + if (stack) info += `Stack: ${stack}\n`; + if (rawText) info += `Raw Response: ${rawText}`; + return info.trim(); +} + /** * Modularized API Error Handler. * Implements the "DeepSeek-style" verbose debugging design. @@ -429,6 +452,11 @@ export function handleApiError( progressReporter.log(`[${providerName}] Error response: ${rawText}`); } else { progressReporter.log(`[${providerName}] Error details: ${message}`); + // Use shared helper for stack trace if it's an error object + if (errorOrResponse instanceof Error) { + const extraDebug = getDebugInfo(errorOrResponse); + if (extraDebug) progressReporter.log(`[${providerName}] ${extraDebug}`); + } } } diff --git a/src/main.ts b/src/main.ts index 1b71976e..debd0bd5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -863,7 +863,7 @@ export default class NotemdPlugin extends Plugin { useReporter.updateStatus(`Testing ${provider.name}...`, 50); const testingNotice = new Notice(`Testing connection to ${provider.name}...`, 0); - const result = await testAPI(provider); // Use utility function + const result = await testAPI(provider, this.settings.enableApiErrorDebugMode); // Use utility function testingNotice.hide(); if (result.success) { diff --git a/src/searchUtils.ts b/src/searchUtils.ts index 91642f8a..2929fa29 100644 --- a/src/searchUtils.ts +++ b/src/searchUtils.ts @@ -1,7 +1,7 @@ import { App, requestUrl, Notice, Editor, MarkdownView } from 'obsidian'; // Added Notice, Editor, MarkdownView import { NotemdSettings, ProgressReporter, SearchResult } from './types'; import { estimateTokens, getProviderForTask, getModelForTask } from './utils'; // Added getProviderForTask, getModelForTask -import { callDeepSeekAPI, callOpenAIApi, callAnthropicApi, callGoogleApi, callMistralApi, callAzureOpenAIApi, callLMStudioApi, callOllamaApi, callOpenRouterAPI, handleApiError } from './llmUtils'; // Added LLM callers +import { callDeepSeekAPI, callOpenAIApi, callAnthropicApi, callGoogleApi, callMistralApi, callAzureOpenAIApi, callLMStudioApi, callOllamaApi, callOpenRouterAPI, handleApiError, getDebugInfo } from './llmUtils'; // Added LLM callers import { cleanupLatexDelimiters, refineMermaidBlocks } from './mermaidProcessor'; // Added post-processors import { ErrorModal } from './ui/ErrorModal'; // Added ErrorModal import { getSystemPrompt } from './promptUtils'; // Import for default prompts @@ -110,9 +110,10 @@ export async function searchDuckDuckGo(query: string, settings: NotemdSettings, * Fetches content from a URL and extracts basic text. * @param url The URL to fetch. * @param progressReporter For logging. + * @param debugMode Whether to log detailed debug info on error. * @returns A promise resolving to the extracted text content or an error message string. */ -export async function fetchContentFromUrl(url: string, progressReporter: ProgressReporter): Promise { +export async function fetchContentFromUrl(url: string, progressReporter: ProgressReporter, debugMode: boolean = false): Promise { progressReporter.log(`Fetching content from: ${url}`); try { const response = await requestUrl({ @@ -152,6 +153,14 @@ export async function fetchContentFromUrl(url: string, progressReporter: Progres } catch (error: unknown) { const message = error instanceof Error ? error.message : String(error); progressReporter.log(`Error fetching content from ${url}: ${message}`); + + if (debugMode) { + const debugInfo = getDebugInfo(error); + if (debugInfo) { + progressReporter.log(`[DEBUG] Fetch Error Details:\n${debugInfo}`); + } + } + return `[Content skipped: Error fetching - ${message}]`; } } @@ -229,7 +238,7 @@ export async function _performResearch(app: App, settings: NotemdSettings, topic if (progressReporter.cancelled) throw new Error(`Processing cancelled by user before fetching DDG result ${index + 1}.`); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout fetching ${result.url}`)), settings.ddgFetchTimeout * 1000)); try { - return await Promise.race([fetchContentFromUrl(result.url, progressReporter), timeoutPromise]); + return await Promise.race([fetchContentFromUrl(result.url, progressReporter, settings.enableApiErrorDebugMode), timeoutPromise]); } catch (fetchError: unknown) { const message = fetchError instanceof Error ? fetchError.message : String(fetchError); if (message.includes("cancelled by user")) throw fetchError; diff --git a/src/ui/NotemdSettingTab.ts b/src/ui/NotemdSettingTab.ts index 34c2f1f1..5ef6fbcd 100644 --- a/src/ui/NotemdSettingTab.ts +++ b/src/ui/NotemdSettingTab.ts @@ -193,7 +193,7 @@ export class NotemdSettingTab extends PluginSettingTab { button.setDisabled(true).setButtonText('Testing...'); const testingNotice = new Notice(`Testing connection to ${activeProvider.name}...`, 0); try { - const result = await testAPI(activeProvider); // Use imported testAPI + const result = await testAPI(activeProvider, this.plugin.settings.enableApiErrorDebugMode); // Use imported testAPI testingNotice.hide(); if (result.success) { new Notice(`✅ Success: ${result.message}`, 5000); } else { new Notice(`❌ Failed: ${result.message}. Check console.`, 10000); }