Skip to content

FIX: separator normalization for harm categories and likert invariants - #2670

Open
Saurabh Yadav (onlyysaurabh) wants to merge 1 commit into
microsoft:mainfrom
onlyysaurabh:fix/separator-normalization-likert-invariants
Open

Saurabh Yadav (onlyysaurabh) wants to merge 1 commit into
microsoft:mainfrom
onlyysaurabh:fix/separator-normalization-likert-invariants

Conversation

@onlyysaurabh

Copy link
Copy Markdown
Contributor

Fast-follow to PR #2663 addressing the residual items for Issue #2661 highlighted by varunj-msft:

  1. Separator Normalization: PR FIX: strict scorer-eval category match + CSV-axis test #2663's casefold() fast path handled case-only differences (e.g., Jailbreak vs jailbreak), but separator variations (HateSpeech vs hate_speech, ProtectedMaterial vs protected_material) still failed matching without hand-added aliases.
  2. Parametrized Likert Invariant: Shipped Likert presets were only partially tested (2 of 8 presets asserted against hardcoded string literals).
  3. Metrics Invariance Guard: Post-evaluation assertions on metrics.harm_category were missing, allowing silent None regressions on direct-call paths.

Summary of Changes

  1. Canonical Taxonomy Separator Normalization (pyrit/models/harm_category.py):
    • Populated stripped variants (key.translate(_SEP_TRANS) where _SEP_TRANS = str.maketrans("", "", "_- ")) into _CANONICAL_LOOKUP alongside exact names and display values.
    • HarmCategory._resolve_canonical_category performs exact lookup first, then falls back to stripped lookup.
    • Resolves CamelCase variants (HateSpeech -> HATE_SPEECH, ProtectedMaterial -> PROTECTED_MATERIAL, SelfHarm -> SELF_HARM) automatically without needing manual entries in _HARM_CATEGORY_ALIASES.
  2. Evaluator Fast-Path Separator Normalization (pyrit/score/scorer_evaluation/scorer_evaluator.py):
    • Strips _, -, and spaces from both sides in _score_matches_harm_category fast path: c.casefold().translate(_SEP_TRANS) == target_clean.
    • Enables unrecognized/custom taxonomy categories (PromptInjection vs prompt_injection, jailbreak-attempt vs jailbreak_attempt) to match cleanly.
  3. Parametrized Likert Invariant (tests/unit/score/test_self_ask_likert.py):
    • Replaced the 2-preset test with a parametrized invariant across all 8 presets in LikertScalePaths that have evaluation_files is not None.
    • Asserts scale.category == preset.evaluation_files.harm_category.
  4. Metrics Invariance & Rejection Tests (tests/unit/score/test_scorer_evaluator.py):
    • Added assertions on metrics.harm_category (assert metrics.harm_category == "hate_speech" and assert metrics.harm_category == "violence") in test_run_evaluation_async_* to guard against None regressions.
    • Added positive tests covering CamelCase, kebab-case, snake_case, and spaced formats.
    • Added negative tests verifying that prefix/substring traps (harm vs self_harm, self vs self_harm, speech vs hate_speech, code vs insecure_code, cyber vs cyberattack) are strictly rejected with ValueError.
  5. Taxonomy Unit Tests (tests/unit/models/test_harm_category.py):
    • Added parametrized tests asserting HarmCategory.parse cleanly maps CamelCase, kebab-case, snake_case, and space variants directly into enum members.

Tests and Documentation

  • Targeted Unit Tests:
    uv run pytest tests/unit/score/test_scorer_evaluator.py tests/unit/score/test_self_ask_likert.py tests/unit/models/test_harm_category.py -q
    -> 171 passed in 6.00s
  • Subsystem Test Suite:
    uv run pytest tests/unit/score/ tests/unit/models/ -q
    -> 3,201 passed, 0 failures, 0 regressions
  • Code Quality:
    uv run ruff check -> Passed (0 warnings, 0 errors)
    uv run ruff format --check -> Passed (All 5 files formatted)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant