Skip to content

Commit 4f82d22

Browse files
committed
fix(knowledge): hand only a large bounded set's first page to the GIN ranking
Tin and GIN order candidates differently, so an offset advanced through one ranking cannot resume the other. A large bounded set's first page that Tin's widest window cannot fill goes to GIN; a later page stays with Tin and is left short as a narrow reader's is.
1 parent ca55d8c commit 4f82d22

2 files changed

Lines changed: 29 additions & 3 deletions

File tree

‎apps/sim/lib/knowledge/search/queries.test.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,28 @@ describe('permitted-document planner', () => {
18451845
expect(JSON.stringify(ginStatements()[0])).not.toContain('doc-4999')
18461846
})
18471847

1848+
it('leaves a later page short rather than resuming a different ranking at its offset', async () => {
1849+
/** The first page fills from Tin; hydration keeps half, so a second page is asked for. */
1850+
const first = Array.from({ length: 40 }, (_, index) => hit(`t-${index}`, 'src-a'))
1851+
tinPages = [
1852+
{ ranked: 2000, candidates: first },
1853+
{ ranked: 2000, candidates: [] },
1854+
{ ranked: 20_000, candidates: [] },
1855+
]
1856+
queueTableRows(
1857+
schemaMock.embedding,
1858+
first.slice(0, 20).map((row) => ({ ...row, content: 'release notes' }))
1859+
)
1860+
const results = await keyword({
1861+
topK: 40,
1862+
permitted: { kind: 'bounded', documents: large },
1863+
accessPlan,
1864+
})
1865+
expect(results).toHaveLength(20)
1866+
expect(tinStatements()).toHaveLength(3)
1867+
expect(ginStatements()).toHaveLength(0)
1868+
})
1869+
18481870
it('keeps the bounded read for a set under the size', async () => {
18491871
mockResolveTinKeywordQuery.mockResolvedValue(null)
18501872
await keyword({ permitted: { kind: 'bounded', documents: large.slice(0, -1) }, accessPlan })

‎apps/sim/lib/knowledge/search/queries.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,8 +2155,10 @@ export async function executeKeywordSearch(params: KeywordSearchParams): Promise
21552155
* A resolved scope decides readability on the ranked row. The windows widen while the page
21562156
* is short, a narrow reader's to a wide one sooner and no further, and what the widest
21572157
* cannot fill is left short rather than handed to a ranking over every match. A large
2158-
* bounded set is the exception: its bounded read was exhaustive, so the widest window that
2159-
* still falls short hands the page to the GIN ranking, which covers every match.
2158+
* bounded set is the exception on its first page: its bounded read was exhaustive, so the
2159+
* widest window that still falls short hands that page to the GIN ranking, which covers
2160+
* every match. A later page stays with Tin: the two rankers order differently, so an offset
2161+
* advanced through one cannot resume the other.
21602162
*/
21612163
const narrow =
21622164
accessPlan !== undefined &&
@@ -2221,7 +2223,9 @@ export async function executeKeywordSearch(params: KeywordSearchParams): Promise
22212223
if (
22222224
page.candidates.length >= limit ||
22232225
page.ranked < window ||
2224-
(accessPlan !== undefined && !largePermittedSet && window === windows[windows.length - 1])
2226+
(accessPlan !== undefined &&
2227+
!(largePermittedSet && offset === 0) &&
2228+
window === windows[windows.length - 1])
22252229
) {
22262230
return { candidates: page.candidates, nextOffset: offset + page.candidates.length }
22272231
}

0 commit comments

Comments
 (0)