pandion 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.
- pandion-0.1.0/.gitignore +49 -0
- pandion-0.1.0/LICENSE +21 -0
- pandion-0.1.0/PKG-INFO +118 -0
- pandion-0.1.0/README.md +81 -0
- pandion-0.1.0/pyproject.toml +54 -0
- pandion-0.1.0/src/pandion/__init__.py +45 -0
- pandion-0.1.0/src/pandion/answer.py +16 -0
- pandion-0.1.0/src/pandion/artifacts.py +44 -0
- pandion-0.1.0/src/pandion/chunk.py +23 -0
- pandion-0.1.0/src/pandion/clean.py +21 -0
- pandion-0.1.0/src/pandion/cli.py +87 -0
- pandion-0.1.0/src/pandion/config.py +381 -0
- pandion-0.1.0/src/pandion/embedding_cache.py +145 -0
- pandion-0.1.0/src/pandion/embeddings.py +354 -0
- pandion-0.1.0/src/pandion/endpoints.py +171 -0
- pandion-0.1.0/src/pandion/errors.py +35 -0
- pandion-0.1.0/src/pandion/events.py +21 -0
- pandion-0.1.0/src/pandion/executor.py +342 -0
- pandion-0.1.0/src/pandion/graph.py +284 -0
- pandion-0.1.0/src/pandion/ids.py +86 -0
- pandion-0.1.0/src/pandion/llm.py +411 -0
- pandion-0.1.0/src/pandion/manifest.py +63 -0
- pandion-0.1.0/src/pandion/models.py +96 -0
- pandion-0.1.0/src/pandion/parse.py +20 -0
- pandion-0.1.0/src/pandion/payloads.py +238 -0
- pandion-0.1.0/src/pandion/pipeline.py +700 -0
- pandion-0.1.0/src/pandion/plugins/__init__.py +60 -0
- pandion-0.1.0/src/pandion/plugins/answer/__init__.py +5 -0
- pandion-0.1.0/src/pandion/plugins/answer/_grounding.py +161 -0
- pandion-0.1.0/src/pandion/plugins/answer/chat.py +589 -0
- pandion-0.1.0/src/pandion/plugins/answer/search.py +125 -0
- pandion-0.1.0/src/pandion/plugins/chunk/__init__.py +235 -0
- pandion-0.1.0/src/pandion/plugins/chunk/layout_blocks.py +449 -0
- pandion-0.1.0/src/pandion/plugins/chunk/markdown_header.py +202 -0
- pandion-0.1.0/src/pandion/plugins/chunk/recursive_character.py +298 -0
- pandion-0.1.0/src/pandion/plugins/chunk/sentence_window.py +203 -0
- pandion-0.1.0/src/pandion/plugins/chunk/token_based.py +156 -0
- pandion-0.1.0/src/pandion/plugins/clean/__init__.py +223 -0
- pandion-0.1.0/src/pandion/plugins/clean/dedupe_blocks.py +221 -0
- pandion-0.1.0/src/pandion/plugins/clean/drop_matching.py +165 -0
- pandion-0.1.0/src/pandion/plugins/clean/header_footer_strip.py +303 -0
- pandion-0.1.0/src/pandion/plugins/index/__init__.py +1 -0
- pandion-0.1.0/src/pandion/plugins/index/lancedb_store.py +372 -0
- pandion-0.1.0/src/pandion/plugins/parse/__init__.py +0 -0
- pandion-0.1.0/src/pandion/plugins/parse/docling.py +448 -0
- pandion-0.1.0/src/pandion/plugins/parse/pdfium.py +270 -0
- pandion-0.1.0/src/pandion/plugins/query/__init__.py +0 -0
- pandion-0.1.0/src/pandion/plugins/query/llm_rewrite.py +241 -0
- pandion-0.1.0/src/pandion/plugins/query/text.py +98 -0
- pandion-0.1.0/src/pandion/plugins/rerank/__init__.py +1 -0
- pandion-0.1.0/src/pandion/plugins/rerank/cross_encoder.py +204 -0
- pandion-0.1.0/src/pandion/plugins/rerank/llm_rerank.py +271 -0
- pandion-0.1.0/src/pandion/plugins/rerank/mmr.py +240 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/__init__.py +10 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/_base.py +270 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/_prf.py +65 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/bm25.py +94 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/dense.py +98 -0
- pandion-0.1.0/src/pandion/plugins/retrieve/hybrid_rrf.py +278 -0
- pandion-0.1.0/src/pandion/plugins/source/__init__.py +0 -0
- pandion-0.1.0/src/pandion/plugins/source/upload.py +94 -0
- pandion-0.1.0/src/pandion/ports.py +106 -0
- pandion-0.1.0/src/pandion/registry.py +95 -0
- pandion-0.1.0/src/pandion/rerank.py +15 -0
- pandion-0.1.0/src/pandion/results.py +26 -0
- pandion-0.1.0/src/pandion/retrieve.py +15 -0
- pandion-0.1.0/src/pandion/rewrite.py +22 -0
- pandion-0.1.0/src/pandion/sources.py +51 -0
- pandion-0.1.0/src/pandion/stages.py +191 -0
- pandion-0.1.0/src/pandion/storage.py +147 -0
- pandion-0.1.0/src/pandion/text.py +125 -0
- pandion-0.1.0/src/pandion/textmatch.py +87 -0
- pandion-0.1.0/src/pandion/tokenize.py +35 -0
- pandion-0.1.0/src/pandion/transform.py +223 -0
- pandion-0.1.0/tests/chunkdocs.py +111 -0
- pandion-0.1.0/tests/conftest.py +178 -0
- pandion-0.1.0/tests/pdfs.py +124 -0
- pandion-0.1.0/tests/test_answer_search.py +215 -0
- pandion-0.1.0/tests/test_boundaries.py +35 -0
- pandion-0.1.0/tests/test_chunk.py +498 -0
- pandion-0.1.0/tests/test_chunk_layout_blocks.py +547 -0
- pandion-0.1.0/tests/test_chunk_sentence_window.py +260 -0
- pandion-0.1.0/tests/test_clean.py +928 -0
- pandion-0.1.0/tests/test_clean_drop_matching.py +227 -0
- pandion-0.1.0/tests/test_cli.py +74 -0
- pandion-0.1.0/tests/test_config.py +157 -0
- pandion-0.1.0/tests/test_config_styles.py +341 -0
- pandion-0.1.0/tests/test_context_extras.py +130 -0
- pandion-0.1.0/tests/test_discovery.py +69 -0
- pandion-0.1.0/tests/test_embedding_cache.py +145 -0
- pandion-0.1.0/tests/test_embeddings.py +437 -0
- pandion-0.1.0/tests/test_embeddings_models.py +73 -0
- pandion-0.1.0/tests/test_endpoints.py +294 -0
- pandion-0.1.0/tests/test_errors.py +48 -0
- pandion-0.1.0/tests/test_executor.py +575 -0
- pandion-0.1.0/tests/test_graph.py +518 -0
- pandion-0.1.0/tests/test_grounding.py +200 -0
- pandion-0.1.0/tests/test_ids.py +102 -0
- pandion-0.1.0/tests/test_index_lancedb.py +411 -0
- pandion-0.1.0/tests/test_llm.py +591 -0
- pandion-0.1.0/tests/test_metadata.py +39 -0
- pandion-0.1.0/tests/test_models.py +103 -0
- pandion-0.1.0/tests/test_package.py +5 -0
- pandion-0.1.0/tests/test_parse_docling.py +628 -0
- pandion-0.1.0/tests/test_parse_pdfium.py +289 -0
- pandion-0.1.0/tests/test_payloads.py +420 -0
- pandion-0.1.0/tests/test_pipeline.py +133 -0
- pandion-0.1.0/tests/test_pipeline_ask.py +207 -0
- pandion-0.1.0/tests/test_pipeline_retrieve.py +236 -0
- pandion-0.1.0/tests/test_query_llm_rewrite.py +297 -0
- pandion-0.1.0/tests/test_query_text.py +108 -0
- pandion-0.1.0/tests/test_recipe_hash.py +265 -0
- pandion-0.1.0/tests/test_registry.py +310 -0
- pandion-0.1.0/tests/test_rerank.py +465 -0
- pandion-0.1.0/tests/test_rerank_cross_encoder.py +419 -0
- pandion-0.1.0/tests/test_rerank_llm.py +402 -0
- pandion-0.1.0/tests/test_retrieve.py +809 -0
- pandion-0.1.0/tests/test_retrieve_then_rerank.py +78 -0
- pandion-0.1.0/tests/test_source_upload.py +86 -0
- pandion-0.1.0/tests/test_sources.py +49 -0
- pandion-0.1.0/tests/test_stages.py +304 -0
- pandion-0.1.0/tests/test_storage.py +115 -0
- pandion-0.1.0/tests/test_sweep.py +144 -0
- pandion-0.1.0/tests/test_sync.py +214 -0
- pandion-0.1.0/tests/test_text.py +161 -0
- pandion-0.1.0/tests/test_tokenize.py +58 -0
- pandion-0.1.0/tests/test_transform.py +272 -0
pandion-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.remember/
|
|
2
|
+
|
|
3
|
+
# Python
|
|
4
|
+
.venv/
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.pyc
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.coverage
|
|
9
|
+
htmlcov/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
dist/
|
|
12
|
+
build/
|
|
13
|
+
|
|
14
|
+
# Playground data — regenerable, sometimes large
|
|
15
|
+
artifacts/
|
|
16
|
+
sources/
|
|
17
|
+
runs/
|
|
18
|
+
graphs/
|
|
19
|
+
qa_sets/
|
|
20
|
+
|
|
21
|
+
# Secrets
|
|
22
|
+
.env
|
|
23
|
+
.env.*
|
|
24
|
+
!.env.example
|
|
25
|
+
|
|
26
|
+
# Node (phase 5)
|
|
27
|
+
node_modules/
|
|
28
|
+
web/dist/
|
|
29
|
+
|
|
30
|
+
# Editors / OS
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
35
|
+
|
|
36
|
+
# Third-party agent skills (installed via `npx skills add`, not project source)
|
|
37
|
+
.agents/
|
|
38
|
+
.claude/skills/
|
|
39
|
+
|
|
40
|
+
# Browser-automation screenshots and traces
|
|
41
|
+
.playwright-mcp/
|
|
42
|
+
|
|
43
|
+
# Agent working notes, and the private plans, specs and design system (.superpowers/docs/)
|
|
44
|
+
.superpowers/
|
|
45
|
+
# Local launch-video output and the skill installer lock file
|
|
46
|
+
brag-output*/
|
|
47
|
+
skills-lock.json
|
|
48
|
+
# Agent worktrees
|
|
49
|
+
.claude/worktrees/
|
pandion-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nadeem Khan
|
|
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.
|
pandion-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pandion
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Retrieval pipelines you can inspect, step by step
|
|
5
|
+
Project-URL: Homepage, https://rag.codewithnk.com
|
|
6
|
+
Project-URL: Documentation, https://github.com/nadeem4/rag-playground/blob/main/docs/pandion.md
|
|
7
|
+
Project-URL: Source, https://github.com/nadeem4/rag-playground/tree/main/packages/pandion
|
|
8
|
+
Project-URL: Issues, https://github.com/nadeem4/rag-playground/issues
|
|
9
|
+
Author: Nadeem Khan
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: chunking,embeddings,pdf,rag,reranking,retrieval,search
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Topic :: Text Processing :: Indexing
|
|
18
|
+
Requires-Python: <3.14,>=3.13
|
|
19
|
+
Requires-Dist: pydantic>=2.9
|
|
20
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
21
|
+
Provides-Extra: anthropic
|
|
22
|
+
Requires-Dist: anthropic>=1.7.0; extra == 'anthropic'
|
|
23
|
+
Provides-Extra: docling
|
|
24
|
+
Requires-Dist: docling>=2.129.0; extra == 'docling'
|
|
25
|
+
Requires-Dist: onnxruntime>=1.30.0; extra == 'docling'
|
|
26
|
+
Provides-Extra: lancedb
|
|
27
|
+
Requires-Dist: lancedb<0.39,>=0.38; extra == 'lancedb'
|
|
28
|
+
Requires-Dist: pyarrow>=25.0.1; extra == 'lancedb'
|
|
29
|
+
Provides-Extra: local
|
|
30
|
+
Requires-Dist: sentence-transformers>=6.1.0; extra == 'local'
|
|
31
|
+
Requires-Dist: torch>=2.14.0; extra == 'local'
|
|
32
|
+
Provides-Extra: openai
|
|
33
|
+
Requires-Dist: openai>=3.17.0; extra == 'openai'
|
|
34
|
+
Provides-Extra: pdfium
|
|
35
|
+
Requires-Dist: pypdfium2>=5.13.0; extra == 'pdfium'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# Pandion
|
|
39
|
+
|
|
40
|
+
Retrieval pipelines you can inspect, step by step.
|
|
41
|
+
|
|
42
|
+
Pandion parses your documents, cleans and chunks them, embeds and stores them, then
|
|
43
|
+
searches, reranks and answers questions. Every step is a strategy you can swap, and every
|
|
44
|
+
step tells you what it did. Each result is cached under a hash of its settings, so the
|
|
45
|
+
same files with the same settings are never processed twice.
|
|
46
|
+
|
|
47
|
+
Pandion is named after the osprey, which spots a fish under the water and dives to
|
|
48
|
+
retrieve it. It is the engine of [RAG Playground](https://rag.codewithnk.com), a web app
|
|
49
|
+
that shows every step of a pipeline on a PDF.
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
Pandion needs Python 3.13.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install "pandion[pdfium,lancedb,local]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The base install is small. Parsers, stores and models are extras, and a step whose extra
|
|
60
|
+
is missing tells you which one to install:
|
|
61
|
+
|
|
62
|
+
| Extra | What it brings | Needed by |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `pdfium` | pypdfium2 | the fast `pdfium` parser |
|
|
65
|
+
| `docling` | Docling and its OCR engine | the `docling` parser, for layout, tables and scans |
|
|
66
|
+
| `lancedb` | LanceDB and pyarrow | the index, and the `dense`, `bm25` and `hybrid_rrf` search |
|
|
67
|
+
| `local` | sentence-transformers and torch | the local embedders, and the `mmr` and `cross_encoder` rerankers |
|
|
68
|
+
| `anthropic` | the Anthropic SDK | `llm_rewrite`, `llm_rerank` and `chat` with Claude |
|
|
69
|
+
| `openai` | the OpenAI SDK | the same steps with OpenAI, OpenRouter or a compatible server |
|
|
70
|
+
|
|
71
|
+
`pandion install pipeline.yaml` installs the extras a pipeline file needs.
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from pandion import Pipeline
|
|
77
|
+
|
|
78
|
+
p = Pipeline(
|
|
79
|
+
path="./my-index",
|
|
80
|
+
parse="pdfium",
|
|
81
|
+
chunk={"use": "recursive_character", "chunk_size": 400, "chunk_overlap": 40},
|
|
82
|
+
embed="bge-small-en-v1.5",
|
|
83
|
+
store="lancedb",
|
|
84
|
+
search="hybrid_rrf",
|
|
85
|
+
answer="search",
|
|
86
|
+
)
|
|
87
|
+
p.index.run("report.pdf")
|
|
88
|
+
print(p.ask("What does the report recommend?").text)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The embedder downloads once, on first use. After that, a second `p.index.run` on the
|
|
92
|
+
same file finds every step already stored and reuses it.
|
|
93
|
+
|
|
94
|
+
## What you can do
|
|
95
|
+
|
|
96
|
+
- **Run one step on its own**, with no pipeline and no cache:
|
|
97
|
+
`pandion.parse(pdf, use="pdfium")`, `pandion.clean(...)`, `pandion.chunk(...)`.
|
|
98
|
+
- **Write a pipeline three ways**: as a YAML file (`Pipeline.from_file("pipeline.yaml")`),
|
|
99
|
+
as a dict per step, or with classes such as `from pandion.parse import Docling`. The
|
|
100
|
+
three give the same pipeline and the same cache.
|
|
101
|
+
- **Build and update an index**: `p.index.run(files)` indexes them, and
|
|
102
|
+
`p.index.sync(folder)` adds, changes and removes files to match a folder.
|
|
103
|
+
- **Search**: `p.retrieve(question)` runs the rewrite, search and rerank steps and returns
|
|
104
|
+
each step's result as well as the hits.
|
|
105
|
+
- **Ask**: `p.ask(question)` returns the pieces found, or an answer from a chat model
|
|
106
|
+
that cites them.
|
|
107
|
+
|
|
108
|
+
## Learn more
|
|
109
|
+
|
|
110
|
+
- [The guide](https://github.com/nadeem4/rag-playground/blob/main/docs/pandion.md): every
|
|
111
|
+
step, setting and error.
|
|
112
|
+
- [Examples](https://github.com/nadeem4/rag-playground/tree/main/examples/pandion): index,
|
|
113
|
+
sync, search, ask, and the three ways to write a pipeline.
|
|
114
|
+
- [RAG Playground](https://rag.codewithnk.com): try the same pipelines in the browser.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT. See [LICENSE](https://github.com/nadeem4/rag-playground/blob/main/LICENSE).
|
pandion-0.1.0/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Pandion
|
|
2
|
+
|
|
3
|
+
Retrieval pipelines you can inspect, step by step.
|
|
4
|
+
|
|
5
|
+
Pandion parses your documents, cleans and chunks them, embeds and stores them, then
|
|
6
|
+
searches, reranks and answers questions. Every step is a strategy you can swap, and every
|
|
7
|
+
step tells you what it did. Each result is cached under a hash of its settings, so the
|
|
8
|
+
same files with the same settings are never processed twice.
|
|
9
|
+
|
|
10
|
+
Pandion is named after the osprey, which spots a fish under the water and dives to
|
|
11
|
+
retrieve it. It is the engine of [RAG Playground](https://rag.codewithnk.com), a web app
|
|
12
|
+
that shows every step of a pipeline on a PDF.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
Pandion needs Python 3.13.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install "pandion[pdfium,lancedb,local]"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The base install is small. Parsers, stores and models are extras, and a step whose extra
|
|
23
|
+
is missing tells you which one to install:
|
|
24
|
+
|
|
25
|
+
| Extra | What it brings | Needed by |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `pdfium` | pypdfium2 | the fast `pdfium` parser |
|
|
28
|
+
| `docling` | Docling and its OCR engine | the `docling` parser, for layout, tables and scans |
|
|
29
|
+
| `lancedb` | LanceDB and pyarrow | the index, and the `dense`, `bm25` and `hybrid_rrf` search |
|
|
30
|
+
| `local` | sentence-transformers and torch | the local embedders, and the `mmr` and `cross_encoder` rerankers |
|
|
31
|
+
| `anthropic` | the Anthropic SDK | `llm_rewrite`, `llm_rerank` and `chat` with Claude |
|
|
32
|
+
| `openai` | the OpenAI SDK | the same steps with OpenAI, OpenRouter or a compatible server |
|
|
33
|
+
|
|
34
|
+
`pandion install pipeline.yaml` installs the extras a pipeline file needs.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from pandion import Pipeline
|
|
40
|
+
|
|
41
|
+
p = Pipeline(
|
|
42
|
+
path="./my-index",
|
|
43
|
+
parse="pdfium",
|
|
44
|
+
chunk={"use": "recursive_character", "chunk_size": 400, "chunk_overlap": 40},
|
|
45
|
+
embed="bge-small-en-v1.5",
|
|
46
|
+
store="lancedb",
|
|
47
|
+
search="hybrid_rrf",
|
|
48
|
+
answer="search",
|
|
49
|
+
)
|
|
50
|
+
p.index.run("report.pdf")
|
|
51
|
+
print(p.ask("What does the report recommend?").text)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The embedder downloads once, on first use. After that, a second `p.index.run` on the
|
|
55
|
+
same file finds every step already stored and reuses it.
|
|
56
|
+
|
|
57
|
+
## What you can do
|
|
58
|
+
|
|
59
|
+
- **Run one step on its own**, with no pipeline and no cache:
|
|
60
|
+
`pandion.parse(pdf, use="pdfium")`, `pandion.clean(...)`, `pandion.chunk(...)`.
|
|
61
|
+
- **Write a pipeline three ways**: as a YAML file (`Pipeline.from_file("pipeline.yaml")`),
|
|
62
|
+
as a dict per step, or with classes such as `from pandion.parse import Docling`. The
|
|
63
|
+
three give the same pipeline and the same cache.
|
|
64
|
+
- **Build and update an index**: `p.index.run(files)` indexes them, and
|
|
65
|
+
`p.index.sync(folder)` adds, changes and removes files to match a folder.
|
|
66
|
+
- **Search**: `p.retrieve(question)` runs the rewrite, search and rerank steps and returns
|
|
67
|
+
each step's result as well as the hits.
|
|
68
|
+
- **Ask**: `p.ask(question)` returns the pieces found, or an answer from a chat model
|
|
69
|
+
that cites them.
|
|
70
|
+
|
|
71
|
+
## Learn more
|
|
72
|
+
|
|
73
|
+
- [The guide](https://github.com/nadeem4/rag-playground/blob/main/docs/pandion.md): every
|
|
74
|
+
step, setting and error.
|
|
75
|
+
- [Examples](https://github.com/nadeem4/rag-playground/tree/main/examples/pandion): index,
|
|
76
|
+
sync, search, ask, and the three ways to write a pipeline.
|
|
77
|
+
- [RAG Playground](https://rag.codewithnk.com): try the same pipelines in the browser.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT. See [LICENSE](https://github.com/nadeem4/rag-playground/blob/main/LICENSE).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pandion"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Retrieval pipelines you can inspect, step by step"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Nadeem Khan" }]
|
|
9
|
+
requires-python = ">=3.13,<3.14"
|
|
10
|
+
keywords = ["retrieval", "rag", "search", "embeddings", "pdf", "chunking", "reranking"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
16
|
+
"Topic :: Text Processing :: Indexing",
|
|
17
|
+
]
|
|
18
|
+
dependencies = ["pydantic>=2.9", "pyyaml>=6.0.3"]
|
|
19
|
+
|
|
20
|
+
# Parsers, models and stores are extras, so the base install stays small.
|
|
21
|
+
# A step whose extra is missing says which one to install.
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
pdfium = ["pypdfium2>=5.13.0"]
|
|
24
|
+
docling = [
|
|
25
|
+
"docling>=2.129.0",
|
|
26
|
+
# Docling's OCR engine. RapidOCR's ONNX checkpoints ship inside its wheel,
|
|
27
|
+
# so OCR downloads nothing; without onnxruntime Docling falls back to
|
|
28
|
+
# RapidOCR's torch backend, which fetches .pth files into site-packages.
|
|
29
|
+
"onnxruntime>=1.30.0",
|
|
30
|
+
]
|
|
31
|
+
# Local models: the embedders, the cross-encoder and mmr. On Linux the root
|
|
32
|
+
# pyproject.toml takes torch from the CPU-only index.
|
|
33
|
+
local = ["sentence-transformers>=6.1.0", "torch>=2.14.0"]
|
|
34
|
+
lancedb = ["lancedb>=0.38,<0.39", "pyarrow>=25.0.1"]
|
|
35
|
+
# Chat models, for llm_rewrite: OpenAI, OpenRouter and any OpenAI-compatible
|
|
36
|
+
# server through the OpenAI SDK, and Claude through the Anthropic SDK.
|
|
37
|
+
openai = ["openai>=3.17.0"]
|
|
38
|
+
anthropic = ["anthropic>=1.7.0"]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://rag.codewithnk.com"
|
|
42
|
+
Documentation = "https://github.com/nadeem4/rag-playground/blob/main/docs/pandion.md"
|
|
43
|
+
Source = "https://github.com/nadeem4/rag-playground/tree/main/packages/pandion"
|
|
44
|
+
Issues = "https://github.com/nadeem4/rag-playground/issues"
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
pandion = "pandion.cli:main"
|
|
48
|
+
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["hatchling"]
|
|
51
|
+
build-backend = "hatchling.build"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/pandion"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Pandion: indexing and retrieval pipelines you can inspect stage by stage.
|
|
2
|
+
|
|
3
|
+
The engine lives in plain modules (`pandion.graph`, `pandion.executor`,
|
|
4
|
+
`pandion.storage` and the rest). A name that starts with an underscore is
|
|
5
|
+
private; the RAG Playground app may import every other name.
|
|
6
|
+
|
|
7
|
+
`pandion.parse`, `pandion.clean`, `pandion.chunk` and `pandion.rewrite` run one
|
|
8
|
+
stage when called, and hold that stage's step classes. `pandion.retrieve`,
|
|
9
|
+
`pandion.rerank` and `pandion.answer` hold the step classes of the rest.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from pandion.models import ModelSettings, preload_embedder, preload_reranker
|
|
13
|
+
from pandion.pipeline import (
|
|
14
|
+
Answer,
|
|
15
|
+
IndexPipeline,
|
|
16
|
+
Pipeline,
|
|
17
|
+
QueryPipeline,
|
|
18
|
+
Retrieval,
|
|
19
|
+
RetrievalPipeline,
|
|
20
|
+
)
|
|
21
|
+
from pandion.results import StageResult
|
|
22
|
+
from pandion import answer, chunk, clean, parse, rerank, retrieve, rewrite
|
|
23
|
+
|
|
24
|
+
__version__ = "0.1.0"
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"Answer",
|
|
28
|
+
"IndexPipeline",
|
|
29
|
+
"ModelSettings",
|
|
30
|
+
"Pipeline",
|
|
31
|
+
"QueryPipeline",
|
|
32
|
+
"Retrieval",
|
|
33
|
+
"RetrievalPipeline",
|
|
34
|
+
"StageResult",
|
|
35
|
+
"answer",
|
|
36
|
+
"chunk",
|
|
37
|
+
"clean",
|
|
38
|
+
"parse",
|
|
39
|
+
"preload_embedder",
|
|
40
|
+
"preload_reranker",
|
|
41
|
+
"rerank",
|
|
42
|
+
"retrieve",
|
|
43
|
+
"rewrite",
|
|
44
|
+
"__version__",
|
|
45
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""The answer steps, as classes for a pipeline written in code.
|
|
2
|
+
|
|
3
|
+
`Search(top_k=5)` shows the pieces retrieval found, as they are. `Chat(model=
|
|
4
|
+
"claude-haiku-4-5")` asks a chat model for an answer whose claims cite those
|
|
5
|
+
pieces. Each is the same as its block, for example `{"use": "search",
|
|
6
|
+
"top_k": 5}` (`pandion.config.step_class`). Answering runs through a
|
|
7
|
+
pipeline: `p.ask(question)`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pandion.config import step_class
|
|
11
|
+
from pandion.ports import Stage
|
|
12
|
+
|
|
13
|
+
Search = step_class(Stage.USE_CASE, "search", __name__)
|
|
14
|
+
Chat = step_class(Stage.USE_CASE, "chat", __name__)
|
|
15
|
+
|
|
16
|
+
__all__ = ["Chat", "Search"]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Artifact types and the immutable artifact value.
|
|
2
|
+
|
|
3
|
+
An artifact is what a transform produces: an immutable value identified by a
|
|
4
|
+
recipe hash (see `pandion.ids`). The payload itself lives on disk and is loaded
|
|
5
|
+
lazily; `meta` is small and always in memory.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from enum import StrEnum
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ArtifactType(StrEnum):
|
|
16
|
+
"""The value types that flow between pipeline stages.
|
|
17
|
+
|
|
18
|
+
A StrEnum rather than a Literal so that `inputs = {"file": ArtifactType.RAW_FILE}`
|
|
19
|
+
typechecks in plugins without an explicit annotation, and so it serializes to
|
|
20
|
+
JSON as a plain string.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
RAW_FILE = "raw_file"
|
|
24
|
+
QUERY = "query"
|
|
25
|
+
PARSED_DOC = "parsed_doc"
|
|
26
|
+
CHUNK_SET = "chunk_set"
|
|
27
|
+
INDEX = "index"
|
|
28
|
+
RETRIEVAL_RESULT = "retrieval_result"
|
|
29
|
+
OUTPUT = "output"
|
|
30
|
+
QA_SET = "qa_set"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class Artifact:
|
|
35
|
+
"""A committed pipeline value.
|
|
36
|
+
|
|
37
|
+
`id` is a *recipe* hash, not a content hash — it is computed from the
|
|
38
|
+
transform and its inputs before the payload exists. That is sound only for
|
|
39
|
+
deterministic transforms; see `Transform.deterministic`.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
id: str
|
|
43
|
+
type: ArtifactType
|
|
44
|
+
meta: dict[str, Any] = field(default_factory=dict)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""The chunkers: call this module to chunk one document, or use a class in a pipeline.
|
|
2
|
+
|
|
3
|
+
`pandion.chunk(doc, use="recursive_character", chunk_size=400)` cuts one parsed
|
|
4
|
+
document into pieces (`pandion.stages.chunk`). `RecursiveCharacter(chunk_size=400)`
|
|
5
|
+
is the same settings for a pipeline written in code (`pandion.config.step_class`).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from pandion.config import step_class
|
|
11
|
+
from pandion.ports import Stage
|
|
12
|
+
from pandion.stages import StageModule
|
|
13
|
+
from pandion.stages import chunk as run
|
|
14
|
+
|
|
15
|
+
RecursiveCharacter = step_class(Stage.CHUNK, "recursive_character", __name__)
|
|
16
|
+
LayoutBlocks = step_class(Stage.CHUNK, "layout_blocks", __name__)
|
|
17
|
+
MarkdownHeader = step_class(Stage.CHUNK, "markdown_header", __name__)
|
|
18
|
+
TokenBased = step_class(Stage.CHUNK, "token_based", __name__)
|
|
19
|
+
SentenceWindow = step_class(Stage.CHUNK, "sentence_window", __name__)
|
|
20
|
+
|
|
21
|
+
__all__ = ["LayoutBlocks", "MarkdownHeader", "RecursiveCharacter", "SentenceWindow", "TokenBased"]
|
|
22
|
+
|
|
23
|
+
sys.modules[__name__].__class__ = StageModule
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""The cleaners: call this module to clean one document, or use a class in a pipeline.
|
|
2
|
+
|
|
3
|
+
`pandion.clean(doc, steps=[{"use": "dedupe_blocks"}])` cleans one parsed
|
|
4
|
+
document (`pandion.stages.clean`). `DedupeBlocks()` is the same step for a
|
|
5
|
+
pipeline written in code (`pandion.config.step_class`).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from pandion.config import step_class
|
|
11
|
+
from pandion.ports import Stage
|
|
12
|
+
from pandion.stages import StageModule
|
|
13
|
+
from pandion.stages import clean as run
|
|
14
|
+
|
|
15
|
+
HeaderFooterStrip = step_class(Stage.CLEAN, "header_footer_strip", __name__)
|
|
16
|
+
DedupeBlocks = step_class(Stage.CLEAN, "dedupe_blocks", __name__)
|
|
17
|
+
DropMatching = step_class(Stage.CLEAN, "drop_matching", __name__)
|
|
18
|
+
|
|
19
|
+
__all__ = ["DedupeBlocks", "DropMatching", "HeaderFooterStrip"]
|
|
20
|
+
|
|
21
|
+
sys.modules[__name__].__class__ = StageModule
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""The `pandion` command.
|
|
2
|
+
|
|
3
|
+
`pandion install pipeline.yaml` reads the file, works out which extras its
|
|
4
|
+
steps need, and installs them with pip into the Python that runs it.
|
|
5
|
+
`--print` only prints the command. Nothing is ever installed while a pipeline
|
|
6
|
+
runs.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from pandion.config import ConfigError, PipelineConfig, load
|
|
16
|
+
|
|
17
|
+
#: The extra each strategy or embedder needs. A name that is not here needs
|
|
18
|
+
#: only the base install.
|
|
19
|
+
EXTRAS = {
|
|
20
|
+
"pdfium": "pdfium",
|
|
21
|
+
"docling": "docling",
|
|
22
|
+
"lancedb": "lancedb",
|
|
23
|
+
"qwen3-embedding-0.6b": "local",
|
|
24
|
+
"bge-small-en-v1.5": "local",
|
|
25
|
+
"mmr": "local",
|
|
26
|
+
"cross_encoder": "local",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#: Steps that call a chat model, by stage; their extra depends on the model's provider.
|
|
30
|
+
CHAT_STEPS = {"llm_rewrite": "query", "llm_rerank": "rerank", "chat": "use_case"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def extras_for(config: PipelineConfig) -> list[str]:
|
|
34
|
+
"""The extras the pipeline's steps need, sorted."""
|
|
35
|
+
index = config.index
|
|
36
|
+
names = [index.parse[0], *(use for use, _ in index.clean), index.chunk[0]]
|
|
37
|
+
names += ["lancedb", index.store["embedder"]]
|
|
38
|
+
extras = {EXTRAS[name] for name in names if name in EXTRAS}
|
|
39
|
+
retrieve = config.retrieve
|
|
40
|
+
if retrieve is not None:
|
|
41
|
+
steps = [*([retrieve.rewrite] if retrieve.rewrite else []), retrieve.search, *retrieve.rerank]
|
|
42
|
+
if config.answer is not None:
|
|
43
|
+
steps.append(config.answer)
|
|
44
|
+
for use, settings in steps:
|
|
45
|
+
if use in CHAT_STEPS:
|
|
46
|
+
extras.add(_chat_extra(use, settings))
|
|
47
|
+
elif use in EXTRAS:
|
|
48
|
+
extras.add(EXTRAS[use])
|
|
49
|
+
return sorted(extras)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _chat_extra(use: str, settings: dict) -> str:
|
|
53
|
+
"""Claude models need the Anthropic SDK; every other model the OpenAI SDK."""
|
|
54
|
+
from pandion.llm import CHAT_MODELS
|
|
55
|
+
from pandion.plugins import discover
|
|
56
|
+
from pandion.ports import Stage
|
|
57
|
+
from pandion.registry import registry
|
|
58
|
+
|
|
59
|
+
discover()
|
|
60
|
+
model = registry.get(Stage(CHAT_STEPS[use]), use).config_model(**settings).model
|
|
61
|
+
return "anthropic" if CHAT_MODELS[model].provider == "anthropic" else "openai"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def main(argv: list[str] | None = None) -> int:
|
|
65
|
+
parser = argparse.ArgumentParser(prog="pandion")
|
|
66
|
+
commands = parser.add_subparsers(dest="command", required=True)
|
|
67
|
+
install = commands.add_parser("install", help="install the extras a pipeline file needs")
|
|
68
|
+
install.add_argument("config", help="the pipeline file, for example pipeline.yaml")
|
|
69
|
+
install.add_argument("--print", action="store_true", help="only print the pip command")
|
|
70
|
+
args = parser.parse_args(argv)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
config = load(args.config)
|
|
74
|
+
except (ConfigError, OSError) as exc:
|
|
75
|
+
print(f"pandion: {exc}", file=sys.stderr)
|
|
76
|
+
return 2
|
|
77
|
+
extras = extras_for(config)
|
|
78
|
+
package = f"pandion[{','.join(extras)}]" if extras else "pandion"
|
|
79
|
+
command = [sys.executable, "-m", "pip", "install", package]
|
|
80
|
+
if args.print:
|
|
81
|
+
print(f'"{sys.executable}" -m pip install "{package}"')
|
|
82
|
+
return 0
|
|
83
|
+
return subprocess.call(command)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
raise SystemExit(main())
|