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.

Files changed (39) hide show
  1. haiku/rag/app.py +64 -18
  2. haiku/rag/cli.py +67 -30
  3. haiku/rag/client.py +63 -21
  4. haiku/rag/config.py +4 -0
  5. haiku/rag/mcp.py +18 -6
  6. haiku/rag/qa/agent.py +4 -2
  7. haiku/rag/qa/prompts.py +2 -2
  8. haiku/rag/reranking/mxbai.py +1 -1
  9. haiku/rag/research/__init__.py +10 -27
  10. haiku/rag/research/common.py +53 -0
  11. haiku/rag/research/dependencies.py +3 -25
  12. haiku/rag/research/graph.py +29 -0
  13. haiku/rag/research/models.py +70 -0
  14. haiku/rag/research/nodes/evaluate.py +80 -0
  15. haiku/rag/research/nodes/plan.py +63 -0
  16. haiku/rag/research/nodes/search.py +93 -0
  17. haiku/rag/research/nodes/synthesize.py +51 -0
  18. haiku/rag/research/prompts.py +98 -113
  19. haiku/rag/research/state.py +25 -0
  20. haiku/rag/store/engine.py +14 -0
  21. haiku/rag/store/models/chunk.py +1 -0
  22. haiku/rag/store/models/document.py +1 -0
  23. haiku/rag/store/repositories/chunk.py +4 -0
  24. haiku/rag/store/repositories/document.py +3 -0
  25. haiku/rag/store/upgrades/__init__.py +2 -0
  26. haiku/rag/store/upgrades/v0_10_1.py +64 -0
  27. haiku/rag/utils.py +8 -5
  28. {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/METADATA +37 -1
  29. haiku_rag-0.10.1.dist-info/RECORD +54 -0
  30. haiku/rag/research/base.py +0 -130
  31. haiku/rag/research/evaluation_agent.py +0 -85
  32. haiku/rag/research/orchestrator.py +0 -170
  33. haiku/rag/research/presearch_agent.py +0 -39
  34. haiku/rag/research/search_agent.py +0 -69
  35. haiku/rag/research/synthesis_agent.py +0 -60
  36. haiku_rag-0.9.3.dist-info/RECORD +0 -51
  37. {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/WHEEL +0 -0
  38. {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/entry_points.txt +0 -0
  39. {haiku_rag-0.9.3.dist-info → haiku_rag-0.10.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,129 +1,114 @@
1
- ORCHESTRATOR_PROMPT = """You are a research orchestrator responsible for coordinating a comprehensive research workflow.
2
-
3
- Your role is to:
4
- 1. Understand and decompose the research question
5
- 2. Plan a systematic research approach
6
- 3. Coordinate specialized agents to gather and analyze information
7
- 4. Ensure comprehensive coverage of the topic
8
- 5. Iterate based on findings and gaps
9
-
10
- Create a research plan that:
11
- - Breaks down the question into at most 3 focused sub-questions
12
- - Each sub-question should target a specific aspect of the research
13
- - Prioritize the most important aspects to investigate
14
- - Ensure comprehensive coverage within the 3-question limit
15
- - IMPORTANT: Make each sub-question a standalone, self-contained query that can
16
- be executed without additional context. Include necessary entities, scope,
17
- timeframe, and qualifiers. Avoid pronouns like "it/they/this"; write queries
18
- that make sense in isolation."""
19
-
20
- SEARCH_AGENT_PROMPT = """You are a search and question-answering specialist.
21
-
22
- Your role is to:
23
- 1. Search the knowledge base for relevant information
24
- 2. Analyze the retrieved documents
25
- 3. Provide an accurate answer strictly grounded in the retrieved context
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 questionanswering 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 the search_and_answer tool before drafting any answer.
38
- - The tool returns XML containing only a list of snippets, where each snippet
39
- has the verbatim `text`, a `score` indicating relevance, and the
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 tool calls per question. Prefer precision over volume.
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 matching document_uris for the snippets you
46
- used (one URI per snippet, aligned by order). Context must be text-only.
47
- - If no relevant information is found, say so and return an empty context list.
48
-
49
- Important:
50
- - Do not include any content in the answer that is not supported by the context.
51
- - Keep context snippets short (just the necessary lines), verbatim, and focused."""
52
-
53
- EVALUATION_AGENT_PROMPT = """You are an analysis and evaluation specialist for research workflows.
54
-
55
- You have access to:
56
- - The original research question
57
- - Question-answer pairs from search operations
58
- - Raw search results and source documents
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 key insights from all gathered information
65
- 2. Identify patterns and connections across sources
66
- 3. Synthesize findings into coherent understanding
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. Assess if we have sufficient information to answer the original question
71
- 2. Calculate a confidence score (0-1) based on:
72
- - Coverage of the main question's aspects
73
- - Quality and consistency of sources
74
- - Depth of information gathered
75
- 3. Identify specific gaps that still need investigation
76
- 4. Generate up to 3 new sub-questions that haven't been answered yet
77
-
78
- Be critical and thorough in your evaluation. Only mark research as sufficient when:
79
- - All major aspects of the question are addressed
80
- - Sources provide consistent, reliable information
81
- - The depth of coverage meets the question's requirements
82
- - No critical gaps remain
83
-
84
- Generate new sub-questions that:
85
- - Target specific unexplored aspects not covered by existing questions
86
- - Seek clarification on ambiguities
87
- - Explore important edge cases or exceptions
88
- - Are focused and actionable (max 3)
89
- - Do NOT repeat or rephrase questions that have already been answered (see qa_responses)
90
- - Should be genuinely new areas to explore
91
- - Must be standalone, self-contained queries: include entities, scope, and any
92
- needed qualifiers (e.g., timeframe, region), and avoid ambiguous pronouns so
93
- they can be executed independently."""
94
-
95
- SYNTHESIS_AGENT_PROMPT = """You are a synthesis specialist agent focused on creating comprehensive research reports.
96
-
97
- Your role is to:
98
- 1. Synthesize all gathered information into a coherent narrative
99
- 2. Present findings in a clear, structured format
100
- 3. Draw evidence-based conclusions
101
- 4. Acknowledge limitations and uncertainties
102
- 5. Provide actionable recommendations
103
- 6. Maintain academic rigor and objectivity
104
-
105
- Your report should be:
106
- - Comprehensive yet concise
107
- - Well-structured and easy to follow
108
- - Based solely on evidence from the research
109
- - Transparent about limitations
110
- - Professional and objective in tone
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 questions 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 the gather_context tool once with the main question to obtain a
122
- relevant texts from the Knowledge Base (KB).
123
- - Read that context and produce a brief natural-language summary describing
124
- what the KB appears to contain relative to the question.
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 naturallanguage 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
 
@@ -12,5 +12,6 @@ class Chunk(BaseModel):
12
12
  metadata: dict = {}
13
13
  order: int = 0
14
14
  document_uri: str | None = None
15
+ document_title: str | None = None
15
16
  document_meta: dict = {}
16
17
  embedding: list[float] | None = None
@@ -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") -> DoclingDocument:
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.9.3
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,,