Skip to main content

paritex

pypi

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 reads original.pdf itself, writes main.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 unless ANTHROPIC_API_KEY is 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 a filecontents* block that paritex materializes as refs.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.

Release files for paritex 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paritex 0.1.2
File Size Uploaded
paritex-0.1.2.tar.gz 25.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for paritex 0.1.2
File Interpreter ABI Platform
paritex-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 47.0 kB

Release files / paritex-0.1.2.tar.gz

Download URL paritex-0.1.2.tar.gz
Size 25.4 kB
Tags Source
SHA-256 checksum
How to use checksums
ef333b9aebea061d7437facc9797a0b0593a61598810b5ed9fc1344871d85d3c
BLAKE2b-256 checksum
How to use checksums
001362e3bccb057fd2a5cb7f38301d7e2808e3ac50f6aa4a6e14f2f32ff75883
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.2-py3-none-any.whl

Download URL paritex-0.1.2-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e37910a7c1a7457e1664b09abe8ad9f88379f44c24c271099f7f0326d0564b40
BLAKE2b-256 checksum
How to use checksums
16a6ca099b58325313625cf42b76b6e3f6c7b9a510f8543862dba2d126063872
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 history Release notifications | RSS feed

0.1.3

2 release files

This release

0.1.2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page