haiku.rag 0.9.3__py3-none-any.whl → 0.10.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/app.py +64 -18
- haiku/rag/cli.py +67 -30
- haiku/rag/client.py +63 -21
- haiku/rag/config.py +4 -0
- haiku/rag/mcp.py +18 -6
- haiku/rag/qa/agent.py +4 -2
- haiku/rag/qa/prompts.py +2 -2
- haiku/rag/reranking/mxbai.py +1 -1
- haiku/rag/research/__init__.py +10 -27
- haiku/rag/research/common.py +53 -0
- haiku/rag/research/dependencies.py +3 -25
- haiku/rag/research/graph.py +29 -0
- haiku/rag/research/models.py +70 -0
- haiku/rag/research/nodes/evaluate.py +80 -0
- haiku/rag/research/nodes/plan.py +63 -0
- haiku/rag/research/nodes/search.py +93 -0
- haiku/rag/research/nodes/synthesize.py +51 -0
- haiku/rag/research/prompts.py +98 -113
- haiku/rag/research/state.py +25 -0
- haiku/rag/store/engine.py +14 -0
- haiku/rag/store/models/chunk.py +1 -0
- haiku/rag/store/models/document.py +1 -0
- haiku/rag/store/repositories/chunk.py +4 -0
- haiku/rag/store/repositories/document.py +3 -0
- haiku/rag/store/upgrades/__init__.py +2 -0
- haiku/rag/store/upgrades/v0_10_1.py +64 -0
- haiku/rag/utils.py +8 -5
- {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/METADATA +37 -1
- haiku_rag-0.10.1.dist-info/RECORD +54 -0
- haiku/rag/research/base.py +0 -130
- haiku/rag/research/evaluation_agent.py +0 -85
- haiku/rag/research/orchestrator.py +0 -170
- haiku/rag/research/presearch_agent.py +0 -39
- haiku/rag/research/search_agent.py +0 -69
- haiku/rag/research/synthesis_agent.py +0 -60
- haiku_rag-0.9.3.dist-info/RECORD +0 -51
- {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/WHEEL +0 -0
- {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/entry_points.txt +0 -0
- {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/licenses/LICENSE +0 -0
haiku/rag/research/prompts.py
CHANGED
|
@@ -1,129 +1,114 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
- Each
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
SEARCH_AGENT_PROMPT = """You are a search and question
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
1. Search the knowledge base for relevant
|
|
24
|
-
2. Analyze
|
|
25
|
-
3. Provide an
|
|
26
|
-
|
|
27
|
-
Output format:
|
|
28
|
-
- You must return a SearchAnswer model with fields:
|
|
29
|
-
- query: the question being answered (echo the user query)
|
|
30
|
-
- answer: your final answer based only on the provided context
|
|
31
|
-
- context: list[str] of only the minimal set of verbatim snippet texts you
|
|
32
|
-
used to justify the answer (do not include unrelated text; do not invent)
|
|
33
|
-
- sources: list[str] of document_uri values corresponding to the snippets you
|
|
34
|
-
actually used in the answer (one URI per context snippet, order aligned)
|
|
1
|
+
PLAN_PROMPT = """You are the research orchestrator for a focused, iterative
|
|
2
|
+
workflow.
|
|
3
|
+
|
|
4
|
+
Responsibilities:
|
|
5
|
+
1. Understand and decompose the main question
|
|
6
|
+
2. Propose a minimal, high‑leverage plan
|
|
7
|
+
3. Coordinate specialized agents to gather evidence
|
|
8
|
+
4. Iterate based on gaps and new findings
|
|
9
|
+
|
|
10
|
+
Plan requirements:
|
|
11
|
+
- Produce at most 3 sub_questions that together cover the main question.
|
|
12
|
+
- Each sub_question must be a standalone, self‑contained query that can run
|
|
13
|
+
without extra context. Include concrete entities, scope, timeframe, and any
|
|
14
|
+
qualifiers. Avoid ambiguous pronouns (it/they/this/that).
|
|
15
|
+
- Prioritize the highest‑value aspects first; avoid redundancy and overlap.
|
|
16
|
+
- Prefer questions that are likely answerable from the current knowledge base;
|
|
17
|
+
if coverage is uncertain, make scopes narrower and specific.
|
|
18
|
+
- Order sub_questions by execution priority (most valuable first)."""
|
|
19
|
+
|
|
20
|
+
SEARCH_AGENT_PROMPT = """You are a search and question‑answering specialist.
|
|
21
|
+
|
|
22
|
+
Tasks:
|
|
23
|
+
1. Search the knowledge base for relevant evidence.
|
|
24
|
+
2. Analyze retrieved snippets.
|
|
25
|
+
3. Provide an answer strictly grounded in that evidence.
|
|
35
26
|
|
|
36
27
|
Tool usage:
|
|
37
|
-
- Always call
|
|
38
|
-
- The tool returns
|
|
39
|
-
|
|
40
|
-
`document_uri` it came from.
|
|
28
|
+
- Always call search_and_answer before drafting any answer.
|
|
29
|
+
- The tool returns snippets with verbatim `text`, a relevance `score`, and the
|
|
30
|
+
originating document identifier (document title if available, otherwise URI).
|
|
41
31
|
- You may call the tool multiple times to refine or broaden context, but do not
|
|
42
|
-
exceed 3 total
|
|
32
|
+
exceed 3 total calls. Favor precision over volume.
|
|
43
33
|
- Use scores to prioritize evidence, but include only the minimal subset of
|
|
44
|
-
snippet texts (verbatim) in SearchAnswer.context.
|
|
45
|
-
- Set SearchAnswer.sources to the
|
|
46
|
-
used (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
34
|
+
snippet texts (verbatim) in SearchAnswer.context (typically 1‑4).
|
|
35
|
+
- Set SearchAnswer.sources to the corresponding document identifiers for the
|
|
36
|
+
snippets you used (title if available, otherwise URI; one per snippet; same
|
|
37
|
+
order as context). Context must be text‑only.
|
|
38
|
+
- If no relevant information is found, clearly say so and return an empty
|
|
39
|
+
context list and sources list.
|
|
40
|
+
|
|
41
|
+
Answering rules:
|
|
42
|
+
- Be direct and specific; avoid meta commentary about the process.
|
|
43
|
+
- Do not include any claims not supported by the provided snippets.
|
|
44
|
+
- Prefer concise phrasing; avoid copying long passages.
|
|
45
|
+
- When evidence is partial, state the limits explicitly in the answer."""
|
|
46
|
+
|
|
47
|
+
EVALUATION_AGENT_PROMPT = """You are an analysis and evaluation specialist for
|
|
48
|
+
the research workflow.
|
|
49
|
+
|
|
50
|
+
Inputs available:
|
|
51
|
+
- Original research question
|
|
52
|
+
- Question–answer pairs produced by search
|
|
53
|
+
- Raw search results and source metadata
|
|
59
54
|
- Previously identified insights
|
|
60
55
|
|
|
61
|
-
Your dual role is to:
|
|
62
|
-
|
|
63
56
|
ANALYSIS:
|
|
64
|
-
1. Extract
|
|
65
|
-
2. Identify patterns and
|
|
66
|
-
3.
|
|
67
|
-
4. Focus on the most important discoveries
|
|
57
|
+
1. Extract the most important, non‑obvious insights from the collected evidence.
|
|
58
|
+
2. Identify patterns, agreements, and disagreements across sources.
|
|
59
|
+
3. Note material uncertainties and assumptions.
|
|
68
60
|
|
|
69
61
|
EVALUATION:
|
|
70
|
-
1.
|
|
71
|
-
2.
|
|
72
|
-
- Coverage of the main question
|
|
73
|
-
- Quality and
|
|
74
|
-
- Depth of
|
|
75
|
-
3.
|
|
76
|
-
4.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
Focus on creating a report that provides clear value to the reader by:
|
|
113
|
-
- Answering the original research question thoroughly
|
|
114
|
-
- Highlighting the most important findings
|
|
115
|
-
- Explaining the implications of the research
|
|
116
|
-
- Suggesting concrete next steps"""
|
|
62
|
+
1. Decide if we have sufficient information to answer the original question.
|
|
63
|
+
2. Provide a confidence_score in [0,1] considering:
|
|
64
|
+
- Coverage of the main question’s aspects
|
|
65
|
+
- Quality, consistency, and diversity of sources
|
|
66
|
+
- Depth and specificity of evidence
|
|
67
|
+
3. List concrete gaps that still need investigation.
|
|
68
|
+
4. Propose up to 3 new sub_questions that would close the highest‑value gaps.
|
|
69
|
+
|
|
70
|
+
Strictness:
|
|
71
|
+
- Only mark research as sufficient when all major aspects are addressed with
|
|
72
|
+
consistent, reliable evidence and no critical gaps remain.
|
|
73
|
+
|
|
74
|
+
New sub_questions must:
|
|
75
|
+
- Be genuinely new (not answered or duplicative; check qa_responses).
|
|
76
|
+
- Be standalone and specific (entities, scope, timeframe/region if relevant).
|
|
77
|
+
- Be actionable and scoped to the knowledge base (narrow if necessary).
|
|
78
|
+
- Be ordered by expected impact (most valuable first)."""
|
|
79
|
+
|
|
80
|
+
SYNTHESIS_AGENT_PROMPT = """You are a synthesis specialist producing the final
|
|
81
|
+
research report.
|
|
82
|
+
|
|
83
|
+
Goals:
|
|
84
|
+
1. Synthesize all gathered information into a coherent narrative.
|
|
85
|
+
2. Present findings clearly and concisely.
|
|
86
|
+
3. Draw evidence‑based conclusions and recommendations.
|
|
87
|
+
4. State limitations and uncertainties transparently.
|
|
88
|
+
|
|
89
|
+
Report guidelines (map to output fields):
|
|
90
|
+
- title: concise (5–12 words), informative.
|
|
91
|
+
- executive_summary: 3–5 sentences summarizing the overall answer.
|
|
92
|
+
- main_findings: 4–8 one‑sentence bullets; each reflects evidence from the
|
|
93
|
+
research (do not include inline citations or snippet text).
|
|
94
|
+
- conclusions: 2–4 bullets that follow logically from findings.
|
|
95
|
+
- recommendations: 2–5 actionable bullets tied to findings.
|
|
96
|
+
- limitations: 1–3 bullets describing key constraints or uncertainties.
|
|
97
|
+
- sources_summary: 2–4 sentences summarizing sources used and their reliability.
|
|
98
|
+
|
|
99
|
+
Style:
|
|
100
|
+
- Base all content solely on the collected evidence.
|
|
101
|
+
- Be professional, objective, and specific.
|
|
102
|
+
- Avoid meta commentary and refrain from speculation beyond the evidence."""
|
|
117
103
|
|
|
118
104
|
PRESEARCH_AGENT_PROMPT = """You are a rapid research surveyor.
|
|
119
105
|
|
|
120
106
|
Task:
|
|
121
|
-
- Call
|
|
122
|
-
|
|
123
|
-
- Read that context and produce a
|
|
124
|
-
|
|
107
|
+
- Call gather_context once on the main question to obtain relevant text from
|
|
108
|
+
the knowledge base (KB).
|
|
109
|
+
- Read that context and produce a short natural‑language summary of what the
|
|
110
|
+
KB appears to contain relative to the question.
|
|
125
111
|
|
|
126
112
|
Rules:
|
|
127
113
|
- Base the summary strictly on the provided text; do not invent.
|
|
128
|
-
- Output only the summary as plain text (one short paragraph).
|
|
129
|
-
"""
|
|
114
|
+
- Output only the summary as plain text (one short paragraph)."""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
5
|
+
from haiku.rag.client import HaikuRAG
|
|
6
|
+
from haiku.rag.research.dependencies import ResearchContext
|
|
7
|
+
from haiku.rag.research.models import EvaluationResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class ResearchDeps:
|
|
12
|
+
client: HaikuRAG
|
|
13
|
+
console: Console | None = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ResearchState:
|
|
18
|
+
question: str
|
|
19
|
+
context: ResearchContext
|
|
20
|
+
sub_questions: list[str] = field(default_factory=list)
|
|
21
|
+
iterations: int = 0
|
|
22
|
+
max_iterations: int = 3
|
|
23
|
+
max_concurrency: int = 1
|
|
24
|
+
confidence_threshold: float = 0.8
|
|
25
|
+
last_eval: EvaluationResult | None = None
|
haiku/rag/store/engine.py
CHANGED
|
@@ -19,6 +19,7 @@ class DocumentRecord(LanceModel):
|
|
|
19
19
|
id: str = Field(default_factory=lambda: str(uuid4()))
|
|
20
20
|
content: str
|
|
21
21
|
uri: str | None = None
|
|
22
|
+
title: str | None = None
|
|
22
23
|
metadata: str = Field(default="{}")
|
|
23
24
|
created_at: str = Field(default_factory=lambda: "")
|
|
24
25
|
updated_at: str = Field(default_factory=lambda: "")
|
|
@@ -54,6 +55,19 @@ class Store:
|
|
|
54
55
|
# Create the ChunkRecord model with the correct vector dimension
|
|
55
56
|
self.ChunkRecord = create_chunk_model(self.embedder._vector_dim)
|
|
56
57
|
|
|
58
|
+
# Local filesystem handling for DB directory
|
|
59
|
+
if not self._has_cloud_config():
|
|
60
|
+
if Config.DISABLE_DB_AUTOCREATE:
|
|
61
|
+
# LanceDB uses a directory path for local databases; enforce presence
|
|
62
|
+
if not db_path.exists():
|
|
63
|
+
raise FileNotFoundError(
|
|
64
|
+
f"LanceDB path does not exist: {db_path}. Auto-creation is disabled."
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
# Ensure parent directories exist when autocreation allowed
|
|
68
|
+
if not db_path.parent.exists():
|
|
69
|
+
Path.mkdir(db_path.parent, parents=True)
|
|
70
|
+
|
|
57
71
|
# Connect to LanceDB
|
|
58
72
|
self.db = self._connect_to_lancedb(db_path)
|
|
59
73
|
|
haiku/rag/store/models/chunk.py
CHANGED
|
@@ -11,6 +11,7 @@ class Document(BaseModel):
|
|
|
11
11
|
id: str | None = None
|
|
12
12
|
content: str
|
|
13
13
|
uri: str | None = None
|
|
14
|
+
title: str | None = None
|
|
14
15
|
metadata: dict = {}
|
|
15
16
|
created_at: datetime = Field(default_factory=datetime.now)
|
|
16
17
|
updated_at: datetime = Field(default_factory=datetime.now)
|
|
@@ -317,6 +317,7 @@ class ChunkRepository:
|
|
|
317
317
|
)
|
|
318
318
|
|
|
319
319
|
doc_uri = doc_results[0].uri if doc_results else None
|
|
320
|
+
doc_title = doc_results[0].title if doc_results else None
|
|
320
321
|
doc_meta = doc_results[0].metadata if doc_results else "{}"
|
|
321
322
|
|
|
322
323
|
chunks: list[Chunk] = []
|
|
@@ -330,6 +331,7 @@ class ChunkRepository:
|
|
|
330
331
|
metadata=md,
|
|
331
332
|
order=rec.order,
|
|
332
333
|
document_uri=doc_uri,
|
|
334
|
+
document_title=doc_title,
|
|
333
335
|
document_meta=json.loads(doc_meta),
|
|
334
336
|
)
|
|
335
337
|
)
|
|
@@ -398,6 +400,7 @@ class ChunkRepository:
|
|
|
398
400
|
# Get document info from pre-fetched map
|
|
399
401
|
doc = documents_map.get(chunk_record.document_id)
|
|
400
402
|
doc_uri = doc.uri if doc else None
|
|
403
|
+
doc_title = doc.title if doc else None
|
|
401
404
|
doc_meta = doc.metadata if doc else "{}"
|
|
402
405
|
|
|
403
406
|
md = json.loads(chunk_record.metadata)
|
|
@@ -409,6 +412,7 @@ class ChunkRepository:
|
|
|
409
412
|
metadata=md,
|
|
410
413
|
order=chunk_record.order,
|
|
411
414
|
document_uri=doc_uri,
|
|
415
|
+
document_title=doc_title,
|
|
412
416
|
document_meta=json.loads(doc_meta),
|
|
413
417
|
)
|
|
414
418
|
|
|
@@ -34,6 +34,7 @@ class DocumentRepository:
|
|
|
34
34
|
id=record.id,
|
|
35
35
|
content=record.content,
|
|
36
36
|
uri=record.uri,
|
|
37
|
+
title=record.title,
|
|
37
38
|
metadata=json.loads(record.metadata),
|
|
38
39
|
created_at=datetime.fromisoformat(record.created_at)
|
|
39
40
|
if record.created_at
|
|
@@ -56,6 +57,7 @@ class DocumentRepository:
|
|
|
56
57
|
id=doc_id,
|
|
57
58
|
content=entity.content,
|
|
58
59
|
uri=entity.uri,
|
|
60
|
+
title=entity.title,
|
|
59
61
|
metadata=json.dumps(entity.metadata),
|
|
60
62
|
created_at=now,
|
|
61
63
|
updated_at=now,
|
|
@@ -97,6 +99,7 @@ class DocumentRepository:
|
|
|
97
99
|
values={
|
|
98
100
|
"content": entity.content,
|
|
99
101
|
"uri": entity.uri,
|
|
102
|
+
"title": entity.title,
|
|
100
103
|
"metadata": json.dumps(entity.metadata),
|
|
101
104
|
"updated_at": now,
|
|
102
105
|
},
|
|
@@ -55,6 +55,8 @@ def run_pending_upgrades(store: Store, from_version: str, to_version: str) -> No
|
|
|
55
55
|
|
|
56
56
|
from .v0_9_3 import upgrade_fts_phrase as upgrade_0_9_3_fts # noqa: E402
|
|
57
57
|
from .v0_9_3 import upgrade_order as upgrade_0_9_3_order # noqa: E402
|
|
58
|
+
from .v0_10_1 import upgrade_add_title as upgrade_0_10_1_add_title # noqa: E402
|
|
58
59
|
|
|
59
60
|
upgrades.append(upgrade_0_9_3_order)
|
|
60
61
|
upgrades.append(upgrade_0_9_3_fts)
|
|
62
|
+
upgrades.append(upgrade_0_10_1_add_title)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from lancedb.pydantic import LanceModel
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from haiku.rag.store.engine import Store
|
|
7
|
+
from haiku.rag.store.upgrades import Upgrade
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _apply_add_document_title(store: Store) -> None:
|
|
11
|
+
"""Add a nullable 'title' column to the documents table."""
|
|
12
|
+
|
|
13
|
+
# Read existing rows using Arrow for schema-agnostic access
|
|
14
|
+
try:
|
|
15
|
+
docs_arrow = store.documents_table.search().to_arrow()
|
|
16
|
+
rows = docs_arrow.to_pylist()
|
|
17
|
+
except Exception:
|
|
18
|
+
rows = []
|
|
19
|
+
|
|
20
|
+
class DocumentRecordV2(LanceModel):
|
|
21
|
+
id: str
|
|
22
|
+
content: str
|
|
23
|
+
uri: str | None = None
|
|
24
|
+
title: str | None = None
|
|
25
|
+
metadata: str = Field(default="{}")
|
|
26
|
+
created_at: str = Field(default_factory=lambda: "")
|
|
27
|
+
updated_at: str = Field(default_factory=lambda: "")
|
|
28
|
+
|
|
29
|
+
# Drop and recreate documents table with the new schema
|
|
30
|
+
try:
|
|
31
|
+
store.db.drop_table("documents")
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
store.documents_table = store.db.create_table("documents", schema=DocumentRecordV2)
|
|
36
|
+
|
|
37
|
+
# Reinsert previous rows with title=None
|
|
38
|
+
if rows:
|
|
39
|
+
backfilled = []
|
|
40
|
+
for row in rows:
|
|
41
|
+
backfilled.append(
|
|
42
|
+
DocumentRecordV2(
|
|
43
|
+
id=row.get("id"),
|
|
44
|
+
content=row.get("content", ""),
|
|
45
|
+
uri=row.get("uri"),
|
|
46
|
+
title=None,
|
|
47
|
+
metadata=(
|
|
48
|
+
row.get("metadata")
|
|
49
|
+
if isinstance(row.get("metadata"), str)
|
|
50
|
+
else json.dumps(row.get("metadata") or {})
|
|
51
|
+
),
|
|
52
|
+
created_at=row.get("created_at", ""),
|
|
53
|
+
updated_at=row.get("updated_at", ""),
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
store.documents_table.add(backfilled)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
upgrade_add_title = Upgrade(
|
|
61
|
+
version="0.10.1",
|
|
62
|
+
apply=_apply_add_document_title,
|
|
63
|
+
description="Add nullable 'title' column to documents table",
|
|
64
|
+
)
|
haiku/rag/utils.py
CHANGED
|
@@ -9,10 +9,6 @@ from io import BytesIO
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from types import ModuleType
|
|
11
11
|
|
|
12
|
-
import httpx
|
|
13
|
-
from docling.document_converter import DocumentConverter
|
|
14
|
-
from docling_core.types.doc.document import DoclingDocument
|
|
15
|
-
from docling_core.types.io import DocumentStream
|
|
16
12
|
from packaging.version import Version, parse
|
|
17
13
|
|
|
18
14
|
|
|
@@ -82,6 +78,9 @@ async def is_up_to_date() -> tuple[bool, Version, Version]:
|
|
|
82
78
|
the running version and the latest version.
|
|
83
79
|
"""
|
|
84
80
|
|
|
81
|
+
# Lazy import to avoid pulling httpx (and its deps) on module import
|
|
82
|
+
import httpx
|
|
83
|
+
|
|
85
84
|
async with httpx.AsyncClient() as client:
|
|
86
85
|
running_version = parse(metadata.version("haiku.rag"))
|
|
87
86
|
try:
|
|
@@ -94,7 +93,7 @@ async def is_up_to_date() -> tuple[bool, Version, Version]:
|
|
|
94
93
|
return running_version >= pypi_version, running_version, pypi_version
|
|
95
94
|
|
|
96
95
|
|
|
97
|
-
def text_to_docling_document(text: str, name: str = "content.md")
|
|
96
|
+
def text_to_docling_document(text: str, name: str = "content.md"):
|
|
98
97
|
"""Convert text content to a DoclingDocument.
|
|
99
98
|
|
|
100
99
|
Args:
|
|
@@ -104,6 +103,10 @@ def text_to_docling_document(text: str, name: str = "content.md") -> DoclingDocu
|
|
|
104
103
|
Returns:
|
|
105
104
|
A DoclingDocument created from the text content.
|
|
106
105
|
"""
|
|
106
|
+
# Lazy import docling deps to keep import-time light
|
|
107
|
+
from docling.document_converter import DocumentConverter # type: ignore
|
|
108
|
+
from docling_core.types.io import DocumentStream # type: ignore
|
|
109
|
+
|
|
107
110
|
bytes_io = BytesIO(text.encode("utf-8"))
|
|
108
111
|
doc_stream = DocumentStream(name=name, stream=bytes_io)
|
|
109
112
|
converter = DocumentConverter()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haiku.rag
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Agentic Retrieval Augmented Generation (RAG) with LanceDB
|
|
5
5
|
Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -23,6 +23,7 @@ Requires-Dist: fastmcp>=2.12.3
|
|
|
23
23
|
Requires-Dist: httpx>=0.28.1
|
|
24
24
|
Requires-Dist: lancedb>=0.25.0
|
|
25
25
|
Requires-Dist: pydantic-ai>=1.0.8
|
|
26
|
+
Requires-Dist: pydantic-graph>=1.0.8
|
|
26
27
|
Requires-Dist: pydantic>=2.11.9
|
|
27
28
|
Requires-Dist: python-dotenv>=1.1.1
|
|
28
29
|
Requires-Dist: rich>=14.1.0
|
|
@@ -48,6 +49,7 @@ Retrieval-Augmented Generation (RAG) library built on LanceDB.
|
|
|
48
49
|
- **Local LanceDB**: No external servers required, supports also LanceDB cloud storage, S3, Google Cloud & Azure
|
|
49
50
|
- **Multiple embedding providers**: Ollama, VoyageAI, OpenAI, vLLM
|
|
50
51
|
- **Multiple QA providers**: Any provider/model supported by Pydantic AI
|
|
52
|
+
- **Research graph (multi‑agent)**: Plan → Search → Evaluate → Synthesize with agentic AI
|
|
51
53
|
- **Native hybrid search**: Vector + full-text search with native LanceDB RRF reranking
|
|
52
54
|
- **Reranking**: Default search result reranking with MixedBread AI, Cohere, or vLLM
|
|
53
55
|
- **Question answering**: Built-in QA agents on your documents
|
|
@@ -75,6 +77,14 @@ haiku-rag ask "Who is the author of haiku.rag?"
|
|
|
75
77
|
# Ask questions with citations
|
|
76
78
|
haiku-rag ask "Who is the author of haiku.rag?" --cite
|
|
77
79
|
|
|
80
|
+
# Multi‑agent research (iterative plan/search/evaluate)
|
|
81
|
+
haiku-rag research \
|
|
82
|
+
"What are the main drivers and trends of global temperature anomalies since 1990?" \
|
|
83
|
+
--max-iterations 2 \
|
|
84
|
+
--confidence-threshold 0.8 \
|
|
85
|
+
--max-concurrency 3 \
|
|
86
|
+
--verbose
|
|
87
|
+
|
|
78
88
|
# Rebuild database (re-chunk and re-embed all documents)
|
|
79
89
|
haiku-rag rebuild
|
|
80
90
|
|
|
@@ -90,6 +100,13 @@ haiku-rag serve
|
|
|
90
100
|
|
|
91
101
|
```python
|
|
92
102
|
from haiku.rag.client import HaikuRAG
|
|
103
|
+
from haiku.rag.research import (
|
|
104
|
+
ResearchContext,
|
|
105
|
+
ResearchDeps,
|
|
106
|
+
ResearchState,
|
|
107
|
+
build_research_graph,
|
|
108
|
+
PlanNode,
|
|
109
|
+
)
|
|
93
110
|
|
|
94
111
|
async with HaikuRAG("database.lancedb") as client:
|
|
95
112
|
# Add document
|
|
@@ -107,6 +124,25 @@ async with HaikuRAG("database.lancedb") as client:
|
|
|
107
124
|
# Ask questions with citations
|
|
108
125
|
answer = await client.ask("Who is the author of haiku.rag?", cite=True)
|
|
109
126
|
print(answer)
|
|
127
|
+
|
|
128
|
+
# Multi‑agent research pipeline (Plan → Search → Evaluate → Synthesize)
|
|
129
|
+
graph = build_research_graph()
|
|
130
|
+
state = ResearchState(
|
|
131
|
+
question=(
|
|
132
|
+
"What are the main drivers and trends of global temperature "
|
|
133
|
+
"anomalies since 1990?"
|
|
134
|
+
),
|
|
135
|
+
context=ResearchContext(original_question="…"),
|
|
136
|
+
max_iterations=2,
|
|
137
|
+
confidence_threshold=0.8,
|
|
138
|
+
max_concurrency=3,
|
|
139
|
+
)
|
|
140
|
+
deps = ResearchDeps(client=client)
|
|
141
|
+
start = PlanNode(provider=None, model=None)
|
|
142
|
+
result = await graph.run(start, state=state, deps=deps)
|
|
143
|
+
report = result.output
|
|
144
|
+
print(report.title)
|
|
145
|
+
print(report.executive_summary)
|
|
110
146
|
```
|
|
111
147
|
|
|
112
148
|
## MCP Server
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
haiku/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
haiku/rag/app.py,sha256=06nsdjrljPNqZew4gsLFIA0BSwv-CxPovJaAYSFzm-w,13265
|
|
3
|
+
haiku/rag/chunker.py,sha256=PVe6ysv8UlacUd4Zb3_8RFWIaWDXnzBAy2VDJ4TaUsE,1555
|
|
4
|
+
haiku/rag/cli.py,sha256=4aYUXPr54q-7U2-cySNpSnW1ntvD0Jck6oj8vvA6IoI,10830
|
|
5
|
+
haiku/rag/client.py,sha256=iUaa6YUac3CXFniIm8DsaaNsiyHsi4cp8-fPhF5XuVU,22925
|
|
6
|
+
haiku/rag/config.py,sha256=SEV2OzaKavYwHZ0LmRzBj-0dbI6YFIRuNiTw9el7SO0,2307
|
|
7
|
+
haiku/rag/logging.py,sha256=dm65AwADpcQsH5OAPtRA-4hsw0w5DK-sGOvzYkj6jzw,1720
|
|
8
|
+
haiku/rag/mcp.py,sha256=H7XibtSNUviFeaJVsXzHiRqUm0nJCpA7A1QHuBv6SKQ,5057
|
|
9
|
+
haiku/rag/migration.py,sha256=M--KnSF3lxgKjxmokb4vuzGH-pV8eg0C_8e7jvPqW8Y,11058
|
|
10
|
+
haiku/rag/monitor.py,sha256=r386nkhdlsU8UECwIuVwnrSlgMk3vNIuUZGNIzkZuec,2770
|
|
11
|
+
haiku/rag/reader.py,sha256=qkPTMJuQ_o4sK-8zpDl9WFYe_MJ7aL_gUw6rczIpW-g,3274
|
|
12
|
+
haiku/rag/utils.py,sha256=hKH8bBBbAVYlLFBOAcErvX-4cuWIaPTbrAFeeLN1HdM,5062
|
|
13
|
+
haiku/rag/embeddings/__init__.py,sha256=44IfDITGIFTflGT6UEmiYOwpWFVbYv5smLY59D0YeCs,1419
|
|
14
|
+
haiku/rag/embeddings/base.py,sha256=BnSviKrlzjv3L0sZJs_T-pxfawd-bcTak-rsX-D2f3A,497
|
|
15
|
+
haiku/rag/embeddings/ollama.py,sha256=LuLlHH6RGoO9_gFCIlbmesuXOj017gTw6z-p8Ez0CfE,595
|
|
16
|
+
haiku/rag/embeddings/openai.py,sha256=fIFCk-jpUtaW0xsnrQnJ824O0UCjaGG2sgvBzREhilc,503
|
|
17
|
+
haiku/rag/embeddings/vllm.py,sha256=vhaUnCn6VMkfSluLhWKtSV-sekFaPsp4pKo2N7-SBCY,626
|
|
18
|
+
haiku/rag/embeddings/voyageai.py,sha256=UW-MW4tJKnPB6Fs2P7A3yt-ZeRm46H9npckchSriPX8,661
|
|
19
|
+
haiku/rag/qa/__init__.py,sha256=Sl7Kzrg9CuBOcMF01wc1NtQhUNWjJI0MhIHfCWrb8V4,434
|
|
20
|
+
haiku/rag/qa/agent.py,sha256=rtUkEmnD8lMHIxpPPVY6TdmF4aSlZnLjad5eDefrlBw,3145
|
|
21
|
+
haiku/rag/qa/prompts.py,sha256=Lqwn3m4zCsu_CJiC4s9cLsuPNbb9nq6j2PqEF3lw1eA,3380
|
|
22
|
+
haiku/rag/reranking/__init__.py,sha256=IRXHs4qPu6VbGJQpzSwhgtVWWumURH_vEoVFE-extlo,894
|
|
23
|
+
haiku/rag/reranking/base.py,sha256=LM9yUSSJ414UgBZhFTgxGprlRqzfTe4I1vgjricz2JY,405
|
|
24
|
+
haiku/rag/reranking/cohere.py,sha256=1iTdiaa8vvb6oHVB2qpWzUOVkyfUcimVSZp6Qr4aq4c,1049
|
|
25
|
+
haiku/rag/reranking/mxbai.py,sha256=uveGFIdmNmepd2EQsvYr64wv0ra2_wB845hdSZXy5Cw,908
|
|
26
|
+
haiku/rag/reranking/vllm.py,sha256=xVGH9ss-ISWdJ5SKUUHUbTqBo7PIEmA_SQv0ScdJ6XA,1479
|
|
27
|
+
haiku/rag/research/__init__.py,sha256=t4JAmIXcKaWqvpFGX5yaehsNrfblskEMn-4mDmdKn9c,502
|
|
28
|
+
haiku/rag/research/common.py,sha256=EUnsA6VZ3-WMweXESuUYezH1ALit8N38064bsZFqtBE,1688
|
|
29
|
+
haiku/rag/research/dependencies.py,sha256=ZiSQdV6jHti4DuUp4WCaJL73TqYDr5vC8ppB34M2cNg,1639
|
|
30
|
+
haiku/rag/research/graph.py,sha256=m3vDP1nPXWzfS7VeTQzmTOk-lFpoaTvKHvRIF2mbxvs,798
|
|
31
|
+
haiku/rag/research/models.py,sha256=Q92oxBNq3qp3DyUzTim9YGDOBtGzXH25K_mmfLAA7Y8,2329
|
|
32
|
+
haiku/rag/research/prompts.py,sha256=0_EMA5CS7O37QhKJM7OCDdrdgMcoF2DgehBHR4L7xmk,5103
|
|
33
|
+
haiku/rag/research/state.py,sha256=vFwO8c2JmwwfkELE5Mwjt9Oat-bHn5tayf31MIG2SRs,623
|
|
34
|
+
haiku/rag/research/nodes/evaluate.py,sha256=Cp2J-jXYZothiQV3zRZFaCsBLaUU0Tm_-ri-hlgQQII,2897
|
|
35
|
+
haiku/rag/research/nodes/plan.py,sha256=9AkTls01Q3zTLKGgIgSCX9X4VYC8IWjEWii8A_f77YQ,2439
|
|
36
|
+
haiku/rag/research/nodes/search.py,sha256=2ioc5Ba3ciq2zpFxgzoGkZOvVsJ1TBX9zseURLDJpBg,3591
|
|
37
|
+
haiku/rag/research/nodes/synthesize.py,sha256=4acKduqWnE11ML7elUksKLozxzWJTkBLSJ2li_YMxgY,1736
|
|
38
|
+
haiku/rag/store/__init__.py,sha256=hq0W0DAC7ysqhWSP2M2uHX8cbG6kbr-sWHxhq6qQcY0,103
|
|
39
|
+
haiku/rag/store/engine.py,sha256=BceAeTpDgV92B1A3GVcjsTwlD-c0cZPPvGiXW2Gola0,10215
|
|
40
|
+
haiku/rag/store/models/__init__.py,sha256=s0E72zneGlowvZrFWaNxHYjOAUjgWdLxzdYsnvNRVlY,88
|
|
41
|
+
haiku/rag/store/models/chunk.py,sha256=3EuZav4QekJIeHBCub48EM8SjNX8HEJ6wVDXGot4PEQ,421
|
|
42
|
+
haiku/rag/store/models/document.py,sha256=cZXy_jEti-hnhq7FKhuhCfd99ccY9fIHMLovB_Thbb8,425
|
|
43
|
+
haiku/rag/store/repositories/__init__.py,sha256=Olv5dLfBQINRV3HrsfUpjzkZ7Qm7goEYyMNykgo_DaY,291
|
|
44
|
+
haiku/rag/store/repositories/chunk.py,sha256=UfajEWf5VmMuSozGRDlWBjJNR0ngvOVFDrp6_augzBg,15217
|
|
45
|
+
haiku/rag/store/repositories/document.py,sha256=C9GbIl8sa2-Djaml4hlaPTtjV2HwHaz_Wzs35sdbdhg,7876
|
|
46
|
+
haiku/rag/store/repositories/settings.py,sha256=7XMBMavU8zRgdBoQzQg0Obfa7UKjuVnBugidTC6sEW0,5548
|
|
47
|
+
haiku/rag/store/upgrades/__init__.py,sha256=RQ8A6rEXBASLb5PD9vdDnEas_m_GgRzzdVu4B88Snqc,1975
|
|
48
|
+
haiku/rag/store/upgrades/v0_10_1.py,sha256=qNGnxj6hoHaHJ1rKTiALfw0c9NQOi0KAK-VZCD_073A,1959
|
|
49
|
+
haiku/rag/store/upgrades/v0_9_3.py,sha256=NrjNilQSgDtFWRbL3ZUtzQzJ8tf9u0dDRJtnDFwwbdw,3322
|
|
50
|
+
haiku_rag-0.10.1.dist-info/METADATA,sha256=Bu1Nmz3AoD_EquvCvsbcJjGXmFsGDEwqnfaYIBgOLqQ,5879
|
|
51
|
+
haiku_rag-0.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
52
|
+
haiku_rag-0.10.1.dist-info/entry_points.txt,sha256=G1U3nAkNd5YDYd4v0tuYFbriz0i-JheCsFuT9kIoGCI,48
|
|
53
|
+
haiku_rag-0.10.1.dist-info/licenses/LICENSE,sha256=eXZrWjSk9PwYFNK9yUczl3oPl95Z4V9UXH7bPN46iPo,1065
|
|
54
|
+
haiku_rag-0.10.1.dist-info/RECORD,,
|