paper-radar 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. paper_radar-0.1.0/LICENSE +21 -0
  2. paper_radar-0.1.0/PKG-INFO +359 -0
  3. paper_radar-0.1.0/README.md +337 -0
  4. paper_radar-0.1.0/paper_radar/__init__.py +4 -0
  5. paper_radar-0.1.0/paper_radar/__main__.py +3 -0
  6. paper_radar-0.1.0/paper_radar/calibrate.py +99 -0
  7. paper_radar-0.1.0/paper_radar/cli.py +354 -0
  8. paper_radar-0.1.0/paper_radar/cluster.py +125 -0
  9. paper_radar-0.1.0/paper_radar/config.py +388 -0
  10. paper_radar-0.1.0/paper_radar/demo/arxiv_demo.xml +550 -0
  11. paper_radar-0.1.0/paper_radar/demo/radar.demo.toml +48 -0
  12. paper_radar-0.1.0/paper_radar/feedback.py +105 -0
  13. paper_radar-0.1.0/paper_radar/http.py +81 -0
  14. paper_radar-0.1.0/paper_radar/ids.py +26 -0
  15. paper_radar-0.1.0/paper_radar/jev.py +513 -0
  16. paper_radar-0.1.0/paper_radar/models.py +59 -0
  17. paper_radar-0.1.0/paper_radar/notify.py +94 -0
  18. paper_radar-0.1.0/paper_radar/pipeline.py +331 -0
  19. paper_radar-0.1.0/paper_radar/questions.py +95 -0
  20. paper_radar-0.1.0/paper_radar/render.py +430 -0
  21. paper_radar-0.1.0/paper_radar/scoring.py +137 -0
  22. paper_radar-0.1.0/paper_radar/screening.py +282 -0
  23. paper_radar-0.1.0/paper_radar/sources/__init__.py +87 -0
  24. paper_radar-0.1.0/paper_radar/sources/arxiv.py +82 -0
  25. paper_radar-0.1.0/paper_radar/sources/biorxiv.py +81 -0
  26. paper_radar-0.1.0/paper_radar/sources/bluesky.py +157 -0
  27. paper_radar-0.1.0/paper_radar/sources/huggingface.py +102 -0
  28. paper_radar-0.1.0/paper_radar/sources/pubmed.py +186 -0
  29. paper_radar-0.1.0/paper_radar/sources/reddit.py +176 -0
  30. paper_radar-0.1.0/paper_radar/sources/rss.py +118 -0
  31. paper_radar-0.1.0/paper_radar/store.py +162 -0
  32. paper_radar-0.1.0/paper_radar/summarize.py +57 -0
  33. paper_radar-0.1.0/paper_radar.egg-info/PKG-INFO +359 -0
  34. paper_radar-0.1.0/paper_radar.egg-info/SOURCES.txt +47 -0
  35. paper_radar-0.1.0/paper_radar.egg-info/dependency_links.txt +1 -0
  36. paper_radar-0.1.0/paper_radar.egg-info/entry_points.txt +2 -0
  37. paper_radar-0.1.0/paper_radar.egg-info/requires.txt +3 -0
  38. paper_radar-0.1.0/paper_radar.egg-info/top_level.txt +1 -0
  39. paper_radar-0.1.0/pyproject.toml +41 -0
  40. paper_radar-0.1.0/setup.cfg +4 -0
  41. paper_radar-0.1.0/tests/test_claude_backend.py +178 -0
  42. paper_radar-0.1.0/tests/test_cli.py +100 -0
  43. paper_radar-0.1.0/tests/test_config_scoring.py +175 -0
  44. paper_radar-0.1.0/tests/test_huggingface.py +108 -0
  45. paper_radar-0.1.0/tests/test_jev_http.py +156 -0
  46. paper_radar-0.1.0/tests/test_news_sources.py +414 -0
  47. paper_radar-0.1.0/tests/test_pipeline.py +405 -0
  48. paper_radar-0.1.0/tests/test_pubmed_screening.py +365 -0
  49. paper_radar-0.1.0/tests/test_sources.py +125 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paper Radar contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,359 @@
1
+ Metadata-Version: 2.4
2
+ Name: paper-radar
3
+ Version: 0.1.0
4
+ Summary: Let Jev read every new paper on arXiv and bioRxiv each day, then show you the few worth reading. Plain-English interests, calibrated probabilities, cents per day.
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/Eliot5566/JEV-Paper-Radar
7
+ Project-URL: Issues, https://github.com/Eliot5566/JEV-Paper-Radar/issues
8
+ Keywords: arxiv,biorxiv,papers,research,literature,recommendation,rss,jev,typesafe,system-one
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8; extra == "dev"
21
+ Dynamic: license-file
22
+
23
+ <div align="center">
24
+
25
+ # ◎ Paper Radar
26
+
27
+ **Jev reads every new paper on arXiv each morning. You read the few that matter.**
28
+
29
+ Plain-English interests · calibrated probabilities · about 6 cents a day for *all* of arXiv · fork and go, no server
30
+
31
+ [Quick start](#quick-start-5-minutes-no-server) · [Try the demo](#try-it-in-10-seconds) · [How it works](#how-it-works) · [Benchmark](#measured-against-real-reviewers-decisions) · [繁體中文](README.zh-TW.md)
32
+
33
+ <img src="docs/demo.png" width="720" alt="Paper Radar daily page: 50 papers read, 12 shortlisted, 4 must-read">
34
+
35
+ <sub>A real run: 50 new arXiv papers judged by Jev in 5 seconds for $0.002. No key? <code>paper-radar demo</code> shows the same page offline.</sub>
36
+
37
+ </div>
38
+
39
+ ---
40
+
41
+ arXiv now receives more than 30,000 papers a month ([32,040 in June 2026](https://blog.arxiv.org/2026/07/09/arxiv-now-hosts-over-3-million-articles/)), about 1,500 per weekday announcement. Nobody reads the listing any more. Keyword alerts miss papers that use different words, embedding recommenders quietly drop whatever does not look like your past reading, and running a chat LLM over the whole firehose every day is slow and costs real money.
42
+
43
+ Most AI tools read a paper and write you a summary. **Paper Radar does not write anything.** It asks [Jev](https://typesafe.ai/blog/introducing-system-one-models-and-jev) — TypeSafe's System One model — one yes/no question per interest and gets a probability back: *introduces an agent benchmark, 0.97*. Your repo's code decides what that means.
44
+
45
+ Judgement instead of prose is what makes the arithmetic work. There is nothing to parse and no invented labels, and one judgement costs a fraction of a generated sentence — cheap enough to read *all* 1,500 papers rather than a pre-filtered handful, which is where keyword alerts and embedding recommenders lose the paper you actually wanted.
46
+
47
+ | | Keyword alerts | Embedding recommenders | LLM digest bots | **Paper Radar** |
48
+ |-----------------------|:--:|:--:|:--:|:--:|
49
+ | Reads every new paper | ✅ | ✅ | usually only a pre-filtered top-k | ✅ |
50
+ | Understands meaning | ❌ | partly | ✅ | ✅ |
51
+ | Explains *why* a paper matched | keyword | ❌ | free text | per-interest probability |
52
+ | Thresholds you can tune and check | ❌ | ❌ | ❌ | ✅ `calibrate` |
53
+ | Cost to read all of arXiv daily | free | free after setup | higher: you pay for generated text | **≈ $0.06 per day\*** |
54
+
55
+ <sub>\* Measured, not guessed: a real run on 2026-09-23 read 50 papers in 5 seconds for 46,584 input tokens and $0.0020 (932 tokens per paper, `jev-1.13.0`). All of arXiv, about 1,500 papers per weekday, works out to roughly $0.06 a day. Output tokens are free. Every run logs its own tokens and cost in `data/runs.jsonl`; `paper-radar check` estimates your profile before you spend anything.</sub>
56
+
57
+ **Checked against real reviewers, not just claimed:** on four Cochrane reviews it had never seen, screening 19,447 records reproduced **96.9% of the studies the reviewers included** while removing 78% of the reading, for $0.60. The predictions that were wrong on the way there are published too — [see the benchmark](#measured-against-real-reviewers-decisions).
58
+
59
+ ## Subscribe to one before you set anything up
60
+
61
+ Seven radars run here every weekday. Take the RSS link for your field — nothing to install, no key, no account:
62
+
63
+ **https://eliot5566.github.io/JEV-Paper-Radar/public/**
64
+
65
+ | Feed | What it reads |
66
+ |---|---|
67
+ | [AI](https://eliot5566.github.io/JEV-Paper-Radar/public/ai/) | cs.AI, cs.CL, cs.LG — the broad one |
68
+ | [Agents](https://eliot5566.github.io/JEV-Paper-Radar/public/agents/) | tool-using and web-browsing LLM agents |
69
+ | [Efficiency](https://eliot5566.github.io/JEV-Paper-Radar/public/efficiency/) | faster, smaller, cheaper inference |
70
+ | [Robot learning](https://eliot5566.github.io/JEV-Paper-Radar/public/robotics/) | cs.RO |
71
+ | [Neuroscience](https://eliot5566.github.io/JEV-Paper-Radar/public/neuro/) | bioRxiv neuroscience + q-bio.NC |
72
+ | [Clinical AI](https://eliot5566.github.io/JEV-Paper-Radar/public/clinical/) | PubMed — models evaluated on patients |
73
+ | [Signal](https://eliot5566.github.io/JEV-Paper-Radar/public/signal/) | tech news, Reddit and Bluesky — with the retellings removed |
74
+
75
+ Each is an ordinary config file in [`radars/`](radars). Fork the repo and yours will be narrower, and much more useful.
76
+
77
+ ## What you get every morning
78
+
79
+ - **A web page** on GitHub Pages: must-read and maybe lists, near misses, and what your exclusions filtered out
80
+ - **An RSS feed** (`feed.xml`) for any reader, including Zotero (*File → New Feed*), so picks land next to your library
81
+ - **Optional digests** on Slack, Discord, Telegram or email
82
+ - **Optional one-sentence TL;DRs** in your language, written by any LLM for the top 10 papers only
83
+ - **An audit trail**: every decision, probability and model version is committed to `data/`
84
+
85
+ ## Quick start (5 minutes, no server)
86
+
87
+ 1. **Fork this repo.** Then open the **Actions** tab of your fork and click *enable workflows* (GitHub turns them off in new forks).
88
+ 2. **Describe your interests** in [`radar.toml`](radar.toml): one plain-English statement per interest.
89
+ ```toml
90
+ [[interests]]
91
+ id = "agent_eval"
92
+ label = "Agent evaluation"
93
+ text = "Benchmarks or methods for evaluating LLM agents on multi-step tool-use tasks"
94
+ ```
95
+ 3. **Add a key** under *Settings → Secrets and variables → Actions*:
96
+ - `TYPESAFE_API_KEY` from [console.typesafe.ai](https://console.typesafe.ai/settings/keys), **or**
97
+ - `OPENROUTER_API_KEY` if you are still on TypeSafe's waitlist. Set `backend = "openrouter"` and `model = "typesafe/jev-1.13"` in `radar.toml`; [OpenRouter serves Jev without a waitlist](https://openrouter.ai/typesafe/jev-1.13), **or**
98
+ - `ANTHROPIC_API_KEY` to judge with Claude instead of Jev (see below). Set `backend = "claude"` in `radar.toml`.
99
+ 4. **Turn on Pages:** *Settings → Pages → Source: GitHub Actions*.
100
+ 5. **Run it:** *Actions → Paper Radar → Run workflow* (type `50` in *limit* for a cheap first test).
101
+
102
+ ### Use Claude instead of Jev
103
+
104
+ If you already have an Anthropic API key, Claude can do the judging — same
105
+ pipeline, same calibration step, no Jev waitlist:
106
+
107
+ ```toml
108
+ [jev]
109
+ backend = "claude"
110
+ # model = "claude-haiku-5-5" # the default; set it explicitly to pin a version
111
+ ```
112
+
113
+ ```bash
114
+ paper-radar run --backend claude --limit 50
115
+ ```
116
+
117
+ Two honest warnings. First, **Claude's probabilities are self-reported, not
118
+ calibrated**: Jev returns a calibrated `noul`; Claude returns a number it
119
+ wrote in JSON. Run `paper-radar label` on a few dozen papers and then
120
+ `paper-radar calibrate` to fit your thresholds to Claude before trusting a
121
+ run — the Jev thresholds in `radar.toml` will not transfer. Second, **the
122
+ benchmark numbers in this README are Jev-only**: the Cochrane screening
123
+ result (96.9% recall, 78% work saved on 19,447 records) was measured with
124
+ Jev and does not apply to Claude. The API is also billed separately from a
125
+ Claude Pro/Max subscription, and costs more per paper than Jev; set
126
+ `jev.price_per_mtok` to roughly 0.15 for a Haiku cost estimate, and lower
127
+ `jev.requests_per_minute` if you are on a low API tier.
128
+
129
+ After that it runs on weekdays at 02:00 UTC, right after arXiv's daily announcement. Your radar is at `https://<you>.github.io/<repo>/` and the feed at `/feed.xml`.
130
+
131
+ ## Try it in 10 seconds
132
+
133
+ No key needed. The demo uses fictional papers and a keyword heuristic in place of Jev, so you can see the output before you set anything up.
134
+
135
+ ```bash
136
+ pip install git+https://github.com/Eliot5566/JEV-Paper-Radar
137
+ paper-radar demo # writes ./paper-radar-demo/site/index.html
138
+ ```
139
+
140
+ Run it for real from your laptop:
141
+
142
+ ```bash
143
+ # put your key in a .env file next to radar.toml (it is git-ignored) ...
144
+ echo "TYPESAFE_API_KEY=your-key-here" > .env
145
+ # ... or export it in your shell instead
146
+ paper-radar check # validate radar.toml, lint interests, estimate cost
147
+ paper-radar run --limit 50 # judge 50 papers, build ./site
148
+ ```
149
+
150
+ Real environment variables take precedence over `.env`, so the same commands work unchanged in CI.
151
+
152
+ ## Write interests Jev can answer well
153
+
154
+ Jev reads your words literally ([Jev 1.13 jaggedness notes](https://docs.typesafe.ai/model-jaggedness/jev-1.13)). `paper-radar check` warns about the common traps.
155
+
156
+ | Do | Don't |
157
+ |----|-------|
158
+ | One idea per interest — for *interests*, which combine with `max` | "RL for robots and also LLM agents" → split into two |
159
+ | Put negatives in `[[exclude]]`, phrased positively: *"The main application is medical imaging"* | *"Agents, but not robotics"* |
160
+ | Describe the paper: *"Proposes a benchmark for …"* | Ask for counts or dates: *"published after 2024"* |
161
+ | Use `weight = 0.5` for nice-to-have topics | Write ten near-duplicate interests |
162
+
163
+ Measured on one day of 299 papers: the two interests worded as one crisp idea each
164
+ ("introduces a benchmark or an evaluation method", "makes inference faster or cheaper")
165
+ produced 14 confident hits each. Three vaguer ones in the same profile — including
166
+ "clearly outperforms previous approaches on a widely used task" — never crossed 0.95 at all.
167
+
168
+ ## How it works
169
+
170
+ ```mermaid
171
+ flowchart LR
172
+ A[arXiv RSS<br/>bioRxiv / medRxiv<br/>any RSS] --> B[dedupe against<br/>last 14 days]
173
+ B --> C["Jev · one call per paper<br/>Noul per interest + exclusion<br/>Choice: paper type<br/>Noul: code released?"]
174
+ C --> D[your code decides<br/>max / noisy-OR<br/>thresholds · exclusions]
175
+ D --> E[site + RSS]
176
+ D --> F[Slack · Discord<br/>Telegram · email]
177
+ D --> G[data/ audit trail]
178
+ D -.top 10 only.-> H[optional LLM<br/>TL;DR in your language]
179
+ ```
180
+
181
+ Paper Radar follows TypeSafe's own design patterns:
182
+
183
+ - **Atomic questions, composed in code.** Each interest is a separate Noul. Relevance is `max(weight × p)` by default, or noisy-OR if you want papers that match several interests to rank higher.
184
+ - **Speculative fan-out.** All questions for a paper go in one call; more interests add tokens but almost no latency.
185
+ - **Retrieve, then judge.** Jev sees only the title, abstract and categories. Author names and affiliations are left out because they don't bear on the topic.
186
+ - **Cascade.** Jev decides what deserves attention across thousands of papers. A generative model, if you turn it on, writes one sentence for the ten that made the cut.
187
+ - **Confidence-gated output.** The must-read, maybe and near-miss bands come from thresholds you set and can verify with `calibrate`.
188
+
189
+ ## Teach it what you like, in one click
190
+
191
+ Every paper on your page carries 👍 / 👎 links. Clicking one opens a pre-filled GitHub issue
192
+ titled `radar-label: <paper id> yes`; submitting it is the whole interaction. The next run folds
193
+ those issues into `data/labels.jsonl`, closes them, and `calibrate` fits your thresholds to them.
194
+
195
+ In GitHub Actions the links point at your own repo automatically (`GITHUB_REPOSITORY`), so a fork
196
+ needs no configuration. Locally, set `output.feedback_repo = "owner/name"` or run
197
+ `paper-radar harvest --repo owner/name`.
198
+
199
+ ## Calibrate it to *you*
200
+
201
+ TypeSafe's accuracy and calibration figures are self-reported. Check them on your own judgement instead:
202
+
203
+ ```bash
204
+ paper-radar label 2609.01234 yes # or an arXiv URL
205
+ paper-radar label 2609.04321 no
206
+ paper-radar calibrate --precision 0.9 --recall 0.9
207
+ ```
208
+
209
+ `calibrate` reports the Brier score, the expected calibration error, a precision and recall table, and the `must_read` and `maybe` thresholds that hit your targets. Label about 30 papers, including some from the near-miss list.
210
+
211
+ ## Sources
212
+
213
+ | type | what | notes |
214
+ |------|------|-------|
215
+ | `arxiv` | official RSS, any categories, `["*"]` = every archive | new submissions and cross-lists; revisions skipped |
216
+ | `biorxiv` / `medrxiv` | public details API | version-1 preprints, optional category filter |
217
+ | `pubmed` | NCBI E-utilities, any PubMed query | errata and comments skipped; set `NCBI_API_KEY` to go from 3 to 10 req/s |
218
+ | `huggingface` | Hugging Face Daily Papers | upvoted papers from any arXiv category; same ids as `arxiv`, so overlaps merge and the card shows the upvotes; `min_upvotes` floor |
219
+ | `rss` | any RSS or Atom feed | journals, lab blogs, `hnrss.org`, newsletters, GitHub `releases.atom` |
220
+ | `reddit` | subreddit listings via OAuth | needs Reddit-approved `REDDIT_CLIENT_ID` + `REDDIT_CLIENT_SECRET` (self-serve app creation closed 2025-11, anonymous JSON closed 2026-05); without them use the `rss` source on `/r/<sub>/top/.rss?t=day` |
221
+ | `bluesky` | public AppView author feeds | no key, no account; named accounts only — keyword search needs a login |
222
+
223
+ ```toml
224
+ [[sources]]
225
+ type = "pubmed"
226
+ query = '"atrial fibrillation"[Title/Abstract] AND "anticoagulant"[Title/Abstract]'
227
+ days = 2
228
+ email = "you@example.com" # NCBI asks callers to identify themselves
229
+ ```
230
+
231
+ Ready-made profiles live in [`profiles/`](profiles): LLM research, neuroscience (bioRxiv), and a whole-arXiv watch for one narrow topic. PRs with your field's profile are very welcome.
232
+
233
+ ### News is a different problem from papers
234
+
235
+ A paper feed has almost no duplicates and every item is a primary source. A news feed is
236
+ the reverse: one announcement, then fifteen outlets reporting the announcement, then
237
+ three subreddits linking the outlets. Asking "is this about AI?" on a feed of AI news
238
+ fires on everything, which filters nothing.
239
+
240
+ So [`radars/signal.toml`](radars/signal.toml) asks about provenance instead of topic —
241
+ *is this a first-party announcement, does it report a number someone measured, can I use
242
+ it today* — and vetoes coverage-of-coverage, predictions and reaction posts. Before any
243
+ of that, near-duplicates are folded into one story by canonicalised URL plus title token
244
+ overlap: no model, no embeddings, and it runs **before** Jev, so one announcement costs
245
+ one judgement instead of seven. Every folded copy is listed on the card rather than
246
+ dropped.
247
+
248
+ There is no X / Twitter source. X has had no free read API since 2023 and its terms
249
+ forbid reading a timeline through anything else, so Bluesky's public AppView stands in
250
+ for it: author feeds answer with no key, no account and no rate-limit deal. Its
251
+ `searchPosts` endpoint answers 403 unless you are logged in, so the config names accounts
252
+ rather than keywords — and every handle in it was checked against the live API, because
253
+ several obvious guesses turned out to be squatted, empty or parody accounts. The labs
254
+ themselves are not usefully on Bluesky; their own RSS is where first-party announcements
255
+ come from.
256
+
257
+ ## Screening mode, for systematic reviews
258
+
259
+ A review's title-and-abstract stage is the same shape as a daily radar pointed at a search strategy, with one difference that changes everything: a record is eligible only if **every** inclusion criterion holds, and one exclusion criterion disqualifies it. So screening is a separate command with its own scoring.
260
+
261
+ ```bash
262
+ cp profiles/systematic-review.toml review.toml # edit the query and the criteria
263
+ paper-radar screen -c review.toml --limit 50 # a cheap first pass
264
+ paper-radar screen -c review.toml
265
+ ```
266
+
267
+ Each criterion is one Noul. Eligibility is the geometric mean of the inclusion criteria, so every criterion's evidence counts rather than only the weakest one — `combine = "min"` restores the strict weakest-link reading. Every record ever screened is kept in full in `data/screening/`, rejects included, because a review has to account for all of them.
268
+
269
+ > **Writing criteria for screening is the opposite of writing interests.** A radar combines interests with `max`, so one more interest is one more chance to match. Screening is a conjunction, so **one more criterion is one more chance to veto a record**. Write the fewest criteria that capture eligibility, and keep each one to something an abstract can actually answer. Measuring this is how we found it out: splitting compound criteria into more of them took one benchmark topic from 10.0% work saved to 0.1%.
270
+
271
+ The output is the count block PRISMA 2020 asks for:
272
+
273
+ ```
274
+ Records identified 2314
275
+ Duplicate records removed 118
276
+ Records screened 2196
277
+ Records marked ineligible by this tool 1643
278
+ by an exclusion criterion 421
279
+ below the eligibility threshold 1222
280
+ Reports sought for retrieval 194
281
+ Needs manual review (no abstract) 359
282
+ ```
283
+
284
+ PRISMA 2020 has a box for records "removed before screening" by **automation tool exclusions**, reported separately from human decisions. That box is the only one this tool is entitled to fill in.
285
+
286
+ ### Check it before you trust it
287
+
288
+ Screen a few hundred records yourself, label them, and make the tool prove itself against your own judgement:
289
+
290
+ ```bash
291
+ paper-radar label 42777254 yes
292
+ paper-radar screen -c review.toml --report --recall 0.95
293
+ ```
294
+
295
+ It fits the threshold to your recall target and reports **WSS** — work saved over sampling, the metric this literature uses — along with the studies that threshold would have cost you, by ID. Read those before deciding anything.
296
+
297
+ ```
298
+ Suggested threshold 0.372
299
+ Recall at that threshold 95.2%
300
+ Workload saved (WSS) 61.4%
301
+
302
+ You would read about 848 of 2196 abstracts and miss 1 of 21 eligible studies.
303
+ Missed: pubmed:42771903
304
+ ```
305
+
306
+ ### Measured against real reviewers' decisions
307
+
308
+ Twelve Cochrane reviews from the [CLEF eHealth Technology Assisted Reviews 2019](https://github.com/CLEF-TAR/tar) track, whose relevance judgments are made at the **title-and-abstract stage** — the stage this tool works at. Each review's criteria come from its own published selection criteria, quoted verbatim in [`benchmarks/clef_tar_2019/criteria/`](benchmarks/clef_tar_2019/criteria).
309
+
310
+ Four of them were never looked at while any of this was being built or fixed:
311
+
312
+ | Held-out review | Records | Eligible | Recall | Work saved | Cost |
313
+ |---|--:|--:|--:|--:|--:|
314
+ | Antihypertensives: RAS inhibitors vs other classes | 12,319 | 88 | 95.5% | 68.1% | $0.37 |
315
+ | Thromboprophylaxis implementation | 3,574 | 11 | 100% | 95.1% | $0.11 |
316
+ | Cyclodestructive procedures for glaucoma | 2,456 | 12 | 100% | 95.8% | $0.08 |
317
+ | Psychological therapies for depression in COPD | 1,098 | 16 | 100% | 92.5% | $0.03 |
318
+
319
+ **19,447 records, 127 eligible studies, 96.9% recall, 78.0% pooled work saved, $0.60.**
320
+
321
+ The eight reviews used during development did far worse — 38–48% pooled, with individual topics between 0.1% and 82%. **Performance is dominated by the review, not by the tool.** The held-out reviews are large and low in prevalence (0.3–1.5% eligible), which is the regime a real search is in; the development set included 146-record topics where 10% of records were eligible and there was little to save. Treat 78% as what a large search looks like, not as a number you can expect.
322
+
323
+ Three rounds of it, including the predictions that were wrong, are in [`benchmarks/clef_tar_2019/PREREGISTRATION.md`](benchmarks/clef_tar_2019/PREREGISTRATION.md). Rerunning any of it is one command.
324
+
325
+ ### What this is not
326
+
327
+ - **Not a replacement for a human screener.** Published evaluations of automated screening report a mean recall around 93% and a mean WSS@95 of about 55%, and they consistently conclude that these tools belong alongside human reviewers rather than in place of one. Use it as a second screener, or to prioritise the order a human screens in.
328
+ - **The thresholds above are fitted on the same judgments they are scored against.** That is the optimistic case. It says how well the scores *could* separate eligible studies from the rest, not what you get on a review nobody has screened yet.
329
+ - **A third of new PubMed records have no abstract at all.** Measured on 40 consecutive records: 13 had a title and nothing else. Those are never auto-excluded — they go to a manual pile — so the workload saving applies to the records that have something to read.
330
+ - **Pin the model.** A `model` change mid-review is a protocol deviation. Every screening record stores the model that produced it.
331
+ - **Nobody has run this alongside a live review yet.** The benchmark replays decisions that were already made. If you shadow a review in progress, those numbers would be worth a PR more than anything here.
332
+
333
+ ## Honest limits
334
+
335
+ - **Abstracts only.** Jev judges what the abstract says, not the full paper.
336
+ - **Jev is new.** It launched in early access in September 2026. Its benchmark numbers are TypeSafe's own and have not been reproduced independently, so pin `model` and use `calibrate`.
337
+ - **Text in, choices out.** Jev cannot count, compare dates or write prose, so Paper Radar never asks it to.
338
+ - **Adversarial abstracts.** An abstract written to game classifiers can nudge a score. The worst case is one bad pick in your list: nothing is executed on the model's word.
339
+ - **Data sharing.** Titles and abstracts (public data) are sent to your chosen provider. Nothing else is.
340
+ - Not affiliated with TypeSafe AI.
341
+
342
+ ## Roadmap
343
+
344
+ - [x] One-click 👍/👎 on the page via GitHub Issues, harvested into `labels.jsonl`
345
+ - [x] A public demo radar so visitors see real output without a key
346
+ - [x] PubMed source via NCBI E-utilities
347
+ - [x] **Screening mode** for systematic reviews: criteria as Nouls, PRISMA 2020 counts, WSS measured against your own decisions
348
+ - [x] Validated against 12 Cochrane reviews' own title/abstract screening decisions
349
+ - [x] News mode: Reddit and Bluesky sources, with near-duplicate folding before judging
350
+ - [x] Hugging Face Daily Papers source
351
+ - [ ] **Lab mode**: one repo, many members, a page per person plus a shared feed
352
+ - [ ] Citation-claim checks and missing-methods flags for your must-reads
353
+ - [ ] Local open-model backend for fully offline use
354
+
355
+ ## Contributing
356
+
357
+ Profiles, sources and notifiers are small, self-contained files, which makes them good first PRs. Run `pytest` before opening one. See [CONTRIBUTING.md](CONTRIBUTING.md).
358
+
359
+ MIT licensed.