Metadata-Version: 2.4
Name: onetrace
Version: 0.1.0
Summary: onetrace SDK: stage-receipt records and the verbs diff, localize, reproduce
Author-email: Shamik Saha <shamik.saha.rcciit@gmail.com>
License-Expression: Apache-2.0
Keywords: provenance,reproducibility,data-lineage,stage-receipt,audit-trail
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: onetrace-verify>=0.1.0
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Requires-Dist: pytest-timeout>=2.3; extra == "test"
Requires-Dist: numpy<3,>=2.1; extra == "test"
Provides-Extra: langchain
Requires-Dist: beautifulsoup4==4.15.0; extra == "langchain"
Requires-Dist: langchain==1.4.2; extra == "langchain"
Requires-Dist: langchain-classic==1.0.8; extra == "langchain"
Requires-Dist: langchain-community==0.4.2; extra == "langchain"
Requires-Dist: langchain-core==1.6.5; extra == "langchain"
Requires-Dist: langchain-protocol==0.0.19; extra == "langchain"
Requires-Dist: langchain-text-splitters==1.1.2; extra == "langchain"
Requires-Dist: numpy==2.5.3; extra == "langchain"
Requires-Dist: pypdf==6.19.0; extra == "langchain"
Provides-Extra: llamaindex
Requires-Dist: llama-index-core==0.14.25; extra == "llamaindex"
Requires-Dist: llama-index-readers-file==0.7.0; extra == "llamaindex"
Requires-Dist: llama-index-instrumentation==0.6.0; extra == "llamaindex"
Requires-Dist: llama-index-workflows==2.24.1; extra == "llamaindex"
Requires-Dist: pypdf==6.19.0; extra == "llamaindex"
Requires-Dist: numpy==2.4.6; extra == "llamaindex"
Provides-Extra: langflow
Requires-Dist: langflow-base==1.12.3; extra == "langflow"
Requires-Dist: lfx==1.12.3; extra == "langflow"
Dynamic: license-file

# onetrace

onetrace is an SDK for emitting and verifying *stage receipts*: a signed, chained record of what
a pipeline actually did at each step, checkable independently of the code that produced it.
onetrace does not check whether a pipeline's output is *true* — it checks what was recorded, and
whether an independent verifier can confirm that record is internally consistent and unbroken.
Truth is a claim about the world; onetrace only ever speaks to what was recorded and what a
verifier could confirm about it.

## Install and quickstart

```
pip install onetrace
```

`onetrace-verify` (the reference verifier, standard library only) comes along automatically —
it is a declared dependency, not an extra step.

Save this as `demo.py`. It emits a two-stage run with no network, no model, and no file beyond
itself:

```python
#!/usr/bin/env python3
"""onetrace quickstart demo: two stages, no network, no model, deterministic."""
import json
import sys
from pathlib import Path

from onetrace.emit import Instrument, Recorder

QUESTION = "What does the warranty cover?"
CORPUS = [
    {"id": "p1", "text": "The warranty covers manufacturing defects for twelve months."},
    {"id": "p2", "text": "Shipping delays are handled by the logistics partner, not the warranty."},
    {"id": "p3", "text": "Batteries are covered by a separate six-month warranty."},
]


def main(out_dir: str, run_id: str) -> None:
    out = Path(out_dir)
    out.mkdir(parents=True, exist_ok=True)
    corpus_path = out / "corpus.json"
    corpus_path.write_text(json.dumps(CORPUS), encoding="utf-8")

    rec = Recorder(out_dir, run_id=run_id, declared_stages=["retrieve", "answer"],
                   manifest=Path(__file__), policy="fail-closed",
                   anchor_reason="quickstart demo; epoch anchoring is not implemented")

    retrieve_cfg = {"tokenizer": "lower-split", "top_k": "1"}

    @rec.stage("retrieve", Instrument("word-overlap", "retriever", "1.0.0", retrieve_cfg))
    def retrieve(ctx):
        corpus = json.loads(
            ctx.read_external(corpus_path, "application/json", name="corpus",
                              trust_class="operator-authored").decode("utf-8"))
        qwords = set(QUESTION.lower().split())
        scored = [(p["id"], p["text"], len(qwords & set(p["text"].lower().split())))
                 for p in corpus]
        scored.sort(key=lambda s: (-s[2], s[0]))
        best_id, best_text, _ = scored[0]
        for k, v in retrieve_cfg.items():
            ctx.constant(k, v)
        ctx.assertion("candidate_count", str(len(corpus)))
        return ctx.write_json("retrieved.json", {"id": best_id, "text": best_text})

    answer_cfg = {"method": "extractive"}

    @rec.stage("answer", Instrument("extractive", "answerer", "1.0.0", answer_cfg))
    def answer(ctx, retrieved_artifact):
        retrieved = ctx.read_json(retrieved_artifact)
        for k, v in answer_cfg.items():
            ctx.constant(k, v)
        ctx.assertion("source", retrieved["id"])
        return ctx.write_json("answer.json", {"answer": retrieved["text"], "cited": retrieved["id"]})

    retrieved = retrieve()
    answer(retrieved)


if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])
```

Emit two runs of it:

```
python demo.py run_a run-a
python demo.py run_b run-b
```

Verify one with the reference verifier:

```
cd run_a && onetrace-verify .
```

```
[PASS   ] receipts/01-retrieve.json: bytes are canonical
...
[PASS   ] manifest: chain head matches the last receipt

52 pass, 0 fail, 2 not-run  ->  PASS
```

The 2 `not-run` rows are `originality` — this run is unanchored (no epoch anchoring configured),
so the verifier checks that the record is internally consistent, not that it is first-published.

Compare the two runs:

```
onetrace diff run_a run_b --out diff_report
```

```
baseline   run_a  run-a  (run)
candidate  run_b  run-b  (run)

diff: identical

stage         baseline            candidate           verdict
--------------------------------------------------------------------
retrieve      51bd312473c4        51bd312473c4        same
answer        35a281ec6835        35a281ec6835        same

first difference  none
comparisons performed 2; stages identical before the first difference 2
```

`onetrace localize` takes the same two runs (or just one, to find the first unclean stage in it
alone) and finds the first point of divergence:

```
onetrace localize run_a run_b --out localize_report
```

```
baseline   run_a  run-a  (run)
candidate  run_b  run-b  (run)

localize: identical

stage         baseline            candidate           verdict
--------------------------------------------------------------------
retrieve      51bd312473c4        51bd312473c4        same
answer        35a281ec6835        35a281ec6835        same

first difference  none
comparisons performed 2; stages identical before the first difference 2
```

`onetrace reproduce` re-executes a recorded stage and compares its output against what the
receipt claims — it needs the original pipeline code available to re-run, so it isn't part of
this quickstart.

This transcript was run word for word in a fresh virtual environment (`pip install onetrace`
from a locally-built wheel, no editable install, no repository checkout) before being written
here.

## Five verdicts

Every stage in a comparison gets exactly one of five verdicts, and nothing else:

- **same** — the stage's output digest matches on both sides.
- **FIRST DIFFERENCE** — the first stage, in order, whose output digest does not match.
- **downstream** — after the first difference, still different; the divergence propagated.
- **reconverged** — after diverging, a later stage's output matches again (different work,
  same result — this happens in practice and is reported honestly, not hidden).
- **COULD NOT CHECK** — the stage can't be evaluated (an unimplemented format version, a
  declared boundary, a file that can't be read). This is not a pass, and it is not silently
  folded into "same."

A verdict is always about a stage's **output**. A difference in the instrument, its
configuration, or the declared constants never changes a verdict by itself — it appears as an
annotation alongside the ladder, naming exactly what differs, so a `same` result never hides that
something about *how* the output was produced changed even though the bytes didn't.

`diff` and `localize` each also report one overall result word for the whole comparison
(`identical`, `diverged`, `not comparable`, `refused`, or `could not check`, with exit codes
0/1/2/3/4 respectively — `localize` on a single run instead reports `clean` or `located`, exit
0 or 1); `reproduce` reports each stage as `REPRODUCED`, `DIVERGED`, or `COULD NOT CHECK`.

## Links and the trust model

- [Documentation](docs/) — the format in plain words, the five verdicts, the three verbs, one page per adapter, and what onetrace does not claim.
- [The stage-receipt format (Internet-Draft)](draft/draft-saha-stage-receipts-00.txt)
- [The reference verifier](verifier/)
- [The rejection vectors](verifier/rejection/) — records a conforming verifier must refuse, and why

**The verifier does not trust the SDK that emitted a record.** Every record this SDK produces is
checked the same way a record from any other emitter would be — the verifier reads bytes, not
intentions, and it owes nothing to the code that wrote them.

## Stability

The current record format is `stage-receipt/0.2` (format major version `0`), specified by an
Internet-Draft that has not yet reached RFC status. Anything in the format — required members,
canonical-form rules, the rejection vectors — may still change before major version `1`. Format
major version is carried in every record: a manifest declaring one the reference verifier does
not implement is refused outright, by name, rather than silently accepted (verify a manifest with
an unrecognized `format` and it prints `[REFUSED] manifest: format -- ...` and exits non-zero); a
verb comparing individual stages reports the stage's own verdict as `COULD NOT CHECK` for the
same reason. Neither ever guesses.

## License, security, and reporting a problem

Licensed under [Apache-2.0](LICENSE).

This is pre-release software (see the classifiers in `pyproject.toml`). See [SECURITY.md](SECURITY.md)
for what's in scope and how to report a vulnerability; for anything else, open an issue against
this repository.
