haiku.rag 0.12.1__tar.gz → 0.13.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.

Potentially problematic release.


This version of haiku.rag might be problematic. Click here for more details.

Files changed (96) hide show
  1. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/.dockerignore +12 -1
  2. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/.gitignore +5 -1
  3. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/PKG-INFO +18 -10
  4. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/README.md +15 -7
  5. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/mkdocs.yml +1 -0
  6. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/pyproject.toml +3 -3
  7. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/benchmark.py +1 -1
  8. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/llm_judge.py +1 -1
  9. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/__init__.py +3 -3
  10. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/app.py +7 -5
  11. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/chunker.py +1 -1
  12. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/cli.py +72 -31
  13. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/client.py +36 -10
  14. haiku_rag-0.13.0/src/haiku/rag/config/__init__.py +54 -0
  15. haiku_rag-0.13.0/src/haiku/rag/config/loader.py +151 -0
  16. haiku_rag-0.13.0/src/haiku/rag/config/models.py +78 -0
  17. haiku_rag-0.13.0/src/haiku/rag/embeddings/__init__.py +41 -0
  18. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/embeddings/base.py +2 -2
  19. haiku_rag-0.12.1/src/haiku/rag/embeddings/vllm.py → haiku_rag-0.13.0/src/haiku/rag/embeddings/ollama.py +1 -1
  20. haiku_rag-0.12.1/src/haiku/rag/embeddings/ollama.py → haiku_rag-0.13.0/src/haiku/rag/embeddings/vllm.py +3 -1
  21. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/common.py +2 -2
  22. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/mcp.py +14 -8
  23. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/monitor.py +17 -4
  24. haiku_rag-0.13.0/src/haiku/rag/qa/__init__.py +33 -0
  25. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/agent.py +4 -2
  26. haiku_rag-0.13.0/src/haiku/rag/reranking/__init__.py +45 -0
  27. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/reranking/base.py +1 -1
  28. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/reranking/cohere.py +2 -2
  29. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/reranking/mxbai.py +1 -1
  30. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/reranking/vllm.py +1 -1
  31. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/engine.py +19 -12
  32. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/repositories/chunk.py +12 -8
  33. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/repositories/document.py +4 -4
  34. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/repositories/settings.py +19 -9
  35. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/utils.py +9 -9
  36. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/uv.lock +3 -1
  37. haiku_rag-0.12.1/src/haiku/rag/config.py +0 -90
  38. haiku_rag-0.12.1/src/haiku/rag/embeddings/__init__.py +0 -35
  39. haiku_rag-0.12.1/src/haiku/rag/migration.py +0 -316
  40. haiku_rag-0.12.1/src/haiku/rag/qa/__init__.py +0 -20
  41. haiku_rag-0.12.1/src/haiku/rag/reranking/__init__.py +0 -37
  42. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/.pre-commit-config.yaml +0 -0
  43. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/.python-version +0 -0
  44. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/LICENSE +0 -0
  45. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/server.json +0 -0
  46. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/__init__.py +0 -0
  47. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/config.py +0 -0
  48. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/datasets/__init__.py +0 -0
  49. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/datasets/repliqa.py +0 -0
  50. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/datasets/wix.py +0 -0
  51. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/evaluations/prompts.py +0 -0
  52. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/__init__.py +0 -0
  53. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/client.py +0 -0
  54. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/context.py +0 -0
  55. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/models.py +0 -0
  56. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/prompts.py +0 -0
  57. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/skills.py +0 -0
  58. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/storage.py +0 -0
  59. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/a2a/worker.py +0 -0
  60. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/embeddings/openai.py +0 -0
  61. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/embeddings/voyageai.py +0 -0
  62. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/__init__.py +0 -0
  63. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/base.py +0 -0
  64. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/models.py +0 -0
  65. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/nodes/__init__.py +0 -0
  66. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/nodes/analysis.py +0 -0
  67. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/nodes/plan.py +0 -0
  68. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/nodes/search.py +0 -0
  69. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/nodes/synthesize.py +0 -0
  70. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/graph/prompts.py +0 -0
  71. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/logging.py +0 -0
  72. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/__init__.py +0 -0
  73. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/dependencies.py +0 -0
  74. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/graph.py +0 -0
  75. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/models.py +0 -0
  76. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/nodes.py +0 -0
  77. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/prompts.py +0 -0
  78. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/deep/state.py +0 -0
  79. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/qa/prompts.py +0 -0
  80. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/reader.py +0 -0
  81. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/__init__.py +0 -0
  82. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/common.py +0 -0
  83. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/dependencies.py +0 -0
  84. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/graph.py +0 -0
  85. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/models.py +0 -0
  86. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/prompts.py +0 -0
  87. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/state.py +0 -0
  88. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/research/stream.py +0 -0
  89. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/__init__.py +0 -0
  90. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/models/__init__.py +0 -0
  91. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/models/chunk.py +0 -0
  92. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/models/document.py +0 -0
  93. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/repositories/__init__.py +0 -0
  94. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/upgrades/__init__.py +0 -0
  95. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/upgrades/v0_10_1.py +0 -0
  96. {haiku_rag-0.12.1 → haiku_rag-0.13.0}/src/haiku/rag/store/upgrades/v0_9_3.py +0 -0
@@ -25,9 +25,19 @@ wheels/
25
25
  venv/
26
26
  env/
27
27
 
28
+ # Node.js
29
+ node_modules/
30
+ .next/
31
+ npm-debug.log*
32
+ yarn-debug.log*
33
+ yarn-error.log*
34
+
28
35
  # Data
29
36
  *.lancedb/
30
37
  data/
38
+
39
+ # Docs
40
+ mkdocs.yml
31
41
  docs/
32
42
 
33
43
  # IDE
@@ -50,6 +60,7 @@ tests/
50
60
  .pytest_cache/
51
61
  .coverage
52
62
  htmlcov/
53
-
63
+ src/evaluations/
64
+ server.json
54
65
  # Examples
55
66
  examples/
@@ -16,8 +16,9 @@ tests/data/
16
16
  .pytest_cache/
17
17
  .ruff_cache/
18
18
 
19
- # environment variables
19
+ # environment variables and config files
20
20
  .env
21
+ haiku.rag.yaml
21
22
  TODO.md
22
23
  PLAN.md
23
24
  DEVNOTES.md
@@ -25,3 +26,6 @@ DEVNOTES.md
25
26
  # mcp registry
26
27
  .mcpregistry_github_token
27
28
  .mcpregistry_registry_token
29
+
30
+ # MkDocs site directory when doing local docs builds
31
+ site/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiku.rag
3
- Version: 0.12.1
3
+ Version: 0.13.0
4
4
  Summary: Agentic Retrieval Augmented Generation (RAG) with LanceDB
5
5
  Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
6
6
  License: MIT
@@ -13,9 +13,8 @@ Classifier: Operating System :: MacOS
13
13
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
14
14
  Classifier: Operating System :: Microsoft :: Windows :: Windows 11
15
15
  Classifier: Operating System :: POSIX :: Linux
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
16
  Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
19
18
  Classifier: Typing :: Typed
20
19
  Requires-Python: >=3.12
21
20
  Requires-Dist: docling>=2.56.1
@@ -26,6 +25,7 @@ Requires-Dist: pydantic-ai>=1.0.18
26
25
  Requires-Dist: pydantic-graph>=1.0.18
27
26
  Requires-Dist: pydantic>=2.12.2
28
27
  Requires-Dist: python-dotenv>=1.1.1
28
+ Requires-Dist: pyyaml>=6.0.1
29
29
  Requires-Dist: rich>=14.2.0
30
30
  Requires-Dist: tiktoken>=0.12.0
31
31
  Requires-Dist: typer>=0.19.2
@@ -44,7 +44,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
44
44
 
45
45
  `haiku.rag` is a Retrieval-Augmented Generation (RAG) library built to work with LanceDB as a local vector database. It uses LanceDB for storing embeddings and performs semantic (vector) search as well as full-text search combined through native hybrid search with Reciprocal Rank Fusion. Both open-source (Ollama) as well as commercial (OpenAI, VoyageAI) embedding providers are supported.
46
46
 
47
- > **Note**: Starting with version 0.7.0, haiku.rag uses LanceDB instead of SQLite. If you have an existing SQLite database, use `haiku-rag migrate old_database.sqlite` to migrate your data safely.
47
+ > **Note**: Configuration now uses YAML files instead of environment variables. If you're upgrading from an older version, run `haiku-rag init-config --from-env` to migrate your `.env` file to `haiku.rag.yaml`. See [Configuration](https://ggozad.github.io/haiku.rag/configuration/) for details.
48
48
 
49
49
  ## Features
50
50
 
@@ -65,6 +65,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
65
65
 
66
66
  ```bash
67
67
  # Install
68
+ # Python 3.12 or newer required
68
69
  uv pip install haiku.rag
69
70
 
70
71
  # Add documents
@@ -98,14 +99,12 @@ haiku-rag research \
98
99
  # Rebuild database (re-chunk and re-embed all documents)
99
100
  haiku-rag rebuild
100
101
 
101
- # Migrate from SQLite to LanceDB
102
- haiku-rag migrate old_database.sqlite
103
-
104
102
  # Start server with file monitoring
105
- export MONITOR_DIRECTORIES="/path/to/docs"
106
- haiku-rag serve
103
+ haiku-rag serve --monitor
107
104
  ```
108
105
 
106
+ To customize settings, create a `haiku.rag.yaml` config file (see [Configuration](https://ggozad.github.io/haiku.rag/configuration/)).
107
+
109
108
  ## Python Usage
110
109
 
111
110
  ```python
@@ -197,17 +196,26 @@ haiku-rag a2aclient
197
196
  ```
198
197
 
199
198
  The A2A agent provides:
199
+
200
200
  - Multi-turn dialogue with context
201
201
  - Intelligent multi-search for complex questions
202
202
  - Source citations with titles and URIs
203
203
  - Full document retrieval on request
204
204
 
205
+ ## Examples
206
+
207
+ See the [examples directory](examples/) for working examples:
208
+
209
+ - **[Interactive Research Assistant](examples/ag-ui-research/)** - Full-stack research assistant with Pydantic AI and AG-UI featuring human-in-the-loop approval and real-time state synchronization
210
+ - **[Docker Setup](examples/docker/)** - Complete Docker deployment with file monitoring, MCP server, and A2A agent
211
+ - **[A2A Security](examples/a2a-security/)** - Authentication examples (API key, OAuth2, GitHub)
212
+
205
213
  ## Documentation
206
214
 
207
215
  Full documentation at: https://ggozad.github.io/haiku.rag/
208
216
 
209
217
  - [Installation](https://ggozad.github.io/haiku.rag/installation/) - Provider setup
210
- - [Configuration](https://ggozad.github.io/haiku.rag/configuration/) - Environment variables
218
+ - [Configuration](https://ggozad.github.io/haiku.rag/configuration/) - YAML configuration
211
219
  - [CLI](https://ggozad.github.io/haiku.rag/cli/) - Command reference
212
220
  - [Python API](https://ggozad.github.io/haiku.rag/python/) - Complete API docs
213
221
  - [Agents](https://ggozad.github.io/haiku.rag/agents/) - QA agent and multi-agent research
@@ -4,7 +4,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
4
4
 
5
5
  `haiku.rag` is a Retrieval-Augmented Generation (RAG) library built to work with LanceDB as a local vector database. It uses LanceDB for storing embeddings and performs semantic (vector) search as well as full-text search combined through native hybrid search with Reciprocal Rank Fusion. Both open-source (Ollama) as well as commercial (OpenAI, VoyageAI) embedding providers are supported.
6
6
 
7
- > **Note**: Starting with version 0.7.0, haiku.rag uses LanceDB instead of SQLite. If you have an existing SQLite database, use `haiku-rag migrate old_database.sqlite` to migrate your data safely.
7
+ > **Note**: Configuration now uses YAML files instead of environment variables. If you're upgrading from an older version, run `haiku-rag init-config --from-env` to migrate your `.env` file to `haiku.rag.yaml`. See [Configuration](https://ggozad.github.io/haiku.rag/configuration/) for details.
8
8
 
9
9
  ## Features
10
10
 
@@ -25,6 +25,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
25
25
 
26
26
  ```bash
27
27
  # Install
28
+ # Python 3.12 or newer required
28
29
  uv pip install haiku.rag
29
30
 
30
31
  # Add documents
@@ -58,14 +59,12 @@ haiku-rag research \
58
59
  # Rebuild database (re-chunk and re-embed all documents)
59
60
  haiku-rag rebuild
60
61
 
61
- # Migrate from SQLite to LanceDB
62
- haiku-rag migrate old_database.sqlite
63
-
64
62
  # Start server with file monitoring
65
- export MONITOR_DIRECTORIES="/path/to/docs"
66
- haiku-rag serve
63
+ haiku-rag serve --monitor
67
64
  ```
68
65
 
66
+ To customize settings, create a `haiku.rag.yaml` config file (see [Configuration](https://ggozad.github.io/haiku.rag/configuration/)).
67
+
69
68
  ## Python Usage
70
69
 
71
70
  ```python
@@ -157,17 +156,26 @@ haiku-rag a2aclient
157
156
  ```
158
157
 
159
158
  The A2A agent provides:
159
+
160
160
  - Multi-turn dialogue with context
161
161
  - Intelligent multi-search for complex questions
162
162
  - Source citations with titles and URIs
163
163
  - Full document retrieval on request
164
164
 
165
+ ## Examples
166
+
167
+ See the [examples directory](examples/) for working examples:
168
+
169
+ - **[Interactive Research Assistant](examples/ag-ui-research/)** - Full-stack research assistant with Pydantic AI and AG-UI featuring human-in-the-loop approval and real-time state synchronization
170
+ - **[Docker Setup](examples/docker/)** - Complete Docker deployment with file monitoring, MCP server, and A2A agent
171
+ - **[A2A Security](examples/a2a-security/)** - Authentication examples (API key, OAuth2, GitHub)
172
+
165
173
  ## Documentation
166
174
 
167
175
  Full documentation at: https://ggozad.github.io/haiku.rag/
168
176
 
169
177
  - [Installation](https://ggozad.github.io/haiku.rag/installation/) - Provider setup
170
- - [Configuration](https://ggozad.github.io/haiku.rag/configuration/) - Environment variables
178
+ - [Configuration](https://ggozad.github.io/haiku.rag/configuration/) - YAML configuration
171
179
  - [CLI](https://ggozad.github.io/haiku.rag/cli/) - Command reference
172
180
  - [Python API](https://ggozad.github.io/haiku.rag/python/) - Complete API docs
173
181
  - [Agents](https://ggozad.github.io/haiku.rag/agents/) - QA agent and multi-agent research
@@ -57,6 +57,7 @@ plugins:
57
57
  nav:
58
58
  - haiku.rag:
59
59
  - index.md
60
+ - Getting started: tutorial.md
60
61
  - Installation: installation.md
61
62
  - Configuration: configuration.md
62
63
  - CLI: cli.md
@@ -2,7 +2,7 @@
2
2
 
3
3
  name = "haiku.rag"
4
4
  description = "Agentic Retrieval Augmented Generation (RAG) with LanceDB"
5
- version = "0.12.1"
5
+ version = "0.13.0"
6
6
  authors = [{ name = "Yiorgis Gozadinos", email = "ggozadinos@gmail.com" }]
7
7
  license = { text = "MIT" }
8
8
  readme = { file = "README.md", content-type = "text/markdown" }
@@ -16,9 +16,8 @@ classifiers = [
16
16
  "Operating System :: Microsoft :: Windows :: Windows 11",
17
17
  "Operating System :: MacOS",
18
18
  "Operating System :: POSIX :: Linux",
19
- "Programming Language :: Python :: 3.10",
20
- "Programming Language :: Python :: 3.11",
21
19
  "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
22
21
  "Typing :: Typed",
23
22
  ]
24
23
 
@@ -31,6 +30,7 @@ dependencies = [
31
30
  "pydantic-ai>=1.0.18",
32
31
  "pydantic-graph>=1.0.18",
33
32
  "python-dotenv>=1.1.1",
33
+ "pyyaml>=6.0.1",
34
34
  "rich>=14.2.0",
35
35
  "tiktoken>=0.12.0",
36
36
  "typer>=0.19.2",
@@ -174,7 +174,7 @@ async def run_qa_benchmark(
174
174
 
175
175
  judge_model = OpenAIChatModel(
176
176
  model_name=QA_JUDGE_MODEL,
177
- provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
177
+ provider=OllamaProvider(base_url=f"{Config.providers.ollama.base_url}/v1"),
178
178
  )
179
179
 
180
180
  evaluation_dataset = EvalDataset[str, str, dict[str, str]](
@@ -41,7 +41,7 @@ class LLMJudge:
41
41
  # Create Ollama model
42
42
  ollama_model = OpenAIChatModel(
43
43
  model_name=model,
44
- provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
44
+ provider=OllamaProvider(base_url=f"{Config.providers.ollama.base_url}/v1"),
45
45
  )
46
46
 
47
47
  # Create Pydantic AI agent
@@ -57,12 +57,12 @@ def create_a2a_app(
57
57
  """
58
58
  base_storage = InMemoryStorage()
59
59
  storage = LRUMemoryStorage(
60
- storage=base_storage, max_contexts=Config.A2A_MAX_CONTEXTS
60
+ storage=base_storage, max_contexts=Config.a2a.max_contexts
61
61
  )
62
62
  broker = InMemoryBroker()
63
63
 
64
64
  # Create the agent with native search tool
65
- model = get_model(Config.QA_PROVIDER, Config.QA_MODEL)
65
+ model = get_model(Config.qa.provider, Config.qa.model)
66
66
  agent = Agent(
67
67
  model=model,
68
68
  deps_type=AgentDependencies,
@@ -120,7 +120,7 @@ def create_a2a_app(
120
120
  # Create FastA2A app with custom worker lifecycle
121
121
  @asynccontextmanager
122
122
  async def lifespan(app):
123
- logger.info(f"Started A2A server (max contexts: {Config.A2A_MAX_CONTEXTS})")
123
+ logger.info(f"Started A2A server (max contexts: {Config.a2a.max_contexts})")
124
124
  async with app.task_manager:
125
125
  async with worker.run():
126
126
  yield
@@ -231,8 +231,8 @@ class HaikuRAGApp:
231
231
  )
232
232
 
233
233
  start_node = DeepQAPlanNode(
234
- provider=Config.QA_PROVIDER,
235
- model=Config.QA_MODEL,
234
+ provider=Config.qa.provider,
235
+ model=Config.qa.model,
236
236
  )
237
237
 
238
238
  result = await graph.run(
@@ -278,8 +278,8 @@ class HaikuRAGApp:
278
278
  )
279
279
 
280
280
  start = PlanNode(
281
- provider=Config.RESEARCH_PROVIDER or Config.QA_PROVIDER,
282
- model=Config.RESEARCH_MODEL or Config.QA_MODEL,
281
+ provider=Config.research.provider or Config.qa.provider,
282
+ model=Config.research.model or Config.qa.model,
283
283
  )
284
284
  report = None
285
285
  async for event in stream_research_graph(graph, start, state, deps):
@@ -474,7 +474,9 @@ class HaikuRAGApp:
474
474
 
475
475
  # Start file monitor if enabled
476
476
  if enable_monitor:
477
- monitor = FileWatcher(paths=Config.MONITOR_DIRECTORIES, client=client)
477
+ monitor = FileWatcher(
478
+ paths=Config.storage.monitor_directories, client=client
479
+ )
478
480
  monitor_task = asyncio.create_task(monitor.observe())
479
481
  tasks.append(monitor_task)
480
482
 
@@ -22,7 +22,7 @@ class Chunker:
22
22
 
23
23
  def __init__(
24
24
  self,
25
- chunk_size: int = Config.CHUNK_SIZE,
25
+ chunk_size: int = Config.processing.chunk_size,
26
26
  ):
27
27
  self.chunk_size = chunk_size
28
28
  tokenizer = OpenAITokenizer(
@@ -42,10 +42,21 @@ def main(
42
42
  callback=version_callback,
43
43
  help="Show version and exit",
44
44
  ),
45
+ config: Path | None = typer.Option(
46
+ None,
47
+ "--config",
48
+ help="Path to YAML configuration file",
49
+ ),
45
50
  ):
46
51
  """haiku.rag CLI - Vector database RAG system"""
52
+ # Store config path in environment for config loader to use
53
+ if config:
54
+ import os
55
+
56
+ os.environ["HAIKU_RAG_CONFIG_PATH"] = str(config.absolute())
57
+
47
58
  # Configure logging minimally for CLI context
48
- if Config.ENV == "development":
59
+ if Config.environment == "development":
49
60
  # Lazy import logfire only in development
50
61
  try:
51
62
  import logfire # type: ignore
@@ -69,7 +80,7 @@ def main(
69
80
  @cli.command("list", help="List all stored documents")
70
81
  def list_documents(
71
82
  db: Path = typer.Option(
72
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
83
+ Config.storage.data_dir / "haiku.rag.lancedb",
73
84
  "--db",
74
85
  help="Path to the LanceDB database file",
75
86
  ),
@@ -116,7 +127,7 @@ def add_document_text(
116
127
  metavar="KEY=VALUE",
117
128
  ),
118
129
  db: Path = typer.Option(
119
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
130
+ Config.storage.data_dir / "haiku.rag.lancedb",
120
131
  "--db",
121
132
  help="Path to the LanceDB database file",
122
133
  ),
@@ -145,7 +156,7 @@ def add_document_src(
145
156
  metavar="KEY=VALUE",
146
157
  ),
147
158
  db: Path = typer.Option(
148
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
159
+ Config.storage.data_dir / "haiku.rag.lancedb",
149
160
  "--db",
150
161
  help="Path to the LanceDB database file",
151
162
  ),
@@ -167,7 +178,7 @@ def get_document(
167
178
  help="The ID of the document to get",
168
179
  ),
169
180
  db: Path = typer.Option(
170
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
181
+ Config.storage.data_dir / "haiku.rag.lancedb",
171
182
  "--db",
172
183
  help="Path to the LanceDB database file",
173
184
  ),
@@ -184,7 +195,7 @@ def delete_document(
184
195
  help="The ID of the document to delete",
185
196
  ),
186
197
  db: Path = typer.Option(
187
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
198
+ Config.storage.data_dir / "haiku.rag.lancedb",
188
199
  "--db",
189
200
  help="Path to the LanceDB database file",
190
201
  ),
@@ -211,7 +222,7 @@ def search(
211
222
  help="Maximum number of results to return",
212
223
  ),
213
224
  db: Path = typer.Option(
214
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
225
+ Config.storage.data_dir / "haiku.rag.lancedb",
215
226
  "--db",
216
227
  help="Path to the LanceDB database file",
217
228
  ),
@@ -228,7 +239,7 @@ def ask(
228
239
  help="The question to ask",
229
240
  ),
230
241
  db: Path = typer.Option(
231
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
242
+ Config.storage.data_dir / "haiku.rag.lancedb",
232
243
  "--db",
233
244
  help="Path to the LanceDB database file",
234
245
  ),
@@ -276,7 +287,7 @@ def research(
276
287
  help="Max concurrent searches per iteration (planned)",
277
288
  ),
278
289
  db: Path = typer.Option(
279
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
290
+ Config.storage.data_dir / "haiku.rag.lancedb",
280
291
  "--db",
281
292
  help="Path to the LanceDB database file",
282
293
  ),
@@ -308,13 +319,61 @@ def settings():
308
319
  app.show_settings()
309
320
 
310
321
 
322
+ @cli.command("init-config", help="Generate a YAML configuration file")
323
+ def init_config(
324
+ output: Path = typer.Argument(
325
+ Path("haiku.rag.yaml"),
326
+ help="Output path for the config file",
327
+ ),
328
+ from_env: bool = typer.Option(
329
+ False,
330
+ "--from-env",
331
+ help="Migrate settings from .env file",
332
+ ),
333
+ ):
334
+ """Generate a YAML configuration file with defaults or from .env."""
335
+ import yaml
336
+
337
+ from haiku.rag.config.loader import generate_default_config, load_config_from_env
338
+
339
+ if output.exists():
340
+ typer.echo(
341
+ f"Error: {output} already exists. Remove it first or choose a different path."
342
+ )
343
+ raise typer.Exit(1)
344
+
345
+ if from_env:
346
+ # Load from environment variables (including .env if present)
347
+ from dotenv import load_dotenv
348
+
349
+ load_dotenv()
350
+ config_data = load_config_from_env()
351
+ if not config_data:
352
+ typer.echo("Warning: No environment variables found to migrate.")
353
+ typer.echo("Generating default configuration instead.")
354
+ config_data = generate_default_config()
355
+ else:
356
+ config_data = generate_default_config()
357
+
358
+ # Write YAML with comments
359
+ with open(output, "w") as f:
360
+ f.write("# haiku.rag configuration file\n")
361
+ f.write(
362
+ "# See https://ggozad.github.io/haiku.rag/configuration/ for details\n\n"
363
+ )
364
+ yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
365
+
366
+ typer.echo(f"Configuration file created: {output}")
367
+ typer.echo("Edit the file to customize your settings.")
368
+
369
+
311
370
  @cli.command(
312
371
  "rebuild",
313
372
  help="Rebuild the database by deleting all chunks and re-indexing all documents",
314
373
  )
315
374
  def rebuild(
316
375
  db: Path = typer.Option(
317
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
376
+ Config.storage.data_dir / "haiku.rag.lancedb",
318
377
  "--db",
319
378
  help="Path to the LanceDB database file",
320
379
  ),
@@ -328,7 +387,7 @@ def rebuild(
328
387
  @cli.command("vacuum", help="Optimize and clean up all tables to reduce disk usage")
329
388
  def vacuum(
330
389
  db: Path = typer.Option(
331
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
390
+ Config.storage.data_dir / "haiku.rag.lancedb",
332
391
  "--db",
333
392
  help="Path to the LanceDB database file",
334
393
  ),
@@ -342,7 +401,7 @@ def vacuum(
342
401
  @cli.command("info", help="Show read-only database info (no upgrades or writes)")
343
402
  def info(
344
403
  db: Path = typer.Option(
345
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
404
+ Config.storage.data_dir / "haiku.rag.lancedb",
346
405
  "--db",
347
406
  help="Path to the LanceDB database file",
348
407
  ),
@@ -371,7 +430,7 @@ def download_models_cmd():
371
430
  )
372
431
  def serve(
373
432
  db: Path = typer.Option(
374
- Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
433
+ Config.storage.data_dir / "haiku.rag.lancedb",
375
434
  "--db",
376
435
  help="Path to the LanceDB database file",
377
436
  ),
@@ -442,24 +501,6 @@ def serve(
442
501
  )
443
502
 
444
503
 
445
- @cli.command("migrate", help="Migrate an SQLite database to LanceDB")
446
- def migrate(
447
- sqlite_path: Path = typer.Argument(
448
- help="Path to the SQLite database file to migrate",
449
- ),
450
- ):
451
- # Generate LanceDB path in same parent directory
452
- lancedb_path = sqlite_path.parent / (sqlite_path.stem + ".lancedb")
453
-
454
- # Lazy import to avoid heavy deps on simple invocations
455
- from haiku.rag.migration import migrate_sqlite_to_lancedb
456
-
457
- success = asyncio.run(migrate_sqlite_to_lancedb(sqlite_path, lancedb_path))
458
-
459
- if not success:
460
- raise typer.Exit(1)
461
-
462
-
463
504
  @cli.command(
464
505
  "a2aclient", help="Run interactive client to chat with haiku.rag's A2A server"
465
506
  )
@@ -8,8 +8,7 @@ from urllib.parse import urlparse
8
8
 
9
9
  import httpx
10
10
 
11
- from haiku.rag.config import Config
12
- from haiku.rag.reader import FileReader
11
+ from haiku.rag.config import AppConfig, Config
13
12
  from haiku.rag.reranking import get_reranker
14
13
  from haiku.rag.store.engine import Store
15
14
  from haiku.rag.store.models.chunk import Chunk
@@ -17,7 +16,6 @@ from haiku.rag.store.models.document import Document
17
16
  from haiku.rag.store.repositories.chunk import ChunkRepository
18
17
  from haiku.rag.store.repositories.document import DocumentRepository
19
18
  from haiku.rag.store.repositories.settings import SettingsRepository
20
- from haiku.rag.utils import text_to_docling_document
21
19
 
22
20
  logger = logging.getLogger(__name__)
23
21
 
@@ -27,16 +25,23 @@ class HaikuRAG:
27
25
 
28
26
  def __init__(
29
27
  self,
30
- db_path: Path = Config.DEFAULT_DATA_DIR / "haiku.rag.lancedb",
28
+ db_path: Path | None = None,
29
+ config: AppConfig = Config,
31
30
  skip_validation: bool = False,
32
31
  ):
33
32
  """Initialize the RAG client with a database path.
34
33
 
35
34
  Args:
36
- db_path: Path to the database file.
35
+ db_path: Path to the database file. If None, uses config.storage.data_dir.
36
+ config: Configuration to use. Defaults to global Config.
37
37
  skip_validation: Whether to skip configuration validation on database load.
38
38
  """
39
- self.store = Store(db_path, skip_validation=skip_validation)
39
+ self._config = config
40
+ if db_path is None:
41
+ db_path = self._config.storage.data_dir / "haiku.rag.lancedb"
42
+ self.store = Store(
43
+ db_path, config=self._config, skip_validation=skip_validation
44
+ )
40
45
  self.document_repository = DocumentRepository(self.store)
41
46
  self.chunk_repository = ChunkRepository(self.store)
42
47
 
@@ -91,6 +96,9 @@ class HaikuRAG:
91
96
  Returns:
92
97
  The created Document instance.
93
98
  """
99
+ # Lazy import to avoid loading docling
100
+ from haiku.rag.utils import text_to_docling_document
101
+
94
102
  # Convert content to DoclingDocument for processing
95
103
  docling_document = text_to_docling_document(content)
96
104
 
@@ -127,6 +135,8 @@ class HaikuRAG:
127
135
  ValueError: If the file/URL cannot be parsed or doesn't exist
128
136
  httpx.RequestError: If URL request fails
129
137
  """
138
+ # Lazy import to avoid loading docling
139
+ from haiku.rag.reader import FileReader
130
140
 
131
141
  # Normalize metadata
132
142
  metadata = metadata or {}
@@ -181,6 +191,9 @@ class HaikuRAG:
181
191
  Raises:
182
192
  ValueError: If the file cannot be parsed or doesn't exist
183
193
  """
194
+ # Lazy import to avoid loading docling
195
+ from haiku.rag.reader import FileReader
196
+
184
197
  metadata = metadata or {}
185
198
 
186
199
  if source_path.suffix.lower() not in FileReader.extensions:
@@ -256,6 +269,9 @@ class HaikuRAG:
256
269
  ValueError: If the content cannot be parsed
257
270
  httpx.RequestError: If URL request fails
258
271
  """
272
+ # Lazy import to avoid loading docling
273
+ from haiku.rag.reader import FileReader
274
+
259
275
  metadata = metadata or {}
260
276
 
261
277
  async with httpx.AsyncClient() as client:
@@ -379,6 +395,9 @@ class HaikuRAG:
379
395
 
380
396
  async def update_document(self, document: Document) -> Document:
381
397
  """Update an existing document."""
398
+ # Lazy import to avoid loading docling
399
+ from haiku.rag.utils import text_to_docling_document
400
+
382
401
  # Convert content to DoclingDocument
383
402
  docling_document = text_to_docling_document(document.content)
384
403
 
@@ -418,7 +437,7 @@ class HaikuRAG:
418
437
  List of (chunk, score) tuples ordered by relevance.
419
438
  """
420
439
  # Get reranker if available
421
- reranker = get_reranker()
440
+ reranker = get_reranker(config=self._config)
422
441
 
423
442
  if reranker is None:
424
443
  # No reranking - return direct search results
@@ -440,18 +459,20 @@ class HaikuRAG:
440
459
  async def expand_context(
441
460
  self,
442
461
  search_results: list[tuple[Chunk, float]],
443
- radius: int = Config.CONTEXT_CHUNK_RADIUS,
462
+ radius: int | None = None,
444
463
  ) -> list[tuple[Chunk, float]]:
445
464
  """Expand search results with adjacent chunks, merging overlapping chunks.
446
465
 
447
466
  Args:
448
467
  search_results: List of (chunk, score) tuples from search.
449
468
  radius: Number of adjacent chunks to include before/after each chunk.
450
- Defaults to CONTEXT_CHUNK_RADIUS config setting.
469
+ If None, uses config.processing.context_chunk_radius.
451
470
 
452
471
  Returns:
453
472
  List of (chunk, score) tuples with expanded and merged context chunks.
454
473
  """
474
+ if radius is None:
475
+ radius = self._config.processing.context_chunk_radius
455
476
  if radius == 0:
456
477
  return search_results
457
478
 
@@ -581,7 +602,9 @@ class HaikuRAG:
581
602
  """
582
603
  from haiku.rag.qa import get_qa_agent
583
604
 
584
- qa_agent = get_qa_agent(self, use_citations=cite, system_prompt=system_prompt)
605
+ qa_agent = get_qa_agent(
606
+ self, config=self._config, use_citations=cite, system_prompt=system_prompt
607
+ )
585
608
  return await qa_agent.answer(question)
586
609
 
587
610
  async def rebuild_database(self) -> AsyncGenerator[str, None]:
@@ -597,6 +620,9 @@ class HaikuRAG:
597
620
  Yields:
598
621
  int: The ID of the document currently being processed
599
622
  """
623
+ # Lazy import to avoid loading docling
624
+ from haiku.rag.utils import text_to_docling_document
625
+
600
626
  await self.chunk_repository.delete_all()
601
627
  self.store.recreate_embeddings_table()
602
628