Metadata-Version: 2.5
Name: paritex
Version: 0.1.3
Summary: Check PDF <-> LaTeX parity for paper reconstruction
Project-URL: Homepage, https://github.com/endremborza/paritex
Author-email: Endre Márk Borza <endremborza@gmail.com>
License: Copyright 2026 Endre Márk Borza
        
        Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: pymupdf>=1.28.2
Description-Content-Type: text/markdown

# paritex

[![pypi](https://img.shields.io/pypi/v/paritex.svg)](https://pypi.org/project/paritex/)

Reconstruct papers as LaTeX with an AI backend, re-render with [tectonic](https://tectonic-typesetting.github.io), and measure how faithful the reconstruction is.

Companion to [hallubib](https://github.com/endremborza/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

```bash
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:

```bash
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:

```json
{
 "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](https://claude.com/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`):

```toml
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

```python
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.
