webpeel 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +86 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/bm25-filter.d.ts +57 -0
- package/dist/core/bm25-filter.d.ts.map +1 -0
- package/dist/core/bm25-filter.js +249 -0
- package/dist/core/bm25-filter.js.map +1 -0
- package/dist/core/chunking.d.ts +43 -0
- package/dist/core/chunking.d.ts.map +1 -0
- package/dist/core/chunking.js +182 -0
- package/dist/core/chunking.js.map +1 -0
- package/dist/core/content-pruner.d.ts +33 -0
- package/dist/core/content-pruner.d.ts.map +1 -0
- package/dist/core/content-pruner.js +249 -0
- package/dist/core/content-pruner.js.map +1 -0
- package/dist/core/fetcher.d.ts +8 -1
- package/dist/core/fetcher.d.ts.map +1 -1
- package/dist/core/fetcher.js +33 -7
- package/dist/core/fetcher.js.map +1 -1
- package/dist/core/hotel-search.d.ts +2 -0
- package/dist/core/hotel-search.d.ts.map +1 -1
- package/dist/core/hotel-search.js +2 -0
- package/dist/core/hotel-search.js.map +1 -1
- package/dist/core/llm-extract.d.ts +15 -1
- package/dist/core/llm-extract.d.ts.map +1 -1
- package/dist/core/llm-extract.js +127 -7
- package/dist/core/llm-extract.js.map +1 -1
- package/dist/core/markdown.d.ts +4 -1
- package/dist/core/markdown.d.ts.map +1 -1
- package/dist/core/markdown.js +11 -2
- package/dist/core/markdown.js.map +1 -1
- package/dist/core/strategies.d.ts +6 -0
- package/dist/core/strategies.d.ts.map +1 -1
- package/dist/core/strategies.js +10 -5
- package/dist/core/strategies.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +47 -3
- package/dist/mcp/server.js.map +1 -1
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25 Query-Focused Content Filter
|
|
3
|
+
*
|
|
4
|
+
* Filters markdown content by BM25 relevance to a query, keeping only the
|
|
5
|
+
* blocks that are most relevant. This can reduce token usage by 70-90% for
|
|
6
|
+
* focused tasks (e.g., "find hotel prices" should not return navigation menus,
|
|
7
|
+
* footer text, or unrelated article sections).
|
|
8
|
+
*
|
|
9
|
+
* Algorithm: BM25 (Best Matching 25) — Okapi BM25
|
|
10
|
+
* score(D, Q) = Σ IDF(qi) * tf(qi,D)*(k1+1) / (tf(qi,D) + k1*(1 - b + b*|D|/avgdl))
|
|
11
|
+
*/
|
|
12
|
+
export interface BM25FilterOptions {
|
|
13
|
+
/** Query to rank content against */
|
|
14
|
+
query: string;
|
|
15
|
+
/** BM25 threshold score. Blocks below this are removed. Default: auto-calculated */
|
|
16
|
+
threshold?: number;
|
|
17
|
+
/** Whether to return scores in output. Default: false */
|
|
18
|
+
includeScores?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface BM25FilterResult {
|
|
21
|
+
/** Filtered content (relevant paragraphs only) */
|
|
22
|
+
content: string;
|
|
23
|
+
/** Number of blocks kept */
|
|
24
|
+
kept: number;
|
|
25
|
+
/** Total number of blocks */
|
|
26
|
+
total: number;
|
|
27
|
+
/** Percentage of content removed */
|
|
28
|
+
reductionPercent: number;
|
|
29
|
+
}
|
|
30
|
+
interface ContentBlock {
|
|
31
|
+
/** Original markdown text (preserved verbatim in output) */
|
|
32
|
+
raw: string;
|
|
33
|
+
/** Index in the original block list (for order preservation) */
|
|
34
|
+
index: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Split markdown content into logical blocks for scoring:
|
|
38
|
+
* - Code fences (``` ... ```) → single block
|
|
39
|
+
* - Heading + immediately following paragraph → single block
|
|
40
|
+
* - Lists (contiguous lines starting with - / * / + / number.) → single block
|
|
41
|
+
* - Tables → single block
|
|
42
|
+
* - Paragraphs → one block each
|
|
43
|
+
*/
|
|
44
|
+
export declare function splitIntoBlocks(content: string): ContentBlock[];
|
|
45
|
+
/**
|
|
46
|
+
* Calculate BM25 scores for all blocks against a query.
|
|
47
|
+
* Returns array of scores in same order as blocks.
|
|
48
|
+
*/
|
|
49
|
+
export declare function scoreBM25(blocks: ContentBlock[], queryTerms: string[]): number[];
|
|
50
|
+
/**
|
|
51
|
+
* Filter markdown content by BM25 relevance to a query.
|
|
52
|
+
* Splits content into blocks (paragraphs, headings+body, list items),
|
|
53
|
+
* scores each by BM25, and returns only blocks above threshold.
|
|
54
|
+
*/
|
|
55
|
+
export declare function filterByRelevance(content: string, options: BM25FilterOptions): BM25FilterResult;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=bm25-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bm25-filter.d.ts","sourceRoot":"","sources":["../../src/core/bm25-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAiDD,UAAU,YAAY;IACpB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CA2E/D;AAMD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA8ChF;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAqF/F"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BM25 Query-Focused Content Filter
|
|
3
|
+
*
|
|
4
|
+
* Filters markdown content by BM25 relevance to a query, keeping only the
|
|
5
|
+
* blocks that are most relevant. This can reduce token usage by 70-90% for
|
|
6
|
+
* focused tasks (e.g., "find hotel prices" should not return navigation menus,
|
|
7
|
+
* footer text, or unrelated article sections).
|
|
8
|
+
*
|
|
9
|
+
* Algorithm: BM25 (Best Matching 25) — Okapi BM25
|
|
10
|
+
* score(D, Q) = Σ IDF(qi) * tf(qi,D)*(k1+1) / (tf(qi,D) + k1*(1 - b + b*|D|/avgdl))
|
|
11
|
+
*/
|
|
12
|
+
// BM25 tuning parameters
|
|
13
|
+
const K1 = 1.5; // term frequency saturation
|
|
14
|
+
const B = 0.75; // length normalization
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Tokenization
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Tokenize text into lowercase terms, stripping punctuation.
|
|
20
|
+
* Markdown formatting characters are also stripped.
|
|
21
|
+
*/
|
|
22
|
+
function tokenize(text) {
|
|
23
|
+
return text
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
// Strip markdown formatting (bold, italic, code, links, images, headings)
|
|
26
|
+
.replace(/!\[.*?\]\(.*?\)/g, ' ') // images
|
|
27
|
+
.replace(/\[.*?\]\(.*?\)/g, ' ') // links
|
|
28
|
+
.replace(/`{1,3}[^`]*`{1,3}/g, ' ') // inline code
|
|
29
|
+
.replace(/[#*_~`>|\\]/g, ' ') // formatting chars
|
|
30
|
+
.replace(/[^\w\s]/g, ' ') // remaining punctuation
|
|
31
|
+
.split(/\s+/)
|
|
32
|
+
.filter(t => t.length > 0);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Strip markdown formatting from text for scoring purposes.
|
|
36
|
+
* Preserves words but removes symbols.
|
|
37
|
+
*/
|
|
38
|
+
function stripMarkdown(text) {
|
|
39
|
+
return text
|
|
40
|
+
.replace(/```[\s\S]*?```/g, ' ') // fenced code blocks
|
|
41
|
+
.replace(/`[^`]+`/g, ' ') // inline code
|
|
42
|
+
.replace(/!\[.*?\]\(.*?\)/g, ' ') // images
|
|
43
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') // links → text
|
|
44
|
+
.replace(/^#{1,6}\s+/gm, '') // headings
|
|
45
|
+
.replace(/[*_~`>|\\]/g, ' ') // formatting
|
|
46
|
+
.replace(/^\s*[-*+]\s+/gm, ' ') // list bullets
|
|
47
|
+
.replace(/^\s*\d+\.\s+/gm, ' ') // numbered list
|
|
48
|
+
.replace(/\s+/g, ' ')
|
|
49
|
+
.trim();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Split markdown content into logical blocks for scoring:
|
|
53
|
+
* - Code fences (``` ... ```) → single block
|
|
54
|
+
* - Heading + immediately following paragraph → single block
|
|
55
|
+
* - Lists (contiguous lines starting with - / * / + / number.) → single block
|
|
56
|
+
* - Tables → single block
|
|
57
|
+
* - Paragraphs → one block each
|
|
58
|
+
*/
|
|
59
|
+
export function splitIntoBlocks(content) {
|
|
60
|
+
// Normalise line endings
|
|
61
|
+
const text = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
62
|
+
// First, extract fenced code blocks so they aren't broken apart
|
|
63
|
+
// We'll replace them with placeholders, split, then restore.
|
|
64
|
+
const codeBlocks = [];
|
|
65
|
+
const withPlaceholders = text.replace(/```[\s\S]*?```/g, (match) => {
|
|
66
|
+
const id = codeBlocks.length;
|
|
67
|
+
codeBlocks.push(match);
|
|
68
|
+
return `\x00CODE_BLOCK_${id}\x00`;
|
|
69
|
+
});
|
|
70
|
+
// Split on double newlines
|
|
71
|
+
const rawChunks = withPlaceholders.split(/\n{2,}/);
|
|
72
|
+
// Re-join heading with its following paragraph
|
|
73
|
+
const merged = [];
|
|
74
|
+
for (let i = 0; i < rawChunks.length; i++) {
|
|
75
|
+
const chunk = rawChunks[i].trim();
|
|
76
|
+
if (!chunk)
|
|
77
|
+
continue;
|
|
78
|
+
const isHeading = /^#{1,6}\s/.test(chunk);
|
|
79
|
+
const nextChunk = rawChunks[i + 1]?.trim();
|
|
80
|
+
if (isHeading && nextChunk && !/^#{1,6}\s/.test(nextChunk)) {
|
|
81
|
+
// Merge heading + following paragraph
|
|
82
|
+
merged.push(chunk + '\n\n' + nextChunk);
|
|
83
|
+
i++; // skip next
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
merged.push(chunk);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Now merge contiguous list lines that got split
|
|
90
|
+
const regrouped = [];
|
|
91
|
+
for (const chunk of merged) {
|
|
92
|
+
const lines = chunk.split('\n');
|
|
93
|
+
const isListBlock = lines.every(l => l.trim() === '' || /^\s*[-*+]\s/.test(l) || /^\s*\d+\.\s/.test(l) || /^\s*\d+\)\s/.test(l)) && lines.some(l => /^\s*[-*+]\s/.test(l) || /^\s*\d+[.)]\s/.test(l));
|
|
94
|
+
const isTableBlock = lines.some(l => /^\|/.test(l.trim()));
|
|
95
|
+
if (isListBlock || isTableBlock) {
|
|
96
|
+
// Check if previous block was the same type (adjacent lists should merge)
|
|
97
|
+
const prev = regrouped[regrouped.length - 1];
|
|
98
|
+
if (prev) {
|
|
99
|
+
const prevLines = prev.split('\n');
|
|
100
|
+
const prevIsListOrTable = prevLines.some(l => /^\s*[-*+]\s/.test(l) || /^\s*\d+[.)]\s/.test(l) || /^\|/.test(l.trim()));
|
|
101
|
+
if (prevIsListOrTable && isListBlock === prevIsListOrTable) {
|
|
102
|
+
regrouped[regrouped.length - 1] = prev + '\n' + chunk;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
regrouped.push(chunk);
|
|
108
|
+
}
|
|
109
|
+
// Restore code blocks and build final ContentBlock array
|
|
110
|
+
const blocks = [];
|
|
111
|
+
for (let i = 0; i < regrouped.length; i++) {
|
|
112
|
+
let raw = regrouped[i];
|
|
113
|
+
// Restore code block placeholders
|
|
114
|
+
raw = raw.replace(/\x00CODE_BLOCK_(\d+)\x00/g, (_m, idx) => codeBlocks[Number(idx)]);
|
|
115
|
+
if (raw.trim()) {
|
|
116
|
+
blocks.push({ raw: raw.trim(), index: i });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return blocks;
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// BM25 Scoring
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
/**
|
|
125
|
+
* Calculate BM25 scores for all blocks against a query.
|
|
126
|
+
* Returns array of scores in same order as blocks.
|
|
127
|
+
*/
|
|
128
|
+
export function scoreBM25(blocks, queryTerms) {
|
|
129
|
+
if (blocks.length === 0 || queryTerms.length === 0) {
|
|
130
|
+
return blocks.map(() => 0);
|
|
131
|
+
}
|
|
132
|
+
const N = blocks.length;
|
|
133
|
+
// Tokenize each block (strip markdown for scoring)
|
|
134
|
+
const blockTokens = blocks.map(b => tokenize(stripMarkdown(b.raw)));
|
|
135
|
+
const blockLengths = blockTokens.map(t => t.length);
|
|
136
|
+
const avgdl = blockLengths.reduce((s, l) => s + l, 0) / N || 1;
|
|
137
|
+
// Build term frequency maps for each block
|
|
138
|
+
const tfMaps = blockTokens.map(tokens => {
|
|
139
|
+
const tf = new Map();
|
|
140
|
+
for (const t of tokens) {
|
|
141
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
142
|
+
}
|
|
143
|
+
return tf;
|
|
144
|
+
});
|
|
145
|
+
// For each query term, compute IDF and score contribution
|
|
146
|
+
const scores = new Array(N).fill(0);
|
|
147
|
+
for (const term of queryTerms) {
|
|
148
|
+
// n(qi) = number of documents containing the term
|
|
149
|
+
let nqi = 0;
|
|
150
|
+
for (const tf of tfMaps) {
|
|
151
|
+
if (tf.has(term))
|
|
152
|
+
nqi++;
|
|
153
|
+
}
|
|
154
|
+
// IDF(qi) = log((N - n(qi) + 0.5) / (n(qi) + 0.5) + 1)
|
|
155
|
+
const idf = Math.log((N - nqi + 0.5) / (nqi + 0.5) + 1);
|
|
156
|
+
for (let d = 0; d < N; d++) {
|
|
157
|
+
const tf = tfMaps[d].get(term) ?? 0;
|
|
158
|
+
if (tf === 0)
|
|
159
|
+
continue;
|
|
160
|
+
const dl = blockLengths[d];
|
|
161
|
+
// BM25 term score
|
|
162
|
+
const termScore = idf * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * dl / avgdl));
|
|
163
|
+
scores[d] += termScore;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return scores;
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Main filter function
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
/**
|
|
172
|
+
* Filter markdown content by BM25 relevance to a query.
|
|
173
|
+
* Splits content into blocks (paragraphs, headings+body, list items),
|
|
174
|
+
* scores each by BM25, and returns only blocks above threshold.
|
|
175
|
+
*/
|
|
176
|
+
export function filterByRelevance(content, options) {
|
|
177
|
+
const { query, threshold, includeScores = false } = options;
|
|
178
|
+
// Empty query → return full content
|
|
179
|
+
if (!query || !query.trim()) {
|
|
180
|
+
return {
|
|
181
|
+
content,
|
|
182
|
+
kept: 0,
|
|
183
|
+
total: 0,
|
|
184
|
+
reductionPercent: 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const blocks = splitIntoBlocks(content);
|
|
188
|
+
const total = blocks.length;
|
|
189
|
+
if (total === 0) {
|
|
190
|
+
return { content, kept: 0, total: 0, reductionPercent: 0 };
|
|
191
|
+
}
|
|
192
|
+
const queryTerms = tokenize(query);
|
|
193
|
+
if (queryTerms.length === 0) {
|
|
194
|
+
return { content, kept: total, total, reductionPercent: 0 };
|
|
195
|
+
}
|
|
196
|
+
const scores = scoreBM25(blocks, queryTerms);
|
|
197
|
+
// Determine threshold
|
|
198
|
+
let effectiveThreshold;
|
|
199
|
+
if (threshold !== undefined) {
|
|
200
|
+
effectiveThreshold = threshold;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const meanScore = scores.reduce((s, v) => s + v, 0) / scores.length;
|
|
204
|
+
effectiveThreshold = meanScore * 0.5;
|
|
205
|
+
}
|
|
206
|
+
// Select blocks above threshold
|
|
207
|
+
let keptIndices = scores
|
|
208
|
+
.map((score, i) => ({ score, i }))
|
|
209
|
+
.filter(({ score }) => score >= effectiveThreshold)
|
|
210
|
+
.map(({ i }) => i);
|
|
211
|
+
// Fallback: never return empty — keep top 3
|
|
212
|
+
if (keptIndices.length === 0) {
|
|
213
|
+
keptIndices = scores
|
|
214
|
+
.map((score, i) => ({ score, i }))
|
|
215
|
+
.sort((a, b) => b.score - a.score)
|
|
216
|
+
.slice(0, 3)
|
|
217
|
+
.map(({ i }) => i)
|
|
218
|
+
.sort((a, b) => a - b); // restore document order
|
|
219
|
+
}
|
|
220
|
+
// Preserve original document order
|
|
221
|
+
keptIndices.sort((a, b) => a - b);
|
|
222
|
+
const keptBlocks = keptIndices.map(i => blocks[i]);
|
|
223
|
+
const kept = keptBlocks.length;
|
|
224
|
+
// Build output content
|
|
225
|
+
let outputParts;
|
|
226
|
+
if (includeScores) {
|
|
227
|
+
outputParts = keptBlocks.map(b => {
|
|
228
|
+
const score = scores[b.index];
|
|
229
|
+
return `<!-- BM25: ${score.toFixed(4)} -->\n${b.raw}`;
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
outputParts = keptBlocks.map(b => b.raw);
|
|
234
|
+
}
|
|
235
|
+
const filteredContent = outputParts.join('\n\n');
|
|
236
|
+
// Calculate reduction percent based on character count
|
|
237
|
+
const originalLen = content.length;
|
|
238
|
+
const filteredLen = filteredContent.length;
|
|
239
|
+
const reductionPercent = originalLen > 0
|
|
240
|
+
? Math.round(((originalLen - filteredLen) / originalLen) * 100)
|
|
241
|
+
: 0;
|
|
242
|
+
return {
|
|
243
|
+
content: filteredContent,
|
|
244
|
+
kept,
|
|
245
|
+
total,
|
|
246
|
+
reductionPercent,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=bm25-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bm25-filter.js","sourceRoot":"","sources":["../../src/core/bm25-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart content chunking for LLM processing.
|
|
3
|
+
*
|
|
4
|
+
* Splits content into manageable pieces with configurable overlap and strategy.
|
|
5
|
+
* Zero external dependencies; target <1ms for typical page content.
|
|
6
|
+
*/
|
|
7
|
+
export interface ChunkOptions {
|
|
8
|
+
/** Target tokens per chunk. Default: 4000 */
|
|
9
|
+
chunkSize?: number;
|
|
10
|
+
/** Overlap tokens between chunks. Default: 200 */
|
|
11
|
+
overlap?: number;
|
|
12
|
+
/** Chunking strategy. Default: 'semantic' */
|
|
13
|
+
strategy?: 'fixed' | 'semantic' | 'paragraph';
|
|
14
|
+
}
|
|
15
|
+
export interface Chunk {
|
|
16
|
+
/** Chunk index (0-based) */
|
|
17
|
+
index: number;
|
|
18
|
+
/** Chunk content */
|
|
19
|
+
content: string;
|
|
20
|
+
/** Estimated tokens in this chunk */
|
|
21
|
+
tokens: number;
|
|
22
|
+
/** Character offset in original content */
|
|
23
|
+
startOffset: number;
|
|
24
|
+
/** Whether this is the last chunk */
|
|
25
|
+
isLast: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface ChunkResult {
|
|
28
|
+
/** Array of content chunks */
|
|
29
|
+
chunks: Chunk[];
|
|
30
|
+
/** Total chunks */
|
|
31
|
+
totalChunks: number;
|
|
32
|
+
/** Total tokens across all chunks */
|
|
33
|
+
totalTokens: number;
|
|
34
|
+
/** Original content tokens */
|
|
35
|
+
originalTokens: number;
|
|
36
|
+
}
|
|
37
|
+
/** Estimate token count using chars/4 heuristic. Accurate within ±10%. */
|
|
38
|
+
export declare function estimateTokens(text: string): number;
|
|
39
|
+
/**
|
|
40
|
+
* Split content into chunks suitable for LLM processing.
|
|
41
|
+
*/
|
|
42
|
+
export declare function chunkContent(content: string, options?: ChunkOptions): ChunkResult;
|
|
43
|
+
//# sourceMappingURL=chunking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../../src/core/chunking.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;CAC/C;AAED,MAAM,WAAW,KAAK;IACpB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,CA0CjF"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart content chunking for LLM processing.
|
|
3
|
+
*
|
|
4
|
+
* Splits content into manageable pieces with configurable overlap and strategy.
|
|
5
|
+
* Zero external dependencies; target <1ms for typical page content.
|
|
6
|
+
*/
|
|
7
|
+
/** Estimate token count using chars/4 heuristic. Accurate within ±10%. */
|
|
8
|
+
export function estimateTokens(text) {
|
|
9
|
+
return Math.ceil(text.length / 4);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Split content into chunks suitable for LLM processing.
|
|
13
|
+
*/
|
|
14
|
+
export function chunkContent(content, options) {
|
|
15
|
+
const chunkSize = options?.chunkSize ?? 4000;
|
|
16
|
+
const overlap = options?.overlap ?? 200;
|
|
17
|
+
const strategy = options?.strategy ?? 'semantic';
|
|
18
|
+
const originalTokens = estimateTokens(content);
|
|
19
|
+
if (content.length === 0) {
|
|
20
|
+
return { chunks: [], totalChunks: 0, totalTokens: 0, originalTokens: 0 };
|
|
21
|
+
}
|
|
22
|
+
let rawChunks;
|
|
23
|
+
switch (strategy) {
|
|
24
|
+
case 'fixed':
|
|
25
|
+
rawChunks = chunkFixed(content, chunkSize, overlap);
|
|
26
|
+
break;
|
|
27
|
+
case 'paragraph':
|
|
28
|
+
rawChunks = chunkParagraph(content, chunkSize);
|
|
29
|
+
break;
|
|
30
|
+
case 'semantic':
|
|
31
|
+
default:
|
|
32
|
+
rawChunks = chunkSemantic(content, chunkSize, overlap);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
const chunks = rawChunks.map((raw, i) => ({
|
|
36
|
+
index: i,
|
|
37
|
+
content: raw.content,
|
|
38
|
+
tokens: estimateTokens(raw.content),
|
|
39
|
+
startOffset: raw.startOffset,
|
|
40
|
+
isLast: i === rawChunks.length - 1,
|
|
41
|
+
}));
|
|
42
|
+
const totalTokens = chunks.reduce((sum, c) => sum + c.tokens, 0);
|
|
43
|
+
return {
|
|
44
|
+
chunks,
|
|
45
|
+
totalChunks: chunks.length,
|
|
46
|
+
totalTokens,
|
|
47
|
+
originalTokens,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Fixed strategy
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
function chunkFixed(content, chunkSize, overlap) {
|
|
54
|
+
const chunkChars = chunkSize * 4;
|
|
55
|
+
const overlapChars = overlap * 4;
|
|
56
|
+
const result = [];
|
|
57
|
+
let start = 0;
|
|
58
|
+
while (start < content.length) {
|
|
59
|
+
const end = Math.min(start + chunkChars, content.length);
|
|
60
|
+
result.push({ content: content.slice(start, end), startOffset: start });
|
|
61
|
+
if (end >= content.length)
|
|
62
|
+
break;
|
|
63
|
+
start = end - overlapChars;
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Semantic strategy
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
function findNaturalBreak(content, target, tolerance) {
|
|
71
|
+
const min = target - tolerance;
|
|
72
|
+
const max = Math.min(target + tolerance, content.length);
|
|
73
|
+
// 1. Heading break: newline immediately before ##
|
|
74
|
+
const headingRe = /\n(?=#{1,6} )/g;
|
|
75
|
+
let best = null;
|
|
76
|
+
headingRe.lastIndex = min;
|
|
77
|
+
let m = headingRe.exec(content);
|
|
78
|
+
while (m && m.index <= max) {
|
|
79
|
+
const dist = Math.abs(m.index - target);
|
|
80
|
+
if (!best || best.priority < 4 || dist < Math.abs(best.pos - target)) {
|
|
81
|
+
best = { pos: m.index, priority: 4 };
|
|
82
|
+
}
|
|
83
|
+
m = headingRe.exec(content);
|
|
84
|
+
}
|
|
85
|
+
// 2. Paragraph break: \n\n
|
|
86
|
+
const paraRe = /\n\n/g;
|
|
87
|
+
paraRe.lastIndex = min;
|
|
88
|
+
m = paraRe.exec(content);
|
|
89
|
+
while (m && m.index <= max) {
|
|
90
|
+
const dist = Math.abs(m.index - target);
|
|
91
|
+
if (!best || best.priority < 3 || (best.priority === 3 && dist < Math.abs(best.pos - target))) {
|
|
92
|
+
best = { pos: m.index + 2, priority: 3 };
|
|
93
|
+
}
|
|
94
|
+
m = paraRe.exec(content);
|
|
95
|
+
}
|
|
96
|
+
// 3. Sentence end: '. ', '! ', '? ' followed by capital or newline
|
|
97
|
+
const sentRe = /[.!?](?:\s+(?=[A-Z\n])|(?=\n))/g;
|
|
98
|
+
sentRe.lastIndex = min;
|
|
99
|
+
m = sentRe.exec(content);
|
|
100
|
+
while (m && m.index <= max) {
|
|
101
|
+
const pos = m.index + m[0].length;
|
|
102
|
+
const dist = Math.abs(pos - target);
|
|
103
|
+
if (!best || best.priority < 2 || (best.priority === 2 && dist < Math.abs(best.pos - target))) {
|
|
104
|
+
best = { pos, priority: 2 };
|
|
105
|
+
}
|
|
106
|
+
m = sentRe.exec(content);
|
|
107
|
+
}
|
|
108
|
+
// 4. Word boundary (space)
|
|
109
|
+
if (!best || best.priority < 1) {
|
|
110
|
+
const spaceRe = / /g;
|
|
111
|
+
spaceRe.lastIndex = min;
|
|
112
|
+
m = spaceRe.exec(content);
|
|
113
|
+
while (m && m.index <= max) {
|
|
114
|
+
const pos = m.index + 1;
|
|
115
|
+
const dist = Math.abs(pos - target);
|
|
116
|
+
if (!best || (best.priority < 2 && dist < Math.abs(best.pos - target))) {
|
|
117
|
+
best = { pos, priority: 1 };
|
|
118
|
+
}
|
|
119
|
+
m = spaceRe.exec(content);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return best ? best.pos : Math.min(target, content.length);
|
|
123
|
+
}
|
|
124
|
+
function chunkSemantic(content, chunkSize, overlap) {
|
|
125
|
+
const chunkChars = chunkSize * 4;
|
|
126
|
+
const overlapChars = overlap * 4;
|
|
127
|
+
const tolerance = Math.floor(chunkChars * 0.2);
|
|
128
|
+
const result = [];
|
|
129
|
+
let start = 0;
|
|
130
|
+
while (start < content.length) {
|
|
131
|
+
const remaining = content.length - start;
|
|
132
|
+
// If the rest fits, take it all
|
|
133
|
+
if (remaining <= chunkChars + tolerance) {
|
|
134
|
+
result.push({ content: content.slice(start), startOffset: start });
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
const breakPos = findNaturalBreak(content, start + chunkChars, tolerance);
|
|
138
|
+
const end = Math.max(breakPos, start + 1); // always advance
|
|
139
|
+
result.push({ content: content.slice(start, end), startOffset: start });
|
|
140
|
+
// Next chunk starts with overlap from end of this chunk
|
|
141
|
+
const nextStart = Math.max(start + 1, end - overlapChars);
|
|
142
|
+
start = nextStart;
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Paragraph strategy
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
function chunkParagraph(content, chunkSize) {
|
|
150
|
+
const chunkChars = chunkSize * 4;
|
|
151
|
+
const paragraphs = content.split(/\n\n/);
|
|
152
|
+
const result = [];
|
|
153
|
+
let currentParts = [];
|
|
154
|
+
let currentLen = 0;
|
|
155
|
+
let currentOffset = 0;
|
|
156
|
+
let offsetTracker = 0;
|
|
157
|
+
for (let i = 0; i < paragraphs.length; i++) {
|
|
158
|
+
const para = paragraphs[i];
|
|
159
|
+
// +2 for the \n\n separator (except for first paragraph)
|
|
160
|
+
const paraLen = para.length + (currentParts.length > 0 ? 2 : 0);
|
|
161
|
+
if (currentParts.length > 0 && currentLen + paraLen > chunkChars) {
|
|
162
|
+
// Flush current group
|
|
163
|
+
result.push({ content: currentParts.join('\n\n'), startOffset: currentOffset });
|
|
164
|
+
currentOffset = offsetTracker;
|
|
165
|
+
currentParts = [para];
|
|
166
|
+
currentLen = para.length;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (currentParts.length === 0) {
|
|
170
|
+
currentOffset = offsetTracker;
|
|
171
|
+
}
|
|
172
|
+
currentParts.push(para);
|
|
173
|
+
currentLen += paraLen;
|
|
174
|
+
}
|
|
175
|
+
offsetTracker += para.length + 2; // account for \n\n
|
|
176
|
+
}
|
|
177
|
+
if (currentParts.length > 0) {
|
|
178
|
+
result.push({ content: currentParts.join('\n\n'), startOffset: currentOffset });
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=chunking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunking.js","sourceRoot":"","sources":["../../src/core/chunking.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Density Pruner
|
|
3
|
+
*
|
|
4
|
+
* Scores HTML block elements by text quality and removes low-value blocks
|
|
5
|
+
* (sidebars, footers, navigation, ads) that CSS selectors miss.
|
|
6
|
+
*
|
|
7
|
+
* Inspired by Crawl4AI's fit_markdown approach — typical 40-60% token savings.
|
|
8
|
+
*/
|
|
9
|
+
export interface PruneOptions {
|
|
10
|
+
/** Score threshold (0-1). Blocks below this are removed. Default: 0.4 */
|
|
11
|
+
threshold?: number;
|
|
12
|
+
/** Minimum word count for a block to be considered. Default: 3 */
|
|
13
|
+
minWords?: number;
|
|
14
|
+
/** Whether threshold adapts to content distribution. Default: true */
|
|
15
|
+
dynamic?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface PruneResult {
|
|
18
|
+
/** Pruned HTML */
|
|
19
|
+
html: string;
|
|
20
|
+
/** Number of nodes removed */
|
|
21
|
+
nodesRemoved: number;
|
|
22
|
+
/** Percentage of content removed (by character count) */
|
|
23
|
+
reductionPercent: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Prune low-value HTML blocks using content density scoring.
|
|
27
|
+
*
|
|
28
|
+
* @param html - Raw HTML to prune
|
|
29
|
+
* @param options - Pruning configuration
|
|
30
|
+
* @returns Pruned HTML with stats
|
|
31
|
+
*/
|
|
32
|
+
export declare function pruneContent(html: string, options?: PruneOptions): PruneResult;
|
|
33
|
+
//# sourceMappingURL=content-pruner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-pruner.d.ts","sourceRoot":"","sources":["../../src/core/content-pruner.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,WAAW,YAAY;IAC3B,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AA2LD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,WAAW,CAoGlF"}
|