haiku.rag 0.9.1__tar.gz → 0.9.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (92) hide show
  1. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/PKG-INFO +1 -1
  2. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/agents.md +2 -0
  3. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/pyproject.toml +1 -1
  4. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/app.py +2 -7
  5. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/qa/agent.py +3 -0
  6. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/__init__.py +2 -0
  7. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/base.py +9 -1
  8. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/evaluation_agent.py +3 -1
  9. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/orchestrator.py +37 -2
  10. haiku_rag-0.9.2/src/haiku/rag/research/presearch_agent.py +34 -0
  11. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/prompts.py +13 -0
  12. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/search_agent.py +1 -0
  13. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/synthesis_agent.py +4 -3
  14. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/research/test_orchestrator.py +0 -1
  15. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/uv.lock +1 -1
  16. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.github/FUNDING.yml +0 -0
  17. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.github/workflows/build-docs.yml +0 -0
  18. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.github/workflows/build-publish.yml +0 -0
  19. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.gitignore +0 -0
  20. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.pre-commit-config.yaml +0 -0
  21. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/.python-version +0 -0
  22. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/LICENSE +0 -0
  23. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/README.md +0 -0
  24. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/benchmarks.md +0 -0
  25. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/cli.md +0 -0
  26. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/configuration.md +0 -0
  27. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/index.md +0 -0
  28. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/installation.md +0 -0
  29. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/mcp.md +0 -0
  30. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/python.md +0 -0
  31. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/docs/server.md +0 -0
  32. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/mkdocs.yml +0 -0
  33. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/__init__.py +0 -0
  34. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/chunker.py +0 -0
  35. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/cli.py +0 -0
  36. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/client.py +0 -0
  37. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/config.py +0 -0
  38. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/__init__.py +0 -0
  39. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/base.py +0 -0
  40. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/ollama.py +0 -0
  41. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/openai.py +0 -0
  42. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/vllm.py +0 -0
  43. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/embeddings/voyageai.py +0 -0
  44. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/logging.py +0 -0
  45. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/mcp.py +0 -0
  46. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/migration.py +0 -0
  47. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/monitor.py +0 -0
  48. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/qa/__init__.py +0 -0
  49. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/qa/prompts.py +0 -0
  50. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reader.py +0 -0
  51. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reranking/__init__.py +0 -0
  52. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reranking/base.py +0 -0
  53. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reranking/cohere.py +0 -0
  54. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reranking/mxbai.py +0 -0
  55. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/reranking/vllm.py +0 -0
  56. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/research/dependencies.py +0 -0
  57. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/__init__.py +0 -0
  58. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/engine.py +0 -0
  59. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/models/__init__.py +0 -0
  60. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/models/chunk.py +0 -0
  61. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/models/document.py +0 -0
  62. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/repositories/__init__.py +0 -0
  63. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/repositories/chunk.py +0 -0
  64. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/repositories/document.py +0 -0
  65. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/repositories/settings.py +0 -0
  66. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/store/upgrades/__init__.py +0 -0
  67. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/src/haiku/rag/utils.py +0 -0
  68. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/__init__.py +0 -0
  69. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/conftest.py +0 -0
  70. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/generate_benchmark_db.py +0 -0
  71. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/llm_judge.py +0 -0
  72. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/research/test_evaluation_agent.py +0 -0
  73. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/research/test_search_agent.py +0 -0
  74. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/research/test_synthesis_agent.py +0 -0
  75. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_app.py +0 -0
  76. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_chunk.py +0 -0
  77. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_chunker.py +0 -0
  78. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_cli.py +0 -0
  79. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_client.py +0 -0
  80. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_document.py +0 -0
  81. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_embedder.py +0 -0
  82. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_lancedb_connection.py +0 -0
  83. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_monitor.py +0 -0
  84. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_preprocessor.py +0 -0
  85. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_qa.py +0 -0
  86. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_reader.py +0 -0
  87. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_rebuild.py +0 -0
  88. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_reranker.py +0 -0
  89. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_search.py +0 -0
  90. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_settings.py +0 -0
  91. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_utils.py +0 -0
  92. {haiku_rag-0.9.1 → haiku_rag-0.9.2}/tests/test_versioning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haiku.rag
3
- Version: 0.9.1
3
+ Version: 0.9.2
4
4
  Summary: Agentic Retrieval Augmented Generation (RAG) with LanceDB
5
5
  Author-email: Yiorgis Gozadinos <ggozadinos@gmail.com>
6
6
  License: MIT
@@ -43,6 +43,8 @@ The research workflow coordinates specialized agents to plan, search, analyze, a
43
43
  Components:
44
44
 
45
45
  - Orchestrator: Plans, coordinates, and loops until confidence is sufficient
46
+ - Presearch Survey: Runs a quick KB scan and summarizes relevant chunk text to
47
+ ground the initial plan (plain-text summary; no URIs or scores)
46
48
  - Search Specialist: Performs targeted RAG searches and answers sub‑questions
47
49
  - Analysis & Evaluation: Extracts insights, identifies gaps, proposes new questions
48
50
  - Synthesis: Produces a final structured research report
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "haiku.rag"
3
- version = "0.9.1"
3
+ version = "0.9.2"
4
4
  description = "Agentic Retrieval Augmented Generation (RAG) with LanceDB"
5
5
  authors = [{ name = "Yiorgis Gozadinos", email = "ggozadinos@gmail.com" }]
6
6
  license = { text = "MIT" }
@@ -122,12 +122,7 @@ class HaikuRAGApp:
122
122
  self.console.print(f"• {finding}")
123
123
  self.console.print()
124
124
 
125
- # Themes
126
- if report.themes:
127
- self.console.print("[bold cyan]Key Themes:[/bold cyan]")
128
- for theme, explanation in report.themes.items():
129
- self.console.print(f"• [bold]{theme}[/bold]: {explanation}")
130
- self.console.print()
125
+ # (Themes section removed)
131
126
 
132
127
  # Conclusions
133
128
  if report.conclusions:
@@ -261,7 +256,7 @@ class HaikuRAGApp:
261
256
  elif transport == "sse":
262
257
  await server.run_sse_async()
263
258
  else:
264
- await server.run_http_async("streamable-http")
259
+ await server.run_http_async(transport="streamable-http")
265
260
  except KeyboardInterrupt:
266
261
  pass
267
262
  finally:
@@ -49,6 +49,9 @@ class QuestionAnswerAgent:
49
49
  limit: int = 3,
50
50
  ) -> list[SearchResult]:
51
51
  """Search the knowledge base for relevant documents."""
52
+
53
+ # Remove quotes from queries as this requires positional indexing in lancedb
54
+ query = query.replace('"', "")
52
55
  search_results = await ctx.deps.client.search(query, limit=limit)
53
56
  expanded_results = await ctx.deps.client.expand_context(search_results)
54
57
 
@@ -12,6 +12,7 @@ from haiku.rag.research.evaluation_agent import (
12
12
  EvaluationResult,
13
13
  )
14
14
  from haiku.rag.research.orchestrator import ResearchOrchestrator, ResearchPlan
15
+ from haiku.rag.research.presearch_agent import PresearchSurveyAgent
15
16
  from haiku.rag.research.search_agent import SearchSpecialistAgent
16
17
  from haiku.rag.research.synthesis_agent import ResearchReport, SynthesisAgent
17
18
 
@@ -25,6 +26,7 @@ __all__ = [
25
26
  # Specialized agents
26
27
  "SearchAnswer",
27
28
  "SearchSpecialistAgent",
29
+ "PresearchSurveyAgent",
28
30
  "AnalysisEvaluationAgent",
29
31
  "EvaluationResult",
30
32
  "SynthesisAgent",
@@ -33,10 +33,18 @@ class BaseResearchAgent[T](ABC):
33
33
  # Import deps type lazily to avoid circular import during module load
34
34
  from haiku.rag.research.dependencies import ResearchDependencies
35
35
 
36
+ # If the agent is expected to return plain text, pass `str` directly.
37
+ # Otherwise, wrap the model with ToolOutput for robust tool-handling retries.
38
+ agent_output_type: Any
39
+ if self.output_type is str: # plain text output
40
+ agent_output_type = str
41
+ else:
42
+ agent_output_type = ToolOutput(self.output_type, max_retries=3)
43
+
36
44
  self._agent = Agent(
37
45
  model=model_obj,
38
46
  deps_type=ResearchDependencies,
39
- output_type=ToolOutput(self.output_type, max_retries=3),
47
+ output_type=agent_output_type,
40
48
  system_prompt=self.get_system_prompt(),
41
49
  )
42
50
 
@@ -11,7 +11,9 @@ class EvaluationResult(BaseModel):
11
11
  description="Main insights extracted from the research so far"
12
12
  )
13
13
  new_questions: list[str] = Field(
14
- description="New sub-questions to add to the research (max 3)", max_length=3
14
+ description="New sub-questions to add to the research (max 3)",
15
+ max_length=3,
16
+ default=[],
15
17
  )
16
18
  confidence_score: float = Field(
17
19
  description="Confidence level in the completeness of research (0-1)",
@@ -12,6 +12,7 @@ from haiku.rag.research.evaluation_agent import (
12
12
  AnalysisEvaluationAgent,
13
13
  EvaluationResult,
14
14
  )
15
+ from haiku.rag.research.presearch_agent import PresearchSurveyAgent
15
16
  from haiku.rag.research.prompts import ORCHESTRATOR_PROMPT
16
17
  from haiku.rag.research.search_agent import SearchSpecialistAgent
17
18
  from haiku.rag.research.synthesis_agent import ResearchReport, SynthesisAgent
@@ -41,6 +42,9 @@ class ResearchOrchestrator(BaseResearchAgent[ResearchPlan]):
41
42
  self.search_agent: SearchSpecialistAgent = SearchSpecialistAgent(
42
43
  provider, model
43
44
  )
45
+ self.presearch_agent: PresearchSurveyAgent = PresearchSurveyAgent(
46
+ provider, model
47
+ )
44
48
  self.evaluation_agent: AnalysisEvaluationAgent = AnalysisEvaluationAgent(
45
49
  provider, model
46
50
  )
@@ -61,7 +65,12 @@ class ResearchOrchestrator(BaseResearchAgent[ResearchPlan]):
61
65
  "original_question": context.original_question,
62
66
  "unanswered_questions": context.sub_questions,
63
67
  "qa_responses": [
64
- {"question": qa.query, "answer": qa.answer}
68
+ {
69
+ "question": qa.query,
70
+ "answer": qa.answer,
71
+ "context_snippets": qa.context,
72
+ "sources": qa.sources,
73
+ }
65
74
  for qa in context.qa_responses
66
75
  ],
67
76
  "insights": context.insights,
@@ -99,12 +108,38 @@ class ResearchOrchestrator(BaseResearchAgent[ResearchPlan]):
99
108
  # Use provided console or create a new one
100
109
  console = console or Console() if verbose else None
101
110
 
111
+ # Run a simple presearch survey to summarize KB context
112
+ if console:
113
+ console.print(
114
+ "\n[bold cyan]🔎 Presearch: summarizing KB context...[/bold cyan]"
115
+ )
116
+
117
+ presearch_result = await self.presearch_agent.run(question, deps=deps)
118
+
102
119
  # Create initial research plan
103
120
  if console:
104
121
  console.print("\n[bold cyan]📋 Creating research plan...[/bold cyan]")
105
122
 
123
+ # Include the presearch summary to ground the planning step.
124
+
125
+ planning_context_xml = format_as_xml(
126
+ {
127
+ "original_question": question,
128
+ "presearch_summary": presearch_result.output or "",
129
+ },
130
+ root_tag="planning_context",
131
+ )
132
+
133
+ plan_prompt = (
134
+ "Create a research plan for the main question below.\n\n"
135
+ f"Main question: {question}\n\n"
136
+ "Use this brief presearch summary to inform the plan. Focus the 3 sub-questions "
137
+ "on the most important aspects not already obvious from the current KB context.\n\n"
138
+ f"{planning_context_xml}"
139
+ )
140
+
106
141
  plan_result: AgentRunResult[ResearchPlan] = await self.run(
107
- f"Create a research plan for: {question}", deps=deps
142
+ plan_prompt, deps=deps
108
143
  )
109
144
 
110
145
  context.sub_questions = plan_result.output.sub_questions
@@ -0,0 +1,34 @@
1
+ from pydantic_ai import RunContext
2
+ from pydantic_ai.run import AgentRunResult
3
+
4
+ from haiku.rag.research.base import BaseResearchAgent
5
+ from haiku.rag.research.dependencies import ResearchDependencies
6
+ from haiku.rag.research.prompts import PRESEARCH_AGENT_PROMPT
7
+
8
+
9
+ class PresearchSurveyAgent(BaseResearchAgent[str]):
10
+ """Presearch agent that gathers verbatim context and summarizes it."""
11
+
12
+ def __init__(self, provider: str, model: str) -> None:
13
+ super().__init__(provider, model, str)
14
+
15
+ async def run(
16
+ self, prompt: str, deps: ResearchDependencies, **kwargs
17
+ ) -> AgentRunResult[str]:
18
+ return await super().run(prompt, deps, **kwargs)
19
+
20
+ def get_system_prompt(self) -> str:
21
+ return PRESEARCH_AGENT_PROMPT
22
+
23
+ def register_tools(self) -> None:
24
+ @self.agent.tool
25
+ async def gather_context(
26
+ ctx: RunContext[ResearchDependencies],
27
+ query: str,
28
+ limit: int = 6,
29
+ ) -> str:
30
+ """Return verbatim concatenation of relevant chunk texts."""
31
+ query = query.replace('"', "")
32
+ results = await ctx.deps.client.search(query, limit=limit)
33
+ expanded = await ctx.deps.client.expand_context(results)
34
+ return "\n\n".join(chunk.content for chunk, _ in expanded)
@@ -114,3 +114,16 @@ Focus on creating a report that provides clear value to the reader by:
114
114
  - Highlighting the most important findings
115
115
  - Explaining the implications of the research
116
116
  - Suggesting concrete next steps"""
117
+
118
+ PRESEARCH_AGENT_PROMPT = """You are a rapid research surveyor.
119
+
120
+ 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.
125
+
126
+ Rules:
127
+ - Base the summary strictly on the provided text; do not invent.
128
+ - Output only the summary as plain text (one short paragraph).
129
+ """
@@ -42,6 +42,7 @@ class SearchSpecialistAgent(BaseResearchAgent[SearchAnswer]):
42
42
  ) -> str:
43
43
  """Search the KB and return a concise context pack."""
44
44
  # Remove quotes from queries as this requires positional indexing in lancedb
45
+ # XXX: Investigate how to do that with lancedb
45
46
  query = query.replace('"', "")
46
47
  search_results = await ctx.deps.client.search(query, limit=limit)
47
48
  expanded = await ctx.deps.client.expand_context(search_results)
@@ -12,11 +12,12 @@ class ResearchReport(BaseModel):
12
12
  main_findings: list[str] = Field(
13
13
  description="Primary research findings with supporting evidence"
14
14
  )
15
- themes: dict[str, str] = Field(description="Major themes and their explanations")
16
15
  conclusions: list[str] = Field(description="Evidence-based conclusions")
17
- limitations: list[str] = Field(description="Limitations of the current research")
16
+ limitations: list[str] = Field(
17
+ description="Limitations of the current research", default=[]
18
+ )
18
19
  recommendations: list[str] = Field(
19
- description="Actionable recommendations based on findings"
20
+ description="Actionable recommendations based on findings", default=[]
20
21
  )
21
22
  sources_summary: str = Field(
22
23
  description="Summary of sources used and their reliability"
@@ -172,7 +172,6 @@ class TestResearchOrchestrator:
172
172
  assert report.title
173
173
  assert report.executive_summary
174
174
  assert isinstance(report.main_findings, list)
175
- assert isinstance(report.themes, dict)
176
175
  assert isinstance(report.conclusions, list)
177
176
  assert isinstance(report.limitations, list)
178
177
  assert isinstance(report.recommendations, list)
@@ -1111,7 +1111,7 @@ wheels = [
1111
1111
 
1112
1112
  [[package]]
1113
1113
  name = "haiku-rag"
1114
- version = "0.9.1"
1114
+ version = "0.9.2"
1115
1115
  source = { editable = "." }
1116
1116
  dependencies = [
1117
1117
  { name = "docling" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes