fix: search visual cards show localized language first (JA before EN)

- Bilingual format: 【JA】... then 【English】...
- Summary uses localized language (vlm_description_lang) as primary
- Load More function now also supports bilingual format
- Fallback: only vlm_description shows single language without labels
This commit is contained in:
2026-10-23 04:41:15 +08:00
parent 342d4e33b8
commit 1ffceaba26

View File

@@ -1178,18 +1178,22 @@ async function sendMessage() {
const frameNumber = s.frame_number ?? Math.round(ts * fps) const frameNumber = s.frame_number ?? Math.round(ts * fps)
const windowSec = 5 const windowSec = 5
// Build bilingual description // Build bilingual description: localized language first, then English
let vlmDesc = s.vlm_description || '' let vlmDesc = s.vlm_description || ''
let textContent = vlmDesc let textContent = vlmDesc
if (bilingual && s.vlm_description_en && s.vlm_description_lang) { if (bilingual && s.vlm_description_en && s.vlm_description_lang) {
// Show both English and preferred language // Show localized language first, then English
textContent = `【English】\n${s.vlm_description_en}\n\n【${outputLang.toUpperCase()}】\n${s.vlm_description_lang}` textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_lang}\n\n【English】\n${s.vlm_description_en}`
vlmDesc = textContent vlmDesc = s.vlm_description_lang
} else if (bilingual && s.vlm_description_lang) {
// Only localized language available
textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_lang}`
vlmDesc = s.vlm_description_lang
} else if (bilingual && s.vlm_description_en) { } else if (bilingual && s.vlm_description_en) {
// Only English available // Only English available
textContent = `【English】\n${s.vlm_description_en}` textContent = `【English】\n${s.vlm_description_en}`
vlmDesc = textContent vlmDesc = s.vlm_description_en
} }
return { return {
@@ -1217,16 +1221,22 @@ async function sendMessage() {
const ts = t.start_time ?? t.timestamp_sec ?? (frame / fps) const ts = t.start_time ?? t.timestamp_sec ?? (frame / fps)
const windowSec = 5 const windowSec = 5
// Build bilingual description // Build bilingual description: localized language first, then English
let vlmDesc = t.vlm_description || '' let vlmDesc = t.vlm_description || ''
let textContent = vlmDesc let textContent = vlmDesc
if (bilingual && t.vlm_description_en && t.vlm_description_lang) { if (bilingual && t.vlm_description_en && t.vlm_description_lang) {
textContent = `【English】\n${t.vlm_description_en}\n\n【${outputLang.toUpperCase()}】\n${t.vlm_description_lang}` // Show localized language first, then English
vlmDesc = textContent textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_lang}\n\n【English】\n${t.vlm_description_en}`
vlmDesc = t.vlm_description_lang
} else if (bilingual && t.vlm_description_lang) {
// Only localized language available
textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_lang}`
vlmDesc = t.vlm_description_lang
} else if (bilingual && t.vlm_description_en) { } else if (bilingual && t.vlm_description_en) {
// Only English available
textContent = `【English】\n${t.vlm_description_en}` textContent = `【English】\n${t.vlm_description_en}`
vlmDesc = textContent vlmDesc = t.vlm_description_en
} }
return { return {
@@ -1323,6 +1333,7 @@ async function loadMore() {
const base = getApiBase() const base = getApiBase()
const maxResults = 10 const maxResults = 10
const outputLang = searchConfig.value.vlm_output_language || 'en' const outputLang = searchConfig.value.vlm_output_language || 'en'
const bilingual = outputLang !== 'en'
// Use offset-based pagination instead of page // Use offset-based pagination instead of page
const offset = nextOffset.value const offset = nextOffset.value
@@ -1339,33 +1350,72 @@ async function loadMore() {
}).then(r => r.json()).catch(() => ({})) }).then(r => r.json()).catch(() => ({}))
]) ])
const sceneResults = (scenesRes.scenes || []).map((s: any) => ({ const sceneResults = (scenesRes.scenes || []).map((s: any) => {
file_uuid: s.file_uuid, let vlmDesc = s.vlm_description || ''
file_name: s.file_name || '', let textContent = vlmDesc
summary: `[SCENE] ${s.vlm_description || ''}`,
similarity: s.score ?? null, if (bilingual && s.vlm_description_en && s.vlm_description_lang) {
start_frame: s.frame_number ?? Math.round((s.timestamp_sec || 0) * 30), textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_lang}\n\n【English】\n${s.vlm_description_en}`
end_frame: (s.frame_number ?? Math.round((s.timestamp_sec || 0) * 30)) + 30, vlmDesc = s.vlm_description_lang
start_time: s.timestamp_sec, } else if (bilingual && s.vlm_description_lang) {
end_time: (s.timestamp_sec || 0) + 1, textContent = `【${outputLang.toUpperCase()}】\n${s.vlm_description_lang}`
source_type: 'scene', vlmDesc = s.vlm_description_lang
thumbnail_url: s.thumbnail_url, } else if (bilingual && s.vlm_description_en) {
vlm_tags: s.vlm_tags || [], textContent = `【English】\n${s.vlm_description_en}`
search_query: q vlmDesc = s.vlm_description_en
})) }
return {
file_uuid: s.file_uuid,
file_name: s.file_name || '',
summary: vlmDesc ? `[SCENE] ${vlmDesc}` : `[SCENE] ${s.vlm_description || ''}`,
text_content: textContent,
similarity: s.score ?? null,
start_frame: s.frame_number ?? Math.round((s.timestamp_sec || 0) * 30),
end_frame: (s.frame_number ?? Math.round((s.timestamp_sec || 0) * 30)) + 30,
start_time: s.timestamp_sec,
end_time: (s.timestamp_sec || 0) + 1,
source_type: 'scene',
thumbnail_url: s.thumbnail_url,
vlm_tags: s.vlm_tags || [],
bilingual,
vlm_description_en: s.vlm_description_en,
vlm_description_lang: s.vlm_description_lang,
search_query: q
}
})
const traceResults = (tracesRes.traces || []).map((t: any) => ({ const traceResults = (tracesRes.traces || []).map((t: any) => {
file_uuid: t.file_uuid, let vlmDesc = t.vlm_description || ''
file_name: t.file_name || '', let textContent = vlmDesc
summary: `[TRACE] ${t.vlm_description || ''}`,
similarity: t.score ?? null, if (bilingual && t.vlm_description_en && t.vlm_description_lang) {
start_frame: t.start_frame, textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_lang}\n\n【English】\n${t.vlm_description_en}`
end_frame: t.end_frame, vlmDesc = t.vlm_description_lang
source_type: 'trace', } else if (bilingual && t.vlm_description_lang) {
trace_id: t.trace_id, textContent = `【${outputLang.toUpperCase()}】\n${t.vlm_description_lang}`
vlm_description: t.vlm_description, vlmDesc = t.vlm_description_lang
search_query: q } else if (bilingual && t.vlm_description_en) {
})) textContent = `【English】\n${t.vlm_description_en}`
vlmDesc = t.vlm_description_en
}
return {
file_uuid: t.file_uuid,
file_name: t.file_name || '',
summary: vlmDesc || t.text_content || t.vlm_description || 'Person appearance',
text_content: textContent,
similarity: t.score ?? null,
start_frame: t.start_frame,
end_frame: t.end_frame,
source_type: 'trace',
trace_id: t.trace_id,
bilingual,
vlm_description_en: t.vlm_description_en,
vlm_description_lang: t.vlm_description_lang,
search_query: q
}
})
newResults = [...sceneResults, ...traceResults].slice(0, maxResults) newResults = [...sceneResults, ...traceResults].slice(0, maxResults)
} else if (currentMode === 'people') { } else if (currentMode === 'people') {