Skip to content
Closed
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cd notes-rag
pip install -e ".[dev]"
pytest
python -m notes_rag --mock "What is a git remote?"
# add --json for machine-readable answer and sources
```

You should see a `[mock]` answer plus a **Sources** list. That means retrieval works before you spend a token.
Expand Down
28 changes: 26 additions & 2 deletions src/notes_rag/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

Expand Down Expand Up @@ -34,6 +35,11 @@ def build_parser() -> argparse.ArgumentParser:
action="store_true",
help="Answer from retrieved notes without calling an API",
)
p.add_argument(
"--json",
action="store_true",
help="Emit the answer and sources as JSON",
)
return p


Expand All @@ -47,8 +53,26 @@ def main(argv: list[str] | None = None) -> int:
hits = retrieve(notes, args.question, k=args.k)
answerer = Answerer.from_env(mock=args.mock)
try:
print(answerer.answer(args.question, hits))
if hits:
answer = answerer.answer(args.question, hits)
if args.json:
print(
json.dumps(
{
"answer": answer,
"sources": [
{
"title": note.title,
"file": note.path.name,
"score": score,
}
for note, score in hits
],
}
)
)
else:
print(answer)
if hits and not args.json:
print("\nSources:")
for note, score in hits:
print(f" - {note.title} ({note.path.name}, score={score:.3f})")
Expand Down
10 changes: 10 additions & 0 deletions tests/test_notes_rag.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from pathlib import Path

from notes_rag.answer import Answerer
Expand Down Expand Up @@ -37,6 +38,15 @@ def test_mock_answer_cites(capsys):
assert "Sources:" in out


def test_mock_json_output(capsys):
code = main(["--mock", "--json", "--notes", str(NOTES), "What is a git remote?"])
out = capsys.readouterr().out
assert code == 0
result = json.loads(out)
assert result["answer"].startswith("[mock]")
assert result["sources"]


def test_empty_notes_dir(tmp_path, capsys):
empty = tmp_path / "empty"
empty.mkdir()
Expand Down