metrale_bench/benchmarks/mlperf_agentic/
provision.rs1use std::io::Read;
12use std::path::{Path, PathBuf};
13
14use anyhow::{Context, Result, bail};
15
16use crate::artifacts::ArtifactStore;
17use crate::plugin::PluginHandle;
18
19pub const PLUGIN_ID: &str = "mlperf-agentic";
20
21pub const UPSTREAM_DATASET_STATUS: &str = "mlcommons/endpoints@7935df4 examples/10_Agentic_Inference/README.md: \"The official \
23 MLPerf dataset can be downloaded from MLCommons storage (link TBD)\"";
24
25#[derive(Clone, Debug)]
26pub struct Artifacts {
27 pub dir: PathBuf,
28 pub dataset: PathBuf,
29 pub file_sha256: String,
32}
33
34pub fn ensure(
38 store: &ArtifactStore,
39 handle: &PluginHandle,
40 expected_sha256: &str,
41) -> Result<Artifacts> {
42 let dir = store.plugin_dir(PLUGIN_ID)?;
43 let dataset = dir.join("dataset.jsonl");
44 if !dataset.is_file() {
45 bail!(
48 "the MLPerf Agentic Inference dataset is not provisioned: {} does not exist.\n\
49 The official dataset (613 trajectories: 500 Workato workflow + 113 DeepSWE \
50 coding, 30,335 client turns) is NOT yet published — {}. There is no download \
51 URL, license, or auth story to automate, and this leg deliberately refuses to \
52 substitute a proxy or reconstructed dataset: a score from a different draw is \
53 comparable to nothing, however official it looks.\n\
54 When MLCommons publishes the file: place it at the path above, record its \
55 SHA256 as this leg's expected_sha256 parameter (and in BENCH.toml), and run \
56 the calibration protocol in the BENCH.toml note before trusting any number.",
57 dataset.display(),
58 UPSTREAM_DATASET_STATUS,
59 );
60 }
61
62 let (file_sha256, bytes) = sha256_file(&dataset)?;
63 if !expected_sha256.is_empty() && !expected_sha256.eq_ignore_ascii_case(&file_sha256) {
64 bail!(
65 "{} does not match its pin: sha256 {file_sha256} ({bytes} bytes), expected \
66 {expected_sha256}. A replay of the wrong file scores against the wrong ground \
67 truth; refusing to run.",
68 dataset.display()
69 );
70 }
71 handle.info(format!(
72 "dataset {} — {bytes} bytes, sha256 {file_sha256}{}",
73 dataset.display(),
74 if expected_sha256.is_empty() {
75 " (UNPINNED: no expected_sha256 — record-only until the official file ships)"
76 } else {
77 " (pin verified)"
78 }
79 ));
80 std::fs::write(
81 dir.join("dataset_summary.json"),
82 serde_json::to_string_pretty(&serde_json::json!({
83 "file_sha256": file_sha256,
84 "bytes": bytes,
85 }))? + "\n",
86 )?;
87 Ok(Artifacts {
88 dir,
89 dataset,
90 file_sha256,
91 })
92}
93
94fn sha256_file(path: &Path) -> Result<(String, u64)> {
96 use sha2::{Digest, Sha256};
97 let mut file =
98 std::fs::File::open(path).with_context(|| format!("opening {}", path.display()))?;
99 let mut digest = Sha256::new();
100 let mut buf = [0u8; 64 * 1024];
101 let mut total = 0u64;
102 loop {
103 let n = file
104 .read(&mut buf)
105 .with_context(|| format!("reading {}", path.display()))?;
106 if n == 0 {
107 break;
108 }
109 total += n as u64;
110 digest.update(&buf[..n]);
111 }
112 Ok((metrale_closure::hex_lower(&digest.finalize()), total))
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 fn tmp(name: &str) -> PathBuf {
120 let d =
121 std::env::temp_dir().join(format!("metrale-mlperf-prov-{name}-{}", std::process::id()));
122 let _ = std::fs::remove_dir_all(&d);
123 std::fs::create_dir_all(&d).unwrap();
124 d
125 }
126
127 #[test]
128 fn sha256_matches_a_known_vector() {
129 let dir = tmp("sha");
130 let p = dir.join("f");
131 std::fs::write(&p, b"abc").unwrap();
132 let (sha, n) = sha256_file(&p).unwrap();
133 assert_eq!(n, 3);
134 assert_eq!(
135 sha,
136 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
137 );
138 }
139
140 }