MCPcopy Create free account
hub / github.com/QodeXcli/QodeX / retrieveRelevantFiles

Function retrieveRelevantFiles

src/context/retrieval.ts:434–530  ·  view source on GitHub ↗
(
  cwd: string,
  query: string,
  opts: RetrieveOptions = {},
)

Source from the content-addressed store, hash-verified

432 * MUST treat null as "skip silently", never as an error.
433 */
434export async function retrieveRelevantFiles(
435 cwd: string,
436 query: string,
437 opts: RetrieveOptions = {},
438): Promise<RankedFile[] | null> {
439 const baseUrl = opts.baseUrl ?? process.env.OLLAMA_BASE_URL ?? 'http://localhost:11434';
440 const model = opts.embeddingModel ?? 'nomic-embed-text';
441 const topKChunks = opts.topKChunks ?? 24;
442 const maxFiles = opts.maxFiles ?? 6;
443
444 try {
445 if (!(await ollamaReachable(baseUrl, opts.signal))) return null;
446
447 const idxPath = indexPath(cwd, model);
448 let idx = await loadIndex(idxPath);
449
450 if (!idx) {
451 if (!opts.buildIfMissing) {
452 // No JSON index — but a SQLite index might still exist. Probe it.
453 try {
454 const { openSqliteIndex } = await import('./sqlite-index.js');
455 const probe = await openSqliteIndex(cwd, model);
456 if (probe) { probe.db.close(); }
457 else return null;
458 } catch { return null; }
459 } else {
460 idx = await buildIndex(cwd, model, baseUrl, opts.maxFilesToIndex ?? 1500, opts.signal, () => {});
461 await persistIndex(cwd, model, idx);
462 }
463 }
464
465 const [queryEmbedding] = await ollamaEmbed(baseUrl, model, [query], opts.signal);
466 if (!queryEmbedding) return null;
467
468 // Hybrid (BM25 + semantic) ranking over the persisted index (SQLite preferred).
469 const scored = await searchPersisted(cwd, model, query, queryEmbedding, topKChunks, idx);
470 if (scored.length === 0) return null;
471
472 // Stage-2 cross-encoder rerank (optional). The bi-encoder gives us a fast,
473 // wide candidate set; the cross-encoder re-scores query+doc jointly for true
474 // relevance, then we narrow to maxFiles. If no reranker is reachable this is
475 // a no-op and we keep the bi-encoder order.
476 let ranked: RankedFile[];
477 if (opts.rerank) {
478 const candPool = opts.rerankCandidates ?? 40;
479 const wideFiles = aggregateToFiles(scored, candPool); // wide net first
480 try {
481 const { crossEncoderRerank } = await import('./reranker.js');
482 const reranked = await crossEncoderRerank(
483 query,
484 wideFiles.map(f => ({
485 id: f.file,
486 // Feed the cross-encoder the REAL code (prefixed with the path for light
487 // context). Falling back to the filename+line string only when a candidate
488 // has no chunk text — otherwise the reranker scores on path tokens alone,
489 // which is what made its scores degenerate.
490 text: f.text && f.text.trim()
491 ? `${f.file}\n${f.text}`

Callers 3

retrieval.test.tsFile · 0.85
hybridSearchToolFunction · 0.85
buildInitialMessagesMethod · 0.85

Calls 13

ollamaReachableFunction · 0.85
indexPathFunction · 0.85
loadIndexFunction · 0.85
openSqliteIndexFunction · 0.85
persistIndexFunction · 0.85
ollamaEmbedFunction · 0.85
searchPersistedFunction · 0.85
aggregateToFilesFunction · 0.85
crossEncoderRerankFunction · 0.85
expandRankedViaGraphFunction · 0.85
debugMethod · 0.80
buildIndexFunction · 0.70

Tested by

no test coverage detected