Temper Bench v2

October 10, 2026

The first version of our benchmark, which was used to evaluate Temper 0.5B, eventually stopped meeting our expectations. It had 100 tasks at three levels: 50 trivial, 30 easy and 20 medium. It did not overlap with the training dataset and had no leaks, but its tasks were prepared by the same DeepSeek models that generated the training data, so the benchmark matched the dataset in style. On top of that, most of it consisted of short algorithmic exercises such as primality checks, factorials, GCD or FizzBuzz. They test knowledge of syntax well, but say little about how a model handles real code, and that was what mattered most to us.

We wrote the second version to be a measure of applied work. Instead of algorithmic exercises, it uses small self-contained tasks that resemble common parts of real projects, such as computing a delivery fee, parsing a config, parsing an HTTP request or building a cache that several threads use at once. Each task has at least five tests, always including empty input and edge cases. The benchmark turned out hard even for larger models: Qwen2.5-Coder-7B solves 46% of basic tasks on the first try, 22% of medium ones and only 10% of advanced ones.

0 10 20 30 40 50 Qwen2.5Coder7B 46 Temper 11.5B 42 Qwen2.5Coder1.5B 14 Qwen2.5Coder3B 26 Temper 10.5B 22
0 10 20 30 40 50 Qwen7B 46 Temper1.5B 42 Qwen1.5B 14 Qwen3B 26 Temper0.5B 22
Fig. 1. Percentage of Temper Bench v2 tasks solved on the first try (greedy pass@1, %) by difficulty level.

Task examples

Task (as the model receives it)

Write normalize_phone(raw: &str) -> Option<String> for US phone numbers. Ignore everything that isn't a digit. If there are 10 digits, or 11 digits starting with 1, format the last 10 as (555) 123-4567. Anything else gives None. Reply with a single file containing the solution.

Hidden tests
#[cfg(test)]
mod tests {
    use super::*;

    fn some(s: &str) -> Option<String> {
        Some(s.to_string())
    }

    #[test]
    fn plain_ten_digits() {
        assert_eq!(normalize_phone("5551234567"), some("(555) 123-4567"));
    }

    #[test]
    fn various_separators() {
        assert_eq!(normalize_phone("555-123-4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("(555) 123 4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("555.123.4567"), some("(555) 123-4567"));
    }

    #[test]
    fn country_code() {
        assert_eq!(normalize_phone("+1 555 123 4567"), some("(555) 123-4567"));
        assert_eq!(normalize_phone("15551234567"), some("(555) 123-4567"));
    }

    #[test]
    fn eleven_digits_without_one() {
        assert_eq!(normalize_phone("25551234567"), None);
    }

    #[test]
    fn wrong_length() {
        assert_eq!(normalize_phone("555123456"), None);
        assert_eq!(normalize_phone("115551234567"), None);
        assert_eq!(normalize_phone(""), None);
        assert_eq!(normalize_phone("call me"), None);
    }

    #[test]
    fn letters_ignored() {
        assert_eq!(normalize_phone("tel: 555 123 4567 (home)"), some("(555) 123-4567"));
    }
}
Reference solution
pub fn normalize_phone(raw: &str) -> Option<String> {
    let digits: String = raw.chars().filter(|c| c.is_ascii_digit()).collect();
    let digits = match digits.len() {
        10 => digits.as_str(),
        11 if digits.starts_with('1') => &digits[1..],
        _ => return None,
    };
    Some(format!("({}) {}-{}", &digits[..3], &digits[3..6], &digits[6..]))
}

Limitations

We tried to make Temper Bench v2 honest and high-quality. The tasks are written from scratch, solutions are checked by the compiler and hidden tests, and the benchmark has been checked for leaks. But we are not a research lab and have not built benchmarks before, so it is too early to put it on par with HumanEval or MBPP, which the community has vetted for years. We made it because Rust has few ready alternatives beyond translations of those same sets. A final verdict on a model is still best made on your own tasks, by trying it in practice.