ai-memory-backend / tests /test_benchmark_validators.py
Baida07's picture
sync: 166 file da Baida98/AI@a6ac2424e11e5c320c5ff688e1ce7addac64cdab (local-fallback deploy-all) (#12)
80065ea
Raw
History Blame Contribute Delete
9.64 kB
import unittest
from benchmarks.validators import (
validate_coding_output,
validate_coding_retry,
validate_mmlu_output,
validate_reasoning_output,
validate_reasoning_retry,
)
class MMLUValidatorTests(unittest.TestCase):
def test_accepts_explicit_answer_with_explanation(self):
result = validate_mmlu_output(
"ANSWER: C\nPerché la complessità nel caso peggiore è quadratica.",
expected="C",
)
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "C")
self.assertTrue(result.evidence["correct"])
def test_accepts_marked_choice(self):
result = validate_mmlu_output("La scelta corretta è (B). La stack segue LIFO.")
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "B")
def test_accepts_single_isolated_letter(self):
result = validate_mmlu_output("D")
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "D")
def test_accepts_final_answer_contract_used_by_retry(self):
result = validate_mmlu_output("Final answer: D\nThe two values overflow because both are negative.")
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "D")
def test_accepts_runner_bold_contract(self):
result = validate_mmlu_output("**(B)** — risposta scelta")
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "B")
def test_explanation_letters_do_not_override_explicit_answer(self):
result = validate_mmlu_output("ANSWER: A. Le opzioni B, C e D sono errate.")
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "A")
def test_rejects_missing_answer(self):
result = validate_mmlu_output("La spiegazione descrive il concetto ma non seleziona un'opzione.")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "answer_missing")
def test_rejects_conflicting_explicit_answers(self):
result = validate_mmlu_output("ANSWER: A\nFinal answer: C")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "answer_ambiguous")
self.assertEqual(result.evidence["distinct_candidates"], ["A", "C"])
def test_rejects_empty_output(self):
result = validate_mmlu_output(None)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "answer_missing")
def test_expected_answer_only_affects_evidence(self):
result = validate_mmlu_output("ANSWER: B", expected="C")
self.assertTrue(result.valid)
self.assertFalse(result.evidence["correct"])
self.assertEqual(result.normalized, "B")
class CodingValidatorTests(unittest.TestCase):
def test_accepts_typescript_fence_and_required_symbol(self):
output = """Ecco l'implementazione:
```typescript
export function reverseWords(value: string): string {
return value.trim().split(/\\s+/).reverse().join(' ');
}
```
"""
result = validate_coding_output(output, required_symbols=["reverseWords"], min_significant_lines=3)
self.assertTrue(result.valid)
self.assertIn("reverseWords", result.normalized)
self.assertEqual(result.evidence["missing_symbols"], [])
def test_accepts_json_envelope(self):
output = '{"language":"typescript","code":"export const add = (a: number, b: number): number => a + b;"}'
result = validate_coding_output(output, required_symbols=["add"])
self.assertTrue(result.valid)
self.assertEqual(result.evidence["extraction"], "json:code")
def test_rejects_empty_fence(self):
result = validate_coding_output("```typescript\n\n```")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "code_empty")
def test_rejects_missing_code_block(self):
result = validate_coding_output("La soluzione è implementata nel testo seguente, ma il codice non è incluso.")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "code_missing")
def test_rejects_wrong_language_fence(self):
result = validate_coding_output("```python\ndef add(a, b): return a + b\n```")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "code_wrong_language")
def test_rejects_required_symbol_missing(self):
result = validate_coding_output(
"```ts\nexport function subtract(a: number, b: number): number { return a - b; }\n```",
required_symbols=["add"],
)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "required_symbol_missing")
self.assertEqual(result.evidence["missing_symbols"], ["add"])
def test_repaired_typescript_output_passes_contract(self):
result = validate_coding_output(
"```typescript\nexport function add(a: number, b: number): number {\n return a + b;\n}\n```",
required_symbols=["add"],
min_significant_lines=3,
)
self.assertTrue(result.valid)
self.assertIsNone(result.failure_code)
def test_retry_is_requested_for_missing_typescript_before_last_attempt(self):
result = validate_coding_retry(
"code_correct: implementa TypeScript",
"La spiegazione non contiene codice.",
is_last_attempt=False,
)
self.assertIsNotNone(result)
self.assertEqual(result.failure_code, "code_missing")
def test_retry_is_not_requested_on_last_attempt(self):
result = validate_coding_retry(
"code_correct: implementa TypeScript",
"La spiegazione non contiene codice.",
is_last_attempt=True,
)
self.assertIsNone(result)
def test_retry_is_not_requested_for_non_coding_goal(self):
result = validate_coding_retry(
"Scrivi una spiegazione concettuale",
"La spiegazione non contiene codice.",
is_last_attempt=False,
)
self.assertIsNone(result)
def test_retry_is_not_requested_for_valid_typescript(self):
result = validate_coding_retry(
"code_correct: implementa TypeScript",
"```typescript\nexport const add = (a: number, b: number): number => a + b;\n```",
is_last_attempt=False,
)
self.assertIsNone(result)
def test_rejects_placeholder_implementation(self):
result = validate_coding_output(
"```typescript\nexport function add(a: number, b: number): number {\n // TODO implement here\n return 0;\n}\n```",
required_symbols=["add"],
)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "code_placeholder")
def test_rejects_non_typescript_prose_inside_fence(self):
result = validate_coding_output("```typescript\nThis is only explanatory prose.\n```")
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "code_syntax_suspect")
class ReasoningValidatorTests(unittest.TestCase):
def test_accepts_gsm8k_contract_with_thousands_separator(self):
result = validate_reasoning_output(
"Somma i valori: 100 + 125 = 225.\n#### 225",
expected=225,
)
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "225")
self.assertTrue(result.evidence["correct"])
def test_accepts_labeled_final_answer(self):
result = validate_reasoning_output("I passaggi portano al totale. Final answer: 2,250", expected=2250)
self.assertTrue(result.valid)
self.assertEqual(result.normalized, "2250")
def test_classifies_wrong_numeric_answer(self):
result = validate_reasoning_output("Calcolo completo. #### 250", expected=225)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "wrong_numeric_answer")
self.assertFalse(result.evidence["correct"])
def test_classifies_missing_numeric_answer(self):
result = validate_reasoning_output("La spiegazione termina senza un numero finale.", expected=225)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "answer_missing")
def test_classifies_conflicting_explicit_answers(self):
result = validate_reasoning_output("#### 250\nFinal answer: 225", expected=225)
self.assertFalse(result.valid)
self.assertEqual(result.failure_code, "calculation_conflict")
self.assertEqual(result.evidence["distinct_candidates"], [250, 225])
def test_reasoning_retry_is_requested_for_wrong_answer_before_last_attempt(self):
result = validate_reasoning_retry(
"reasoning GSM8K: risolvi il problema",
"#### 250",
expected=225,
is_last_attempt=False,
)
self.assertIsNotNone(result)
self.assertEqual(result.failure_code, "wrong_numeric_answer")
def test_reasoning_retry_is_not_requested_on_last_attempt(self):
result = validate_reasoning_retry(
"reasoning GSM8K: risolvi il problema",
"#### 250",
expected=225,
is_last_attempt=True,
)
self.assertIsNone(result)
def test_reasoning_retry_is_not_requested_for_non_reasoning_goal(self):
result = validate_reasoning_retry(
"Implementa un componente TypeScript",
"#### 250",
expected=225,
is_last_attempt=False,
)
self.assertIsNone(result)
if __name__ == "__main__":
unittest.main()