# DocJev: classifying and splitting document packets with a hosted model

> DocJev is an Apache-2.0 Python library, CLI and local app that reads PDF, DOCX and PPTX files, extracts their text locally with LiteParse, and asks the hosted Jev service to assign a category or find the boundaries between the documents packed into one file. It suits a team ingesting public filings, and the decision step is a network call rather than a local model.

**jerryjliu/docjev** — A very fast document classifier/splitter using Jev 

- Repository: https://github.com/jerryjliu/docjev
- Stars: 534 · Forks: 35
- Language: Python
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/jerryjliu-docjev

## What it does, and the three verbs

DocJev does document classification and splitting with Jev, LiteParse and optional LlamaParse, and the readme gives it to you as three verbs.

Classify means one document in and one category out, with probabilities and review flags. Split means one packet in and an ordered list of categories and page ranges out, with optional per-document PDF export. And Measure means separate OCR and decision timing, provider usage, and a small-LLM comparison.

The inputs are PDFs, DOCX and PPTX files, and the categories are given as natural-language rules rather than as a trained model. The library, the CLI and the local app all take the same shape of input, which is a sensible design because the three are for different moments: a library call from a pipeline, a command from a shell, and an application for looking at what the tool decided.

The mechanism has two halves. LiteParse extracts complete page text locally, and Jev predicts the document category or the boundaries between its component documents. Optional LlamaParse tiers provide cloud OCR for difficult inputs, and that is the only place the third provider appears.

There is an explicit independence statement, and it matters for a tool in this space. This is an independent open-source implementation, it does not call LlamaIndex's hosted Classify or Split APIs or use their implementation, and LlamaParse is used only for optional OCR. The project also states the limit of its own design: Jev is a hosted service, and local OCR does not make inference offline.

The licence is Apache-2.0, the primary language is Python, and the last push to the repository was on 2026-09-26. There are no GitHub releases, and the package version in the project metadata is 0.1.0.

## The real-document example, which is the case that matters

The demonstration uses original IRS, Treasury, BEA and SEC publications, and the example chosen is a good one because it is hard.

The packet is fifteen pages and it contains two adjacent Treasury auction results with the same category. A naive splitter that groups by category would merge them. The splitter identifies them as separate documents, and it keeps a ten-page BEA release, including its dense statistical tables, together rather than fragmenting it.

Those two behaviours are the difference between a splitter that works on the demo set and one that works in production. The first is about telling two documents of the same type apart, which needs a signal beyond the category, such as the document reference. The second is about not splitting a document with a layout that is unlike the others, because a ten-page statistical release with dense tables is exactly the kind of thing that gets chopped up by a boundary detector that keys on visual changes.

The same real-document set is used for the measured results, and the numbers are reported in a way that is more useful than a single score. The separate 40-document accuracy pilot is complete, both engines classified 40 of 40 originals correctly, Jev split 7 of 8 packets exactly and the baseline split 8 of 8. The video demonstrations are kept as separate evidence from that pilot, which is a distinction that costs the author a rhetorically better claim and buys the reader a clearer picture.

And the project is honest about what is next. The next 40-document challenge targets scans, ambiguous categories and attachment boundaries, and it is explicitly described as a preparation protocol, not another measured result. The obvious hard cases, a scan, a category that is genuinely ambiguous, a document with an attachment, are exactly the ones where the current 40 of 40 figure tells you nothing.

The visual report is linked as well, with 40 real PDFs, eight packets, and the Jev and Luna accuracy and decision latency, and the report lets a reader browse all 40 documents, compare packet boundaries, replay recorded median timings and inspect the one extra split. Being able to look at the single failure is the most useful thing a benchmark report can offer.

## The rules, and why they are prose

The categories are defined in a YAML file with stable category identifiers and descriptions of document purpose, and the example in the documentation is short enough to read in full:

```yaml
categories:
  - id: invoice
    description: A seller's request for payment for goods or services already supplied.
  - id: purchase_order
    description: A buyer's authorization to supply goods or services.
  - id: other
    description: Content that does not fit the defined categories.
instructions: Classify the whole document by its main purpose.
splitting_instructions: Keep continuation pages together. Different invoice references identify separate documents, even when adjacent documents share a category.
```

The design point is the splitting instructions sentence. A rule that says different invoice references identify separate documents is a rule that captures the exact heuristic the real packet example exercises, and it is stated in the same language as everything else, so it can be changed without touching code.

There is also a convenience worth knowing about. The other category is added automatically if it is omitted, because a classifier with no catch-all has to invent a category, and a catch-all is almost always what you want as the default.

The real demo ships its own rules, with the classification rules distinguishing tax forms, transaction-specific financial reports, narrative press releases and legal notices. That list is a good sanity check on the approach, because those four categories are genuinely different in structure, and the difference between a tax form and a press release is exactly the kind of thing a prose description can express and a fixed label set cannot.

So the configuration is a small prompt, per category, plus two instruction lines. It is a different maintenance model from a classifier, and the trade is that you are asking a hosted model to apply your description rather than learning a fixed set of labels.

## Installing it, and the environment variable that catches everyone

The quick start needs Python 3.11 or later and a TypeSafe API key, and the install is three commands.

```sh
git clone https://github.com/jerryjliu/docjev.git
cd docjev
uv sync
uv run docjev doctor --smoke
```

The doctor command with the smoke flag is a diagnostic run that checks a raster-only PDF, and it exists because the first parsed scan may download English OCR language data, which is exactly the kind of thing that fails on a first run with a confusing error.

The naming story is worth knowing because it is layered. The package and the primary CLI are named docjev, and jev-docs remains a compatibility alias, while the Python imports are jev_docs, the environment variables keep the JEV_DOCS_ prefix, and the .jev-docs/ cache directory keeps its name. So a rename happened, and the project kept the old identifiers working for anything that referenced them, which is the difference between a rename and a break.

The environment variables are the second trap. You export TYPESAFE_API_KEY in your shell before running a Jev decision, and the .env.example file lists the supported variables with deliberately empty values. The CLI does not automatically load .env files. LLAMA_CLOUD_API_KEY and OPENAI_API_KEY are optional, for cloud OCR and the baseline respectively, and the documentation asks you to keep actual values outside version control and recordings, which matters for a project that publishes demonstration videos.

The example commands show the two modes with real files:

```sh
# Classify an original ten-page BEA release.
uv run docjev classify examples/real/originals/r04.pdf \
  --rules examples/real/classify/rules.yaml

# Split the assembled packet and export each original publication.
uv run docjev split examples/real/public-finance-packet.pdf \
  --rules examples/real/split/rules.yaml \
  --export-dir output/real-segments \
  --output output/real-split.json
```

The behaviour around output is defined precisely. Output files are preserved by default and --overwrite has to be given explicitly to replace them, results go to stdout and progress and errors to stderr, and directory classification writes one success or error record per supported file and returns a nonzero exit status if any file fails. That last combination is what makes the tool usable in a batch job.

## Format handling, the local app, and how the project is maintained

Two practical details decide whether the tool works in your pipeline.

The first is LibreOffice. DOCX and PPTX require it, and their page numbers refer to a retained PDF rendering, with DOCX pagination following that rendering and PPTX using one page per slide. Native PDF input does not need LibreOffice. The page contract is therefore explicit rather than emergent, and the documentation points at a compatibility and page contracts document, because a page number is the unit the split boundaries are expressed in and a page number that moves between systems is a silent data corruption.

The second is the local visual app, which is the best way to understand what the tool does. It is installed with the demo and baseline extras and started with uv run docjev demo, and it serves on port 8765. It defaults to the original public documents, with editable categories, source previews, live classification and splitting, and PDF downloads, and the synthetic fixtures load when JEV_DOCS_DEMO_SET=synthetic is set. The comparison panel labels saved pilot results and their sample size, and the keys stay on the local server. There is also a race page that prepares the LiteParse text once and then launches both models together, with the model decision times excluding OCR, which is displayed separately.

The last thing to note is the maintenance statement, and it is unusual enough to deserve a mention. The project is maintained entirely by Codex, including issue triage, pull request reviews and merges, and the stated priorities are correctness, backward compatibility and keeping the codebase small.

An automated maintainer is a new proposition, and the priorities listed are the ones you would choose if you were trying to make that safe. Correctness and backward compatibility are the two things that a rename or a behavioural change would break, and keeping the codebase small is the thing that makes an automated reviewer reliable rather than a source of endless churn. The compatibility alias, the retained import names, the preserved output files and the opt-in live test marker are all consistent with those priorities.

The repository layout follows the same split, with src/ for the package, tests/, benchmarks/ for the pilot and the results, datasets/ for the challenge set, docs/ for the reports and media, examples/ for the real and synthetic fixtures, research/, scripts/, plans/ and a .python-version file, plus a NOTICE and a SECURITY.md.

## Conclusion

DocJev is worth evaluating if you receive documents that arrive in packets, because the splitting mode is the part that is genuinely hard to build and the one the project demonstrates on real filings, and because the category rules are plain prose in a YAML file rather than a trained classifier, so a new document type is a paragraph rather than a retraining run. It is the wrong tool if your documents arrive one at a time, and it is the wrong tool if the decision has to happen without a network. Before you rely on it, check three things: that you have a TypeSafe API key and accept that the classification and the split boundaries are made by a hosted service, because the documentation is explicit that Jev is a hosted service and that local OCR does not make inference offline; that you check the evidence rather than the demos, because the completed accuracy pilot reports that both engines classified 40 of 40 originals correctly while Jev split 7 of 8 packets exactly and the baseline split 8 of 8, and that the next 40-document challenge of scans, ambiguous categories and attachment boundaries is described as a preparation protocol rather than another measured result; and that you understand the packaging, since the CLI does not load .env files automatically and the environment variables have to be exported in your shell, which is the step that turns a first run into a confusing authentication error.

## FAQ

### What is DocJev and what does it need to run?

It is a Python library, CLI and local app that classifies and splits PDF, DOCX and PPTX documents. It needs Python 3.11 or later and a TypeSafe API key, because Jev is a hosted service and the decision step is a network call, although LiteParse extracts the text locally and LlamaParse is only used for optional cloud OCR.

### How are the document categories defined?

In a YAML rules file with stable category identifiers and prose descriptions of each document's purpose, plus an instructions line for classification and a splitting_instructions line for boundary rules. The other category is added automatically if it is omitted, and the real demo ships rules distinguishing tax forms, transaction-specific financial reports, narrative press releases and legal notices.

### What are the measured accuracy results for DocJev?

The separate 40-document accuracy pilot is complete: both engines classified 40 of 40 originals correctly, Jev split 7 of 8 packets exactly and the baseline split 8 of 8. The next 40-document challenge of scans, ambiguous categories and attachment boundaries is described as a preparation protocol and not another measured result, and the visual report lets a reader inspect the one extra split.

## Sources

- [Issues](https://github.com/jerryjliu/docjev/issues)
- [jerryjliu/docjev on GitHub](https://github.com/jerryjliu/docjev)
- [License: Apache-2.0](https://github.com/jerryjliu/docjev/blob/main/LICENSE)
- [README](https://github.com/jerryjliu/docjev/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jerryjliu-docjev
