Skip to content

Commit 1ea4c24

Browse files
committed
fix(knowledge): keep the reach memo at five minutes and treat a spent leg during the count as short
A stale strategy costs recall at the margin, so the memo stays at five minutes now that a count costs at most the probe's share. A leg whose own deadline passes during the count reports short, as it did before, instead of failing the search.
1 parent fd418a3 commit 1ea4c24

2 files changed

Lines changed: 43 additions & 20 deletions

File tree

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,20 @@ describe('permitted-document planner', () => {
20512051
expect(reachCounts()).toHaveLength(2)
20522052
})
20532053

2054+
it('reports a leg whose own deadline passed during the count as short, not failed', async () => {
2055+
const budget = new SearchBudget('vector', performance.now() - 1)
2056+
await expect(
2057+
resolveReach(['org-index'], scope('spent-leg'), budget, {
2058+
connectors: { workspace: [], admin: [], members: [], liveProofRequired: [] },
2059+
observers: { confirmed: [], observed: [] },
2060+
memberSources: [],
2061+
connectorTypes: new Map(),
2062+
uploads: true,
2063+
})
2064+
).resolves.toEqual({ kind: 'unbounded', broad: true })
2065+
expect(budget.timedOut).toBe(true)
2066+
})
2067+
20542068
it('counts a resolved reach against a small index instead of assuming it broad', async () => {
20552069
/** A bound inside the probe limit proves nothing without a saturated probe. */
20562070
dbChainMockFns.execute.mockImplementation(async (query) => {

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

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,11 +1180,9 @@ export const BROAD_REACH_SHARE = 0.25
11801180
/**
11811181
* How long a caller's saturated reach is remembered. Reach counts the documents a caller's tokens
11821182
* touch in the bases, which moves slowly, and an unbounded set only means the legs search the
1183-
* index with the full access predicate, so a stale answer costs speed, never access. Counting it
1184-
* is the one read of a search that scales with the caller's reach rather than the query, so it is
1185-
* remembered for long.
1183+
* index with the full access predicate, so a stale answer costs speed, never access.
11861184
*/
1187-
const SATURATED_REACH_TTL_MS = 60 * 60 * 1000
1185+
const SATURATED_REACH_TTL_MS = 5 * 60 * 1000
11881186

11891187
/** A saturated reach, and whether it is broad enough to walk the whole graph for. */
11901188
const saturatedReach = new LRUCache<string, { broad: boolean }>({
@@ -1346,11 +1344,17 @@ export async function resolveReach(
13461344
const key = reachKey(knowledgeBaseIds, access, plan)
13471345
const remembered = key ? saturatedReach.get(key) : undefined
13481346
if (remembered) return { kind: 'unbounded', broad: remembered.broad }
1349-
const broad = await reachIsBroad(knowledgeBaseIds, access, budget, plan, false)
1350-
/** A count that ran out of time decides this search only; the next one counts again. */
1351-
if (broad === null) return { kind: 'unbounded', broad: true }
1352-
if (key) saturatedReach.set(key, { broad })
1353-
return { kind: 'unbounded', broad }
1347+
try {
1348+
const broad = await reachIsBroad(knowledgeBaseIds, access, budget, plan, false)
1349+
/** A count that ran out of time decides this search only; the next one counts again. */
1350+
if (broad === null) return { kind: 'unbounded', broad: true }
1351+
if (key) saturatedReach.set(key, { broad })
1352+
return { kind: 'unbounded', broad }
1353+
} catch (error) {
1354+
/** The leg's own deadline passed during the count: the leg is short, the search is not failed. */
1355+
if (!budget?.isTimeout(error)) throw error
1356+
return { kind: 'unbounded', broad: true }
1357+
}
13541358
}
13551359

13561360
/**
@@ -1403,17 +1407,22 @@ export async function resolvePermittedDocuments(params: {
14031407
probe = { kind: 'timed_out' }
14041408
}
14051409
if (probe.kind === 'saturated') {
1406-
const counted = await reachIsBroad(
1407-
params.knowledgeBaseIds,
1408-
params.access,
1409-
params.budget,
1410-
params.accessPlan,
1411-
true
1412-
)
1413-
/** A count that ran out of time decides this search only; the next one counts again. */
1414-
if (counted !== null) {
1415-
broad = counted
1416-
if (key) saturatedReach.set(key, { broad })
1410+
try {
1411+
const counted = await reachIsBroad(
1412+
params.knowledgeBaseIds,
1413+
params.access,
1414+
params.budget,
1415+
params.accessPlan,
1416+
true
1417+
)
1418+
/** A count that ran out of time decides this search only; the next one counts again. */
1419+
if (counted !== null) {
1420+
broad = counted
1421+
if (key) saturatedReach.set(key, { broad })
1422+
}
1423+
} catch (error) {
1424+
/** The leg's own deadline passed during the count: the leg is short, the search is not failed. */
1425+
if (!params.budget?.isTimeout(error)) throw error
14171426
}
14181427
}
14191428
}

0 commit comments

Comments
 (0)