Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/hf-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: HF dataset publish

# Daily snapshot of the live citable feed pushed to
# https://huggingface.co/datasets/OpenChainBench/benchmarks.
#
# Trigger:
# - 00:00 UTC every day (after the daily Prom roll-up is settled).
# - workflow_dispatch with an optional `dry_run` flag so we can
# validate changes without touching HF.
#
# Required secrets:
# HF_TOKEN write-scoped token for the dataset repo.
# SLACK_WEBHOOK_URL optional, incoming-webhook URL for ops alerts.

on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
dry_run:
description: "Stage parquet locally without pushing to HF"
type: boolean
default: false

concurrency:
group: hf-publish
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
OCB_API: https://openchainbench.com
HF_REPO_ID: OpenChainBench/benchmarks
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip
cache-dependency-path: scripts/hf_publisher/requirements.txt

- name: Install deps
run: pip install -r scripts/hf_publisher/requirements.txt

- name: Run publisher tests (offline)
# Schema/quorum regressions caught here never reach the dataset.
run: |
cd scripts/hf_publisher
python -m unittest test_publish.py -v

- name: Publish snapshot
run: |
cd scripts/hf_publisher
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.dry_run }}" = "true" ]; then
python publish.py --dry-run
else
python publish.py
fi

- name: Summary
if: always()
run: |
{
echo "## HF publish ${{ job.status }}"
echo ""
echo "**Repo:** https://huggingface.co/datasets/${HF_REPO_ID}"
echo "**Mode:** ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run == true && 'dry-run' || 'live' }}"
echo "**Time:** $(date -u +%FT%TZ)"
} >> "$GITHUB_STEP_SUMMARY"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,8 @@ harnesses/*/script
harnesses/*/monitor
harnesses/*/cmd/script/script
harnesses/*/cmd/monitor/monitor

# Python venvs for the HF publisher (created locally for dry-runs)
scripts/hf_publisher/.venv/
**/__pycache__/
*.pyc
55 changes: 55 additions & 0 deletions scripts/hf_publisher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# HF dataset publisher

Daily snapshot publisher for the public
[OpenChainBench/benchmarks](https://huggingface.co/datasets/OpenChainBench/benchmarks)
dataset on Hugging Face.

## What it does

- Fetches `https://openchainbench.com/api/citable` and
`/api/stat/<slug>` for every live bench.
- Refuses to publish if the source feed is degraded (quorum guard: half
the count below `live` status, or count below the floor).
- Projects the JSON into three Hive-partitioned Parquet tables:
- `headlines/` 1 row per (slug, day)
- `providers/` 1 row per (slug, provider, day)
- `timeseries/` 1 row per (slug, point, day)
- Stages a fixed set of static assets (README, CITATION.cff, LICENSE,
JSON schemas, example queries) and pushes the whole thing to HF.

## Schema versioning

`SCHEMA_VERSION` in `publish.py` is the source of truth. Bump it any
time a column is added. **Never** rename or remove columns: the dataset
is a long-lived public artifact and consumers will write queries
against the column names.

## Local dry-run

```bash
cd scripts/hf_publisher
pip install -r requirements.txt
python publish.py --dry-run --out /tmp/ocb-hf-test
ls /tmp/ocb-hf-test
```

## CI

The `.github/workflows/hf-publish.yml` workflow runs the tests first,
then either `publish.py --dry-run` (manual dispatch with the flag) or
the real push (scheduled run or manual without the flag).

Required GitHub secrets:
- `HF_TOKEN` write-scoped token on the dataset repo.
- `SLACK_WEBHOOK_URL` optional incoming-webhook URL for ops alerts.

## Tests

```bash
cd scripts/hf_publisher
python -m unittest test_publish.py -v
```

Tests cover the quorum guard, all three row builders, the partition
path layout, and the static-asset templating step. They never hit the
live API or HF Hub.
25 changes: 25 additions & 0 deletions scripts/hf_publisher/dataset_template/CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
cff-version: 1.2.0
title: OpenChainBench Benchmarks
abstract: >-
Daily snapshots of every public benchmark on openchainbench.com -
open, reproducible measurements of crypto infrastructure (RPCs,
oracles, bridges, data APIs, Polymarket adapters, Hyperliquid
builders). Released as Hive-partitioned Parquet under CC-BY-4.0.
authors:
- name: OpenChainBench Contributors
website: https://openchainbench.com
type: dataset
license: CC-BY-4.0
repository-code: https://github.com/ChainBench/OpenChainBench
url: https://huggingface.co/datasets/OpenChainBench/benchmarks
date-released: "{{snapshot_date}}"
keywords:
- blockchain
- crypto
- benchmarks
- infrastructure
- latency
- oracles
- bridges
- polymarket
- hyperliquid
23 changes: 23 additions & 0 deletions scripts/hf_publisher/dataset_template/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Creative Commons Attribution 4.0 International (CC BY 4.0)

You are free to:
Share - copy and redistribute the material in any medium or format
Adapt - remix, transform, and build upon the material for any purpose,
even commercially.

Under the following terms:
Attribution - You must give appropriate credit, provide a link to the
license, and indicate if changes were made. You may do
so in any reasonable manner, but not in any way that
suggests the licensor endorses you or your use.

No additional restrictions - You may not apply legal terms or
technological measures that legally restrict others from doing anything
the license permits.

Full license text: https://creativecommons.org/licenses/by/4.0/legalcode
Summary: https://creativecommons.org/licenses/by/4.0/

Suggested attribution:
OpenChainBench. (2026). OpenChainBench Benchmarks [Data set].
Hugging Face. https://huggingface.co/datasets/OpenChainBench/benchmarks
142 changes: 142 additions & 0 deletions scripts/hf_publisher/dataset_template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
license: cc-by-4.0
language:
- en
pretty_name: OpenChainBench Benchmarks
task_categories:
- tabular-classification
- other
tags:
- crypto
- blockchain
- benchmarks
- rpc
- oracles
- bridges
- polymarket
- infrastructure
- latency
- finance
size_categories:
- 1K<n<10K
configs:
- config_name: headlines
data_files:
- split: train
path: headlines/**/*.parquet
- config_name: providers
data_files:
- split: train
path: providers/**/*.parquet
- config_name: timeseries
data_files:
- split: train
path: timeseries/**/*.parquet
---

# OpenChainBench Benchmarks

Daily snapshots of every public benchmark on
[openchainbench.com](https://openchainbench.com), released as
Hive-partitioned Parquet under CC-BY-4.0.

OCB measures latency, cost, coverage and accuracy of crypto
infrastructure (RPCs, oracles, bridges, data APIs, Polymarket adapters,
Hyperliquid builders). Every snapshot here mirrors the
[/api/citable](https://openchainbench.com/api/citable) and
[/api/stat/&lt;slug&gt;](https://openchainbench.com/api/stat/bridge-quote-latency)
JSON feeds at the time of capture.

Latest snapshot: **{{snapshot_date}}** (captured at {{captured_at}}, schema v{{schema_version}}).

## Tables

| Config | Grain | What's in it |
|---|---|---|
| `headlines` | 1 row per (bench, day) | Title, metric, leader, headline value, license, source URL |
| `providers` | 1 row per (bench, provider, day) | p50/p90/p99, mean, success rate, sample size, is_leader flag |
| `timeseries` | 1 row per (bench, point, day) | 24h sparkline values (~72 points per bench) |

All tables are partitioned by `snapshot_date=YYYY-MM-DD`. Each row also
carries a `captured_at` timestamp (UTC, ISO 8601) and a `schema_version`
integer.

## Quick start

### Python (datasets)

```python
from datasets import load_dataset
ds = load_dataset("OpenChainBench/benchmarks", "headlines", split="train")
print(ds.filter(lambda r: r["slug"] == "bridge-quote-latency")[0])
```

### Polars (recommended for analytics)

```python
import polars as pl
df = pl.scan_parquet(
"hf://datasets/OpenChainBench/benchmarks/headlines/**/*.parquet"
)
latest = (
df.filter(pl.col("snapshot_date") == df.select(pl.col("snapshot_date").max()).collect().item())
.select(["slug", "leader_name", "value", "unit"])
.collect()
)
print(latest)
```

### DuckDB (one-liner)

```sql
SELECT slug, leader_name, value, unit
FROM 'hf://datasets/OpenChainBench/benchmarks/headlines/**/*.parquet'
WHERE snapshot_date = (SELECT max(snapshot_date) FROM 'hf://datasets/OpenChainBench/benchmarks/headlines/**/*.parquet');
```

More examples in `examples/`.

## Methodology

Each benchmark documents its full methodology on the OCB site (per-bench
URL is in the `citation_url` column of `headlines`). Reading list:

- Per-bench page (open methodology): `https://openchainbench.com/benchmarks/<slug>`
- Site-wide methodology: `https://openchainbench.com/methodology`
- YAML spec source (Apache 2.0): `https://github.com/ChainBench/OpenChainBench/tree/main/benchmarks`

Harness code is open source where applicable (column `source_url`).

## Schema stability promise

- New columns may be added without warning. Consumer queries should
select named columns rather than `SELECT *`.
- Existing columns will never be renamed or removed. If a breaking
change ever proves unavoidable, a parallel v2 folder ships alongside
the v1 partitions so old consumers keep working.
- `schema_version` integer in each row marks the additive schema epoch.

## Update cadence

Snapshots run daily at 00:00 UTC via a GitHub Action. If a run aborts
on a quorum check (the source feed has fewer than half its benches
live), no new partition is added that day - the previous good snapshot
stays as truth.

## License & citation

Data is released under
[Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/).
Use it freely with attribution to OpenChainBench.

For academic citation see `CITATION.cff` at the root of this dataset
(GitHub / HF / Zenodo all parse it). Suggested attribution string:

> OpenChainBench. (2026). OpenChainBench Benchmarks [Data set].
> Hugging Face. https://huggingface.co/datasets/OpenChainBench/benchmarks

## Provenance

- Source code (publisher + benchmark YAML specs): https://github.com/ChainBench/OpenChainBench
- Live measurement APIs: https://openchainbench.com/api/citable, /api/stat, /api/llm-context, /api/mcp
- Issues / questions: https://github.com/ChainBench/OpenChainBench/issues
25 changes: 25 additions & 0 deletions scripts/hf_publisher/dataset_template/examples/01_pandas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Load the OCB headlines feed with pandas via the Hugging Face datasets
library. Good when you want a familiar DataFrame and the dataset is
small enough to fit in memory (it is).
"""

from datasets import load_dataset

ds = load_dataset(
"OpenChainBench/benchmarks",
"headlines",
split="train",
)
df = ds.to_pandas()

# Latest snapshot only
latest = df["snapshot_date"].max()
today = df[df["snapshot_date"] == latest]

# Top 10 benchmarks by sample size today
print(
today.sort_values("sample_size", ascending=False)[
["slug", "leader_name", "value", "unit", "sample_size"]
].head(10)
)
23 changes: 23 additions & 0 deletions scripts/hf_publisher/dataset_template/examples/02_polars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Stream the OCB providers feed with Polars directly from HF. Pushdown
predicate + projection means only the columns and partitions you ask
for ever hit the wire. Recommended for analytic workloads.
"""

import polars as pl

providers = pl.scan_parquet(
"hf://datasets/OpenChainBench/benchmarks/providers/**/*.parquet"
)

# Trend of Mobula's p50 latency on bridge-quote-latency across all snapshots
trend = (
providers.filter(
(pl.col("bench_slug") == "bridge-quote-latency")
& (pl.col("provider_slug") == "mobula")
)
.select(["snapshot_date", "p50", "p90", "p99", "sample_size"])
.sort("snapshot_date")
.collect()
)
print(trend)
Loading
Loading