[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-96446":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":10,"trendingCount":15,"starSnapshotCount":15,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},96446,"tax-doc-classifier","kyotofin\u002Ftax-doc-classifier","kyotofin","Tax document page classifier built on Jev decisions. 100% strict accuracy across 261 IRS forms, ~$0.001 per page.","",null,"TypeScript",289,29,1,0,70,210,4.43,"Apache License 2.0",false,"main",true,[],"2026-09-21 02:04:32","# tax-doc-classifier\n\nA tax document classifier built with Jev.\n\nWe ingest thousands of tax documents using an LLM pipeline built last tax season. Jev classifies 100% of our tax document corpus at $0.001 per page — 34× cheaper and 6× faster than that LLM setup. This is the classifier, open sourced.\n\nOne request per page. The page's text goes to [Jev](https:\u002F\u002Fdocs.typesafe.ai), TypeSafe's decision model, which returns a probability over 261 IRS forms and 7 page kinds instead of text. No model is trained and nothing is hosted: the classifier is a JSON file describing each form, generated from the IRS's own PDFs.\n\n```ts\nimport { classifyPage, jevBackend, pdfPageLines } from 'tax-doc-classifier'\nimport criteria from 'tax-doc-classifier\u002Fdata\u002Fcriteria.json' with { type: 'json' }\n\nconst lines = await pdfPageLines('return.pdf', 3)\nconst r = await classifyPage(lines, { backend: jevBackend(), criteria })\n\u002F\u002F r.form            'form-1040-schedule-a'\n\u002F\u002F r.kind            'form_page'\n\u002F\u002F r.formConfidence  0.99\n\u002F\u002F r.gated           true   (formConfidence >= 0.95)\n```\n\nNo model is trained and nothing is hosted. The classifier is a JSON file of form descriptions (`data\u002Fcriteria.json`) and a decision model that reads a page and picks from them. The decision model is [TypeSafe Jev](https:\u002F\u002Fdocs.typesafe.ai), a System One model that returns a probability over options instead of text.\n\n## Setup\n\n1. **Node 20+ and pnpm.**\n2. **poppler** — provides `pdftotext` and `pdfinfo`, used to read PDF pages. Only `classifyPage` on lines of text works without it.\n   - macOS: `brew install poppler`\n   - Debian\u002FUbuntu: `apt install poppler-utils`\n3. **A TypeSafe API key.** Get one at [typesafe.ai](https:\u002F\u002Ftypesafe.ai), then:\n   ```\n   export TYPESAFE_API_KEY=…\n   ```\n   or put it in a `.env` file (git-ignored) and load it with your usual tooling. The key is read from the environment by `jevBackend()`; it is never written to disk or logged.\n4. **Install and check:**\n   ```\n   pnpm install\n   pnpm typecheck\n   pnpm test\n   ```\n\nTo use it as a dependency in another project:\n\n```\npnpm add github:\u003Corg>\u002Ftax-doc-classifier\n```\n\nThe published package ships `dist\u002F` and `data\u002Fcriteria.json`; `pdftotext` is still required on the host for the PDF helpers.\n\n## Results\n\nThe score is **strict**: a page counts as an error if the answer is wrong *or* its confidence is below 0.95. Every number below is reproducible with `pnpm eval`.\n\n| Corpus | Pages | Forms | Wrong | Strict errors | Cost |\n|---|---|---|---|---|---|\n| TaxCalcBench filled forms (W-2, 1040, 1099-x, 1098-x) | 314 | 15 | 0 | **0 (0.00%)** | $0.36 |\n| Blank IRS forms (every TY2025 MeF 1040-series form + 40 information returns) | 753 | 261 | 0 | 38 (5.05%) | $0.86 |\n\nThe 38 low-confidence pages on the blank corpus are instruction pages that name no form, deep pages of corporate forms (5471, 8865, 1118), and single-schedule forms hedging against their parent. None is wrong.\n\n### Against the LLM setup it replaces\n\nMeasured on the same bench pages, same machine, same hour. The previous classifier sends each page as a PDF to Claude Sonnet with the form registry in the prompt; different pages never share the prompt cache, so every page pays the full prompt.\n\n| Per page | Sonnet classifier | Jev classifier | |\n|---|---|---|---|\n| Cost | $0.039 | $0.00115 | **34× cheaper** |\n| Latency, warm | ~3.3 s | ~0.5 s | **6× faster** |\n| Forms it can name | 30 (phrase table) | 261 | |\n\n## How it works\n\n```\nPDF page ──pdftotext──▶ { header, body, footer }\n                              │\n                              ▼  one request\n                     kind  : 7 options\n                     form  : 230 options + not_in_this_list\n                              │\n                              ▼  second request only if form ∈ {5471, 8865, 8933, 1118, 5713}\n                     sub   : that form and its schedules\n                              │\n                              ▼\n        { form, kind, formConfidence, gated, probabilities }\n```\n\n- A blank page is answered without a call.\n- The 230 first-list options are every form as the page prints it. Five corporate\u002Fforeign forms absorb their 35 schedules and get a second, small question; the hierarchy exists only where the page announces the parent more clearly than the schedule.\n- `formConfidence` is the minimum over steps. Gate on it: above the gate, act; below, fall back to whatever you use today.\n\n### Page kinds\n\n`form_page` · `instructions` · `blank` · `cover_sheet` · `state_tax_form` · `broker_or_bank_statement` · `letter_or_other`\n\nThe last three route a page away from the federal form list; they are defined but not yet evaluated.\n\n## Form ids\n\nIds follow the IRS MeF naming, kebab-cased, parent first:\n\n```\nform-1040                 form-1040-schedule-a        form-1040-nr-schedule-nec\nform-1099-int             form-w-2g                   form-5471-schedule-j\nform-1065-schedule-k-1    form-8995-a-schedule-a\n```\n\n`Form 1040 Schedule A` in the IRS accepted-forms list becomes `form-1040-schedule-a`, mechanically and in both directions (`mefNameToId`). Every id matches `ID_GRAMMAR`, and the test suite refuses a `criteria.json` that contains one that does not.\n\nA page number, when a caller asks for it, is a suffix: `form-1040\u002Fp2`. Every prefix of an id is a valid, less specific id. No suffix means the page was not asked, never \"page 1\".\n\n## The data file\n\n`data\u002Fcriteria.json` is generated — never edited by hand:\n\n```\npnpm build-criteria\n```\n\nreads the IRS [accepted forms and schedules](https:\u002F\u002Fwww.irs.gov\u002Ftax-professionals\u002Ftax-year-2025-modernized-e-file-schema-and-business-rules-for-individual-tax-returns-and-extensions) XLSX, downloads each form's PDF from irs.gov, and extracts per form: the printed label, the title, the page count, the parent (for schedules), up to 8 box labels (information returns only), and the sibling forms it must not be confused with. Re-run it when the IRS publishes new revisions.\n\n## Evaluation\n\n```\npnpm eval:download                                 # IRS PDFs → eval\u002Fcorpus\u002Fblank\nTYPESAFE_API_KEY=… pnpm eval --corpus=blank\nTYPESAFE_API_KEY=… pnpm eval --corpus=bench --dir=\u002Fpath\u002Fto\u002Ftax_calc_bench\u002Fty25\u002Ftest_data\n```\n\nEach run prints the strict score, lists every strict error with its confidence and runner-up, and writes the full per-page results to `eval\u002Fresults\u002F`.\n\nThe bench corpus is [TaxCalcBench](https:\u002F\u002Fgithub.com\u002Fcolumn-tax\u002Ftax-calc-bench) and is not redistributed here.\n\n## Plugging in another model\n\n`Backend` is one method: `ask(state, questions) → { answers, inputTokens }` where each answer is a probability distribution over the question's options. `jevBackend` is the only implementation shipped. Anything that can return calibrated probabilities over a labelled option set can implement it.\n\n## Limits\n\n- Text only. Scanned pages need OCR first; a page with no text layer is reported as `blank`.\n- Federal forms only. State forms are detected by `kind` but not identified.\n- English only.\n- Requires `pdftotext` and `pdfinfo` (poppler) on the path for the PDF helpers; `classifyPage` itself takes lines of text and needs neither.\n- The confidence is calibrated on the corpora above. Calibrate on your own pages before choosing a gate.\n\n## Requirements\n\nNode 20+, poppler, a TypeSafe API key in `TYPESAFE_API_KEY`.\n\n## Licence\n\nApache-2.0. See `DATA-LICENSE.md` for the IRS-derived data.\n",2,"2026-09-20 02:30:05","CREATED_QUERY"]