met 1.0.0-beta-preview

Metrale Engine: pure Rust LLM inference server (HTTP API, scheduler, TUI, CLI).
// SPDX-License-Identifier: MIT OR Apache-2.0

//! 2026-09-28: `met circuit` against the embedded circuit files: every instance loads, the
//! CLI's plan is the checked-in golden plan, and bad requests are refused by name.
//!
//! Owner: server CLI.
//! Invariants: none beyond the types.

use clap::Parser;

use super::*;
use crate::cli::{Cli, Command};
use metrale_model_layers::circuit_exec::sources::{BLOCKS, CIRCUITS, INSTANCES, lookup};

fn parse(args: &[&str]) -> CircuitArgs {
    let mut argv = vec!["met", "circuit"];
    argv.extend_from_slice(args);
    match Cli::try_parse_from(argv).expect("parses").command {
        Command::Circuit(c) => c,
        other => panic!("parsed {other:?}"),
    }
}

fn plan_args(args: &[&str]) -> CircuitPlanArgs {
    match parse(args).action {
        CircuitAction::Show(p) => p,
        CircuitAction::Display(d) => d.plan,
        CircuitAction::Diff(_) => panic!("parsed diff"),
        CircuitAction::Venn(_) => panic!("parsed venn"),
        CircuitAction::Plan(_) => panic!("parsed plan"),
        CircuitAction::Memory(_) => panic!("parsed memory"),
        CircuitAction::Precision(_) => panic!("parsed precision"),
    }
}

#[test]
fn every_instance_source_is_embedded_and_loads() {
    let all = metrale_circuit::parse_instances(INSTANCES).unwrap();
    assert!(all.len() >= 2);
    for inst in &all {
        let src = sources(inst).unwrap_or_else(|e| panic!("{}: {e}", inst.recipe));
        metrale_circuit::load(inst, src).unwrap_or_else(|e| panic!("{}: {e}", inst.recipe));
        let circuit = lookup(&CIRCUITS, &inst.arch, "circuit").unwrap();
        for name in metrale_circuit::includes_of(circuit).unwrap() {
            lookup(&BLOCKS, &name, "block library").unwrap();
        }
    }
}

#[test]
fn the_cli_plan_is_the_checked_in_golden_plan() {
    let inst = instance("qwen3.8/qwen3.8-27b-nvfp4-unsloth").unwrap();
    let loaded = metrale_circuit::load(&inst, sources(&inst).unwrap()).unwrap();
    let avail = AvailableKernels::all_named_by(&loaded.rules);
    for (mode, rows, file) in [
        (Mode::Decode, 1, "qwen3_5-decode-n1.txt"),
        (Mode::Verify, 4, "qwen3_5-verify-n4.txt"),
    ] {
        let families = super::families_for(&inst).unwrap();
        let shown =
            metrale_circuit::render_plan(&inst, &loaded, &avail, mode, rows, &families).unwrap();
        let path = format!(
            "{}/../../kernels/circuits/plans/{file}",
            env!("CARGO_MANIFEST_DIR")
        );
        assert_eq!(shown, std::fs::read_to_string(path).unwrap(), "{file}");
    }
}

#[test]
fn an_unknown_recipe_names_the_known_ones() {
    let err = instance("qwen3.6/not-a-recipe").unwrap_err().to_string();
    assert!(err.contains("qwen3.6/not-a-recipe"), "{err}");
    assert!(err.contains("qwen3.8/qwen3.8-27b-nvfp4-unsloth"), "{err}");
}

#[test]
fn rows_default_only_where_one_row_is_the_plan() {
    let inst = instance("qwen3.6/qwen3.6-35b-a3b-fp8-bf16head").unwrap();
    let r = "--recipe=qwen3.6/qwen3.6-35b-a3b-fp8-bf16head";
    assert_eq!(rows_of(&inst, &plan_args(&["show", r])).unwrap(), 1);
    assert_eq!(
        rows_of(&inst, &plan_args(&["show", r, "--mode", "draft"])).unwrap(),
        1
    );
    assert_eq!(
        rows_of(
            &inst,
            &plan_args(&["display", r, "--mode", "multi_seq", "--rows", "96"])
        )
        .unwrap(),
        96
    );
    let err = rows_of(&inst, &plan_args(&["show", r, "--mode", "verify"]))
        .unwrap_err()
        .to_string();
    assert!(
        err.contains("needs --rows") && err.contains("[2, 3, 4]"),
        "{err}"
    );
    assert!(rows_of(&inst, &plan_args(&["show", r, "--rows", "0"])).is_err());
}

#[test]
fn display_flags_parse_and_conflict() {
    let r = "--recipe=qwen3.6/qwen3.6-35b-a3b-fp8-bf16head";
    match parse(&["display", r, "--layer", "7", "--ascii", "--color", "never"]).action {
        CircuitAction::Display(d) => {
            assert_eq!(d.layer, Some(7));
            assert!(d.ascii && !d.all_layers);
            assert_eq!(d.color, crate::cli::ColorChoice::Never);
        }
        other => panic!("parsed {other:?}"),
    }
    let both = Cli::try_parse_from([
        "met",
        "circuit",
        "display",
        r,
        "--layer",
        "1",
        "--all-layers",
    ]);
    assert!(
        both.is_err(),
        "--layer and --all-layers together must be refused"
    );
    assert!(Cli::try_parse_from(["met", "circuit", "show", r, "--mode", "prefill"]).is_err());
}

const DENSE_CONFIG: &str = r#"{
  "architectures": ["Qwen3_5ForConditionalGeneration"],
  "model_type": "qwen3_5",
  "text_config": {
    "model_type": "qwen3_5_text",
    "hidden_size": 5120, "intermediate_size": 17408, "vocab_size": 248320,
    "num_hidden_layers": 8, "num_attention_heads": 24, "num_key_value_heads": 4,
    "head_dim": 256, "full_attention_interval": 4,
    "layer_types": ["linear_attention", "linear_attention", "linear_attention", "full_attention",
                    "linear_attention", "linear_attention", "linear_attention", "full_attention"],
    "linear_num_key_heads": 16, "linear_key_head_dim": 128,
    "linear_num_value_heads": 48, "linear_value_head_dim": 128, "linear_conv_kernel_dim": 4,
    "rms_norm_eps": 1e-6, "max_position_embeddings": 262144, "mtp_num_hidden_layers": 1
  }
}"#;

#[test]
fn the_config_map_matches_the_instance_and_names_drift() {
    let from_config = metrale_circuit::map_checkpoint(DENSE_CONFIG).unwrap().shape;
    let mut stated = instance("qwen3.8/qwen3.8-27b-nvfp4-unsloth").unwrap().shape;
    stated.layer_kinds.truncate(8);
    assert_eq!(shape_drift(&stated, &from_config), Vec::<String>::new());
    let wider = DENSE_CONFIG.replace("\"hidden_size\": 5120", "\"hidden_size\": 6144");
    let drift = shape_drift(
        &stated,
        &metrale_circuit::map_checkpoint(&wider).unwrap().shape,
    );
    assert_eq!(
        drift,
        ["hidden: INSTANCES.toml 5120, config.json Some(6144)"]
    );
    stated.layer_kinds.pop();
    assert_eq!(shape_drift(&stated, &from_config).len(), 1);
}

/// 2026-09-30: The executor's served shape IS the config map's: for each golden checkpoint's
/// real config.json (crates/circuit/tests/fixtures/checkpoints), `checkpoint_shape` at the
/// checkpoint's own vocabulary is `map_checkpoint`'s shape and INSTANCES.toml's, and a
/// tokenizer-capped vocabulary moves `vocab` alone, which `served_shape` accepts.
#[test]
fn the_executor_shape_is_the_config_map_shape() {
    use metrale_model_layers::circuit_exec::sources::{checkpoint_shape, served_shape};
    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
    for recipe in [
        "qwen3.8/qwen3.8-27b-nvfp4-unsloth",
        "qwen3.6/qwen3.6-35b-a3b-fp8-bf16head",
    ] {
        let inst = instance(recipe).unwrap();
        let dir = root
            .join("crates/circuit/tests/fixtures/checkpoints")
            .join(inst.checkpoint.replace('/', "--"));
        let text = std::fs::read_to_string(dir.join("config.json")).unwrap();
        let mapped = metrale_circuit::map_checkpoint(&text).unwrap().shape;
        assert_eq!(mapped, inst.shape, "{recipe}: the config map");
        let vocab = mapped.dims["vocab"];
        assert_eq!(checkpoint_shape(&text, vocab).unwrap(), mapped, "{recipe}");
        let capped = checkpoint_shape(&text, vocab - 243).unwrap();
        let mut want = mapped.clone();
        want.dims.insert("vocab".into(), vocab - 243);
        assert_eq!(capped, want, "{recipe}: only vocab moves");
        assert_eq!(
            served_shape(&inst.shape, &capped).unwrap(),
            capped,
            "{recipe}"
        );
    }
}

#[test]
fn diff_takes_its_counts_explicitly_and_the_serve_flags_after_them() {
    let d = match parse(&[
        "diff",
        "--steps",
        "64",
        "--prompts",
        "4",
        "--out",
        "r.json",
        "m/x",
    ])
    .action
    {
        CircuitAction::Diff(d) => d,
        other => panic!("parsed {other:?}"),
    };
    assert_eq!((d.steps, d.prompts), (64, 4));
    assert_eq!(d.serve.model.as_deref(), Some("m/x"));
    for missing in ["--steps", "--prompts", "--out"] {
        let mut argv = vec![
            "met",
            "circuit",
            "diff",
            "--steps",
            "64",
            "--prompts",
            "4",
            "--out",
            "r",
        ];
        let at = argv.iter().position(|a| *a == missing).unwrap();
        argv.drain(at..at + 2);
        assert!(Cli::try_parse_from(argv).is_err(), "{missing} is required");
    }
}