rindo-convert — the Rindo converter agent (KB2)
Vision-grounded conversion of hard formats (text-layer PDF, scanned PDF, PPTX)
into reviewable Rindo documents. Invoked by rindo-runner
through the command-template contract; the result is always a REV-8 pending
draft a human approves — the agent can never publish.
The rendered page is the only ground truth. Machine extraction (Docling for PDFs, python-pptx for decks) is a text donor the model may copy characters from — never a structure authority. A deterministic repair pass (ported from the Rindo worker's extraction pipeline, with drift tests) runs on every page regardless of model quality. DOCX / XLSX / CSV / HTML / MD / TXT are declined politely: their machine representation loses ~nothing a render carries.
Operator documentation lives in the Rindo ops runbook §4.7 (register the
agent, mint the runner token, designate converter_agent_id, duration + cost
guidance). This README is the package-local quickstart.
Install (on the runner host)
rindo-convert is Apache-2.0 (LICENSE beside this file) — like
rindo-runner, not part of the proprietary Software. It is published on PyPI:
uv tool install rindo-convert # base install — scanned-PDF + PPTX arms work
uv tool install 'rindo-convert[docling]' # + the Docling scaffold for text-layer PDFs (~5 GB)
# from a source checkout instead: cd agents/converter && uv sync [--extra docling]
Host prerequisites:
- PPTX arm: LibreOffice (
soffice) and CJK fonts covering the deck's script (fonts-noto-cjk; verifyfc-match :lang=jaresolves to a JP face — the render is the ground truth, so a substituted face degrades silently). The tool refuses to run the PPTX arm without a JA-capable font. - PDF arms: nothing — pypdfium2 ships wheels; poppler is not needed.
- An Anthropic API key (
ANTHROPIC_API_KEY, or anant auth loginprofile).
Wire it to the runner
# ~/.config/rindo-runner/config.toml
server_url = "https://rindo.example.com"
token = "rndr_..."
command = "/opt/rindo-convert/run.sh {payload_file}"
poll_timeout = 60
#!/usr/bin/env bash
# /opt/rindo-convert/run.sh (chmod 700 — it holds the API key)
set -euo pipefail
export ANTHROPIC_API_KEY="sk-ant-..."
export RINDO_CONVERT_MODEL="claude-opus-5"
exec /opt/rindo-convert/.venv/bin/rindo-convert "$1"
The runner injects RINDO_SERVER_URL / RINDO_MCP_URL /
RINDO_RUNNER_TOKEN / RINDO_JOB_ID; model and engine knobs ride the
ambient environment (the wrapper). One job at a time per runner token.
Knobs (RINDO_CONVERT_*)
| Var | Default | Meaning |
|---|---|---|
MODEL |
claude-opus-5 |
Vision model. The request shape is capability-gated per model — see config.py's MODEL_CAPS. |
ESCALATE_MODEL |
(unset) | Documented re-request rung for decks whose review effort is too high. |
EFFORT |
high |
output_config.effort on models that take it. |
MAX_PAGES |
60 |
Above it the job declines with a split-the-file message. |
MAX_TOKENS |
3000000 |
Cumulative spend ceiling for the whole job (checked between pages against real usage). |
PAGE_MAX_TOKENS |
64000 |
Per-call max_tokens (thinking shares this cap). |
PAGE_TIMEOUT_SECONDS |
240 |
Per-page wall clock. |
DEADLINE_MARGIN_SECONDS |
90 |
Reserved before the job's deadline_at — stop early and salvage rather than be killed. |
MAX_FAILED_PAGE_RATIO |
0.25 |
Above it, abort instead of submitting a placeholder-riddled draft. |
RENDER_DPI |
150 |
Raster DPI; the long edge is clamped to the model's vision ceiling regardless. A cost knob. |
ENGINE |
docling |
docling | none (pure vision). Docling failure degrades to none with a provenance note. |
DONOR_ESCALATION |
0 |
§5.4 conditional second donor. OFF unless measurement M2 ruled it in. |
SOFFICE |
soffice |
LibreOffice binary path. |
WORKDIR |
(mkdtemp) | Scratch — defaults outside any repo; kept on failure (it holds final.md, the manual-resubmit remedy). |
KEEP_WORKDIR |
0 |
Keep it on success too (debugging, bench). |
DRY_RUN |
0 |
Convert but skip all MCP traffic. The bench's measurement lever. |
FALLBACKS |
default |
Server-side refusal fallback on models that support it (off to disable). |
USAGE_LOG |
~/.cache/rindo-convert/usage.jsonl |
One JSONL line per page (real usage, never estimates). |
LOG_LEVEL / LOG_JSON |
info / 0 |
stderr logging (stdout is the job-summary channel). |
Exit codes (the runner maps non-zero → job failed)
| Code | Meaning |
|---|---|
| 0 | Draft submitted (or content hash-equal to head — a recorded no-op) |
| 1 | Decline: out-of-scope format, page cap, or a pending draft already on the target |
| 2 | Config/environment error (API key, soffice, CJK font, bad env value) |
| 3 | Conversion failure (download, render, model, submit, budget) |
The classified reason is always the final RESULT: line of the output tail
(agent_jobs.result.summary_md on the server), after the usage/cost summary.
Development
uv run pytest -q # offline: no network, no key, no docling, no soffice
uv run ruff check . && uv run ruff format --check .
Ported backend symbols (postpass.py, ooxmlguard.py, the router constants)
are byte-copies with drift tests: tests/test_ported_drift.py reads the
backend sources by relative path and AST-compares. When it fails, re-sync the
copy, re-record the sha, and re-run the postpass goldens. The bench harness
(measurements M1/M2, the judging protocol) lives in bench/ and
writes only outside the repo.
Metadata
Release files for rindo-convert 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rindo_convert-1.0.0.tar.gz | 331.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rindo_convert-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 415.5 kB
Release files / rindo_convert-1.0.0.tar.gz
| Download URL | rindo_convert-1.0.0.tar.gz |
|---|---|
| Size | 331.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
34ec0d01706006a1c4109b9538b0f2b9e177fdc4043ee609c0c69133fe8f2aa4
|
|
BLAKE2b-256 checksum How to use checksums |
1e448c9e28c6eaae6391e91e7f80efaa84ff7567211fcc7563d22743c7022e92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","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 / rindo_convert-1.0.0-py3-none-any.whl
| Download URL | rindo_convert-1.0.0-py3-none-any.whl |
|---|---|
| Size | 83.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c000f0308dbd3cc4ab1ad0ee840270bc9e79b01e8a859ac97722e55f1fffad9e
|
|
BLAKE2b-256 checksum How to use checksums |
dd8c8c4d298a65fc4f15c3564641a34440dc2193ceb149ce8f9a6d811689a457
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.24 {"installer":{"name":"uv","version":"0.12.24","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}
|