ragkit-dev 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.
Files changed (49) hide show
  1. ragkit_dev-0.1.0/CHANGELOG.md +86 -0
  2. ragkit_dev-0.1.0/LICENSE +21 -0
  3. ragkit_dev-0.1.0/PKG-INFO +448 -0
  4. ragkit_dev-0.1.0/README.md +394 -0
  5. ragkit_dev-0.1.0/hatch_build.py +17 -0
  6. ragkit_dev-0.1.0/pyproject.toml +118 -0
  7. ragkit_dev-0.1.0/src/rag_kit/__init__.py +44 -0
  8. ragkit_dev-0.1.0/src/rag_kit/__main__.py +5 -0
  9. ragkit_dev-0.1.0/src/rag_kit/api.py +155 -0
  10. ragkit_dev-0.1.0/src/rag_kit/cli.py +484 -0
  11. ragkit_dev-0.1.0/src/rag_kit/env.py +84 -0
  12. ragkit_dev-0.1.0/src/rag_kit/errors.py +37 -0
  13. ragkit_dev-0.1.0/src/rag_kit/log.py +42 -0
  14. ragkit_dev-0.1.0/src/rag_kit/pdf_font_repair.py +132 -0
  15. ragkit_dev-0.1.0/src/rag_kit/pipeline.py +169 -0
  16. ragkit_dev-0.1.0/src/rag_kit/py.typed +0 -0
  17. ragkit_dev-0.1.0/src/rag_kit/settings.py +247 -0
  18. ragkit_dev-0.1.0/src/rag_kit/step01_load_pdf.py +38 -0
  19. ragkit_dev-0.1.0/src/rag_kit/step02_extract_text.py +138 -0
  20. ragkit_dev-0.1.0/src/rag_kit/step03_clean_text.py +76 -0
  21. ragkit_dev-0.1.0/src/rag_kit/step04_chunking.py +36 -0
  22. ragkit_dev-0.1.0/src/rag_kit/step05_embedding.py +77 -0
  23. ragkit_dev-0.1.0/src/rag_kit/step06_vector_store.py +251 -0
  24. ragkit_dev-0.1.0/src/rag_kit/step08_query_embedding.py +17 -0
  25. ragkit_dev-0.1.0/src/rag_kit/step09_retrieve.py +34 -0
  26. ragkit_dev-0.1.0/src/rag_kit/step10_generate.py +84 -0
  27. ragkit_dev-0.1.0/src/rag_kit/step11_answer.py +60 -0
  28. ragkit_dev-0.1.0/src/rag_kit/ui/__init__.py +1 -0
  29. ragkit_dev-0.1.0/src/rag_kit/ui/app.py +153 -0
  30. ragkit_dev-0.1.0/tests/__init__.py +0 -0
  31. ragkit_dev-0.1.0/tests/conftest.py +96 -0
  32. ragkit_dev-0.1.0/tests/fixtures/empty_env.txt +1 -0
  33. ragkit_dev-0.1.0/tests/integration/__init__.py +0 -0
  34. ragkit_dev-0.1.0/tests/integration/test_real_pipeline.py +106 -0
  35. ragkit_dev-0.1.0/tests/test_answer.py +116 -0
  36. ragkit_dev-0.1.0/tests/test_api.py +188 -0
  37. ragkit_dev-0.1.0/tests/test_cli.py +357 -0
  38. ragkit_dev-0.1.0/tests/test_env.py +175 -0
  39. ragkit_dev-0.1.0/tests/test_font_repair.py +75 -0
  40. ragkit_dev-0.1.0/tests/test_imports.py +85 -0
  41. ragkit_dev-0.1.0/tests/test_logging.py +92 -0
  42. ragkit_dev-0.1.0/tests/test_network_guard.py +38 -0
  43. ragkit_dev-0.1.0/tests/test_project_template.py +75 -0
  44. ragkit_dev-0.1.0/tests/test_release_gate.py +907 -0
  45. ragkit_dev-0.1.0/tests/test_release_scripts.py +163 -0
  46. ragkit_dev-0.1.0/tests/test_release_workflow.py +308 -0
  47. ragkit_dev-0.1.0/tests/test_settings.py +213 -0
  48. ragkit_dev-0.1.0/tests/test_text_processing.py +157 -0
  49. ragkit_dev-0.1.0/tests/test_vector_store.py +100 -0
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0rc1 (release candidate)
4
+
5
+ Release candidate for 0.1.0, used to rehearse the release on TestPyPI. It
6
+ contains everything listed under 0.1.0 below; the generator's npm version is
7
+ `0.1.0-rc.1`. Rehearsals never publish to PyPI or npm.
8
+
9
+ ## 0.1.0 (2026-11-10)
10
+
11
+ First packaged release: the toolkit is now the installable Python package
12
+ `ragkit-dev` on PyPI (import name `rag_kit`) with a `rag-kit` command. The
13
+ distribution is not called `rag-kit`: PyPI's name-similarity check would reject
14
+ `rag-kit` as too close to the existing, unrelated `ragkit` project.
15
+
16
+ ### Migrating from the script-based checkout
17
+
18
+ Install the checkout as a package first (`pip install -e ".[local,gemini]"`,
19
+ plus `api`, `ui` or `dev` as needed; see the README).
20
+
21
+ | Before | Now |
22
+ | ------ | --- |
23
+ | `pip install -r requirements.txt` | `pip install -e ".[local,gemini]"` |
24
+ | `pip install -r requirements-api.txt` / `-ui.txt` / `-dev.txt` | extras `api`, `ui`, `dev` |
25
+ | `python cli.py <command> …` | `rag-kit <command> …` or `python -m rag_kit <command> …` |
26
+ | `python ingest.py FILE` | `rag-kit ingest FILE` |
27
+ | `python ingest.py --pdf FILE` | `rag-kit ingest FILE` (`--pdf` was removed) |
28
+ | `python ask.py "question"` | `rag-kit ask "question"` |
29
+ | `uvicorn main:app` | `rag-kit serve`, or `uvicorn rag_kit.api:app` |
30
+ | `streamlit run streamlit_app.py` | `rag-kit ui` |
31
+ | `python scripts/smoke_bge_m3.py` | `rag-kit check --deep` |
32
+ | `from rag.pipeline import Pipeline` | `from rag_kit import Pipeline` (or `rag_kit.pipeline`) |
33
+ | `from rag.<module> import …` | `from rag_kit.<module> import …` (same module names) |
34
+ | `import config; config.TOP_K` | `from rag_kit import Settings; Settings.load().top_k` |
35
+ | `main.create_app(...)` | `rag_kit.api.create_app(...)` |
36
+ | logger `rag` | logger `rag_kit` |
37
+ | `rag.step07_user_question` | removed (unused; the CLI and API take the question) |
38
+
39
+ ### Changed behaviour
40
+
41
+ - **Project folder.** Commands work on the project given by `--project DIR`,
42
+ else `RAG_PROJECT_DIR`, else the nearest folder at or above the current
43
+ directory that contains `rag-kit.toml`. Without one they stop with an error.
44
+ Previously paths were tied to the folder `cli.py` lived in, so commands
45
+ worked from anywhere; now run them inside the project or pass `--project`.
46
+ This repository contains a `rag-kit.toml`, so its `.env` and `qdrant_data/`
47
+ keep working.
48
+ - **Environment export.** Importing `config` used to copy every `.env` key,
49
+ including `GOOGLE_API_KEY`, into `os.environ`. Now only the `rag-kit`
50
+ command, the default API app and the UI export, and only an allow-list
51
+ (Hugging Face / PyTorch runtime variables, proxies, CA bundles, plus names
52
+ in `RAG_EXPORT_ENV`). rag-kit's own settings are never exported. See
53
+ "Variables for other libraries" in the README.
54
+ - **`rag-kit serve` / `ui`** always pass the project to the server process in
55
+ `RAG_PROJECT_DIR`; `--collection` still applies to that process only.
56
+ - **`rag-kit check`** reads dependencies from the installed package's extras
57
+ and names the extra to install (`pip install "ragkit-dev[api]"`). It also
58
+ prints the project folder and the names of exported variables, and explains
59
+ how to create `.env` from `.env.example` when it is missing.
60
+ - **UI uploads** go to the project's `uploads/` folder.
61
+ - **API** returns 503 when no project can be found.
62
+
63
+ ### Unchanged
64
+
65
+ Collection defaults (`pdf_rag`, `BAAI/bge-m3`), the chunk ID namespace and
66
+ metadata keys, the `[S1]` source markers, the `/health`, `/query` and
67
+ `documents --json` schemas, and the 4000-character question limit. Existing
68
+ Qdrant collections need no migration.
69
+
70
+ ### Project generator
71
+
72
+ - New npm package `rag-kit-dev` (in `generator/`, version-locked to rag-kit):
73
+ `npx rag-kit-dev my-rag` creates a project with `rag-kit.toml`,
74
+ `.env.example`, `.gitignore`, `requirements.txt`, `data/`, `example.py` and
75
+ a README, and installs rag-kit into `.venv` with uv or pip. It never creates
76
+ `.env`, asks for API keys, downloads the embedding model, overwrites files or
77
+ deletes anything.
78
+
79
+ ### Earlier fixes in this release
80
+
81
+ - Header/footer removal no longer deletes matching words from body text.
82
+ - The Gemini SDK is imported only when an answer is generated.
83
+ - Settings are an injectable, side-effect-free `Settings` object.
84
+ - Library code logs instead of printing; `--json` keeps stdout pure.
85
+ - API application factory (`create_app`) with a race-free pipeline and a
86
+ 4000-character question limit.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Riyad
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.
@@ -0,0 +1,448 @@
1
+ Metadata-Version: 2.5
2
+ Name: ragkit-dev
3
+ Version: 0.1.0
4
+ Summary: A reusable, developer-focused RAG toolkit for document ingestion, retrieval, and source-grounded answers.
5
+ Project-URL: Repository, https://github.com/shamiulriyad/rag-learning
6
+ Project-URL: Issues, https://github.com/shamiulriyad/rag-learning/issues
7
+ Author: Riyad
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: embeddings,langchain,pdf,qdrant,rag,retrieval-augmented-generation
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
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 :: Text Processing :: Indexing
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: <3.13,>=3.11
21
+ Requires-Dist: langchain-core<2,>=1.6.9
22
+ Requires-Dist: langchain-qdrant<2,>=1.1
23
+ Requires-Dist: langchain-text-splitters<2,>=1.1
24
+ Requires-Dist: packaging>=24
25
+ Requires-Dist: pymupdf<3,>=1.28
26
+ Requires-Dist: python-dotenv<2,>=1.2
27
+ Requires-Dist: qdrant-client<2,>=1.19
28
+ Provides-Extra: all
29
+ Requires-Dist: fastapi<1,>=0.143; extra == 'all'
30
+ Requires-Dist: langchain-google-genai<5,>=4.4; extra == 'all'
31
+ Requires-Dist: langchain-huggingface<2,>=1.2; extra == 'all'
32
+ Requires-Dist: sentence-transformers<7,>=6.1; extra == 'all'
33
+ Requires-Dist: streamlit<2,>=1.65; extra == 'all'
34
+ Requires-Dist: uvicorn[standard]<1,>=0.54; extra == 'all'
35
+ Provides-Extra: api
36
+ Requires-Dist: fastapi<1,>=0.143; extra == 'api'
37
+ Requires-Dist: uvicorn[standard]<1,>=0.54; extra == 'api'
38
+ Provides-Extra: dev
39
+ Requires-Dist: fastapi<1,>=0.143; extra == 'dev'
40
+ Requires-Dist: httpx<1,>=0.28; extra == 'dev'
41
+ Requires-Dist: pytest-socket<1,>=0.8.1; extra == 'dev'
42
+ Requires-Dist: pytest<10,>=9.1; extra == 'dev'
43
+ Requires-Dist: pyyaml<7,>=6.0.2; extra == 'dev'
44
+ Requires-Dist: ruff<1,>=0.17; extra == 'dev'
45
+ Requires-Dist: uvicorn[standard]<1,>=0.54; extra == 'dev'
46
+ Provides-Extra: gemini
47
+ Requires-Dist: langchain-google-genai<5,>=4.4; extra == 'gemini'
48
+ Provides-Extra: local
49
+ Requires-Dist: langchain-huggingface<2,>=1.2; extra == 'local'
50
+ Requires-Dist: sentence-transformers<7,>=6.1; extra == 'local'
51
+ Provides-Extra: ui
52
+ Requires-Dist: streamlit<2,>=1.65; extra == 'ui'
53
+ Description-Content-Type: text/markdown
54
+
55
+ # rag-kit
56
+
57
+ A reusable, developer-focused RAG toolkit for document ingestion, retrieval,
58
+ and source-grounded answers. Index PDF, text and Markdown files into Qdrant,
59
+ then ask questions and get answers that cite the chunks (and PDF page numbers)
60
+ they came from.
61
+
62
+ Every pipeline step is its own module under [`src/rag_kit/`](https://github.com/shamiulriyad/rag-learning/tree/main/src/rag_kit), so
63
+ you can read, test and swap each one. Use it from the `rag-kit` command, from
64
+ Python, through an optional FastAPI service, or through an optional Streamlit
65
+ test UI.
66
+
67
+ ![Pipeline: ingest (load, extract, clean, chunk, embed, store in Qdrant) and query (question, embed, retrieve, Gemini, answer with sources); CLI, FastAPI and Streamlit all call the same Pipeline](https://raw.githubusercontent.com/shamiulriyad/rag-learning/main/docs/architecture.svg)
68
+
69
+ > **Names:** the Python package is `ragkit-dev` (you `import rag_kit` and run
70
+ > `rag-kit`); the project generator is the npm package `rag-kit-dev`. Version
71
+ > 0.1.0. Upgrading from the script-based version? See
72
+ > [CHANGELOG.md](https://github.com/shamiulriyad/rag-learning/blob/main/CHANGELOG.md) for the old-to-new command table.
73
+
74
+ ## Start a new project
75
+
76
+ `rag-kit-dev`, the npm package in [`generator/`](https://github.com/shamiulriyad/rag-learning/tree/main/generator),
77
+ creates a project folder (settings template, `data/`, an example) and a `.venv`
78
+ with rag-kit installed. It explains the local-vs-Gemini embedding choice before
79
+ installing anything and never creates `.env` or asks for an API key.
80
+
81
+ ```bash
82
+ npx rag-kit-dev my-rag
83
+ ```
84
+
85
+ To try an unreleased build instead (testing only), build the wheel in a
86
+ checkout and point the generator at it. The `--package-spec` path is relative
87
+ to the folder you run the command in:
88
+
89
+ ```bash
90
+ uv build --out-dir dist
91
+ node generator/bin/rag-kit-dev.js my-rag --embeddings gemini --package-spec dist/ragkit_dev-0.1.0-py3-none-any.whl
92
+ ```
93
+
94
+ ## What it does
95
+
96
+ | Step | Module | Notes |
97
+ | ---- | ------ | ----- |
98
+ | 1 Load | `step01_load_pdf.py` | `.pdf`, `.txt`, `.md`; validates before any work |
99
+ | 2 Extract | `step02_extract_text.py` | one Document per PDF page; optional font repair |
100
+ | 3 Clean | `step03_clean_text.py` | unwrap lines, join hyphenation, drop headers/footers |
101
+ | 4 Chunk | `step04_chunking.py` | recursive splitter, overlap, `chunk_id` per document |
102
+ | 5 Embed | `step05_embedding.py` | local Hugging Face model (default `BAAI/bge-m3`) or Gemini |
103
+ | 6 Store | `step06_vector_store.py` | Qdrant, embedded on disk or a server |
104
+ | 8–9 Retrieve | `step08`, `step09` | cosine search, optional minimum score |
105
+ | 10 Generate | `step10_generate.py` | Gemini, context-only, tags claims `[S1]`, `[S2]`… |
106
+ | 11 Answer | `step11_answer.py` | answer plus the sources behind each marker |
107
+
108
+ Step 7 (the question) comes from the CLI, the API or the UI.
109
+ [`pipeline.py`](https://github.com/shamiulriyad/rag-learning/blob/main/src/rag_kit/pipeline.py) wires the steps together; the CLI,
110
+ API and UI all use it. See [docs/pipeline.md](https://github.com/shamiulriyad/rag-learning/blob/main/docs/pipeline.md) for per-step
111
+ notes.
112
+
113
+ ## Prerequisites
114
+
115
+ - **Python 3.11 or 3.12** (the package refuses other versions for now).
116
+ - A **Gemini API key** for answer generation
117
+ ([get one](https://aistudio.google.com/app/apikey)). Ingestion and retrieval
118
+ with the default local embeddings need no key.
119
+ - For local embeddings: disk space for PyTorch and the model. `BAAI/bge-m3` is
120
+ about 2.2 GB and is downloaded to the Hugging Face cache on first use.
121
+ - Optional: Docker, if you prefer a Qdrant server to the embedded store.
122
+
123
+ ## Install
124
+
125
+ ```bash
126
+ py -3.11 -m venv .venv # macOS/Linux: python3.11 -m venv .venv
127
+ .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate
128
+ python -m pip install -U pip
129
+ pip install "ragkit-dev[local,gemini]" # local embeddings + Gemini answers
130
+ ```
131
+
132
+ The package is named `ragkit-dev`; `pip install ragkit` installs a different,
133
+ unrelated project. `pip install ragkit-dev` alone gives the core (enough for
134
+ `rag-kit check`); add extras in brackets, e.g. `pip install "ragkit-dev[gemini,api]"`.
135
+
136
+ **From a checkout (development):** to work on the code or test unreleased
137
+ changes, install the repository in editable mode instead. Wherever this README
138
+ says `pip install "ragkit-dev[...]"`, use `pip install -e ".[...]"` from the
139
+ checkout:
140
+
141
+ ```bash
142
+ git clone https://github.com/shamiulriyad/rag-learning.git
143
+ cd rag-learning
144
+ pip install -e ".[local,gemini]"
145
+ ```
146
+
147
+ Choose extras for what you need:
148
+
149
+ | Extra | Adds | Needed for |
150
+ | ----- | ---- | ---------- |
151
+ | *(none)* | LangChain core, Qdrant, PyMuPDF | ingestion/retrieval plumbing, `rag-kit check` |
152
+ | `local` | sentence-transformers (pulls in PyTorch) | `EMBEDDING_PROVIDER=huggingface` (default) |
153
+ | `gemini` | langchain-google-genai | answers, and `EMBEDDING_PROVIDER=google` |
154
+ | `api` | FastAPI, uvicorn | `rag-kit serve` |
155
+ | `ui` | Streamlit | `rag-kit ui` |
156
+ | `dev` | pytest, httpx, ruff (+ `api`) | running the tests |
157
+ | `all` | `local`, `gemini`, `api`, `ui` | everything |
158
+
159
+ **Linux and CPU-only PyTorch (recommended):** on Linux, PyTorch from PyPI
160
+ pulls in CUDA libraries (several GB). For CPU-only local embeddings, install
161
+ the CPU build from PyTorch's own index **first**, in the same virtual
162
+ environment, then rag-kit:
163
+
164
+ ```bash
165
+ pip install torch --index-url https://download.pytorch.org/whl/cpu
166
+ pip install "ragkit-dev[local,gemini]" # keeps the CPU torch that is installed
167
+
168
+ # with uv instead of pip:
169
+ uv pip install torch --index-url https://download.pytorch.org/whl/cpu
170
+ uv pip install "ragkit-dev[local,gemini]"
171
+ ```
172
+
173
+ From a checkout, the second command is `pip install -e ".[local,gemini]"`
174
+ (`uv pip install -e ".[local,gemini]"`).
175
+
176
+ This works on x86_64 and aarch64 Linux with Python 3.11 and 3.12. Later
177
+ `pip install -U "ragkit-dev[local]"` keeps the CPU build as long as it satisfies
178
+ the requirement; upgrading torch itself (`pip install -U torch`) without the
179
+ `--index-url` above would switch to the CUDA build. If a CUDA build is already
180
+ installed, start from a fresh virtual environment: reinstalling torch over it
181
+ leaves the `nvidia-*` packages behind. With an NVIDIA GPU you want to use,
182
+ skip the first command.
183
+
184
+ On Windows and macOS, PyTorch from PyPI does not pull in CUDA libraries, so no
185
+ extra step is needed.
186
+
187
+ ## Set up a project
188
+
189
+ A project is a folder with a `rag-kit.toml` marker file. It holds your `.env`,
190
+ your documents and the embedded Qdrant store; nothing is ever written inside
191
+ the installed package. This repository is itself a project.
192
+
193
+ ```bash
194
+ mkdir my-rag && cd my-rag
195
+ # create the marker (its contents are reserved; an empty file is fine)
196
+ echo "# rag-kit project" > rag-kit.toml
197
+ # copy .env.example from this repository to my-rag/.env, then set GOOGLE_API_KEY
198
+ rag-kit check # settings, dependencies, vector store
199
+ rag-kit check --deep # also loads the embedding model, compares dimensions
200
+ ```
201
+
202
+ Commands find the project like this:
203
+
204
+ 1. `--project DIR` (any command; no marker needed),
205
+ 2. else the `RAG_PROJECT_DIR` environment variable,
206
+ 3. else the nearest folder at or above the current directory that contains
207
+ `rag-kit.toml`.
208
+
209
+ If none applies, the command stops with an error instead of creating a store
210
+ somewhere unexpected. Relative paths in `.env` (`QDRANT_PATH`, `PDF_PATH`) are
211
+ resolved from the project folder, so commands behave the same from any
212
+ subfolder.
213
+
214
+ ## Configure
215
+
216
+ Settings come from the project's `.env` (or the file named by `RAG_ENV_FILE`).
217
+ Environment variables win over `.env`, and command-line flags such as
218
+ `--collection`, `-k` and `--min-score` win over both.
219
+
220
+ | Variable | Default | Purpose |
221
+ | -------- | ------- | ------- |
222
+ | `GOOGLE_API_KEY` | – | Gemini key (generation; embeddings if provider is `google`) |
223
+ | `LLM_MODEL` | `gemini-2.5-flash` | Gemini model for answers |
224
+ | `TEMPERATURE` | `0.2` | LLM temperature |
225
+ | `EMBEDDING_PROVIDER` | `huggingface` | `huggingface` (local) or `google` |
226
+ | `EMBEDDING_MODEL` | `BAAI/bge-m3` | 1024-dim; `models/gemini-embedding-001` for `google` |
227
+ | `EMBEDDING_DIM` | auto | set only to skip auto-detection |
228
+ | `QDRANT_PATH` | – | embedded store folder, e.g. `qdrant_data` (wins over URL) |
229
+ | `QDRANT_URL` / `QDRANT_API_KEY` | `http://localhost:6333` | Qdrant server |
230
+ | `COLLECTION_NAME` | `pdf_rag` | collection to read and write |
231
+ | `PDF_PATH` | `data/sample.pdf` | file for `ingest` with no arguments |
232
+ | `PDF_FONT_REPAIR` | `false` | repair scrambled-glyph PDFs (see below) |
233
+ | `CHUNK_SIZE` / `CHUNK_OVERLAP` | `1000` / `150` | chunking |
234
+ | `TOP_K` | `4` | chunks per question |
235
+ | `MIN_SCORE` | – | drop chunks below this cosine score |
236
+ | `EMBED_BATCH_SIZE` / `EMBED_SLEEP` | `90` / `0` (`60` for google) | ingestion batching |
237
+ | `RAG_PROJECT_DIR` | – | project folder (environment only) |
238
+ | `RAG_ENV_FILE` | `<project>/.env` | use another settings file (environment only) |
239
+ | `RAG_EXPORT_ENV` | – | extra `.env` names to export, comma-separated (see below) |
240
+
241
+ Without `QDRANT_PATH`, rag-kit connects to a Qdrant server at `QDRANT_URL`.
242
+ A collection is tied to the embedding model that built it. If the model's
243
+ dimension does not match the collection, every command stops with an error
244
+ instead of writing incompatible vectors.
245
+
246
+ ### Variables for other libraries (`HF_HOME`, proxies)
247
+
248
+ Some libraries read only the process environment: Hugging Face reads `HF_HOME`
249
+ when it is first imported, and HTTP clients read proxy variables. So that
250
+ these can live in `.env` too, the `rag-kit` command, `rag-kit serve` (the
251
+ default `rag_kit.api:app`) and `rag-kit ui` copy an **allow-list** of `.env`
252
+ keys into the environment at startup:
253
+
254
+ - `HF_*`, `HUGGINGFACE_*`, `TRANSFORMERS_*`, `SENTENCE_TRANSFORMERS_*`, `TORCH_*`,
255
+ `TOKENIZERS_PARALLELISM`, `CUDA_VISIBLE_DEVICES`, `OMP_NUM_THREADS`
256
+ - `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, `ALL_PROXY` (either case),
257
+ `REQUESTS_CA_BUNDLE`, `SSL_CERT_FILE`, `SSL_CERT_DIR`, `CURL_CA_BUNDLE`
258
+ - any names you list in `RAG_EXPORT_ENV=NAME1,NAME2`
259
+
260
+ rag-kit's own settings, including `GOOGLE_API_KEY` and `QDRANT_API_KEY`, and
261
+ any `RAG_*` variable are **never** exported, even if listed. Variables already
262
+ set in the environment win, empty values are skipped, and only names are ever
263
+ printed (`rag-kit check` lists them). `HF_TOKEN` is a secret but is exported,
264
+ because Hugging Face reads it only from the environment.
265
+
266
+ Importing `rag_kit`, building a `Pipeline` or calling `Settings.load()` never
267
+ changes the environment. In your own program, call
268
+ `rag_kit.env.export_env_file(settings)` before the first local embedding is
269
+ created if you want the same behaviour.
270
+
271
+ ## Ingest documents
272
+
273
+ ```bash
274
+ rag-kit ingest data/book.pdf notes.md # several files at once
275
+ rag-kit ingest # PDF_PATH from .env
276
+ rag-kit ingest report.pdf --collection experiments
277
+ rag-kit ingest scanned-font.pdf --font-repair on
278
+ rag-kit ingest --project ~/my-rag report.pdf
279
+ ```
280
+
281
+ - **Re-ingesting replaces.** Chunks get IDs derived from file name, content
282
+ hash and position. Ingesting a file again overwrites its chunks and removes
283
+ any older ones with the same file name, so nothing is duplicated. Two
284
+ different files with the same name in one collection count as one document.
285
+ - **Delete one document:** `rag-kit delete report.pdf` (asks first; `-y` to skip).
286
+ - **List documents:** `rag-kit documents`, or `--json` for
287
+ `{"collection": str, "total_chunks": int, "documents": [{"file": str, "chunks": int}]}`
288
+ (stable schema; files sorted by name).
289
+ - **Start over:** `rag-kit ingest file.pdf --recreate` drops the whole
290
+ collection first. Needed after changing the embedding model.
291
+ - **File names starting with `-`** look like options to the CLI. Write
292
+ `./-name.pdf`, or put `--` before the files:
293
+ `rag-kit ingest --font-repair on -- -Free-English-Grammar.pdf`.
294
+
295
+ ## Ask questions
296
+
297
+ ```bash
298
+ rag-kit ask "What is a countable noun?"
299
+ rag-kit ask # interactive, 'exit' to quit
300
+ rag-kit ask "..." --show-chunks # also print each chunk in full
301
+ rag-kit ask "..." --json # machine-readable
302
+ ```
303
+
304
+ The answer is printed with its `[S1]`… markers followed by the sources, each
305
+ with file, page (PDFs only) and similarity score. If nothing is retrieved (or
306
+ nothing passes `--min-score`), the LLM is not called and the status is
307
+ `no_results`.
308
+
309
+ ## Inspect retrieval
310
+
311
+ ```bash
312
+ rag-kit search "What is a countable noun?" -k 8 # no LLM call
313
+ rag-kit search "..." --full # whole chunk text
314
+ rag-kit search "..." --json # scores + metadata
315
+ ```
316
+
317
+ Each hit shows score, file, page, `chunk_id` and `doc_id`. With `--json`, step
318
+ progress goes to stderr and stdout holds only the JSON.
319
+
320
+ ## Optional FastAPI service
321
+
322
+ ```bash
323
+ pip install "ragkit-dev[api]" # from a checkout: pip install -e ".[api]"
324
+ rag-kit serve --port 8000 # or: uvicorn rag_kit.api:app --port 8000
325
+ rag-kit serve --collection experiments
326
+ ```
327
+
328
+ `--collection` (also on `ui`) applies to that server process only; without it
329
+ the server uses `COLLECTION_NAME`. The command prints which collection it uses,
330
+ passes the project to the server in `RAG_PROJECT_DIR`, and never modifies
331
+ `.env`. When you start uvicorn yourself, run it inside the project or set
332
+ `RAG_PROJECT_DIR`.
333
+
334
+ - `GET /health` → `{"status": "ok", "ready": true}`
335
+ - `POST /query` with `{"question": "...", "top_k": 4}` →
336
+ `{"answer": "...", "status": "answered" | "no_results", "sources": [{"page", "source", "score", "marker", "chunk_id", "text"}]}`
337
+
338
+ `answer` has the markers stripped; `marker` on each source maps them back.
339
+ Pipeline failures (including a missing project) return 503 with the reason.
340
+ Questions longer than 4000 characters are rejected with 422.
341
+
342
+ The service has no authentication and binds to `127.0.0.1` by default; do not
343
+ expose it publicly. To embed it elsewhere, build your own instance with
344
+ `rag_kit.api.create_app(settings=..., pipeline=...)`.
345
+
346
+ ## Optional Streamlit test UI
347
+
348
+ ```bash
349
+ pip install "ragkit-dev[ui]" # from a checkout: pip install -e ".[ui]"
350
+ rag-kit ui # http://localhost:8501
351
+ ```
352
+
353
+ Upload and ingest documents, ask questions or run retrieval only, and inspect
354
+ scores, pages, chunk text and metadata. Uploads are saved under the project's
355
+ `uploads/` folder (git-ignored here). It is a testing tool, not a production
356
+ front end.
357
+
358
+ ## Use from Python
359
+
360
+ ```python
361
+ from rag_kit import Pipeline, Settings
362
+
363
+ settings = Settings.load("path/to/project") # reads path/to/project/.env
364
+ pipeline = Pipeline(settings=settings)
365
+ pipeline.ingest("data/book.pdf")
366
+ result = pipeline.ask("What is a noun?")
367
+ print(result.status, result.answer_without_markers)
368
+ for source in result.sources:
369
+ print(source["marker"], source["file"], source["page"], source["score"])
370
+ pipeline.close()
371
+ ```
372
+
373
+ `Settings.load()` without a folder finds the project the same way the CLI
374
+ does. Override values with `settings.replace(top_k=8)`. `Settings.load` does
375
+ not change `os.environ`, and API keys are left out of its `repr`.
376
+
377
+ The pipeline reports progress through the `rag_kit` logger and prints nothing
378
+ on its own; call `rag_kit.log.show_progress()` (as the CLI does) or configure
379
+ `logging` yourself to see it.
380
+
381
+ ## Supported inputs and providers
382
+
383
+ - **Files:** `.pdf` (text layer required), `.txt` and `.md` (UTF-8).
384
+ - **Embeddings:** any sentence-transformers model through Hugging Face, or
385
+ Gemini embeddings.
386
+ - **LLM:** Gemini via `langchain-google-genai`.
387
+ - **Vector store:** Qdrant, embedded (`QDRANT_PATH`) or server (`QDRANT_URL`).
388
+
389
+ ### PDF font repair
390
+
391
+ Some PDFs embed subset fonts without a `ToUnicode` map, and every extractor
392
+ returns scrambled glyphs (`& ? ! & &`).
393
+ [`pdf_font_repair.py`](https://github.com/shamiulriyad/rag-learning/blob/main/src/rag_kit/pdf_font_repair.py) reverses that
394
+ substitution for the `-Free-English-Grammar.pdf` kept in this repository (it
395
+ is not part of any package). It is off by default; enable it with
396
+ `PDF_FONT_REPAIR=true` or `--font-repair on`. Its maps are specific to that
397
+ PDF; for other broken PDFs, use OCR instead.
398
+
399
+ ## Tests
400
+
401
+ The tests live in the repository, so run them from a checkout:
402
+
403
+ ```bash
404
+ pip install -e ".[dev,gemini]"
405
+ python -m pytest # unit tests: no network, no model download, no API calls
406
+ ```
407
+
408
+ The unit tests never read your `.env` or touch your Qdrant store. Integration
409
+ tests are opt-in. They run `rag-kit` against this checkout's real `.env` and
410
+ need the `local` extra:
411
+
412
+ ```bash
413
+ # PowerShell: $env:RAG_RUN_INTEGRATION = "1"
414
+ RAG_RUN_INTEGRATION=1 python -m pytest -m integration
415
+ RAG_RUN_INTEGRATION=1 RAG_RUN_GEMINI=1 python -m pytest -m "integration or gemini" # uses Gemini quota
416
+ ```
417
+
418
+ They read `COLLECTION_NAME` without changing it, and ingest only into a
419
+ uniquely named collection inside a temporary Qdrant folder. Stop `serve` and
420
+ `ui` first.
421
+
422
+ ## Troubleshooting
423
+
424
+ | Message | Fix |
425
+ | ------- | --- |
426
+ | `No rag-kit project found` | Run inside a folder with `rag-kit.toml`, pass `--project DIR`, or set `RAG_PROJECT_DIR`. |
427
+ | `[warn] .env not created yet` (from `rag-kit check`) | Copy the template: `cp .env.example .env` (PowerShell: `Copy-Item .env.example .env`), then set `GOOGLE_API_KEY`. Without `.env`, rag-kit looks for a Qdrant server instead of a local store. |
428
+ | `No local Qdrant store at …` | Ingest something first, or point `QDRANT_PATH` at the existing store. |
429
+ | `… is in use by another process` | Embedded Qdrant allows one process. Stop `serve`/`ui`/other commands, or use a Qdrant server. |
430
+ | `Collection 'x' holds N-dimensional vectors, but … produces M` | The embedding model changed. Restore `EMBEDDING_MODEL`, use another `COLLECTION_NAME`, or `--recreate`. |
431
+ | `GOOGLE_API_KEY is not set` | Put the key in the project's `.env`. |
432
+ | `… is not installed. Run: pip install "ragkit-dev[…]"` | Install the extra named in the message. |
433
+ | `… could not answer … 429` | Gemini rate limit or quota. Wait, or raise `EMBED_SLEEP` when using Gemini embeddings. |
434
+ | `… page(s) look corrupted` | Scrambled text: try `--font-repair on`; scanned PDF: run OCR (e.g. `ocrmypdf`). |
435
+ | `Unsupported file type` | Convert to PDF, `.txt` or `.md`. |
436
+
437
+ ## Known limitations
438
+
439
+ - Embedded Qdrant is single-process: run one of CLI / API / UI at a time.
440
+ - No OCR: scanned PDFs need to be OCR'd first.
441
+ - Documents are identified by file name within a collection.
442
+ - Only Gemini is wired up for generation.
443
+ - Chunks indexed before the metadata migration lack `doc_id` and `file_type`;
444
+ re-ingest the file to add them.
445
+
446
+ ## License
447
+
448
+ [MIT](https://github.com/shamiulriyad/rag-learning/blob/main/LICENSE)