openpay/
compliance/ PCI-DSS readiness package (scope-map,
pci-zero-architecture, hsm-kms-guidance,
qsa-checklist), citing PCI-DSS v4.0.1
requirements. (#1)
examples/pci-zero/ Runnable example demonstrating a deployment
where merchant code never touches a PAN. (#1)
crates/op-core/ Tokenized<Card> typestate + NetworkToken
+ CardNetwork + Cryptogram. (#2)
crates/op-rails-card/network_token/
NetworkTokenProvider trait + VTS adapter
+ MDES adapter, with deterministic stubs
behind a 'live' feature flag. (#2)
crates/op-driver-sdk/conformance/network_token.rs
Conformance harness for third-party
NetworkTokenProvider implementations. (#2)
crates/op-cli/src/ledger/ 'openpay ledger as-of' subcommand for
bi-temporal queries. (#3)
crates/op-orchestrator/src/telemetry.rs
OpenTelemetry span emission per
Payment<S> transition with semantic-
conventions tags. (#3)
docs/observability/ Grafana dashboard JSON + wiring guide
for the bi-temporal ledger lens. (#3)
smart-byte/
smart-byte-rs/ Rust reference implementation: envelope
encode/decode (canonical CBOR + BLAKE3
content addressing), Ed25519 sign +
verify, lockstep BFT commit MVP, Iroh
transport scaffold, CLI. (#4)
spec/identity_and_key_rotation.md
Adopts KERI SAIDs + key-event log
format; explicitly does not adopt ACDC
chaining wholesale. (#5)
conformance/v1/ 21 test vectors covering canonicalisation,
SAID computation, signature verification,
lockstep commit, gossip propagation.
CI gate enforces spec/vector coupling. (#6)
eoc/
eoc-rs/ Rust + WASM reference implementation:
4-stage cascade (cache -> kv -> graph ->
neural), joule counters via RAPL / NVML /
powermetrics, CLI, wasm-bindgen surface.
18 tests pass. (#7)
eoc-bench/ Benchmark harness, MT-Bench and Router
case sets, 'joules per MT-Bench point'
metric, 'eoc-bench run | compare' CLI. (#8)
standards/sci-for-ai-submission.md
Submission proposal mapping EOC's
receipt schema to GSF SCI-for-AI. (#9)
cookbook/ HuggingFace-style cookbook (markdown +
runnable Python) demonstrating EOC's
cascade with HF AI Energy Score. (#9)
docs/bench-v0.md Reproducibility doc for eoc-bench.
Workspace state:
- openpay clippy -- -D warnings: clean across all touched crates
- smart-byte-rs cargo test --workspace + clippy: clean
- eoc-rs cargo test --workspace + clippy: clean (18 tests)
- eoc-bench cargo check --workspace + clippy + 15 tests: clean
Rust 1.95.0 edition 2024 pinned at the open-standards root via
rust-toolchain.toml; every workspace inherits.
Closes #1 #2 #3 #4 #5 #6 #7 #8 #9.
282 lines
13 KiB
Markdown
282 lines
13 KiB
Markdown
# EOC Benchmark v0 — `joules-per-MT-Bench-point`
|
||
|
||
> Status: draft v0. Numbers in the reference table below are placeholders
|
||
> until a live deployment is benchmarked end-to-end.
|
||
> License: text CC-BY-4.0, harness code Apache-2.0.
|
||
|
||
## 1. Why a joules-per-point metric
|
||
|
||
Every other benchmark in the LLM industry answers a single question:
|
||
*how good is the answer?* MMLU, MT-Bench, ARC, HumanEval, GPQA — all of
|
||
them measure quality on a fixed input distribution and ignore the energy
|
||
it took to produce the answer. That made sense when the only inference
|
||
substrate was a transformer running on a GPU and the only knob was
|
||
"more parameters". It does not make sense for an Energy-Optimised
|
||
Compute (EOC) deployment, where the substrate is a four-stage cascade
|
||
(cache → KV → graph → neural) and the explicit goal is to answer as
|
||
many queries as possible at the cheaper stages.
|
||
|
||
In that setting, the question that matters is:
|
||
|
||
> **How many joules did it take to earn one MT-Bench point?**
|
||
|
||
A cascade that scores 7.2 on MT-Bench at 14 J/correct is doing
|
||
qualitatively different work than a 7.2 GPU-baseline that burns 480
|
||
J/correct. The benchmark exists so a deployment can show that
|
||
difference rather than claim it.
|
||
|
||
The v0 formula is:
|
||
|
||
```
|
||
joules-per-MT-Bench-point =
|
||
total_joules / (correct_answers × mt_bench_score_per_correct)
|
||
```
|
||
|
||
`total_joules` is the wall-time energy sum reported by the cascade for
|
||
the suite, in joules. `correct_answers` is the count of cases the
|
||
harness scored as a pass. `mt_bench_score_per_correct` is the suite's
|
||
own per-question weight; for the bundled MT-Bench-style set it is `1.0`,
|
||
so for v0 the metric reduces to **joules-per-correct**. The factor is
|
||
kept in the formula so that suites that adopt MT-Bench's 10-point judge
|
||
scoring later can plug it in without reshaping the dimensional
|
||
analysis.
|
||
|
||
A complementary metric, **stage-distribution**, reports the fraction of
|
||
cases resolved at each stage. A deployment that drives
|
||
joules-per-correct down by short-circuiting at cache and KV will see
|
||
that show up directly in the distribution. A deployment that scores
|
||
high by simply running every query through the neural stage will see
|
||
its joules-per-correct number balloon, regardless of how good the
|
||
final answers are.
|
||
|
||
## 2. What the harness actually does
|
||
|
||
The harness has four small crates:
|
||
|
||
| Crate | Role |
|
||
| -------------------- | ------------------------------------------------------------------------------------- |
|
||
| `eoc-bench-runner` | `BenchCase`, `BenchResult`, `BenchReport`; `run()`; `aggregate()`. |
|
||
| `eoc-bench-mt` | 20 MT-Bench-style cases bundled as JSON (`data/cases.json`). |
|
||
| `eoc-bench-router` | 30 router-shaped cases chosen to exercise all four cascade stages. |
|
||
| `eoc-bench` (binary) | CLI: `eoc-bench run --suite {mt,router,all}` and `eoc-bench compare`. |
|
||
|
||
`run()` walks the supplied cases through an `eoc_cascade::Cascade` one
|
||
by one. Sequential execution is intentional: parallel queries on shared
|
||
hardware pollute the joule attribution that the cascade hands back, and
|
||
the metric we care about is **energy per query**, not throughput. A
|
||
throughput-oriented variant is a future work item, not a v0 deliverable.
|
||
|
||
For each case the runner records:
|
||
|
||
* the response payload,
|
||
* which stage resolved it (`cache | kv | graph | neural`),
|
||
* the joule cost in microjoules,
|
||
* wall-clock latency in milliseconds,
|
||
* whether the response contained the expected substring (when the
|
||
case has an `expected` field).
|
||
|
||
The default accuracy predicate is a case-insensitive substring match.
|
||
That is sufficient for the v0 case set, which uses unambiguous facts
|
||
("Paris", "Mars", "4") rather than open-ended writing tasks. Cases
|
||
designed to elicit creative output (`mt-writing-*`, `rt-neural-*`)
|
||
deliberately leave `expected` as `null`; they count towards joules and
|
||
latency but never towards accuracy.
|
||
|
||
`aggregate()` rolls a slice of `BenchResult`s up into a `BenchReport`
|
||
containing `case_count`, `total_joules`, `joules_per_correct`,
|
||
`accuracy_pct`, `latency_p50_ms`, `latency_p95_ms`, `latency_p99_ms`,
|
||
and a `stage_distribution` map. Percentiles use the nearest-rank
|
||
method (no interpolation) so the result is always one of the observed
|
||
latencies — this matches how operators read percentiles in production
|
||
dashboards.
|
||
|
||
## 3. The case sets
|
||
|
||
**MT (`eoc-bench-mt`).** Twenty cases drawn from the broad shape of
|
||
MT-Bench's eight categories — arithmetic, multi-step reasoning,
|
||
multi-turn dialogue, code, writing, factual recall, explanation,
|
||
summarisation. Multi-turn cases are concatenated with a literal
|
||
`Follow-up:` marker; the cascade sees the full conversation as a
|
||
single prompt, which is the same convention MT-Bench uses for turn
|
||
2 grading. Twenty is deliberately small: v0 is a debug-loop tool, not
|
||
a Goodhart target.
|
||
|
||
**Router (`eoc-bench-router`).** Thirty cases sized so a well-warmed
|
||
cascade hits all four stages. Case ids carry a routing hint:
|
||
|
||
* `rt-cache-*` — six short, repeated prompts that should memoise after
|
||
the first warmup pass.
|
||
* `rt-kv-*` — seven exact-key lookups (well-known constants, ports,
|
||
ISO codes) the reference KV stage is seeded with.
|
||
* `rt-graph-*` — eight subject–predicate–object questions the
|
||
reference triple store can answer.
|
||
* `rt-neural-*` — nine open-ended generation prompts no pre-populated
|
||
store can answer.
|
||
|
||
The hints are advisory. The harness measures *where the cascade
|
||
actually resolved each case*. A deployment that lands an
|
||
`rt-graph-*` case at `neural` because its triple store was not warmed
|
||
sees that show up in the stage distribution, and the joules-per-correct
|
||
number reflects the cost.
|
||
|
||
## 4. The reference cascade the CLI ships with
|
||
|
||
`eoc-bench run` instantiates the four-stage cascade from `eoc-rs`
|
||
using deliberately small fixture data so the CLI works out of the
|
||
box. The LRU cache has a 1024-entry capacity. The KV stage is seeded
|
||
with the same constants that the router suite's `rt-kv-*` cases ask
|
||
about. The graph stage holds the triples the `rt-graph-*` cases need.
|
||
The neural stage uses the `EchoBackend` from `eoc-neural` with an
|
||
estimated cost of 50 J per inference — well above realistic GPU cost
|
||
for any single token, but a useful default for visualising the
|
||
cost spread in the report. Production deployments substitute their
|
||
own wiring; the `run` function in `eoc-bench-runner` accepts any
|
||
`eoc_cascade::Cascade`.
|
||
|
||
## 5. Reference numbers
|
||
|
||
These are placeholders. They will be replaced as soon as the harness
|
||
runs against a live deployment.
|
||
|
||
| Suite | Cases | Accuracy % | Joules/correct | p50 / p95 / p99 (ms) | Stage mix (cache / kv / graph / neural) |
|
||
| --------- | ----- | ----------------------------- | ----------------------------- | ------------------------------------------------------ | -------------------------------------------------------------- |
|
||
| mt | 20 | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ |
|
||
| router | 30 | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ |
|
||
| all | 50 | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ | _to be filled when running against a live deployment_ |
|
||
|
||
Targets we publish v0 results against:
|
||
|
||
* `joules-per-correct` ≤ **5 J** on the router suite for a well-warmed
|
||
cascade (most cases resolve at cache/KV/graph).
|
||
* `joules-per-correct` ≤ **25 J** on the MT suite when wired against a
|
||
small open-weights neural backend on commodity GPU.
|
||
* `latency_p95_ms` ≤ **250 ms** on the router suite end-to-end.
|
||
* `accuracy_pct` ≥ **80%** on the union of scored cases.
|
||
|
||
These thresholds are aspirational, not contractual. A deployment is
|
||
free to publish lower numbers and explain why; the metric is what
|
||
matters, not the threshold.
|
||
|
||
## 6. Reproducibility
|
||
|
||
The harness is fully embedded — the case JSON is `include_str!`'d at
|
||
build time, the path dependencies on `eoc-rs` are pinned in the root
|
||
`Cargo.toml`, and the toolchain is pinned via the workspace's
|
||
`rust-toolchain.toml` to Rust 1.95.0 edition 2024.
|
||
|
||
### Command lines
|
||
|
||
Build (default features include `eoc-rs`):
|
||
|
||
```
|
||
cargo build --release -p eoc-bench
|
||
```
|
||
|
||
Run a suite and capture a report:
|
||
|
||
```
|
||
target/release/eoc-bench run --suite mt > mt-baseline.json
|
||
target/release/eoc-bench run --suite router > router-baseline.json
|
||
target/release/eoc-bench run --suite all > all-baseline.json
|
||
```
|
||
|
||
Compare two reports:
|
||
|
||
```
|
||
target/release/eoc-bench compare \
|
||
--baseline all-baseline.json \
|
||
--candidate all-candidate.json
|
||
```
|
||
|
||
`compare` prints a small JSON object with the three deltas operators
|
||
care about:
|
||
|
||
* `joules_per_correct_pct` — percent change in J/correct (negative is
|
||
better).
|
||
* `latency_p95_pct` — percent change in p95 latency (negative is
|
||
better).
|
||
* `accuracy_pp_delta` — absolute change in accuracy percentage points
|
||
(positive is better).
|
||
|
||
### BenchReport JSON schema
|
||
|
||
The `BenchReport` type serialises as:
|
||
|
||
```json
|
||
{
|
||
"case_count": 20,
|
||
"total_joules": 0.0,
|
||
"joules_per_correct": 0.0,
|
||
"accuracy_pct": 0.0,
|
||
"latency_p50_ms": 0,
|
||
"latency_p95_ms": 0,
|
||
"latency_p99_ms": 0,
|
||
"stage_distribution": {
|
||
"cache": 0,
|
||
"kv": 0,
|
||
"graph": 0,
|
||
"neural": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
Field semantics:
|
||
|
||
| Field | Type | Meaning |
|
||
| ---------------------- | ------------------- | ----------------------------------------------------------------------- |
|
||
| `case_count` | `usize` | Number of cases in the run. |
|
||
| `total_joules` | `f64` | Sum of stage-reported energy, expressed in joules. |
|
||
| `joules_per_correct` | `f64` | `total_joules / correct_answers`. `Infinity` if no answers were correct. |
|
||
| `accuracy_pct` | `f64` | Percentage of *scored* cases that passed. Cases with no expected answer are excluded from this number. |
|
||
| `latency_p50_ms` | `u64` | Nearest-rank p50 latency across all cases. |
|
||
| `latency_p95_ms` | `u64` | Nearest-rank p95 latency. |
|
||
| `latency_p99_ms` | `u64` | Nearest-rank p99 latency. |
|
||
| `stage_distribution` | `map<string,usize>` | Counts keyed by stable stage id (`cache`, `kv`, `graph`, `neural`). |
|
||
|
||
### Diffability
|
||
|
||
Reports are intended to land in a repository as text-diffable JSON.
|
||
The aggregator is deterministic: same input cases plus the same cascade
|
||
configuration produces byte-identical reports. `serde_json::to_string_pretty`
|
||
emits stable key order because the underlying maps are `BTreeMap`s.
|
||
|
||
## 7. Honest limitations
|
||
|
||
This is v0. The following are known and tracked:
|
||
|
||
1. **The bundled neural backend is `EchoBackend`.** It does not produce
|
||
correct answers for open-ended cases; its job is to exercise the
|
||
joule attribution pipe and let the runner end-to-end. Real numbers
|
||
require wiring a real backend (`llama.cpp`, vLLM, an inference
|
||
provider) at the `NeuralBackend` trait surface.
|
||
|
||
2. **Accuracy scoring is substring-based.** That works for the v0
|
||
case set because the expected answers are short and unambiguous.
|
||
v1 should support pluggable judges, including LLM-as-judge for the
|
||
writing/reasoning categories (matching MT-Bench's pipeline).
|
||
|
||
3. **The case sets are small.** Twenty MT cases and thirty router
|
||
cases are enough to debug a cascade, not enough to publish a
|
||
leaderboard against. v1 will adopt the full 80-question MT-Bench
|
||
set and add a router suite of at least 250 cases.
|
||
|
||
4. **Joule readings come from the cascade, not from independent
|
||
hardware counters.** The cascade itself trusts its meter (the
|
||
default is a `StubCounter`). Deployments that need defensible
|
||
numbers must wire `eoc-meter` against RAPL / NVML / `powermetrics`
|
||
and re-run.
|
||
|
||
5. **`mt_bench_score_per_correct = 1.0`.** The MT-Bench original uses a
|
||
10-point judge score per question. v0 collapses that to a binary
|
||
pass/fail so the harness has no dependency on a judging model. v1
|
||
will lift this restriction.
|
||
|
||
The point of shipping v0 anyway is that the *shape* of the metric — a
|
||
joule denominator times an accuracy numerator, with a stage
|
||
distribution alongside — is what makes the cascade visible. The
|
||
absolute numbers will get sharper. The shape will not.
|
||
|
||
## 8. License
|
||
|
||
Harness code (every Rust file under `eoc-bench/`): Apache-2.0.
|
||
This document: CC-BY-4.0. Bundled case JSON: CC-BY-4.0.
|