This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
// The chunker: one text into citation-sized pieces. Pure TypeScript, unit-tested without a store
|
||||
// or a model. Chunks split on blank lines where they can, so a snippet reads as a sentence and not
|
||||
// as a byte range; a chunk that fits stays whole.
|
||||
|
||||
import { validationFailed } from "@conusai-kit/core"
|
||||
import type { KnowledgeChunk } from "./ports.js"
|
||||
|
||||
export const CHUNK_SIZE = 2_000
|
||||
const CHUNK_OVERLAP = 200
|
||||
|
||||
/**
|
||||
* Splits one document's text into chunks of at most {@link CHUNK_SIZE} characters. Paragraphs
|
||||
* longer than the size are hard-split; the overlap keeps a sentence split across two chunks
|
||||
* retrievable from either side.
|
||||
*/
|
||||
export function chunkText(text: string): KnowledgeChunk[] {
|
||||
if (text.length === 0)
|
||||
throw validationFailed([{ path: "file", message: "the file has no text to index" }], "nothing to index")
|
||||
const paragraphs = text.split(/\r?\n\r?\n/).flatMap((paragraph) => {
|
||||
if (paragraph.length <= CHUNK_SIZE) return [paragraph]
|
||||
const pieces: string[] = []
|
||||
for (let at = 0; at < paragraph.length; at += CHUNK_SIZE - CHUNK_OVERLAP) {
|
||||
pieces.push(paragraph.slice(at, at + CHUNK_SIZE))
|
||||
}
|
||||
return pieces
|
||||
})
|
||||
const chunks: KnowledgeChunk[] = []
|
||||
let current = ""
|
||||
for (const paragraph of paragraphs) {
|
||||
if (current !== "" && current.length + paragraph.length + 2 > CHUNK_SIZE) {
|
||||
chunks.push({ position: chunks.length + 1, text: current })
|
||||
current = ""
|
||||
}
|
||||
current = current === "" ? paragraph : `${current}\n\n${paragraph}`
|
||||
}
|
||||
if (current !== "") chunks.push({ position: chunks.length + 1, text: current })
|
||||
return chunks
|
||||
}
|
||||
Reference in New Issue
Block a user