prod-rag 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.
- prod_rag-0.1.0/LICENSE +21 -0
- prod_rag-0.1.0/PKG-INFO +441 -0
- prod_rag-0.1.0/README.md +401 -0
- prod_rag-0.1.0/prod_rag.egg-info/PKG-INFO +441 -0
- prod_rag-0.1.0/prod_rag.egg-info/SOURCES.txt +36 -0
- prod_rag-0.1.0/prod_rag.egg-info/dependency_links.txt +1 -0
- prod_rag-0.1.0/prod_rag.egg-info/requires.txt +20 -0
- prod_rag-0.1.0/prod_rag.egg-info/top_level.txt +1 -0
- prod_rag-0.1.0/pyproject.toml +98 -0
- prod_rag-0.1.0/rag_core/__init__.py +79 -0
- prod_rag-0.1.0/rag_core/chunking/__init__.py +10 -0
- prod_rag-0.1.0/rag_core/chunking/chunking_service.py +98 -0
- prod_rag-0.1.0/rag_core/chunking/text_splitter.py +184 -0
- prod_rag-0.1.0/rag_core/config.py +157 -0
- prod_rag-0.1.0/rag_core/embeddings/__init__.py +11 -0
- prod_rag-0.1.0/rag_core/embeddings/base.py +52 -0
- prod_rag-0.1.0/rag_core/embeddings/factory.py +30 -0
- prod_rag-0.1.0/rag_core/embeddings/jina_provider.py +166 -0
- prod_rag-0.1.0/rag_core/exceptions.py +37 -0
- prod_rag-0.1.0/rag_core/generation/__init__.py +8 -0
- prod_rag-0.1.0/rag_core/generation/generation_service.py +142 -0
- prod_rag-0.1.0/rag_core/ingestion/__init__.py +12 -0
- prod_rag-0.1.0/rag_core/ingestion/ingestion_pipeline.py +118 -0
- prod_rag-0.1.0/rag_core/ingestion/ingestion_service.py +119 -0
- prod_rag-0.1.0/rag_core/ingestion/pdf_parser.py +91 -0
- prod_rag-0.1.0/rag_core/llm/__init__.py +9 -0
- prod_rag-0.1.0/rag_core/llm/base.py +21 -0
- prod_rag-0.1.0/rag_core/llm/openai_compat.py +104 -0
- prod_rag-0.1.0/rag_core/models/__init__.py +13 -0
- prod_rag-0.1.0/rag_core/models/chunk.py +51 -0
- prod_rag-0.1.0/rag_core/models/document.py +35 -0
- prod_rag-0.1.0/rag_core/models/retrieval.py +25 -0
- prod_rag-0.1.0/rag_core/pipeline.py +290 -0
- prod_rag-0.1.0/rag_core/retrieval/__init__.py +7 -0
- prod_rag-0.1.0/rag_core/retrieval/retrieval_service.py +75 -0
- prod_rag-0.1.0/rag_core/vector_stores/__init__.py +7 -0
- prod_rag-0.1.0/rag_core/vector_stores/qdrant_store.py +276 -0
- prod_rag-0.1.0/setup.cfg +4 -0
prod_rag-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Production RAG Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
prod_rag-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prod-rag
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Production-ready RAG library with document ingestion, chunking, embedding, and grounded answer generation
|
|
5
|
+
Author: Production RAG Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/1divyanshdev/production-rag
|
|
8
|
+
Project-URL: Repository, https://github.com/1divyanshdev/production-rag
|
|
9
|
+
Project-URL: Issues, https://github.com/1divyanshdev/production-rag/issues
|
|
10
|
+
Keywords: rag,retrieval,embeddings,llm,document-processing,vector-search
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pymupdf>=1.23.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: qdrant-client>=1.10.0
|
|
26
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
27
|
+
Provides-Extra: api
|
|
28
|
+
Requires-Dist: fastapi>=0.115.0; extra == "api"
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == "api"
|
|
30
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "api"
|
|
31
|
+
Requires-Dist: python-dotenv>=1.0.0; extra == "api"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: prod-rag[api,dev]; extra == "all"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# production-rag
|
|
42
|
+
|
|
43
|
+
[](https://github.com/1divyanshdev/production-rag/actions/workflows/test.yml)
|
|
44
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](https://opensource.org/licenses/MIT)
|
|
46
|
+
|
|
47
|
+
A production-oriented, full-stack **RAG (Retrieval-Augmented Generation) platform** — not a "chat with PDF" demo. Upload documents, index them through a transparent pipeline, ask questions, and get grounded answers with page-level citations and honest refusals when the corpus can't support an answer.
|
|
48
|
+
|
|
49
|
+
**Two ways to use this project:**
|
|
50
|
+
|
|
51
|
+
1. **As a reusable Python library** (`rag_core`) — install in your own application, no FastAPI required
|
|
52
|
+
2. **As a full-stack application** — Next.js frontend + FastAPI backend + Qdrant vector store
|
|
53
|
+
|
|
54
|
+
## Table of Contents
|
|
55
|
+
|
|
56
|
+
- [Using rag_core as a Library](#using-rag_core-as-a-library)
|
|
57
|
+
- [Using the Full-Stack Application](#using-the-full-stack-application)
|
|
58
|
+
- [Architecture](#architecture)
|
|
59
|
+
- [Project Structure](#project-structure)
|
|
60
|
+
- [Pipeline](#pipeline)
|
|
61
|
+
- [API](#api)
|
|
62
|
+
- [Testing](#testing)
|
|
63
|
+
- [Documentation](#documentation)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Using rag_core as a Library
|
|
68
|
+
|
|
69
|
+
The `rag_core` package is a standalone, reusable Python library for building RAG systems. Install it in any Python project — no FastAPI, no web server required.
|
|
70
|
+
|
|
71
|
+
### Installation
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
# Install from source (editable mode)
|
|
75
|
+
uv pip install -e .
|
|
76
|
+
|
|
77
|
+
# Or in your own project
|
|
78
|
+
uv pip install -e path/to/production-rag
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Quick Start
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from rag_core import RAGPipeline, RAGConfig
|
|
85
|
+
from rag_core import EmbeddingSettings, LLMSettings
|
|
86
|
+
from dotenv import load_dotenv
|
|
87
|
+
|
|
88
|
+
# Load environment variables
|
|
89
|
+
load_dotenv()
|
|
90
|
+
|
|
91
|
+
# Or configure explicitly
|
|
92
|
+
from rag_core.config import EmbeddingSettings, LLMSettings
|
|
93
|
+
|
|
94
|
+
config = RAGConfig(
|
|
95
|
+
embedding_settings=EmbeddingSettings(
|
|
96
|
+
api_key="your-jina-key",
|
|
97
|
+
provider="jina"
|
|
98
|
+
),
|
|
99
|
+
llm_settings=LLMSettings(
|
|
100
|
+
api_key="your-llm-key",
|
|
101
|
+
base_url="https://api.groq.com/openai/v1",
|
|
102
|
+
model="openai/gpt-oss-20b" # Free tier model
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Initialize
|
|
107
|
+
pipeline = RAGPipeline(config)
|
|
108
|
+
|
|
109
|
+
# Index a document
|
|
110
|
+
result = pipeline.index_document("paper.pdf")
|
|
111
|
+
print(f"Indexed {result.indexed_chunks} chunks from {result.filename}")
|
|
112
|
+
|
|
113
|
+
# Ask questions
|
|
114
|
+
answer = pipeline.query("What are the key findings?", top_k=5)
|
|
115
|
+
|
|
116
|
+
if answer.refused:
|
|
117
|
+
print(f"Cannot answer: {answer.answer}")
|
|
118
|
+
else:
|
|
119
|
+
print(f"Answer: {answer.answer}")
|
|
120
|
+
for i, source in enumerate(answer.sources, 1):
|
|
121
|
+
print(f" [{i}] {source['filename']} (page {source['page_number']})")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Features
|
|
125
|
+
|
|
126
|
+
- **Standalone** — No FastAPI dependencies; use in CLI tools, notebooks, batch jobs
|
|
127
|
+
- **Provider flexibility** — Swap embedding and LLM providers via configuration
|
|
128
|
+
- **Typed API** — Full type hints and Pydantic validation
|
|
129
|
+
- **Production-ready** — Deterministic IDs, idempotent indexing, error handling
|
|
130
|
+
- **Testable** — 177 offline tests; fake providers for unit testing
|
|
131
|
+
|
|
132
|
+
### Documentation
|
|
133
|
+
|
|
134
|
+
- **[Python Library Guide](docs/PYTHON_LIBRARY_GUIDE.md)** — Comprehensive 600+ line guide covering architecture, execution flows, configuration, design decisions, and extension patterns
|
|
135
|
+
- **[Examples](examples/)** — 5 runnable examples from basic usage to standalone applications
|
|
136
|
+
- **[Test Suite](tests/library/)** — Library-specific test suite demonstrating usage patterns
|
|
137
|
+
|
|
138
|
+
### When to Use the Library
|
|
139
|
+
|
|
140
|
+
Use `rag_core` directly when:
|
|
141
|
+
|
|
142
|
+
- Building a CLI tool or batch processor
|
|
143
|
+
- Integrating RAG into an existing application
|
|
144
|
+
- Running in Jupyter notebooks or research scripts
|
|
145
|
+
- You don't need a REST API or web interface
|
|
146
|
+
- You want full control over the RAG pipeline
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Using the Full-Stack Application
|
|
151
|
+
|
|
152
|
+
The full-stack application combines `rag_core` with a FastAPI backend and Next.js frontend for a complete web-based RAG platform.
|
|
153
|
+
|
|
154
|
+
### Architecture
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
PDF upload → ingestion → parsing/cleaning → chunking → embeddings → Qdrant
|
|
158
|
+
↓
|
|
159
|
+
Next.js frontend ← FastAPI /api/ask ← retrieval ← grounded generation ←┘
|
|
160
|
+
(landing, upload, /api/documents (citations, refusal on
|
|
161
|
+
chat, sources UI) low confidence)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The backend (`backend/app/`) is a thin adapter layer that wraps `rag_core` with FastAPI routes and dependency injection. All RAG logic lives in the reusable library.
|
|
165
|
+
|
|
166
|
+
## Project Structure
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
production-rag/
|
|
170
|
+
├── rag_core/ # 🆕 Reusable Python library (NO FastAPI)
|
|
171
|
+
│ ├── __init__.py # Public API: RAGPipeline, RAGConfig
|
|
172
|
+
│ ├── pipeline.py # Main RAGPipeline orchestration
|
|
173
|
+
│ ├── config.py # Settings classes for all components
|
|
174
|
+
│ ├── exceptions.py # Typed domain exceptions
|
|
175
|
+
│ ├── models/
|
|
176
|
+
│ │ ├── document.py # Document + chunk data models
|
|
177
|
+
│ │ └── retrieval.py # Retrieval result models
|
|
178
|
+
│ ├── ingestion/
|
|
179
|
+
│ │ ├── ingestion_service.py # SHA-256 dedup, per-page extraction
|
|
180
|
+
│ │ ├── ingestion_pipeline.py # Orchestrates full index pipeline
|
|
181
|
+
│ │ └── pdf_parser.py # PyMuPDF parsing
|
|
182
|
+
│ ├── chunking/
|
|
183
|
+
│ │ ├── text_splitter.py # Recursive-separator splitter
|
|
184
|
+
│ │ └── chunking_service.py # Char offsets, overlap, lineage
|
|
185
|
+
│ ├── embeddings/
|
|
186
|
+
│ │ ├── base.py # EmbeddingProvider ABC
|
|
187
|
+
│ │ ├── factory.py # Provider selection by config
|
|
188
|
+
│ │ └── jina_provider.py # Jina AI implementation
|
|
189
|
+
│ ├── llm/
|
|
190
|
+
│ │ ├── base.py # LLMProvider ABC
|
|
191
|
+
│ │ └── openai_compat.py # OpenAI-compatible chat
|
|
192
|
+
│ ├── vector_stores/
|
|
193
|
+
│ │ └── vector_store.py # Qdrant (embedded/server modes)
|
|
194
|
+
│ ├── retrieval/
|
|
195
|
+
│ │ └── retrieval_service.py # Query → top-k chunks
|
|
196
|
+
│ └── generation/
|
|
197
|
+
│ └── generation_service.py # Prompting, citations, refusal
|
|
198
|
+
├── backend/ # FastAPI adapter (thin wrapper over rag_core)
|
|
199
|
+
│ └── app/
|
|
200
|
+
│ ├── main.py # FastAPI app factory, CORS, exception mapping
|
|
201
|
+
│ ├── api/
|
|
202
|
+
│ │ ├── routes.py # All API endpoints
|
|
203
|
+
│ │ ├── schemas.py # Pydantic request/response models
|
|
204
|
+
│ │ └── deps.py # Dependency injection (services)
|
|
205
|
+
│ └── core/
|
|
206
|
+
│ └── config.py # FastAPI-specific settings
|
|
207
|
+
├── frontend/ # Next.js 15 + React 19 App Router
|
|
208
|
+
│ ├── app/
|
|
209
|
+
│ │ ├── page.tsx # Landing page
|
|
210
|
+
│ │ ├── console/page.tsx # Q&A interface with citations
|
|
211
|
+
│ │ └── playground/page.tsx # Document manager
|
|
212
|
+
│ ├── components/
|
|
213
|
+
│ │ ├── EvidenceDemo.tsx # Citation UI components
|
|
214
|
+
│ │ └── Reveal.tsx # Scroll-reveal animation
|
|
215
|
+
│ └── lib/
|
|
216
|
+
│ ├── api.ts # Typed API client (mirrors backend schemas)
|
|
217
|
+
│ └── site.ts # Site-wide constants
|
|
218
|
+
├── tests/
|
|
219
|
+
│ ├── library/ # 🆕 Library-specific tests (35 tests)
|
|
220
|
+
│ │ ├── test_public_api.py # RAGPipeline API testing
|
|
221
|
+
│ │ └── test_configuration.py # Config validation
|
|
222
|
+
│ └── unit/ # Component unit tests (142 tests)
|
|
223
|
+
├── examples/ # 🆕 Runnable library examples
|
|
224
|
+
│ ├── 01_basic_usage.py # Quickstart
|
|
225
|
+
│ ├── 02_advanced_configuration.py # Customization
|
|
226
|
+
│ ├── 03_citations_and_sources.py # Working with results
|
|
227
|
+
│ ├── 04_error_handling.py # Robust patterns
|
|
228
|
+
│ ├── 05_standalone_application.py # Library independence
|
|
229
|
+
│ └── README.md # Example usage guide
|
|
230
|
+
├── docs/
|
|
231
|
+
│ └── PYTHON_LIBRARY_GUIDE.md # 🆕 Comprehensive library documentation
|
|
232
|
+
├── scripts/
|
|
233
|
+
│ └── demo_rag.py # Full offline pipeline demo (no keys needed)
|
|
234
|
+
├── pyproject.toml # 🆕 Package metadata for rag_core
|
|
235
|
+
├── Dockerfile
|
|
236
|
+
├── docker-compose.yml
|
|
237
|
+
└── .env.example
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Key architectural decision:** The library (`rag_core/`) contains ALL RAG logic and has zero web framework dependencies. The backend (`backend/app/`) is a thin adapter that exposes `rag_core` via REST endpoints. This means:
|
|
241
|
+
|
|
242
|
+
- You can use `rag_core` standalone without running the FastAPI server
|
|
243
|
+
- The backend delegates all RAG operations to the library
|
|
244
|
+
- Tests for core RAG logic don't require FastAPI or HTTP mocking
|
|
245
|
+
|
|
246
|
+
## Pipeline
|
|
247
|
+
|
|
248
|
+
Every stage is a typed, modular component in `rag_core/`, each with its own offline test suite. External providers (embeddings, LLM) sit behind abstract interfaces and are switched by configuration.
|
|
249
|
+
|
|
250
|
+
| Stage | Module | What it does |
|
|
251
|
+
|---|---|---|
|
|
252
|
+
| Ingestion | `rag_core/ingestion/ingestion_service.py` | SHA-256 hashing, per-page extraction, empty-page policy |
|
|
253
|
+
| PDF parsing | `rag_core/ingestion/pdf_parser.py` | PyMuPDF, encrypted/corrupt detection |
|
|
254
|
+
| Chunking | `rag_core/chunking/text_splitter.py`, `chunking_service.py` | Recursive-separator splitter with exact char offsets and overlap; full document lineage kept |
|
|
255
|
+
| Embeddings | `rag_core/embeddings/` | `EmbeddingProvider` ABC + Jina AI implementation (batching, retries, backoff) |
|
|
256
|
+
| Vector store | `rag_core/vector_stores/vector_store.py` | Qdrant — embedded local mode **or** server mode; hash-derived point IDs (idempotent re-index), metadata filtering |
|
|
257
|
+
| Retrieval | `rag_core/retrieval/retrieval_service.py` | query → vector → top-k `ScoredChunk`s |
|
|
258
|
+
| Generation | `rag_core/generation/generation_service.py` | numbered-context prompting, `[n]` citations, refusal below confidence threshold |
|
|
259
|
+
| LLM | `rag_core/llm/` | OpenAI-compatible chat (Groq / OpenRouter / Cohere-compat — all have free tiers) |
|
|
260
|
+
|
|
261
|
+
## Quickstart (Full-Stack Application)
|
|
262
|
+
|
|
263
|
+
Requires Python 3.11+.
|
|
264
|
+
|
|
265
|
+
```powershell
|
|
266
|
+
# 1. Install dependencies
|
|
267
|
+
uv venv --python 3.11 .venv
|
|
268
|
+
uv pip install -r requirements.txt -r requirements-dev.txt
|
|
269
|
+
|
|
270
|
+
# Or install the library in editable mode
|
|
271
|
+
uv pip install -e .
|
|
272
|
+
|
|
273
|
+
# 2. Configuration
|
|
274
|
+
Copy-Item .env.example .env # then fill in your keys (see below)
|
|
275
|
+
|
|
276
|
+
# 3. Run tests (fully offline - no keys needed)
|
|
277
|
+
.venv\Scripts\python.exe -m pytest -v
|
|
278
|
+
|
|
279
|
+
# 4. Start the API
|
|
280
|
+
.venv\Scripts\python.exe -m uvicorn backend.app.main:app --reload
|
|
281
|
+
# Interactive docs: http://127.0.0.1:8000/docs
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### API keys (free tiers, no credit card)
|
|
285
|
+
|
|
286
|
+
| Key | Where | Used by |
|
|
287
|
+
|---|---|---|
|
|
288
|
+
| `EMBEDDING_API_KEY` | [jina.ai](https://jina.ai) → API Key | `/api/documents` (indexing) |
|
|
289
|
+
| `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` | e.g. [console.groq.com](https://console.groq.com) (`https://api.groq.com/openai/v1`, `openai/gpt-oss-20b`) | `/api/ask` |
|
|
290
|
+
|
|
291
|
+
Keys live in `.env` (gitignored) — never in code, tests, or logs. **The API works partially configured:** uploading needs only the embedding key; the LLM stack is built lazily on first question.
|
|
292
|
+
|
|
293
|
+
### Offline demo (no keys at all)
|
|
294
|
+
|
|
295
|
+
```powershell
|
|
296
|
+
# Demo the full RAG pipeline with deterministic fakes
|
|
297
|
+
.venv\Scripts\python.exe scripts\demo_rag.py
|
|
298
|
+
|
|
299
|
+
# Or try library examples
|
|
300
|
+
.venv\Scripts\python.exe examples\01_basic_usage.py
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The script runs the full pipeline — index, retrieve, answer, refuse — with deterministic fakes. See [examples/README.md](examples/README.md) for all available examples.
|
|
304
|
+
|
|
305
|
+
## Docker
|
|
306
|
+
|
|
307
|
+
```powershell
|
|
308
|
+
Copy-Item .env.example .env # add keys first
|
|
309
|
+
docker compose up --build
|
|
310
|
+
# API: http://localhost:8000/docs
|
|
311
|
+
# Qdrant: http://localhost:6333
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Compose runs Qdrant in **server mode** (`VECTOR_STORE_URL=http://qdrant:6333`) — the production shape — while local development defaults to embedded mode with zero infrastructure.
|
|
315
|
+
|
|
316
|
+
## Frontend (Next.js)
|
|
317
|
+
|
|
318
|
+
React 19 + Next.js 15 App Router in `frontend/`. Three views:
|
|
319
|
+
|
|
320
|
+
- **Landing** (`/`) — hero, feature overview, how-it-works, use cases, roadmap
|
|
321
|
+
- **Console** (`/console`) — question box, grounded answer with clickable `[n]` citation markers, source list with page numbers and similarity scores, refusal banner when confidence is low
|
|
322
|
+
- **Playground** (`/playground`) — drag-and-drop PDF upload (duplicate-hash skips shown), document table with chunk counts, per-document delete
|
|
323
|
+
|
|
324
|
+
```powershell
|
|
325
|
+
cd frontend
|
|
326
|
+
npm install
|
|
327
|
+
Copy-Item .env.example .env.local # points at http://127.0.0.1:8000
|
|
328
|
+
npm run dev # http://localhost:3000
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Requires the backend API running. The typed API client in `frontend/lib/api.ts` mirrors the backend schemas one-to-one.
|
|
332
|
+
|
|
333
|
+
## API
|
|
334
|
+
|
|
335
|
+
| Method | Route | Description |
|
|
336
|
+
|---|---|---|
|
|
337
|
+
| GET | `/api/health` | liveness — returns `{ status, version }` |
|
|
338
|
+
| POST | `/api/documents` | multipart PDF upload → parse → chunk → embed → index (50 MB limit, dedup by file hash) |
|
|
339
|
+
| GET | `/api/documents` | list indexed documents with chunk counts |
|
|
340
|
+
| DELETE | `/api/documents/{id}` | remove all chunks for a document |
|
|
341
|
+
| POST | `/api/ask` | grounded answer + sources + `refused` flag |
|
|
342
|
+
| GET | `/api/stats` | collection name and total chunk count |
|
|
343
|
+
|
|
344
|
+
### Error handling
|
|
345
|
+
|
|
346
|
+
Domain exceptions are mapped to precise HTTP status codes at the app boundary — nothing below the API layer touches HTTP:
|
|
347
|
+
|
|
348
|
+
| Exception | HTTP status |
|
|
349
|
+
|---|---|
|
|
350
|
+
| `PDFParseError` | 400 |
|
|
351
|
+
| `NoTextExtractedError` | 422 |
|
|
352
|
+
| `EmbeddingError` | 502 |
|
|
353
|
+
| `LLMError` | 502 / 503 |
|
|
354
|
+
| `VectorStoreError` | 500 |
|
|
355
|
+
|
|
356
|
+
All exceptions are defined in `rag_core/exceptions.py` and mapped to HTTP status codes at the FastAPI boundary in `backend/app/main.py`.
|
|
357
|
+
|
|
358
|
+
## LLM Provider
|
|
359
|
+
|
|
360
|
+
The LLM layer (`rag_core/llm/openai_compat.py`) works with any OpenAI-compatible endpoint — the same `POST /chat/completions` shape covers Groq, OpenRouter, and Cohere's compatibility endpoint. Features:
|
|
361
|
+
|
|
362
|
+
- Exponential backoff with 3 retries on 429 / 5xx
|
|
363
|
+
- Lazy initialization — the client is only built when the first question arrives
|
|
364
|
+
- Config-only provider switching via `LLM_BASE_URL` + `LLM_MODEL`
|
|
365
|
+
|
|
366
|
+
Use the same provider in standalone library mode or through the FastAPI backend.
|
|
367
|
+
|
|
368
|
+
## Testing
|
|
369
|
+
|
|
370
|
+
177 offline tests across library and unit test suites — deterministic PDF fixtures, in-memory Qdrant, mocked HTTP transports. No test depends on a paid API.
|
|
371
|
+
|
|
372
|
+
```powershell
|
|
373
|
+
# Run all tests
|
|
374
|
+
.venv\Scripts\python.exe -m pytest -v
|
|
375
|
+
|
|
376
|
+
# Run only library tests (RAGPipeline API, configuration)
|
|
377
|
+
.venv\Scripts\python.exe -m pytest tests/library/ -v
|
|
378
|
+
|
|
379
|
+
# Run only unit tests (individual components)
|
|
380
|
+
.venv\Scripts\python.exe -m pytest tests/unit/ -v
|
|
381
|
+
|
|
382
|
+
# Run with coverage
|
|
383
|
+
.venv\Scripts\python.exe -m pytest --cov=rag_core --cov=backend tests/
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Test breakdown:**
|
|
387
|
+
- `tests/library/` — 35 tests for `rag_core` public API (RAGPipeline, RAGConfig, library independence)
|
|
388
|
+
- `tests/unit/` — 142 tests for individual components (chunking, embeddings, retrieval, generation, etc.)
|
|
389
|
+
|
|
390
|
+
All tests use deterministic fakes (`FakeEmbeddingProvider`, `FakeLLMProvider`) defined in `tests/conftest.py` for reproducible, offline testing.
|
|
391
|
+
|
|
392
|
+
## Documentation
|
|
393
|
+
|
|
394
|
+
- **[Python Library Guide](docs/PYTHON_LIBRARY_GUIDE.md)** — Comprehensive 600+ line guide covering:
|
|
395
|
+
- Complete architecture and execution flows (with mermaid diagrams)
|
|
396
|
+
- Public API reference and configuration options
|
|
397
|
+
- Design decisions and rationale
|
|
398
|
+
- Extension patterns for custom providers
|
|
399
|
+
- Testing strategies and debugging tips
|
|
400
|
+
- Complete build walkthrough
|
|
401
|
+
|
|
402
|
+
- **[Examples](examples/)** — 5 runnable examples with explanations:
|
|
403
|
+
- Basic usage (quickstart pattern)
|
|
404
|
+
- Advanced configuration (provider customization)
|
|
405
|
+
- Citations and sources (working with results)
|
|
406
|
+
- Error handling (robust patterns)
|
|
407
|
+
- Standalone application (library independence demo)
|
|
408
|
+
|
|
409
|
+
- **[API Documentation](http://127.0.0.1:8000/docs)** — Interactive OpenAPI docs (when backend is running)
|
|
410
|
+
|
|
411
|
+
## Roadmap
|
|
412
|
+
|
|
413
|
+
- [ ] Hybrid search (dense + BM25) and reranking (Jina reranker)
|
|
414
|
+
- [ ] Retrieval evaluation harness (precision, recall metrics)
|
|
415
|
+
- [ ] CI/CD (GitHub Actions)
|
|
416
|
+
- [ ] Multi-user / auth layer
|
|
417
|
+
- [x] **Reusable Python library** (`rag_core`) with comprehensive documentation
|
|
418
|
+
- [x] Next.js frontend (landing page, upload UI, chat with clickable citations)
|
|
419
|
+
- [x] Docker Compose stack with Qdrant server mode
|
|
420
|
+
- [x] OpenAI-compatible LLM provider (Groq, OpenRouter)
|
|
421
|
+
|
|
422
|
+
## Project principles
|
|
423
|
+
|
|
424
|
+
**Library-first architecture** · typed data structures · modular replaceable providers · meaningful error translation at boundaries · config via env vars only · offline-deterministic tests · no technology without a reason.
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Contributing
|
|
429
|
+
|
|
430
|
+
This project maintains a clean separation between:
|
|
431
|
+
- **`rag_core/`** — Reusable library (no web framework dependencies)
|
|
432
|
+
- **`backend/`** — FastAPI adapter (thin wrapper exposing REST endpoints)
|
|
433
|
+
- **`frontend/`** — Next.js UI (consumes REST API)
|
|
434
|
+
|
|
435
|
+
When adding features:
|
|
436
|
+
1. Implement core logic in `rag_core/` with unit tests
|
|
437
|
+
2. Expose via `RAGPipeline` public API if needed
|
|
438
|
+
3. Add FastAPI routes in `backend/` only for web access
|
|
439
|
+
4. Update documentation and examples as appropriate
|
|
440
|
+
|
|
441
|
+
Run `pytest -v` before submitting changes to ensure no regressions.
|