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.
Files changed (127) hide show
  1. pandion-0.1.0/.gitignore +49 -0
  2. pandion-0.1.0/LICENSE +21 -0
  3. pandion-0.1.0/PKG-INFO +118 -0
  4. pandion-0.1.0/README.md +81 -0
  5. pandion-0.1.0/pyproject.toml +54 -0
  6. pandion-0.1.0/src/pandion/__init__.py +45 -0
  7. pandion-0.1.0/src/pandion/answer.py +16 -0
  8. pandion-0.1.0/src/pandion/artifacts.py +44 -0
  9. pandion-0.1.0/src/pandion/chunk.py +23 -0
  10. pandion-0.1.0/src/pandion/clean.py +21 -0
  11. pandion-0.1.0/src/pandion/cli.py +87 -0
  12. pandion-0.1.0/src/pandion/config.py +381 -0
  13. pandion-0.1.0/src/pandion/embedding_cache.py +145 -0
  14. pandion-0.1.0/src/pandion/embeddings.py +354 -0
  15. pandion-0.1.0/src/pandion/endpoints.py +171 -0
  16. pandion-0.1.0/src/pandion/errors.py +35 -0
  17. pandion-0.1.0/src/pandion/events.py +21 -0
  18. pandion-0.1.0/src/pandion/executor.py +342 -0
  19. pandion-0.1.0/src/pandion/graph.py +284 -0
  20. pandion-0.1.0/src/pandion/ids.py +86 -0
  21. pandion-0.1.0/src/pandion/llm.py +411 -0
  22. pandion-0.1.0/src/pandion/manifest.py +63 -0
  23. pandion-0.1.0/src/pandion/models.py +96 -0
  24. pandion-0.1.0/src/pandion/parse.py +20 -0
  25. pandion-0.1.0/src/pandion/payloads.py +238 -0
  26. pandion-0.1.0/src/pandion/pipeline.py +700 -0
  27. pandion-0.1.0/src/pandion/plugins/__init__.py +60 -0
  28. pandion-0.1.0/src/pandion/plugins/answer/__init__.py +5 -0
  29. pandion-0.1.0/src/pandion/plugins/answer/_grounding.py +161 -0
  30. pandion-0.1.0/src/pandion/plugins/answer/chat.py +589 -0
  31. pandion-0.1.0/src/pandion/plugins/answer/search.py +125 -0
  32. pandion-0.1.0/src/pandion/plugins/chunk/__init__.py +235 -0
  33. pandion-0.1.0/src/pandion/plugins/chunk/layout_blocks.py +449 -0
  34. pandion-0.1.0/src/pandion/plugins/chunk/markdown_header.py +202 -0
  35. pandion-0.1.0/src/pandion/plugins/chunk/recursive_character.py +298 -0
  36. pandion-0.1.0/src/pandion/plugins/chunk/sentence_window.py +203 -0
  37. pandion-0.1.0/src/pandion/plugins/chunk/token_based.py +156 -0
  38. pandion-0.1.0/src/pandion/plugins/clean/__init__.py +223 -0
  39. pandion-0.1.0/src/pandion/plugins/clean/dedupe_blocks.py +221 -0
  40. pandion-0.1.0/src/pandion/plugins/clean/drop_matching.py +165 -0
  41. pandion-0.1.0/src/pandion/plugins/clean/header_footer_strip.py +303 -0
  42. pandion-0.1.0/src/pandion/plugins/index/__init__.py +1 -0
  43. pandion-0.1.0/src/pandion/plugins/index/lancedb_store.py +372 -0
  44. pandion-0.1.0/src/pandion/plugins/parse/__init__.py +0 -0
  45. pandion-0.1.0/src/pandion/plugins/parse/docling.py +448 -0
  46. pandion-0.1.0/src/pandion/plugins/parse/pdfium.py +270 -0
  47. pandion-0.1.0/src/pandion/plugins/query/__init__.py +0 -0
  48. pandion-0.1.0/src/pandion/plugins/query/llm_rewrite.py +241 -0
  49. pandion-0.1.0/src/pandion/plugins/query/text.py +98 -0
  50. pandion-0.1.0/src/pandion/plugins/rerank/__init__.py +1 -0
  51. pandion-0.1.0/src/pandion/plugins/rerank/cross_encoder.py +204 -0
  52. pandion-0.1.0/src/pandion/plugins/rerank/llm_rerank.py +271 -0
  53. pandion-0.1.0/src/pandion/plugins/rerank/mmr.py +240 -0
  54. pandion-0.1.0/src/pandion/plugins/retrieve/__init__.py +10 -0
  55. pandion-0.1.0/src/pandion/plugins/retrieve/_base.py +270 -0
  56. pandion-0.1.0/src/pandion/plugins/retrieve/_prf.py +65 -0
  57. pandion-0.1.0/src/pandion/plugins/retrieve/bm25.py +94 -0
  58. pandion-0.1.0/src/pandion/plugins/retrieve/dense.py +98 -0
  59. pandion-0.1.0/src/pandion/plugins/retrieve/hybrid_rrf.py +278 -0
  60. pandion-0.1.0/src/pandion/plugins/source/__init__.py +0 -0
  61. pandion-0.1.0/src/pandion/plugins/source/upload.py +94 -0
  62. pandion-0.1.0/src/pandion/ports.py +106 -0
  63. pandion-0.1.0/src/pandion/registry.py +95 -0
  64. pandion-0.1.0/src/pandion/rerank.py +15 -0
  65. pandion-0.1.0/src/pandion/results.py +26 -0
  66. pandion-0.1.0/src/pandion/retrieve.py +15 -0
  67. pandion-0.1.0/src/pandion/rewrite.py +22 -0
  68. pandion-0.1.0/src/pandion/sources.py +51 -0
  69. pandion-0.1.0/src/pandion/stages.py +191 -0
  70. pandion-0.1.0/src/pandion/storage.py +147 -0
  71. pandion-0.1.0/src/pandion/text.py +125 -0
  72. pandion-0.1.0/src/pandion/textmatch.py +87 -0
  73. pandion-0.1.0/src/pandion/tokenize.py +35 -0
  74. pandion-0.1.0/src/pandion/transform.py +223 -0
  75. pandion-0.1.0/tests/chunkdocs.py +111 -0
  76. pandion-0.1.0/tests/conftest.py +178 -0
  77. pandion-0.1.0/tests/pdfs.py +124 -0
  78. pandion-0.1.0/tests/test_answer_search.py +215 -0
  79. pandion-0.1.0/tests/test_boundaries.py +35 -0
  80. pandion-0.1.0/tests/test_chunk.py +498 -0
  81. pandion-0.1.0/tests/test_chunk_layout_blocks.py +547 -0
  82. pandion-0.1.0/tests/test_chunk_sentence_window.py +260 -0
  83. pandion-0.1.0/tests/test_clean.py +928 -0
  84. pandion-0.1.0/tests/test_clean_drop_matching.py +227 -0
  85. pandion-0.1.0/tests/test_cli.py +74 -0
  86. pandion-0.1.0/tests/test_config.py +157 -0
  87. pandion-0.1.0/tests/test_config_styles.py +341 -0
  88. pandion-0.1.0/tests/test_context_extras.py +130 -0
  89. pandion-0.1.0/tests/test_discovery.py +69 -0
  90. pandion-0.1.0/tests/test_embedding_cache.py +145 -0
  91. pandion-0.1.0/tests/test_embeddings.py +437 -0
  92. pandion-0.1.0/tests/test_embeddings_models.py +73 -0
  93. pandion-0.1.0/tests/test_endpoints.py +294 -0
  94. pandion-0.1.0/tests/test_errors.py +48 -0
  95. pandion-0.1.0/tests/test_executor.py +575 -0
  96. pandion-0.1.0/tests/test_graph.py +518 -0
  97. pandion-0.1.0/tests/test_grounding.py +200 -0
  98. pandion-0.1.0/tests/test_ids.py +102 -0
  99. pandion-0.1.0/tests/test_index_lancedb.py +411 -0
  100. pandion-0.1.0/tests/test_llm.py +591 -0
  101. pandion-0.1.0/tests/test_metadata.py +39 -0
  102. pandion-0.1.0/tests/test_models.py +103 -0
  103. pandion-0.1.0/tests/test_package.py +5 -0
  104. pandion-0.1.0/tests/test_parse_docling.py +628 -0
  105. pandion-0.1.0/tests/test_parse_pdfium.py +289 -0
  106. pandion-0.1.0/tests/test_payloads.py +420 -0
  107. pandion-0.1.0/tests/test_pipeline.py +133 -0
  108. pandion-0.1.0/tests/test_pipeline_ask.py +207 -0
  109. pandion-0.1.0/tests/test_pipeline_retrieve.py +236 -0
  110. pandion-0.1.0/tests/test_query_llm_rewrite.py +297 -0
  111. pandion-0.1.0/tests/test_query_text.py +108 -0
  112. pandion-0.1.0/tests/test_recipe_hash.py +265 -0
  113. pandion-0.1.0/tests/test_registry.py +310 -0
  114. pandion-0.1.0/tests/test_rerank.py +465 -0
  115. pandion-0.1.0/tests/test_rerank_cross_encoder.py +419 -0
  116. pandion-0.1.0/tests/test_rerank_llm.py +402 -0
  117. pandion-0.1.0/tests/test_retrieve.py +809 -0
  118. pandion-0.1.0/tests/test_retrieve_then_rerank.py +78 -0
  119. pandion-0.1.0/tests/test_source_upload.py +86 -0
  120. pandion-0.1.0/tests/test_sources.py +49 -0
  121. pandion-0.1.0/tests/test_stages.py +304 -0
  122. pandion-0.1.0/tests/test_storage.py +115 -0
  123. pandion-0.1.0/tests/test_sweep.py +144 -0
  124. pandion-0.1.0/tests/test_sync.py +214 -0
  125. pandion-0.1.0/tests/test_text.py +161 -0
  126. pandion-0.1.0/tests/test_tokenize.py +58 -0
  127. pandion-0.1.0/tests/test_transform.py +272 -0
@@ -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).
@@ -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())