( text: string, chunkSizeChars: number, stepChars?: number )
| 56 | * using a sliding window where chunks stay within the size limit. |
| 57 | */ |
| 58 | export function splitAtWordBoundaries( |
| 59 | text: string, |
| 60 | chunkSizeChars: number, |
| 61 | stepChars?: number |
| 62 | ): string[] { |
| 63 | const parts: string[] = [] |
| 64 | let pos = 0 |
| 65 | |
| 66 | while (pos < text.length) { |
| 67 | let end = Math.min(pos + chunkSizeChars, text.length) |
| 68 | |
| 69 | if (end < text.length) { |
| 70 | const lastSpace = text.lastIndexOf(' ', end) |
| 71 | if (lastSpace > pos) { |
| 72 | end = lastSpace |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | const part = text.slice(pos, end).trim() |
| 77 | if (part) { |
| 78 | parts.push(part) |
| 79 | } |
| 80 | |
| 81 | if (stepChars !== undefined) { |
| 82 | // Sliding window: advance by step for predictable overlap |
| 83 | const nextPos = pos + Math.max(1, stepChars) |
| 84 | if (nextPos >= text.length) break |
| 85 | pos = nextPos |
| 86 | } else { |
| 87 | // Non-overlapping: advance from end of extracted content |
| 88 | if (end >= text.length) break |
| 89 | pos = end |
| 90 | } |
| 91 | while (pos < text.length && text[pos] === ' ') pos++ |
| 92 | } |
| 93 | |
| 94 | return parts |
| 95 | } |
| 96 | |
| 97 | export function buildChunks(texts: string[], overlapTokens: number): Chunk[] { |
| 98 | let previousEndIndex = 0 |
no test coverage detected