Skip to content

dropped_citations is computed but never surfaced in the API response or the CLI #4

Description

@royalpinto007

What we want

Surface dropped_citations in the /ask API response and in the CLI, instead of computing it and throwing it away.

Why it matters

Citation verification is the point of app/generate.py. Its docstring, rule 2, says models "cite chunk 3 while paraphrasing chunk 1", so the code checks what the model claimed against what it was actually given and drops the invented ones (lines 136 to 146). The dropped indices are recorded on the answer:

dropped_citations: list[int] = field(default_factory=list)  # model cited these; we couldn't verify

(app/generate.py, Answer, line 67.)

Nothing ever reads it in a code path a user can see:

  • app/main.py, AskResponse (lines 46 to 52) has answer, answered, citations, conflict, refusal_reason, and query_id. No dropped citations field, and the construction at lines 159 to 168 does not pass one.
  • app/cli.py, _ask (lines 93 to 98) prints the answer, the citations, and the refusal reason. Not the dropped ones.

So the single clearest signal that the model just hallucinated a source is detected, stored on the dataclass, and then silently discarded. A caller cannot tell the difference between "the model cited two sources and both checked out" and "the model cited five and three were invented". That is exactly the signal an operator wants to alert on.

Note the one case where it does escape: no_verifiable_citation at lines 148 to 155 passes dropped_citations through, but only when every citation was invented. The partial case, which test_partially_hallucinated_citations_keep_the_real_ones (tests/test_generate.py line 73) proves is real, is invisible.

How to fix it

  1. Add dropped_citations: list[int] = [] to AskResponse in app/main.py (near line 49), and pass answer.dropped_citations where the response is built (line 159 onward).
  2. In app/cli.py _ask, print a short warning when answer.dropped_citations is non-empty, in the same [yellow] style already used for conflicts and staleness at lines 88 to 91. Something like: unverified citations dropped: [4, 7].
  3. Add a test in tests/test_api.py using the existing client fixture and a FakeLLM response that cites an out-of-range index, asserting the field comes back populated. tests/test_generate.py line 60, test_hallucinated_citation_is_dropped, shows how to script that response.
  4. Update the API table in the README (the /ask row, around line 222) if the response shape is documented there.

Running it

docker compose up -d db
pytest -q

Comment here if you would like to take this one and I will assign it. I usually reply within a day.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions