Skip to content

Commit e7d8f7d

Browse files
committed
fix(memory): run memory+supplement searches in parallel for corpus=all (fixes #92633)
When corpus=all, the memory search and supplement (wiki) search ran sequentially. If each search consumed most of the 15s deadline, the total exceeded the timeout even though each individual corpus succeeded. Convert both searches to run as parallel promises: memory search results are awaited first (for early-return on paused index identity), then supplement results are collected. This halves the wall-clock time for corpus=all queries where both searches are needed.
1 parent c9f0bfd commit e7d8f7d

1 file changed

Lines changed: 30 additions & 20 deletions

File tree

extensions/memory-core/src/tools.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -447,8 +447,10 @@ export function createMemorySearchTool(options: {
447447
hits: number;
448448
}
449449
| undefined;
450-
if (shouldQueryMemory && memory && !("error" in memory)) {
451-
await runUnavailablePhase("memory", async () => {
450+
// Run memory and supplement searches in parallel for corpus=all
451+
// to avoid sequential timeout when each search uses most of the deadline.
452+
const memorySearchPromise = (shouldQueryMemory && memory && !("error" in memory))
453+
? runUnavailablePhase("memory", async () => {
452454
let activeMemory = memory;
453455
const runtimeDebug: MemorySearchRuntimeDebug[] = [];
454456
const qmdSearchModeOverride = resolveActiveMemoryQmdSearchModeOverride(
@@ -549,25 +551,33 @@ export function createMemorySearchTool(options: {
549551
searchMs: Math.max(0, Date.now() - searchStartedAt),
550552
hits: rawResults.length,
551553
};
552-
});
553-
if (pausedIndexIdentityReason) {
554-
return jsonResult(
555-
buildPausedMemoryIndexUnavailableResult(pausedIndexIdentityReason),
556-
);
557-
}
554+
})
555+
: Promise.resolve(null);
556+
// Run supplement search without runUnavailablePhase to avoid
557+
// overwriting activeUnavailablePhase from the parallel memory search.
558+
const supplementSearchPromise = shouldQuerySupplements
559+
? searchMemoryCorpusSupplements({
560+
query,
561+
maxResults,
562+
agentSessionKey: options.agentSessionKey,
563+
corpus: requestedCorpus,
564+
}).catch((error: unknown) => {
565+
// Only set failedUnavailablePhase if memory didn't already fail.
566+
if (!failedUnavailablePhase) {
567+
failedUnavailablePhase = "supplement";
568+
}
569+
throw error;
570+
})
571+
: Promise.resolve([]);
572+
// Start supplement search eagerly, then await memory search.
573+
const supplementSearchHandle = supplementSearchPromise;
574+
await memorySearchPromise;
575+
if (pausedIndexIdentityReason) {
576+
return jsonResult(
577+
buildPausedMemoryIndexUnavailableResult(pausedIndexIdentityReason),
578+
);
558579
}
559-
const supplementResults = shouldQuerySupplements
560-
? await runUnavailablePhase(
561-
"supplement",
562-
async () =>
563-
await searchMemoryCorpusSupplements({
564-
query,
565-
maxResults,
566-
agentSessionKey: options.agentSessionKey,
567-
corpus: requestedCorpus,
568-
}),
569-
)
570-
: [];
580+
const supplementResults = await supplementSearchHandle;
571581
// Wiki and memory scores use incomparable scales, so corpus=all first
572582
// balances candidate selection and then backfills any unused slots.
573583
const effectiveMax = Math.max(1, maxResults ?? 10);

0 commit comments

Comments
 (0)