Skip to main content

llmground

Your schema validated. The value was still made up.

Structured output libraries guarantee the shape of extracted data. None of them check whether the values are actually in the document. llmground runs after extraction and tells you which fields are real.

from llmground import verify

report = verify(extracted, source_document)

if report.has_hallucinations:
    route_to_human_review(report.ungrounded)

No API calls. No model. No cost. Zero dependencies.

The bug this catches

extracted = {
    "vendor": "ACME Manufacturing Ltd.",
    "total": 2846.25,          # the invoice says 2,486.25
}

Right types. Passes any JSON schema. Passes Pydantic. The number is a transposition the model invented, and it goes straight into your accounting system. Shape validation cannot see this, because there is nothing wrong with the shape.

GROUNDED    vendor = 'ACME Manufacturing Ltd.'
            found: ACME Manufacturing Ltd. 14 Sundar Industrial Estate...
UNGROUNDED  total = 2846.25
            nearest number in source is '$2,486.25'

6/8 fields grounded (75%), 0 weak, 2 ungrounded

Install

pip install llmground

It works with what you already use

llmground doesn't replace Instructor, Outlines, or PydanticAI. It runs after them. Pydantic models, dataclasses, and plain dicts all work, and Pydantic is optional rather than required.

import instructor
from llmground import verify

invoice = client.chat.completions.create(response_model=Invoice, ...)

report = verify(invoice, source_pdf_text)
if report.grounding_rate < 0.9:
    escalate(report)

Why matching values is harder than it looks

The naive version of this check is str(value) in source, and it produces so many false alarms that people switch it off within a day. Real documents don't write values the way models return them.

Model returns Document says Naive check llmground
4500.0 $4,500.00 miss matched
"2026-01-05" 5 January 2026 miss matched
"O'Brien Ltd" O'Brien Ltd (smart quote) miss matched
1234.56 1.234,56 (European) miss matched
-1234.00 (1,234.00) (accounting) miss matched
"ACME Inc" ACME, Inc. miss matched

Every one of these is handled deterministically, with no model in the loop.

Verdicts

Verdict Meaning
grounded Found in the source
weak Found only by fuzzy match. A human should look.
ungrounded Not in the source. Likely fabricated.
skipped Not verifiable: booleans, nulls, values like "1" or "yes"

Fuzzy matches are weak, not grounded, by default. A 90%-similar string is a different string, and on a financial or medical field that difference is the entire problem. Opt in with accept_fuzzy=True when you're handling OCR noise and know what you're trading away.

Skipped fields are excluded from the grounding rate. Counting booleans as grounded would inflate the score and hide real problems.

Spans, for human review

Every match reports character offsets into the source, so you can highlight the evidence rather than just flagging a field.

for r in report.grounded:
    span = r.match.spans[0]
    highlight(source, span.start, span.end)

That's the difference between telling a reviewer "this field is suspicious" and showing them why.

CLI

llmground extracted.json --source invoice.txt --fail-under 0.9

Exits non-zero below the threshold, so it drops into CI as a quality gate.

Options

verify(
    extracted,
    source,
    accept_fuzzy=False,        # count fuzzy matches as grounded
    fuzzy_threshold=0.85,      # similarity floor for reporting a fuzzy match
    number_tolerance=0.01,     # allowed numeric difference
    day_first=True,            # how to read 03/04/2026
    ignore=["metadata.run_id"],  # skip computed fields
)

Limitations

  • Extractive only. It verifies values that should appear in the source. A field that's legitimately inferred rather than copied (a category label, a computed total) will read as ungrounded. Use ignore for those.
  • Ambiguous dates are not guessed. 03/04/2026 returns no match unless you pass day_first, because a silently wrong date is worse than an unverified one.
  • Grounded is not correct. It confirms a value appears in the document, not that the model pulled it from the right place. A total that matches a different line item's amount will pass.
  • Text sources only. PDFs and images need to be through OCR first.

Development

pip install -e ".[dev]"
pytest

MIT.

Metadata

Release files for llmground 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for llmground 0.1.0
File Size Uploaded Provenance Actions
llmground-0.1.0.tar.gz 21.2 kB Without provenance Details for llmground-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for llmground 0.1.0
File Interpreter ABI Platform Actions
llmground-0.1.0-py3-none-any.whl Python 3 none any Details for llmground-0.1.0-py3-none-any.whl

Total release size: 39.2 kB

Release files / llmground-0.1.0.tar.gz

Download URL llmground-0.1.0.tar.gz
Size 21.2 kB
Tags Source
SHA-256 checksum
How to use checksums
17149cae253ede3f0c3dbc8e09532beeea7bab804ef999202072a250f971d8e8
BLAKE2b-256 checksum
How to use checksums
5a089609cf24ea02d8e5fc51c6e357fc011520d471df305a39b929fcc27147f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release files / llmground-0.1.0-py3-none-any.whl

Download URL llmground-0.1.0-py3-none-any.whl
Size 18.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd1f335a00e8a3de5eee6e1452cac54660cbc2792f98a7d94337e594050f1646
BLAKE2b-256 checksum
How to use checksums
8313a2d3a96eb5a1f2df671ff1aad3f53432fd5dd756869573a00ff2165ffecb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Security for llmground 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page