Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ ignore = ["E402", "E501", "E722", "E712", "F841", "F811", "UP031", "UP035"]
[tool.skylos]
exclude = [".repowise", ".codegraph", "docs", "__pycache__"]

# SQL injection false positives: all findings use parameterized queries (?)
# or build SQL templates (table names, IN clauses) — not user data injection
ignore = ["SKY-D211"]

[tool.skylos.quality]
max_complexity = 15
max_lines = 100
Expand Down
60 changes: 60 additions & 0 deletions rag/chunking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Text chunking for RAG — split text into overlapping chunks."""


def chunk_text(text: str, max_size: int = 500, overlap: int = 100) -> list[str]:
"""Split text into chunks with sliding overlap for semantic continuity.

Rules:
1. Split on double newline (paragraph).
2. When accumulated buffer reaches max_size, flush it.
Last `overlap` chars carry over to next chunk.
3. Paragraphs longer than max_size are split by words
(overlap only at paragraph boundaries, not within).
"""
if overlap >= max_size:
raise ValueError("overlap=%d must be < max_size=%d" % (overlap, max_size))

paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()]
chunks: list[str] = []
buffer: list[str] = []

def _flush(buf: list[str]) -> None:
if not buf:
return
chunks.append("\n\n".join(buf).strip())

def _take_overlap(buf: list[str], n: int) -> list[str]:
"""Return last n chars of joined buffer as leading part for next chunk."""
if n <= 0 or not buf:
return []
joined = "\n\n".join(buf)
tail = joined[-n:]
return [tail]

for p in paragraphs:
if len(p) > max_size:
# Flush current buffer first
_flush(buffer)
buffer = []
# Split long paragraph by words
words = p.split()
word_buf: list[str] = []
for w in words:
if len(" ".join(word_buf + [w])) > max_size and word_buf:
chunks.append(" ".join(word_buf).strip())
# Word-level overlap: keep last N words
word_buf = word_buf[-max(1, overlap // 8) :] + [w] if overlap else [w]
else:
word_buf.append(w)
if word_buf:
chunks.append(" ".join(word_buf).strip())
continue

projected = "\n\n".join(buffer + [p])
if len(projected) > max_size and buffer:
_flush(buffer)
buffer = _take_overlap(buffer, overlap)
buffer.append(p)

_flush(buffer)
return chunks
Loading
Loading