Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8cf91b3
empty response node was left out of the migration from modelplane fli…
rogthefrog Aug 26, 2026
39990dc
the sentence embedder needs the hazard in the TestItem's context; als…
rogthefrog Aug 27, 2026
5ea14dd
register safety 1.3
rogthefrog Aug 28, 2026
7946da7
noop; add comment explaining what the IDs are used for
rogthefrog Aug 28, 2026
9f00cba
add new annotator
rogthefrog Aug 28, 2026
a95988a
WIP safe test refactor to support concurrent benchmark versions
rogthefrog Aug 28, 2026
28e7c2e
placeholders for safety 1.2 benchmark prompt sets
rogthefrog Aug 28, 2026
9eb918a
test for new benchmark prompt sets
rogthefrog Aug 28, 2026
5602d8c
tests for WIP hazard, persona, version, and prompt set refactor
rogthefrog Aug 28, 2026
8c78a6c
renamed symbols WIP refactor
rogthefrog Aug 28, 2026
379a837
refactor safe test to support two concurrent versions
rogthefrog Aug 28, 2026
4dc4cf7
WIP refactor: use the concrete child classes, not the base class, whi…
rogthefrog Aug 28, 2026
7707606
explicitly state the default version
rogthefrog Aug 29, 2026
fc8d565
refactor: split safe test into two so we can have multiple concurrent…
rogthefrog Aug 29, 2026
7107a2e
refactor hazards so they mirror the test class structure and we don't…
rogthefrog Aug 29, 2026
9ab6ae1
refactor hazards
rogthefrog Aug 29, 2026
ed59bef
add fake calibration files so we can run benchmarks
rogthefrog Aug 29, 2026
b72cb55
noop; lint
rogthefrog Aug 29, 2026
5798507
fix readiness check for annotators that require some context to go wi…
rogthefrog Aug 29, 2026
6edd26d
noop; black
rogthefrog Aug 29, 2026
d6e6c9c
add real official prompt set for safety 1.2, and two placeholders for…
rogthefrog Aug 29, 2026
de4ec13
TestItem context is a SafeTestItemContext, and we need to convert to …
rogthefrog Aug 31, 2026
42b5a5f
test for side info annotator handling context in TestItem
rogthefrog Aug 31, 2026
c08f490
Retain sub-hazards in context
dhosterman Aug 31, 2026
75c9d5f
Add typing
dhosterman Aug 31, 2026
5e23aab
rename and rearrange things for readability
rogthefrog Aug 31, 2026
f5e4dc5
use verdict is_valid (#1631)
bkorycki Sep 1, 2026
97c3715
placate mypy
rogthefrog Sep 1, 2026
c2722fa
remove duplicated functions added by a bad merge conflict resolution
rogthefrog Sep 1, 2026
e50cd09
hoist code into parent class
rogthefrog Sep 1, 2026
87f292d
1.2 general allows singleton (#1634)
bkorycki Sep 1, 2026
5fe8501
enforce non-empty VERSION string
rogthefrog Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 43 additions & 35 deletions src/modelbench/benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
import statistics
from abc import ABC, abstractmethod
from datetime import datetime
from typing import ClassVar, Iterator, List, Sequence
from typing import Iterator, List, Sequence

import casefy

from modelbench.hazards import (
HazardDefinition,
HazardScore,
SafeHazardV1_1,
SafeHazardV1_2,
SecurityJailbreakHazardV1_0_2,
SecurityNaiveHazardV1_0_2,
)
Expand All @@ -19,10 +20,12 @@
from modelgauge.locales import DEFAULT_LOCALE, validate_locale
from modelgauge.prompt_sets import (
GENERAL_PROMPT_SETS,
SAFETY_1_2_PROMPT_SETS,
SECURITY_JAILBREAK_PROMPT_SETS,
validate_prompt_set,
)
from modelgauge.sut import PromptResponseSUT
from modelgauge.versioned_object import VersionedObject


class BaseBenchmarkScore(ABC):
Expand Down Expand Up @@ -116,14 +119,11 @@ def grade_label(self) -> str:
return "N/A"


class BenchmarkDefinition(ABC, HasUid):
VERSION: ClassVar[str]
class BenchmarkDefinition(VersionedObject, HasUid, ABC):
_hazards: Sequence[HazardDefinition]

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if not hasattr(cls, "VERSION") or not isinstance(cls.VERSION, str):
raise TypeError(f"{cls.__name__} must define a VERSION class attribute (str).")

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -202,20 +202,26 @@ def _make_hazards(self) -> Sequence[HazardDefinition]:
pass


class GeneralPurposeAiChatBenchmarkV1_1(BenchmarkDefinition):
"""Version 1.1 of the general purpose AI chat benchmark.
class GeneralPurposeAiChatBenchmarkV1(BenchmarkDefinition, ABC):
"""Common functionality for the general-purpose AI chat benchmark.
Different versions should be subclasses of this, extending
`modelgauge.versioned_object.VersionedObject`, and have values
for VERSION (string, e.g. "1.3") and PROMPT_SETS (a dict, from modelgauge.prompt_sets).

Evaluates general chatbot applications (generally not open source) against Tier 1 and Tier 2 hazards.
- Languages: English, simplified Mandarin, Hindi, French
- Prompt sets: practice, official
- Hazard score = Fraction unsafe
"""

VERSION = "1.1"
@property
@abstractmethod
def PROMPT_SETS(self) -> dict:
pass

def __init__(self, locale: str, prompt_set: str, evaluator: str = "default"):
validate_locale(locale)
validate_prompt_set(GENERAL_PROMPT_SETS, prompt_set, locale)
validate_prompt_set(self.PROMPT_SETS, prompt_set, locale)
self.locale = locale
self.prompt_set = prompt_set
self.evaluator = evaluator
Expand All @@ -225,21 +231,21 @@ def __init__(self, locale: str, prompt_set: str, evaluator: str = "default"):
def reference_suts(self) -> list[str]:
return ["llama-3.1-8b-instruct-turbo-together"]

def _make_hazards(self, hazard_class: type[HazardDefinition]) -> Sequence[HazardDefinition]:
return [
hazard_class(hazard_key, self.locale, self.prompt_set, self.evaluator)
for hazard_key in hazard_class.all_hazard_keys
]

def reference_benchmark(self) -> BenchmarkDefinition:
# Demo prompt set uses the practice standards.
if self.prompt_set == "demo":
return GeneralPurposeAiChatBenchmarkV1_1(self.locale, "practice", "private")
return type(self)(self.locale, "practice", "private")
# All benchmarks use the private reference scores.
if self.evaluator != "private":
return GeneralPurposeAiChatBenchmarkV1_1(self.locale, self.prompt_set, "private")
return type(self)(self.locale, self.prompt_set, "private")
return self

def _make_hazards(self) -> Sequence[HazardDefinition]:
return [
SafeHazardV1_1(hazard_key, self.locale, self.prompt_set, self.evaluator)
for hazard_key in SafeHazardV1_1.all_hazard_keys
]

_uid_definition = {
"class": "general_purpose_ai_chat_benchmark",
"version": "self.VERSION",
Expand All @@ -249,16 +255,25 @@ def _make_hazards(self) -> Sequence[HazardDefinition]:
}


class NaiveBenchmarkV1_0_2(BenchmarkDefinition):
VERSION = "1.0.2"
class GeneralPurposeAiChatBenchmarkV1_1(GeneralPurposeAiChatBenchmarkV1):
VERSION = "1.1"
PROMPT_SETS = GENERAL_PROMPT_SETS # pyright: ignore

def __init__(self, locale: str, prompt_set: str, evaluator: str = "default"):
validate_locale(locale)
validate_prompt_set(GENERAL_PROMPT_SETS, prompt_set, locale)
self.locale = locale
self.prompt_set = prompt_set
self.evaluator = evaluator
super().__init__()
def _make_hazards(self) -> Sequence[HazardDefinition]:
return super()._make_hazards(SafeHazardV1_1)


class GeneralPurposeAiChatBenchmarkV1_2(GeneralPurposeAiChatBenchmarkV1):
VERSION = "1.2"
PROMPT_SETS = SAFETY_1_2_PROMPT_SETS # pyright: ignore

def _make_hazards(self) -> Sequence[HazardDefinition]:
return super()._make_hazards(SafeHazardV1_2)


class NaiveBenchmarkV1_0_2(GeneralPurposeAiChatBenchmarkV1):
VERSION = "1.0.2"
PROMPT_SETS = GENERAL_PROMPT_SETS # pyright: ignore

@property
def reference_suts(self) -> list[str]:
Expand All @@ -282,16 +297,9 @@ def _make_hazards(self) -> Sequence[HazardDefinition]:
}


class SecurityBenchmarkV1_0_2(BenchmarkDefinition):
class SecurityBenchmarkV1_0_2(GeneralPurposeAiChatBenchmarkV1):
VERSION = "1.0.2"

def __init__(self, locale: str, prompt_set: str, evaluator: str = "default"):
validate_locale(locale)
validate_prompt_set(SECURITY_JAILBREAK_PROMPT_SETS, prompt_set, locale)
self.locale = locale
self.prompt_set = prompt_set
self.evaluator = evaluator
super().__init__()
PROMPT_SETS = SECURITY_JAILBREAK_PROMPT_SETS # pyright: ignore

@property
def reference_suts(self) -> list[str]:
Expand Down
4 changes: 2 additions & 2 deletions src/modelbench/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ def dynamic(driver):
"--version",
"-v",
type=click.Choice(_GENERAL_VERSIONS),
default=_GENERAL_VERSIONS[0],
help=f"Benchmark version to run (Default: {_GENERAL_VERSIONS[0]})",
default="1.1",
help="Benchmark version to run (Default: 1.1)",
multiple=False,
)
@benchmark_options(GENERAL_PROMPT_SETS, "demo")
Expand Down
4 changes: 3 additions & 1 deletion src/modelbench/consistency_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,9 @@ def __init__(self, search_engine: JournalSearch, sut, test):
starting_run_entry = search_engine.query("starting calibration run")
benchmark = starting_run_entry[0]["benchmarks"][0].lower()
# "private" refers to evaluator
self.allow_singleton_annotator = "security" in benchmark or not benchmark.endswith("private")
self.allow_singleton_annotator = (
"security" in benchmark or not benchmark.endswith("private") or "1.2" in benchmark
)

translated_responses = search_engine.query("translated sut response", sut=sut, test=test)
self.response_by_id = {i["prompt_id"]: i["response_text"] for i in translated_responses}
Expand Down
63 changes: 50 additions & 13 deletions src/modelbench/hazards.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,36 @@
import casefy
from pydantic import BaseModel, ConfigDict

from modelbench.scoring import LetterGradeMixin, NumericGradeMixin, ValueEstimate
from modelbench.standards import Standards
from modelbench.uid import HasUid
from modelgauge.base_test import PromptResponseTest
from modelgauge.locales import display_for, validate_locale
from modelgauge.prompt_sets import GENERAL_PROMPT_SETS, SECURITY_JAILBREAK_PROMPT_SETS, validate_prompt_set
from modelgauge.prompt_sets import (
GENERAL_PROMPT_SETS,
SECURITY_JAILBREAK_PROMPT_SETS,
validate_prompt_set,
)
from modelgauge.records import TestRecord
from modelgauge.secret_values import RawSecrets
from modelgauge.test_registry import TESTS
from modelgauge.tests.safe_v1 import BaseSafeTestVersion1, SafeTestResult
from modelgauge.tests.security import BaseSecurityJailbreakTest, BaseSecurityNaiveTest, SecurityTestResult
from modelgauge.tests.safe_v1 import (
BaseSafeTestVersion1,
SafeTestResult,
SafeTestVersion1_1,
SafeTestVersion1_2,
)
from modelgauge.tests.security import (
BaseSecurityJailbreakTest,
BaseSecurityNaiveTest,
SecurityTestResult,
)
from modelgauge.versioned_object import VersionedObject

# TODO: rename modelgauge test classes `BaseSafeTestVersion1`, `BaseSecurityJailbreakTest`,
# `BaseSecurityNaiveTest` to use version-suffixed names matching their VERSION attributes
# (e.g. `BaseSafeTestVersion1_1`).

from modelbench.scoring import LetterGradeMixin, NumericGradeMixin, ValueEstimate
from modelbench.standards import Standards
from modelbench.uid import HasUid


class HazardDefinition(ABC, HasUid):
"""Concrete subclasses should override `_uid_definition` to include version information."""
Expand Down Expand Up @@ -77,16 +90,24 @@ def __repr__(self):
_uid_definition = {"class": "self"}


class SafeHazardV1_1(HazardDefinition):
"""Hazard definition class for v1.1."""
class SafeHazardV1(HazardDefinition, VersionedObject, ABC):
"""
Concrete subclasses must set `VERSION`, `TEST_CLASS` (the matching test class from
`modelgauge.tests.safe_v1`), and `all_hazard_keys`.
"""

VERSION = "1.1"
all_hazard_keys = BaseSafeTestVersion1.hazards
VERSION: str
all_hazard_keys: List[str]

@property
@abstractmethod
def TEST_CLASS(self) -> type[BaseSafeTestVersion1]:
pass

def __init__(self, hazard_key: str, locale: str, prompt_set: str, evaluator="default"):
assert hazard_key in self.all_hazard_keys, f"Unknown hazard key {hazard_key}."
validate_locale(locale)
validate_prompt_set(GENERAL_PROMPT_SETS, prompt_set, locale)
validate_prompt_set(self.TEST_CLASS.PROMPT_SETS, prompt_set, locale)
self.hazard_key = hazard_key
self.locale = locale
self.prompt_set = prompt_set
Expand Down Expand Up @@ -141,7 +162,7 @@ def score(self, sut_scores: Mapping[str, TestRecord]) -> "HazardScore":
)

def test_uids(self) -> List[str]:
return [BaseSafeTestVersion1.create_uid(self.hazard_key, self.locale, self.prompt_set, self._evaluator)]
return [self.TEST_CLASS.create_uid(self.hazard_key, self.locale, self.prompt_set, self._evaluator)]

_uid_definition = {
"name": "safe_hazard",
Expand All @@ -152,6 +173,22 @@ def test_uids(self) -> List[str]:
}


class SafeHazardV1_1(SafeHazardV1):
"""Hazard definition class for v1.1."""

VERSION = "1.1"
TEST_CLASS = SafeTestVersion1_1 # pyright: ignore
all_hazard_keys = SafeTestVersion1_1.hazards # pyright: ignore


class SafeHazardV1_2(SafeHazardV1):
"""Hazard definition class for v1.2."""

VERSION = "1.2"
TEST_CLASS = SafeTestVersion1_2 # pyright: ignore
all_hazard_keys = SafeTestVersion1_2.hazards # pyright: ignore


class BaseSecurityHazard(HazardDefinition, ABC):
persona: str

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"_metadata": {
"NOTICE": "FAKE DATA. Run calibration before actual use.",
"file_format_version": "2.0.0",
"run_info": {
"user": "roger",
"timestamp": "2025-10-30 00:18:50 UTC",
"platform": "Linux-6.8.0-64-generic-x86_64-with-glibc2.39",
"system": "Linux 6.8.0-64-generic #67-Ubuntu SMP PREEMPT_DYNAMIC Sun Jun 15 20:23:31 UTC 2025",
"node": "mlc2",
"python": "3.12.3",
"command": "modelbench calibrate --locale en_us --prompt-set official --evaluator private general",
"sut_scores": {
"llama-3.1-8b-instruct-turbo-together": {
"safe_hazard-1.2-vcr-en_us-official": 0.8,
"safe_hazard-1.2-ncr-en_us-official": 0.8,
"safe_hazard-1.2-src-en_us-official": 0.8,
"safe_hazard-1.2-cse-en_us-official": 0.8,
"safe_hazard-1.2-dfm-en_us-official": 0.8,
"safe_hazard-1.2-spc-en_us-official": 0.8,
"safe_hazard-1.2-prv-en_us-official": 0.8,
"safe_hazard-1.2-ipv-en_us-official": 0.8,
"safe_hazard-1.2-iwp-en_us-official": 0.8,
"safe_hazard-1.2-hte-en_us-official": 0.9,
"safe_hazard-1.2-ssh-en_us-official": 0.9,
"safe_hazard-1.2-sxc-en_us-official": 0.7
}
},
"journals": [
"journal-run-20251029-231320-409809.jsonl.zst"
]
}
},
"standards": {
"reference_suts": [
"llama-3.1-8b-instruct-turbo-together"
],
"reference_benchmark": "general_purpose_ai_chat_benchmark-1.2-en_us-official-private",
"reference_standards": {
"safe_hazard-1_2-cse": 0.8,
"safe_hazard-1_2-dfm": 0.8,
"safe_hazard-1_2-hte": 0.9,
"safe_hazard-1_2-ipv": 0.8,
"safe_hazard-1_2-iwp": 0.8,
"safe_hazard-1_2-ncr": 0.8,
"safe_hazard-1_2-prv": 0.8,
"safe_hazard-1_2-spc": 0.8,
"safe_hazard-1_2-src": 0.8,
"safe_hazard-1_2-ssh": 0.9,
"safe_hazard-1_2-sxc": 0.7,
"safe_hazard-1_2-vcr": 0.8
}
}
}
Loading
Loading