From fa8e4a9191c6bcf132128f6555cffa03730ef70b Mon Sep 17 00:00:00 2001 From: Momentry Studio Date: Fri, 23 Oct 2026 04:59:55 +0800 Subject: [PATCH] fix: simplify bilingual VLM search - use single API call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Core API now returns both vlm_description (EN) and vlm_description_localized (JA) - Removed duplicate API calls (was fetching EN + JA separately) - Single API call with output_language param, uses both fields from response - Load More also updated to use results array and correct field names - Format: 【JA】localized text...【English】english text... --- src/views/SearchView.vue | 131 +++++++++------------------------------ 1 file changed, 30 insertions(+), 101 deletions(-) diff --git a/src/views/SearchView.vue b/src/views/SearchView.vue index b5c2467..0e95371 100644 --- a/src/views/SearchView.vue +++ b/src/views/SearchView.vue @@ -1087,79 +1087,22 @@ async function sendMessage() { let traces: any[] = [] try { - if (bilingual) { - // Bilingual mode: fetch both English and preferred language - const [scenesEn, scenesLang, tracesEn, tracesLang] = await Promise.all([ - fetch(`${base}/api/v1/search/vlm/scenes`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: 'en' }) - }).then(r => r.json()).catch(() => ({})), - fetch(`${base}/api/v1/search/vlm/scenes`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: outputLang }) - }).then(r => r.json()).catch(() => ({})), - fetch(`${base}/api/v1/search/vlm/traces`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: 'en' }) - }).then(r => r.json()).catch(() => ({})), - fetch(`${base}/api/v1/search/vlm/traces`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: outputLang }) - }).then(r => r.json()).catch(() => ({})) - ]) - - // Merge bilingual results - const scenesEnList = (scenesEn?.results || scenesEn || []) as any[] - const scenesLangList = (scenesLang?.results || scenesLang || []) as any[] - const tracesEnList = (tracesEn?.results || tracesEn || []) as any[] - const tracesLangList = (tracesLang?.results || tracesLang || []) as any[] - - // Match results by file_uuid + scene_index - scenes = scenesEnList.map((sEn: any) => { - const sceneIndex = sEn.index ?? sEn.scene_index ?? 0 - const sLang = scenesLangList.find((s: any) => - s.file_uuid === sEn.file_uuid && (s.index ?? s.scene_index ?? 0) === sceneIndex - ) - return { - ...sEn, - vlm_description_en: sEn.vlm_description || '', - vlm_description_localized: sLang?.vlm_description || '', - vlm_description: sEn.vlm_description || '' - } - }) - - traces = tracesEnList.map((tEn: any) => { - const traceId = tEn.trace_id - const tLang = tracesLangList.find((t: any) => t.file_uuid === tEn.file_uuid && t.trace_id === traceId) - return { - ...tEn, - vlm_description_en: tEn.vlm_description || '', - vlm_description_localized: tLang?.vlm_description || '', - vlm_description: tEn.vlm_description || '' - } - }) - } else { - // English only - const [scenesRes, tracesRes] = await Promise.all([ - fetch(`${base}/api/v1/search/vlm/scenes`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: 'en' }) - }).then(r => r.json()).catch(() => ({})), - fetch(`${base}/api/v1/search/vlm/traces`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: 'en' }) - }).then(r => r.json()).catch(() => ({})) - ]) - - scenes = (scenesRes?.results || scenesRes || []) as any[] - traces = (tracesRes?.results || tracesRes || []) as any[] - } + // Core API returns both vlm_description (EN) and vlm_description_localized (JA) in single response + const [scenesRes, tracesRes] = await Promise.all([ + fetch(`${base}/api/v1/search/vlm/scenes`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: outputLang }) + }).then(r => r.json()).catch(() => ({})), + fetch(`${base}/api/v1/search/vlm/traces`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ query: q, page_size: maxResults, min_score: 0.0, output_language: outputLang }) + }).then(r => r.json()).catch(() => ({})) + ]) + + scenes = (scenesRes?.results || scenesRes || []) as any[] + traces = (tracesRes?.results || tracesRes || []) as any[] } catch (e) { console.error('Visual search API error:', e) } @@ -1182,18 +1125,14 @@ async function sendMessage() { let vlmDesc = s.vlm_description || '' let textContent = vlmDesc - if (bilingual && s.vlm_description_en && s.vlm_description_localized) { + if (bilingual && s.vlm_description && s.vlm_description_localized) { // Show localized language first, then English - textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}\n\n【English】\n${s.vlm_description_en}` + textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}\n\n【English】\n${s.vlm_description}` vlmDesc = s.vlm_description_localized } else if (bilingual && s.vlm_description_localized) { // Only localized language available textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}` vlmDesc = s.vlm_description_localized - } else if (bilingual && s.vlm_description_en) { - // Only English available - textContent = `【English】\n${s.vlm_description_en}` - vlmDesc = s.vlm_description_en } return { @@ -1209,7 +1148,7 @@ async function sendMessage() { similarity: s.score ?? null, thumbnail_url: `${base}/api/v1/file/${s.file_uuid}/scenes/scene_${String(sceneIndex).padStart(3, '0')}.jpg`, bilingual, - vlm_description_en: s.vlm_description_en, + vlm_description_en: s.vlm_description, vlm_description_localized: s.vlm_description_localized } }) @@ -1225,18 +1164,14 @@ async function sendMessage() { let vlmDesc = t.vlm_description || '' let textContent = vlmDesc - if (bilingual && t.vlm_description_en && t.vlm_description_localized) { + if (bilingual && t.vlm_description && t.vlm_description_localized) { // Show localized language first, then English - textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}\n\n【English】\n${t.vlm_description_en}` + textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}\n\n【English】\n${t.vlm_description}` vlmDesc = t.vlm_description_localized } else if (bilingual && t.vlm_description_localized) { // Only localized language available textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}` vlmDesc = t.vlm_description_localized - } else if (bilingual && t.vlm_description_en) { - // Only English available - textContent = `【English】\n${t.vlm_description_en}` - vlmDesc = t.vlm_description_en } return { @@ -1251,7 +1186,7 @@ async function sendMessage() { text_content: textContent, similarity: t.score ?? null, bilingual, - vlm_description_en: t.vlm_description_en, + vlm_description_en: t.vlm_description, vlm_description_localized: t.vlm_description_localized } }) @@ -1350,19 +1285,16 @@ async function loadMore() { }).then(r => r.json()).catch(() => ({})) ]) - const sceneResults = (scenesRes.scenes || []).map((s: any) => { + const sceneResults = (scenesRes.results || []).map((s: any) => { let vlmDesc = s.vlm_description || '' let textContent = vlmDesc - if (bilingual && s.vlm_description_en && s.vlm_description_localized) { - textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}\n\n【English】\n${s.vlm_description_en}` + if (bilingual && s.vlm_description && s.vlm_description_localized) { + textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}\n\n【English】\n${s.vlm_description}` vlmDesc = s.vlm_description_localized } else if (bilingual && s.vlm_description_localized) { textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_localized}` vlmDesc = s.vlm_description_localized - } else if (bilingual && s.vlm_description_en) { - textContent = `【English】\n${s.vlm_description_en}` - vlmDesc = s.vlm_description_en } return { @@ -1379,25 +1311,22 @@ async function loadMore() { thumbnail_url: s.thumbnail_url, vlm_tags: s.vlm_tags || [], bilingual, - vlm_description_en: s.vlm_description_en, + vlm_description_en: s.vlm_description, vlm_description_localized: s.vlm_description_localized, search_query: q } }) - const traceResults = (tracesRes.traces || []).map((t: any) => { + const traceResults = (tracesRes.results || []).map((t: any) => { let vlmDesc = t.vlm_description || '' let textContent = vlmDesc - if (bilingual && t.vlm_description_en && t.vlm_description_localized) { - textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}\n\n【English】\n${t.vlm_description_en}` + if (bilingual && t.vlm_description && t.vlm_description_localized) { + textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}\n\n【English】\n${t.vlm_description}` vlmDesc = t.vlm_description_localized } else if (bilingual && t.vlm_description_localized) { textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_localized}` vlmDesc = t.vlm_description_localized - } else if (bilingual && t.vlm_description_en) { - textContent = `【English】\n${t.vlm_description_en}` - vlmDesc = t.vlm_description_en } return { @@ -1411,7 +1340,7 @@ async function loadMore() { source_type: 'trace', trace_id: t.trace_id, bilingual, - vlm_description_en: t.vlm_description_en, + vlm_description_en: t.vlm_description, vlm_description_localized: t.vlm_description_localized, search_query: q }