@v1nvn/readability 0.44.0 → 0.45.1
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/README.md +3 -3
- package/package.json +7 -9
- package/src/cli.js +99 -0
- package/src/cli.ts +134 -0
- package/src/config.js +29 -0
- package/src/config.ts +42 -0
- package/src/dev.js +55 -0
- package/src/dev.ts +77 -0
- package/src/errors.js +16 -0
- package/src/errors.ts +19 -0
- package/src/handler.js +4 -0
- package/src/handler.ts +8 -0
- package/src/host-sampling.js +57 -0
- package/src/host-sampling.ts +86 -0
- package/src/index.js +23 -0
- package/src/index.ts +27 -0
- package/src/logger.js +2 -0
- package/src/logger.ts +3 -0
- package/src/output/format.js +92 -0
- package/src/output/format.ts +132 -0
- package/src/pipeline/cell-text.js +51 -0
- package/src/pipeline/cell-text.ts +56 -0
- package/src/pipeline/context.js +1 -0
- package/src/pipeline/context.ts +88 -0
- package/src/pipeline/dom.js +16 -0
- package/src/pipeline/dom.ts +23 -0
- package/src/pipeline/footnotes.js +148 -0
- package/src/pipeline/footnotes.ts +181 -0
- package/src/pipeline/math.js +113 -0
- package/src/pipeline/math.ts +130 -0
- package/src/pipeline/normalize.js +507 -0
- package/src/pipeline/normalize.ts +594 -0
- package/src/pipeline/readability.js +10 -0
- package/src/pipeline/readability.ts +36 -0
- package/src/pipeline/resolver.js +113 -0
- package/src/pipeline/resolver.ts +143 -0
- package/src/pipeline/sanitize.js +26 -0
- package/src/pipeline/sanitize.ts +42 -0
- package/src/pipeline/tables.js +239 -0
- package/src/pipeline/tables.ts +286 -0
- package/src/pipeline/turndown.js +153 -0
- package/src/pipeline/turndown.ts +191 -0
- package/src/pipeline/urls.js +14 -0
- package/src/pipeline/urls.ts +13 -0
- package/src/policy/chunk.js +365 -0
- package/src/policy/chunk.ts +482 -0
- package/src/policy/diagnostics.js +53 -0
- package/src/policy/diagnostics.ts +96 -0
- package/src/policy/explain.js +104 -0
- package/src/policy/explain.ts +182 -0
- package/src/policy/fallback.js +68 -0
- package/src/policy/fallback.ts +110 -0
- package/src/policy/gating.js +90 -0
- package/src/policy/gating.ts +106 -0
- package/src/policy/grid-detector.js +207 -0
- package/src/policy/grid-detector.ts +313 -0
- package/src/policy/identifiers.js +62 -0
- package/src/policy/identifiers.ts +75 -0
- package/src/policy/images.js +49 -0
- package/src/policy/images.ts +64 -0
- package/src/policy/list-detector.js +232 -0
- package/src/policy/list-detector.ts +308 -0
- package/src/policy/lost-signal.js +44 -0
- package/src/policy/lost-signal.ts +69 -0
- package/src/policy/markdown.js +58 -0
- package/src/policy/markdown.ts +76 -0
- package/src/policy/metadata.js +216 -0
- package/src/policy/metadata.ts +312 -0
- package/src/policy/outline-chains.js +165 -0
- package/src/policy/outline-chains.ts +215 -0
- package/src/policy/outline.js +71 -0
- package/src/policy/outline.ts +89 -0
- package/src/policy/pagination.js +79 -0
- package/src/policy/pagination.ts +104 -0
- package/src/policy/presets.js +100 -0
- package/src/policy/presets.ts +142 -0
- package/src/policy/section.js +111 -0
- package/src/policy/section.ts +127 -0
- package/src/policy/selector-lint.js +143 -0
- package/src/policy/selector-lint.ts +186 -0
- package/src/policy/sibling-scan.js +92 -0
- package/src/policy/sibling-scan.ts +99 -0
- package/src/policy/text.js +18 -0
- package/src/policy/text.ts +36 -0
- package/src/policy/truncate.js +45 -0
- package/src/policy/truncate.ts +59 -0
- package/src/preset-cache.js +165 -0
- package/src/preset-cache.ts +221 -0
- package/src/resources.js +227 -0
- package/src/resources.ts +317 -0
- package/src/sampling.js +45 -0
- package/src/sampling.ts +74 -0
- package/src/server.js +73 -0
- package/src/server.ts +85 -0
- package/src/tools/chunk_text.js +40 -0
- package/src/tools/chunk_text.ts +65 -0
- package/src/tools/explain.js +200 -0
- package/src/tools/explain.ts +299 -0
- package/src/tools/extract.js +263 -0
- package/src/tools/extract.ts +368 -0
- package/src/tools/extract_grid.js +56 -0
- package/src/tools/extract_grid.ts +90 -0
- package/src/tools/extract_links.js +98 -0
- package/src/tools/extract_links.ts +138 -0
- package/src/tools/extract_list.js +66 -0
- package/src/tools/extract_list.ts +97 -0
- package/src/tools/extract_metadata.js +67 -0
- package/src/tools/extract_metadata.ts +102 -0
- package/src/tools/extract_section.js +56 -0
- package/src/tools/extract_section.ts +84 -0
- package/src/tools/extract_tables.js +59 -0
- package/src/tools/extract_tables.ts +101 -0
- package/src/tools/html-source.js +30 -0
- package/src/tools/html-source.ts +40 -0
- package/src/tools/html_to_markdown.js +113 -0
- package/src/tools/html_to_markdown.ts +180 -0
- package/src/tools/outline.js +47 -0
- package/src/tools/outline.ts +77 -0
- package/src/tools/output-schema.js +504 -0
- package/src/tools/output-schema.ts +715 -0
- package/src/tools/schemas.js +288 -0
- package/src/tools/schemas.ts +442 -0
- package/src/tools/suggest-preset.js +504 -0
- package/src/tools/suggest-preset.ts +708 -0
- package/src/types/jsdom.d.ts +15 -0
- package/src/types/turndown-plugin-gfm.d.ts +11 -0
- package/dist/assets/cli-Dfjg7dOQ.js +0 -76
- package/dist/assets/cli-Dfjg7dOQ.js.map +0 -1
- package/dist/assets/extract-CdDp-KwX.js +0 -2880
- package/dist/assets/extract-CdDp-KwX.js.map +0 -1
- package/dist/index.js +0 -2267
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// Token-bounded chunking of extracted markdown for RAG/embedding. Chunk
|
|
2
|
+
// `tokenCount` uses the shared chars/4 estimator (policy/text.ts), keeping it
|
|
3
|
+
// directly comparable to `metadata.tokenEstimate`. This is the server-side
|
|
4
|
+
// chunking path for large documents; transport-level streaming was rejected —
|
|
5
|
+
// MCP tool-result progress isn't universally host-supported, and this option
|
|
6
|
+
// already keeps a multi-MB page from returning as one payload.
|
|
7
|
+
import { hardSplitLines, headingText, parseBlocks } from "./markdown.js";
|
|
8
|
+
import { estimateTokens } from "./text.js";
|
|
9
|
+
const HEADING_FIRST_LINE = /^#{1,6}\s/;
|
|
10
|
+
function splitBlocks(markdown) {
|
|
11
|
+
const parts = markdown.split(/\n{2,}/);
|
|
12
|
+
const blocks = [];
|
|
13
|
+
let heading = '';
|
|
14
|
+
for (const part of parts) {
|
|
15
|
+
const text = part.trim();
|
|
16
|
+
if (!text) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (HEADING_FIRST_LINE.test(text)) {
|
|
20
|
+
heading = headingText(text);
|
|
21
|
+
}
|
|
22
|
+
blocks.push({ headingContext: heading, text });
|
|
23
|
+
}
|
|
24
|
+
return blocks;
|
|
25
|
+
}
|
|
26
|
+
// Char strategy may break a code block — the semantic strategy avoids that.
|
|
27
|
+
function splitOversizedBlock(block, maxChars) {
|
|
28
|
+
return hardSplitLines(block.text, maxChars).map(text => ({
|
|
29
|
+
headingContext: block.headingContext,
|
|
30
|
+
text,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function toUnits(blocks, maxChars) {
|
|
34
|
+
const units = [];
|
|
35
|
+
for (const block of blocks) {
|
|
36
|
+
if (block.text.length <= maxChars) {
|
|
37
|
+
units.push(block);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
units.push(...splitOversizedBlock(block, maxChars));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return units;
|
|
44
|
+
}
|
|
45
|
+
function chunkMarkdownChar(markdown, options) {
|
|
46
|
+
if (!markdown) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const maxTokens = Math.max(1, Math.floor(options.maxTokens));
|
|
50
|
+
const maxChars = maxTokens * 4;
|
|
51
|
+
const overlapTokens = Math.max(0, Math.floor(options.overlap));
|
|
52
|
+
// Overlap must be strictly less than maxChars — otherwise a chunk's tail
|
|
53
|
+
// could consume the entire budget and we'd loop forever re-emitting it.
|
|
54
|
+
const overlapChars = Math.min(overlapTokens * 4, maxChars - 1);
|
|
55
|
+
const blocks = splitBlocks(markdown);
|
|
56
|
+
if (blocks.length === 0) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const units = toUnits(blocks, maxChars);
|
|
60
|
+
if (units.length === 0) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const chunks = [];
|
|
64
|
+
let i = 0;
|
|
65
|
+
let overlapText = '';
|
|
66
|
+
let overlapHeading = '';
|
|
67
|
+
while (i < units.length) {
|
|
68
|
+
const spans = [];
|
|
69
|
+
let chunkText = '';
|
|
70
|
+
if (overlapText) {
|
|
71
|
+
chunkText = overlapText;
|
|
72
|
+
spans.push({
|
|
73
|
+
end: overlapText.length,
|
|
74
|
+
headingContext: overlapHeading,
|
|
75
|
+
start: 0,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Force at least one new unit per chunk so the loop terminates. If carrying
|
|
79
|
+
// the overlap would push the first new unit past maxChars, drop the overlap
|
|
80
|
+
// for this chunk (the unit was pre-split to <= maxChars so it always fits alone).
|
|
81
|
+
const firstUnit = units[i];
|
|
82
|
+
const sepLen = chunkText ? 2 : 0;
|
|
83
|
+
if (chunkText &&
|
|
84
|
+
chunkText.length + sepLen + firstUnit.text.length > maxChars) {
|
|
85
|
+
chunkText = '';
|
|
86
|
+
spans.length = 0;
|
|
87
|
+
}
|
|
88
|
+
{
|
|
89
|
+
const sep = chunkText ? '\n\n' : '';
|
|
90
|
+
const start = chunkText.length + sep.length;
|
|
91
|
+
chunkText = chunkText + sep + firstUnit.text;
|
|
92
|
+
spans.push({
|
|
93
|
+
end: chunkText.length,
|
|
94
|
+
headingContext: firstUnit.headingContext,
|
|
95
|
+
start,
|
|
96
|
+
});
|
|
97
|
+
i += 1;
|
|
98
|
+
}
|
|
99
|
+
while (i < units.length) {
|
|
100
|
+
const unit = units[i];
|
|
101
|
+
const candidate = `${chunkText}\n\n${unit.text}`;
|
|
102
|
+
if (candidate.length > maxChars) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const start = chunkText.length + 2;
|
|
106
|
+
chunkText = candidate;
|
|
107
|
+
spans.push({
|
|
108
|
+
end: chunkText.length,
|
|
109
|
+
headingContext: unit.headingContext,
|
|
110
|
+
start,
|
|
111
|
+
});
|
|
112
|
+
i += 1;
|
|
113
|
+
}
|
|
114
|
+
const text = chunkText.trim();
|
|
115
|
+
if (text) {
|
|
116
|
+
chunks.push({
|
|
117
|
+
headingContext: spans[0]?.headingContext ?? '',
|
|
118
|
+
index: chunks.length,
|
|
119
|
+
text,
|
|
120
|
+
tokenCount: estimateTokens(text).tokenEstimate,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (overlapChars > 0 && i < units.length) {
|
|
124
|
+
const overlapStart = Math.max(0, chunkText.length - overlapChars);
|
|
125
|
+
const carrier = spans.find(span => overlapStart >= span.start && overlapStart < span.end) ?? spans.find(span => span.start >= overlapStart);
|
|
126
|
+
overlapHeading = carrier?.headingContext ?? '';
|
|
127
|
+
overlapText = chunkText.slice(overlapStart).replace(/^\n+/, '');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
overlapText = '';
|
|
131
|
+
overlapHeading = '';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return chunks;
|
|
135
|
+
}
|
|
136
|
+
function parseSemanticUnits(markdown) {
|
|
137
|
+
const units = [];
|
|
138
|
+
const stack = [];
|
|
139
|
+
function context() {
|
|
140
|
+
return stack.map(h => h.text).join(' > ');
|
|
141
|
+
}
|
|
142
|
+
for (const block of parseBlocks(markdown)) {
|
|
143
|
+
const text = markdown.slice(block.start, block.end);
|
|
144
|
+
if (block.kind === 'heading') {
|
|
145
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= block.depth) {
|
|
146
|
+
stack.pop();
|
|
147
|
+
}
|
|
148
|
+
stack.push({ level: block.depth, text: headingText(text) });
|
|
149
|
+
units.push({ headingContext: context(), kind: 'heading', text });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
units.push({
|
|
153
|
+
headingContext: context(),
|
|
154
|
+
kind: block.kind === 'code' ? 'code' : 'text',
|
|
155
|
+
text,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return units;
|
|
159
|
+
}
|
|
160
|
+
// A section = a heading unit plus its following non-heading units until the
|
|
161
|
+
// next heading. Pre-first-heading content forms its own section with an empty
|
|
162
|
+
// hierarchy path.
|
|
163
|
+
function groupSections(units) {
|
|
164
|
+
const sections = [];
|
|
165
|
+
let current = [];
|
|
166
|
+
let currentContext = '';
|
|
167
|
+
function flush() {
|
|
168
|
+
if (current.length > 0) {
|
|
169
|
+
sections.push({ headingContext: currentContext, units: current });
|
|
170
|
+
current = [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
for (const unit of units) {
|
|
174
|
+
if (unit.kind === 'heading') {
|
|
175
|
+
flush();
|
|
176
|
+
current = [unit];
|
|
177
|
+
currentContext = unit.headingContext;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
if (current.length === 0) {
|
|
181
|
+
currentContext = unit.headingContext;
|
|
182
|
+
}
|
|
183
|
+
current.push(unit);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
flush();
|
|
187
|
+
return sections;
|
|
188
|
+
}
|
|
189
|
+
function joinedLength(units) {
|
|
190
|
+
if (units.length === 0) {
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
let total = units[0].text.length;
|
|
194
|
+
for (let i = 1; i < units.length; i++) {
|
|
195
|
+
total += 2 + units[i].text.length;
|
|
196
|
+
}
|
|
197
|
+
return total;
|
|
198
|
+
}
|
|
199
|
+
// Hard-split a text unit that alone exceeds the budget — line-aware first, then
|
|
200
|
+
// hard-split any oversized line. Only ever applied to paragraphs; code blocks
|
|
201
|
+
// are never passed through here.
|
|
202
|
+
function splitOversizedTextUnit(unit, maxChars) {
|
|
203
|
+
return hardSplitLines(unit.text, maxChars).map(text => ({
|
|
204
|
+
headingContext: unit.headingContext,
|
|
205
|
+
kind: 'text',
|
|
206
|
+
text,
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
// Pack sections into chunk groups <= maxChars, preferring heading boundaries.
|
|
210
|
+
// A section that fits is never split; an oversized section is split into
|
|
211
|
+
// sub-chunks by body unit (paragraphs may split; code blocks never do).
|
|
212
|
+
function buildBaseGroups(sections, maxChars) {
|
|
213
|
+
const groups = [];
|
|
214
|
+
let current = [];
|
|
215
|
+
let currentLen = 0;
|
|
216
|
+
function emit() {
|
|
217
|
+
if (current.length > 0) {
|
|
218
|
+
groups.push(current);
|
|
219
|
+
current = [];
|
|
220
|
+
currentLen = 0;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function append(unit) {
|
|
224
|
+
const sep = current.length > 0 ? 2 : 0;
|
|
225
|
+
current.push(unit);
|
|
226
|
+
currentLen += sep + unit.text.length;
|
|
227
|
+
}
|
|
228
|
+
for (const section of sections) {
|
|
229
|
+
const sectionLen = joinedLength(section.units);
|
|
230
|
+
const sep = current.length > 0 ? 2 : 0;
|
|
231
|
+
if (currentLen + sep + sectionLen <= maxChars) {
|
|
232
|
+
for (const unit of section.units) {
|
|
233
|
+
append(unit);
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// Doesn't fit merged — close the current group and retry the section alone
|
|
238
|
+
// so it has first dibs on the next group (it may still merge with a later
|
|
239
|
+
// section that fits behind it).
|
|
240
|
+
emit();
|
|
241
|
+
if (sectionLen <= maxChars) {
|
|
242
|
+
for (const unit of section.units) {
|
|
243
|
+
append(unit);
|
|
244
|
+
}
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
// Oversized section: pack body units under the heading; split oversized
|
|
248
|
+
// paragraphs, emit oversized code blocks whole.
|
|
249
|
+
const [headingUnit, ...body] = section.units;
|
|
250
|
+
current.push(headingUnit);
|
|
251
|
+
currentLen = headingUnit.text.length;
|
|
252
|
+
for (const unit of body) {
|
|
253
|
+
if (unit.kind === 'code' && unit.text.length > maxChars) {
|
|
254
|
+
// Semantic never splits a code fence: a fence cut mid-way is
|
|
255
|
+
// unrecoverable, so the block is emitted as its own chunk even though
|
|
256
|
+
// it exceeds the token budget. Deliberate tradeoff vs. the char cap.
|
|
257
|
+
emit();
|
|
258
|
+
groups.push([unit]);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const sepNow = current.length > 0 ? 2 : 0;
|
|
262
|
+
if (unit.text.length <= maxChars &&
|
|
263
|
+
currentLen + sepNow + unit.text.length <= maxChars) {
|
|
264
|
+
append(unit);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const pieces = unit.text.length > maxChars
|
|
268
|
+
? splitOversizedTextUnit(unit, maxChars)
|
|
269
|
+
: [unit];
|
|
270
|
+
for (const piece of pieces) {
|
|
271
|
+
const pieceSep = current.length > 0 ? 2 : 0;
|
|
272
|
+
if (current.length > 0 &&
|
|
273
|
+
currentLen + pieceSep + piece.text.length > maxChars) {
|
|
274
|
+
emit();
|
|
275
|
+
}
|
|
276
|
+
append(piece);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Leave the trailing sub-chunk in `current` so a following section can
|
|
280
|
+
// merge into it if it fits.
|
|
281
|
+
}
|
|
282
|
+
emit();
|
|
283
|
+
return groups;
|
|
284
|
+
}
|
|
285
|
+
// Carry the trailing text (non-code) units from the previous group as the
|
|
286
|
+
// prefix of the next, up to overlapChars. Never carry into or out of a code
|
|
287
|
+
// block: a trailing code run is skipped, then contiguous text units before it
|
|
288
|
+
// are carried (stop at the first heading or code block encountered).
|
|
289
|
+
function applySemanticOverlap(groups, overlapChars) {
|
|
290
|
+
const result = [];
|
|
291
|
+
for (let i = 0; i < groups.length; i++) {
|
|
292
|
+
const group = groups[i];
|
|
293
|
+
const carrier = [];
|
|
294
|
+
if (i > 0 && overlapChars > 0) {
|
|
295
|
+
const prev = groups[i - 1];
|
|
296
|
+
let end = prev.length;
|
|
297
|
+
while (end > 0 && prev[end - 1].kind === 'code') {
|
|
298
|
+
end -= 1;
|
|
299
|
+
}
|
|
300
|
+
let carrierLen = 0;
|
|
301
|
+
for (let j = end - 1; j >= 0; j--) {
|
|
302
|
+
const unit = prev[j];
|
|
303
|
+
if (unit.kind !== 'text') {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
const sep = carrier.length > 0 ? 2 : 0;
|
|
307
|
+
if (carrierLen + sep + unit.text.length > overlapChars) {
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
carrier.unshift(unit);
|
|
311
|
+
carrierLen += sep + unit.text.length;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const units = carrier.length === 0 ? group : [...carrier, ...group];
|
|
315
|
+
result.push({
|
|
316
|
+
// The chunk's heading context follows its first non-carrier unit, so a
|
|
317
|
+
// heading-leading chunk keeps its heading's path even with overlap.
|
|
318
|
+
headingContext: group[0]?.headingContext ?? '',
|
|
319
|
+
units,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
function chunkMarkdownSemantic(markdown, options) {
|
|
325
|
+
if (!markdown) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const maxTokens = Math.max(1, Math.floor(options.maxTokens));
|
|
329
|
+
const maxChars = maxTokens * 4;
|
|
330
|
+
const overlapTokens = Math.max(0, Math.floor(options.overlap));
|
|
331
|
+
const overlapChars = Math.min(overlapTokens * 4, maxChars - 1);
|
|
332
|
+
const units = parseSemanticUnits(markdown);
|
|
333
|
+
if (units.length === 0) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
const sections = groupSections(units);
|
|
337
|
+
const baseGroups = buildBaseGroups(sections, maxChars);
|
|
338
|
+
const groups = applySemanticOverlap(baseGroups, overlapChars);
|
|
339
|
+
const chunks = [];
|
|
340
|
+
for (const group of groups) {
|
|
341
|
+
if (group.units.length === 0) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const text = group.units
|
|
345
|
+
.map(unit => unit.text)
|
|
346
|
+
.join('\n\n')
|
|
347
|
+
.trim();
|
|
348
|
+
if (!text) {
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
chunks.push({
|
|
352
|
+
headingContext: group.headingContext,
|
|
353
|
+
index: chunks.length,
|
|
354
|
+
text,
|
|
355
|
+
tokenCount: estimateTokens(text).tokenEstimate,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return chunks;
|
|
359
|
+
}
|
|
360
|
+
export function chunkMarkdown(markdown, options) {
|
|
361
|
+
if (options.strategy === 'semantic') {
|
|
362
|
+
return chunkMarkdownSemantic(markdown, options);
|
|
363
|
+
}
|
|
364
|
+
return chunkMarkdownChar(markdown, options);
|
|
365
|
+
}
|