rag-ingestion 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.3
2
+ Name: rag-ingestion
3
+ Version: 0.1.0
4
+ Summary: A lightweight document ingestion component for RAG pipelines.
5
+ Author: satyampandya
6
+ Requires-Dist: docx2txt>=0.9
7
+ Requires-Dist: langchain-community>=0.4.2
8
+ Requires-Dist: langchain-text-splitters>=1.1.2
9
+ Requires-Dist: openpyxl>=3.1.5
10
+ Requires-Dist: pypdf>=6.18.0
11
+ Requires-Dist: python-docx>=1.2.0
12
+ Requires-Dist: tiktoken>=0.14.0
13
+ Requires-Dist: unstructured[xlsx]>=0.27.5
14
+ Requires-Python: >=3.13
15
+ Description-Content-Type: text/markdown
16
+
17
+ # RAG Ingestion
18
+
19
+ A lightweight document ingestion component for Retrieval-Augmented Generation (RAG) pipelines.
20
+
21
+ RAG Ingestion handles the initial document-processing stage of a RAG workflow: loading supported files, extracting their content, tracking token usage, cleaning extracted text, and splitting documents into chunks.
22
+
23
+ The package is designed to work with LangChain `Document` objects and can be used as a standalone component or as part of a larger RAG pipeline.
24
+
25
+ ## Features
26
+
27
+ * Load multiple documents from file paths
28
+ * Support for PDF, DOCX, and XLSX files
29
+ * Extract content into LangChain `Document` objects
30
+ * Token counting with `tiktoken`
31
+ * Optional maximum token budget
32
+ * Optional strict error handling
33
+ * Basic text cleaning while preserving document metadata
34
+ * Configurable LangChain text splitter
35
+ * Configurable chunk size and chunk overlap
36
+ * Compatible with custom tokenizers and text splitters
37
+
38
+ ## Supported File Types
39
+
40
+ | File type | Loader |
41
+ | --------- | ------------------------- |
42
+ | `.pdf` | `PyPDFLoader` |
43
+ | `.docx` | `Docx2txtLoader` |
44
+ | `.xlsx` | `UnstructuredExcelLoader` |
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install rag-ingestion
50
+ ```
51
+
52
+ ## Basic Usage
53
+
54
+ ```python
55
+ from rag_ingestion import RAGIngection
56
+
57
+ ingestion = RAGIngection()
58
+
59
+ result = ingestion.load([
60
+ "document.pdf",
61
+ "document.docx",
62
+ "spreadsheet.xlsx",
63
+ ])
64
+
65
+ documents = result["documents"]
66
+
67
+ chunks = ingestion.chunk(documents)
68
+ ```
69
+
70
+ The `load()` method returns:
71
+
72
+ ```python
73
+ {
74
+ "documents": [...],
75
+ "total_tokens": ...,
76
+ "files_processed": ...
77
+ }
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ The ingestion component can be configured with optional parameters:
83
+
84
+ ```python
85
+ ingestion = RAGIngection(
86
+ tokenizer=custom_tokenizer,
87
+ max_tokens=4000,
88
+ strict=True,
89
+ chunk_size=1000,
90
+ chunk_overlap=200,
91
+ clean=True,
92
+ )
93
+ ```
94
+
95
+ ### Tokenizer
96
+
97
+ A custom tokenizer can be supplied when required.
98
+
99
+ If no tokenizer is provided, the package uses the default `cl100k_base` encoding from `tiktoken`.
100
+
101
+ ### Token Budget
102
+
103
+ `max_tokens` can be used to limit the total number of extracted tokens processed by the loader.
104
+
105
+ ```python
106
+ ingestion = RAGIngection(max_tokens=4000)
107
+ ```
108
+
109
+ If no limit is provided, token processing is unlimited.
110
+
111
+ ### Strict Mode
112
+
113
+ By default, unsupported or failed files are skipped.
114
+
115
+ To raise an exception instead:
116
+
117
+ ```python
118
+ ingestion = RAGIngection(strict=True)
119
+ ```
120
+
121
+ ### Chunking
122
+
123
+ Documents can be split using the default recursive character text splitter:
124
+
125
+ ```python
126
+ chunks = ingestion.chunk(documents)
127
+ ```
128
+
129
+ Chunking can be configured with:
130
+
131
+ ```python
132
+ ingestion = RAGIngection(
133
+ chunk_size=1000,
134
+ chunk_overlap=200,
135
+ )
136
+ ```
137
+
138
+ A custom LangChain text splitter can also be supplied.
139
+
140
+ ## Pipeline
141
+
142
+ The basic processing flow is:
143
+
144
+ ```text
145
+ Files
146
+ ↓
147
+ Load & Extract
148
+ ↓
149
+ Token Counting
150
+ ↓
151
+ Text Cleaning
152
+ ↓
153
+ Chunking
154
+ ↓
155
+ LangChain Documents
156
+ ```
157
+
158
+ The resulting chunks can then be passed to a retrieval system, vector store, hybrid retriever, or another downstream RAG component.
159
+
160
+ ## Dependencies
161
+
162
+ This package uses components from the LangChain ecosystem together with document loaders and tokenization utilities.
163
+
164
+ The package dependencies are defined in `pyproject.toml`.
165
+
166
+ ## Status
167
+
168
+ This is the initial version of the RAG ingestion component, developed as a modular building block for a larger RAG system.
169
+
170
+ The package is intentionally focused on document ingestion and preprocessing rather than retrieval or generation.
171
+
172
+ ## License
173
+
174
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
@@ -0,0 +1,158 @@
1
+ # RAG Ingestion
2
+
3
+ A lightweight document ingestion component for Retrieval-Augmented Generation (RAG) pipelines.
4
+
5
+ RAG Ingestion handles the initial document-processing stage of a RAG workflow: loading supported files, extracting their content, tracking token usage, cleaning extracted text, and splitting documents into chunks.
6
+
7
+ The package is designed to work with LangChain `Document` objects and can be used as a standalone component or as part of a larger RAG pipeline.
8
+
9
+ ## Features
10
+
11
+ * Load multiple documents from file paths
12
+ * Support for PDF, DOCX, and XLSX files
13
+ * Extract content into LangChain `Document` objects
14
+ * Token counting with `tiktoken`
15
+ * Optional maximum token budget
16
+ * Optional strict error handling
17
+ * Basic text cleaning while preserving document metadata
18
+ * Configurable LangChain text splitter
19
+ * Configurable chunk size and chunk overlap
20
+ * Compatible with custom tokenizers and text splitters
21
+
22
+ ## Supported File Types
23
+
24
+ | File type | Loader |
25
+ | --------- | ------------------------- |
26
+ | `.pdf` | `PyPDFLoader` |
27
+ | `.docx` | `Docx2txtLoader` |
28
+ | `.xlsx` | `UnstructuredExcelLoader` |
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install rag-ingestion
34
+ ```
35
+
36
+ ## Basic Usage
37
+
38
+ ```python
39
+ from rag_ingestion import RAGIngection
40
+
41
+ ingestion = RAGIngection()
42
+
43
+ result = ingestion.load([
44
+ "document.pdf",
45
+ "document.docx",
46
+ "spreadsheet.xlsx",
47
+ ])
48
+
49
+ documents = result["documents"]
50
+
51
+ chunks = ingestion.chunk(documents)
52
+ ```
53
+
54
+ The `load()` method returns:
55
+
56
+ ```python
57
+ {
58
+ "documents": [...],
59
+ "total_tokens": ...,
60
+ "files_processed": ...
61
+ }
62
+ ```
63
+
64
+ ## Configuration
65
+
66
+ The ingestion component can be configured with optional parameters:
67
+
68
+ ```python
69
+ ingestion = RAGIngection(
70
+ tokenizer=custom_tokenizer,
71
+ max_tokens=4000,
72
+ strict=True,
73
+ chunk_size=1000,
74
+ chunk_overlap=200,
75
+ clean=True,
76
+ )
77
+ ```
78
+
79
+ ### Tokenizer
80
+
81
+ A custom tokenizer can be supplied when required.
82
+
83
+ If no tokenizer is provided, the package uses the default `cl100k_base` encoding from `tiktoken`.
84
+
85
+ ### Token Budget
86
+
87
+ `max_tokens` can be used to limit the total number of extracted tokens processed by the loader.
88
+
89
+ ```python
90
+ ingestion = RAGIngection(max_tokens=4000)
91
+ ```
92
+
93
+ If no limit is provided, token processing is unlimited.
94
+
95
+ ### Strict Mode
96
+
97
+ By default, unsupported or failed files are skipped.
98
+
99
+ To raise an exception instead:
100
+
101
+ ```python
102
+ ingestion = RAGIngection(strict=True)
103
+ ```
104
+
105
+ ### Chunking
106
+
107
+ Documents can be split using the default recursive character text splitter:
108
+
109
+ ```python
110
+ chunks = ingestion.chunk(documents)
111
+ ```
112
+
113
+ Chunking can be configured with:
114
+
115
+ ```python
116
+ ingestion = RAGIngection(
117
+ chunk_size=1000,
118
+ chunk_overlap=200,
119
+ )
120
+ ```
121
+
122
+ A custom LangChain text splitter can also be supplied.
123
+
124
+ ## Pipeline
125
+
126
+ The basic processing flow is:
127
+
128
+ ```text
129
+ Files
130
+ ↓
131
+ Load & Extract
132
+ ↓
133
+ Token Counting
134
+ ↓
135
+ Text Cleaning
136
+ ↓
137
+ Chunking
138
+ ↓
139
+ LangChain Documents
140
+ ```
141
+
142
+ The resulting chunks can then be passed to a retrieval system, vector store, hybrid retriever, or another downstream RAG component.
143
+
144
+ ## Dependencies
145
+
146
+ This package uses components from the LangChain ecosystem together with document loaders and tokenization utilities.
147
+
148
+ The package dependencies are defined in `pyproject.toml`.
149
+
150
+ ## Status
151
+
152
+ This is the initial version of the RAG ingestion component, developed as a modular building block for a larger RAG system.
153
+
154
+ The package is intentionally focused on document ingestion and preprocessing rather than retrieval or generation.
155
+
156
+ ## License
157
+
158
+ This project is licensed under the MIT License. See the `LICENSE` file for details.
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "rag-ingestion"
3
+ version = "0.1.0"
4
+ description = "A lightweight document ingestion component for RAG pipelines."
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ dependencies = [
8
+ "docx2txt>=0.9",
9
+ "langchain-community>=0.4.2",
10
+ "langchain-text-splitters>=1.1.2",
11
+ "openpyxl>=3.1.5",
12
+ "pypdf>=6.18.0",
13
+ "python-docx>=1.2.0",
14
+ "tiktoken>=0.14.0",
15
+ "unstructured[xlsx]>=0.27.5",
16
+ ]
17
+
18
+ [[project.authors]]
19
+ name = "satyampandya"
20
+
21
+ [build-system]
22
+ requires = ["uv_build>=0.12.10,<0.13.0"]
23
+ build-backend = "uv_build"
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "rag-ingestion"
3
+ version = "0.1.0"
4
+ description = "A lightweight document ingestion component for RAG pipelines."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "satyampandya" }
8
+ ]
9
+ requires-python = ">=3.13"
10
+ dependencies = [
11
+ "docx2txt>=0.9",
12
+ "langchain-community>=0.4.2",
13
+ "langchain-text-splitters>=1.1.2",
14
+ "openpyxl>=3.1.5",
15
+ "pypdf>=6.18.0",
16
+ "python-docx>=1.2.0",
17
+ "tiktoken>=0.14.0",
18
+ "unstructured[xlsx]>=0.27.5",
19
+ ]
20
+
21
+ [build-system]
22
+ requires = ["uv_build>=0.12.10,<0.13.0"]
23
+ build-backend = "uv_build"
@@ -0,0 +1,7 @@
1
+ """Public API for the RAG ingestion package."""
2
+
3
+ # The implementation module/class retain their original spelling for
4
+ # compatibility; export the correctly spelled public API used by callers.
5
+ from .ingestion import RAGIngestion
6
+
7
+ __all__ = ["RAGIngestion"]
@@ -0,0 +1,166 @@
1
+ import tiktoken
2
+ from pathlib import Path
3
+ from typing import Any, Sequence
4
+ from langchain_core.documents import Document
5
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
6
+ from langchain_community.document_loaders import (
7
+ Docx2txtLoader,
8
+ PyPDFLoader,
9
+ UnstructuredExcelLoader
10
+ )
11
+
12
+ class RAGIngestion:
13
+ """Identify uploaded files and extract their content."""
14
+ LOADERS = {
15
+ ".pdf": PyPDFLoader,
16
+ ".docx": Docx2txtLoader,
17
+ ".xlsx": UnstructuredExcelLoader
18
+ }
19
+
20
+ def __init__(
21
+ self,
22
+ tokenizer: Any | None = None,
23
+ max_tokens: int | None = None,
24
+ strict: bool = False,
25
+ text_splitter: Any | None = None,
26
+ chunk_size: int = 1000,
27
+ chunk_overlap: int = 200,
28
+ clean: bool = True
29
+ ) -> None:
30
+
31
+ """
32
+ Initialize the ingestion pipeline.
33
+
34
+ Args:
35
+ tokenizer:
36
+ Optional tokenizer. If omitted, the default tiktoken
37
+ encoding is used when token counting is required.
38
+
39
+ max_tokens:
40
+ Maximum number of tokens to process. None means unlimited.
41
+
42
+ strict:
43
+ If True, raise an exception when a file cannot be processed.
44
+ If False, skip unsupported/failed files.
45
+
46
+ text_splitter:
47
+ Optional custom LangChain text splitter.
48
+
49
+ chunk_size:
50
+ Default chunk size.
51
+
52
+ chunk_overlap:
53
+ Default chunk overlap.
54
+
55
+ clean:
56
+ If True, clean documents before chunking.
57
+ """
58
+
59
+ self.max_tokens = max_tokens
60
+ self.strict = strict
61
+ self.tokenizer = tokenizer or tiktoken.get_encoding("cl100k_base")
62
+
63
+ self.text_splitter = text_splitter or RecursiveCharacterTextSplitter(
64
+ chunk_size=chunk_size,
65
+ chunk_overlap=chunk_overlap
66
+ )
67
+ self.clean_enabled = clean
68
+
69
+ def load(
70
+ self,
71
+ files: str | Path | Sequence[str | Path]
72
+ ) -> dict:
73
+ """
74
+ Load one or multiple files and extract their content.
75
+
76
+ Args:
77
+ files:
78
+ A single file path or a list of file paths.
79
+
80
+ Returns:
81
+ Dictionary containing:
82
+ documents:
83
+ Extracted LangChain Document objects.
84
+
85
+ total_tokens:
86
+ Total number of extracted tokens.
87
+
88
+ files_processed:
89
+ Number of successfully processed files.
90
+ """
91
+ if isinstance(files, (str, Path)):
92
+ files = [files]
93
+
94
+ documents = []
95
+ total_tokens = 0
96
+ files_processed = 0
97
+
98
+ for file in files:
99
+ file_path = Path(file)
100
+ extension = file_path.suffix.lower()
101
+
102
+ if extension not in self.LOADERS:
103
+ if self.strict:
104
+ raise ValueError(
105
+ f"Unsupported file type: {extension}"
106
+ )
107
+ continue
108
+
109
+ try:
110
+ loader = self.LOADERS[extension](str(file_path))
111
+ file_documents = loader.load()
112
+ file_tokens = sum(len(self.tokenizer.encode(document.page_content))for document in file_documents)
113
+ if (
114
+ self.max_tokens is not None and
115
+ total_tokens + file_tokens > self.max_tokens
116
+ ):
117
+ break
118
+
119
+ documents.extend(file_documents)
120
+ total_tokens += file_tokens
121
+ files_processed += 1
122
+ except Exception:
123
+ if self.strict:
124
+ raise
125
+
126
+ return {
127
+ "documents": documents,
128
+ "total_tokens": total_tokens,
129
+ "files_processed": files_processed
130
+ }
131
+
132
+ def clean(self, documents: list[Document]) -> list[Document]:
133
+ """Perform basic cleaning while preserving document metadata."""
134
+ cleaned_documents = []
135
+
136
+ for document in documents:
137
+ text = document.page_content
138
+ text = text.replace("\r\n", "\n").replace("\r", "\n")
139
+ lines = [line.strip() for line in text.split("\n")]
140
+ text = "\n".join(line for line in lines if line).strip()
141
+
142
+ if not text:
143
+ continue
144
+
145
+ cleaned_documents.append(
146
+ Document(
147
+ page_content=text,
148
+ metadata=document.metadata.copy()
149
+ )
150
+ )
151
+
152
+ return cleaned_documents
153
+
154
+ def chunk(
155
+ self,
156
+ documents: list[Document],
157
+ text_splitter: Any | None = None,
158
+ ) -> list[Document]:
159
+ """Split documents using a LangChain text splitter."""
160
+
161
+ if self.clean_enabled:
162
+ documents = self.clean(documents)
163
+
164
+ splitter = text_splitter or self.text_splitter
165
+
166
+ return splitter.split_documents(documents)