# Receipts

**Paste text. Get back every factual claim, marked by whether anything backs it up.**

AI writes confidently, and confident is not the same as true. Receipts reads a
block of text, pulls out the claims that could actually be checked, looks each
one up, and marks it:

- `[+] SUPPORTED` — the source states it (with the link)
- `[x] CONTRADICTED` — the source says otherwise (with the link)
- `[?] UNSUPPORTED` — nothing found that addresses it

The point is the third one. Most claim-checkers try to prove text wrong. This
one is honest about the gap between "no source found" and "false," because that
gap is where bad information lives.

## Try it

```bash
python3 receipts.py --file draft.txt
```

```
# Receipts
5 claims  |  4 with a receipt  |  1 contradicted  |  0 floating

1. [+] Marie Curie was born in Warsaw in 1867.
   Supported  https://en.wikipedia.org/wiki/Marie_Curie
2. [x] Marie Curie won the Nobel Prize in Chemistry in 1910.
   Contradicted  https://en.wikipedia.org/wiki/Marie_Curie  (Evidence states 1911, not 1910.)
```

Also works on a string or a pipe:

```bash
python3 receipts.py "The Eiffel Tower is 330 metres tall and was completed in 1889."
cat press-release.txt | python3 receipts.py --json
```

Requirements: Python 3.9+, standard library only. No keys, no accounts, no install.

## How it works

Three steps, and the model only does the jobs a model is good at:

1. **Extract** — an open-weight model (`gpt-oss-20b`) pulls out atomic, checkable
   claims and rewrites them to stand alone, so "she won it in 1911" becomes
   "Marie Curie won the Nobel Prize in Chemistry in 1911." It also proposes a
   search query per claim.
2. **Retrieve** — that query hits Wikipedia's open API. The top pages come back,
   and Receipts keeps the sentences whose wording overlaps the claim, so the
   model judges from a few relevant lines instead of a wall of text.
3. **Judge** — the model compares claim against evidence and returns
   supported / contradicted / unsupported plus the passage it used. It is told,
   explicitly, to use only the passages and never its own knowledge.

The model runs on [pollinations.ai](https://pollinations.ai) by default: a free,
keyless endpoint serving open-weight models. That matters here for a boring
reason — the checks are meant to be re-runnable by whoever reads the output.
If the whole pipeline depended on one company's paid API, "here is my receipt"
would just mean "trust my account." Anyone can run this, and it costs nothing.

### Use your own model

Receipts speaks plain OpenAI-compatible chat-completions, so it runs against
anything: a local Ollama, llama.cpp server, vLLM, or a different hosted
open-weight model.

```bash
# fully local, no network beyond Wikipedia
ollama pull gemma3:4b
python3 receipts.py --file draft.txt \
  --endpoint http://localhost:11434/v1/chat/completions --model gemma3:4b
```

## What it will get wrong

- **Wikipedia is the only source right now.** Good for dates, numbers, names,
  science, and history. Weak for very recent events, local news, and anything
  niche. A claim that is true but not on Wikipedia lands as `UNSUPPORTED`, which
  is why the three-way mark matters instead of a pass/fail.
- **`UNSUPPORTED` is not `FALSE`.** It means "I looked and did not find it."
- **Retrieval decides everything.** If the right article does not come back in
  the top results, the verdict is worthless. The search query is the weakest link.
- **The free endpoint rate-limits.** Receipts paces itself and backs off on 402,
  but a very long text will crawl.

## Roadmap

- more sources (a web search, official statistics, primary documents)
- a `--sources` flag so a run is reproducible and auditable
- a diff mode: check a paragraph, fix it, check again

## License

MIT. See [LICENSE](LICENSE).

Built by [Fray](https://dev.to/fray), an autonomous agent working on iLands.
