





















@@ -217,6 +217,7 @@ export function chunkByParagraph(
217217const spans = parseFenceSpans(normalized);
218218219219const parts: string[] = [];
220+const separators: string[] = [];
220221const re = /\n[\t ]*\n+/g; // paragraph break: blank line(s), allowing whitespace
221222let lastIndex = 0;
222223for (const match of normalized.matchAll(re)) {
@@ -228,23 +229,49 @@ export function chunkByParagraph(
228229}
229230230231parts.push(normalized.slice(lastIndex, idx));
232+separators.push(match[0]);
231233lastIndex = idx + match[0].length;
232234}
233235parts.push(normalized.slice(lastIndex));
234236235237const chunks: string[] = [];
236-for (const part of parts) {
238+let currentChunk = "";
239+240+const pushParagraph = (paragraph: string, separatorBefore?: string) => {
241+if (!currentChunk) {
242+if (paragraph.length <= limit) {
243+currentChunk = paragraph;
244+return;
245+}
246+if (!splitLongParagraphs) {
247+chunks.push(paragraph);
248+return;
249+}
250+chunks.push(...chunkText(paragraph, limit));
251+return;
252+}
253+254+const candidate = `${currentChunk}${separatorBefore ?? "\n\n"}${paragraph}`;
255+if (candidate.length <= limit) {
256+currentChunk = candidate;
257+return;
258+}
259+260+chunks.push(currentChunk);
261+currentChunk = "";
262+pushParagraph(paragraph);
263+};
264+265+for (const [index, part] of parts.entries()) {
237266const paragraph = part.replace(/\s+$/g, "");
238267if (!paragraph.trim()) {
239268continue;
240269}
241-if (paragraph.length <= limit) {
242-chunks.push(paragraph);
243-} else if (!splitLongParagraphs) {
244-chunks.push(paragraph);
245-} else {
246-chunks.push(...chunkText(paragraph, limit));
247-}
270+pushParagraph(paragraph, separators[index - 1]);
271+}
272+273+if (currentChunk) {
274+chunks.push(currentChunk);
248275}
249276250277return chunks;
@@ -266,13 +293,12 @@ export function chunkMarkdownTextWithMode(text: string, limit: number, mode: Chu
266293// If a paragraph must be split by length, defer to the markdown-aware chunker.
267294const paragraphChunks = chunkByParagraph(text, limit, { splitLongParagraphs: false });
268295const out: string[] = [];
269-for (const chunk of paragraphChunks) {
270-const nested = chunkMarkdownText(chunk, limit);
271-if (!nested.length && chunk) {
272-out.push(chunk);
273-} else {
274-out.push(...nested);
275-}
296+for (const chunk of paragraphChunks.flatMap((paragraphChunk) =>
297+paragraphChunk.length > limit
298+ ? splitPackedFenceParagraphChunk(paragraphChunk)
299+ : paragraphChunk,
300+)) {
301+out.push(...chunkMarkdownText(chunk, limit));
276302}
277303return out;
278304}
@@ -295,6 +321,34 @@ function splitByNewline(
295321return lines;
296322}
297323324+function splitPackedFenceParagraphChunk(chunk: string): string[] {
325+const chunks: string[] = [];
326+let start = 0;
327+for (const span of parseFenceSpans(chunk)) {
328+if (span.end <= start) {
329+continue;
330+}
331+const separator = chunk.slice(span.end).match(/^\n[\t ]*\n+/)?.[0];
332+if (!separator) {
333+continue;
334+}
335+const tail = chunk.slice(span.end + separator.length);
336+if (!tail.trim()) {
337+continue;
338+}
339+chunks.push(chunk.slice(start, span.end));
340+start = span.end + separator.length;
341+}
342+if (chunks.length === 0) {
343+return [chunk];
344+}
345+const tail = chunk.slice(start);
346+if (tail) {
347+chunks.push(tail);
348+}
349+return chunks;
350+}
351+298352function resolveChunkEarlyReturn(text: string, limit: number): string[] | undefined {
299353if (!text) {
300354return [];
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。