diff --git a/src/views/SearchView.vue b/src/views/SearchView.vue index 38aa339..537b7c3 100644 --- a/src/views/SearchView.vue +++ b/src/views/SearchView.vue @@ -1236,25 +1236,33 @@ async function loadMore() { const q = lastUserMsg.query || '' const currentMode = lastUserMsg.mode || mode.value + // Build set of existing result IDs for deduplication + const existingIds = new Set() + for (const r of lastMsg.results) { + const id = `${r.file_uuid}:${r.start_frame || 0}:${r.source_type || 'unknown'}` + existingIds.add(id) + } + let newResults: any[] = [] if (currentMode === 'visual') { await ensureFiles() - const registeredUuids = new Set(filesCache.value.map((f: any) => f.file_uuid || f.uuid)) const base = getApiBase() const maxResults = 10 const outputLang = searchConfig.value.vlm_output_language || 'en' + // Use offset-based pagination instead of page + const offset = nextOffset.value 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: Math.floor(nextOffset.value / 10) + 1, page_size: maxResults, min_score: 0.0, output_language: outputLang }) + body: JSON.stringify({ query: q, offset: offset, 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: Math.floor(nextOffset.value / 10) + 1, page_size: maxResults, min_score: 0.0, output_language: outputLang }) + body: JSON.stringify({ query: q, offset: offset, page_size: maxResults, min_score: 0.0, output_language: outputLang }) }).then(r => r.json()).catch(() => ({})) ]) @@ -1288,7 +1296,7 @@ async function loadMore() { newResults = [...sceneResults, ...traceResults].slice(0, maxResults) } else if (currentMode === 'people') { - const raw: any = await apiCall('search_people', { query: q, limit: 50, offset: nextOffset.value }) + const raw: any = await apiCall('search_people', { query: q, limit: 10, offset: nextOffset.value }) const peopleList = raw?.people || [] const personMap = new Map() @@ -1325,22 +1333,36 @@ async function loadMore() { } }).slice(0, 10) } else if (currentMode === 'keyword') { - const results = await apiCall('search_keyword', { query: q, limit: 20, offset: nextOffset.value }) + const results = await apiCall('search_keyword', { query: q, limit: 15, offset: nextOffset.value }) const rawResults = (results?.results || results || []) as any[] const seen = new Set() newResults = rawResults.filter(r => { const key = `${r.file_uuid}:${r.start_frame || 0}` - if (seen.has(key)) return false + const globalKey = `${r.file_uuid}:${r.start_frame || 0}:${r.source_type || 'unknown'}` + if (seen.has(key) || existingIds.has(globalKey)) return false seen.add(key) return true }).map(r => ({ ...r, search_query: q })).slice(0, 10) } else if (currentMode === 'semantic') { - const results = await apiCall('search_semantic', { query: q, limit: 20, offset: nextOffset.value }) + const results = await apiCall('search_semantic', { query: q, limit: 15, offset: nextOffset.value }) const rawResults = (results?.results || results || []) as any[] const seen = new Set() newResults = rawResults.filter(r => { const key = `${r.file_uuid}:${r.start_frame || 0}` - if (seen.has(key)) return false + const globalKey = `${r.file_uuid}:${r.start_frame || 0}:${r.source_type || 'unknown'}` + if (seen.has(key) || existingIds.has(globalKey)) return false + seen.add(key) + return true + }).map(r => ({ ...r, search_query: q })).slice(0, 10) + } else if (currentMode === 'agent') { + // Agent mode: fallback to semantic search for more results + const results = await apiCall('search_semantic', { query: q, limit: 15, offset: nextOffset.value }) + const rawResults = (results?.results || results || []) as any[] + const seen = new Set() + newResults = rawResults.filter(r => { + const key = `${r.file_uuid}:${r.start_frame || 0}` + const globalKey = `${r.file_uuid}:${r.start_frame || 0}:${r.source_type || 'unknown'}` + if (seen.has(key) || existingIds.has(globalKey)) return false seen.add(key) return true }).map(r => ({ ...r, search_query: q })).slice(0, 10)