Skip to content

Page Classification

Classify each page of a document against a caller-supplied label set. Single-label (exactly one) or multi-label (any subset). Result populates ExtractedDocument.page_classifications.

  • Routing: assign each page to a downstream queue (“invoice”, “contract”, “id_document”, “receipt”).
  • Filtering: drop or down-rank pages that match a “irrelevant” or “boilerplate” label.
  • Document triage: bucket multi-page PDFs into per-page categories without writing a custom classifier.
Python
import asyncio
from xberg import ExtractInput, extract, ExtractionConfig, PageClassificationConfig, LlmConfig
async def main() -> None:
config = ExtractionConfig(
page_classification=PageClassificationConfig(
labels=["invoice", "contract", "id_document", "receipt"],
multi_label=False,
llm=LlmConfig(model="openai/gpt-4o-mini"),
),
)
result = await extract(ExtractInput(uri="packet.pdf"), config)
for page in result.results[0].page_classifications or []:
chosen = page.labels[0].label
print(f"page {page.page_number}: {chosen}")
asyncio.run(main())

multi_label = false (default) forces the model to return exactly one label per page. multi_label = true lets the model return any subset. Pick the latter when pages can legitimately match more than one category (“invoice” + “purchase_order” on the same page).

Python
from xberg import ExtractionConfig, PageClassificationConfig, LlmConfig
config = ExtractionConfig(
page_classification=PageClassificationConfig(
labels=["invoice", "purchase_order", "delivery_note"],
multi_label=True,
llm=LlmConfig(model="openai/gpt-4o-mini"),
),
)

Override the default classification prompt with a Minijinja template:

Python
from xberg import ExtractionConfig, PageClassificationConfig, LlmConfig
config = ExtractionConfig(
page_classification=PageClassificationConfig(
labels=["invoice", "contract", "id_document", "receipt"],
prompt_template=(
"You are a document triage assistant.\n"
"Classify the page below using these labels: {{ labels }}.\n"
"Multi-label: {{ multi_label }}.\n\n"
"Page text:\n{{ page_text }}"
),
llm=LlmConfig(model="openai/gpt-4o-mini"),
),
)
Variable Description
{{ labels }} The configured label list.
{{ page_text }} The page’s extracted text.
{{ multi_label }} Boolean — true when multi-label.

The output is JSON-schema-enforced: the response must be a JSON array of strings drawn from the configured labels.

ExtractedDocument.page_classifications is Option<Vec<PageClassification>>. JSON shape:

{
"page_classifications": [
{ "page_number": 1, "labels": [{ "label": "invoice", "confidence": 0.94 }] },
{ "page_number": 2, "labels": [{ "label": "purchase_order", "confidence": 0.88 }, { "label": "invoice", "confidence": 0.71 }] }
]
}

labels always carries at least one entry in single-label mode. In multi-label mode it may be empty if the model declines to pick anything.

Pick any liter-llm provider. The provider matrix from LLM Integration applies here. For high-volume classification, gpt-4o-mini, claude-3-5-haiku, and google/gemini-2.0-flash give good cost/accuracy trade-offs.

Set PageClassificationConfig.llm.api_key explicitly, or leave it unset to fall back to the provider’s standard env var (OPENAI_API_KEY, ANTHROPIC_API_KEY, …). See LLM Integration for the full precedence chain.