apex-rag 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
apex_rag-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 G S ABINIVAS
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,332 @@
1
+ Metadata-Version: 2.4
2
+ Name: apex-rag
3
+ Version: 0.1.0
4
+ Summary: A production-grade, local-first Agentic RAG library using structural document navigation.
5
+ Project-URL: Homepage, https://github.com/your-org/apex-rag
6
+ Project-URL: Repository, https://github.com/your-org/apex-rag
7
+ Project-URL: Issues, https://github.com/your-org/apex-rag/issues
8
+ Author: ApexRAG Contributors
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 G S ABINIVAS
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: agentic,llm,nlp,ollama,rag,retrieval
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
39
+ Requires-Python: >=3.10
40
+ Requires-Dist: aiosqlite>=0.20.0
41
+ Requires-Dist: alembic>=1.13.0
42
+ Requires-Dist: asyncpg>=0.29.0
43
+ Requires-Dist: fastapi>=0.111.0
44
+ Requires-Dist: httpx>=0.27.0
45
+ Requires-Dist: markitdown[all]>=0.0.1a2
46
+ Requires-Dist: ollama>=0.2.0
47
+ Requires-Dist: pydantic-settings>=2.3.0
48
+ Requires-Dist: pydantic>=2.7.0
49
+ Requires-Dist: python-dotenv>=1.0.1
50
+ Requires-Dist: python-multipart>=0.0.9
51
+ Requires-Dist: rich>=13.7.0
52
+ Requires-Dist: sqlalchemy>=2.0.0
53
+ Requires-Dist: tenacity>=8.3.0
54
+ Requires-Dist: uvicorn[standard]>=0.29.0
55
+ Provides-Extra: dev
56
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
57
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
58
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
59
+ Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
60
+ Requires-Dist: pytest>=8.2.0; extra == 'dev'
61
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
62
+ Provides-Extra: docling
63
+ Requires-Dist: docling>=1.16.0; extra == 'docling'
64
+ Provides-Extra: postgres
65
+ Requires-Dist: asyncpg>=0.29.0; extra == 'postgres'
66
+ Requires-Dist: psycopg2-binary>=2.9.9; extra == 'postgres'
67
+ Description-Content-Type: text/markdown
68
+
69
+ # ApexRAG
70
+
71
+ > **Production-grade, local-first Agentic RAG Library.**
72
+ > Replaces vector similarity search with structural, agentic navigation of documents.
73
+
74
+ ---
75
+
76
+ ## 🧠 The Core Idea
77
+
78
+ Traditional RAG embeds text into vectors and finds the "closest" chunks. This creates **retrieval hallucinations** β€” the model returns semantically-similar-but-wrong content because it has no understanding of document structure.
79
+
80
+ **ApexRAG takes a fundamentally different approach:**
81
+
82
+ 1. **Parse** the document into a structural tree (based on headings) and extract page numbers.
83
+ 2. **Synthesize** a 30-word *Semantic Map* for every node using a local LLM.
84
+ 3. **Navigate** the tree with an LLM agent that reads summaries and decides which branch to enter β€” trying multiple candidates if necessary.
85
+ 4. **Verify** the exact leaf node answers the query via a strict secondary LLM check (99.999% accuracy).
86
+ 5. **Return** the exact leaf node content β€” not a blended, hallucinated average.
87
+
88
+ ```
89
+ Query: "What were Q3 revenues?"
90
+ β”‚
91
+ Root (Annual Report)
92
+ β”œβ”€β”€ Chapter 1: Executive Summary ← LLM: "Not here"
93
+ └── Chapter 2: Revenue Analysis ← LLM: "Enter this"
94
+ β”œβ”€β”€ Q1 Revenue ← LLM: "Not Q3"
95
+ β”œβ”€β”€ Q2 Revenue ← LLM: "Not Q3"
96
+ └── Q3 Revenue ← LLM: "This is it!" β†’ Return content
97
+ ```
98
+
99
+ ---
100
+
101
+ ## πŸ“ Project Structure
102
+
103
+ ```
104
+ apex_rag/
105
+ β”œβ”€β”€ src/
106
+ β”‚ β”œβ”€β”€ __init__.py # Public API exports
107
+ β”‚ β”œβ”€β”€ api.py # FastAPI App & UI dashboard
108
+ β”‚ β”œβ”€β”€ client.py # Thread-safe user-facing ApexIndex class
109
+ β”‚ β”œβ”€β”€ ingestion.py # Document parsing & tree synthesis
110
+ β”‚ β”œβ”€β”€ navigation.py # Recursive LLM navigation agent
111
+ β”‚ β”œβ”€β”€ storage.py # SQLAlchemy async ORM & PageIndexEntry
112
+ β”‚ └── utils.py # ReasoningTrace, retry decorator, helpers
113
+ β”œβ”€β”€ tests/
114
+ β”‚ β”œβ”€β”€ test_tree.py # Parser & storage unit tests
115
+ β”‚ └── test_search.py # Navigation agent unit tests (no Ollama needed)
116
+ β”œβ”€β”€ examples/
117
+ β”‚ └── basic_usage.py # End-to-end demo
118
+ β”œβ”€β”€ pyproject.toml
119
+ └── docker-compose.yml
120
+ ```
121
+
122
+ ---
123
+
124
+ ## ⚑ Quick Start
125
+
126
+ ### 1. Install
127
+
128
+ ```bash
129
+ # Clone and set up
130
+ cd ApexRAG
131
+ pip install -e ".[dev]"
132
+ ```
133
+
134
+ ### 2. Start Ollama
135
+
136
+ ```bash
137
+ ollama serve
138
+ ollama pull llama3.1 # or phi3, mistral, etc.
139
+ ```
140
+
141
+ ### 3. Ingest & Query
142
+
143
+ ```python
144
+ import asyncio
145
+ from src.client import ApexIndex
146
+
147
+ async def main():
148
+ async with await ApexIndex.create(
149
+ db_url="sqlite+aiosqlite:///apex.db",
150
+ model="llama3.1",
151
+ ) as index:
152
+ # Ingest a PDF
153
+ doc_id = await index.ingest("path/to/your/report.pdf")
154
+
155
+ # Query it
156
+ result = await index.query(
157
+ "What are the Q3 revenue figures?",
158
+ doc_id,
159
+ )
160
+
161
+ if result:
162
+ print(result.content)
163
+ print(f"Found at path: {result.path}")
164
+ print(f"Navigation trace: {result.trace}")
165
+
166
+ asyncio.run(main())
167
+ ```
168
+
169
+ ### 4. Start the FastAPI Server & Visual Index Dashboard
170
+
171
+ ```bash
172
+ uvicorn src.api:app --reload
173
+ ```
174
+
175
+ Open your browser to:
176
+ - **Dashboard:** [http://localhost:8000](http://localhost:8000)
177
+ - **API Docs:** [http://localhost:8000/docs](http://localhost:8000/docs)
178
+
179
+ From the dashboard, you can click on an ingested document to view its full structural tree and its book-style alphabetical page index!
180
+
181
+ ---
182
+
183
+ ## πŸ—οΈ Architecture Deep Dive
184
+
185
+ ### Ingestion Engine (`ingestion.py`)
186
+
187
+ | Step | Description |
188
+ |------|-------------|
189
+ | **Convert** | `markitdown` or `docling` converts PDF/DOCX β†’ Markdown |
190
+ | **Parse** | Regex walks ATX headings (`#`, `##`, `###`) to build `ParsedSection` tree |
191
+ | **Persist** | Nodes written to DB with LTree-style path (`1.2.3`) |
192
+ | **Synthesize** | Ollama generates 30-word summaries in parallel (bounded by semaphore) |
193
+
194
+ ### Storage Layer (`storage.py`)
195
+
196
+ ```
197
+ DocumentNode table:
198
+ id BIGINT PRIMARY KEY
199
+ doc_id VARCHAR(255) -- logical document identifier
200
+ parent_id BIGINT FK (self) -- NULL for root nodes
201
+ path VARCHAR(512) -- "1.2.3" LTree-style
202
+ title VARCHAR(512) -- section heading
203
+ summary TEXT -- 30-word Semantic Map
204
+ content TEXT -- leaf content (NULL for intermediate)
205
+ metadata TEXT (JSON) -- page numbers, char count, source file
206
+ depth INTEGER -- nesting level (0 = root)
207
+ position INTEGER -- sibling order
208
+ created_at TIMESTAMP
209
+ ```
210
+
211
+ Supports both `sqlite+aiosqlite://` (local) and `postgresql+asyncpg://` (production).
212
+
213
+ ### Navigation Agent (`navigation.py`)
214
+
215
+ ```
216
+ find(query, doc_id)
217
+ └── _navigate(current_node)
218
+ β”œβ”€β”€ [Leaf?] β†’ return content immediately
219
+ β”œβ”€β”€ fetch children
220
+ β”œβ”€β”€ _ask_llm(query, child_summaries)
221
+ β”‚ └── "Which child ID contains the answer?"
222
+ β”œβ”€β”€ [ID returned] β†’ recurse into chosen child
223
+ β”‚ └── [child returns None] β†’ try siblings
224
+ └── [NONE returned] β†’ backtrack to parent
225
+ ```
226
+
227
+ **LLM Response Parsing** is robust β€” 4-tier fallback:
228
+ 1. Strict `json.loads()`
229
+ 2. Regex extraction from prose-wrapped JSON
230
+ 3. Explicit `"NONE"` keyword detection
231
+ 4. Heuristic: scan for any valid child ID number in the response
232
+
233
+ **High Accuracy (99.999%) Verification:**
234
+ At the leaf level, a second LLM prompt strictly verifies if the leaf content answers the query. If it fails, the agent backtracks and explores the fallback candidates (second-best choices) up the tree.
235
+
236
+ ### Reasoning Trace (`utils.py`)
237
+
238
+ Every navigation decision is printed with color-coded indicators:
239
+
240
+ ```
241
+ ━━━ ApexRAG Navigation Start ━━━
242
+ Query : What are the Q3 revenue figures?
243
+ Root : node_id=1
244
+
245
+ ↳ ENTER node=1 path=1
246
+ Covers the full annual financial report for 2024…
247
+ ⟳ EXPLORE node=1 β†’ evaluating 2 child summaries
248
+ βœ” AGENT β†’ node=3 reason: Revenue Analysis contains quarterly breakdown
249
+ ↳ ENTER node=3 path=1.2
250
+ ⟳ EXPLORE node=3 β†’ evaluating 4 child summaries
251
+ βœ” AGENT β†’ node=6 reason: Q3 Revenue section is exactly what's needed
252
+ β˜… LEAF REACHED node=6
253
+ preview: Q3 revenue was $165M. Growth slowed slightly…
254
+
255
+ ━━━ Navigation Complete ━━━ result=SUCCESS elapsed=3.41s
256
+ ```
257
+
258
+ ---
259
+
260
+ ## πŸ§ͺ Testing
261
+
262
+ ```bash
263
+ # Run all tests (no Ollama required)
264
+ pytest
265
+
266
+ # With coverage
267
+ pytest --cov=src --cov-report=term-missing
268
+
269
+ # Specific test file
270
+ pytest tests/test_search.py -v
271
+ ```
272
+
273
+ Tests use an in-memory SQLite database and mock LLM responses β€” zero external dependencies.
274
+
275
+ ---
276
+
277
+ ## 🐳 Production Deployment
278
+
279
+ ```bash
280
+ # Copy and edit environment
281
+ cp .env.example .env
282
+
283
+ # Start everything (Ollama + PostgreSQL + API)
284
+ docker-compose up -d
285
+
286
+ # Pull the model inside the Ollama container
287
+ docker exec apex_ollama ollama pull llama3.1
288
+ ```
289
+
290
+ **Environment variables:**
291
+
292
+ | Variable | Default | Description |
293
+ |----------|---------|-------------|
294
+ | `APEX_DB_URL` | `sqlite+aiosqlite:///apex.db` | SQLAlchemy async DB URL |
295
+ | `APEX_OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
296
+ | `APEX_MODEL` | `llama3.1` | Ollama model for navigation |
297
+ | `APEX_LOG_LEVEL` | `INFO` | Logging verbosity |
298
+
299
+ ---
300
+
301
+ ## πŸ”§ Configuration Reference
302
+
303
+ ```python
304
+ await ApexIndex.create(
305
+ db_url="postgresql+asyncpg://user:pass@host/db", # Production DB
306
+ ollama_host="http://localhost:11434",
307
+ model="llama3.1", # Navigation model
308
+ summariser_model="phi3", # Cheaper model for ingestion summaries
309
+ max_concurrent_summaries=8, # Parallelism (tune to your GPU VRAM)
310
+ parser_backend="markitdown", # "markitdown" | "docling" | "plaintext"
311
+ trace_enabled=True, # Color-coded console output
312
+ db_echo=False, # SQL query logging
313
+ )
314
+ ```
315
+
316
+ ---
317
+
318
+ ## πŸ“‹ Roadmap
319
+
320
+ - [x] FastAPI REST API wrapper (`/documents/ingest/file`, `/query`, `/documents`)
321
+ - [x] Book-style Page Index and Visual tree dashboard
322
+ - [x] Unlimited navigation depth with backtrack and verification
323
+ - [ ] Streaming query responses via SSE
324
+ - [ ] Multi-document cross-reference queries
325
+ - [ ] Alembic migrations for schema versioning
326
+ - [ ] Support for `docling` table extraction (structured data cells as leaf nodes)
327
+
328
+ ---
329
+
330
+ ## πŸ“„ License
331
+
332
+ MIT License β€” see [LICENSE](LICENSE).
@@ -0,0 +1,264 @@
1
+ # ApexRAG
2
+
3
+ > **Production-grade, local-first Agentic RAG Library.**
4
+ > Replaces vector similarity search with structural, agentic navigation of documents.
5
+
6
+ ---
7
+
8
+ ## 🧠 The Core Idea
9
+
10
+ Traditional RAG embeds text into vectors and finds the "closest" chunks. This creates **retrieval hallucinations** β€” the model returns semantically-similar-but-wrong content because it has no understanding of document structure.
11
+
12
+ **ApexRAG takes a fundamentally different approach:**
13
+
14
+ 1. **Parse** the document into a structural tree (based on headings) and extract page numbers.
15
+ 2. **Synthesize** a 30-word *Semantic Map* for every node using a local LLM.
16
+ 3. **Navigate** the tree with an LLM agent that reads summaries and decides which branch to enter β€” trying multiple candidates if necessary.
17
+ 4. **Verify** the exact leaf node answers the query via a strict secondary LLM check (99.999% accuracy).
18
+ 5. **Return** the exact leaf node content β€” not a blended, hallucinated average.
19
+
20
+ ```
21
+ Query: "What were Q3 revenues?"
22
+ β”‚
23
+ Root (Annual Report)
24
+ β”œβ”€β”€ Chapter 1: Executive Summary ← LLM: "Not here"
25
+ └── Chapter 2: Revenue Analysis ← LLM: "Enter this"
26
+ β”œβ”€β”€ Q1 Revenue ← LLM: "Not Q3"
27
+ β”œβ”€β”€ Q2 Revenue ← LLM: "Not Q3"
28
+ └── Q3 Revenue ← LLM: "This is it!" β†’ Return content
29
+ ```
30
+
31
+ ---
32
+
33
+ ## πŸ“ Project Structure
34
+
35
+ ```
36
+ apex_rag/
37
+ β”œβ”€β”€ src/
38
+ β”‚ β”œβ”€β”€ __init__.py # Public API exports
39
+ β”‚ β”œβ”€β”€ api.py # FastAPI App & UI dashboard
40
+ β”‚ β”œβ”€β”€ client.py # Thread-safe user-facing ApexIndex class
41
+ β”‚ β”œβ”€β”€ ingestion.py # Document parsing & tree synthesis
42
+ β”‚ β”œβ”€β”€ navigation.py # Recursive LLM navigation agent
43
+ β”‚ β”œβ”€β”€ storage.py # SQLAlchemy async ORM & PageIndexEntry
44
+ β”‚ └── utils.py # ReasoningTrace, retry decorator, helpers
45
+ β”œβ”€β”€ tests/
46
+ β”‚ β”œβ”€β”€ test_tree.py # Parser & storage unit tests
47
+ β”‚ └── test_search.py # Navigation agent unit tests (no Ollama needed)
48
+ β”œβ”€β”€ examples/
49
+ β”‚ └── basic_usage.py # End-to-end demo
50
+ β”œβ”€β”€ pyproject.toml
51
+ └── docker-compose.yml
52
+ ```
53
+
54
+ ---
55
+
56
+ ## ⚑ Quick Start
57
+
58
+ ### 1. Install
59
+
60
+ ```bash
61
+ # Clone and set up
62
+ cd ApexRAG
63
+ pip install -e ".[dev]"
64
+ ```
65
+
66
+ ### 2. Start Ollama
67
+
68
+ ```bash
69
+ ollama serve
70
+ ollama pull llama3.1 # or phi3, mistral, etc.
71
+ ```
72
+
73
+ ### 3. Ingest & Query
74
+
75
+ ```python
76
+ import asyncio
77
+ from src.client import ApexIndex
78
+
79
+ async def main():
80
+ async with await ApexIndex.create(
81
+ db_url="sqlite+aiosqlite:///apex.db",
82
+ model="llama3.1",
83
+ ) as index:
84
+ # Ingest a PDF
85
+ doc_id = await index.ingest("path/to/your/report.pdf")
86
+
87
+ # Query it
88
+ result = await index.query(
89
+ "What are the Q3 revenue figures?",
90
+ doc_id,
91
+ )
92
+
93
+ if result:
94
+ print(result.content)
95
+ print(f"Found at path: {result.path}")
96
+ print(f"Navigation trace: {result.trace}")
97
+
98
+ asyncio.run(main())
99
+ ```
100
+
101
+ ### 4. Start the FastAPI Server & Visual Index Dashboard
102
+
103
+ ```bash
104
+ uvicorn src.api:app --reload
105
+ ```
106
+
107
+ Open your browser to:
108
+ - **Dashboard:** [http://localhost:8000](http://localhost:8000)
109
+ - **API Docs:** [http://localhost:8000/docs](http://localhost:8000/docs)
110
+
111
+ From the dashboard, you can click on an ingested document to view its full structural tree and its book-style alphabetical page index!
112
+
113
+ ---
114
+
115
+ ## πŸ—οΈ Architecture Deep Dive
116
+
117
+ ### Ingestion Engine (`ingestion.py`)
118
+
119
+ | Step | Description |
120
+ |------|-------------|
121
+ | **Convert** | `markitdown` or `docling` converts PDF/DOCX β†’ Markdown |
122
+ | **Parse** | Regex walks ATX headings (`#`, `##`, `###`) to build `ParsedSection` tree |
123
+ | **Persist** | Nodes written to DB with LTree-style path (`1.2.3`) |
124
+ | **Synthesize** | Ollama generates 30-word summaries in parallel (bounded by semaphore) |
125
+
126
+ ### Storage Layer (`storage.py`)
127
+
128
+ ```
129
+ DocumentNode table:
130
+ id BIGINT PRIMARY KEY
131
+ doc_id VARCHAR(255) -- logical document identifier
132
+ parent_id BIGINT FK (self) -- NULL for root nodes
133
+ path VARCHAR(512) -- "1.2.3" LTree-style
134
+ title VARCHAR(512) -- section heading
135
+ summary TEXT -- 30-word Semantic Map
136
+ content TEXT -- leaf content (NULL for intermediate)
137
+ metadata TEXT (JSON) -- page numbers, char count, source file
138
+ depth INTEGER -- nesting level (0 = root)
139
+ position INTEGER -- sibling order
140
+ created_at TIMESTAMP
141
+ ```
142
+
143
+ Supports both `sqlite+aiosqlite://` (local) and `postgresql+asyncpg://` (production).
144
+
145
+ ### Navigation Agent (`navigation.py`)
146
+
147
+ ```
148
+ find(query, doc_id)
149
+ └── _navigate(current_node)
150
+ β”œβ”€β”€ [Leaf?] β†’ return content immediately
151
+ β”œβ”€β”€ fetch children
152
+ β”œβ”€β”€ _ask_llm(query, child_summaries)
153
+ β”‚ └── "Which child ID contains the answer?"
154
+ β”œβ”€β”€ [ID returned] β†’ recurse into chosen child
155
+ β”‚ └── [child returns None] β†’ try siblings
156
+ └── [NONE returned] β†’ backtrack to parent
157
+ ```
158
+
159
+ **LLM Response Parsing** is robust β€” 4-tier fallback:
160
+ 1. Strict `json.loads()`
161
+ 2. Regex extraction from prose-wrapped JSON
162
+ 3. Explicit `"NONE"` keyword detection
163
+ 4. Heuristic: scan for any valid child ID number in the response
164
+
165
+ **High Accuracy (99.999%) Verification:**
166
+ At the leaf level, a second LLM prompt strictly verifies if the leaf content answers the query. If it fails, the agent backtracks and explores the fallback candidates (second-best choices) up the tree.
167
+
168
+ ### Reasoning Trace (`utils.py`)
169
+
170
+ Every navigation decision is printed with color-coded indicators:
171
+
172
+ ```
173
+ ━━━ ApexRAG Navigation Start ━━━
174
+ Query : What are the Q3 revenue figures?
175
+ Root : node_id=1
176
+
177
+ ↳ ENTER node=1 path=1
178
+ Covers the full annual financial report for 2024…
179
+ ⟳ EXPLORE node=1 β†’ evaluating 2 child summaries
180
+ βœ” AGENT β†’ node=3 reason: Revenue Analysis contains quarterly breakdown
181
+ ↳ ENTER node=3 path=1.2
182
+ ⟳ EXPLORE node=3 β†’ evaluating 4 child summaries
183
+ βœ” AGENT β†’ node=6 reason: Q3 Revenue section is exactly what's needed
184
+ β˜… LEAF REACHED node=6
185
+ preview: Q3 revenue was $165M. Growth slowed slightly…
186
+
187
+ ━━━ Navigation Complete ━━━ result=SUCCESS elapsed=3.41s
188
+ ```
189
+
190
+ ---
191
+
192
+ ## πŸ§ͺ Testing
193
+
194
+ ```bash
195
+ # Run all tests (no Ollama required)
196
+ pytest
197
+
198
+ # With coverage
199
+ pytest --cov=src --cov-report=term-missing
200
+
201
+ # Specific test file
202
+ pytest tests/test_search.py -v
203
+ ```
204
+
205
+ Tests use an in-memory SQLite database and mock LLM responses β€” zero external dependencies.
206
+
207
+ ---
208
+
209
+ ## 🐳 Production Deployment
210
+
211
+ ```bash
212
+ # Copy and edit environment
213
+ cp .env.example .env
214
+
215
+ # Start everything (Ollama + PostgreSQL + API)
216
+ docker-compose up -d
217
+
218
+ # Pull the model inside the Ollama container
219
+ docker exec apex_ollama ollama pull llama3.1
220
+ ```
221
+
222
+ **Environment variables:**
223
+
224
+ | Variable | Default | Description |
225
+ |----------|---------|-------------|
226
+ | `APEX_DB_URL` | `sqlite+aiosqlite:///apex.db` | SQLAlchemy async DB URL |
227
+ | `APEX_OLLAMA_HOST` | `http://localhost:11434` | Ollama server URL |
228
+ | `APEX_MODEL` | `llama3.1` | Ollama model for navigation |
229
+ | `APEX_LOG_LEVEL` | `INFO` | Logging verbosity |
230
+
231
+ ---
232
+
233
+ ## πŸ”§ Configuration Reference
234
+
235
+ ```python
236
+ await ApexIndex.create(
237
+ db_url="postgresql+asyncpg://user:pass@host/db", # Production DB
238
+ ollama_host="http://localhost:11434",
239
+ model="llama3.1", # Navigation model
240
+ summariser_model="phi3", # Cheaper model for ingestion summaries
241
+ max_concurrent_summaries=8, # Parallelism (tune to your GPU VRAM)
242
+ parser_backend="markitdown", # "markitdown" | "docling" | "plaintext"
243
+ trace_enabled=True, # Color-coded console output
244
+ db_echo=False, # SQL query logging
245
+ )
246
+ ```
247
+
248
+ ---
249
+
250
+ ## πŸ“‹ Roadmap
251
+
252
+ - [x] FastAPI REST API wrapper (`/documents/ingest/file`, `/query`, `/documents`)
253
+ - [x] Book-style Page Index and Visual tree dashboard
254
+ - [x] Unlimited navigation depth with backtrack and verification
255
+ - [ ] Streaming query responses via SSE
256
+ - [ ] Multi-document cross-reference queries
257
+ - [ ] Alembic migrations for schema versioning
258
+ - [ ] Support for `docling` table extraction (structured data cells as leaf nodes)
259
+
260
+ ---
261
+
262
+ ## πŸ“„ License
263
+
264
+ MIT License β€” see [LICENSE](LICENSE).
@@ -0,0 +1,70 @@
1
+ version: "3.9"
2
+
3
+ services:
4
+ # ── Ollama ─────────────────────────────────────────────────────────────
5
+ ollama:
6
+ image: ollama/ollama:latest
7
+ container_name: apex_ollama
8
+ ports:
9
+ - "11434:11434"
10
+ volumes:
11
+ - ollama_data:/root/.ollama
12
+ deploy:
13
+ resources:
14
+ reservations:
15
+ devices:
16
+ - driver: nvidia
17
+ count: all
18
+ capabilities: [gpu]
19
+ restart: unless-stopped
20
+ healthcheck:
21
+ test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
22
+ interval: 30s
23
+ timeout: 10s
24
+ retries: 5
25
+ start_period: 60s
26
+
27
+ # ── PostgreSQL (production storage) ────────────────────────────────────
28
+ postgres:
29
+ image: postgres:16-alpine
30
+ container_name: apex_postgres
31
+ environment:
32
+ POSTGRES_USER: ${POSTGRES_USER:-apex}
33
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-apexsecret}
34
+ POSTGRES_DB: ${POSTGRES_DB:-apex_rag}
35
+ ports:
36
+ - "5432:5432"
37
+ volumes:
38
+ - postgres_data:/var/lib/postgresql/data
39
+ restart: unless-stopped
40
+ healthcheck:
41
+ test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-apex} -d ${POSTGRES_DB:-apex_rag}"]
42
+ interval: 10s
43
+ timeout: 5s
44
+ retries: 5
45
+
46
+ # ── ApexRAG FastAPI Service ─────────────────────────────────────────────
47
+ apex_api:
48
+ build:
49
+ context: .
50
+ dockerfile: Dockerfile
51
+ container_name: apex_api
52
+ ports:
53
+ - "8000:8000"
54
+ environment:
55
+ APEX_DB_URL: postgresql+asyncpg://${POSTGRES_USER:-apex}:${POSTGRES_PASSWORD:-apexsecret}@postgres:5432/${POSTGRES_DB:-apex_rag}
56
+ APEX_OLLAMA_HOST: http://ollama:11434
57
+ APEX_MODEL: ${APEX_MODEL:-llama3.1}
58
+ APEX_LOG_LEVEL: ${APEX_LOG_LEVEL:-INFO}
59
+ depends_on:
60
+ postgres:
61
+ condition: service_healthy
62
+ ollama:
63
+ condition: service_healthy
64
+ volumes:
65
+ - ./uploads:/app/uploads
66
+ restart: unless-stopped
67
+
68
+ volumes:
69
+ ollama_data:
70
+ postgres_data: