8000 Releases · ynishi/evalhub · GitHub
[go: up one dir, main page]

Skip to content

Releases

evalhub 0.4.0

Choose a tag to compare

@github-actions github-actions released this 28 Sep 02:40
ebda7bd
8000

Close the evalhub.eval/1.0 window: a 1.0 body is 422 runs_moved, GET /schemas/eval is gone (0.4.0) (#54)

0.4.0: the evalhub.eval/1.0 compatibility window is closed

Closes #45 and #51. Releases 0.2.0 and 0.3.0 accepted an evalhub.eval/1.0 body (header and runs[] in one document) on POST /evals/{ns}/{name} and converted it at ingest into a 2.0 header and its runs, answering with a Deprecation header and converted_from / converted_runs in the envelope. The schema crate's rule is that the previous major is accepted for one release and then refused; the README and the crate docs said 0.3.0 would refuse it, and 0.3.0 shipped with the window open (#51). 0.4.0 closes it.

Behaviour

  • POST /evals/{ns}/{name} with schema: evalhub.eval/1.0 answers 422 with exactly one error, runs_moved at /runs, with or without runs and whatever else is wrong with the body. It is the error a 2.0 header carrying runs already got. No Deprecation header, no converted_from, nothing is written. The hint names PUT /evals/{ns}/{name}/runs/{run_id} and POST /evals/{ns}/{name}/runs:batch.
  • A 2.0 header carrying runs whose attachments[].sha256 is malformed also answers that single runs_moved (previously the sha error was appended after it).
  • GET /schemas/eval (the 1.0 document) is gone: 404. GET /schemas/eval-2 and GET /schemas/run are unchanged.
  • Stored data is untouched. A 0.1.x database still migrates: 0003_runs_split keeps its own reading of the 1.0 shape inside evalhub-store (unpublished), byte-for-byte the conversion evalhub-core used to expose.
  • Producers still sending 1.0 bodies get 422 after this deploy; the README's 0.1.x → 0.2.0 mapping stays as the record of what to change.

SDK (why 0.4.0)

just semver-check reports major for evalhub-schema and evalhub-core; evalhub-query is unchanged.

  • evalhub-schema: eval::v1 (Eval, Run, Outcome) and schema_for_eval_v1() removed; SCHEMA_NAMES is [&str; 5] (eval dropped; cargo-semver-checks does not flag the array length); RecordKind::Eval.schema_ids() is ["evalhub.eval/2.0"], schema_for("evalhub.eval/1.0") is None, all_schemas() no longer yields eval; schemas/eval.json removed; ErrorCode::RunsMoved doc covers the 1.0 declaration.
  • evalhub-core: EvalSchema and declared_schema removed (the 1.0 arm of the validator). The eval module went with them: split_v1, SplitV1, UNRECORDED, V1_MIGRATION_META_KEY now live in evalhub-store for the data migration only. validate(Eval, …) on a 1.0 body returns [runs_moved].
  • OpenAPI: ConvertedRunsDto, converted_from, converted_runs removed; the web client (schema.d.ts) regenerated.

Docs

Every sentence that promised the removal for 0.3.0 (README §API, §Upgrading from 0.1.x; rustdoc in schema, core, store, server) now records that 0.2.0 and 0.3.0 accepted 1.0 and 0.4.0 closed the window. web/README.md names eval-2 instead of the removed eval.

Verification

cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test -p evalhub-schema (20) / -core (56) / -query (27) / -store (104, Docker) / -server (86, Docker), cargo insta test -p evalhub-schema and -p evalhub-server (no pending snapshots), RUSTDOCFLAGS=-D warnings cargo doc for schema / core / store, just semver-check (major for schema and core before the bump; "no semver update required" for all three at 0.4.0), just package (three crates ok), corepack pnpm check / pnpm lint in web/. RED observed before each behaviour change: schema_ids() still listed 1.0; a 1.0 body validated with zero or unrelated errors; POST answered 201 with converted_from; GET /schemas/eval answered 200; a malformed sha256 produced two errors.

Fly is deployed separately after the release (fly deploy --ha=false); nothing stored changes, so no pre-deploy data check is needed. Producers still posting 1.0 bodies get 422 runs_moved from then on.


SDK crates: evalhub-schema evalhub-core evalhub-query
Image: ghcr.io/ynishi/evalhub:0.4.0

evalhub 0.3.0

Choose a tag to compare

@github-actions github-actions released this 27 Sep 08:41
c594835

Validate ext keys as {ns}/{name} on records, runs and facets: ext_key_invalid (0.3.0) (#49)

0.3.0: ext keys are checked as {ns}/{name}

Fixes #46. The schema crate and the served JSON Schema have always said that ext is keyed by namespace ({ns}/{name}), and nothing checked it: a record or a run with "ext": {"notes": {}} was accepted, stored and served.

Behaviour

  • Every key of ext is validated with the same rule as a metric id, at the top level of a record or a run and inside each facet that carries an ext of its own (model, task, harness, generation, trial, grading, env).
  • A violation is 422 with one ext_key_invalid per key, at /ext/{key} or /{facet}/ext/{key}. Values are still not looked into.
  • Stored bodies are not rewritten. The rule applies to writes; a 0.2.0 body with an un-namespaced key keeps being read.

SDK (why 0.3.0)

  • evalhub_schema::error::ErrorCode gains ExtKeyInvalid and is now #[non_exhaustive], as its doc already asked of clients ("treat an unknown code as a generic 422"). A match on it in another crate needs a fallback arm.
  • schemas/error.json, the OpenAPI document and the web client's ErrorCode union are regenerated.

Deploy

Before fly deploy, run bash deploy/fly/ext-keys-check.sh (read-only) to list the stored keys a 0.2.0 hub accepted that 0.3.0 refuses on write, and record the result on #46. See docs/hosting.md, "Upgrading to 0.3.0".

Verification

cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test -p evalhub-schema / -core / -query / -server (Docker), cargo insta test -p evalhub-server --test boot, just semver-check, just package, pnpm check / pnpm lint. Store tests not run (no Rust change there).


SDK crates: evalhub-schema evalhub-core evalhub-query
Image: ghcr.io 8000 /ynishi/evalhub:0.3.0

evalhub 0.2.0

Choose a tag to compare

@github-actions github-actions released this 26 Sep 08:00
e8d8b6d

Release 0.2.0: runs as first-class rows (#44)

0.2.0 makes an Eval's runs first-class. An Eval is now a versioned header plus a set of runs that belong to the record, not to a version: each run is a row written by run_id, with its own conditions (the header's facets as defaults), status, metrics and attachments, and it can be overwritten, archived or deleted without appending a header version. The verdict on a run moves to the Card (run_results), and GET /evals/{ns}/{name}/runs reads runs × metrics × the chosen Cards' judgements in one page. Four hashes keep it verifiable: the header's content_hash, each run's content_hash, the record's runs_hash, and a Card's used-set hash, from which changed_since_card is derived. This is the 9-stage change of #24, landed as one release.

Upgrading a hosted database

evalhub migrate for 0.2.0 applies the 0003 DDL and then a one-shot data migration, 0003_runs_split, recorded in data_migrations so it never runs twice. serve refuses to start until both have run.

  • Runs are collected from every live 1.0 version by run_id (highest seq wins) and become rows. pass / fail → ok, error → error with error.kind: unrecorded, skipped → skipped with skip_reason: unrecorded; the dropped outcome is kept as meta.v0.2.0_migration.outcome; the source version's header facets are copied onto each run.
  • Every live 1.0 version body becomes its 2.0 header (no runs, schema 2.0). Header content_hash values change; the old and new hash of every version are in the audit log (migration.runs_split). Versions that differed only in runs become identical header versions.
  • Each Card's used runs are rebuilt from the Eval version it points at, so a run that changed since shows as changed at once. A Card's relations keep resolving because they point at version ids, not hashes. Used run ids with no run are skipped and recorded as migration.card_runs_unmatched (0.1.x never checked attrs.runs); these are expected, not errors.
  • The step is one transaction: a failure (or the release command's 5-minute timeout) rolls it back and leaves it pending. A run_id it cannot accept is named in the error, and nothing is written.

Production deploy (docs/hosting.md, "Upgrading to 0.2.0"; not the plain fly deploy of other releases), from the checkout of the v0.2.0 tag:

  1. Snapshot: fly volumes snapshots create <volume-id> --app evalhub-db (and a pg_dump).
  2. Stop writes: fly machine update <machine-id> --autostart=false --skip-start --app evalhub, then fly machine stop <machine-id> --app evalhub; /api/v1/healthz must fail. Otherwise the old binary can store a 1.0 body after the migration and nothing would split it.
  3. Deploy: fly deploy --ha=false. The release command logs applied data migration 0003_runs_split: …. If the Machine is still stopped afterwards, fly machine start <machine-id> --app evalhub.
  4. Confirm: fly releases --app evalhub and fly ssh console --app evalhub -C "evalhub --version" (→ evalhub 0.2.0); /api/v1/healthz and /openapi.json (info.version stays the contract's 1.0.0) confirm liveness only.
  5. Smoke: bash deploy/fly/smoke.sh (header POST, two run PUTs, a Card with run_results, the projection, anonymous reads after publishing).

Upgrading a client

0.1.x 0.2.0
POST /evals/{ns}/{name} with runs[] in the body POST the header without runs, then PUT …/runs/{run_id} per run or POST …/runs:batch
runs[].outcome (pass / fail / error / skipped) the run's status (ok / error with error.kind / skipped with skip_reason), and the pass / fail in the Card's run_results[]
"schema": "evalhub.eval/1.0" "schema": "evalhub.eval/2.0" (GET /schemas/eval-2)
"schema": "evalhub.card/1.0" "schema": "evalhub.card/1.1" (adds run_results); evalhub.card/1.0 is still accepted
  • An evalhub.eval/1.0 body with runs[] is accepted and converted in 0.2.0 only: stored as a 2.0 header plus run rows, with a Deprecation: @1790294400 header (RFC 9745), converted_from: "evalhub.eval/1.0" and converted_runs in the response. 0.3.0 rejects it with 422 runs_moved, as 0.2.0 already does for a 2.0 header carrying runs. The issue for that removal is opened when 0.2.0 lands, with its date.
  • Two things a 1.0 client notices already in 0.2.0: the returned content_hash is the converted 2.0 header's, not the hash of the posted body; and re-posting a body that omits a run no longer drops that run (runs are rows now; archive or delete it).
  • A run PUT must carry run_id in the body, equal to the path's.

API

  • Runs under /api/v1/evals/{ns}/{name}: PUT /runs/{run_id} (201 created / 200 updated / 200 unchanged), POST /runs:batch (all or nothing; a refusal lists every failing element), GET /runs (the projection: cards=, where, sort, limit 1–200, cursor, include=archived,deleted for members), GET / PATCH (archived) / DELETE (tombstone) /runs/{run_id}. Runs follow the Eval's visibility; every route is 404 for an Eval the caller may not see.
  • Projection paths, bracket form: run_id, status, error.kind, started_at, ended_at, facet keys (model.id, …), fingerprint.{facet}, metrics[{ns}/{name}], results[{card}][{metric}].value / .label, ext.*. Per Card the page reports runs_used, used_set_hash, posted_used_set_hash and changed_since_card.
  • GET /evals/{ns}/{name}[@…] carries runs: { count, by_status, archived, deleted, runs_hash } (archived / deleted for members only). The comparison view (GET /evals/…/cards) compares same_harness / same_model against every run a Card used and adds runs_used, used_set_hash, changed_since_card.
  • Redaction: a public Card's run_results entries that name an Eval the reader may not see are removed and counted in withheld.run_results (GET and POST /cards/query); a writer naming a run of an Eval they may not see gets run_unknown, as for a missing run.
  • Limits ([limits]): body_bytes 16 MiB (413 body_too_large, every endpoint; was axum's 2 MiB default), batch_runs 1,000 (413 batch_too_large), run_results 100,000 per Card version (422 too_many_run_results). Refused, never truncated.
  • Schemas: GET /schemas/eval-2 (the header) and GET /schemas/run are new; GET /schemas/eval stays the 1.0 document until 0.3.0.
  • Error codes (new): runs_moved, run_status_detail, run_id_invalid, run_id_mismatch, run_result_value_or_label, run_results_eval_unknown, run_unknown, run_not_in_used_set, too_many_run_results, batch_duplicate_run_id (422); run_deleted (409); batch_too_large, body_too_large (413).
  • Audit: run.create / run.update / run.archive / run.unarchive / run.delete, migration.runs_split, migration.card_runs_unmatched.
  • info.version of /openapi.json stays 1.0.0.

Crates

evalhub-schema, evalhub-core and evalhub-query are published at 0.2.0 (the store and the server ship as ghcr.io/ynishi/evalhub:0.2.0). just semver-check against 0.1.4 on crates.io, before the bump:

Crate Verdict
evalhub-schema requires a new major (0.2 on 0.x): 5 lints — Eval.runs removed, eval::Run and eval::Outcome removed, Card.run_results added to an exhaustively constructible struct, 13 ErrorCode variants added
evalhub-core no semver update required (196 checks: 196 pass, 58 skip); bumped with the workspace
evalhub-query requires a new major (0.2 on 0.x): 1 lint — Column::Run and SortKey::Run added to exhaustive enums

At 0.2.0 the same command reports "no semver update required" for all three (0 checks, 254 skipped: a major bump skips the lints).

Changes by stage

  • #36 Schema: Run leaves the Eval body; per-run facets, status and metrics; Card.run_results; evalhub.eval/2.0 (#27)
  • #37 Core: validate a Run, refuse runs in an Eval body, check run_results, fingerprint a run, publish the run hash formulas (#28)
  • #38 Query: a run projection target — path table, run columns, sort keys and cursor for GET /evals/{ns}/{name}/runs (#29)
  • #39 Store: run tables, run put/batch/archive/delete, runs_hash, audit actions, GC and download references for run attachments (#30)
  • #40 Store: the Card's used set and run_results at ingest, the run projection query, hidden Evals for redaction, comparison over used runs (#31)
  • #41 Migration: split runs out of stored Eval bodies (0.1.x → 0.2.0), record that it ran, refuse to serve until it has (#32)
  • #42 Server: run endpoints, runs:batch, request limits, run_results redaction, the Eval runs summary, comparison counts, OpenAPI (#33)
  • #43 Web UI and deploy scripts: the Eval page reads /runs and shows status, error kind, metrics and withheld counts; the Fly smoke test PUTs runs (#34)
  • This PR: README (run paths, [limits], schema ids, Upgrading from 0.1.x), the crate docs' final pass, the 0.2.0 bump (#35)

Verified

Command Outcome
cargo fmt --check clean
cargo clippy --workspace --all-targets -- -D warnings clean
cargo test -p evalhub-schema 20 passed
cargo test -p evalhub-core 62 passed
cargo test -p evalhub-query 27 passed
cargo test -p evalhub-store (Docker) 96 passed
cargo test -p evalhub-server (Docker) 82 passed, no .snap.new
just sqlx-check (throwaway postgres:16-alpine, migrations 0001–0003) exit 0
cargo deny check advisories ok, bans ok, licenses ok, sources ok
RUSTDOCFLAGS=-D warnings cargo doc --no-deps (five crates) clean
just semver-check (before the bump) schema: major, 5 lints; core: none; query: major, 1 lint
just semver-check (at 0.2.0, on the commit) all three: "no semver update required" (0 checks, 254 skipped: a major bump skips the lints)
just package (on the commit) evalhub-schema 66422, `...
Read more

evalhub 0.1.4

Choose a tag to compare

@github-actions github-actions released this 25 Sep 01:16
c262373

Relations: a reader or writer learns nothing of a version it may not see (0.1.4) (#26)

For #25. Releases 0.1.4.

evalhub_schema::eval promises that when a public Card points at a private Eval, a reader without access learns that the Eval exists, its version_id and its content_hash, and nothing else. The hub kept that promise only in GET …/relations. Three other paths gave the Eval away:

  1. Reads returned the Card body verbatim. GET /cards/… and POST /cards/query returned relations[].to, which is the private ns/name@seq as text.
  2. Query conditions matched every edge. A relations.to condition (exact or prefix) was evaluated against targets the caller could not see, so a caller could confirm a private name by whether a public Card was hit.
  3. Writes resolved targets regardless of visibility. Posting a Card or POST …/relations that named someone else's private version resolved it, so the response and the refs_resolved badge told the writer that the version existed.

Export was not a path: hf-model-index is rendered from results[] and carries no relation.

What changes

  • Reads withhold what the reader may not see. Every read that returns a body removes the relations[] elements whose target the reader may not see, and lists them in the envelope as the commitment:

    "withheld": { "relations": [ { "type": "core/uses_eval", "version_id": "…", "content_hash": "…" } ] }

    A body with elements withheld is not the stored body, so its content_hash does not match it. Verify the hash only on responses that have no withheld. A member of the Eval's namespace gets the body unchanged and no withheld.

  • Query conditions see only visible targets. A relations.to condition matches only edges whose target the caller may see. Negated, the hidden edge is just as absent.

  • A write learns no more than a read. At ingest, in POST …/relations and in the deferred resolver, a target the writer may not see is handled exactly like one that does not exist: it is stored unresolved, the response is the same and no refs_resolved badge is given. Once the target is visible to a reader, the edge resolves for them. The writer's own namespace and their organisations' namespaces are visible, so legitimate private references resolve as before.

  • One visibility predicate (evalhub_store::relations::visible_to) serves the relation DTO, body redaction and resolution.

Contract

  • withheld is new and optional on the version envelope and on query hits. The OpenAPI snapshot and the web/ client are regenerated.
  • The SDK crates' public API is unchanged: just semver-check reports no semver update required for evalhub-schema, -core and -query. The schema change is limited to the crate doc (eval § "Privacy across the relation").
  • .sqlx is regenerated for the changed queries.

Verification

  • crates/evalhub-server/tests/privacy.rs: five HTTP tests covering the body on GET, the body on query, the relations.to condition, the badge on POST, and POST …/relations. All five were first run against main and failed on the defect itself: alice/secret@1 appeared in the body, bob's condition hit, the badge differed from that of a nonexistent target, and the relation response revealed the target.
  • Store tests: relations_resolve_only_to_versions_the_writer_may_see, resolution_never_reveals_a_private_version and relation_filters_see_only_targets_the_caller_may_see were checked against this commit with the three visibility checks disabled, and each failed on the leak.
  • cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test -p evalhub-schema / -core / -query / -store / -server (Docker); just sqlx-check; pnpm check; just e2e (18 passed); just package + package-gate; just semver-check.

Test image: MinIO from Chainguard

The first CI run of this PR failed in evalhub-store tests/objects.rs before any test code ran: quay.io now answers 401 to anonymous pulls of quay.io/minio/minio (and minio/mc), which passed on main the day before. The store and server test harnesses and compose.yml now use cgr.dev/chainguard/minio, pinned by digest (it publishes latest only). It ships mc and a shell, so the bucket setup is unchanged. The tests mount a tmpfs on /data and wait for the API: http banner, because on the overlay root MinIO first logs a rename error that also starts with API:.


SDK crates: evalhub-schema evalhub-core evalhub-query
Image: ghcr.io/ynishi/evalhub:0.1.4

evalhub 0.1.3

Choose a tag to compare

@github-actions github-actions released this 24 Sep 04:36
0c96b4a

README: the UI ships in the binary, and /whoami returns no roles (0.1.3) (#23)

Release 0.1.3. What changed since 0.1.2:

Organisations in the web UI (#17)

Settings creates an organisation (the creator is its first admin); the organisation's namespace page shows its roster and adds, updates and removes members. Browser tests drive the flow against the release binary.

The roster rule, documented as the token rule it is (#20)

GET /orgs/{org}/members asks that the presented token names the organisation, like every other read. The module doc, the handler, /openapi.json and the README said membership was enough; they now say what the code does. docs/hosting.md gains a section on making an organisation in two steps: create it, then issue a token that names it.

The organisation page after creating one (#21)

A signed-in token that does not name the organisation now gets a line saying why the roster is missing and a link to Settings with the token form filled in; Settings fills the same form right after creating an organisation. Member controls appear only for an admin token of an admin member, which is what the hub accepts.

This pull request

README.md: §Status no longer says the UI is being built, and the /whoami row names the fields the response has (user, scope, namespaces — no organisation roles). Carries the version bump to 0.1.3.

Verification

cargo fmt --check; cargo clippy --workspace --all-targets -- -D warnings; cargo test -p evalhub-schema, -core, -query; just package (all three crates at 0.1.3, gate ok); just semver-check: no semver update required for any of the three. No Rust source changed in this pull request; evalhub-store / evalhub-server tests were not run here.

The UI changes of #21 were deployed to the hosted service from main before this release and passed deploy/fly/smoke.sh.

Refs #22


SDK crates: evalhub-schema evalhub-core evalhub-query
Image: ghcr.io/ynishi/evalhub:0.1.3

evalhub 0.1.2

Choose a tag to compare

@github-actions github-actions released this 22 Sep 11:01
98d39c6

Release from the tag: crates, image and GitHub Release by one workflow (0.1.2) (#15)

The tag push becomes the only release action a person takes; the release workflow does the rest.

What changes

  • .github/workflows/release.yml on a v* tag:
    • check: the tag names the version in Cargo.toml and the tagged commit is on main; otherwise nothing runs.
    • crates: just package, cargo semver-checks against the versions on crates.io, then cargo publish for whichever of evalhub-schema / evalhub-core / evalhub-query is not yet on crates.io at that version. Authentication is crates.io Trusted Publishing (OIDC, rust-lang/crates-io-auth-action); no token is stored. A rerun publishes nothing twice.
    • image: as before (ghcr.io/ynishi/evalhub:<version>, :latest).
    • release: a GitHub Release for the tag, notes from the merged pull request's body plus crate and image links.
  • deploy/release/land.sh <pr> <issue>: merge with a merge commit (never squash), close the issue, fast-forward main, then tag.sh.
  • deploy/release/tag.sh: on main, clean, HEAD is origin/main, CI for HEAD green (waits), version from Cargo.toml, tag once, push, follow the workflow with gh run watch.
  • just semver-check; CONTRIBUTING §Releases; pointers in AGENTS.md and docs/hosting.md.

Why not release-plz (dsl-kit's flow)

Its version and changelog are computed from Conventional Commit headers, which this repository does not use (prose subjects, bump riding with the last change), and its release PR carries a bot commit without Signed-off-by. What it is for (no hand-typed publish, one tag as the trigger, a semver check, a release page) is taken; the rest is left.

0.1.2

This PR is the release that exercises the workflow it adds: the version bump rides in its one commit, and landing it (deploy/release/land.sh 15 14) tags v0.1.2 from a commit that contains release.yml. No Rust code changed since 0.1.1.

Verified

  • bash -n on both scripts; release.yml parses; the PR lookup by merge sha returns #13 for the 0.1.1 merge commit; gh run watch --exit-status exists.
  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test -p evalhub-schema / -core / -query, just package at 0.1.2, just semver-check against 0.1.1.
  • Trusted Publishing is enabled on crates.io for the three crates (repository ynishi/evalhub, workflow release.yml).
  • Not run before merge: the workflow itself; that is what the tag does.

Refs #14


SDK crates: evalhub-schema evalhub-core evalhub-query
Image: ghcr.io/ynishi/evalhub:0.1.2

0