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

Function selectRelevantPassages

src/tools/web/extract-select.ts:79–124  ·  view source on GitHub ↗
(text: string, opts: { query?: string; budget: number })

Source from the content-addressed store, hash-verified

77 * query, fall back to the head+tail window. PURE.
78 */
79export function selectRelevantPassages(text: string, opts: { query?: string; budget: number }): ExtractSelection {
80 const fullLength = text.length;
81 const budget = Math.max(200, opts.budget);
82 if (fullLength <= budget) return { content: text, mode: 'whole', fullLength, keptChars: fullLength, omittedChars: 0 };
83
84 const query = (opts.query ?? '').trim();
85 const passages = splitPassages(text);
86 const qv = query ? termFreq(semanticTokens(query)) : null;
87 if (!qv || qv.size === 0 || passages.length <= 1) {
88 const ht = headTailWindow(text, budget);
89 return { content: ht.content, mode: 'head-tail', fullLength, keptChars: ht.keptChars, omittedChars: fullLength - ht.keptChars };
90 }
91
92 // Score every passage; always keep passage 0 (title/lede) as context anchor.
93 const scored = passages.map((p, i) => ({ i, p, score: cosineSim(qv, termFreq(semanticTokens(p.text))) }));
94 const chosen = new Set<number>([0]);
95 let used = passages[0]!.text.length;
96 for (const s of scored.filter(s => s.i !== 0 && s.score > 0).sort((a, b) => b.score - a.score)) {
97 if (used + s.p.text.length + 2 > budget) continue; // skip; a smaller relevant one may still fit
98 chosen.add(s.i); used += s.p.text.length + 2;
99 }
100 // If the query matched nothing beyond the anchor, positional is the safer bet.
101 if (chosen.size === 1 && passages.length > 1) {
102 const ht = headTailWindow(text, budget);
103 return { content: ht.content, mode: 'head-tail', fullLength, keptChars: ht.keptChars, omittedChars: fullLength - ht.keptChars };
104 }
105
106 // Emit chosen passages in document order, marking gaps between non-adjacent selections.
107 const order = [...chosen].sort((a, b) => a - b);
108 const chunks: string[] = [];
109 let kept = 0; let prev = -1;
110 for (const idx of order) {
111 if (prev >= 0 && idx > prev + 1) {
112 const gap = passages.slice(prev + 1, idx).reduce((a, p) => a + p.text.length, 0);
113 chunks.push(`[… ${gap} chars omitted (less relevant) …]`);
114 }
115 chunks.push(passages[idx]!.text);
116 kept += passages[idx]!.text.length;
117 prev = idx;
118 }
119 if (prev < passages.length - 1) {
120 const gap = passages.slice(prev + 1).reduce((a, p) => a + p.text.length, 0);
121 chunks.push(`[… ${gap} chars omitted (less relevant) …]`);
122 }
123 return { content: chunks.join('\n\n'), mode: 'semantic', fullLength, keptChars: kept, omittedChars: fullLength - kept };
124}

Callers 3

executeMethod · 0.85
capMarkdownFunction · 0.85

Calls 7

splitPassagesFunction · 0.85
termFreqFunction · 0.85
semanticTokensFunction · 0.85
headTailWindowFunction · 0.85
addMethod · 0.80
cosineSimFunction · 0.50
pushMethod · 0.45

Tested by

no test coverage detected