Add verbose flag to ask

This commit is contained in:
Yiorgis Gozadinos 2025-09-30 14:38:47 +03:00
parent 2dfb1882dd
commit 49eac86259
No known key found for this signature in database
6 changed files with 91 additions and 8 deletions

View file

@ -43,6 +43,9 @@ haiku-rag ask "Who is the author of haiku.rag?" --cite
# Deep QA (multi-agent question decomposition)
haiku-rag ask "Who is the author of haiku.rag?" --deep --cite
# Deep QA with verbose output
haiku-rag ask "Who is the author of haiku.rag?" --deep --verbose
# Multiagent research (iterative plan/search/evaluate)
haiku-rag research \
"What are the main drivers and trends of global temperature anomalies since 1990?" \

View file

@ -97,7 +97,12 @@ Use deep QA for complex questions (multi-agent decomposition):
haiku-rag ask "What are the main features and architecture of haiku.rag?" --deep --cite
```
The QA agent will search your documents for relevant information and provide a comprehensive answer. With `--cite`, responses include citations showing which documents were used. With `--deep`, the question is decomposed into sub-questions that are answered in parallel before synthesizing a final answer.
Show verbose output with deep QA:
```bash
haiku-rag ask "What are the main features and architecture of haiku.rag?" --deep --verbose
```
The QA agent will search your documents for relevant information and provide a comprehensive answer. With `--cite`, responses include citations showing which documents were used. With `--deep`, the question is decomposed into sub-questions that are answered in parallel before synthesizing a final answer. With `--verbose` (only with `--deep`), you'll see the planning, searching, evaluation, and synthesis steps as they happen.
When available, citations use the document title; otherwise they fall back to the URI.
## Research

View file

@ -194,10 +194,18 @@ class HaikuRAGApp:
for chunk, score in results:
self._rich_print_search_result(chunk, score)
async def ask(self, question: str, cite: bool = False, deep: bool = False):
async def ask(
self,
question: str,
cite: bool = False,
deep: bool = False,
verbose: bool = False,
):
async with HaikuRAG(db_path=self.db_path) as self.client:
try:
if deep:
from rich.console import Console
from haiku.rag.qa.deep.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph
from haiku.rag.qa.deep.nodes import DeepPlanNode
@ -208,7 +216,9 @@ class HaikuRAGApp:
original_question=question, use_citations=cite
)
state = DeepQAState(context=context)
deps = DeepQADeps(client=self.client)
deps = DeepQADeps(
client=self.client, console=Console() if verbose else None
)
start_node = DeepPlanNode(
provider=Config.QA_PROVIDER,

View file

@ -304,11 +304,16 @@ def ask(
"--deep",
help="Use deep multi-agent QA for complex questions",
),
verbose: bool = typer.Option(
False,
"--verbose",
help="Show verbose progress output (only with --deep)",
),
):
from haiku.rag.app import HaikuRAGApp
app = HaikuRAGApp(db_path=db)
asyncio.run(app.ask(question=question, cite=cite, deep=deep))
asyncio.run(app.ask(question=question, cite=cite, deep=deep, verbose=verbose))
@cli.command("research", help="Run multi-agent research and output a concise report")

View file

@ -248,6 +248,23 @@ async def test_ask_with_cite(app: HaikuRAGApp, monkeypatch):
mock_client.ask.assert_called_once_with("test question", cite=True)
@pytest.mark.asyncio
async def test_ask_with_verbose(app: HaikuRAGApp, monkeypatch):
"""Test asking a question with verbose (should be ignored for non-deep)."""
mock_answer = "Test answer"
mock_client = AsyncMock()
mock_client.ask.return_value = mock_answer
mock_client.__aenter__.return_value = mock_client
mock_print = MagicMock()
monkeypatch.setattr(app.console, "print", mock_print)
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
await app.ask("test question", verbose=True)
mock_client.ask.assert_called_once_with("test question", cite=False)
@pytest.mark.asyncio
async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
"""Test asking a question with deep QA."""
@ -308,3 +325,32 @@ async def test_ask_with_deep_and_cite(app: HaikuRAGApp, monkeypatch):
call_kwargs = mock_graph.run.call_args[1]
assert call_kwargs["state"].context.original_question == "test question"
assert call_kwargs["state"].context.use_citations is True
@pytest.mark.asyncio
async def test_ask_with_deep_and_verbose(app: HaikuRAGApp, monkeypatch):
"""Test asking a question with deep QA and verbose output."""
from haiku.rag.qa.deep.models import DeepAnswer
mock_output = DeepAnswer(answer="Deep QA answer", sources=["test.md"])
mock_result = MagicMock()
mock_result.output = mock_output
mock_graph = AsyncMock()
mock_graph.run.return_value = mock_result
mock_client = AsyncMock()
mock_client.__aenter__.return_value = mock_client
mock_print = MagicMock()
monkeypatch.setattr(app.console, "print", mock_print)
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
with patch(
"haiku.rag.qa.deep.graph.build_deep_qa_graph", return_value=mock_graph
):
await app.ask("test question", deep=True, verbose=True)
mock_graph.run.assert_called_once()
call_kwargs = mock_graph.run.call_args[1]
assert call_kwargs["deps"].console is not None

View file

@ -207,7 +207,7 @@ def test_ask():
assert result.exit_code == 0
mock_app_instance.ask.assert_called_once_with(
question="What is Python?", cite=False, deep=False
question="What is Python?", cite=False, deep=False, verbose=False
)
@ -221,7 +221,7 @@ def test_ask_with_cite():
assert result.exit_code == 0
mock_app_instance.ask.assert_called_once_with(
question="What is Python?", cite=True, deep=False
question="What is Python?", cite=True, deep=False, verbose=False
)
@ -235,7 +235,7 @@ def test_ask_with_deep():
assert result.exit_code == 0
mock_app_instance.ask.assert_called_once_with(
question="What is Python?", cite=False, deep=True
question="What is Python?", cite=False, deep=True, verbose=False
)
@ -249,7 +249,21 @@ def test_ask_with_deep_and_cite():
assert result.exit_code == 0
mock_app_instance.ask.assert_called_once_with(
question="What is Python?", cite=True, deep=True
question="What is Python?", cite=True, deep=True, verbose=False
)
def test_ask_with_deep_and_verbose():
with patch("haiku.rag.app.HaikuRAGApp") as mock_app:
mock_app_instance = MagicMock()
mock_app_instance.ask = AsyncMock()
mock_app.return_value = mock_app_instance
result = runner.invoke(cli, ["ask", "What is Python?", "--deep", "--verbose"])
assert result.exit_code == 0
mock_app_instance.ask.assert_called_once_with(
question="What is Python?", cite=False, deep=True, verbose=True
)