mnemosyne/tests/test_entropy.py
Joey Yakimowich-Payne 681c1454cb feat: add memory management pipeline
Admission control, entropy-based micro-faulting, phantom tool
injection for backing store queries, and xMemory session hierarchy
for long conversations (50+ turns).

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-13 11:41:12 -06:00

182 lines
6.9 KiB
Python

"""Tests for the entropy-gated faulting module.
Tests the EntropyDetector's ability to detect hedging, uncertainty,
evicted entity references, and the should_fault decision logic.
"""
from __future__ import annotations
from mnemosyne.entropy import (
DEFAULT_FAULT_THRESHOLD,
EntropyDetector,
EntropySignal,
)
# ── Hedging detection ────────────────────────────────────────────────────
def test_entropy_hedging_detected():
"""Hedging language like 'I think' and 'probably' is detected."""
detector = EntropyDetector()
signal = detector.analyze_response(
"I think the function is probably in utils.py",
evicted_entities=[],
)
assert signal.has_hedging is True
assert signal.score > 0
def test_entropy_multiple_patterns():
"""Multiple hedging patterns increase the score."""
detector = EntropyDetector()
signal_one = detector.analyze_response(
"I think it might work.",
evicted_entities=[],
)
signal_many = detector.analyze_response(
"I think it probably might be in the file, if I recall correctly. I believe so.",
evicted_entities=[],
)
assert signal_many.score >= signal_one.score
# ── Uncertainty detection ────────────────────────────────────────────────
def test_entropy_uncertainty_detected():
"""Explicit uncertainty like 'I'm not sure' is detected."""
detector = EntropyDetector()
signal = detector.analyze_response(
"I'm not sure about the exact implementation. It's unclear to me.",
evicted_entities=[],
)
assert signal.has_uncertainty is True
assert signal.score > 0
# ── No signals ───────────────────────────────────────────────────────────
def test_entropy_no_signals():
"""Clean, confident response has no entropy signals."""
detector = EntropyDetector()
signal = detector.analyze_response(
"The function `calculate_total` is defined in src/utils.py at line 42. "
"It takes two arguments: price and quantity.",
evicted_entities=[],
)
assert signal.has_hedging is False
assert signal.has_uncertainty is False
assert signal.references_evicted is False
assert signal.has_hallucination_risk is False
assert signal.score == 0.0
# ── Evicted entity references ───────────────────────────────────────────
def test_entropy_evicted_references():
"""References to evicted entities are detected."""
detector = EntropyDetector()
signal = detector.analyze_response(
"The config is in settings.py and the handler is in views.py",
evicted_entities=["settings.py", "views.py", "models.py"],
)
assert signal.references_evicted is True
assert "settings.py" in signal.referenced_entities
assert "views.py" in signal.referenced_entities
assert "models.py" not in signal.referenced_entities
def test_entropy_hallucination_risk():
"""Hedging + evicted references = hallucination risk."""
detector = EntropyDetector()
signal = detector.analyze_response(
"I think the config is probably in settings.py somewhere",
evicted_entities=["settings.py"],
)
assert signal.has_hedging is True
assert signal.references_evicted is True
assert signal.has_hallucination_risk is True
# Hallucination risk adds a bonus to the score
assert signal.score > 0.3
# ── Composite score ──────────────────────────────────────────────────────
def test_entropy_composite_score():
"""Score combines hedging, uncertainty, and evicted references."""
detector = EntropyDetector()
# All signals present
signal = detector.analyze_response(
"I'm not sure, but I think the code is probably in utils.py",
evicted_entities=["utils.py"],
)
assert signal.has_hedging is True
assert signal.has_uncertainty is True
assert signal.references_evicted is True
assert signal.score > 0.5 # Should be high with all signals
# ── should_fault decisions ───────────────────────────────────────────────
def test_entropy_should_fault_above_threshold():
"""Faulting triggers when score >= threshold AND references evicted."""
detector = EntropyDetector(fault_threshold=0.4)
signal = detector.analyze_response(
"I think the code is probably in utils.py, if I recall correctly. I believe so.",
evicted_entities=["utils.py"],
)
# Score should be above 0.4 with hedging + evicted ref + hallucination bonus
entities = detector.should_fault(signal)
assert len(entities) > 0
assert "utils.py" in entities
def test_entropy_should_fault_below_threshold():
"""No faulting when score is below threshold."""
detector = EntropyDetector(fault_threshold=0.99) # Very high threshold
signal = detector.analyze_response(
"I think it might be there.",
evicted_entities=[],
)
entities = detector.should_fault(signal)
assert entities == []
def test_entropy_should_fault_no_evicted_refs():
"""No faulting when there are no evicted entity references, even with high hedging."""
detector = EntropyDetector(fault_threshold=0.1) # Very low threshold
signal = detector.analyze_response(
"I think it probably might be somewhere, if I recall. I believe so.",
evicted_entities=[],
)
# Even though hedging score is high, no evicted refs → no fault
entities = detector.should_fault(signal)
assert entities == []
# ── Stats tracking ───────────────────────────────────────────────────────
def test_entropy_stats_tracking():
"""Stats correctly count analyzed responses and triggered faults."""
detector = EntropyDetector(fault_threshold=0.3)
assert detector.stats == {"analyzed": 0, "faults_triggered": 0}
# Analyze a clean response
detector.analyze_response("The function is in utils.py.", evicted_entities=[])
assert detector.stats["analyzed"] == 1
assert detector.stats["faults_triggered"] == 0
# Analyze and fault a hedging response with evicted refs
signal = detector.analyze_response(
"I think the code is probably in utils.py, if I recall correctly. I believe so.",
evicted_entities=["utils.py"],
)
assert detector.stats["analyzed"] == 2
detector.should_fault(signal)
assert detector.stats["faults_triggered"] == 1