From 3fec6e55eb7694eb1aa5d09810400ad6536a5d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Fri, 3 Apr 2026 01:40:33 +0800 Subject: [PATCH] fix: Always save diagnostics on any error - Add saveDiagnostics method - Use defer in Generate to catch all errors - Simplify typeInput error handling - Automatic screenshot and HTML dump on failure --- .../geminiweb/playwright_provider.go | 95 ++++++++----------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/internal/providers/geminiweb/playwright_provider.go b/internal/providers/geminiweb/playwright_provider.go index 126740e..213f85e 100644 --- a/internal/providers/geminiweb/playwright_provider.go +++ b/internal/providers/geminiweb/playwright_provider.go @@ -114,7 +114,15 @@ func (p *PlaywrightProvider) launchIfNeeded() error { } // Generate 生成回應 -func (p *PlaywrightProvider) Generate(ctx context.Context, model string, messages []apitypes.Message, tools []apitypes.Tool, cb func(apitypes.StreamChunk)) error { +func (p *PlaywrightProvider) Generate(ctx context.Context, model string, messages []apitypes.Message, tools []apitypes.Tool, cb func(apitypes.StreamChunk)) (err error) { + // 確保在返回錯誤時保存診斷 + defer func() { + if err != nil { + fmt.Println("[GeminiWeb] Error occurred, saving diagnostics...") + _ = p.saveDiagnostics() + } + }() + fmt.Printf("[GeminiWeb] Starting generation with model: %s\n", model) // 1. 確保瀏覽器已啟動 @@ -237,6 +245,36 @@ func (p *PlaywrightProvider) Close() error { return nil } +// saveDiagnostics 保存診斷信息 +func (p *PlaywrightProvider) saveDiagnostics() error { + if p.page == nil { + return fmt.Errorf("no page available") + } + + // 截圖 + screenshotPath := "/tmp/gemini-debug.png" + if _, err := p.page.Screenshot(playwright.PageScreenshotOptions{ + Path: playwright.String(screenshotPath), + }); err == nil { + fmt.Printf("[GeminiWeb] Screenshot saved: %s\n", screenshotPath) + } + + // HTML + htmlPath := "/tmp/gemini-debug.html" + if html, err := p.page.Content(); err == nil { + if err := os.WriteFile(htmlPath, []byte(html), 0644); err == nil { + fmt.Printf("[GeminiWeb] HTML saved: %s\n", htmlPath) + } + } + + // 輸出頁面信息 + url := p.page.URL() + title, _ := p.page.Title() + fmt.Printf("[GeminiWeb] Diagnostics: URL=%s, Title=%s\n", url, title) + + return nil +} + // waitForPageReady 等待頁面完全就緒(project-golem 策略) func (p *PlaywrightProvider) waitForPageReady() error { fmt.Println("[GeminiWeb] Checking for ready state...") @@ -393,61 +431,10 @@ func (p *PlaywrightProvider) typeInput(text string) error { } if !found { - // 保存診斷信息 - screenshotPath := "/tmp/gemini-debug.png" - htmlPath := "/tmp/gemini-debug.html" - - // 截圖 - if _, err := p.page.Screenshot(playwright.PageScreenshotOptions{ - Path: playwright.String(screenshotPath), - }); err == nil { - fmt.Printf("[GeminiWeb] Screenshot saved: %s\n", screenshotPath) - } - - // HTML - if html, err := p.page.Content(); err == nil { - if err := os.WriteFile(htmlPath, []byte(html), 0644); err == nil { - fmt.Printf("[GeminiWeb] HTML saved: %s\n", htmlPath) - } - } - - // 輸出調試信息 + // 錯誤會被 Generate 的 defer 捕獲並保存診斷 url := p.page.URL() title, _ := p.page.Title() - fmt.Printf("[GeminiWeb] DEBUG: URL=%s\n", url) - fmt.Printf("[GeminiWeb] DEBUG: Title=%s\n", title) - - // 嘗試用 JavaScript 找輸入框 - fmt.Println("[GeminiWeb] Trying JavaScript element search...") - _, _ = p.page.Evaluate(` - () => { - console.log('=== DEBUG: Looking for input elements ==='); - const selectors = [ - '.ProseMirror', - 'rich-textarea', - 'div[role="textbox"]', - 'div[contenteditable="true"]', - 'textarea', - 'input' - ]; - for (const sel of selectors) { - const els = document.querySelectorAll(sel); - if (els.length > 0) { - console.log('Found', els.length, 'elements for:', sel); - } - } - - // 檢查頁面中所有可交互元素 - const interactable = document.querySelectorAll('div[contenteditable="true"], textarea, input[type="text"], [role="textbox"]'); - console.log('Total interactable elements:', interactable.length); - - return { found: false }; - } - `) - - // 顯示最後的URL和標題 - return fmt.Errorf("input field not found (URL=%s, Title=%s). Screenshot saved to %s, HTML saved to %s", - url, title, screenshotPath, htmlPath) + return fmt.Errorf("input field not found (URL=%s, Title=%s). Diagnostics will be saved to /tmp/", url, title) } // Focus 並填充(Playwright 自動等待)