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 0.1.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-0.1.0.tar.gz | 331.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rindo_convert-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 415.5 kB
Release files / rindo_convert-0.1.0.tar.gz
| Download URL | rindo_convert-0.1.0.tar.gz |
|---|---|
| Size | 331.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d8d53aa8f9c3686c8edfef735bc057449959c5e8b88e676e150e72fbe865d69
|
|
BLAKE2b-256 checksum How to use checksums |
a989d6d9e043130a0c70369824c0283f2482c1623f024bda6c77f0f16acd86c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / rindo_convert-0.1.0-py3-none-any.whl
| Download URL | rindo_convert-0.1.0-py3-none-any.whl |
|---|---|
| Size | 83.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0afdfd7291b4bb9790f8cc91a281ad0538bc7edc36cf4e9c0808fcbe6dd2db32
|
|
BLAKE2b-256 checksum How to use checksums |
bb47c202a412ce3ab7240bbc2420777a940ea5df8c96c428639e3dc3ad74525d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|