Skip to main content

ragas-openttt

TTTPS Proof-of-Time provenance metric for Ragas. It answers one question about a sample: does this response have a genuine Proof-of-Time anchor on the public self-serve KPP Provenance API, and does that anchor cover this exact response?

Deterministic and rule based, in the same family as ragas' own ExactMatch and StringPresence. There is no LLM judge and no model call.

This checks a cryptographic audit-trail timestamp and integrity hash. It does not certify legal or regulatory compliance (EU AI Act, FDA, and so on). Treat it as an audit-trail timestamp, not a compliance claim.

Install

pip install ragas-openttt

The dependency list pins langchain-community<0.4.2 on purpose. ragas 0.4.3 imports langchain_community.chat_models.vertexai unconditionally at import time and langchain-community removed that module in 0.4.2, so without the bound import ragas raises ModuleNotFoundError on a fresh install. Measured 2026-08-05: the module is present in 0.4 and 0.4.1, absent from 0.4.2. Drop the bound once ragas no longer needs it.

Usage

The receipt id travels in retrieved_context_ids, as a one element list. SingleTurnSample is a closed pydantic model with no free metadata field, and a subclass that adds one is rejected by ragas' strict sample type check, so this is the one slot that keeps a sample usable by real ragas.evaluate().

from ragas import evaluate
from ragas.dataset_schema import EvaluationDataset, SingleTurnSample
from ragas_openttt import TTTPSProvenanceMetric

sample = SingleTurnSample(
    user_input="how tall is the Eiffel Tower?",
    response="The Eiffel Tower is 330 metres tall.",
    retrieved_context_ids=[receipt_id],       # from POST /v1/anchor
)

result = evaluate(EvaluationDataset(samples=[sample]), metrics=[TTTPSProvenanceMetric()])
print(result)

To seal responses that have no receipt yet, pass a key (mint one for free with POST https://kpp.kenosian.com/v1/keys):

TTTPSProvenanceMetric(anchor_if_missing=True, kpp_key="...")   # or KPP_API_KEY

What it checks, and what it cannot

The API is the source of truth. Given a receipt id the metric calls POST /v1/verify, requires verified: true, and with bind_content=True (the default) requires the anchored digest to equal sha256(sample.response).

That design has a consequence worth stating plainly: the metric never reads a receipt blob held by your pipeline, so edits to that blob are invisible to it. If the anchor on the server is intact, the metric says so, even when the receipt JSON you display to a user has been rewritten.

Measured 2026-08-05 over 25 rows of the published corpus Pittro/verifiable-ai-provenance-bench plus 5 negatives built by mutating real receipts:

case bind_content=False default (bind_content=True) check_receipt_blob()
genuine receipt lifted onto different output 1.0 (missed) 0.0 (caught) 0.0 (caught)
receipt blob edited, one hex nibble flipped 1.0 (missed) 1.0 (missed) 0.0 (caught)
timestamp rewritten one year into the past 1.0 (missed) 1.0 (missed) 0.0 (caught)
receipt id that was never issued 0.0 (caught) 0.0 (caught) 0.0 (caught)
no receipt at all 0.0 (caught) 0.0 (caught) 0.0 (caught)
25 published genuine receipts 1.00 1.00 not applicable

Negatives caught by the metric: 2 of 5 without content binding, 3 of 5 with the default. Separation between the genuine mean and the negative mean with the default: 0.60.

The two misses are the two blob edits, for the reason above: the server copy of both receipts is genuine, so the server says verified. If your pipeline keeps or displays the receipt JSON, close that gap with the helper this package ships, which reads the blob and caught 5 of 5 in the same run:

from ragas_openttt import check_receipt_blob

ok, reason = check_receipt_blob(receipt_dict, response_text)

It checks content binding, existence, digest agreement and timestamp agreement, and returns (ok, reason).

Reproducing those rows needs only your own key: anchor a text, then (a) attach the receipt to a different text, (b) flip one hex character of content_hash, (c) rewrite time by a year, (d) invent a 24 hex character receipt id, (e) attach nothing.

What none of it tells you: whether the response is correct, safe or useful. A receipt proves the content existed no later than the anchored time, and never that it did not exist earlier.

Score semantics

Per sample the score is binary, 1.0 or 0.0. The continuous number a Ragas report shows for a dataset is the mean over N samples, in other words a pass rate, not a per sample confidence.

Options

argument default meaning
bind_content True require sha256(response) to equal the anchored digest
anchor_if_missing False seal the response when no receipt id is present
kpp_key "" API key for anchoring, falls back to KPP_API_KEY
kpp_base None API base, falls back to KPP_BASE
timeout_s None request timeout in seconds, falls back to KPP_TIMEOUT_S, then 1.0
threshold 1.0 pass threshold

Set bind_content=False only for corpora that publish digests without the plaintext they cover. With bind_content=True and no response text the metric scores 0.0 rather than silently skipping the check.

Fail-open: a slow or unreachable API degrades the score and records why in metric._last_reason. Scoring never raises out of the eval run. The metric makes one POST /v1/verify round trip per sample.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ragas_openttt-0.1.0.tar.gz (10.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ragas_openttt-0.1.0-py3-none-any.whl (9.5 kB view details)

Uploaded Python 3

File details

Details for the file ragas_openttt-0.1.0.tar.gz.

File metadata

  • Download URL: ragas_openttt-0.1.0.tar.gz
  • Upload date:
  • Size: 10.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for ragas_openttt-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8edc2c248782161d99692e742cf7961a260ca72a5be7a9450961b8bcb21a995b
MD5 9c80100dcc165c460b33ba91ac8751a9
BLAKE2b-256 bf9f8eaa08e264435cbd16dd36e145d97626a28c3f0463f9a09696b0d6470cca

See more details on using hashes here.

File details

Details for the file ragas_openttt-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ragas_openttt-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 9.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for ragas_openttt-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f9e17d52c306853f717767c001170b8214806a84b5bb62c3cb37d10b61bac751
MD5 3c777af0b2f4110eabbc15a19c503709
BLAKE2b-256 d6eafb1618a6ae3a3538489390a8292aad25c38ed7a553df1fa4be7858c7e9a8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page