Add verbose flag to ask
This commit is contained in:
parent
2dfb1882dd
commit
49eac86259
6 changed files with 91 additions and 8 deletions
|
|
@ -43,6 +43,9 @@ haiku-rag ask "Who is the author of haiku.rag?" --cite
|
||||||
# Deep QA (multi-agent question decomposition)
|
# Deep QA (multi-agent question decomposition)
|
||||||
haiku-rag ask "Who is the author of haiku.rag?" --deep --cite
|
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
|
||||||
|
|
||||||
# Multi‑agent research (iterative plan/search/evaluate)
|
# Multi‑agent research (iterative plan/search/evaluate)
|
||||||
haiku-rag research \
|
haiku-rag research \
|
||||||
"What are the main drivers and trends of global temperature anomalies since 1990?" \
|
"What are the main drivers and trends of global temperature anomalies since 1990?" \
|
||||||
|
|
|
||||||
|
|
@ -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
|
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.
|
When available, citations use the document title; otherwise they fall back to the URI.
|
||||||
|
|
||||||
## Research
|
## Research
|
||||||
|
|
|
||||||
|
|
@ -194,10 +194,18 @@ class HaikuRAGApp:
|
||||||
for chunk, score in results:
|
for chunk, score in results:
|
||||||
self._rich_print_search_result(chunk, score)
|
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:
|
async with HaikuRAG(db_path=self.db_path) as self.client:
|
||||||
try:
|
try:
|
||||||
if deep:
|
if deep:
|
||||||
|
from rich.console import Console
|
||||||
|
|
||||||
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
from haiku.rag.qa.deep.dependencies import DeepQAContext
|
||||||
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
from haiku.rag.qa.deep.graph import build_deep_qa_graph
|
||||||
from haiku.rag.qa.deep.nodes import DeepPlanNode
|
from haiku.rag.qa.deep.nodes import DeepPlanNode
|
||||||
|
|
@ -208,7 +216,9 @@ class HaikuRAGApp:
|
||||||
original_question=question, use_citations=cite
|
original_question=question, use_citations=cite
|
||||||
)
|
)
|
||||||
state = DeepQAState(context=context)
|
state = DeepQAState(context=context)
|
||||||
deps = DeepQADeps(client=self.client)
|
deps = DeepQADeps(
|
||||||
|
client=self.client, console=Console() if verbose else None
|
||||||
|
)
|
||||||
|
|
||||||
start_node = DeepPlanNode(
|
start_node = DeepPlanNode(
|
||||||
provider=Config.QA_PROVIDER,
|
provider=Config.QA_PROVIDER,
|
||||||
|
|
|
||||||
|
|
@ -304,11 +304,16 @@ def ask(
|
||||||
"--deep",
|
"--deep",
|
||||||
help="Use deep multi-agent QA for complex questions",
|
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
|
from haiku.rag.app import HaikuRAGApp
|
||||||
|
|
||||||
app = HaikuRAGApp(db_path=db)
|
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")
|
@cli.command("research", help="Run multi-agent research and output a concise report")
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,23 @@ async def test_ask_with_cite(app: HaikuRAGApp, monkeypatch):
|
||||||
mock_client.ask.assert_called_once_with("test question", cite=True)
|
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
|
@pytest.mark.asyncio
|
||||||
async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
|
async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
|
||||||
"""Test asking a question with deep QA."""
|
"""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]
|
call_kwargs = mock_graph.run.call_args[1]
|
||||||
assert call_kwargs["state"].context.original_question == "test question"
|
assert call_kwargs["state"].context.original_question == "test question"
|
||||||
assert call_kwargs["state"].context.use_citations is True
|
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
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ def test_ask():
|
||||||
|
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
mock_app_instance.ask.assert_called_once_with(
|
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
|
assert result.exit_code == 0
|
||||||
mock_app_instance.ask.assert_called_once_with(
|
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
|
assert result.exit_code == 0
|
||||||
mock_app_instance.ask.assert_called_once_with(
|
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
|
assert result.exit_code == 0
|
||||||
mock_app_instance.ask.assert_called_once_with(
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue