//! `sm121 bench` — run `tool-eval-bench` against a live serve and land the //! reports in `recipes//reports/`. The tool needs a scratch cwd (it //! writes `runs//.md` relative to cwd), an absolute `--json-file` //! path, and the `.md` copied into the lane — this command does the //! babysitting. Campaign convention (seed 42, hardmode, perf, c1,2) is the //! default; `sm121 bench -d` prints the exact command. use std::fs; use std::path::PathBuf; use anyhow::Result; use crate::bench_target::{pick_head, run_tool, sanitize, served_model}; use crate::config::{Config, load_recipe, select_cluster_by_name}; /// Fields of the `sm121 bench` subcommand (mirrored by the clap variant in /// `cli.rs`). Served name/port come from the recipe; the tool is on PATH. #[derive(Clone)] pub struct Args { pub recipe: String, pub cluster: String, pub label: Option, pub concurrency: String, pub seed: u64, /// Scenario streams run concurrently (tool `--parallel`; None = serial). pub parallel: Option, /// Skip the scenario phase (tool `--perf-only`). pub perf_only: bool, /// Run the context-depth suite (16K..256K) after the main pass — off by /// default (a full sweep takes ~45 min on a 2x lane). pub depth: bool, /// Local tokenizer source for llama-benchy (tool `--tokenizer`): a /// tokenizer.json or a dir holding one. Needed when the served model /// id is not resolvable in the local HF cache (custom export lanes). pub tokenizer: Option, /// When set, the tool-eval-bench invocation runs on this host over SSH /// (the local machine only orchestrates), keeping the bench client's own /// network (e.g. a WiFi laptop) off the measurement path. The tokenizer /// is uploaded to the host; the report + run package land locally. The /// host must have `tool-eval-bench` on PATH and reach the serve. pub ssh_host: Option, /// Bench target node within the cluster (default: rank 0). Use when the /// serve was launched with `serve --node ` — a pinned/solo run may /// live on a non-rank-0 node, and benching the wrong head measures a /// different (or no) engine. pub node: Option, /// Disable thinking/reasoning in the harness (tool `--no-think`, sets /// `enable_thinking=false`). Needed for thinking-by-default models (Ling, /// GLM-4.5-style) whose long default reasoning blows the per-turn timeout /// and excludes most scenarios. pub no_think: bool, /// Per-turn request timeout seconds forwarded to the harness (tool /// `--timeout`). Raise above the 120 s default for slow/thinking models. pub timeout: Option, } /// Served model name from the recipe's `--served-model-name` arg. /// Run the bench and land `recipes//reports/_