From 86e914709f0a963930cb1a191d7bcfd80462d97c Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 21 Feb 2026 05:06:25 +0800 Subject: [PATCH] fix(agent): prefer remote image for qwen fallback vision parsing --- lib/ai/smart-analyzer.ts | 89 ++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts index b7cbd9a34..335450b72 100644 --- a/lib/ai/smart-analyzer.ts +++ b/lib/ai/smart-analyzer.ts @@ -371,11 +371,16 @@ async function buildAnalyzerVisionImagesByMode(params: { return buildAnalyzerVisionImages(urls); } -async function getQwen3VlFallbackImageInput(imageUrls?: string[]): Promise { +async function getQwen3VlFallbackImageCandidates(imageUrls?: string[]): Promise { const firstImageUrl = (imageUrls || []).find((url) => isHttpUrl(url)); - if (!firstImageUrl) return null; + if (!firstImageUrl) return []; + + const candidates: string[] = [firstImageUrl]; const inlineDataUrl = await toAnalyzerDataUrl(firstImageUrl); - return inlineDataUrl || firstImageUrl; + if (inlineDataUrl) { + candidates.push(inlineDataUrl); + } + return candidates; } async function analyzeWithHardcodedQwen3VlFallback(params: { @@ -388,8 +393,8 @@ async function analyzeWithHardcodedQwen3VlFallback(params: { }): Promise { if (!params.hasImage) return null; - const imageInput = await getQwen3VlFallbackImageInput(params.imageUrls); - if (!imageInput) { + const imageCandidates = await getQwen3VlFallbackImageCandidates(params.imageUrls); + if (imageCandidates.length === 0) { console.warn( `[Smart Analyzer] qwen3-vl fallback skipped: no valid image url (${params.reason})`, ); @@ -421,22 +426,29 @@ async function analyzeWithHardcodedQwen3VlFallback(params: { console.warn( `[Smart Analyzer] Primary analysis failed, fallback to hardcoded qwen3-vl (${params.reason})`, ); - const { text: fallbackText, model } = await callQwen3Vl({ - imageUrl: imageInput, - prompt, - temperature: 0.1, - maxTokens: 360, - timeoutMs: ANALYZER_TIMEOUT_MS, - retries: 1, - }); - const parsed = parseAnalysisResultFromModelText( - fallbackText, - params.text, - params.hasImage, - ); - if (parsed) { - console.log(`[Smart Analyzer] qwen3-vl fallback success: ${model}`); - return parsed; + for (const imageInput of imageCandidates) { + const inputMode = imageInput.startsWith("data:image/") ? "inline" : "remote"; + try { + const { text: fallbackText, model } = await callQwen3Vl({ + imageUrl: imageInput, + prompt, + temperature: 0.1, + maxTokens: 360, + timeoutMs: ANALYZER_TIMEOUT_MS, + retries: 1, + }); + const parsed = parseAnalysisResultFromModelText( + fallbackText, + params.text, + params.hasImage, + ); + if (parsed) { + console.log(`[Smart Analyzer] qwen3-vl fallback success: ${model}, input=${inputMode}`); + return parsed; + } + } catch (attemptError) { + console.warn(`[Smart Analyzer] qwen3-vl fallback attempt failed: input=${inputMode}`, attemptError); + } } console.warn("[Smart Analyzer] qwen3-vl fallback returned unparsable content"); return null; @@ -451,8 +463,8 @@ async function summarizeWithHardcodedQwen3VlFallback(params: { context?: string; imageUrls?: string[]; }): Promise { - const imageInput = await getQwen3VlFallbackImageInput(params.imageUrls); - if (!imageInput) return null; + const imageCandidates = await getQwen3VlFallbackImageCandidates(params.imageUrls); + if (imageCandidates.length === 0) return null; const prompt = [ "你是图像观察助手。只描述图片可见内容,不要写执行过程,不要推测用户意图。", @@ -466,18 +478,25 @@ async function summarizeWithHardcodedQwen3VlFallback(params: { try { const { callQwen3Vl } = await import("./qwen3-vl"); console.warn("[Smart Analyzer] Visual summary fallback to hardcoded qwen3-vl"); - const { text, model } = await callQwen3Vl({ - imageUrl: imageInput, - prompt, - temperature: 0.1, - maxTokens: 140, - timeoutMs: ANALYZER_TIMEOUT_MS, - retries: 1, - }); - const normalized = normalizeVisualSummaryText(text); - if (normalized && !isWeakVisualSummary(normalized)) { - console.log(`[Smart Analyzer] qwen3-vl summary fallback success: ${model}`); - return normalized; + for (const imageInput of imageCandidates) { + const inputMode = imageInput.startsWith("data:image/") ? "inline" : "remote"; + try { + const { text, model } = await callQwen3Vl({ + imageUrl: imageInput, + prompt, + temperature: 0.1, + maxTokens: 140, + timeoutMs: ANALYZER_TIMEOUT_MS, + retries: 1, + }); + const normalized = normalizeVisualSummaryText(text); + if (normalized && !isWeakVisualSummary(normalized)) { + console.log(`[Smart Analyzer] qwen3-vl summary fallback success: ${model}, input=${inputMode}`); + return normalized; + } + } catch (attemptError) { + console.warn(`[Smart Analyzer] qwen3-vl summary fallback attempt failed: input=${inputMode}`, attemptError); + } } return null; } catch (error) {