gtrex
Grounded text extraction you can check.
gtrex checks whether a quotation extracted by a language model appears in
its source text, under a verification policy that is explicit, versioned and
fingerprinted. It certifies literal quotation provenance. It does not certify
that an extracted fact is true.
pip install gtrex # Python >= 3.10, no runtime dependencies
Verify
from gtrex import Policy, Verifier
verifier = Verifier(source_text, Policy())
cert = verifier.verify(model_quote)
cert.grounded # whether the policy accepts the quotation
cert.tier # how much normalization it took, e.g. 'typography'
cert.start # character offsets into the original source text
cert.span_text # the source's own characters
Verification always checks the whole quotation, so a verbatim opening with an
invented tail is not grounded. If your extraction stores a source location,
verifier.verify_at(model_quote, start, end) checks that location only: a
match elsewhere in the document cannot validate it. The certificate then
reports scope="span", the requested_span, and, after a failed check,
whether a contiguous match exists elsewhere.
Policies
A grounding rate depends on choices: which normalizations to apply, what to
do with an ellipsis, whether stitched fragments count, and which tier counts
as grounded. A Policy records all of them. Policy.fingerprint digests that
configuration together with a verifier revision, which changes whenever the
code changes a verdict. Report the fingerprint, the package version and the
Unicode database version with any grounding rate; the run manifest records
all three.
The default ladder has five cumulative tiers: exact, whitespace,
typography, case and skeleton. Each tier declares whether it is
sound, an assumption that it forgives presentation, not content.
Verification reports the tightest tier that contains the whole quotation, and
a match must cover whole source characters. skeleton is unsound, because
removing punctuation makes different statements collide:
"Operating loss was ($10.0 million)." -> operatinglosswas100million
"Operating loss was $100 million." -> operatinglosswas100million
Counting an unsound tier as grounded requires allow_unsound=True.
Three policies are built in:
STRICTaccepts only character-identical quotations.DEFAULTaccepts the sound tiers. A quotation joined from separate fragments with an ellipsis is reported as stitched, not grounded.PERMISSIVEaccepts the skeleton tier, ignores ellipses and admits stitched fragments. It exists to measure how much such a policy inflates a rate, not as a recommendation.
gtrex explain --policy strict prints a policy with every assumption and its
fingerprint. If an upstream parser inserted text the model never saw, such as
layout markers, genuine quotations fail; declare it with marker_pattern.
Repair
A Repairer handles near-misses:
verify -> [fragment salvage, opt-in] -> locate-and-substitute -> reject
In the default substitute mode it locates the best-matching source span
and stores the source's own characters, keeping the model's wording and the
certificates before and after. annotate keeps the model's wording and
attaches the located span; off only verifies. The substitution threshold
has no default: calibrate one or set one, because repairing without one is an
error. A substitution may store a source span shorter than the claim; the
stored text is certified by its own after certificate.
Two guards refuse substitutions that no similarity threshold can catch, because the edited text shares almost every token with the source:
claim: "Operating loss was $100 million for the year ended ..."
span: "Operating loss was ($10.0 million) for the year ended ..." -> numeric_guard
claim: "We believe that we will be able to compete effectively ..."
span: "We believe that we will not be able to compete effectively ..." -> polarity_guard
The numeric guard compares figures, their order and adjacent signs. The
polarity guard compares negation, modal and direction words (not, may,
will, increase, decrease, more, less, higher, lower and others)
in the claim and the located span. Both are partial checks: they block common
meaning-changing edits; they do not prove that a substitution preserves
meaning.
Command line
gtrex explain # the policy and every assumption
gtrex verify claims.jsonl -o certified.jsonl --manifest run.json
gtrex repair claims.jsonl -o repaired.jsonl --calibration calibration.json
gtrex calibrate labelled.jsonl -o calibration.json --alpha 0.05
gtrex sensitivity claims.jsonl # the same claims under the three built-in policies
gtrex audit-export answers.csv -o audit.json
Input is JSON Lines. Each record needs a string claim and exactly one of an
inline source or a source_path (read as UTF-8, byte for byte):
{"id": "q1", "claim": "We depend on a limited number of suppliers", "source_path": "docs/report.txt"}
Other fields pass through unchanged and in input order, and results go under
one key, gtrex. A record that already has a gtrex field is refused unless
you pass --replace-existing. verify --at-offsets requires start and
end in every record and checks only that span.
repair needs a threshold: --calibration uses the one calibrate wrote,
after checking that it was made for this policy's scorer and pipeline, and
--threshold sets one by hand. It stores the record's text in claim (in
substitute mode, the source's own text) and keeps the model's wording in
gtrex.claim_original, with the certificates gtrex.before and
gtrex.after. gtrex.accepted says the record was kept; gtrex.grounded
says its text verifies. An annotation is accepted and not grounded.
calibrate reads JSON Lines of {"score": ..., "faithful": true, "group": ...}, where score is a repair's gtrex.alignment.score and faithful is
your judgement of that repair. sensitivity reports, for each built-in
policy, the share of claims it accepts and the share contained as one
contiguous run.
audit-export checks that a CSV export kept an evidence value for every
populated answer column (by default Q... columns paired with
Q..._evidence) and exits nonzero if one is missing. It checks completeness
only, not the evidence itself.
Exit status is 0 on success and 1 on invalid input (the message names the file and line), a failed calibration or an incomplete export. Output is always UTF-8.
Calibration, precisely
A repair is accepted when score >= threshold, and calibration uses the same
rule. calibrate_risk implements Learn-then-Test with fixed-sequence testing
over the observed scores (plus 1.0, or a grid you supply), using the exact
binomial tail as the p-value, and returns the least conservative certifiable
threshold among those candidates. The controlled quantity is the joint risk
P(score >= threshold AND the repair is unfaithful)
for claims like the calibration claims that reach the alignment-score filter.
It is not P(unfaithful | accepted), which is reported only as a point
estimate, and it does not cover claims accepted at verification or by
fragment salvage. The guarantee, P(risk <= alpha) >= 1 - delta over the
calibration draw, holds only if the calibration examples are independent
draws from the claims you will repair. Several examples from one document,
or several perturbations of one quotation, are not independent: give each
example a group (such as a document id), and calibrate_risk refuses
repeated groups. Overriding that is recorded in the calibration as
uncertified.
calibrate_coverage is split conformal on 1 - similarity, giving
P(a genuine repair is accepted) >= 1 - alpha for exchangeable scores at the
same stage; the guards may still block some of those repairs afterwards.
A calibration records its target, stage, assumptions, a digest of its data,
and the identity of the scorer and pipeline its scores came from. A manifest
or repair --calibration refuses a calibration made for another
configuration, and the loader rejects incompatible method revisions,
acceptance rules, targets, stages and unknown fields. This validates the
file's declared format; it does not protect it against deliberate editing.
A threshold calibrated on synthetic perturbations is calibrated for those
perturbations. Use the certified value at full precision (scores and
thresholds are exported unrounded), because rounding it down admits scores
the certificate excluded. Both functions raise CalibrationError rather than
return an uncertifiable threshold.
What it does not check
- Entailment. A verbatim quotation can be attached to the wrong entity, relation or direction. Grounding is necessary, not sufficient.
- Recall. Nothing here measures what the model failed to extract.
- Other fields. Verify the quotation, then validate your schema's other fields against it separately.
A rejected claim is not a detected hallucination. It is a claim the policy will not vouch for, which also covers splicing, reordering, paraphrase, parser artifacts, and a source that was not the document the model saw.
Known limitations
- Lexical boundaries are heuristic. By default a match may not start or
end inside a word, hyphenated term, signed figure, decimal, grouped number
or time. HTML entities are decoded before the edges are checked, and
apostrophe-suffixed names and sentence punctuation are allowed. Scripts
that do not separate words with spaces need a language-specific tokenizer.
match_boundaries=Falseturns the check off. - "Sound" is an assumption. Case folding merges
mWandMW, and typography folding merges a minus sign with a hyphen. Choose the ladder for your domain. - The guards are partial. The numeric guard reads numbers by the English
convention (
,groups thousands,.marks decimals). A malformed number, or one written with a fraction character such as1½, must match verbatim, and a superscript is a separate figure. It ignores physical units, uncommon scale words, many sign conventions and spelled-out numbers. The polarity guard also reads negative words just before the located span. It misses some shifted negation scopes, it counts the month "May" as a modal, and it can refuse a valid repair because of a negation earlier in the sentence, such as in a preceding clause or table row. The guards run on alignment repair, not on claims that already verify as literal source text. - The fingerprint covers the configuration, including every normalization step's id, version and parameters, plus the verifier revision. It is not a hash of the code, so report the package version with it.
- Unicode. Normalization is per character, so a decomposed accent does not match its precomposed form. Verdicts depend on Python's Unicode database, which the manifest records; the same fingerprint under another database is not a claim of identical verdicts.
- Calibration is synthetic until you label your own repairs. The guarantees hold for the population the calibration examples were drawn from, at the alignment stage only.
- Repair. Substitution trusts the aligner's best span above your
threshold. Keep
gtrex.claim_original, and review substituted records where it matters. Opted-in salvage keeps one verified fragment and records what it dropped; what was dropped is not certified. - Performance. Each source is normalized once per tier, so very large corpora are slower than they need to be.
Upgrading from a pre-release
Policy files written by 0.1.0b1 to 0.1.0b4 load unchanged, with their own
fingerprints. Files from earlier pre-releases load through an explicit
migration that warns and assigns a new fingerprint. In 1.0.0 the default
policies also watch "higher" and "lower", so the built-in fingerprints
changed. A calibration stays bound to the policy it was made for: use it
with that policy's file, or recalibrate.
The repository includes the 0.1.0b4 default policy as
tests/fixtures/gtrex-0.1.0b4-policy.json for reproducing that beta's
calibrated decisions. Its stored fingerprint is 63c0146e38cdb3cd;
the 1.0.0 built-in DEFAULT has a different fingerprint.
License
Apache-2.0.
Metadata
Release files for gtrex 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 | Provenance | Actions |
|---|---|---|---|---|
| gtrex-1.0.0.tar.gz | 92.3 kB | Without provenance | Details for gtrex-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gtrex-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for gtrex-1.0.0-py3-none-any.whl |
Total release size: 177.8 kB
Release files / gtrex-1.0.0.tar.gz
| Download URL | gtrex-1.0.0.tar.gz |
|---|---|
| Size | 92.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
046475bd6763356b7e23712ebe05e69ee75490f1f8f4dd94d21900a7fdc4fcc2
|
|
BLAKE2b-256 checksum How to use checksums |
b3f77a1f642f5633d79054f0a7be0c3a19652a5c9abae658cd16206473b23f95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / gtrex-1.0.0-py3-none-any.whl
| Download URL | gtrex-1.0.0-py3-none-any.whl |
|---|---|
| Size | 85.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19cad8f50babc0302ca478642dc0c260f2be0949e99597d9f047c88aec13377e
|
|
BLAKE2b-256 checksum How to use checksums |
a3cdbbdc156e1f8a997ca6ac21906678a6899607806072447d368991e0418ed2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for gtrex 1.0.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.