haiku.rag 0.10.1__tar.gz → 0.11.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.
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/PKG-INFO +34 -14
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/README.md +33 -13
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/agents.md +59 -10
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/cli.md +41 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/index.md +2 -1
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/installation.md +10 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/pyproject.toml +1 -1
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/app.py +152 -28
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/cli.py +72 -2
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/migration.py +2 -2
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/__init__.py +8 -0
- haiku_rag-0.11.0/src/haiku/rag/research/common.py +118 -0
- haiku_rag-0.11.0/src/haiku/rag/research/dependencies.py +215 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/graph.py +5 -3
- haiku_rag-0.11.0/src/haiku/rag/research/models.py +203 -0
- haiku_rag-0.11.0/src/haiku/rag/research/nodes/analysis.py +181 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/nodes/plan.py +16 -9
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/nodes/search.py +14 -11
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/nodes/synthesize.py +7 -3
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/research/prompts.py +67 -28
- haiku_rag-0.11.0/src/haiku/rag/research/state.py +32 -0
- haiku_rag-0.11.0/src/haiku/rag/research/stream.py +177 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/__init__.py +1 -1
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/models/__init__.py +1 -1
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/utils.py +34 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_app.py +15 -8
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_cli.py +93 -7
- haiku_rag-0.11.0/tests/test_info.py +79 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_research_graph.py +1 -2
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_research_graph_integration.py +62 -13
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/uv.lock +1 -1
- haiku_rag-0.10.1/src/haiku/rag/research/common.py +0 -53
- haiku_rag-0.10.1/src/haiku/rag/research/dependencies.py +0 -47
- haiku_rag-0.10.1/src/haiku/rag/research/models.py +0 -70
- haiku_rag-0.10.1/src/haiku/rag/research/nodes/evaluate.py +0 -80
- haiku_rag-0.10.1/src/haiku/rag/research/state.py +0 -25
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.github/FUNDING.yml +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.github/workflows/build-docs.yml +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.github/workflows/build-publish.yml +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.gitignore +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.pre-commit-config.yaml +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/.python-version +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/LICENSE +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/benchmarks.md +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/configuration.md +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/mcp.md +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/python.md +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/docs/server.md +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/mkdocs.yml +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/chunker.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/client.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/config.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/base.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/ollama.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/openai.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/vllm.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/embeddings/voyageai.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/logging.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/mcp.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/monitor.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/qa/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/qa/agent.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/qa/prompts.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reader.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reranking/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reranking/base.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reranking/cohere.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reranking/mxbai.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/reranking/vllm.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/engine.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/models/chunk.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/models/document.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/repositories/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/repositories/chunk.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/repositories/document.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/repositories/settings.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/upgrades/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/upgrades/v0_10_1.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/src/haiku/rag/store/upgrades/v0_9_3.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/__init__.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/conftest.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/generate_benchmark_db.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/llm_judge.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_chunk.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_chunker.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_client.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_document.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_embedder.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_lancedb_connection.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_monitor.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_preprocessor.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_qa.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_reader.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_rebuild.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_reranker.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_search.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_settings.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_utils.py +0 -0
- {haiku_rag-0.10.1 → haiku_rag-0.11.0}/tests/test_versioning.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haiku.rag
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Agentic Retrieval Augmented Generation (RAG) with LanceDB
|
|
5
5
|
Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -66,7 +66,8 @@ uv pip install haiku.rag
|
|
|
66
66
|
|
|
67
67
|
# Add documents
|
|
68
68
|
haiku-rag add "Your content here"
|
|
69
|
-
haiku-rag add
|
|
69
|
+
haiku-rag add "Your content here" --meta author=alice --meta topic=notes
|
|
70
|
+
haiku-rag add-src document.pdf --meta source=manual
|
|
70
71
|
|
|
71
72
|
# Search
|
|
72
73
|
haiku-rag search "query"
|
|
@@ -101,11 +102,12 @@ haiku-rag serve
|
|
|
101
102
|
```python
|
|
102
103
|
from haiku.rag.client import HaikuRAG
|
|
103
104
|
from haiku.rag.research import (
|
|
105
|
+
PlanNode,
|
|
104
106
|
ResearchContext,
|
|
105
107
|
ResearchDeps,
|
|
106
108
|
ResearchState,
|
|
107
109
|
build_research_graph,
|
|
108
|
-
|
|
110
|
+
stream_research_graph,
|
|
109
111
|
)
|
|
110
112
|
|
|
111
113
|
async with HaikuRAG("database.lancedb") as client:
|
|
@@ -127,22 +129,40 @@ async with HaikuRAG("database.lancedb") as client:
|
|
|
127
129
|
|
|
128
130
|
# Multi‑agent research pipeline (Plan → Search → Evaluate → Synthesize)
|
|
129
131
|
graph = build_research_graph()
|
|
132
|
+
question = (
|
|
133
|
+
"What are the main drivers and trends of global temperature "
|
|
134
|
+
"anomalies since 1990?"
|
|
135
|
+
)
|
|
130
136
|
state = ResearchState(
|
|
131
|
-
|
|
132
|
-
"What are the main drivers and trends of global temperature "
|
|
133
|
-
"anomalies since 1990?"
|
|
134
|
-
),
|
|
135
|
-
context=ResearchContext(original_question="…"),
|
|
137
|
+
context=ResearchContext(original_question=question),
|
|
136
138
|
max_iterations=2,
|
|
137
139
|
confidence_threshold=0.8,
|
|
138
|
-
max_concurrency=
|
|
140
|
+
max_concurrency=2,
|
|
139
141
|
)
|
|
140
142
|
deps = ResearchDeps(client=client)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
|
|
144
|
+
# Blocking run (final result only)
|
|
145
|
+
result = await graph.run(
|
|
146
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
147
|
+
state=state,
|
|
148
|
+
deps=deps,
|
|
149
|
+
)
|
|
150
|
+
print(result.output.title)
|
|
151
|
+
|
|
152
|
+
# Streaming progress (log/report/error events)
|
|
153
|
+
async for event in stream_research_graph(
|
|
154
|
+
graph,
|
|
155
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
156
|
+
state,
|
|
157
|
+
deps,
|
|
158
|
+
):
|
|
159
|
+
if event.type == "log":
|
|
160
|
+
iteration = event.state.iterations if event.state else state.iterations
|
|
161
|
+
print(f"[{iteration}] {event.message}")
|
|
162
|
+
elif event.type == "report":
|
|
163
|
+
print("\nResearch complete!\n")
|
|
164
|
+
print(event.report.title)
|
|
165
|
+
print(event.report.executive_summary)
|
|
146
166
|
```
|
|
147
167
|
|
|
148
168
|
## MCP Server
|
|
@@ -28,7 +28,8 @@ uv pip install haiku.rag
|
|
|
28
28
|
|
|
29
29
|
# Add documents
|
|
30
30
|
haiku-rag add "Your content here"
|
|
31
|
-
haiku-rag add
|
|
31
|
+
haiku-rag add "Your content here" --meta author=alice --meta topic=notes
|
|
32
|
+
haiku-rag add-src document.pdf --meta source=manual
|
|
32
33
|
|
|
33
34
|
# Search
|
|
34
35
|
haiku-rag search "query"
|
|
@@ -63,11 +64,12 @@ haiku-rag serve
|
|
|
63
64
|
```python
|
|
64
65
|
from haiku.rag.client import HaikuRAG
|
|
65
66
|
from haiku.rag.research import (
|
|
67
|
+
PlanNode,
|
|
66
68
|
ResearchContext,
|
|
67
69
|
ResearchDeps,
|
|
68
70
|
ResearchState,
|
|
69
71
|
build_research_graph,
|
|
70
|
-
|
|
72
|
+
stream_research_graph,
|
|
71
73
|
)
|
|
72
74
|
|
|
73
75
|
async with HaikuRAG("database.lancedb") as client:
|
|
@@ -89,22 +91,40 @@ async with HaikuRAG("database.lancedb") as client:
|
|
|
89
91
|
|
|
90
92
|
# Multi‑agent research pipeline (Plan → Search → Evaluate → Synthesize)
|
|
91
93
|
graph = build_research_graph()
|
|
94
|
+
question = (
|
|
95
|
+
"What are the main drivers and trends of global temperature "
|
|
96
|
+
"anomalies since 1990?"
|
|
97
|
+
)
|
|
92
98
|
state = ResearchState(
|
|
93
|
-
|
|
94
|
-
"What are the main drivers and trends of global temperature "
|
|
95
|
-
"anomalies since 1990?"
|
|
96
|
-
),
|
|
97
|
-
context=ResearchContext(original_question="…"),
|
|
99
|
+
context=ResearchContext(original_question=question),
|
|
98
100
|
max_iterations=2,
|
|
99
101
|
confidence_threshold=0.8,
|
|
100
|
-
max_concurrency=
|
|
102
|
+
max_concurrency=2,
|
|
101
103
|
)
|
|
102
104
|
deps = ResearchDeps(client=client)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
|
|
106
|
+
# Blocking run (final result only)
|
|
107
|
+
result = await graph.run(
|
|
108
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
109
|
+
state=state,
|
|
110
|
+
deps=deps,
|
|
111
|
+
)
|
|
112
|
+
print(result.output.title)
|
|
113
|
+
|
|
114
|
+
# Streaming progress (log/report/error events)
|
|
115
|
+
async for event in stream_research_graph(
|
|
116
|
+
graph,
|
|
117
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
118
|
+
state,
|
|
119
|
+
deps,
|
|
120
|
+
):
|
|
121
|
+
if event.type == "log":
|
|
122
|
+
iteration = event.state.iterations if event.state else state.iterations
|
|
123
|
+
print(f"[{iteration}] {event.message}")
|
|
124
|
+
elif event.type == "report":
|
|
125
|
+
print("\nResearch complete!\n")
|
|
126
|
+
print(event.report.title)
|
|
127
|
+
print(event.report.executive_summary)
|
|
108
128
|
```
|
|
109
129
|
|
|
110
130
|
## MCP Server
|
|
@@ -47,9 +47,10 @@ title: Research graph
|
|
|
47
47
|
---
|
|
48
48
|
stateDiagram-v2
|
|
49
49
|
PlanNode --> SearchDispatchNode
|
|
50
|
-
SearchDispatchNode -->
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
SearchDispatchNode --> AnalyzeInsightsNode
|
|
51
|
+
AnalyzeInsightsNode --> DecisionNode
|
|
52
|
+
DecisionNode --> SearchDispatchNode
|
|
53
|
+
DecisionNode --> SynthesizeNode
|
|
53
54
|
SynthesizeNode --> [*]
|
|
54
55
|
```
|
|
55
56
|
|
|
@@ -57,12 +58,15 @@ Key nodes:
|
|
|
57
58
|
|
|
58
59
|
- Plan: builds up to 3 standalone sub‑questions (uses an internal presearch tool)
|
|
59
60
|
- Search (batched): answers sub‑questions using the KB with minimal, verbatim context
|
|
60
|
-
-
|
|
61
|
+
- Analyze: aggregates fresh insights, updates gaps, and suggests new sub-questions
|
|
62
|
+
- Decision: checks sufficiency/confidence thresholds and chooses whether to iterate
|
|
61
63
|
- Synthesize: generates a final structured report
|
|
62
64
|
|
|
63
65
|
Primary models:
|
|
64
66
|
|
|
65
67
|
- `SearchAnswer` — one per sub‑question (query, answer, context, sources)
|
|
68
|
+
- `InsightRecord` / `GapRecord` — structured tracking of findings and open issues
|
|
69
|
+
- `InsightAnalysis` — output of the analysis stage (insights, gaps, commentary)
|
|
66
70
|
- `EvaluationResult` — insights, new questions, sufficiency, confidence
|
|
67
71
|
- `ResearchReport` — final report (title, executive summary, findings, conclusions, …)
|
|
68
72
|
|
|
@@ -76,30 +80,75 @@ haiku-rag research "How does haiku.rag organize and query documents?" \
|
|
|
76
80
|
--verbose
|
|
77
81
|
```
|
|
78
82
|
|
|
79
|
-
Python usage:
|
|
83
|
+
Python usage (blocking result):
|
|
80
84
|
|
|
81
85
|
```python
|
|
82
86
|
from haiku.rag.client import HaikuRAG
|
|
83
87
|
from haiku.rag.research import (
|
|
88
|
+
PlanNode,
|
|
84
89
|
ResearchContext,
|
|
85
90
|
ResearchDeps,
|
|
86
91
|
ResearchState,
|
|
87
92
|
build_research_graph,
|
|
88
|
-
PlanNode,
|
|
89
93
|
)
|
|
90
94
|
|
|
91
95
|
async with HaikuRAG(path_to_db) as client:
|
|
92
96
|
graph = build_research_graph()
|
|
97
|
+
question = "What are the main drivers and trends of global temperature anomalies since 1990?"
|
|
93
98
|
state = ResearchState(
|
|
94
|
-
question
|
|
95
|
-
context=ResearchContext(original_question=... ),
|
|
99
|
+
context=ResearchContext(original_question=question),
|
|
96
100
|
max_iterations=2,
|
|
97
101
|
confidence_threshold=0.8,
|
|
98
|
-
max_concurrency=
|
|
102
|
+
max_concurrency=2,
|
|
99
103
|
)
|
|
100
104
|
deps = ResearchDeps(client=client)
|
|
101
|
-
|
|
105
|
+
|
|
106
|
+
result = await graph.run(
|
|
107
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
108
|
+
state=state,
|
|
109
|
+
deps=deps,
|
|
110
|
+
)
|
|
111
|
+
|
|
102
112
|
report = result.output
|
|
103
113
|
print(report.title)
|
|
104
114
|
print(report.executive_summary)
|
|
105
115
|
```
|
|
116
|
+
|
|
117
|
+
Python usage (streamed events):
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from haiku.rag.client import HaikuRAG
|
|
121
|
+
from haiku.rag.research import (
|
|
122
|
+
PlanNode,
|
|
123
|
+
ResearchContext,
|
|
124
|
+
ResearchDeps,
|
|
125
|
+
ResearchState,
|
|
126
|
+
build_research_graph,
|
|
127
|
+
stream_research_graph,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
async with HaikuRAG(path_to_db) as client:
|
|
131
|
+
graph = build_research_graph()
|
|
132
|
+
question = "What are the main drivers and trends of global temperature anomalies since 1990?"
|
|
133
|
+
state = ResearchState(
|
|
134
|
+
context=ResearchContext(original_question=question),
|
|
135
|
+
max_iterations=2,
|
|
136
|
+
confidence_threshold=0.8,
|
|
137
|
+
max_concurrency=2,
|
|
138
|
+
)
|
|
139
|
+
deps = ResearchDeps(client=client)
|
|
140
|
+
|
|
141
|
+
async for event in stream_research_graph(
|
|
142
|
+
graph,
|
|
143
|
+
PlanNode(provider="openai", model="gpt-4o-mini"),
|
|
144
|
+
state,
|
|
145
|
+
deps,
|
|
146
|
+
):
|
|
147
|
+
if event.type == "log":
|
|
148
|
+
iteration = event.state.iterations if event.state else state.iterations
|
|
149
|
+
print(f"[{iteration}] {event.message}")
|
|
150
|
+
elif event.type == "report":
|
|
151
|
+
print("\nResearch complete!\n")
|
|
152
|
+
print(event.report.title)
|
|
153
|
+
print(event.report.executive_summary)
|
|
154
|
+
```
|
|
@@ -27,6 +27,9 @@ haiku-rag list
|
|
|
27
27
|
From text:
|
|
28
28
|
```bash
|
|
29
29
|
haiku-rag add "Your document content here"
|
|
30
|
+
|
|
31
|
+
# Attach metadata (repeat --meta for multiple entries)
|
|
32
|
+
haiku-rag add "Your document content here" --meta author=alice --meta topic=notes
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
From file or URL:
|
|
@@ -36,6 +39,10 @@ haiku-rag add-src https://example.com/article.html
|
|
|
36
39
|
|
|
37
40
|
# Optionally set a human‑readable title stored in the DB schema
|
|
38
41
|
haiku-rag add-src /mnt/data/doc1.pdf --title "Q3 Financial Report"
|
|
42
|
+
|
|
43
|
+
# Optionally attach metadata (repeat --meta). Values use JSON parsing if possible:
|
|
44
|
+
# numbers, booleans, null, arrays/objects; otherwise kept as strings.
|
|
45
|
+
haiku-rag add-src /mnt/data/doc1.pdf --meta source=manual --meta page_count=12 --meta published=true
|
|
39
46
|
```
|
|
40
47
|
|
|
41
48
|
!!! note
|
|
@@ -106,6 +113,8 @@ Flags:
|
|
|
106
113
|
- `--max-concurrency`: number of sub-questions searched in parallel each iteration (default: 3)
|
|
107
114
|
- `--verbose`: show planning, searching previews, evaluation summary, and stop reason
|
|
108
115
|
|
|
116
|
+
When `--verbose` is set the CLI also consumes the internal research stream, printing every `log` event as agents progress through planning, search, evaluation, and synthesis. If you build your own integration, call `stream_research_graph` to access the same `log`, `report`, and `error` events and render them however you like while the graph is running.
|
|
117
|
+
|
|
109
118
|
## Server
|
|
110
119
|
|
|
111
120
|
Start the MCP server:
|
|
@@ -126,6 +135,26 @@ haiku-rag settings
|
|
|
126
135
|
|
|
127
136
|
## Maintenance
|
|
128
137
|
|
|
138
|
+
### Info (Read-only)
|
|
139
|
+
|
|
140
|
+
Display database metadata without upgrading or modifying it:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
haiku-rag info [--db /path/to/your.lancedb]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Shows:
|
|
147
|
+
- path to the database
|
|
148
|
+
- stored haiku.rag version (from settings)
|
|
149
|
+
- embeddings provider/model and vector dimension
|
|
150
|
+
- number of documents
|
|
151
|
+
- table versions per table (documents, chunks)
|
|
152
|
+
|
|
153
|
+
At the end, a separate “Versions” section lists runtime package versions:
|
|
154
|
+
- haiku.rag
|
|
155
|
+
- lancedb
|
|
156
|
+
- docling
|
|
157
|
+
|
|
129
158
|
### Vacuum (Optimize and Cleanup)
|
|
130
159
|
|
|
131
160
|
Reduce disk usage by optimizing and pruning old table versions across all tables:
|
|
@@ -143,6 +172,18 @@ when want to switch embeddings provider or model:
|
|
|
143
172
|
haiku-rag rebuild
|
|
144
173
|
```
|
|
145
174
|
|
|
175
|
+
### Download Models
|
|
176
|
+
|
|
177
|
+
Download required runtime models:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
haiku-rag download-models
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This command:
|
|
184
|
+
- Downloads Docling OCR/conversion models (no-op if already present).
|
|
185
|
+
- Pulls Ollama models referenced in your configuration (embeddings, QA, research, rerank).
|
|
186
|
+
|
|
146
187
|
## Migration
|
|
147
188
|
|
|
148
189
|
### Migrate from SQLite to LanceDB
|
|
@@ -43,7 +43,8 @@ async with HaikuRAG("database.lancedb") as client:
|
|
|
43
43
|
Or use the CLI:
|
|
44
44
|
```bash
|
|
45
45
|
haiku-rag add "Your document content"
|
|
46
|
-
haiku-rag add
|
|
46
|
+
haiku-rag add "Your document content" --meta author=alice
|
|
47
|
+
haiku-rag add-src /path/to/document.pdf --title "Q3 Financial Report" --meta source=manual
|
|
47
48
|
haiku-rag search "query"
|
|
48
49
|
haiku-rag ask "Who is the author of haiku.rag?"
|
|
49
50
|
haiku-rag migrate old_database.sqlite # Migrate from SQLite
|
|
@@ -72,3 +72,13 @@ VLLM_RERANK_BASE_URL="http://localhost:8001"
|
|
|
72
72
|
- Python 3.10+
|
|
73
73
|
- Ollama (for default embeddings)
|
|
74
74
|
- vLLM server (for vLLM provider)
|
|
75
|
+
|
|
76
|
+
## Pre-download Models (Optional)
|
|
77
|
+
|
|
78
|
+
You can prefetch all required runtime models before first use:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
haiku-rag download-models
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This will download Docling models and pull any Ollama models referenced by your current configuration.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
name = "haiku.rag"
|
|
4
4
|
description = "Agentic Retrieval Augmented Generation (RAG) with LanceDB"
|
|
5
|
-
version = "0.
|
|
5
|
+
version = "0.11.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" }
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from importlib.metadata import version as pkg_version
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
|
|
4
6
|
from rich.console import Console
|
|
@@ -16,6 +18,7 @@ from haiku.rag.research.graph import (
|
|
|
16
18
|
ResearchState,
|
|
17
19
|
build_research_graph,
|
|
18
20
|
)
|
|
21
|
+
from haiku.rag.research.stream import stream_research_graph
|
|
19
22
|
from haiku.rag.store.models.chunk import Chunk
|
|
20
23
|
from haiku.rag.store.models.document import Document
|
|
21
24
|
|
|
@@ -25,26 +28,141 @@ class HaikuRAGApp:
|
|
|
25
28
|
self.db_path = db_path
|
|
26
29
|
self.console = Console()
|
|
27
30
|
|
|
31
|
+
async def info(self):
|
|
32
|
+
"""Display read-only information about the database without modifying it."""
|
|
33
|
+
|
|
34
|
+
import lancedb
|
|
35
|
+
|
|
36
|
+
# Basic: show path
|
|
37
|
+
self.console.print("[bold]haiku.rag database info[/bold]")
|
|
38
|
+
self.console.print(
|
|
39
|
+
f" [repr.attrib_name]path[/repr.attrib_name]: {self.db_path}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if not self.db_path.exists():
|
|
43
|
+
self.console.print("[red]Database path does not exist.[/red]")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Connect without going through Store to avoid upgrades/validation writes
|
|
47
|
+
try:
|
|
48
|
+
db = lancedb.connect(self.db_path)
|
|
49
|
+
table_names = set(db.table_names())
|
|
50
|
+
except Exception as e:
|
|
51
|
+
self.console.print(f"[red]Failed to open database: {e}[/red]")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
ldb_version = pkg_version("lancedb")
|
|
56
|
+
except Exception:
|
|
57
|
+
ldb_version = "unknown"
|
|
58
|
+
try:
|
|
59
|
+
hr_version = pkg_version("haiku.rag")
|
|
60
|
+
except Exception:
|
|
61
|
+
hr_version = "unknown"
|
|
62
|
+
try:
|
|
63
|
+
docling_version = pkg_version("docling")
|
|
64
|
+
except Exception:
|
|
65
|
+
docling_version = "unknown"
|
|
66
|
+
|
|
67
|
+
# Read settings (if present) to find stored haiku.rag version and embedding config
|
|
68
|
+
stored_version = "unknown"
|
|
69
|
+
embed_provider: str | None = None
|
|
70
|
+
embed_model: str | None = None
|
|
71
|
+
vector_dim: int | None = None
|
|
72
|
+
|
|
73
|
+
if "settings" in table_names:
|
|
74
|
+
settings_tbl = db.open_table("settings")
|
|
75
|
+
arrow = settings_tbl.search().where("id = 'settings'").limit(1).to_arrow()
|
|
76
|
+
rows = arrow.to_pylist() if arrow is not None else []
|
|
77
|
+
if rows:
|
|
78
|
+
raw = rows[0].get("settings") or "{}"
|
|
79
|
+
data = json.loads(raw) if isinstance(raw, str) else (raw or {})
|
|
80
|
+
stored_version = str(data.get("version", stored_version))
|
|
81
|
+
embed_provider = data.get("EMBEDDINGS_PROVIDER")
|
|
82
|
+
embed_model = data.get("EMBEDDINGS_MODEL")
|
|
83
|
+
vector_dim = (
|
|
84
|
+
int(data.get("EMBEDDINGS_VECTOR_DIM")) # pyright: ignore[reportArgumentType]
|
|
85
|
+
if data.get("EMBEDDINGS_VECTOR_DIM") is not None
|
|
86
|
+
else None
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
num_docs = 0
|
|
90
|
+
if "documents" in table_names:
|
|
91
|
+
docs_tbl = db.open_table("documents")
|
|
92
|
+
num_docs = int(docs_tbl.count_rows()) # type: ignore[attr-defined]
|
|
93
|
+
|
|
94
|
+
# Table versions per table (direct API)
|
|
95
|
+
doc_versions = (
|
|
96
|
+
len(list(db.open_table("documents").list_versions()))
|
|
97
|
+
if "documents" in table_names
|
|
98
|
+
else 0
|
|
99
|
+
)
|
|
100
|
+
chunk_versions = (
|
|
101
|
+
len(list(db.open_table("chunks").list_versions()))
|
|
102
|
+
if "chunks" in table_names
|
|
103
|
+
else 0
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
self.console.print(
|
|
107
|
+
f" [repr.attrib_name]haiku.rag version (db)[/repr.attrib_name]: {stored_version}"
|
|
108
|
+
)
|
|
109
|
+
if embed_provider or embed_model or vector_dim:
|
|
110
|
+
provider_part = embed_provider or "unknown"
|
|
111
|
+
model_part = embed_model or "unknown"
|
|
112
|
+
dim_part = f"{vector_dim}" if vector_dim is not None else "unknown"
|
|
113
|
+
self.console.print(
|
|
114
|
+
" [repr.attrib_name]embeddings[/repr.attrib_name]: "
|
|
115
|
+
f"{provider_part}/{model_part} (dim: {dim_part})"
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
self.console.print(
|
|
119
|
+
" [repr.attrib_name]embeddings[/repr.attrib_name]: unknown"
|
|
120
|
+
)
|
|
121
|
+
self.console.print(
|
|
122
|
+
f" [repr.attrib_name]documents[/repr.attrib_name]: {num_docs}"
|
|
123
|
+
)
|
|
124
|
+
self.console.print(
|
|
125
|
+
f" [repr.attrib_name]versions (documents)[/repr.attrib_name]: {doc_versions}"
|
|
126
|
+
)
|
|
127
|
+
self.console.print(
|
|
128
|
+
f" [repr.attrib_name]versions (chunks)[/repr.attrib_name]: {chunk_versions}"
|
|
129
|
+
)
|
|
130
|
+
self.console.rule()
|
|
131
|
+
self.console.print("[bold]Versions[/bold]")
|
|
132
|
+
self.console.print(
|
|
133
|
+
f" [repr.attrib_name]haiku.rag[/repr.attrib_name]: {hr_version}"
|
|
134
|
+
)
|
|
135
|
+
self.console.print(
|
|
136
|
+
f" [repr.attrib_name]lancedb[/repr.attrib_name]: {ldb_version}"
|
|
137
|
+
)
|
|
138
|
+
self.console.print(
|
|
139
|
+
f" [repr.attrib_name]docling[/repr.attrib_name]: {docling_version}"
|
|
140
|
+
)
|
|
141
|
+
|
|
28
142
|
async def list_documents(self):
|
|
29
143
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
|
30
144
|
documents = await self.client.list_documents()
|
|
31
145
|
for doc in documents:
|
|
32
146
|
self._rich_print_document(doc, truncate=True)
|
|
33
147
|
|
|
34
|
-
async def add_document_from_text(self, text: str):
|
|
148
|
+
async def add_document_from_text(self, text: str, metadata: dict | None = None):
|
|
35
149
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
|
36
|
-
doc = await self.client.create_document(text)
|
|
150
|
+
doc = await self.client.create_document(text, metadata=metadata)
|
|
37
151
|
self._rich_print_document(doc, truncate=True)
|
|
38
152
|
self.console.print(
|
|
39
|
-
f"[
|
|
153
|
+
f"[bold green]Document {doc.id} added successfully.[/bold green]"
|
|
40
154
|
)
|
|
41
155
|
|
|
42
|
-
async def add_document_from_source(
|
|
156
|
+
async def add_document_from_source(
|
|
157
|
+
self, source: str, title: str | None = None, metadata: dict | None = None
|
|
158
|
+
):
|
|
43
159
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
|
44
|
-
doc = await self.client.create_document_from_source(
|
|
160
|
+
doc = await self.client.create_document_from_source(
|
|
161
|
+
source, title=title, metadata=metadata
|
|
162
|
+
)
|
|
45
163
|
self._rich_print_document(doc, truncate=True)
|
|
46
164
|
self.console.print(
|
|
47
|
-
f"[
|
|
165
|
+
f"[bold green]Document {doc.id} added successfully.[/bold green]"
|
|
48
166
|
)
|
|
49
167
|
|
|
50
168
|
async def get_document(self, doc_id: str):
|
|
@@ -59,7 +177,9 @@ class HaikuRAGApp:
|
|
|
59
177
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
|
60
178
|
deleted = await self.client.delete_document(doc_id)
|
|
61
179
|
if deleted:
|
|
62
|
-
self.console.print(
|
|
180
|
+
self.console.print(
|
|
181
|
+
f"[bold green]Document {doc_id} deleted successfully.[/bold green]"
|
|
182
|
+
)
|
|
63
183
|
else:
|
|
64
184
|
self.console.print(
|
|
65
185
|
f"[yellow]Document with id {doc_id} not found.[/yellow]"
|
|
@@ -69,7 +189,7 @@ class HaikuRAGApp:
|
|
|
69
189
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
|
70
190
|
results = await self.client.search(query, limit=limit)
|
|
71
191
|
if not results:
|
|
72
|
-
self.console.print("[
|
|
192
|
+
self.console.print("[yellow]No results found.[/yellow]")
|
|
73
193
|
return
|
|
74
194
|
for chunk, score in results:
|
|
75
195
|
self._rich_print_search_result(chunk, score)
|
|
@@ -102,9 +222,9 @@ class HaikuRAGApp:
|
|
|
102
222
|
self.console.print()
|
|
103
223
|
|
|
104
224
|
graph = build_research_graph()
|
|
225
|
+
context = ResearchContext(original_question=question)
|
|
105
226
|
state = ResearchState(
|
|
106
|
-
|
|
107
|
-
context=ResearchContext(original_question=question),
|
|
227
|
+
context=context,
|
|
108
228
|
max_iterations=max_iterations,
|
|
109
229
|
confidence_threshold=confidence_threshold,
|
|
110
230
|
max_concurrency=max_concurrency,
|
|
@@ -117,22 +237,20 @@ class HaikuRAGApp:
|
|
|
117
237
|
provider=Config.RESEARCH_PROVIDER or Config.QA_PROVIDER,
|
|
118
238
|
model=Config.RESEARCH_MODEL or Config.QA_MODEL,
|
|
119
239
|
)
|
|
120
|
-
# Prefer graph.run; fall back to iter if unavailable
|
|
121
240
|
report = None
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if run.result:
|
|
133
|
-
report = run.result.output
|
|
241
|
+
async for event in stream_research_graph(graph, start, state, deps):
|
|
242
|
+
if event.type == "report":
|
|
243
|
+
report = event.report
|
|
244
|
+
break
|
|
245
|
+
if event.type == "error":
|
|
246
|
+
self.console.print(
|
|
247
|
+
f"[red]Error during research: {event.message}[/red]"
|
|
248
|
+
)
|
|
249
|
+
return
|
|
250
|
+
|
|
134
251
|
if report is None:
|
|
135
|
-
|
|
252
|
+
self.console.print("[red]Research did not produce a report.[/red]")
|
|
253
|
+
return
|
|
136
254
|
|
|
137
255
|
# Display the report
|
|
138
256
|
self.console.print("[bold green]Research Report[/bold green]")
|
|
@@ -202,14 +320,16 @@ class HaikuRAGApp:
|
|
|
202
320
|
return
|
|
203
321
|
|
|
204
322
|
self.console.print(
|
|
205
|
-
f"[
|
|
323
|
+
f"[bold cyan]Rebuilding database with {total_docs} documents...[/bold cyan]"
|
|
206
324
|
)
|
|
207
325
|
with Progress() as progress:
|
|
208
326
|
task = progress.add_task("Rebuilding...", total=total_docs)
|
|
209
327
|
async for _ in client.rebuild_database():
|
|
210
328
|
progress.update(task, advance=1)
|
|
211
329
|
|
|
212
|
-
self.console.print(
|
|
330
|
+
self.console.print(
|
|
331
|
+
"[bold green]Database rebuild completed successfully.[/bold green]"
|
|
332
|
+
)
|
|
213
333
|
except Exception as e:
|
|
214
334
|
self.console.print(f"[red]Error rebuilding database: {e}[/red]")
|
|
215
335
|
|
|
@@ -218,7 +338,9 @@ class HaikuRAGApp:
|
|
|
218
338
|
try:
|
|
219
339
|
async with HaikuRAG(db_path=self.db_path, skip_validation=True) as client:
|
|
220
340
|
await client.vacuum()
|
|
221
|
-
self.console.print(
|
|
341
|
+
self.console.print(
|
|
342
|
+
"[bold green]Vacuum completed successfully.[/bold green]"
|
|
343
|
+
)
|
|
222
344
|
except Exception as e:
|
|
223
345
|
self.console.print(f"[red]Error during vacuum: {e}[/red]")
|
|
224
346
|
|
|
@@ -240,7 +362,9 @@ class HaikuRAGApp:
|
|
|
240
362
|
else:
|
|
241
363
|
display_value = field_value
|
|
242
364
|
|
|
243
|
-
self.console.print(
|
|
365
|
+
self.console.print(
|
|
366
|
+
f" [repr.attrib_name]{field_name}[/repr.attrib_name]: {display_value}"
|
|
367
|
+
)
|
|
244
368
|
|
|
245
369
|
def _rich_print_document(self, doc: Document, truncate: bool = False):
|
|
246
370
|
"""Format a document for display."""
|