| |
| import re |
| from typing import List, Tuple |
|
|
|
|
| def approx_token_count(text: str) -> int: |
| """ |
| Rough token estimate for chunk sizing (heuristic). |
| Keep this simple but consistent: ~4 chars per token. |
| """ |
| return max(1, len(text) // 4) |
|
|
|
|
| def chunk_pages( |
| pages: List[str], |
| target_tokens: int = 520, |
| overlap_tokens: int = 80, |
| ) -> List[Tuple[str, int, int]]: |
| """ |
| Split pages (list[str]) into overlapping chunks. |
| Returns list of tuples: (chunk_text, page_start, page_end) |
| |
| Overlap is implemented at paragraph level (keeps page ranges correct). |
| """ |
| chunks: List[Tuple[str, int, int]] = [] |
|
|
| buffer: List[str] = [] |
| buffer_pages: List[int] = [] |
| buffer_tokens = 0 |
|
|
| def make_chunk(): |
| nonlocal buffer, buffer_pages, buffer_tokens |
| if not buffer: |
| return |
| chunk_text = "\n\n".join(buffer).strip() |
| page_start = min(buffer_pages) |
| page_end = max(buffer_pages) |
| chunks.append((chunk_text, page_start, page_end)) |
|
|
| for page_idx, page in enumerate(pages, start=1): |
| paragraphs = [p.strip() for p in re.split(r"\n\s*\n", page) if p.strip()] |
|
|
| for para in paragraphs: |
| t = approx_token_count(para) |
|
|
| |
| if buffer_tokens == 0 and t > target_tokens: |
| |
| buffer = [para] |
| buffer_pages = [page_idx] |
| buffer_tokens = t |
| make_chunk() |
| buffer, buffer_pages, buffer_tokens = [], [], 0 |
| continue |
|
|
| |
| if buffer_tokens + t > target_tokens: |
| make_chunk() |
|
|
| |
| tail_buffer: List[str] = [] |
| tail_pages: List[int] = [] |
| tail_tokens = 0 |
| |
| for p, p_pg in zip(reversed(buffer), reversed(buffer_pages)): |
| pt = approx_token_count(p) |
| tail_buffer.insert(0, p) |
| tail_pages.insert(0, p_pg) |
| tail_tokens += pt |
| if tail_tokens >= overlap_tokens: |
| break |
|
|
| buffer = tail_buffer |
| buffer_pages = tail_pages |
| buffer_tokens = tail_tokens |
|
|
| |
| buffer.append(para) |
| buffer_pages.append(page_idx) |
| buffer_tokens += t |
|
|
| |
| if buffer: |
| make_chunk() |
|
|
| return chunks |
|
|