Python library
Use chew() to extract a paper or a batch, and Chewer to reuse a pipeline
across calls. import bibr stays light: pipeline stages and their ML
dependencies are loaded lazily.
One file
import bibr
result = bibr.chew("paper.pdf")
result.title # metadata fields pass through as attributes
result.references # list of dicts (alias for the schema's "bib" table)
result.save("paper.json")
chew() returns a Result, an attribute-based view over the export dict
with a validated v11 PaperExport model available as result.model.
Table-shaped keys (bib, author, text, section, url, bib_match,
xref, figure, table, eq) come back as Records, a list subclass
with a .df convenience property for pandas:
result.references.df # bib rows as a pandas DataFrame
result.authors.df # alias for the "author" table
result.data # the raw export dict backing the Result
result.model.metadata.title # typed Pydantic model access
references, authors, and sections are friendly aliases for the
schema's bib, author, and section tables; metadata fields (title,
doi, …), source fields (file_name, file_hash, input_format), and
remaining top-level keys (paper_id, extraction, …) resolve as attributes
too.
Batch
Directories and lists of paths run as a batch on a single pipeline, reusing resources according to the selected memory mode:
results = bibr.chew(["a.pdf", "b.pdf"]) # list of paths -> order-aligned list
results = bibr.chew("papers/") # sorted supported files, non-recursive
A failure in one file doesn't abort the batch — its slot holds a
ChewFailure instead of a Result, in place, so the output list always
lines up with the input:
good = [r for r in results if r.ok] # ChewFailure.ok is False
for r in results:
if isinstance(r, bibr.ChewFailure):
print(r.path, r.error, r.failed_stage)
For a list input, results keep the input order. For a directory input,
files are processed in sorted order and anything that isn't
.pdf/.docx/.xml/.html/.htm/.epub is skipped. An empty list returns []; a directory
with no supported files raises ValueError. Single-file failures
raise an exception instead of returning ChewFailure.
For a fixed return type, use chew_file() and chew_many():
result = bibr.chew_file("paper.pdf") # Result
results = bibr.chew_many(["a.pdf", "b.pdf"]) # list[Result | ChewFailure]
Their async equivalents are achew_file() and achew_many(). The same
four methods are available on Chewer. File-specific methods reject
directories; batch-specific methods take an explicit sequence of paths.
Options
chew() (and Chewer, below) accept keyword options that mirror the bibr
chew CLI flags:
| Option | CLI equivalent | Description |
|---|---|---|
ocr |
--ocr |
OCR backend, e.g. "paddle" (default selector), "paddle-http", "paddle-vllm", or an explicit "glm-*" compatibility backend |
llm |
--llm |
LLM backend: "cloud", or a managed local server ("local", "vllm", "vllm-mlx", "rapid-mlx", "llama-cpp", "llmster") |
memory |
--memory |
Memory mode: "aggressive", "balanced", "keep_all" (default: selected from hardware) |
refs |
--refs |
Reference parser: "ner" (default), "llm" (batched LLM), "llm-chunked" (region-aligned chunk parsing), or "off" / False to skip references |
ref_seg |
--ref-seg |
Reference segmentation strategy: "geom" (default), "region", "llm", "crf" |
no_llm |
--no-llm |
Skip metadata LLM calls, equation extraction, citation linking, and Crossref; native metadata survives. OCR still uses the selected backend |
device |
--device |
Force compute device: "cuda", "mps", "cpu" |
crossref |
--crossref / --no-crossref |
Tri-state: True runs Crossref/resolver reference enrichment for this call, False skips it, omitted/None follows CROSSREF_ENRICH (off by default) |
equations |
--no-equations (inverted) |
Enable/disable equation extraction |
pages |
--pages |
Page range to process, 1-based (e.g. "1-5") |
figure_images |
--figure-images |
Include base64-encoded figure images in the output |
include_regions |
--regions |
Include the extraction.regions debug payload (per-region bbox/font/content) |
include_region_meta |
--region-meta |
Include the per-text _bbox_2d/_font_size/_region_type/... underscore fields (opt-in v4-training metadata, distinct from extraction.regions) |
ocr_url |
--ocr-url |
URL for an external OCR server |
ocr_model |
--ocr-model |
OCR model path or served model alias |
ocr_profile |
--ocr-profile |
"paddle" or "glm"; required when a custom model alias does not identify its family |
start_page, end_page |
--pages |
Lower-level zero-based, inclusive page indices; use these or pages, not both |
paper_id |
--paper-id |
Paper ID override (single-file calls only) |
batch_size |
--batch-size |
Files per chunk in batch processing (batch calls only) |
consolidate |
--consolidate |
Merge accepted Crossref matches into bib before export: True / "fill" fills only missing fields, "replace" also overwrites disagreeing ones; False forces it off |
settings |
.env / environment |
A GlobalSettings instance copied into the pipeline at construction |
refs="off" is equivalent to bibr chew paper.pdf --refs off. It keeps
core metadata and equations while skipping reference segmentation, parsing,
bibliographic citation linking, and inline Crossref enrichment.
Backend and processing defaults also come from .env or environment
settings such as LLM_PROVIDER, REF_SEG_STRATEGY, and
CROSSREF_CONSOLIDATE. File-specific options such as paper_id and pages
are call arguments. See the Settings reference.
Use a settings snapshot to configure independent pipelines without mutating
the global Settings object:
from bibr.config import GlobalSettings
settings = GlobalSettings()
settings.crossref.enrich = False
result = bibr.chew_file("paper.pdf", settings=settings)
Chewer snapshots settings when it is constructed, even though its pipeline
is built on the first call. Later changes to the source settings do not
change an existing session.
To merge Crossref matches after the fact instead of at extraction time, call
.consolidate() on a Result — it returns a new Result and leaves the
original extracted fields untouched. This only merges matches already
present in bib_match; it does not query Crossref:
result = bibr.chew("paper.pdf")
enriched = result.consolidate() # mode="fill" by default
enriched = result.consolidate("replace")
Warm sessions
Calling chew() repeatedly builds and tears down a pipeline (and its
models) every time. For repeated calls over time — notebooks, queue workers
— Chewer keeps a pipeline warm across calls instead:
with bibr.Chewer(ocr="paddle") as chewer:
r1 = chewer.chew("a.pdf")
r2 = chewer.chew("b.pdf") # reuses the pipeline and retained models
Chewer takes the same options as chew(), except paper_id and
batch_size, which move to .chew() / .achew() per call. The pipeline is
built lazily on the first call and released on close() / context exit; you
can also manage it explicitly:
chewer = bibr.Chewer(ocr="paddle")
try:
r1 = chewer.chew("a.pdf")
r2 = chewer.chew("b.pdf")
finally:
chewer.close()
Use one mode per instance: sync .chew() calls drive a private event loop
that async calls don't share. Inside a running event loop, use the async
form instead:
Async
Inside Jupyter or any other already-running event loop, chew() raises —
use the async twin instead:
achew() mirrors chew()'s signature and options exactly, including batch
input (await bibr.achew(["a.pdf", "b.pdf"])) and per-call paper_id /
batch_size.
OCR runtime selection and diagnostics
ocr="paddle" is the default automatic selector. It chooses one runtime at
startup. Linux x86_64 tries paddle-vllm when an NVIDIA GPU with at least
8 GB VRAM is detected, followed by glm-llama; other Linux machines go
directly to glm-llama. Apple Silicon tries paddle-rapid-mlx,
paddle-mlx-vlm, glm-rapid-mlx, then glm-llama.
Fallback is startup-only, so a ready Paddle runtime is not silently replaced
with GLM for a failed recognition call. Pass a concrete ocr="glm-*" value
when GLM is the deliberate choice.
The normal external-Paddle configuration uses the served alias and profile:
Custom ocr_model aliases must also set OCR_PROFILE (paddle or glm) so
the model prompt and normalizer are unambiguous. Paddle table output is decoded
from OTSL to HTML and formulas are normalized to their LaTeX body. To inspect
those transformations, request include_regions=True; extraction.regions
preserves the canonical content and raw_ocr_content. The export
extraction.ocr and OCR cache identity retain the selected
backend/model/profile.
The equivalent per-call configuration is:
result = bibr.chew_file(
"paper.pdf",
ocr="paddle-http",
ocr_url="http://localhost:8080/v1",
ocr_model="my-paddle-model",
ocr_profile="paddle",
)
A bare ocr_url selects the GLM HTTP compatibility path, so specify
ocr="paddle-http" when connecting to Paddle.
The equivalent per-call configuration is:
result = bibr.chew_file(
"paper.pdf",
ocr="paddle-http",
ocr_url="http://localhost:8080/v1",
ocr_model="my-paddle-model",
ocr_profile="paddle",
)
A bare ocr_url selects the GLM HTTP compatibility path, so specify
ocr="paddle-http" when connecting to Paddle.
Escape hatch: LocalPipeline
chew() and Chewer cover single calls, batches, and warm sessions. For
full control over the pipeline — custom orchestration, holding a pipeline
across a larger application, or driving process_chunk() directly — build
LocalPipeline yourself:
import asyncio
from bibr import LocalPipeline
async def main():
pipeline = LocalPipeline(memory_mode="balanced")
try:
return await pipeline.process_file("paper.pdf")
finally:
await pipeline.aclose()
data = asyncio.run(main())
The returned dict matches the bibr v11.0 JSON schema —
chew() wraps that same dict in a Result view. See the
Architecture guide for how the pipeline stages
(validate, input, structure, extract, enrich, export) fit together.
Memory modes
The memory option (--memory on the CLI) controls how aggressively
models are loaded and unloaded to fit available GPU/RAM:
| Mode | Description | Use when |
|---|---|---|
balanced |
Keeps layout + segmenter loaded; OCR stays resident unless a local LLM needs the memory | Default when neither low-memory condition below applies |
aggressive |
Loads/unloads models between phases | Auto-selected for ≤8 GB system RAM or a CUDA GPU with ≤8 GB VRAM |
keep_all |
Keeps models loaded for the duration of the run | Explicit opt-in when all selected models fit in memory |
Leave memory unset to get the auto-detected default; pass it explicitly
(or set PIPELINE_MEMORY_MODE) to override.