deepeval-openttt
TTTPS Proof-of-Time provenance metric for DeepEval. It answers one question about a test case: does this output carry a genuine Proof-of-Time receipt that actually covers it, according to the public self-serve KPP Provenance API?
Deterministic and rule based. There is no LLM judge, no model call, and no API key needed for scoring existing receipts.
This attaches and 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 deepeval-openttt
Usage
Score a receipt your pipeline already produced, for example one attached by
litellm-openttt, llamaindex-openttt or smolagents-openttt:
from deepeval.test_case import LLMTestCase
from deepeval_openttt import TTTPSProvenanceMetric
test_case = LLMTestCase(
input="how tall is the Eiffel Tower?",
actual_output="The Eiffel Tower is 330 metres tall.",
metadata={"tttps_receipt": receipt}, # raw KPP receipt dict
)
metric = TTTPSProvenanceMetric()
metric.measure(test_case)
print(metric.score, metric.reason)
# 1.0 Structurally valid receipt (id=...). cross-checked against the KPP Provenance API (...)
Or let the metric seal the output itself (needs a key, mint one for free with
POST https://kpp.kenosian.com/v1/keys):
metric = TTTPSProvenanceMetric(anchor_if_missing=True) # reads KPP_API_KEY
Both evaluate([...], [metric]) and assert_test work, since this is a
plain BaseMetric with measure() and a_measure().
What it checks, and what it cannot
Two layers. The structural layer asks "is a well formed receipt attached". The cross-check layer, on by default, asks "does the API still have this record, and does it cover this exact output":
- content binding:
sha256(actual_output)equals the anchored digest - existence: the API returns a verified record for this receipt id
- field agreement: the receipt's digest and timestamp equal the API's
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 | cross_check=False |
default (cross_check=True) |
|---|---|---|
| genuine receipt lifted onto different output | 1.0 (missed) | 0.0 (caught) |
| receipt blob edited, one hex nibble flipped | 1.0 (missed) | 0.0 (caught) |
| timestamp rewritten one year into the past | 1.0 (missed) | 0.0 (caught) |
| receipt id that was never issued | 1.0 (missed) | 0.0 (caught) |
| no receipt at all | 0.0 (caught) | 0.0 (caught) |
| 25 published genuine receipts | 1.00 | 1.00 |
Negatives caught: 1 of 5 structure only, 5 of 5 with the default. Separation between the genuine mean and the negative mean: 0.20 structure only, 1.00 with the default.
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 it does not tell you:
- nothing about whether the output is correct, safe or useful. It is a provenance check, not a quality check
- a receipt proves the content existed no later than the anchored time. It cannot prove the content did not exist earlier
- if your pipeline anchors a different byte string than
actual_output(a canonical JSON, a trimmed answer), content binding fails. Anchor the exact string you evaluate, or setbind_content=Falseand accept that a lifted receipt then scores 1.0 - with
cross_check=Falseit detects nothing beyond a missing or malformed receipt - it does not require any particular time source. The API reports
time_source(roughtime_chainorlocal) on the record kept inmetric.tttps_server_record, so gate on that yourself if chain backed time matters to you
Score semantics
Per case the score is binary, 1.0 or 0.0: a receipt either covers this output or it does not. The continuous number a DeepEval report shows for a suite is the mean over N cases, in other words a pass rate, not a per case confidence.
Options
| argument | default | meaning |
|---|---|---|
cross_check |
True |
verify the receipt against the API |
bind_content |
True |
require sha256(actual_output) to equal the anchored digest |
anchor_if_missing |
False |
seal the output when no receipt is attached |
metadata_key |
"tttps_receipt" |
where to read the receipt on the test case |
timestamp_tolerance_s |
1.0 |
allowed gap between claimed and recorded time |
threshold |
1.0 |
DeepEval pass threshold |
Environment: KPP_BASE (default https://kpp.kenosian.com), KPP_API_KEY,
KPP_TIMEOUT_S (seconds, default 1.0).
Fail-open: a slow or unreachable API degrades the score and records why in
metric.reason. It never raises out of the eval run. With cross_check=True
the metric makes one POST /v1/verify round trip per test case.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file deepeval_openttt-0.1.0.tar.gz.
File metadata
- Download URL: deepeval_openttt-0.1.0.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8666bda25285ac396e6f11691233da6c8bce2fadea695253da91ef6d4a7f3743
|
|
| MD5 |
723328617bd05e02c0f1561280af5f8a
|
|
| BLAKE2b-256 |
82370c56eca5fdb785ccfd2d100fba30040d74e4c34d65caacc94246fc51b8b5
|
File details
Details for the file deepeval_openttt-0.1.0-py3-none-any.whl.
File metadata
- Download URL: deepeval_openttt-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c29f97d9da943f71b901dbdd0309af52d2c28f18f187d9af201da793cb4555b1
|
|
| MD5 |
61a33117dbf340cbdc626ec2fe590015
|
|
| BLAKE2b-256 |
5ce2c52e2092a662cf6b169b6f8d8f4187e63f8a0fde17bd3d72c339834fdc16
|