Skip to content

Commit 7cbb526

Browse files
committed
feat(api): increase search depth for notebook-scoped chat queries
Notebook @mentions in normal chat used limit=3/collection and cap=8 results, while dedicated notebook chats use limit=30 and cap=40. This boosts notebook- scoped searches to limit=10, cap=20, reranker 20→12, and 8000 char context budget — all gated on notebookCollectionIds so general chat is unaffected.
1 parent e298147 commit 7cbb526

3 files changed

Lines changed: 24 additions & 8 deletions

File tree

apps/api/agents/langgraph/ChatGraph/nodes/rerankNode.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,18 @@ export async function rerankNode(state: ChatGraphState): Promise<Partial<ChatGra
5252
const startTime = Date.now();
5353
const { searchResults, searchQuery, aiWorkerPool, hasTemporal } = state;
5454

55+
// Notebook-scoped searches get higher limits for deeper recall
56+
const isNotebookScoped = (state.notebookCollectionIds?.length ?? 0) > 0;
57+
const inputLimit = isNotebookScoped ? 20 : RERANK_INPUT_LIMIT;
58+
const outputLimit = isNotebookScoped ? 12 : RERANK_OUTPUT_LIMIT;
59+
5560
// Skip reranking if too few results
5661
if (searchResults.length <= 3) {
5762
log.info(`[Rerank] Skipping — only ${searchResults.length} results`);
5863
return { rerankTimeMs: Date.now() - startTime };
5964
}
6065

61-
const candidates = searchResults.slice(0, RERANK_INPUT_LIMIT);
66+
const candidates = searchResults.slice(0, inputLimit);
6267

6368
log.info(
6469
`[Rerank] Reranking ${candidates.length} results for query: "${searchQuery?.slice(0, 50)}..."`
@@ -115,7 +120,7 @@ ${passageList}`;
115120
// B3: Apply MMR diversity reranking as second pass
116121
// Keep top 2 results unchanged, apply diversity for positions 3+
117122
const diverse = filtered.length > 3 ? applyMMR(filtered, 0.7, 2) : filtered;
118-
const reranked = diverse.slice(0, RERANK_OUTPUT_LIMIT);
123+
const reranked = diverse.slice(0, outputLimit);
119124

120125
log.info(
121126
`[Rerank] Complete: ${candidates.length}${reranked.length} results (diversity applied) in ${rerankTimeMs}ms`

apps/api/agents/langgraph/ChatGraph/nodes/respondNode.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ async function formatSearchContext(state: ChatGraphState): Promise<string> {
203203
}
204204

205205
// Default: budget-based truncation
206-
const topResults = state.searchResults.slice(0, MAX_SEARCH_RESULTS);
206+
// Notebook-scoped searches get more results and higher budget for deeper answers
207+
const isNotebookScoped = (state.notebookCollectionIds?.length ?? 0) > 0;
208+
const maxResults = isNotebookScoped ? 12 : MAX_SEARCH_RESULTS;
209+
const topResults = state.searchResults.slice(0, maxResults);
207210

208211
// Document chat gets the highest budget for focused Q&A
209212
const isDocumentChat = state.documentChatIds?.length > 0;
@@ -213,9 +216,11 @@ async function formatSearchContext(state: ChatGraphState): Promise<string> {
213216
const isMultiSource = (state.searchSources?.length || 0) > 1;
214217
const budget = isDocumentChat
215218
? SEARCH_CONTEXT_BUDGET_DOCUMENTCHAT
216-
: hasCrawledContent || isMultiSource
217-
? SEARCH_CONTEXT_BUDGET_CRAWLED
218-
: SEARCH_CONTEXT_BUDGET;
219+
: isNotebookScoped
220+
? SEARCH_CONTEXT_BUDGET_DOCUMENTCHAT
221+
: hasCrawledContent || isMultiSource
222+
? SEARCH_CONTEXT_BUDGET_CRAWLED
223+
: SEARCH_CONTEXT_BUDGET;
219224

220225
// Crawled results get 2x weight in budget allocation
221226
const weightedRelevance = topResults.map((r) => {

apps/api/agents/langgraph/ChatGraph/nodes/searchNode.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,18 +480,22 @@ export async function searchNode(state: ChatGraphState): Promise<Partial<ChatGra
480480
}
481481
// Deduplicate in case of overlap
482482
const uniqueCollections = [...new Set(collectionsToSearch)];
483+
const isNotebookScoped =
484+
state.notebookCollectionIds && state.notebookCollectionIds.length > 0;
483485

484486
const query = searchQuery || '';
485487

486488
// Search all sub-queries (if decomposed) across all collections
489+
// Notebook-scoped searches get deeper recall (10 vs 3 per collection)
487490
const subQueries = state.subQueries?.length ? state.subQueries : [query];
491+
const perCollectionLimit = isNotebookScoped ? 10 : 3;
488492

489493
const searchPromises = uniqueCollections.flatMap((collection) =>
490494
subQueries.map((sq) =>
491495
executeDirectSearch({
492496
query: sq,
493497
collection,
494-
limit: 3,
498+
limit: perCollectionLimit,
495499
filters: detectedFilters || undefined,
496500
}).catch((err: any) => {
497501
log.warn(
@@ -531,8 +535,10 @@ export async function searchNode(state: ChatGraphState): Promise<Partial<ChatGra
531535
}
532536

533537
// Sort by relevance and take top results
538+
// Notebook-scoped searches keep more candidates for reranking
534539
allResults.sort((a, b) => (b.relevance || 0) - (a.relevance || 0));
535-
results = allResults.slice(0, 8);
540+
const resultsCap = isNotebookScoped ? 20 : 8;
541+
results = allResults.slice(0, resultsCap);
536542
citations = buildCitations(results);
537543

538544
// Track which collections were searched for observability

0 commit comments

Comments
 (0)