@alextis59/athena 1.0.0

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 (201) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +147 -0
  3. package/dist/agent/ask.d.ts +7 -0
  4. package/dist/agent/ask.d.ts.map +1 -0
  5. package/dist/agent/ask.js +35 -0
  6. package/dist/agent/ask.js.map +1 -0
  7. package/dist/agent/citations.d.ts +27 -0
  8. package/dist/agent/citations.d.ts.map +1 -0
  9. package/dist/agent/citations.js +190 -0
  10. package/dist/agent/citations.js.map +1 -0
  11. package/dist/agent/fake-provider.d.ts +9 -0
  12. package/dist/agent/fake-provider.d.ts.map +1 -0
  13. package/dist/agent/fake-provider.js +51 -0
  14. package/dist/agent/fake-provider.js.map +1 -0
  15. package/dist/agent/index.d.ts +10 -0
  16. package/dist/agent/index.d.ts.map +1 -0
  17. package/dist/agent/index.js +6 -0
  18. package/dist/agent/index.js.map +1 -0
  19. package/dist/agent/loop.d.ts +11 -0
  20. package/dist/agent/loop.d.ts.map +1 -0
  21. package/dist/agent/loop.js +156 -0
  22. package/dist/agent/loop.js.map +1 -0
  23. package/dist/agent/providers.d.ts +17 -0
  24. package/dist/agent/providers.d.ts.map +1 -0
  25. package/dist/agent/providers.js +171 -0
  26. package/dist/agent/providers.js.map +1 -0
  27. package/dist/agent/tools.d.ts +8 -0
  28. package/dist/agent/tools.d.ts.map +1 -0
  29. package/dist/agent/tools.js +233 -0
  30. package/dist/agent/tools.js.map +1 -0
  31. package/dist/agent/types.d.ts +84 -0
  32. package/dist/agent/types.d.ts.map +1 -0
  33. package/dist/agent/types.js +2 -0
  34. package/dist/agent/types.js.map +1 -0
  35. package/dist/cli/help.d.ts +2 -0
  36. package/dist/cli/help.d.ts.map +1 -0
  37. package/dist/cli/help.js +28 -0
  38. package/dist/cli/help.js.map +1 -0
  39. package/dist/cli/main.d.ts +3 -0
  40. package/dist/cli/main.d.ts.map +1 -0
  41. package/dist/cli/main.js +5 -0
  42. package/dist/cli/main.js.map +1 -0
  43. package/dist/cli/options.d.ts +21 -0
  44. package/dist/cli/options.d.ts.map +1 -0
  45. package/dist/cli/options.js +97 -0
  46. package/dist/cli/options.js.map +1 -0
  47. package/dist/cli/run.d.ts +21 -0
  48. package/dist/cli/run.d.ts.map +1 -0
  49. package/dist/cli/run.js +144 -0
  50. package/dist/cli/run.js.map +1 -0
  51. package/dist/config/index.d.ts +4 -0
  52. package/dist/config/index.d.ts.map +1 -0
  53. package/dist/config/index.js +3 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/config/loader.d.ts +14 -0
  56. package/dist/config/loader.d.ts.map +1 -0
  57. package/dist/config/loader.js +352 -0
  58. package/dist/config/loader.js.map +1 -0
  59. package/dist/config/schema.d.ts +6 -0
  60. package/dist/config/schema.d.ts.map +1 -0
  61. package/dist/config/schema.js +72 -0
  62. package/dist/config/schema.js.map +1 -0
  63. package/dist/config/types.d.ts +53 -0
  64. package/dist/config/types.d.ts.map +1 -0
  65. package/dist/config/types.js +2 -0
  66. package/dist/config/types.js.map +1 -0
  67. package/dist/doctor/doctor.d.ts +47 -0
  68. package/dist/doctor/doctor.d.ts.map +1 -0
  69. package/dist/doctor/doctor.js +159 -0
  70. package/dist/doctor/doctor.js.map +1 -0
  71. package/dist/embeddings/index.d.ts +117 -0
  72. package/dist/embeddings/index.d.ts.map +1 -0
  73. package/dist/embeddings/index.js +515 -0
  74. package/dist/embeddings/index.js.map +1 -0
  75. package/dist/errors.d.ts +19 -0
  76. package/dist/errors.d.ts.map +1 -0
  77. package/dist/errors.js +37 -0
  78. package/dist/errors.js.map +1 -0
  79. package/dist/index.d.ts +17 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +9 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/indexer/discovery.d.ts +10 -0
  84. package/dist/indexer/discovery.d.ts.map +1 -0
  85. package/dist/indexer/discovery.js +38 -0
  86. package/dist/indexer/discovery.js.map +1 -0
  87. package/dist/indexer/hash.d.ts +3 -0
  88. package/dist/indexer/hash.d.ts.map +1 -0
  89. package/dist/indexer/hash.js +11 -0
  90. package/dist/indexer/hash.js.map +1 -0
  91. package/dist/indexer/index.d.ts +7 -0
  92. package/dist/indexer/index.d.ts.map +1 -0
  93. package/dist/indexer/index.js +5 -0
  94. package/dist/indexer/index.js.map +1 -0
  95. package/dist/indexer/kinds.d.ts +4 -0
  96. package/dist/indexer/kinds.d.ts.map +1 -0
  97. package/dist/indexer/kinds.js +38 -0
  98. package/dist/indexer/kinds.js.map +1 -0
  99. package/dist/indexer/run.d.ts +42 -0
  100. package/dist/indexer/run.d.ts.map +1 -0
  101. package/dist/indexer/run.js +563 -0
  102. package/dist/indexer/run.js.map +1 -0
  103. package/dist/logging/logger.d.ts +11 -0
  104. package/dist/logging/logger.d.ts.map +1 -0
  105. package/dist/logging/logger.js +18 -0
  106. package/dist/logging/logger.js.map +1 -0
  107. package/dist/mcp/index.d.ts +35 -0
  108. package/dist/mcp/index.d.ts.map +1 -0
  109. package/dist/mcp/index.js +392 -0
  110. package/dist/mcp/index.js.map +1 -0
  111. package/dist/parsers/chunking.d.ts +4 -0
  112. package/dist/parsers/chunking.d.ts.map +1 -0
  113. package/dist/parsers/chunking.js +112 -0
  114. package/dist/parsers/chunking.js.map +1 -0
  115. package/dist/parsers/index.d.ts +8 -0
  116. package/dist/parsers/index.d.ts.map +1 -0
  117. package/dist/parsers/index.js +5 -0
  118. package/dist/parsers/index.js.map +1 -0
  119. package/dist/parsers/line-parser.d.ts +13 -0
  120. package/dist/parsers/line-parser.d.ts.map +1 -0
  121. package/dist/parsers/line-parser.js +242 -0
  122. package/dist/parsers/line-parser.js.map +1 -0
  123. package/dist/parsers/normalize.d.ts +4 -0
  124. package/dist/parsers/normalize.d.ts.map +1 -0
  125. package/dist/parsers/normalize.js +17 -0
  126. package/dist/parsers/normalize.js.map +1 -0
  127. package/dist/parsers/parser.d.ts +14 -0
  128. package/dist/parsers/parser.d.ts.map +1 -0
  129. package/dist/parsers/parser.js +77 -0
  130. package/dist/parsers/parser.js.map +1 -0
  131. package/dist/parsers/pdf2llm.d.ts +14 -0
  132. package/dist/parsers/pdf2llm.d.ts.map +1 -0
  133. package/dist/parsers/pdf2llm.js +215 -0
  134. package/dist/parsers/pdf2llm.js.map +1 -0
  135. package/dist/parsers/types.d.ts +48 -0
  136. package/dist/parsers/types.d.ts.map +1 -0
  137. package/dist/parsers/types.js +2 -0
  138. package/dist/parsers/types.js.map +1 -0
  139. package/dist/retrieval/index.d.ts +4 -0
  140. package/dist/retrieval/index.d.ts.map +1 -0
  141. package/dist/retrieval/index.js +3 -0
  142. package/dist/retrieval/index.js.map +1 -0
  143. package/dist/retrieval/schemas.d.ts +127 -0
  144. package/dist/retrieval/schemas.d.ts.map +1 -0
  145. package/dist/retrieval/schemas.js +65 -0
  146. package/dist/retrieval/schemas.js.map +1 -0
  147. package/dist/retrieval/tools.d.ts +31 -0
  148. package/dist/retrieval/tools.d.ts.map +1 -0
  149. package/dist/retrieval/tools.js +803 -0
  150. package/dist/retrieval/tools.js.map +1 -0
  151. package/dist/retrieval/types.d.ts +104 -0
  152. package/dist/retrieval/types.d.ts.map +1 -0
  153. package/dist/retrieval/types.js +2 -0
  154. package/dist/retrieval/types.js.map +1 -0
  155. package/dist/server/index.d.ts +3 -0
  156. package/dist/server/index.d.ts.map +1 -0
  157. package/dist/server/index.js +2 -0
  158. package/dist/server/index.js.map +1 -0
  159. package/dist/server/server.d.ts +49 -0
  160. package/dist/server/server.d.ts.map +1 -0
  161. package/dist/server/server.js +517 -0
  162. package/dist/server/server.js.map +1 -0
  163. package/dist/shared/types.d.ts +98 -0
  164. package/dist/shared/types.d.ts.map +1 -0
  165. package/dist/shared/types.js +2 -0
  166. package/dist/shared/types.js.map +1 -0
  167. package/dist/storage/database.d.ts +18 -0
  168. package/dist/storage/database.d.ts.map +1 -0
  169. package/dist/storage/database.js +66 -0
  170. package/dist/storage/database.js.map +1 -0
  171. package/dist/storage/index.d.ts +8 -0
  172. package/dist/storage/index.d.ts.map +1 -0
  173. package/dist/storage/index.js +5 -0
  174. package/dist/storage/index.js.map +1 -0
  175. package/dist/storage/migrations.d.ts +15 -0
  176. package/dist/storage/migrations.d.ts.map +1 -0
  177. package/dist/storage/migrations.js +170 -0
  178. package/dist/storage/migrations.js.map +1 -0
  179. package/dist/storage/paths.d.ts +15 -0
  180. package/dist/storage/paths.d.ts.map +1 -0
  181. package/dist/storage/paths.js +27 -0
  182. package/dist/storage/paths.js.map +1 -0
  183. package/dist/storage/schema.d.ts +11 -0
  184. package/dist/storage/schema.d.ts.map +1 -0
  185. package/dist/storage/schema.js +40 -0
  186. package/dist/storage/schema.js.map +1 -0
  187. package/dist/ui/index.d.ts +2 -0
  188. package/dist/ui/index.d.ts.map +1 -0
  189. package/dist/ui/index.js +2 -0
  190. package/dist/ui/index.js.map +1 -0
  191. package/dist/ui/static-app.d.ts +2 -0
  192. package/dist/ui/static-app.d.ts.map +1 -0
  193. package/dist/ui/static-app.js +735 -0
  194. package/dist/ui/static-app.js.map +1 -0
  195. package/docs/adr/0001-sqlite-driver.md +23 -0
  196. package/docs/agentic-rag-specifications.md +659 -0
  197. package/docs/agentic-rag-study.md +1056 -0
  198. package/docs/implementation-plan.md +1425 -0
  199. package/docs/mcp-integration.md +154 -0
  200. package/docs/validation-corpus.md +71 -0
  201. package/package.json +79 -0
@@ -0,0 +1,1056 @@
1
+ # Agentic RAG Tool Design Study
2
+
3
+ Date: 2026-07-03
4
+
5
+ Source chat: https://chatgpt.com/share/6a47e6fe-1124-83ed-b2b6-8d3321dbcfcc
6
+
7
+ This study turns the chat session into an implementation-oriented plan for a
8
+ lightweight Node.js documentation RAG tool. It treats `athena` as the project
9
+ that will own the CLI, index, retrieval engine, local chat UI, and optional
10
+ agent integrations.
11
+
12
+ Naming convention:
13
+
14
+ - Project name: `athena`.
15
+ - npm package name: `@alextis59/athena`.
16
+ - CLI binary/command name: `athena`.
17
+ - Package-run form: `npx @alextis59/athena`.
18
+ - Local cache directory: `.athena/`.
19
+
20
+ ## Executive Summary
21
+
22
+ The target product is a local-first documentation assistant that can be started
23
+ from any documentation folder with a single Node command. It should discover
24
+ docs, parse them, build a reusable cache, launch a local web chat UI, and answer
25
+ questions with exact source references.
26
+
27
+ The settled direction from the chat is:
28
+
29
+ ```txt
30
+ Node.js + TypeScript CLI
31
+ + custom agent loop with no agent SDK dependency
32
+ + local web chat UI
33
+ + SQLite cache/index
34
+ + SQLite FTS5 lexical search
35
+ + embedded vector search
36
+ + local default embeddings
37
+ + optional cloud embeddings
38
+ + pdf-2-llm for PDF ingestion
39
+ + exact citations into source files and PDF page regions
40
+ ```
41
+
42
+ The key architectural decision is that no SDK should handle Athena's agentic
43
+ part. Athena should implement the agent loop directly: model-provider adapters,
44
+ JSON-schema tools, tool execution, step limits, streaming, memory compaction, and
45
+ citation validation. Existing agent hosts such as OpenCode can still be
46
+ supported later through MCP or custom tools, but they should call Athena's
47
+ retrieval API rather than replace it.
48
+
49
+ The second major decision is that PDF handling must be treated as a serious
50
+ ingestion subsystem. The chat originally explored building a custom PDF parser,
51
+ then updated the plan once the user pointed to `pdf-2-llm`. The final design
52
+ should use the published `pdf-2-llm` npm package as the canonical PDF parser and
53
+ consume its Markdown, source maps, document IR, assets, warnings, diagnostics,
54
+ confidence scores, and table sidecars.
55
+
56
+ The third major decision is local-first embeddings. Athena should default to a
57
+ small, fast, open-source embedding model executed locally, with cloud embeddings
58
+ available as optional provider adapters. A good first default is
59
+ `mixedbread-ai/mxbai-embed-xsmall-v1` via Transformers.js or ONNX. It is an
60
+ open-source English retrieval model with 384-dimensional embeddings and 4096
61
+ context support, which keeps a local SQLite vector index practical.
62
+
63
+ ## Product Goals
64
+
65
+ Athena should optimize for documentation lookup in large local repositories,
66
+ SDK docs, generated reference docs, PDFs, Markdown handbooks, and mixed-format
67
+ technical archives.
68
+
69
+ Primary goals:
70
+
71
+ - Run from a folder with one command.
72
+ - Build and reuse a local index.
73
+ - Avoid reprocessing unchanged files.
74
+ - Answer questions through a local chat UI.
75
+ - Use retrieval tools rather than stuffing entire docs into the model context.
76
+ - Return exact evidence references for every substantive answer.
77
+ - Let users click citations to open the source file, line range, Markdown
78
+ anchor, or PDF page region.
79
+ - Preserve useful follow-up context without replaying every intermediate tool
80
+ call.
81
+ - Keep the default stack lightweight and Node-native.
82
+ - Keep private documentation local by default.
83
+
84
+ Non-goals for the first version:
85
+
86
+ - Hosted multi-tenant service.
87
+ - Distributed vector database.
88
+ - Full replacement for a coding agent.
89
+ - Fully autonomous repository editing.
90
+ - Perfect PDF reconstruction for every malformed or scanned PDF.
91
+ - Large framework dependency as the agent runtime.
92
+
93
+ ## Default Stack
94
+
95
+ | Layer | Recommended Default | Notes |
96
+ |---|---|---|
97
+ | Runtime | Node.js + TypeScript | Matches the single-command requirement. |
98
+ | CLI | `athena` | The project and command are named `athena`; `npx @alextis59/athena` is only the package-run form. |
99
+ | Agent loop | Custom implementation | No SDK owns tool calling, step control, memory, or citation validation. |
100
+ | Chat UI | Local web app served by the CLI | Gives control over citations and source previews. |
101
+ | Cache/index | SQLite | One portable local database file. |
102
+ | Lexical search | SQLite FTS5 | Good for exact API names, symbols, flags, and errors. |
103
+ | Vector search | `sqlite-vec` or equivalent embedded SQLite vector extension | Keeps semantic search local and serverless. |
104
+ | Default embeddings | `mixedbread-ai/mxbai-embed-xsmall-v1` | Small local open-source retrieval model. |
105
+ | Embedding runtime | Transformers.js/ONNX adapter first | In-process local embedding path. |
106
+ | Optional local embeddings | Ollama adapter | Useful for users already running local models. |
107
+ | Optional cloud embeddings | OpenAI, Voyage, Cohere, Gemini adapters | Opt-in quality/cost tradeoff. |
108
+ | PDF parser | `pdf-2-llm` | Use the published npm package and its structured result, not plain extracted text. |
109
+ | Optional agent integration | MCP/OpenCode | Expose Athena's retrieval tools externally. |
110
+
111
+ SQLite FTS5 is a native SQLite virtual table module for full-text search, so it
112
+ is a good lexical-search base for a local index. `sqlite-vec` is a lightweight
113
+ SQLite vector-search extension with Node bindings and no separate vector server.
114
+ Those properties match the "single local command" requirement.
115
+
116
+ ## Alternatives and Tradeoffs from the Chat
117
+
118
+ The chat evaluated several existing tools before settling on the custom core.
119
+ Those tools are still useful context:
120
+
121
+ - Vercel AI SDK was the initial recommendation for streaming, tool calling, and
122
+ multi-step agent behavior, but the final decision is to avoid SDK-managed
123
+ agentic behavior entirely. Athena should implement its own agent loop and use
124
+ third-party SDKs only as optional model-provider adapters when needed.
125
+ - `remark`/`unified` remain good candidates for Markdown and MDX parsing because
126
+ their syntax-tree model can preserve positions for line-level citations.
127
+ - LlamaIndex.TS and LangChain/LangGraph are useful references for agentic RAG
128
+ patterns, but they should not own Athena's runtime.
129
+ - OpenAI File Search is a viable hosted alternative for teams that accept remote
130
+ storage and model-provider coupling, but it is not the default because Athena
131
+ is intended to be local-first and citation-controlled.
132
+ - OpenCode is an integration target, not the product core. It can call Athena's
133
+ tools through MCP after the index and citation model are reliable.
134
+
135
+ The final architecture keeps these options as reference points or adapters
136
+ rather than foundations.
137
+
138
+ ## User Experience
139
+
140
+ The intended workflow:
141
+
142
+ ```bash
143
+ cd path/to/docs-or-project
144
+ athena
145
+ ```
146
+
147
+ For ad hoc use without installing the binary first:
148
+
149
+ ```bash
150
+ npx @alextis59/athena
151
+ ```
152
+
153
+ The command should:
154
+
155
+ 1. Discover supported files.
156
+ 2. Create or update `.athena/index.sqlite`.
157
+ 3. Hash files and skip unchanged content.
158
+ 4. Convert PDFs through `pdf-2-llm`.
159
+ 5. Parse Markdown, HTML, text, and code-adjacent docs.
160
+ 6. Chunk content with stable source locations.
161
+ 7. Build lexical and vector indexes.
162
+ 8. Launch a local web UI, for example `http://localhost:4317`.
163
+ 9. Let the user ask questions.
164
+ 10. Answer with citations that can be opened in context.
165
+
166
+ Common CLI modes:
167
+
168
+ ```bash
169
+ athena # index if needed, then launch UI
170
+ athena index # build/update index only
171
+ athena serve # serve existing index and UI
172
+ athena ask "..." # one-shot terminal question
173
+ athena doctor # inspect parser/index/runtime health
174
+ athena mcp # optional MCP server for external agents
175
+ ```
176
+
177
+ ## Repository and Cache Layout
178
+
179
+ The local cache should live inside the indexed folder and be easy to remove:
180
+
181
+ ```txt
182
+ .athena/
183
+ index.sqlite
184
+ blobs/
185
+ extracted-assets/
186
+ pdf-sidecars/
187
+ models/
188
+ embeddings/
189
+ logs/
190
+ runs/
191
+ ```
192
+
193
+ The cache should include enough version metadata to invalidate only what is
194
+ necessary:
195
+
196
+ - Athena index schema version.
197
+ - parser version by file type.
198
+ - `pdf-2-llm` package version and option hash.
199
+ - embedding provider, model, dimensions, pooling, normalization, and quantization.
200
+ - chunking strategy version.
201
+ - source file path, size, mtime, and content hash.
202
+ - source-map hash for PDFs and generated Markdown.
203
+
204
+ ## Ingestion Pipeline
205
+
206
+ The ingestion pipeline should be deterministic and resumable:
207
+
208
+ ```txt
209
+ discover files
210
+ -> classify by type
211
+ -> hash source bytes
212
+ -> skip unchanged sources
213
+ -> parse to normalized document records
214
+ -> chunk with stable source anchors
215
+ -> embed chunks
216
+ -> write FTS rows
217
+ -> write vector rows
218
+ -> write source-map and asset metadata
219
+ ```
220
+
221
+ Supported first-wave formats:
222
+
223
+ - Markdown: `.md`, `.mdx`.
224
+ - Plain text: `.txt`, `.rst`, `.adoc`.
225
+ - HTML: `.html`, `.htm`.
226
+ - PDF: `.pdf` through `pdf-2-llm`.
227
+ - Code documentation files: `README`, `CHANGELOG`, `CONTRIBUTING`, package docs.
228
+
229
+ File discovery should default to documentation-heavy paths and avoid expensive
230
+ or generated directories:
231
+
232
+ ```txt
233
+ include:
234
+ **/*.md
235
+ **/*.mdx
236
+ **/*.txt
237
+ **/*.rst
238
+ **/*.adoc
239
+ **/*.html
240
+ **/*.pdf
241
+
242
+ exclude:
243
+ node_modules/**
244
+ .git/**
245
+ dist/**
246
+ build/**
247
+ coverage/**
248
+ .athena/**
249
+ ```
250
+
251
+ The implementation should allow users to override both include and exclude
252
+ rules in a config file.
253
+
254
+ ## PDF Ingestion with `pdf-2-llm`
255
+
256
+ The chat's final PDF decision is clear: Athena should not implement its own PDF
257
+ layout system as a parallel project. It should consume the existing
258
+ `pdf-2-llm` output contract.
259
+
260
+ `pdf-2-llm` is now a public npm package. As of July 4, 2026, the npm registry
261
+ shows `pdf-2-llm@1.1.0` as `latest`, licensed `0BSD`, with CLI binaries named
262
+ `pdf-2-llm` and `pdf2md`. The package exports Node, browser, worker, WASM, and
263
+ schema entrypoints.
264
+
265
+ Install it normally:
266
+
267
+ ```bash
268
+ npm install pdf-2-llm
269
+ ```
270
+
271
+ The package exposes:
272
+
273
+ - Markdown output.
274
+ - source-map entries back to PDF page regions.
275
+ - document IR.
276
+ - sidecar assets, including tables and extraction artifacts.
277
+ - warning codes.
278
+ - diagnostics.
279
+ - confidence scores.
280
+ - Node, browser, worker, CLI, schema, and WASM-related entrypoints.
281
+
282
+ Athena should treat this as the default PDF dependency, not as a local workspace
283
+ placeholder.
284
+
285
+ Recommended conversion path:
286
+
287
+ ```ts
288
+ import { readFile } from "node:fs/promises";
289
+ import { convertPdfToMarkdown } from "pdf-2-llm/node";
290
+
291
+ export async function parsePdf(path: string) {
292
+ const bytes = await readFile(path);
293
+ return convertPdfToMarkdown(bytes, {
294
+ markdown: { pageAnchors: true },
295
+ tables: { csvSidecars: true },
296
+ security: { timeoutMs: 30_000 }
297
+ });
298
+ }
299
+ ```
300
+
301
+ Athena should store the full structured result, not just the Markdown string.
302
+ The source map and IR are essential to exact citations, PDF highlights, table
303
+ references, extraction-confidence ranking, and future debugging.
304
+
305
+ ## PDF Alternatives and Fallback Strategy
306
+
307
+ Before the `pdf-2-llm` update, the chat explored a layered PDF strategy:
308
+
309
+ - `pdfjs` as a JavaScript-friendly baseline engine;
310
+ - MuPDF as a stronger native extraction engine when packaging complexity is
311
+ acceptable;
312
+ - a robust mode with OCR and heavier layout recovery for difficult PDFs;
313
+ - Docling and PyMuPDF4LLM as references or optional plugins, not default
314
+ dependencies.
315
+
316
+ After the user identified `pdf-2-llm`, the right move is to use that library as
317
+ the primary parser. The fallback concepts still matter operationally:
318
+
319
+ - low-confidence `pdf-2-llm` output should trigger warnings in the UI;
320
+ - scanned PDFs should route to OCR-capable workflows when available;
321
+ - parser diagnostics should be stored so users can understand extraction limits;
322
+ - Athena should keep the parser adapter boundary clean enough to add a MuPDF,
323
+ Docling, or PyMuPDF4LLM plugin later if `pdf-2-llm` cannot handle a corpus.
324
+
325
+ ## PDF Citation Model
326
+
327
+ For Markdown and text files, a citation can be:
328
+
329
+ ```json
330
+ {
331
+ "kind": "text",
332
+ "path": "docs/example.md",
333
+ "startLine": 42,
334
+ "endLine": 55,
335
+ "snippetHash": "..."
336
+ }
337
+ ```
338
+
339
+ For PDFs, a citation should use both the generated Markdown location and the PDF
340
+ source-map location:
341
+
342
+ ```json
343
+ {
344
+ "kind": "pdf",
345
+ "path": "manual.pdf",
346
+ "markdownOffsetStart": 1204,
347
+ "markdownOffsetEnd": 1422,
348
+ "page": 17,
349
+ "regions": [
350
+ { "x": 72, "y": 144, "width": 440, "height": 88 }
351
+ ],
352
+ "confidence": 0.91,
353
+ "warningCodes": []
354
+ }
355
+ ```
356
+
357
+ The UI should show both:
358
+
359
+ - an answer-side citation label, such as `[manual.pdf p.17]`;
360
+ - a source panel with the exact converted Markdown snippet and a page-region
361
+ preview when possible.
362
+
363
+ The citation validator should reject answer claims that cannot be tied to a
364
+ retrieved chunk or source range.
365
+
366
+ ## Chunking Strategy
367
+
368
+ Chunking should be source-aware rather than purely token-count based.
369
+
370
+ Markdown and HTML chunking:
371
+
372
+ - Prefer heading-bounded chunks.
373
+ - Keep stable heading paths.
374
+ - Preserve line ranges.
375
+ - Keep code blocks intact.
376
+ - Avoid splitting tables unless they are too large.
377
+ - Store neighboring chunk IDs for context expansion.
378
+
379
+ PDF chunking:
380
+
381
+ - Chunk from `pdf-2-llm` Markdown and IR.
382
+ - Preserve PDF page anchors.
383
+ - Keep tables, captions, figures, and paragraphs coherent.
384
+ - Use source-map entries to map chunk spans back to page regions.
385
+ - Mark chunks derived from low-confidence extraction.
386
+ - Keep sidecar references for CSV tables and asset outputs.
387
+
388
+ Chunk size guidelines:
389
+
390
+ - Target 300 to 900 tokens for normal prose.
391
+ - Allow larger chunks for API reference sections with dense examples.
392
+ - Keep a small overlap only when it preserves sentence or list continuity.
393
+ - Store both raw text and normalized searchable text.
394
+
395
+ ## Index Schema
396
+
397
+ A practical first schema can stay small:
398
+
399
+ ```sql
400
+ create table sources (
401
+ id integer primary key,
402
+ path text not null unique,
403
+ kind text not null,
404
+ sha256 text not null,
405
+ size_bytes integer not null,
406
+ mtime_ms integer not null,
407
+ parser_name text not null,
408
+ parser_version text not null,
409
+ parser_options_hash text not null,
410
+ indexed_at text not null
411
+ );
412
+
413
+ create table documents (
414
+ id integer primary key,
415
+ source_id integer not null references sources(id),
416
+ title text,
417
+ metadata_json text not null
418
+ );
419
+
420
+ create table chunks (
421
+ id integer primary key,
422
+ document_id integer not null references documents(id),
423
+ source_id integer not null references sources(id),
424
+ ordinal integer not null,
425
+ heading_path text,
426
+ text text not null,
427
+ normalized_text text not null,
428
+ token_count integer not null,
429
+ locator_json text not null,
430
+ extraction_confidence real,
431
+ warning_codes_json text not null
432
+ );
433
+
434
+ create virtual table chunks_fts using fts5(
435
+ normalized_text,
436
+ heading_path,
437
+ content='chunks',
438
+ content_rowid='id'
439
+ );
440
+
441
+ create table embeddings (
442
+ chunk_id integer primary key references chunks(id),
443
+ provider text not null,
444
+ model text not null,
445
+ dimensions integer not null,
446
+ config_hash text not null,
447
+ vector_blob blob not null
448
+ );
449
+ ```
450
+
451
+ If `sqlite-vec` is used, vector storage can be implemented with a `vec0` virtual
452
+ table or a compatible extension-specific table. Keep the relational metadata in
453
+ normal SQLite tables so vector-extension migrations do not force a full metadata
454
+ rewrite.
455
+
456
+ ## Embedding Model Recommendation
457
+
458
+ The default should be local, open-source, and fast. The chat converged on
459
+ `mixedbread-ai/mxbai-embed-xsmall-v1` as a strong default because it is small,
460
+ retrieval-oriented, 384-dimensional, and supports 4096 context. Those properties
461
+ are attractive for a local SQLite-backed index:
462
+
463
+ - 384 dimensions reduces index size and speeds similarity scoring.
464
+ - 4096 context gives chunking more headroom for long documentation sections.
465
+ - Local inference avoids sending private docs to a cloud service.
466
+ - Apache 2.0 licensing is easier for broad reuse than unclear model terms.
467
+ - Matryoshka and binary-quantization support leave room for future size/speed
468
+ experiments.
469
+
470
+ Recommended default embedding config:
471
+
472
+ ```yaml
473
+ embeddings:
474
+ provider: transformers-js
475
+ model: mixedbread-ai/mxbai-embed-xsmall-v1
476
+ dimensions: 384
477
+ pooling: mean
478
+ normalize: true
479
+ batchSize: 32
480
+ ```
481
+
482
+ Provider alternatives:
483
+
484
+ | Provider | Role | Notes |
485
+ |---|---|---|
486
+ | `transformers-js` | Default in-process local runtime | Best default for single-command Node usage. |
487
+ | `ollama` | Optional local service runtime | Useful when users already manage local models with Ollama. |
488
+ | `openai` | Optional cloud adapter | Simple external quality baseline. |
489
+ | `voyage` | Optional cloud adapter | Consider for code-heavy docs and retrieval quality experiments. |
490
+ | `cohere` | Optional cloud adapter | Consider for multilingual or enterprise setups. |
491
+ | `gemini` | Optional cloud adapter | Consider when already using Google infrastructure. |
492
+
493
+ Ollama should be treated as an optional local provider, not the default, because
494
+ it requires a separate local service. It is still valuable: Ollama documents an
495
+ embedding API and currently recommends models such as `embeddinggemma`,
496
+ `qwen3-embedding`, and `all-minilm`.
497
+
498
+ Cloud embeddings should always be opt-in. The CLI should require explicit
499
+ configuration before sending document text to a remote service.
500
+
501
+ ## Embedding Survey Notes
502
+
503
+ The chat's final recommendation favored `mxbai-embed-xsmall-v1`, but the model
504
+ survey also identified alternatives worth keeping in the design:
505
+
506
+ - `all-MiniLM` is a small, established local baseline and is especially useful
507
+ through Ollama or ONNX distributions.
508
+ - BGE-M3 is a stronger multilingual option with dense, sparse, and multi-vector
509
+ retrieval behavior, but it is heavier than the default.
510
+ - Qwen3 embedding models are promising quality-oriented local options,
511
+ especially for multilingual and code-heavy retrieval, but should be optional.
512
+ - EmbeddingGemma is a useful local Ollama option when users already run Ollama.
513
+ - `nomic-embed-text` is another local-friendly option with long-context
514
+ retrieval variants.
515
+ - FastEmbed-style runtimes are relevant for speed, but Athena should keep a
516
+ provider interface so runtime choice can change without rewriting indexing.
517
+ - Matryoshka representation support and binary quantization are future
518
+ optimization paths for smaller indexes and faster local retrieval.
519
+
520
+ The rule should be empirical: ship one fast local default, then compare optional
521
+ providers on Athena's own documentation-evaluation harness.
522
+
523
+ ## Embedding Provider Interface
524
+
525
+ Athena should isolate embeddings behind a small interface:
526
+
527
+ ```ts
528
+ export type EmbeddingInput = {
529
+ id: string;
530
+ text: string;
531
+ metadata?: Record<string, unknown>;
532
+ };
533
+
534
+ export type EmbeddingVector = {
535
+ id: string;
536
+ vector: Float32Array;
537
+ dimensions: number;
538
+ };
539
+
540
+ export interface EmbeddingProvider {
541
+ readonly name: string;
542
+ readonly model: string;
543
+ readonly dimensions: number;
544
+ readonly configHash: string;
545
+
546
+ embedBatch(inputs: EmbeddingInput[]): Promise<EmbeddingVector[]>;
547
+ }
548
+ ```
549
+
550
+ Embedding cache invalidation should include:
551
+
552
+ - provider name;
553
+ - model name;
554
+ - model revision or digest when available;
555
+ - dimensions;
556
+ - pooling;
557
+ - normalization;
558
+ - quantization settings;
559
+ - chunk text hash;
560
+ - chunking strategy version.
561
+
562
+ Changing any of these should invalidate only affected embeddings, not the whole
563
+ source parse.
564
+
565
+ ## Retrieval Design
566
+
567
+ The retrieval engine should use hybrid search:
568
+
569
+ 1. Rewrite or decompose the user's question when useful.
570
+ 2. Run FTS5 search for exact terms.
571
+ 3. Run vector search for semantic matches.
572
+ 4. Run targeted grep for identifiers, package names, CLI flags, and errors.
573
+ 5. Merge and deduplicate results by source and chunk.
574
+ 6. Boost heading-path matches and exact symbol matches.
575
+ 7. Optionally apply reranking to the merged candidate set when the corpus or
576
+ model provider justifies the extra latency.
577
+ 8. Penalize low-confidence PDF chunks unless the query explicitly targets them.
578
+ 9. Expand around top chunks to include neighboring context.
579
+ 10. Validate that final answer citations point to retrieved evidence.
580
+
581
+ This is the core reason Athena should not depend only on an external coding
582
+ agent's native grep/read tools. Documentation RAG needs indexing metadata,
583
+ source-map awareness, chunk scoring, source confidence, and citation validation.
584
+
585
+ ## Agent Tool Design
586
+
587
+ The agent should see a small set of precise tools.
588
+
589
+ ### `searchDocs`
590
+
591
+ Purpose: hybrid search over indexed docs.
592
+
593
+ Input:
594
+
595
+ ```json
596
+ {
597
+ "query": "string",
598
+ "filters": {
599
+ "pathPrefix": "string",
600
+ "kind": "markdown | pdf | html | text"
601
+ },
602
+ "limit": 10
603
+ }
604
+ ```
605
+
606
+ Output:
607
+
608
+ ```json
609
+ {
610
+ "results": [
611
+ {
612
+ "chunkId": 123,
613
+ "path": "docs/api.md",
614
+ "headingPath": "Authentication > Tokens",
615
+ "snippet": "...",
616
+ "score": 0.82,
617
+ "locator": { "startLine": 10, "endLine": 22 }
618
+ }
619
+ ]
620
+ }
621
+ ```
622
+
623
+ ### `readSourceRange`
624
+
625
+ Purpose: read an exact source range from a non-PDF document.
626
+
627
+ Input:
628
+
629
+ ```json
630
+ {
631
+ "path": "docs/api.md",
632
+ "startLine": 10,
633
+ "endLine": 40
634
+ }
635
+ ```
636
+
637
+ ### `readPdfSource`
638
+
639
+ Purpose: read converted PDF Markdown plus original page-region metadata.
640
+
641
+ Input:
642
+
643
+ ```json
644
+ {
645
+ "path": "manual.pdf",
646
+ "page": 17,
647
+ "regionId": "..."
648
+ }
649
+ ```
650
+
651
+ ### `grepDocs`
652
+
653
+ Purpose: exact string or regex search for identifiers.
654
+
655
+ Input:
656
+
657
+ ```json
658
+ {
659
+ "pattern": "AUTH_TOKEN",
660
+ "caseSensitive": false,
661
+ "limit": 20
662
+ }
663
+ ```
664
+
665
+ ### `listDocs`
666
+
667
+ Purpose: inspect indexed sources and narrow search scope.
668
+
669
+ Input:
670
+
671
+ ```json
672
+ {
673
+ "pathPrefix": "docs/",
674
+ "kind": "pdf"
675
+ }
676
+ ```
677
+
678
+ ### `relatedChunks`
679
+
680
+ Purpose: fetch neighboring or related chunks after a promising hit.
681
+
682
+ Input:
683
+
684
+ ```json
685
+ {
686
+ "chunkId": 123,
687
+ "before": 1,
688
+ "after": 2
689
+ }
690
+ ```
691
+
692
+ ## Custom Agent Loop
693
+
694
+ The agent loop must be small, explicit, and Athena-owned. This is not an SDK
695
+ integration point. It is the core control surface for tool selection, step
696
+ limits, memory policy, streaming, and citation enforcement:
697
+
698
+ ```ts
699
+ export type ModelMessage =
700
+ | { role: "system"; content: string }
701
+ | { role: "user"; content: string }
702
+ | { role: "assistant"; content: string; toolCalls?: ToolCall[] }
703
+ | { role: "tool"; toolCallId: string; content: unknown };
704
+
705
+ export type ToolDef = {
706
+ name: string;
707
+ description: string;
708
+ inputSchema: unknown;
709
+ execute(input: unknown): Promise<unknown>;
710
+ };
711
+
712
+ export interface ModelProvider {
713
+ streamResponse(input: {
714
+ model: string;
715
+ messages: ModelMessage[];
716
+ tools: ToolDef[];
717
+ temperature?: number;
718
+ }): AsyncIterable<ModelEvent>;
719
+ }
720
+ ```
721
+
722
+ Required loop behavior:
723
+
724
+ - enforce a max-step count;
725
+ - stream assistant output to the UI;
726
+ - execute only registered tools;
727
+ - validate tool inputs before execution;
728
+ - record tool traces for debugging;
729
+ - perform conversation compaction between turns;
730
+ - store final answer citations;
731
+ - reject or warn on unsupported citations;
732
+ - expose deterministic failure messages when retrieval is insufficient.
733
+
734
+ The system prompt should instruct the agent to:
735
+
736
+ - search before answering factual doc questions;
737
+ - cite every claim that depends on documentation;
738
+ - prefer exact source ranges over broad summaries;
739
+ - say when the index has no evidence;
740
+ - use follow-up memory only as conversational context, not as documentary proof.
741
+
742
+ ## Follow-Up Memory
743
+
744
+ The chat identified a useful memory policy: keep questions, final answers, and
745
+ citations, but avoid replaying every intermediate search step into the model
746
+ context.
747
+
748
+ A stored turn can look like:
749
+
750
+ ```json
751
+ {
752
+ "question": "How do I configure OAuth?",
753
+ "answerSummary": "Use providers.github and set callbackUrl...",
754
+ "citations": [
755
+ { "path": "docs/auth.md", "startLine": 40, "endLine": 58 }
756
+ ],
757
+ "createdAt": "2026-07-03T12:00:00Z"
758
+ }
759
+ ```
760
+
761
+ On follow-up, the model can receive:
762
+
763
+ - recent user questions;
764
+ - short answer summaries;
765
+ - citation identifiers;
766
+ - any user-pinned sources;
767
+ - fresh retrieval results for the new question.
768
+
769
+ It should not receive full prior tool traces unless the user asks for audit
770
+ details.
771
+
772
+ ## Local Web UI
773
+
774
+ The UI should be simple, but citation-first:
775
+
776
+ - chat column;
777
+ - retrieval trace drawer;
778
+ - source preview panel;
779
+ - clickable citations;
780
+ - line-highlighted Markdown/text viewer;
781
+ - PDF page preview with highlighted regions when available;
782
+ - index status indicator;
783
+ - parser warning badges;
784
+ - "open in editor" links for local files when feasible;
785
+ - controls for reindexing and source filtering.
786
+
787
+ Answer citations should be visible inline and collected at the bottom of each
788
+ answer. Clicking a citation should open the exact source range, not only the
789
+ file.
790
+
791
+ ## Optional MCP and OpenCode Integration
792
+
793
+ OpenCode or another agent should be an optional consumer of Athena's index.
794
+
795
+ The best integration is an MCP server exposing:
796
+
797
+ - `searchDocs`;
798
+ - `readSourceRange`;
799
+ - `readPdfSource`;
800
+ - `grepDocs`;
801
+ - `listDocs`;
802
+ - `relatedChunks`.
803
+
804
+ This lets external agents use Athena as a specialized documentation search
805
+ backend while Athena retains responsibility for indexing, ranking, parsing, and
806
+ citations.
807
+
808
+ Athena should also support a normal local chat UI because many documentation
809
+ queries do not require a coding agent.
810
+
811
+ ## Configuration
812
+
813
+ Example `athena.config.ts`:
814
+
815
+ ```ts
816
+ import { defineConfig } from "@alextis59/athena";
817
+
818
+ export default defineConfig({
819
+ index: {
820
+ include: ["**/*.md", "**/*.mdx", "**/*.pdf", "**/*.html"],
821
+ exclude: ["node_modules/**", "dist/**", ".git/**"]
822
+ },
823
+ embeddings: {
824
+ provider: "transformers-js",
825
+ model: "mixedbread-ai/mxbai-embed-xsmall-v1",
826
+ dimensions: 384,
827
+ pooling: "mean",
828
+ normalize: true
829
+ },
830
+ pdf: {
831
+ parser: "pdf-2-llm",
832
+ pageAnchors: true,
833
+ csvSidecars: true,
834
+ timeoutMs: 30_000,
835
+ minConfidenceForAutoCitation: 0.65
836
+ },
837
+ server: {
838
+ host: "127.0.0.1",
839
+ port: 4317
840
+ }
841
+ });
842
+ ```
843
+
844
+ Cloud providers should require explicit configuration:
845
+
846
+ ```ts
847
+ embeddings: {
848
+ provider: "openai",
849
+ model: "text-embedding-3-small",
850
+ allowRemoteDocumentText: true
851
+ }
852
+ ```
853
+
854
+ The explicit `allowRemoteDocumentText` flag prevents accidental leakage of
855
+ private docs.
856
+
857
+ ## Project Structure
858
+
859
+ A practical implementation layout:
860
+
861
+ ```txt
862
+ athena/
863
+ src/
864
+ cli/
865
+ main.ts
866
+ commands/
867
+ server/
868
+ app.ts
869
+ routes/
870
+ ui/
871
+ ...
872
+ indexer/
873
+ discover.ts
874
+ pipeline.ts
875
+ chunking.ts
876
+ cache.ts
877
+ parsers/
878
+ markdown.ts
879
+ html.ts
880
+ text.ts
881
+ pdf2llm.ts
882
+ embeddings/
883
+ provider.ts
884
+ transformers-js.ts
885
+ ollama.ts
886
+ openai.ts
887
+ retrieval/
888
+ fts.ts
889
+ vector.ts
890
+ hybrid.ts
891
+ rerank.ts
892
+ agent/
893
+ provider.ts
894
+ loop.ts
895
+ tools.ts
896
+ citations.ts
897
+ memory.ts
898
+ storage/
899
+ db.ts
900
+ migrations/
901
+ mcp/
902
+ server.ts
903
+ docs/
904
+ agentic-rag-study.md
905
+ ```
906
+
907
+ ## MVP Plan
908
+
909
+ ### Phase 1: Markdown-Only Local RAG
910
+
911
+ Deliver:
912
+
913
+ - CLI startup.
914
+ - file discovery and hashing.
915
+ - SQLite metadata tables.
916
+ - Markdown parsing with line ranges.
917
+ - FTS5 lexical search.
918
+ - local web chat UI.
919
+ - basic custom agent loop.
920
+ - `searchDocs` and `readSourceRange`.
921
+ - citation validation for Markdown line ranges.
922
+
923
+ This phase proves the end-to-end user experience with the least parser risk.
924
+
925
+ ### Phase 2: Local Embeddings and Hybrid Search
926
+
927
+ Deliver:
928
+
929
+ - Transformers.js embedding provider.
930
+ - `mxbai-embed-xsmall-v1` default.
931
+ - embedding cache invalidation.
932
+ - vector storage.
933
+ - hybrid FTS + vector retrieval.
934
+ - neighbor chunk expansion.
935
+ - answer trace display in the UI.
936
+
937
+ This phase proves answer quality beyond exact keyword matching.
938
+
939
+ ### Phase 3: PDF Ingestion with `pdf-2-llm`
940
+
941
+ Deliver:
942
+
943
+ - `pdf-2-llm` parser adapter.
944
+ - structured result persistence.
945
+ - PDF Markdown chunking.
946
+ - PDF source-map citations.
947
+ - PDF warning/confidence display.
948
+ - `readPdfSource` tool.
949
+ - table sidecar handling.
950
+
951
+ This phase makes Athena useful for real documentation archives.
952
+
953
+ ### Phase 4: Quality Gates and Evaluation
954
+
955
+ Deliver:
956
+
957
+ - fixed evaluation corpus.
958
+ - retrieval recall tests.
959
+ - citation correctness tests.
960
+ - PDF source-map integrity tests.
961
+ - stale-cache invalidation tests.
962
+ - answer faithfulness checks.
963
+ - regression CLI for local runs.
964
+
965
+ This phase prevents the RAG system from becoming a demo that cannot be trusted.
966
+ It should include an evaluation harness that can be run before changing
967
+ chunking, embeddings, ranking, PDF parsing, or citation validation.
968
+
969
+ ### Phase 5: MCP/OpenCode Bridge
970
+
971
+ Deliver:
972
+
973
+ - MCP server wrapping Athena tools.
974
+ - external-agent setup docs.
975
+ - trace compatibility between UI and MCP usage.
976
+ - read-only default permissions.
977
+
978
+ This phase lets coding agents consume the index without taking over the core.
979
+
980
+ ## Evaluation Plan
981
+
982
+ Athena needs tests at three levels.
983
+
984
+ Parser/index tests:
985
+
986
+ - unchanged files are skipped;
987
+ - changed files are reindexed;
988
+ - line ranges remain stable;
989
+ - PDF source maps point to valid page regions;
990
+ - warning and confidence fields survive storage;
991
+ - table sidecars can be opened from citations.
992
+
993
+ Retrieval tests:
994
+
995
+ - exact symbol query returns the symbol's defining section;
996
+ - semantic query returns relevant conceptual section;
997
+ - hybrid retrieval outperforms FTS-only and vector-only on a small corpus;
998
+ - low-confidence PDF chunks are not over-promoted;
999
+ - source filters work by path and type.
1000
+
1001
+ Answer tests:
1002
+
1003
+ - answer includes citations for doc-derived claims;
1004
+ - unsupported claims are rejected or marked as unsupported;
1005
+ - citations open the expected source range;
1006
+ - follow-up questions use prior context without skipping fresh retrieval;
1007
+ - max-step guard stops runaway tool loops.
1008
+
1009
+ Evaluation datasets should include:
1010
+
1011
+ - synthetic Markdown docs with known answers;
1012
+ - realistic API docs;
1013
+ - docs with repeated terms and ambiguous headings;
1014
+ - table-heavy PDFs;
1015
+ - scanned or OCR-planned PDFs;
1016
+ - malformed or encrypted PDFs;
1017
+ - multilingual or RTL/CJK PDFs if those are target users.
1018
+
1019
+ ## Risks and Mitigations
1020
+
1021
+ | Risk | Mitigation |
1022
+ |---|---|
1023
+ | PDF source maps are noisy | Display confidence, warnings, and snippets; gate citations below a threshold. |
1024
+ | Local embeddings are lower quality than cloud | Keep cloud adapters opt-in and add corpus-level evals. |
1025
+ | Vector extension portability issues | Keep vector metadata separate and support rebuilds. |
1026
+ | Agent invents unsupported claims | Citation validator and retrieval-required prompt policy. |
1027
+ | Cache invalidation bugs | Include parser, chunker, model, and source hashes in cache keys. |
1028
+ | Large docs make indexing slow | Incremental hashing, batching, progress UI, and resumable indexing. |
1029
+ | Context flooding in follow-ups | Store summaries and citations, not all tool traces. |
1030
+ | External-agent integration bypasses citations | Expose only citation-aware retrieval tools through MCP. |
1031
+
1032
+ ## Final Recommendation
1033
+
1034
+ Build Athena as a local Node/TypeScript documentation RAG system with a custom,
1035
+ SDK-free agent loop and a specialized retrieval engine. Use SQLite for the
1036
+ cache, FTS5 for exact lexical search, an embedded vector extension for semantic
1037
+ search, Transformers.js/ONNX with `mxbai-embed-xsmall-v1` for default local
1038
+ embeddings, and the published `pdf-2-llm` npm package as the PDF ingestion core.
1039
+
1040
+ Do not use OpenCode, LangChain, LlamaIndex, Vercel AI SDK, or any other agent
1041
+ SDK to handle Athena's agentic behavior. They can be useful references, optional
1042
+ integrations, or model-provider adapters, but the hard part of this product is
1043
+ not "calling a model." The hard parts are stable indexing, source-aware
1044
+ chunking, exact citation mapping, PDF extraction quality, cache correctness, and
1045
+ answer validation. Those should remain Athena-owned.
1046
+
1047
+ ## References
1048
+
1049
+ - Source chat: https://chatgpt.com/share/6a47e6fe-1124-83ed-b2b6-8d3321dbcfcc
1050
+ - `pdf-2-llm` npm package: https://www.npmjs.com/package/pdf-2-llm
1051
+ - `pdf-2-llm`: https://github.com/alextis59/pdf-2-llm
1052
+ - `mixedbread-ai/mxbai-embed-xsmall-v1`: https://huggingface.co/mixedbread-ai/mxbai-embed-xsmall-v1
1053
+ - Transformers.js: https://huggingface.co/docs/transformers.js/en/index
1054
+ - Ollama embeddings: https://docs.ollama.com/capabilities/embeddings
1055
+ - SQLite FTS5: https://www.sqlite.org/fts5.html
1056
+ - `sqlite-vec`: https://alexgarcia.xyz/sqlite-vec/