deltaleague 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deltaleague-0.1.0/ARTIFACTS.md +25 -0
- deltaleague-0.1.0/LOCAL.md +75 -0
- deltaleague-0.1.0/MANIFEST.in +5 -0
- deltaleague-0.1.0/PKG-INFO +83 -0
- deltaleague-0.1.0/README.md +56 -0
- deltaleague-0.1.0/delta_cli/__init__.py +0 -0
- deltaleague-0.1.0/delta_cli/artifacts.py +163 -0
- deltaleague-0.1.0/delta_cli/local.py +193 -0
- deltaleague-0.1.0/delta_cli/local_commands.py +107 -0
- deltaleague-0.1.0/delta_cli/main.py +282 -0
- deltaleague-0.1.0/delta_cli/scoring.py +35 -0
- deltaleague-0.1.0/deltaleague.egg-info/PKG-INFO +83 -0
- deltaleague-0.1.0/deltaleague.egg-info/SOURCES.txt +19 -0
- deltaleague-0.1.0/deltaleague.egg-info/dependency_links.txt +1 -0
- deltaleague-0.1.0/deltaleague.egg-info/entry_points.txt +2 -0
- deltaleague-0.1.0/deltaleague.egg-info/requires.txt +11 -0
- deltaleague-0.1.0/deltaleague.egg-info/top_level.txt +1 -0
- deltaleague-0.1.0/pyproject.toml +27 -0
- deltaleague-0.1.0/setup.cfg +4 -0
- deltaleague-0.1.0/tests/test_cli.py +71 -0
- deltaleague-0.1.0/tests/test_local.py +139 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Delta submission format version 1
|
|
2
|
+
|
|
3
|
+
A directory contains `manifest.json` and `weights.safetensors`. The immutable parent version and its canonical content hash are mandatory. All tensor payloads use safetensors; no pickle or participant code is loaded.
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"schema_version": "delta.submission.v1",
|
|
8
|
+
"reconstruction_version": "delta-reconstruct-v1",
|
|
9
|
+
"model_config_id": "qwen35-08b-v1",
|
|
10
|
+
"parent_version_id": "UUID",
|
|
11
|
+
"parent_content_hash": "64 lowercase hex characters",
|
|
12
|
+
"format": "sparse-diff",
|
|
13
|
+
"operation": "replace",
|
|
14
|
+
"artifacts": [{"name": "weights.safetensors", "sha256": "64 lowercase hex characters", "size_bytes": 1024}],
|
|
15
|
+
"tensors": {"model.language_model.layers.0.mlp.down_proj.weight": {"indices": "t0.indices", "values": "t0.values"}}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Sparse indices are int64, flat, strictly increasing and in bounds. Values are one-dimensional and match index count. Replacement sets those elements; addition accumulates parent and delta in float32 before casting to the registered parent dtype. Untouched tensors inherit the parent. `dense-diff` uses a `values` tensor matching the full target shape. `lora` maps each target to `a`, `b`, and finite `alpha`; apply `W + (alpha/r) * (B @ A)` in float32, then cast. A is `[r, in]`, B is `[out, r]`, rank is 1–256. `full` uses exact parent tensor names and shapes; changed frozen tensors are rejected.
|
|
20
|
+
|
|
21
|
+
Canonical identity is SHA-256 over canonical JSON identity metadata and sorted named tensors, each with a length-prefixed JSON header (name, shape, dtype) followed by contiguous raw CPU bytes. Model/config/tokenizer identity participates in the hash. Server reconstruction is authoritative. Different submissions may resolve to one model content hash without sharing provenance.
|
|
22
|
+
|
|
23
|
+
Client requests use `Authorization: Bearer <delta-token>`. Request `/api/v1/upload-sessions` with the manifest, PUT bytes to returned URLs, then POST `/api/v1/submissions` with `upload_id`, `title`, and an `Idempotency-Key` header. Retrying a submission uses the same key and upload. Signed URLs are credentials; do not publish them. Single-object uploads support the first model; multipart/resumable uploads remain a launch-completion task.
|
|
24
|
+
|
|
25
|
+
All metrics are JSON. Validation is the equally weighted mean of configured benchmark scores on a 0–100 scale. Private test is a single overall score. A missing score is null, never zero. Private example text and generations are not returned. Public validation can be downloaded through `/api/v1/evaluation-configs/<id>`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Run and evaluate locally
|
|
2
|
+
|
|
3
|
+
Local inference uses Transformers and PyTorch. Apple Silicon Macs select MPS, Linux machines with a compatible PyTorch GPU build select CUDA, and other machines use CPU. `--device cpu`, `--device mps`, or `--device cuda` overrides selection. There is no rented compute, server evaluation quota, or automatic submission involved.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
From the CLI repository, in a Python 3.12+ virtual environment:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pip install ".[local]"
|
|
11
|
+
delta competition init qwen35-08b-v1 --output experiment
|
|
12
|
+
cd experiment
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The extra installs the tested Transformers version and math scorer. For Linux CUDA acceleration, install a compatible CUDA build of PyTorch using [the official installer](https://pytorch.org/get-started/locally/). If your existing PyTorch build is CPU-only, installing the CLI extra will not replace it with a CUDA build. For IFEval configurations, additionally install the CLI's `[ifeval]` extra.
|
|
16
|
+
|
|
17
|
+
The portable implementation loads full weights, without automatic quantization. Allow several GB of free RAM for Qwen3.5-0.8B and more for long contexts. CPU/MPS use float32; CUDA uses BF16 where supported. On Macs, unsupported MPS operations can fall back to CPU. If your model encounters an unsupported operator or memory error, try `--device cpu` and lower the output budget. macOS acceleration has not been verified on physical Apple hardware in this release; Linux CPU has been exercised with the actual Qwen checkpoint. CUDA selection is implemented but has not been hardware-tested for this CLI release.
|
|
18
|
+
|
|
19
|
+
## Ask the model
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
delta run "What is 2 + 2?" --max-new-tokens 64 --no-thinking
|
|
23
|
+
delta run "Explain your solution to 17 * 23." --max-new-tokens 512 --json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`run` defaults to 256 output tokens. Thinking behavior otherwise comes from `evaluation.json`. A short budget may end before the final answer; the command reports that. Text goes to stdout, progress to stderr. `--json` adds provenance and token statistics. `--seed` defaults to 1000. Use `--workspace PATH` to work from another directory.
|
|
27
|
+
|
|
28
|
+
## Run any leaderboard entry
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
delta leaderboard --model qwen35-08b-v1
|
|
32
|
+
# Copy the desired entry's version_id, not its submission ID.
|
|
33
|
+
delta run "What is 2 + 2?" --version VERSION_ID --no-thinking
|
|
34
|
+
delta evaluate --version VERSION_ID --limit 2 --max-new-tokens 256 --output entry-quick.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`--version` downloads the immutable resulting checkpoint, verifies its hashes, and caches it under `.delta/models/VERSION_ID` inside the workspace. It works for the original model and submitted model versions, regardless of whether the submission used a LoRA, diff, or full weights. The version must belong to the workspace's model. Downloads need network access but not login. The inference runs locally; no private test data is downloaded. Completed cached files are reused on subsequent commands.
|
|
38
|
+
|
|
39
|
+
For a fully offline workspace based on that entry, run `delta competition init MODEL_ID --parent VERSION_ID --output another-experiment` while online, then run/evaluate from that workspace without `--version`.
|
|
40
|
+
|
|
41
|
+
## Try your own edit or patch
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
delta run "What is 2 + 2?" --weights edited.safetensors --no-thinking
|
|
45
|
+
delta evaluate --weights edited.safetensors --limit 2 --max-new-tokens 256 --output edit-quick.json
|
|
46
|
+
|
|
47
|
+
delta evaluate --patch submission --limit 2 --max-new-tokens 256 --output patch-quick.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`--weights` takes a full modified safetensors checkpoint, using the workspace parent's tokenizer and configuration. `--patch` takes a submission directory with `manifest.json`; it checks the parent and artifact hashes, then reconstructs supported LoRA/sparse/dense/full artifacts into a temporary checkpoint. Neither changes the parent. You can combine either with `--version` when editing that leaderboard checkpoint; a patch must reference that exact version. Temporary reconstructed files need disk space roughly equal to a full checkpoint and are removed on completion.
|
|
51
|
+
|
|
52
|
+
## Evaluate public validation
|
|
53
|
+
|
|
54
|
+
Start small:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
delta evaluate --limit 2 --max-new-tokens 256 --output baseline-quick.json
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`--limit` selects the first N problems **per benchmark**; `--benchmark gsm8k` selects one benchmark. These are development checks, not representative estimates of the full public score.
|
|
61
|
+
|
|
62
|
+
For the entire public set with its configured generation settings:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
delta evaluate --output baseline-full.json
|
|
66
|
+
delta evaluate --weights edited.safetensors --output edit-full.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Full evaluation can be expensive locally: the starter configuration permits 32,768 generated tokens per example. The CLI prints the number of generations and upper token budget before loading the model. Use Ctrl-C to interrupt. Completed generation records remain in the JSONL file; no complete score report is written for an interrupted run. Choose a fresh output filename to rerun; automatic resume is not implemented.
|
|
70
|
+
|
|
71
|
+
The JSON report contains per-benchmark scores, the equal-weight mean across selected benchmarks, per-example sample averaging, checkpoint hash, source version, validation hash, runtime versions, actual generation settings, coverage, and truncation statistics. A companion `*.generations.jsonl` contains answers and per-sample scores. Existing output files are never overwritten.
|
|
72
|
+
|
|
73
|
+
The local scorer shares the platform's answer-scoring implementation: reasoning traces before `</think>` are excluded, math uses math-verify, and multiple-choice answers follow the same extraction rules. Configured samples use the same per-sample seeds. `--max-new-tokens` and `--thinking/--no-thinking` overrides are recorded explicitly.
|
|
74
|
+
|
|
75
|
+
Scores remain **advisory local results**. Transformers, vLLM, device precision, and sampling implementations can differ even with the same seed. Compare candidates using the same local settings, then submit for the official validation and private test scores. Local evaluation does not upload results or assign a leaderboard rank.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deltaleague
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI for Delta League model-weight competitions, submissions, and local evaluation
|
|
5
|
+
Author: Delta League
|
|
6
|
+
Project-URL: Homepage, https://deltaleague.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/ilblackdragon/deltaleague
|
|
8
|
+
Project-URL: Documentation, https://github.com/ilblackdragon/deltaleague/blob/main/README.md
|
|
9
|
+
Project-URL: Issues, https://github.com/ilblackdragon/deltaleague/issues
|
|
10
|
+
Keywords: llm,benchmark,model-weights,leaderboard
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: httpx<1,>=0.28
|
|
19
|
+
Requires-Dist: typer<1,>=0.16
|
|
20
|
+
Requires-Dist: safetensors<1,>=0.5
|
|
21
|
+
Requires-Dist: torch>=2.6
|
|
22
|
+
Provides-Extra: local
|
|
23
|
+
Requires-Dist: transformers==5.22.1; extra == "local"
|
|
24
|
+
Requires-Dist: math-verify==0.9.0; extra == "local"
|
|
25
|
+
Provides-Extra: ifeval
|
|
26
|
+
Requires-Dist: lm-eval<0.5,>=0.4.9; extra == "ifeval"
|
|
27
|
+
|
|
28
|
+
# Delta League CLI
|
|
29
|
+
|
|
30
|
+
The CLI and submission tools for [Delta League](https://deltaleague.ai): the open competition to improve intelligence.
|
|
31
|
+
|
|
32
|
+
Submit a patch. Prove an improvement. Advance the model.
|
|
33
|
+
|
|
34
|
+
Repository: [ilblackdragon/deltaleague](https://github.com/ilblackdragon/deltaleague). Private during launch preparation; intended to become public. The platform is maintained separately. This package contains no private test data.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
Requires Python 3.12+. Clone this repository, then install `pip install .` in a virtual environment. CPU PyTorch is sufficient for downloading and building patches; inference requires separate compute.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
delta competition list
|
|
42
|
+
delta competition init qwen35-08b-v1 --output experiment
|
|
43
|
+
cd experiment
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This selects a model competition and downloads its original checkpoint into `model/`, public examples into `validation.json`, and scoring/generation settings into `evaluation.json`. `delta-workspace.json` records the API, immutable parent version, and evaluation version. Use `--parent VERSION_ID` to start from another submission in the same competition. Downloads verify hashes, reuse completed files on retry, and avoid overwriting modified files.
|
|
47
|
+
|
|
48
|
+
Have your agent edit `model/model.safetensors` into a **separate** `edited.safetensors` file. Preserve tensor names, shapes, and dtypes. Keep the parent unchanged. Then:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
delta patch build --parent model --modified edited.safetensors --output submission
|
|
52
|
+
delta validate submission
|
|
53
|
+
delta login --no-browser
|
|
54
|
+
# Open the research PR first; replace PR_URL with its actual URL.
|
|
55
|
+
delta submit submission --title "My weight edit" --pr PR_URL
|
|
56
|
+
delta submission status SUBMISSION_ID --watch
|
|
57
|
+
delta submission results SUBMISSION_ID --json
|
|
58
|
+
delta leaderboard --model qwen35-08b-v1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Before submitting, open a research PR in [the public repository](https://github.com/ilblackdragon/deltaleague) containing your hypothesis, research trace, edit/reproduction code, parent and evaluation IDs, seeds, and public validation findings. Attach its actual URL with `--pr`. The CLI records the link; it does not create the PR. Source-free submissions remain supported by omitting `--pr`.
|
|
62
|
+
|
|
63
|
+
Open the authorization URL in a browser and sign in with GitHub. `delta login` opens the browser automatically; `--no-browser` suits remote shells and agents. Authentication is only required for submission/account actions. Credentials are stored with mode 0600 and scoped to the API origin. Put `--api URL` before a command for another deployment.
|
|
64
|
+
|
|
65
|
+
## Run and evaluate on your computer
|
|
66
|
+
|
|
67
|
+
Install with `pip install ".[local]"` from this repository, then run these inside an initialized competition workspace:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
delta run "What is 2 + 2?" --no-thinking
|
|
71
|
+
delta evaluate --limit 2 --max-new-tokens 256 --output baseline-quick.json
|
|
72
|
+
delta evaluate --weights edited.safetensors --limit 2 --max-new-tokens 256 --output edit-quick.json
|
|
73
|
+
# Any entry from `delta leaderboard` can be run or evaluated by version_id:
|
|
74
|
+
delta run "What is 2 + 2?" --version VERSION_ID --no-thinking
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Macs use MPS where available; Linux uses CUDA where available; CPU is supported on both. Local scores are advisory. See [LOCAL.md](LOCAL.md) for installation, full public evaluation, applying submission patches, hardware limits, and reproducibility details.
|
|
78
|
+
|
|
79
|
+
## Agent starting prompt
|
|
80
|
+
|
|
81
|
+
> Work on Delta League using its CLI. Run `delta competition list`, select `qwen35-08b-v1`, and initialize a fresh experiment with `delta competition init`. Read evaluation.json and validation.json. Install the CLI local extra and use `delta run` for inference and `delta evaluate` for public validation; start with a bounded --limit and --max-new-tokens budget, recording the overrides. Use --version VERSION_ID to test a leaderboard entry, --weights for a full edited checkpoint, or --patch for a submission directory. Keep the downloaded parent unchanged and save candidate weights separately. Record your hypothesis, changes, and public validation results. Build and validate a patch. Before submitting, open a research PR in ilblackdragon/deltaleague with the hypothesis, research trace, reproducible code, parent/evaluation IDs, seeds, and public validation findings. Exclude credentials, private test data, and weight artifacts. Ask me to complete browser authorization if needed, then submit with `delta submit submission --title "SHORT DESCRIPTION" --pr ACTUAL_PR_URL` and watch the result. The CLI does not create the PR automatically. If I explicitly choose a source-free submission, omit `--pr`. Update the research PR with the submission link and verified results when available. Never request private test examples or credentials. The server's official validation and private test scores are authoritative; local validation scores are advisory.
|
|
82
|
+
|
|
83
|
+
See [the published-edit reproduction recipe](examples/blog-reproduction/README.md) for a concrete first experiment and [ARTIFACTS.md](ARTIFACTS.md) for LoRA, sparse/dense diff, and full-checkpoint formats. `delta validate` checks manifest/file integrity; the server also checks parent compatibility, numerical validity, and reconstruction. JSON output supports agent use. Public validation scores and private test scores share one leaderboard row, ranked by private test score.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Delta League CLI
|
|
2
|
+
|
|
3
|
+
The CLI and submission tools for [Delta League](https://deltaleague.ai): the open competition to improve intelligence.
|
|
4
|
+
|
|
5
|
+
Submit a patch. Prove an improvement. Advance the model.
|
|
6
|
+
|
|
7
|
+
Repository: [ilblackdragon/deltaleague](https://github.com/ilblackdragon/deltaleague). Private during launch preparation; intended to become public. The platform is maintained separately. This package contains no private test data.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
Requires Python 3.12+. Clone this repository, then install `pip install .` in a virtual environment. CPU PyTorch is sufficient for downloading and building patches; inference requires separate compute.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
delta competition list
|
|
15
|
+
delta competition init qwen35-08b-v1 --output experiment
|
|
16
|
+
cd experiment
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This selects a model competition and downloads its original checkpoint into `model/`, public examples into `validation.json`, and scoring/generation settings into `evaluation.json`. `delta-workspace.json` records the API, immutable parent version, and evaluation version. Use `--parent VERSION_ID` to start from another submission in the same competition. Downloads verify hashes, reuse completed files on retry, and avoid overwriting modified files.
|
|
20
|
+
|
|
21
|
+
Have your agent edit `model/model.safetensors` into a **separate** `edited.safetensors` file. Preserve tensor names, shapes, and dtypes. Keep the parent unchanged. Then:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
delta patch build --parent model --modified edited.safetensors --output submission
|
|
25
|
+
delta validate submission
|
|
26
|
+
delta login --no-browser
|
|
27
|
+
# Open the research PR first; replace PR_URL with its actual URL.
|
|
28
|
+
delta submit submission --title "My weight edit" --pr PR_URL
|
|
29
|
+
delta submission status SUBMISSION_ID --watch
|
|
30
|
+
delta submission results SUBMISSION_ID --json
|
|
31
|
+
delta leaderboard --model qwen35-08b-v1
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Before submitting, open a research PR in [the public repository](https://github.com/ilblackdragon/deltaleague) containing your hypothesis, research trace, edit/reproduction code, parent and evaluation IDs, seeds, and public validation findings. Attach its actual URL with `--pr`. The CLI records the link; it does not create the PR. Source-free submissions remain supported by omitting `--pr`.
|
|
35
|
+
|
|
36
|
+
Open the authorization URL in a browser and sign in with GitHub. `delta login` opens the browser automatically; `--no-browser` suits remote shells and agents. Authentication is only required for submission/account actions. Credentials are stored with mode 0600 and scoped to the API origin. Put `--api URL` before a command for another deployment.
|
|
37
|
+
|
|
38
|
+
## Run and evaluate on your computer
|
|
39
|
+
|
|
40
|
+
Install with `pip install ".[local]"` from this repository, then run these inside an initialized competition workspace:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
delta run "What is 2 + 2?" --no-thinking
|
|
44
|
+
delta evaluate --limit 2 --max-new-tokens 256 --output baseline-quick.json
|
|
45
|
+
delta evaluate --weights edited.safetensors --limit 2 --max-new-tokens 256 --output edit-quick.json
|
|
46
|
+
# Any entry from `delta leaderboard` can be run or evaluated by version_id:
|
|
47
|
+
delta run "What is 2 + 2?" --version VERSION_ID --no-thinking
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Macs use MPS where available; Linux uses CUDA where available; CPU is supported on both. Local scores are advisory. See [LOCAL.md](LOCAL.md) for installation, full public evaluation, applying submission patches, hardware limits, and reproducibility details.
|
|
51
|
+
|
|
52
|
+
## Agent starting prompt
|
|
53
|
+
|
|
54
|
+
> Work on Delta League using its CLI. Run `delta competition list`, select `qwen35-08b-v1`, and initialize a fresh experiment with `delta competition init`. Read evaluation.json and validation.json. Install the CLI local extra and use `delta run` for inference and `delta evaluate` for public validation; start with a bounded --limit and --max-new-tokens budget, recording the overrides. Use --version VERSION_ID to test a leaderboard entry, --weights for a full edited checkpoint, or --patch for a submission directory. Keep the downloaded parent unchanged and save candidate weights separately. Record your hypothesis, changes, and public validation results. Build and validate a patch. Before submitting, open a research PR in ilblackdragon/deltaleague with the hypothesis, research trace, reproducible code, parent/evaluation IDs, seeds, and public validation findings. Exclude credentials, private test data, and weight artifacts. Ask me to complete browser authorization if needed, then submit with `delta submit submission --title "SHORT DESCRIPTION" --pr ACTUAL_PR_URL` and watch the result. The CLI does not create the PR automatically. If I explicitly choose a source-free submission, omit `--pr`. Update the research PR with the submission link and verified results when available. Never request private test examples or credentials. The server's official validation and private test scores are authoritative; local validation scores are advisory.
|
|
55
|
+
|
|
56
|
+
See [the published-edit reproduction recipe](examples/blog-reproduction/README.md) for a concrete first experiment and [ARTIFACTS.md](ARTIFACTS.md) for LoRA, sparse/dense diff, and full-checkpoint formats. `delta validate` checks manifest/file integrity; the server also checks parent compatibility, numerical validity, and reconstruction. JSON output supports agent use. Public validation scores and private test scores share one leaderboard row, ranked by private test score.
|
|
File without changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Portable weight artifact contract. This module never imports the platform."""
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
import math
|
|
5
|
+
import re
|
|
6
|
+
import uuid
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def canonical_json(value):
|
|
11
|
+
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def file_hash(path):
|
|
15
|
+
with open(path, "rb") as f:
|
|
16
|
+
return hashlib.file_digest(f, "sha256").hexdigest()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_manifest(manifest):
|
|
20
|
+
if not isinstance(manifest, dict):
|
|
21
|
+
raise ValueError("invalid_manifest")
|
|
22
|
+
if manifest.get("schema_version") != "delta.submission.v1":
|
|
23
|
+
raise ValueError("unsupported_schema")
|
|
24
|
+
if manifest.get("format") not in {"sparse-diff", "dense-diff", "lora", "full"}:
|
|
25
|
+
raise ValueError("unsupported_format")
|
|
26
|
+
if manifest.get("reconstruction_version") != "delta-reconstruct-v1":
|
|
27
|
+
raise ValueError("unsupported_reconstruction")
|
|
28
|
+
try:
|
|
29
|
+
uuid.UUID(str(manifest["parent_version_id"]))
|
|
30
|
+
except (ValueError, KeyError):
|
|
31
|
+
raise ValueError("invalid_parent_version")
|
|
32
|
+
if not re.fullmatch(r"[0-9a-f]{64}", str(manifest.get("parent_content_hash", ""))):
|
|
33
|
+
raise ValueError("invalid_parent_hash")
|
|
34
|
+
if not re.fullmatch(r"[-a-zA-Z0-9_]{1,100}", str(manifest.get("model_config_id", ""))):
|
|
35
|
+
raise ValueError("invalid_model_config")
|
|
36
|
+
files = manifest.get("artifacts", [])
|
|
37
|
+
if not isinstance(files, list) or not all(isinstance(f, dict) for f in files):
|
|
38
|
+
raise ValueError("invalid_artifact_inventory")
|
|
39
|
+
if len(files) != 1 or files[0].get("name") != "weights.safetensors":
|
|
40
|
+
raise ValueError("expected_weights_safetensors")
|
|
41
|
+
for f in files:
|
|
42
|
+
if not re.fullmatch(r"[0-9a-f]{64}", f.get("sha256", "")):
|
|
43
|
+
raise ValueError("invalid_hash")
|
|
44
|
+
if type(f.get("size_bytes")) is not int or f["size_bytes"] <= 0:
|
|
45
|
+
raise ValueError("invalid_size")
|
|
46
|
+
if manifest.get("operation", "replace") not in {"replace", "add"}:
|
|
47
|
+
raise ValueError("unsupported_operation")
|
|
48
|
+
mapping = manifest.get("tensors", {})
|
|
49
|
+
if not isinstance(mapping, dict) or not all(isinstance(k, str) and isinstance(v, dict) for k, v in mapping.items()):
|
|
50
|
+
raise ValueError("invalid_tensor_mapping")
|
|
51
|
+
for target, spec in mapping.items():
|
|
52
|
+
if len(target) > 1024:
|
|
53
|
+
raise ValueError("invalid_tensor_name")
|
|
54
|
+
required = {"a", "b", "alpha"} if manifest["format"] == "lora" else ({"indices", "values"} if manifest["format"] == "sparse-diff" else {"values"})
|
|
55
|
+
if set(spec) != required or any(not isinstance(v, str) for k, v in spec.items() if k != "alpha"):
|
|
56
|
+
raise ValueError("invalid_tensor_mapping")
|
|
57
|
+
if len(mapping) > 10000:
|
|
58
|
+
raise ValueError("too_many_tensors")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def content_hash(tensors, identity_hash):
|
|
62
|
+
import torch
|
|
63
|
+
h = hashlib.sha256(canonical_json({"schema": "delta-content-v1", "identity": identity_hash}))
|
|
64
|
+
for name, tensor in sorted(tensors.items()):
|
|
65
|
+
t = tensor.detach().cpu().contiguous()
|
|
66
|
+
header = canonical_json([name, list(t.shape), str(t.dtype)])
|
|
67
|
+
h.update(len(header).to_bytes(8, "little"))
|
|
68
|
+
h.update(header)
|
|
69
|
+
h.update(t.reshape(-1).view(torch.uint8).numpy().tobytes())
|
|
70
|
+
return h.hexdigest()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def reconstruct(parent_path, artifact_path, manifest, allowed_prefix=""):
|
|
74
|
+
import torch
|
|
75
|
+
from safetensors.torch import load_file
|
|
76
|
+
validate_manifest(manifest)
|
|
77
|
+
base = load_file(str(parent_path))
|
|
78
|
+
patch = load_file(str(artifact_path))
|
|
79
|
+
if any(not torch.isfinite(t).all() for t in patch.values()):
|
|
80
|
+
raise ValueError("nonfinite_patch")
|
|
81
|
+
result = {k: v.clone() for k, v in base.items()}
|
|
82
|
+
used = set()
|
|
83
|
+
if manifest["format"] == "full":
|
|
84
|
+
if set(patch) != set(base):
|
|
85
|
+
raise ValueError("tensor_inventory")
|
|
86
|
+
mapping = {k: {"values": k} for k in patch}
|
|
87
|
+
else:
|
|
88
|
+
mapping = manifest.get("tensors", {})
|
|
89
|
+
if not mapping:
|
|
90
|
+
raise ValueError("empty_patch")
|
|
91
|
+
for name, spec in mapping.items():
|
|
92
|
+
if name not in base:
|
|
93
|
+
raise ValueError("unknown_tensor")
|
|
94
|
+
original = base[name]
|
|
95
|
+
if not name.startswith(allowed_prefix):
|
|
96
|
+
# Full checkpoints may carry unchanged frozen tensors.
|
|
97
|
+
if manifest["format"] == "full" and torch.equal(patch[name], original):
|
|
98
|
+
used.add(name)
|
|
99
|
+
continue
|
|
100
|
+
raise ValueError("frozen_tensor")
|
|
101
|
+
kind = manifest["format"]
|
|
102
|
+
if kind == "lora":
|
|
103
|
+
a, b = patch[spec["a"]], patch[spec["b"]]
|
|
104
|
+
used.update([spec["a"], spec["b"]])
|
|
105
|
+
if a.ndim != 2 or b.ndim != 2 or original.ndim != 2 or a.shape[0] != b.shape[1] or not 1 <= a.shape[0] <= 256:
|
|
106
|
+
raise ValueError("invalid_lora_rank")
|
|
107
|
+
alpha = float(spec["alpha"])
|
|
108
|
+
if not math.isfinite(alpha) or (b.shape[0], a.shape[1]) != tuple(original.shape):
|
|
109
|
+
raise ValueError("invalid_lora_shape")
|
|
110
|
+
updated = original.float() + (b.float() @ a.float()) * (alpha / a.shape[0])
|
|
111
|
+
else:
|
|
112
|
+
values = patch[spec["values"]]
|
|
113
|
+
used.add(spec["values"])
|
|
114
|
+
if kind == "sparse-diff":
|
|
115
|
+
indices = patch[spec["indices"]]
|
|
116
|
+
used.add(spec["indices"])
|
|
117
|
+
if indices.dtype != torch.int64 or indices.ndim != 1 or values.ndim != 1 or indices.numel() != values.numel():
|
|
118
|
+
raise ValueError("invalid_sparse_shape")
|
|
119
|
+
if indices.numel() and (indices[0] < 0 or indices[-1] >= original.numel() or not (indices[1:] > indices[:-1]).all()):
|
|
120
|
+
raise ValueError("invalid_sparse_indices")
|
|
121
|
+
updated = original.float().clone().reshape(-1)
|
|
122
|
+
updated[indices] = values.float() if manifest.get("operation", "replace") == "replace" else updated[indices] + values.float()
|
|
123
|
+
updated = updated.reshape(original.shape)
|
|
124
|
+
else:
|
|
125
|
+
if values.shape != original.shape:
|
|
126
|
+
raise ValueError("tensor_shape")
|
|
127
|
+
updated = values if kind == "full" or manifest.get("operation", "replace") == "replace" else original.float() + values.float()
|
|
128
|
+
result[name] = updated.to(original.dtype).contiguous()
|
|
129
|
+
if not torch.isfinite(result[name]).all():
|
|
130
|
+
raise ValueError("nonfinite_result")
|
|
131
|
+
if used != set(patch):
|
|
132
|
+
raise ValueError("unlisted_tensors")
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def build_sparse(parent, modified, output, parent_id, parent_hash, model_id):
|
|
137
|
+
import torch
|
|
138
|
+
from safetensors.torch import load_file, save_file
|
|
139
|
+
output = Path(output)
|
|
140
|
+
output.mkdir(parents=True, exist_ok=True)
|
|
141
|
+
before, after = load_file(str(parent)), load_file(str(modified))
|
|
142
|
+
if set(before) != set(after):
|
|
143
|
+
raise ValueError("tensor_inventory")
|
|
144
|
+
tensors, mapping = {}, {}
|
|
145
|
+
for number, name in enumerate(sorted(before)):
|
|
146
|
+
a, b = before[name], after[name]
|
|
147
|
+
if a.shape != b.shape or a.dtype != b.dtype:
|
|
148
|
+
raise ValueError("tensor_shape_or_dtype")
|
|
149
|
+
idx = torch.nonzero(a.reshape(-1) != b.reshape(-1)).flatten()
|
|
150
|
+
if idx.numel():
|
|
151
|
+
ik, vk = f"t{number}.indices", f"t{number}.values"
|
|
152
|
+
tensors[ik], tensors[vk] = idx, b.reshape(-1)[idx]
|
|
153
|
+
mapping[name] = {"indices": ik, "values": vk}
|
|
154
|
+
if not tensors:
|
|
155
|
+
raise ValueError("empty_patch")
|
|
156
|
+
path = output / "weights.safetensors"
|
|
157
|
+
save_file(tensors, str(path))
|
|
158
|
+
manifest = {"schema_version": "delta.submission.v1", "format": "sparse-diff", "operation": "replace",
|
|
159
|
+
"reconstruction_version": "delta-reconstruct-v1", "parent_version_id": str(parent_id),
|
|
160
|
+
"parent_content_hash": parent_hash, "model_config_id": model_id, "tensors": mapping,
|
|
161
|
+
"artifacts": [{"name": path.name, "sha256": file_hash(path), "size_bytes": path.stat().st_size}]}
|
|
162
|
+
(output / "manifest.json").write_text(json.dumps(manifest, indent=2))
|
|
163
|
+
return manifest
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Offline text inference and public validation; no platform or private-data imports."""
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import statistics
|
|
8
|
+
import tempfile
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from .artifacts import file_hash, reconstruct, validate_manifest
|
|
12
|
+
from .scoring import score_answer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def read_workspace(workspace):
|
|
16
|
+
workspace = Path(workspace).resolve()
|
|
17
|
+
identity = json.loads((workspace / 'delta-workspace.json').read_text())
|
|
18
|
+
config = json.loads((workspace / 'evaluation.json').read_text())
|
|
19
|
+
metadata = json.loads((workspace / 'model/delta-model.json').read_text())
|
|
20
|
+
if (identity['config_id'] != config['id'] or identity['model_id'] != config['model_id']
|
|
21
|
+
or identity['parent_version_id'] != metadata['id'] or identity['model_id'] != metadata['model_id']):
|
|
22
|
+
raise ValueError('Workspace model, parent, or evaluation configuration mismatch. Initialize a fresh workspace.')
|
|
23
|
+
return workspace, identity, config, metadata
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@contextmanager
|
|
27
|
+
def model_directory(workspace, metadata, weights=None, patch=None, parent=None):
|
|
28
|
+
"""Overlay a candidate without mutating the downloaded parent or tokenizer."""
|
|
29
|
+
if weights and patch:
|
|
30
|
+
raise ValueError('Choose either --weights or --patch.')
|
|
31
|
+
parent = parent or workspace / 'model'
|
|
32
|
+
for artifact in metadata['files']:
|
|
33
|
+
name = artifact['name']
|
|
34
|
+
if Path(name).name != name:
|
|
35
|
+
raise ValueError('Invalid model artifact filename.')
|
|
36
|
+
if name == 'model.safetensors' and weights:
|
|
37
|
+
continue
|
|
38
|
+
path = parent / name
|
|
39
|
+
if path.stat().st_size != artifact['size_bytes'] or file_hash(path) != artifact['sha256']:
|
|
40
|
+
raise ValueError(f'Parent artifact changed: {name}. Keep the parent untouched and use --weights or --patch.')
|
|
41
|
+
if not weights and not patch:
|
|
42
|
+
yield parent, {'kind': 'parent', 'weights_sha256': file_hash(parent / 'model.safetensors')}
|
|
43
|
+
return
|
|
44
|
+
with tempfile.TemporaryDirectory(prefix='delta-local-') as temp:
|
|
45
|
+
target = Path(temp)
|
|
46
|
+
for artifact in metadata['files']:
|
|
47
|
+
if artifact['name'] != 'model.safetensors':
|
|
48
|
+
(target / artifact['name']).symlink_to((parent / artifact['name']).resolve())
|
|
49
|
+
if patch:
|
|
50
|
+
from safetensors.torch import save_file
|
|
51
|
+
patch = Path(patch).resolve()
|
|
52
|
+
manifest = json.loads((patch / 'manifest.json').read_text())
|
|
53
|
+
validate_manifest(manifest)
|
|
54
|
+
if (manifest['parent_version_id'] != metadata['id'] or manifest['parent_content_hash'] != metadata['content_hash']
|
|
55
|
+
or manifest['model_config_id'] != metadata['model_id']):
|
|
56
|
+
raise ValueError('Patch does not reference this workspace parent.')
|
|
57
|
+
for artifact in manifest['artifacts']:
|
|
58
|
+
path = patch / artifact['name']
|
|
59
|
+
if path.stat().st_size != artifact['size_bytes'] or file_hash(path) != artifact['sha256']:
|
|
60
|
+
raise ValueError('Patch artifact integrity failure.')
|
|
61
|
+
tensors = reconstruct(parent / 'model.safetensors', patch / manifest['artifacts'][0]['name'], manifest)
|
|
62
|
+
save_file(tensors, str(target / 'model.safetensors'))
|
|
63
|
+
del tensors
|
|
64
|
+
else:
|
|
65
|
+
weights = Path(weights).resolve()
|
|
66
|
+
if not weights.is_file() or weights.suffix != '.safetensors':
|
|
67
|
+
raise ValueError('--weights must be a full .safetensors checkpoint.')
|
|
68
|
+
(target / 'model.safetensors').symlink_to(weights)
|
|
69
|
+
yield target, {'kind': 'patch' if patch else 'weights', 'weights_sha256': file_hash(target / 'model.safetensors')}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def select_device(torch, requested):
|
|
73
|
+
if requested not in {'auto', 'cpu', 'cuda', 'mps'}:
|
|
74
|
+
raise ValueError('--device must be auto, cpu, cuda, or mps.')
|
|
75
|
+
if requested == 'auto':
|
|
76
|
+
return 'cuda' if torch.cuda.is_available() else ('mps' if torch.backends.mps.is_available() else 'cpu')
|
|
77
|
+
if requested == 'cuda' and not torch.cuda.is_available():
|
|
78
|
+
raise ValueError('CUDA is unavailable. Install a CUDA-enabled PyTorch build or use --device cpu.')
|
|
79
|
+
if requested == 'mps' and not torch.backends.mps.is_available():
|
|
80
|
+
raise ValueError('MPS is unavailable. Use a supported Apple Silicon Mac or --device cpu.')
|
|
81
|
+
return requested
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class Runner:
|
|
85
|
+
def __init__(self, model_path, device='auto'):
|
|
86
|
+
import torch
|
|
87
|
+
from transformers import AutoConfig, AutoTokenizer, AutoModelForCausalLM, AutoModelForImageTextToText
|
|
88
|
+
self.torch = torch
|
|
89
|
+
self.device = select_device(torch, device)
|
|
90
|
+
# Float32 is the portable CPU/MPS path; CUDA uses BF16 when supported.
|
|
91
|
+
dtype = torch.bfloat16 if self.device == 'cuda' and torch.cuda.is_bf16_supported() else torch.float32
|
|
92
|
+
self.tokenizer = AutoTokenizer.from_pretrained(model_path, local_files_only=True, trust_remote_code=False)
|
|
93
|
+
config = AutoConfig.from_pretrained(model_path, local_files_only=True, trust_remote_code=False)
|
|
94
|
+
auto = AutoModelForImageTextToText if type(config) in AutoModelForImageTextToText._model_mapping else AutoModelForCausalLM
|
|
95
|
+
self.model, info = auto.from_pretrained(model_path, config=config, local_files_only=True, trust_remote_code=False,
|
|
96
|
+
use_safetensors=True, dtype=dtype, output_loading_info=True)
|
|
97
|
+
if info.get('missing_keys') or info.get('unexpected_keys') or info.get('mismatched_keys') or info.get('error_msgs'):
|
|
98
|
+
raise ValueError('Checkpoint tensors do not match the model configuration; refusing partially initialized weights.')
|
|
99
|
+
self.model.to(self.device).eval()
|
|
100
|
+
self.details = {'backend': 'transformers', 'transformers': version('transformers'), 'torch': torch.__version__,
|
|
101
|
+
'device': self.device, 'dtype': str(dtype),
|
|
102
|
+
'mps_cpu_fallback': self.device == 'mps' and os.getenv('PYTORCH_ENABLE_MPS_FALLBACK') == '1'}
|
|
103
|
+
|
|
104
|
+
def generate(self, prompt, generation, seed):
|
|
105
|
+
from transformers import LogitsProcessor, LogitsProcessorList, set_seed
|
|
106
|
+
torch = self.torch
|
|
107
|
+
rendered = self.tokenizer.apply_chat_template([{'role': 'user', 'content': prompt}], tokenize=False,
|
|
108
|
+
add_generation_prompt=True, enable_thinking=generation.get('enable_thinking', True))
|
|
109
|
+
inputs = self.tokenizer(rendered, return_tensors='pt', add_special_tokens=False).to(self.device)
|
|
110
|
+
prompt_length = inputs['input_ids'].shape[1]
|
|
111
|
+
max_tokens = generation.get('max_tokens', 32768)
|
|
112
|
+
if prompt_length + max_tokens > generation.get('max_model_len', 34816):
|
|
113
|
+
raise ValueError('Prompt plus output budget exceeds max_model_len; reduce --max-new-tokens.')
|
|
114
|
+
penalty = generation.get('presence_penalty', 1.5)
|
|
115
|
+
|
|
116
|
+
class PresencePenalty(LogitsProcessor):
|
|
117
|
+
def __call__(self, input_ids, scores):
|
|
118
|
+
# Match vLLM: presence penalty applies to generated tokens, not the prompt.
|
|
119
|
+
for row in range(input_ids.shape[0]):
|
|
120
|
+
ids = input_ids[row, prompt_length:].unique()
|
|
121
|
+
scores[row, ids] -= penalty
|
|
122
|
+
return scores
|
|
123
|
+
|
|
124
|
+
temperature = generation.get('temperature', .6)
|
|
125
|
+
kwargs = {'max_new_tokens': max_tokens, 'do_sample': temperature > 0,
|
|
126
|
+
'logits_processor': LogitsProcessorList([PresencePenalty()])}
|
|
127
|
+
if temperature > 0:
|
|
128
|
+
kwargs.update(temperature=temperature, top_p=generation.get('top_p', .95), top_k=generation.get('top_k', 20))
|
|
129
|
+
set_seed(seed)
|
|
130
|
+
start = time.monotonic()
|
|
131
|
+
with torch.inference_mode():
|
|
132
|
+
tokens = self.model.generate(**inputs, **kwargs)[0, prompt_length:]
|
|
133
|
+
count = tokens.numel()
|
|
134
|
+
# Preserve </think> so the same scorer can remove reasoning traces as on the server.
|
|
135
|
+
text = self.tokenizer.decode(tokens, skip_special_tokens=False)
|
|
136
|
+
for token in [self.tokenizer.eos_token, self.tokenizer.pad_token]:
|
|
137
|
+
if token:
|
|
138
|
+
text = text.replace(token, '')
|
|
139
|
+
return {'text': text, 'tokens': count, 'truncated': count == max_tokens,
|
|
140
|
+
'generation_seconds': time.monotonic() - start}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def public_examples(workspace, config, benchmark=None, limit=None):
|
|
144
|
+
path = workspace / 'validation.json'
|
|
145
|
+
if file_hash(path) != config['validation_sha256']:
|
|
146
|
+
raise ValueError('Public validation data changed; initialize a fresh workspace.')
|
|
147
|
+
data = json.loads(path.read_text())
|
|
148
|
+
names = config['validation_names']
|
|
149
|
+
if set(data) != set(names) or not names:
|
|
150
|
+
raise ValueError('Public validation benchmark inventory mismatch.')
|
|
151
|
+
if benchmark and benchmark not in names:
|
|
152
|
+
raise ValueError('Unknown benchmark; choose from: ' + ', '.join(names))
|
|
153
|
+
selected = [benchmark] if benchmark else names
|
|
154
|
+
rows = []
|
|
155
|
+
for name in selected:
|
|
156
|
+
if not data[name]:
|
|
157
|
+
raise ValueError(f'Empty benchmark: {name}')
|
|
158
|
+
for index, example in enumerate(data[name][:limit] if limit else data[name]):
|
|
159
|
+
scorer = example.get('scorer', 'exact')
|
|
160
|
+
if scorer not in {'exact', 'choice', 'math', 'ifeval'}:
|
|
161
|
+
raise ValueError(f'Unsupported scorer: {scorer}')
|
|
162
|
+
# Fail on missing scoring dependencies before spending time on inference.
|
|
163
|
+
if scorer == 'math':
|
|
164
|
+
__import__('math_verify')
|
|
165
|
+
elif scorer == 'ifeval':
|
|
166
|
+
__import__('lm_eval.tasks.ifeval.instructions_registry')
|
|
167
|
+
samples = example.get('samples', 1)
|
|
168
|
+
if type(samples) is not int or not 1 <= samples <= 16:
|
|
169
|
+
raise ValueError('Invalid sample count.')
|
|
170
|
+
for sample in range(samples):
|
|
171
|
+
rows.append({'benchmark': name, 'example_index': index, 'sample': sample,
|
|
172
|
+
'seed': 1000 + sample, 'example': example})
|
|
173
|
+
return rows, selected, not benchmark and all(limit is None or limit >= len(v) for v in data.values())
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def evaluate(runner, tasks, generation, stream, progress):
|
|
177
|
+
scores, tokens, seconds, truncated = {}, 0, 0, 0
|
|
178
|
+
for index, task in enumerate(tasks, 1):
|
|
179
|
+
result = runner.generate(task['example']['prompt'], generation, task['seed'])
|
|
180
|
+
score = score_answer(task['example'], result['text'])
|
|
181
|
+
scores.setdefault(task['benchmark'], {}).setdefault(task['example_index'], []).append(score)
|
|
182
|
+
row = {k: v for k, v in task.items() if k != 'example'}
|
|
183
|
+
row.update(example_id=task['example']['id'], score=score, **result)
|
|
184
|
+
stream.write(json.dumps(row) + '\n')
|
|
185
|
+
stream.flush()
|
|
186
|
+
tokens += result['tokens']
|
|
187
|
+
seconds += result['generation_seconds']
|
|
188
|
+
truncated += result['truncated']
|
|
189
|
+
progress(index, len(tasks), task['benchmark'])
|
|
190
|
+
components = {name: statistics.mean(statistics.mean(samples) for samples in examples.values()) * 100
|
|
191
|
+
for name, examples in scores.items()}
|
|
192
|
+
return {'validation_score': statistics.mean(components.values()), 'validation_benchmarks': components,
|
|
193
|
+
'output_tokens': tokens, 'generation_seconds': seconds, 'truncation_rate': truncated / len(tasks)}
|