haiku.rag 0.12.1__py3-none-any.whl → 0.13.1__py3-none-any.whl
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.
- haiku/rag/a2a/__init__.py +3 -3
- haiku/rag/app.py +7 -5
- haiku/rag/chunker.py +1 -1
- haiku/rag/cli.py +72 -31
- haiku/rag/client.py +36 -10
- haiku/rag/config/__init__.py +50 -0
- haiku/rag/config/loader.py +137 -0
- haiku/rag/config/models.py +82 -0
- haiku/rag/embeddings/__init__.py +25 -11
- haiku/rag/embeddings/base.py +6 -4
- haiku/rag/embeddings/ollama.py +3 -2
- haiku/rag/embeddings/vllm.py +2 -2
- haiku/rag/graph/common.py +2 -2
- haiku/rag/mcp.py +14 -8
- haiku/rag/monitor.py +17 -4
- haiku/rag/qa/__init__.py +16 -3
- haiku/rag/qa/agent.py +4 -2
- haiku/rag/reranking/__init__.py +24 -16
- haiku/rag/reranking/base.py +1 -1
- haiku/rag/reranking/cohere.py +2 -2
- haiku/rag/reranking/mxbai.py +1 -1
- haiku/rag/reranking/vllm.py +1 -1
- haiku/rag/store/engine.py +19 -12
- haiku/rag/store/repositories/chunk.py +12 -8
- haiku/rag/store/repositories/document.py +4 -4
- haiku/rag/store/repositories/settings.py +19 -9
- haiku/rag/utils.py +9 -9
- {haiku_rag-0.12.1.dist-info → haiku_rag-0.13.1.dist-info}/METADATA +20 -10
- {haiku_rag-0.12.1.dist-info → haiku_rag-0.13.1.dist-info}/RECORD +32 -31
- haiku/rag/config.py +0 -90
- haiku/rag/migration.py +0 -316
- {haiku_rag-0.12.1.dist-info → haiku_rag-0.13.1.dist-info}/WHEEL +0 -0
- {haiku_rag-0.12.1.dist-info → haiku_rag-0.13.1.dist-info}/entry_points.txt +0 -0
- {haiku_rag-0.12.1.dist-info → haiku_rag-0.13.1.dist-info}/licenses/LICENSE +0 -0
haiku/rag/utils.py
CHANGED
|
@@ -176,19 +176,19 @@ def prefetch_models():
|
|
|
176
176
|
|
|
177
177
|
# Collect Ollama models from config
|
|
178
178
|
required_models: set[str] = set()
|
|
179
|
-
if Config.
|
|
180
|
-
required_models.add(Config.
|
|
181
|
-
if Config.
|
|
182
|
-
required_models.add(Config.
|
|
183
|
-
if Config.
|
|
184
|
-
required_models.add(Config.
|
|
185
|
-
if Config.
|
|
186
|
-
required_models.add(Config.
|
|
179
|
+
if Config.embeddings.provider == "ollama":
|
|
180
|
+
required_models.add(Config.embeddings.model)
|
|
181
|
+
if Config.qa.provider == "ollama":
|
|
182
|
+
required_models.add(Config.qa.model)
|
|
183
|
+
if Config.research.provider == "ollama":
|
|
184
|
+
required_models.add(Config.research.model)
|
|
185
|
+
if Config.reranking.provider == "ollama":
|
|
186
|
+
required_models.add(Config.reranking.model)
|
|
187
187
|
|
|
188
188
|
if not required_models:
|
|
189
189
|
return
|
|
190
190
|
|
|
191
|
-
base_url = Config.
|
|
191
|
+
base_url = Config.providers.ollama.base_url
|
|
192
192
|
|
|
193
193
|
with httpx.Client(timeout=None) as client:
|
|
194
194
|
for model in sorted(required_models):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haiku.rag
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.1
|
|
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
|
|
@@ -40,11 +40,13 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
# Haiku RAG
|
|
42
42
|
|
|
43
|
+
mcp-name: io.github.ggozad/haiku-rag
|
|
44
|
+
|
|
43
45
|
Retrieval-Augmented Generation (RAG) library built on LanceDB.
|
|
44
46
|
|
|
45
47
|
`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
48
|
|
|
47
|
-
> **Note**:
|
|
49
|
+
> **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
50
|
|
|
49
51
|
## Features
|
|
50
52
|
|
|
@@ -65,6 +67,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
|
|
|
65
67
|
|
|
66
68
|
```bash
|
|
67
69
|
# Install
|
|
70
|
+
# Python 3.12 or newer required
|
|
68
71
|
uv pip install haiku.rag
|
|
69
72
|
|
|
70
73
|
# Add documents
|
|
@@ -98,14 +101,12 @@ haiku-rag research \
|
|
|
98
101
|
# Rebuild database (re-chunk and re-embed all documents)
|
|
99
102
|
haiku-rag rebuild
|
|
100
103
|
|
|
101
|
-
# Migrate from SQLite to LanceDB
|
|
102
|
-
haiku-rag migrate old_database.sqlite
|
|
103
|
-
|
|
104
104
|
# Start server with file monitoring
|
|
105
|
-
|
|
106
|
-
haiku-rag serve
|
|
105
|
+
haiku-rag serve --monitor
|
|
107
106
|
```
|
|
108
107
|
|
|
108
|
+
To customize settings, create a `haiku.rag.yaml` config file (see [Configuration](https://ggozad.github.io/haiku.rag/configuration/)).
|
|
109
|
+
|
|
109
110
|
## Python Usage
|
|
110
111
|
|
|
111
112
|
```python
|
|
@@ -197,17 +198,26 @@ haiku-rag a2aclient
|
|
|
197
198
|
```
|
|
198
199
|
|
|
199
200
|
The A2A agent provides:
|
|
201
|
+
|
|
200
202
|
- Multi-turn dialogue with context
|
|
201
203
|
- Intelligent multi-search for complex questions
|
|
202
204
|
- Source citations with titles and URIs
|
|
203
205
|
- Full document retrieval on request
|
|
204
206
|
|
|
207
|
+
## Examples
|
|
208
|
+
|
|
209
|
+
See the [examples directory](examples/) for working examples:
|
|
210
|
+
|
|
211
|
+
- **[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
|
|
212
|
+
- **[Docker Setup](examples/docker/)** - Complete Docker deployment with file monitoring, MCP server, and A2A agent
|
|
213
|
+
- **[A2A Security](examples/a2a-security/)** - Authentication examples (API key, OAuth2, GitHub)
|
|
214
|
+
|
|
205
215
|
## Documentation
|
|
206
216
|
|
|
207
217
|
Full documentation at: https://ggozad.github.io/haiku.rag/
|
|
208
218
|
|
|
209
219
|
- [Installation](https://ggozad.github.io/haiku.rag/installation/) - Provider setup
|
|
210
|
-
- [Configuration](https://ggozad.github.io/haiku.rag/configuration/) -
|
|
220
|
+
- [Configuration](https://ggozad.github.io/haiku.rag/configuration/) - YAML configuration
|
|
211
221
|
- [CLI](https://ggozad.github.io/haiku.rag/cli/) - Command reference
|
|
212
222
|
- [Python API](https://ggozad.github.io/haiku.rag/python/) - Complete API docs
|
|
213
223
|
- [Agents](https://ggozad.github.io/haiku.rag/agents/) - QA agent and multi-agent research
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
haiku/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
haiku/rag/app.py,sha256=
|
|
3
|
-
haiku/rag/chunker.py,sha256=
|
|
4
|
-
haiku/rag/cli.py,sha256=
|
|
5
|
-
haiku/rag/client.py,sha256=
|
|
6
|
-
haiku/rag/config.py,sha256=FBsMMijl5PxIfPGifk_AJVRjL4omb03jfoZm0P_VqxI,2743
|
|
2
|
+
haiku/rag/app.py,sha256=lNPYYWQLOPlOLYiwIR4yQuwzl-LrRNQn7n2nacgdq_k,21594
|
|
3
|
+
haiku/rag/chunker.py,sha256=pA0S0fFKAuvzGm2dGyp7FAkeFZA0YTCm_ata83Pnflw,1566
|
|
4
|
+
haiku/rag/cli.py,sha256=Y42tnlVFGvCZVjBcLWrIVgM0A7KjNYX9MAuk9-zQvvE,14523
|
|
5
|
+
haiku/rag/client.py,sha256=cG6DAhzJJ4vdo8QFn9p8iA6YTa0arMrTtIswoZc7sY0,26816
|
|
7
6
|
haiku/rag/logging.py,sha256=dm65AwADpcQsH5OAPtRA-4hsw0w5DK-sGOvzYkj6jzw,1720
|
|
8
|
-
haiku/rag/mcp.py,sha256=
|
|
9
|
-
haiku/rag/
|
|
10
|
-
haiku/rag/monitor.py,sha256=VP3bqY0mEodOP60eN4RMldgrL1ti5gMjuDuQ-_vBvFc,2759
|
|
7
|
+
haiku/rag/mcp.py,sha256=txuEnrUMWvs_shQBk15gEkJD7xNdSYzp3z75UUWaHFM,9328
|
|
8
|
+
haiku/rag/monitor.py,sha256=d92oRufhI8oYXH7oF6oYVf1_AcpFUafjM6tl4VhAupI,3322
|
|
11
9
|
haiku/rag/reader.py,sha256=aW8LG0X31kVWS7kU2tKVpe8RqP3Ne_oIidd_X3UDLH0,3307
|
|
12
|
-
haiku/rag/utils.py,sha256=
|
|
13
|
-
haiku/rag/a2a/__init__.py,sha256=
|
|
10
|
+
haiku/rag/utils.py,sha256=47ehVYJlLz6Of_Ua89qj94JclO5ZPBFU9eyonifvnVg,6131
|
|
11
|
+
haiku/rag/a2a/__init__.py,sha256=tY_jLSUM0zKzyBctMkjpqmDWpxWc9QVEK1qAsb-plGs,5933
|
|
14
12
|
haiku/rag/a2a/client.py,sha256=awuiHXgVHn1uzaEXE98RIqqKHj1JjszOvn9WI3Jtth8,8760
|
|
15
13
|
haiku/rag/a2a/context.py,sha256=SofkFUZcGonoJcgZh-RGqHTh0UWT4J7Zl4Mz6WDkMl4,2053
|
|
16
14
|
haiku/rag/a2a/models.py,sha256=XhGYj2g3rgVM4JoCDXlll0YjaysqdalybJrBqFXSwl4,689
|
|
@@ -18,15 +16,18 @@ haiku/rag/a2a/prompts.py,sha256=yCla8x0hbOhKrkuaqVrF1upn-YjQM3-2NsE2TSnet0M,3030
|
|
|
18
16
|
haiku/rag/a2a/skills.py,sha256=dwyD2Bn493eL3Vf4uQzmyxj_9IUSb66kQ-085FBAuCs,2701
|
|
19
17
|
haiku/rag/a2a/storage.py,sha256=c8vmGCiZ3nuV9wUuTnwpoRD2HVVvK2JPySQOc5PVMvg,2759
|
|
20
18
|
haiku/rag/a2a/worker.py,sha256=S9hiA1ncpJPdtN0eEmMjsvr5LQ4wMVN5R8CjYkTeohU,12367
|
|
21
|
-
haiku/rag/
|
|
22
|
-
haiku/rag/
|
|
23
|
-
haiku/rag/
|
|
19
|
+
haiku/rag/config/__init__.py,sha256=PSHsc7gXjvRxpzN4rxR083-WYU-pocqm0hf2uhkr9Vw,1019
|
|
20
|
+
haiku/rag/config/loader.py,sha256=eWkD8uVTa19nf7d7yyZImk7t5k0-SagYH4RSBqfkPxQ,4848
|
|
21
|
+
haiku/rag/config/models.py,sha256=vkq2WyJfuY1cm8YEFlox0Cd8sVyXb4l1XX2fkBjI6I4,2169
|
|
22
|
+
haiku/rag/embeddings/__init__.py,sha256=zwWRU9S5YGEJxlgPv5haHBgj3LUJMe-dEwr3LKLa9RY,1731
|
|
23
|
+
haiku/rag/embeddings/base.py,sha256=kzca54e2HGzS_0YKt9OLESM9lrFKpBm_97V07jx0aas,783
|
|
24
|
+
haiku/rag/embeddings/ollama.py,sha256=_uIIObbZX9QVU1lcgWQFooA3b-AeZRNncM7yQ2TxlEU,825
|
|
24
25
|
haiku/rag/embeddings/openai.py,sha256=BfmPni567DH8KqwLCPiOmr3q-dpzpOJkvFFoUuTR5as,731
|
|
25
|
-
haiku/rag/embeddings/vllm.py,sha256=
|
|
26
|
+
haiku/rag/embeddings/vllm.py,sha256=IZFS3pbvLXkhvdT7pFVi2csFlNTSza5bpybwz7ud3Po,847
|
|
26
27
|
haiku/rag/embeddings/voyageai.py,sha256=6vEuk6q510AJv-K2lL93P2dVrziAjELTOe_w_Zp5YT4,917
|
|
27
28
|
haiku/rag/graph/__init__.py,sha256=BHfMchuUO_UhHKpjjGHjd6xPxNkrIwJzHn4YJiLqG1g,62
|
|
28
29
|
haiku/rag/graph/base.py,sha256=DepZqLF9E64YCCkjmbqmgyp28oNp69WfJCXp614xzh0,819
|
|
29
|
-
haiku/rag/graph/common.py,sha256
|
|
30
|
+
haiku/rag/graph/common.py,sha256=-Pdao6ZiTgv4ppNctrRpwLG0U6za-66aScQWZ0uCUjc,1016
|
|
30
31
|
haiku/rag/graph/models.py,sha256=sgL5_wSbQJrNOITH615jryPBhTE8J3ZiZWVxO9Ty-JI,755
|
|
31
32
|
haiku/rag/graph/prompts.py,sha256=xJgAHjUVczxCgk7YLPyy6DdQFi0lwj42vJqIFnPqcYw,2221
|
|
32
33
|
haiku/rag/graph/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -34,8 +35,8 @@ haiku/rag/graph/nodes/analysis.py,sha256=g-Aw3nPuCHWo0CXM96Ixa4vQI4TpI6tg6ooHT_J
|
|
|
34
35
|
haiku/rag/graph/nodes/plan.py,sha256=Bb6Fva9vwArCU-5xBr24N4pM3wfLP-Vwufgss8HfXMQ,2622
|
|
35
36
|
haiku/rag/graph/nodes/search.py,sha256=DdHhEY7fmWUqis6Nk0bj-di56-ML262B51N9zytzKYk,3699
|
|
36
37
|
haiku/rag/graph/nodes/synthesize.py,sha256=WF0D44SwLP1OK8C6ViOAhFOtGQ0mj3aO54z5bemJb4E,1828
|
|
37
|
-
haiku/rag/qa/__init__.py,sha256=
|
|
38
|
-
haiku/rag/qa/agent.py,sha256=
|
|
38
|
+
haiku/rag/qa/__init__.py,sha256=Q18B5cjgYSuOdzwsJkXDeqcclAI2pu3tBIcWLcMTT5M,949
|
|
39
|
+
haiku/rag/qa/agent.py,sha256=ReuvluxVzaH82PhrFLNAAM3rVrSj-sKHkhki266SsGI,3181
|
|
39
40
|
haiku/rag/qa/prompts.py,sha256=Lqwn3m4zCsu_CJiC4s9cLsuPNbb9nq6j2PqEF3lw1eA,3380
|
|
40
41
|
haiku/rag/qa/deep/__init__.py,sha256=SnCpWxWip-TaFzVKlFyrOgYeXEqT_gpIlaSItEEJ6r0,50
|
|
41
42
|
haiku/rag/qa/deep/dependencies.py,sha256=AKFqcC1D3N1VPudnFmLH29K5eJWEC5wtwUGkO4FM4jc,998
|
|
@@ -44,11 +45,11 @@ haiku/rag/qa/deep/models.py,sha256=siZMQXD21_3nk8kaLCv0BCuD9TydLYo-yC4-9CxQy3E,6
|
|
|
44
45
|
haiku/rag/qa/deep/nodes.py,sha256=XbDujD_xg-NsJS2oYm3LqkfxeHZCzT2f9wBNhVh0wns,10442
|
|
45
46
|
haiku/rag/qa/deep/prompts.py,sha256=t1fEvoD5Rdab92eAOvefv2wBVmkPFuR0BQ8Kh4X0-mY,2565
|
|
46
47
|
haiku/rag/qa/deep/state.py,sha256=ICFIX0oRtBs6Sdq9YnmnP04BkGiQYwucfS3Mf8XLcCU,570
|
|
47
|
-
haiku/rag/reranking/__init__.py,sha256=
|
|
48
|
-
haiku/rag/reranking/base.py,sha256=
|
|
49
|
-
haiku/rag/reranking/cohere.py,sha256=
|
|
50
|
-
haiku/rag/reranking/mxbai.py,sha256=
|
|
51
|
-
haiku/rag/reranking/vllm.py,sha256=
|
|
48
|
+
haiku/rag/reranking/__init__.py,sha256=34xH2YZ7OCC3H8Yb-zyPuehTRQdijMSY9TC8AmZDOGQ,1296
|
|
49
|
+
haiku/rag/reranking/base.py,sha256=Yji15nAR8LyIJGqZvEZifTWmortNQ4k_7ZHst_5mRYk,408
|
|
50
|
+
haiku/rag/reranking/cohere.py,sha256=BhBPPnaSnDoVlkL_MHF74kegXQBrsZGKnWqC40ztiAk,1050
|
|
51
|
+
haiku/rag/reranking/mxbai.py,sha256=qR55dmpaBz15lSN_wXD3-Z6Kqr_bmNKU9q4Pwef_wB8,911
|
|
52
|
+
haiku/rag/reranking/vllm.py,sha256=Ip83qzV2RM7qXTj0mE2St66hvXykovoNW8Hu3AUebDc,1489
|
|
52
53
|
haiku/rag/research/__init__.py,sha256=xfVzYkt8QETjZaP8v4LdK8MC2R_JmxKDD34LefvrJbo,201
|
|
53
54
|
haiku/rag/research/common.py,sha256=mrKXolTnDxcONjodmSPVAtXYWqbU0Bie2W-4lOTclGY,2988
|
|
54
55
|
haiku/rag/research/dependencies.py,sha256=hiGImk7HyJF4LRYnTmsLFGzY1QrGjAyPW5vb_2JQRDo,8148
|
|
@@ -58,19 +59,19 @@ haiku/rag/research/prompts.py,sha256=opz4MXjoDHH1wjG6bPyiqT0LVzk3pBA6y_a9zpBW8yM
|
|
|
58
59
|
haiku/rag/research/state.py,sha256=P8RXJMi3wA3l1j6yo8dsAyso6S27FgqS7fvZUUY447A,917
|
|
59
60
|
haiku/rag/research/stream.py,sha256=amyGDimkNp_FHYUXCqtpbeDOx7sC1jQ-7DwoxuNOL1g,5576
|
|
60
61
|
haiku/rag/store/__init__.py,sha256=R2IRcxtkFDxqa2sgMirqLq3l2-FPdWr6ydYStaqm5OQ,104
|
|
61
|
-
haiku/rag/store/engine.py,sha256=
|
|
62
|
+
haiku/rag/store/engine.py,sha256=FP1-9LOxoEvQBswYcM2GS_E2RpvSZct49vVktL-oPlo,11697
|
|
62
63
|
haiku/rag/store/models/__init__.py,sha256=kc7Ctf53Jr483tk4QTIrcgqBbXDz4ZoeYSkFXfPnpks,89
|
|
63
64
|
haiku/rag/store/models/chunk.py,sha256=3EuZav4QekJIeHBCub48EM8SjNX8HEJ6wVDXGot4PEQ,421
|
|
64
65
|
haiku/rag/store/models/document.py,sha256=cZXy_jEti-hnhq7FKhuhCfd99ccY9fIHMLovB_Thbb8,425
|
|
65
66
|
haiku/rag/store/repositories/__init__.py,sha256=Olv5dLfBQINRV3HrsfUpjzkZ7Qm7goEYyMNykgo_DaY,291
|
|
66
|
-
haiku/rag/store/repositories/chunk.py,sha256=
|
|
67
|
-
haiku/rag/store/repositories/document.py,sha256=
|
|
68
|
-
haiku/rag/store/repositories/settings.py,sha256=
|
|
67
|
+
haiku/rag/store/repositories/chunk.py,sha256=bXa-NBfLdLKJuFLGEKQhFlsLi-XNbojhQYVyBjwUxz8,14205
|
|
68
|
+
haiku/rag/store/repositories/document.py,sha256=UOC_5QEUl-3RnGPJzn92KjrCnhmh5TmWln4yd5cJ4Ss,8133
|
|
69
|
+
haiku/rag/store/repositories/settings.py,sha256=15gS7Xj7cG4qetv_ioxZO_r31by7GuSqtpowOsMkHmc,6129
|
|
69
70
|
haiku/rag/store/upgrades/__init__.py,sha256=RQ8A6rEXBASLb5PD9vdDnEas_m_GgRzzdVu4B88Snqc,1975
|
|
70
71
|
haiku/rag/store/upgrades/v0_10_1.py,sha256=qNGnxj6hoHaHJ1rKTiALfw0c9NQOi0KAK-VZCD_073A,1959
|
|
71
72
|
haiku/rag/store/upgrades/v0_9_3.py,sha256=NrjNilQSgDtFWRbL3ZUtzQzJ8tf9u0dDRJtnDFwwbdw,3322
|
|
72
|
-
haiku_rag-0.
|
|
73
|
-
haiku_rag-0.
|
|
74
|
-
haiku_rag-0.
|
|
75
|
-
haiku_rag-0.
|
|
76
|
-
haiku_rag-0.
|
|
73
|
+
haiku_rag-0.13.1.dist-info/METADATA,sha256=xoojNWUahlMw6gWdujYr_VNti4ss4We0mL0rkTOkxgo,8139
|
|
74
|
+
haiku_rag-0.13.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
75
|
+
haiku_rag-0.13.1.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
|
|
76
|
+
haiku_rag-0.13.1.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
|
|
77
|
+
haiku_rag-0.13.1.dist-info/RECORD,,
|
haiku/rag/config.py
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
from dotenv import load_dotenv
|
|
5
|
-
from pydantic import BaseModel, field_validator
|
|
6
|
-
|
|
7
|
-
from haiku.rag.utils import get_default_data_dir
|
|
8
|
-
|
|
9
|
-
load_dotenv()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AppConfig(BaseModel):
|
|
13
|
-
ENV: str = "production"
|
|
14
|
-
|
|
15
|
-
LANCEDB_API_KEY: str = ""
|
|
16
|
-
LANCEDB_URI: str = ""
|
|
17
|
-
LANCEDB_REGION: str = ""
|
|
18
|
-
|
|
19
|
-
DEFAULT_DATA_DIR: Path = get_default_data_dir()
|
|
20
|
-
MONITOR_DIRECTORIES: list[Path] = []
|
|
21
|
-
|
|
22
|
-
EMBEDDINGS_PROVIDER: str = "ollama"
|
|
23
|
-
EMBEDDINGS_MODEL: str = "qwen3-embedding"
|
|
24
|
-
EMBEDDINGS_VECTOR_DIM: int = 4096
|
|
25
|
-
|
|
26
|
-
RERANK_PROVIDER: str = ""
|
|
27
|
-
RERANK_MODEL: str = ""
|
|
28
|
-
|
|
29
|
-
QA_PROVIDER: str = "ollama"
|
|
30
|
-
QA_MODEL: str = "gpt-oss"
|
|
31
|
-
|
|
32
|
-
# Research defaults (fallback to QA if not provided via env)
|
|
33
|
-
RESEARCH_PROVIDER: str = "ollama"
|
|
34
|
-
RESEARCH_MODEL: str = "gpt-oss"
|
|
35
|
-
|
|
36
|
-
CHUNK_SIZE: int = 256
|
|
37
|
-
CONTEXT_CHUNK_RADIUS: int = 0
|
|
38
|
-
|
|
39
|
-
# Optional dotted path or file path to a callable that preprocesses
|
|
40
|
-
# markdown content before chunking. Examples:
|
|
41
|
-
MARKDOWN_PREPROCESSOR: str = ""
|
|
42
|
-
|
|
43
|
-
OLLAMA_BASE_URL: str = "http://localhost:11434"
|
|
44
|
-
|
|
45
|
-
VLLM_EMBEDDINGS_BASE_URL: str = ""
|
|
46
|
-
VLLM_RERANK_BASE_URL: str = ""
|
|
47
|
-
VLLM_QA_BASE_URL: str = ""
|
|
48
|
-
VLLM_RESEARCH_BASE_URL: str = ""
|
|
49
|
-
|
|
50
|
-
# Provider keys
|
|
51
|
-
VOYAGE_API_KEY: str = ""
|
|
52
|
-
OPENAI_API_KEY: str = ""
|
|
53
|
-
ANTHROPIC_API_KEY: str = ""
|
|
54
|
-
COHERE_API_KEY: str = ""
|
|
55
|
-
|
|
56
|
-
# If true, refuse to auto-create a new LanceDB database or tables
|
|
57
|
-
# and error out when the database does not already exist.
|
|
58
|
-
DISABLE_DB_AUTOCREATE: bool = False
|
|
59
|
-
|
|
60
|
-
# Vacuum retention threshold in seconds. Only versions older than this
|
|
61
|
-
# threshold will be removed during vacuum operations. Default is 60 seconds
|
|
62
|
-
# to allow concurrent connections to safely use recent versions.
|
|
63
|
-
VACUUM_RETENTION_SECONDS: int = 60
|
|
64
|
-
|
|
65
|
-
# Maximum number of A2A contexts to keep in memory. When exceeded, least
|
|
66
|
-
# recently used contexts will be evicted. Default is 1000.
|
|
67
|
-
A2A_MAX_CONTEXTS: int = 1000
|
|
68
|
-
|
|
69
|
-
@field_validator("MONITOR_DIRECTORIES", mode="before")
|
|
70
|
-
@classmethod
|
|
71
|
-
def parse_monitor_directories(cls, v):
|
|
72
|
-
if isinstance(v, str):
|
|
73
|
-
if not v.strip():
|
|
74
|
-
return []
|
|
75
|
-
return [
|
|
76
|
-
Path(path.strip()).absolute() for path in v.split(",") if path.strip()
|
|
77
|
-
]
|
|
78
|
-
return v
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# Expose Config object for app to import
|
|
82
|
-
Config = AppConfig.model_validate(os.environ)
|
|
83
|
-
if Config.OPENAI_API_KEY:
|
|
84
|
-
os.environ["OPENAI_API_KEY"] = Config.OPENAI_API_KEY
|
|
85
|
-
if Config.VOYAGE_API_KEY:
|
|
86
|
-
os.environ["VOYAGE_API_KEY"] = Config.VOYAGE_API_KEY
|
|
87
|
-
if Config.ANTHROPIC_API_KEY:
|
|
88
|
-
os.environ["ANTHROPIC_API_KEY"] = Config.ANTHROPIC_API_KEY
|
|
89
|
-
if Config.COHERE_API_KEY:
|
|
90
|
-
os.environ["CO_API_KEY"] = Config.COHERE_API_KEY
|
haiku/rag/migration.py
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import sqlite3
|
|
3
|
-
import struct
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from uuid import uuid4
|
|
6
|
-
|
|
7
|
-
from rich.console import Console
|
|
8
|
-
from rich.progress import Progress, TaskID
|
|
9
|
-
|
|
10
|
-
from haiku.rag.store.engine import Store
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def deserialize_sqlite_embedding(data: bytes) -> list[float]:
|
|
14
|
-
"""Deserialize sqlite-vec embedding from bytes."""
|
|
15
|
-
if not data:
|
|
16
|
-
return []
|
|
17
|
-
# sqlite-vec stores embeddings as float32 arrays
|
|
18
|
-
num_floats = len(data) // 4
|
|
19
|
-
return list(struct.unpack(f"{num_floats}f", data))
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class SQLiteToLanceDBMigrator:
|
|
23
|
-
"""Migrates data from SQLite to LanceDB."""
|
|
24
|
-
|
|
25
|
-
def __init__(self, sqlite_path: Path, lancedb_path: Path):
|
|
26
|
-
self.sqlite_path = sqlite_path
|
|
27
|
-
self.lancedb_path = lancedb_path
|
|
28
|
-
self.console = Console()
|
|
29
|
-
|
|
30
|
-
async def migrate(self) -> bool:
|
|
31
|
-
"""Perform the migration."""
|
|
32
|
-
try:
|
|
33
|
-
self.console.print(
|
|
34
|
-
f"[blue]Starting migration from {self.sqlite_path} to {self.lancedb_path}[/blue]"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
# Check if SQLite database exists
|
|
38
|
-
if not self.sqlite_path.exists():
|
|
39
|
-
self.console.print(
|
|
40
|
-
f"[red]SQLite database not found: {self.sqlite_path}[/red]"
|
|
41
|
-
)
|
|
42
|
-
return False
|
|
43
|
-
|
|
44
|
-
# Connect to SQLite database
|
|
45
|
-
sqlite_conn = sqlite3.connect(self.sqlite_path)
|
|
46
|
-
sqlite_conn.row_factory = sqlite3.Row
|
|
47
|
-
|
|
48
|
-
# Load the sqlite-vec extension
|
|
49
|
-
try:
|
|
50
|
-
import sqlite_vec # type: ignore
|
|
51
|
-
|
|
52
|
-
sqlite_conn.enable_load_extension(True)
|
|
53
|
-
sqlite_vec.load(sqlite_conn)
|
|
54
|
-
self.console.print("[cyan]Loaded sqlite-vec extension[/cyan]")
|
|
55
|
-
except Exception as e:
|
|
56
|
-
self.console.print(
|
|
57
|
-
f"[yellow]Warning: Could not load sqlite-vec extension: {e}[/yellow]"
|
|
58
|
-
)
|
|
59
|
-
self.console.print(
|
|
60
|
-
"[yellow]Install sqlite-vec with[/yellow]\n[green]uv pip install sqlite-vec [/green]"
|
|
61
|
-
)
|
|
62
|
-
exit(1)
|
|
63
|
-
|
|
64
|
-
# Create LanceDB store
|
|
65
|
-
lance_store = Store(self.lancedb_path, skip_validation=True)
|
|
66
|
-
|
|
67
|
-
with Progress() as progress:
|
|
68
|
-
# Migrate documents
|
|
69
|
-
doc_task = progress.add_task(
|
|
70
|
-
"[green]Migrating documents...", total=None
|
|
71
|
-
)
|
|
72
|
-
document_id_mapping = self._migrate_documents(
|
|
73
|
-
sqlite_conn, lance_store, progress, doc_task
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
# Migrate chunks and embeddings
|
|
77
|
-
chunk_task = progress.add_task(
|
|
78
|
-
"[yellow]Migrating chunks and embeddings...", total=None
|
|
79
|
-
)
|
|
80
|
-
self._migrate_chunks(
|
|
81
|
-
sqlite_conn, lance_store, progress, chunk_task, document_id_mapping
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
# Migrate settings
|
|
85
|
-
settings_task = progress.add_task(
|
|
86
|
-
"[blue]Migrating settings...", total=None
|
|
87
|
-
)
|
|
88
|
-
self._migrate_settings(
|
|
89
|
-
sqlite_conn, lance_store, progress, settings_task
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
sqlite_conn.close()
|
|
93
|
-
|
|
94
|
-
# Optimize and cleanup using centralized vacuum
|
|
95
|
-
self.console.print("[cyan]Optimizing LanceDB...[/cyan]")
|
|
96
|
-
try:
|
|
97
|
-
await lance_store.vacuum()
|
|
98
|
-
self.console.print("[green]✅ Optimization completed[/green]")
|
|
99
|
-
except Exception as e:
|
|
100
|
-
self.console.print(
|
|
101
|
-
f"[yellow]Warning: Optimization failed: {e}[/yellow]"
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
lance_store.close()
|
|
105
|
-
|
|
106
|
-
self.console.print("[green]✅ Migration completed successfully![/green]")
|
|
107
|
-
self.console.print(
|
|
108
|
-
f"[green]✅ Migrated {len(document_id_mapping)} documents[/green]"
|
|
109
|
-
)
|
|
110
|
-
return True
|
|
111
|
-
|
|
112
|
-
except Exception as e:
|
|
113
|
-
self.console.print(f"[red]❌ Migration failed: {e}[/red]")
|
|
114
|
-
import traceback
|
|
115
|
-
|
|
116
|
-
self.console.print(f"[red]{traceback.format_exc()}[/red]")
|
|
117
|
-
return False
|
|
118
|
-
|
|
119
|
-
def _migrate_documents(
|
|
120
|
-
self,
|
|
121
|
-
sqlite_conn: sqlite3.Connection,
|
|
122
|
-
lance_store: Store,
|
|
123
|
-
progress: Progress,
|
|
124
|
-
task: TaskID,
|
|
125
|
-
) -> dict[int, str]:
|
|
126
|
-
"""Migrate documents from SQLite to LanceDB and return ID mapping."""
|
|
127
|
-
cursor = sqlite_conn.cursor()
|
|
128
|
-
cursor.execute(
|
|
129
|
-
"SELECT id, content, uri, metadata, created_at, updated_at FROM documents ORDER BY id"
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
documents = []
|
|
133
|
-
id_mapping = {} # Maps old integer ID to new UUID
|
|
134
|
-
|
|
135
|
-
for row in cursor.fetchall():
|
|
136
|
-
new_uuid = str(uuid4())
|
|
137
|
-
id_mapping[row["id"]] = new_uuid
|
|
138
|
-
|
|
139
|
-
doc_data = {
|
|
140
|
-
"id": new_uuid,
|
|
141
|
-
"content": row["content"],
|
|
142
|
-
"uri": row["uri"],
|
|
143
|
-
"metadata": json.loads(row["metadata"]) if row["metadata"] else {},
|
|
144
|
-
"created_at": row["created_at"],
|
|
145
|
-
"updated_at": row["updated_at"],
|
|
146
|
-
}
|
|
147
|
-
documents.append(doc_data)
|
|
148
|
-
|
|
149
|
-
# Batch insert documents to LanceDB
|
|
150
|
-
if documents:
|
|
151
|
-
from haiku.rag.store.engine import DocumentRecord
|
|
152
|
-
|
|
153
|
-
doc_records = [
|
|
154
|
-
DocumentRecord(
|
|
155
|
-
id=doc["id"],
|
|
156
|
-
content=doc["content"],
|
|
157
|
-
uri=doc["uri"],
|
|
158
|
-
metadata=json.dumps(doc["metadata"]),
|
|
159
|
-
created_at=doc["created_at"],
|
|
160
|
-
updated_at=doc["updated_at"],
|
|
161
|
-
)
|
|
162
|
-
for doc in documents
|
|
163
|
-
]
|
|
164
|
-
lance_store.documents_table.add(doc_records)
|
|
165
|
-
|
|
166
|
-
progress.update(task, completed=len(documents), total=len(documents))
|
|
167
|
-
return id_mapping
|
|
168
|
-
|
|
169
|
-
def _migrate_chunks(
|
|
170
|
-
self,
|
|
171
|
-
sqlite_conn: sqlite3.Connection,
|
|
172
|
-
lance_store: Store,
|
|
173
|
-
progress: Progress,
|
|
174
|
-
task: TaskID,
|
|
175
|
-
document_id_mapping: dict[int, str],
|
|
176
|
-
):
|
|
177
|
-
"""Migrate chunks and embeddings from SQLite to LanceDB."""
|
|
178
|
-
cursor = sqlite_conn.cursor()
|
|
179
|
-
|
|
180
|
-
# Get chunks first
|
|
181
|
-
cursor.execute("""
|
|
182
|
-
SELECT id, document_id, content, metadata
|
|
183
|
-
FROM chunks
|
|
184
|
-
ORDER BY id
|
|
185
|
-
""")
|
|
186
|
-
|
|
187
|
-
chunks_data = cursor.fetchall()
|
|
188
|
-
|
|
189
|
-
# Get embeddings using the sqlite-vec virtual table
|
|
190
|
-
embeddings_map = {}
|
|
191
|
-
try:
|
|
192
|
-
# Use the virtual table to get embeddings properly
|
|
193
|
-
cursor.execute("""
|
|
194
|
-
SELECT chunk_id, embedding
|
|
195
|
-
FROM chunk_embeddings
|
|
196
|
-
""")
|
|
197
|
-
|
|
198
|
-
for row in cursor.fetchall():
|
|
199
|
-
chunk_id = row[0]
|
|
200
|
-
embedding_blob = row[1]
|
|
201
|
-
if embedding_blob and chunk_id not in embeddings_map:
|
|
202
|
-
embeddings_map[chunk_id] = embedding_blob
|
|
203
|
-
|
|
204
|
-
except sqlite3.OperationalError as e:
|
|
205
|
-
self.console.print(
|
|
206
|
-
f"[yellow]Warning: Could not extract embeddings from virtual table: {e}[/yellow]"
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
chunks = []
|
|
210
|
-
for row in chunks_data:
|
|
211
|
-
# Generate new UUID for chunk
|
|
212
|
-
chunk_uuid = str(uuid4())
|
|
213
|
-
|
|
214
|
-
# Map the old document_id to new UUID
|
|
215
|
-
document_uuid = document_id_mapping.get(row["document_id"])
|
|
216
|
-
if not document_uuid:
|
|
217
|
-
self.console.print(
|
|
218
|
-
f"[yellow]Warning: Document ID {row['document_id']} not found in mapping for chunk {row['id']}[/yellow]"
|
|
219
|
-
)
|
|
220
|
-
continue
|
|
221
|
-
|
|
222
|
-
# Get embedding for this chunk
|
|
223
|
-
embedding = []
|
|
224
|
-
embedding_blob = embeddings_map.get(row["id"])
|
|
225
|
-
if embedding_blob:
|
|
226
|
-
try:
|
|
227
|
-
embedding = deserialize_sqlite_embedding(embedding_blob)
|
|
228
|
-
except Exception as e:
|
|
229
|
-
self.console.print(
|
|
230
|
-
f"[yellow]Warning: Failed to deserialize embedding for chunk {row['id']}: {e}[/yellow]"
|
|
231
|
-
)
|
|
232
|
-
# Generate a zero vector of the expected dimension
|
|
233
|
-
embedding = [0.0] * lance_store.embedder._vector_dim
|
|
234
|
-
else:
|
|
235
|
-
# No embedding found, generate zero vector
|
|
236
|
-
embedding = [0.0] * lance_store.embedder._vector_dim
|
|
237
|
-
|
|
238
|
-
chunk_data = {
|
|
239
|
-
"id": chunk_uuid,
|
|
240
|
-
"document_id": document_uuid,
|
|
241
|
-
"content": row["content"],
|
|
242
|
-
"metadata": json.loads(row["metadata"]) if row["metadata"] else {},
|
|
243
|
-
"vector": embedding,
|
|
244
|
-
}
|
|
245
|
-
chunks.append(chunk_data)
|
|
246
|
-
|
|
247
|
-
# Batch insert chunks to LanceDB
|
|
248
|
-
if chunks:
|
|
249
|
-
chunk_records = [
|
|
250
|
-
lance_store.ChunkRecord(
|
|
251
|
-
id=chunk["id"],
|
|
252
|
-
document_id=chunk["document_id"],
|
|
253
|
-
content=chunk["content"],
|
|
254
|
-
metadata=json.dumps(chunk["metadata"]),
|
|
255
|
-
vector=chunk["vector"],
|
|
256
|
-
)
|
|
257
|
-
for chunk in chunks
|
|
258
|
-
]
|
|
259
|
-
lance_store.chunks_table.add(chunk_records)
|
|
260
|
-
|
|
261
|
-
progress.update(task, completed=len(chunks), total=len(chunks))
|
|
262
|
-
|
|
263
|
-
def _migrate_settings(
|
|
264
|
-
self,
|
|
265
|
-
sqlite_conn: sqlite3.Connection,
|
|
266
|
-
lance_store: Store,
|
|
267
|
-
progress: Progress,
|
|
268
|
-
task: TaskID,
|
|
269
|
-
):
|
|
270
|
-
"""Migrate settings from SQLite to LanceDB."""
|
|
271
|
-
cursor = sqlite_conn.cursor()
|
|
272
|
-
|
|
273
|
-
try:
|
|
274
|
-
cursor.execute("SELECT id, settings FROM settings WHERE id = 1")
|
|
275
|
-
row = cursor.fetchone()
|
|
276
|
-
|
|
277
|
-
if row:
|
|
278
|
-
settings_data = json.loads(row["settings"]) if row["settings"] else {}
|
|
279
|
-
|
|
280
|
-
# Update the existing settings in LanceDB (use string ID)
|
|
281
|
-
lance_store.settings_table.update(
|
|
282
|
-
where="id = 'settings'",
|
|
283
|
-
values={"settings": json.dumps(settings_data)},
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
progress.update(task, completed=1, total=1)
|
|
287
|
-
else:
|
|
288
|
-
progress.update(task, completed=0, total=0)
|
|
289
|
-
|
|
290
|
-
except sqlite3.OperationalError:
|
|
291
|
-
# Settings table doesn't exist in old SQLite database
|
|
292
|
-
self.console.print(
|
|
293
|
-
"[yellow]No settings table found in SQLite database[/yellow]"
|
|
294
|
-
)
|
|
295
|
-
progress.update(task, completed=0, total=0)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
async def migrate_sqlite_to_lancedb(
|
|
299
|
-
sqlite_path: Path, lancedb_path: Path | None = None
|
|
300
|
-
) -> bool:
|
|
301
|
-
"""
|
|
302
|
-
Migrate an existing SQLite database to LanceDB.
|
|
303
|
-
|
|
304
|
-
Args:
|
|
305
|
-
sqlite_path: Path to the existing SQLite database
|
|
306
|
-
lancedb_path: Path for the new LanceDB database (optional, will auto-generate if not provided)
|
|
307
|
-
|
|
308
|
-
Returns:
|
|
309
|
-
True if migration was successful, False otherwise
|
|
310
|
-
"""
|
|
311
|
-
if lancedb_path is None:
|
|
312
|
-
# Auto-generate LanceDB path
|
|
313
|
-
lancedb_path = sqlite_path.parent / (sqlite_path.stem + ".lancedb")
|
|
314
|
-
|
|
315
|
-
migrator = SQLiteToLanceDBMigrator(sqlite_path, lancedb_path)
|
|
316
|
-
return await migrator.migrate()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|