paritex
Reconstruct papers as LaTeX with an AI backend, re-render with tectonic, and measure how faithful the reconstruction is.
Companion to hallubib: hallubib asks are your references real?, paritex asks is this LaTeX really your paper?
A paper's source is not recoverable from its rendering, so what the backend produces is a guess — paritex makes the guess earn its keep. Every round must survive three checks: tectonic must compile it, a structural bibliography gate rejects any round whose refs.bib is missing, empty, inlined as thebibliography, or short of the keys the text cites, and word-level parity against the original scores what survived. Failures feed back to the backend; the surviving evidence lands in report.json. Whether the references name real papers is hallubib's question, asked of the refs.bib this contract guarantees exists.
Usage
paritex fetch # download the demo paper set into papers/
paritex reconstruct papers/bitcoin.pdf --rounds 3 # init + AI reconstruct + render + gate + eval
reconstruct on a PDF creates a reconstruction project directory (bitcoin/) and runs the loop: AI backend writes main.tex (+ refs.bib), tectonic renders it to main.pdf, the bibliography gate checks structure, and the result is compared word-level against the original. Render failures, gate violations, and text divergences are fed back to the backend for up to --rounds passes, stopping early at --target parity; a render or gate failure on the final round raises instead of passing quietly.
A project directory is self-contained:
bitcoin/
original.pdf # the source paper
assets/ # raster images extracted from it, for \includegraphics
main.tex # the reconstruction (backend-written)
refs.bib # its bibliography — always a real file, enforced by the gate
main.pdf # tectonic render
report.json # parity ratio, page counts, all divergences
The file names are the produced-files contract: consumers import ORIGINAL, MAIN_TEX, REFS_BIB, REBUILT, ASSETS, REPORT from paritex instead of restating them, and paritex treats files it did not create as opaque, so a consumer can layer its own files into the same directory.
The steps are also available separately:
paritex init papers/attention.pdf # just create the project dir + extract assets
paritex render attention # tectonic main.tex
paritex eval attention # compare main.pdf to original.pdf
paritex parity original.pdf other.pdf # bare two-PDF comparison
The report
report.json is a serialization contract — version-stamped, deterministically ordered, meant to be committed and rendered by consumers:
{
"paritex_version": "0.1.0",
"parity": {
"ratio": 0.992,
"divergences": [
{"kind": "missing", "original": "...", "rebuilt": "...", "page": 3}
]
},
"pages_original": 15,
"pages_rebuilt": 15
}
kind is missing / added / changed, page is where the divergence sits in the original — enough context to show which parts of the paper survived reconstruction verbatim.
Backends
The AI backend is a configured command; three are built in (paritex backends lists them):
claude-code(default) — runs the Claude Code CLI as an agent inside the project dir; it readsoriginal.pdfitself, writesmain.tex/refs.bib, and may run tectonic to self-check. Auth: the box's Claude Code login. The Anthropic auth env vars (ANTHROPIC_API_KEY,ANTHROPIC_AUTH_TOKEN) are scrubbed from the child environment, so a key exported in your shell can never silently take precedence over the login and bill your API account.claude-api— the same agent command, but billed to the API: it refuses to start unlessANTHROPIC_API_KEYis set, and passes it through. Spending credits is an explicit choice of backend, never a side effect of the environment.claude-gen— plain one-shot generation on the box login: the prompt (with the extracted paper text) goes in, LaTeX comes out on stdout; the bibliography arrives as afilecontents*block that paritex materializes asrefs.bib.
All three come from one factory, claude_backend(name, auth="login"|"api", allowed_tools=..., mode=...), which consumers use to build their own flavors (e.g. with extra allowed tools). Auth is expressed through three generic spec keys any backend can use: env (a table of variables set for the child), drop_env (variables scrubbed from it), and require_env (variables that must be present or the run fails before spawning).
Any command can be a backend via paritex.toml (looked up in the working directory, then ~/.config/paritex.toml, or passed with --config):
default = "codex"
[backends.codex]
mode = "agent" # the command writes main.tex itself, cwd = project dir
argv = ["codex", "exec", "--full-auto", "{prompt}"]
timeout = 3600
[backends.llm-cli]
mode = "generate" # stdout becomes main.tex (code fences stripped)
argv = ["llm", "-m", "gpt-5"] # no {prompt} in argv -> prompt is piped to stdin
prompt_file = "my-prompt.txt" # override the default prompt template
require_env = ["OPENAI_API_KEY"] # fail loudly before spawning if unset
drop_env = [] # scrub inherited vars from the child
env = { LLM_TEMPERATURE = "0" } # set vars for the child
Per-backend prompt, feedback, compile_feedback, and bib_feedback templates (inline or *_file) override the defaults in paritex/prompts.py; placeholders like {pdf}, {main_tex}, {refs_bib}, {assets}, {text}, {ratio}, {feedback}, {violations}, {log} are substituted literally, so LaTeX braces are safe.
Consumers with their own config carry the same spec shape: parse_backend(name, spec, base) builds a Backend from a mapping with these keys, so one AI-invocation schema serves every tool without a paritex.toml in sight.
Tables, figures, and bibliographies are covered by the default prompts: tables must be rebuilt as tabular content, extracted assets/ images are offered for \includegraphics (with TikZ as the fallback for vector diagrams), and references go through refs.bib — which the gate then enforces and the word-level parity check verifies.
Demo papers
paritex fetch downloads a small set of famous, shortish, layout-diverse papers to exercise the loop: attention (Transformers; single-column, tables), gan, word2vec, resnet (two-column CVPR), gw150914 (LIGO; two-column REVTeX), bitcoin (whitepaper with diagrams).
Python API
from paritex import init_project, load_backends, reconstruct
backends, default = load_backends()
project = init_project(Path("papers/gan.pdf"))
report = reconstruct(project, backends[default], rounds=3, target=0.98, on_event=print)
print(report.parity.ratio, report.pages_original, report.pages_rebuilt)
on_event receives a Progress per step — backend start, render ok/fail, gate ok/fail, parity ratio and divergence count per round — so a consumer can stream run progress to a terminal or UI instead of wrapping the loop in threads.
Metadata
Release files for paritex 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| paritex-0.1.3.tar.gz | 25.7 kB | Without provenance | Details for paritex-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| paritex-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for paritex-0.1.3-py3-none-any.whl |
Total release size: 47.7 kB
Release files / paritex-0.1.3.tar.gz
| Download URL | paritex-0.1.3.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c27b3d19a442525c87b328488362e0c0a3cfafce8a45b4063663fd39e7bdb62
|
|
BLAKE2b-256 checksum How to use checksums |
1a408d042e94bb5f9bf14a34b83b6a72b1a5d57d888dc81d9b4ddc8aefc50933
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / paritex-0.1.3-py3-none-any.whl
| Download URL | paritex-0.1.3-py3-none-any.whl |
|---|---|
| Size | 22.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7b86acf189cf6a36bdf937bf225b60573e420a88616991d40fb419ec5882b853
|
|
BLAKE2b-256 checksum How to use checksums |
d8ff957a2438857c75192b9fd767a8b12c322504c1e73023bbf946fc4d4c2b72
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for paritex 0.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.