SPB Git forge

spb/uqo-chat

Public
14commits 1branches 0releases
1.4 MBsize
maindefault branch
17 days agolast push
Python 64.6% TypeScript 33.7% CSS 0.8%
2.6 KB · 80 lines python
Raw Blame History
1"""Text chunking (≈600–900 tokens, overlap 120) that respects headings and paragraphs."""23from __future__ import annotations45import re6import unicodedata7from dataclasses import dataclass, field89CHARS_PER_TOKEN = 410TARGET = 750 * CHARS_PER_TOKEN11MAX = 900 * CHARS_PER_TOKEN12OVERLAP = 120 * CHARS_PER_TOKEN131415@dataclass16class Chunk:17    content: str18    module: str = ""19    section: str = ""20    page: str = ""21    url: str = ""22    metadata: dict = field(default_factory=dict)232425def split_paragraphs(text: str) -> list[str]:26    parts = re.split(r"\n\s*\n", text.strip())27    return [p.strip() for p in parts if p.strip()]282930def chunk_text(text: str, **meta: str) -> list[Chunk]:31    """Greedy paragraph packing with overlap; oversize paragraphs are hard-split."""32    paras = split_paragraphs(text)33    chunks: list[Chunk] = []34    buf = ""35    for p in paras:36        if len(p) > MAX:37            if buf:38                chunks.append(Chunk(buf.strip(), **meta))39                buf = ""40            for i in range(0, len(p), TARGET - OVERLAP):41                chunks.append(Chunk(p[i:i + TARGET].strip(), **meta))42            continue43        if len(buf) + len(p) + 2 > TARGET and buf:44            chunks.append(Chunk(buf.strip(), **meta))45            buf = buf[-OVERLAP:] + "\n\n" + p if OVERLAP else p46        else:47            buf = f"{buf}\n\n{p}" if buf else p48    if buf.strip():49        chunks.append(Chunk(buf.strip(), **meta))50    return [c for c in chunks if len(c.content) > 40]515253# ------------------------------------------------------------------ tokenisation (BM25)54STOPWORDS = set("""55le la les l un une des du de d et ou où mais donc or ni car à au aux en dans par pour sur sous56avec sans ce cet cette ces se sa son ses leur leurs mon ma mes ton ta tes notre nos votre vos57qui que quoi dont il elle ils elles on nous vous je tu y ne pas plus moins très est sont été58être avoir a ont fait faire peut peuvent doit doivent comme si the of and to in is are for59""".split())606162def strip_accents(s: str) -> str:63    return "".join(c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) != "Mn")646566def tokenize(text: str) -> list[str]:67    text = strip_accents(text.lower())68    words = re.findall(r"[a-z0-9]+", text)69    out = []70    for w in words:71        if w in STOPWORDS or len(w) < 2:72            continue73        # light French stemming: plural / feminine endings74        for suf in ("ements", "ement", "tions", "tion", "ees", "es", "s", "x", "e"):75            if len(w) > 5 and w.endswith(suf):76                w = w[: -len(suf)]77                break78        out.append(w)79    return out80