hackathon-book-api / rag /chunking /text_splitter.py
Asma-yaseen's picture
Deploy FastAPI backend to Hugging Face Spaces
aee144c
Raw
History Blame Contribute Delete
5.92 kB
"""
Text Chunking Module
Splits documents into optimal chunks for embedding and retrieval
"""
from typing import List
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
TokenTextSplitter,
MarkdownTextSplitter
)
from langchain.schema import Document
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class DocumentChunker:
"""Handles splitting documents into chunks for embedding"""
def __init__(
self,
chunk_size: int = 1000,
chunk_overlap: int = 200,
separators: List[str] = None
):
"""
Initialize chunker with configurable parameters
Args:
chunk_size: Maximum size of each chunk in characters
chunk_overlap: Overlap between consecutive chunks
separators: Custom separators for splitting
"""
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
# Default separators optimized for technical content
if separators is None:
separators = [
"\n\n\n", # Multiple newlines (sections)
"\n\n", # Paragraphs
"\n", # Lines
". ", # Sentences
", ", # Clauses
" ", # Words
"" # Characters
]
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
separators=separators,
length_function=len
)
logger.info(f"Chunker initialized: size={chunk_size}, overlap={chunk_overlap}")
def chunk_documents(self, documents: List[Document]) -> List[Document]:
"""
Split documents into chunks
Args:
documents: List of Document objects to chunk
Returns:
List of chunked Document objects with preserved metadata
"""
try:
chunked_docs = self.text_splitter.split_documents(documents)
# Add chunk index to metadata
for idx, doc in enumerate(chunked_docs):
doc.metadata["chunk_id"] = idx
doc.metadata["chunk_size"] = len(doc.page_content)
logger.info(
f"Split {len(documents)} documents into {len(chunked_docs)} chunks"
)
return chunked_docs
except Exception as e:
logger.error(f"Error chunking documents: {str(e)}")
raise
def chunk_markdown(self, documents: List[Document]) -> List[Document]:
"""
Split markdown documents preserving structure
Args:
documents: List of markdown Document objects
Returns:
List of chunked Documents
"""
md_splitter = MarkdownTextSplitter(
chunk_size=self.chunk_size,
chunk_overlap=self.chunk_overlap
)
try:
chunked_docs = md_splitter.split_documents(documents)
for idx, doc in enumerate(chunked_docs):
doc.metadata["chunk_id"] = idx
doc.metadata["chunk_size"] = len(doc.page_content)
logger.info(
f"Split {len(documents)} markdown documents into {len(chunked_docs)} chunks"
)
return chunked_docs
except Exception as e:
logger.error(f"Error chunking markdown: {str(e)}")
raise
def chunk_by_tokens(
self,
documents: List[Document],
model_name: str = "gpt-3.5-turbo"
) -> List[Document]:
"""
Split documents by token count (useful for LLM context limits)
Args:
documents: List of Document objects
model_name: Model name for tokenization
Returns:
List of chunked Documents
"""
token_splitter = TokenTextSplitter(
chunk_size=self.chunk_size,
chunk_overlap=self.chunk_overlap,
model_name=model_name
)
try:
chunked_docs = token_splitter.split_documents(documents)
for idx, doc in enumerate(chunked_docs):
doc.metadata["chunk_id"] = idx
logger.info(
f"Split {len(documents)} documents into {len(chunked_docs)} token-based chunks"
)
return chunked_docs
except Exception as e:
logger.error(f"Error chunking by tokens: {str(e)}")
raise
def get_chunk_stats(self, chunks: List[Document]) -> dict:
"""
Get statistics about chunks
Args:
chunks: List of chunked documents
Returns:
Dictionary with chunk statistics
"""
if not chunks:
return {"total_chunks": 0}
chunk_sizes = [len(doc.page_content) for doc in chunks]
stats = {
"total_chunks": len(chunks),
"avg_chunk_size": sum(chunk_sizes) / len(chunk_sizes),
"min_chunk_size": min(chunk_sizes),
"max_chunk_size": max(chunk_sizes),
"total_characters": sum(chunk_sizes)
}
logger.info(f"Chunk statistics: {stats}")
return stats
# Example usage
if __name__ == "__main__":
from langchain.schema import Document
# Example document
sample_doc = Document(
page_content="This is a long document. " * 100,
metadata={"source": "example.txt"}
)
chunker = DocumentChunker(chunk_size=500, chunk_overlap=50)
chunks = chunker.chunk_documents([sample_doc])
print(f"Created {len(chunks)} chunks")
print(chunker.get_chunk_stats(chunks))