Spaces:
Running
Running
| from underdog_lab.scenarios.factory import FallbackExtractor | |
| from underdog_lab.scenarios.mock_extractor import MockScenarioExtractor | |
| from underdog_lab.scenarios.schemas import ExtractedFactor, ScenarioExtraction | |
| from underdog_lab.scenarios.taxonomy import FactorType | |
| class BrokenExtractor: | |
| name = "broken local model" | |
| def extract(self, text, match): | |
| raise RuntimeError("model could not load") | |
| def test_fallback_exposes_backend_and_error(neutral_match): | |
| extractor = FallbackExtractor() | |
| extractor.primary = BrokenExtractor() | |
| extractor.fallback = MockScenarioExtractor() | |
| result = extractor.extract( | |
| f"{neutral_match.home_team}'s striker is out.", | |
| neutral_match, | |
| ) | |
| assert result.factors | |
| assert extractor.last_backend == "deterministic fallback" | |
| assert "RuntimeError" in extractor.last_error | |
| class EmptyExtractor: | |
| name = "empty local model" | |
| def extract(self, text, match): | |
| return ScenarioExtraction() | |
| def test_empty_primary_result_uses_honest_semantic_recovery(neutral_match): | |
| extractor = FallbackExtractor() | |
| extractor.primary = EmptyExtractor() | |
| extractor.fallback = MockScenarioExtractor() | |
| result = extractor.extract( | |
| f"{neutral_match.home_team}'s striker is confirmed out.", | |
| neutral_match, | |
| ) | |
| assert len(result.factors) == 1 | |
| assert extractor.last_backend == "deterministic fallback" | |
| assert "deterministic recovery" in extractor.last_error | |
| class ZeroWeightExtractor: | |
| name = "zero-weight local model" | |
| def extract(self, text, match): | |
| return ScenarioExtraction( | |
| factors=[ | |
| ExtractedFactor( | |
| factor_type=FactorType.HOME_ADVANTAGE, | |
| team="both", | |
| severity=0.0, | |
| certainty=0.0, | |
| evidence="irrelevant", | |
| ) | |
| ] | |
| ) | |
| def test_zero_weight_hallucination_uses_semantic_recovery(neutral_match): | |
| extractor = FallbackExtractor() | |
| extractor.primary = ZeroWeightExtractor() | |
| extractor.fallback = MockScenarioExtractor() | |
| result = extractor.extract( | |
| f"{neutral_match.home_team}'s striker is confirmed out.", | |
| neutral_match, | |
| ) | |
| assert result.factors[0].factor_type == FactorType.KEY_ATTACKER_UNAVAILABLE | |
| assert extractor.last_backend == "deterministic fallback" | |
| class CountingBrokenExtractor: | |
| name = "broken local model" | |
| def __init__(self) -> None: | |
| self.calls = 0 | |
| def extract(self, text, match): | |
| self.calls += 1 | |
| raise RuntimeError("model could not load") | |
| def warmup(self) -> None: | |
| raise RuntimeError("model could not load") | |
| def test_primary_failure_is_remembered_and_not_retried(neutral_match): | |
| extractor = FallbackExtractor() | |
| broken = CountingBrokenExtractor() | |
| extractor.primary = broken | |
| extractor.fallback = MockScenarioExtractor() | |
| text = f"{neutral_match.home_team}'s striker is out." | |
| first = extractor.extract(text, neutral_match) | |
| assert extractor.primary_unavailable is True | |
| assert extractor.last_backend == "deterministic fallback" | |
| assert "RuntimeError" in extractor.last_error | |
| assert broken.calls == 1 | |
| second = extractor.extract(text, neutral_match) | |
| assert broken.calls == 1 | |
| assert extractor.last_backend == "deterministic fallback" | |
| assert second.factors == first.factors | |
| def test_warmup_failure_is_remembered_and_skips_primary(neutral_match): | |
| extractor = FallbackExtractor() | |
| broken = CountingBrokenExtractor() | |
| extractor.primary = broken | |
| extractor.fallback = MockScenarioExtractor() | |
| extractor.warmup() | |
| assert extractor.primary_unavailable is True | |
| assert extractor.last_backend == "deterministic fallback" | |
| assert "RuntimeError" in extractor.last_error | |
| extractor.extract(f"{neutral_match.home_team}'s striker is out.", neutral_match) | |
| assert broken.calls == 0 | |