Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ Sources:

That `[mock]` answer plus **Sources** means retrieval works before you spend a token. If titles or scores drift, the note corpus or scorer changed — open an issue before "fixing" ranking by eye.

If no notes match (including an empty notes folder), the command prints a notice
to stderr and exits with status `1` before generating an answer. Try different
words or add a Markdown note to the folder selected by `--notes`. Stdout stays
empty, even with `--json`; no answer or Sources block is printed.

## Real answers (optional)

```bash
Expand Down
6 changes: 6 additions & 0 deletions src/notes_rag/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ def main(argv: list[str] | None = None) -> int:
return 1
notes = load_notes(notes_dir)
hits = retrieve(notes, args.question, k=args.k)
if not hits:
print(
f"no notes matched — try different words or add a note under {notes_dir}",
file=sys.stderr,
)
return 1
answerer = Answerer.from_env(mock=args.mock)
try:
answer = answerer.answer(args.question, hits)
Expand Down
26 changes: 23 additions & 3 deletions tests/test_notes_rag.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import json
from pathlib import Path

import pytest

from notes_rag.answer import Answerer
from notes_rag.cli import main
from notes_rag.retrieve import load_notes, retrieve
Expand Down Expand Up @@ -61,9 +63,27 @@ def test_empty_notes_dir(tmp_path, capsys):
empty = tmp_path / "empty"
empty.mkdir()
code = main(["--mock", "--notes", str(empty), "anything"])
out = capsys.readouterr().out
assert code == 0
assert "could not find" in out.lower()
captured = capsys.readouterr()
assert code == 1
assert captured.out == ""
assert "no notes matched" in captured.err
assert str(empty) in captured.err


@pytest.mark.parametrize("flags", [[], ["--mock"], ["--json"], ["--mock", "--json"]])
def test_no_matching_notes_reports_failure(flags, capsys, monkeypatch):
def unexpected_answerer(**kwargs):
pytest.fail("empty retrieval must not construct an answerer")

monkeypatch.setattr(Answerer, "from_env", unexpected_answerer)
code = main([*flags, "--notes", str(NOTES), "qzxvplm"])
captured = capsys.readouterr()
assert code == 1
assert captured.out == ""
assert "no notes matched" in captured.err
assert "try different words or add a note" in captured.err
assert str(NOTES) in captured.err
assert "Sources:" not in captured.out + captured.err


def test_missing_key_without_mock():
Expand Down