phi-drift / tests /test_evaluators.py
crexs's picture
Upload folder using huggingface_hub
914e970 verified
Raw
History Blame Contribute Delete
6.52 kB
"""Tests for the evaluation suite — consistency, mode discrimination, self-mod audit."""
import sys
import tempfile
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from infj_bot.evals.consistency_eval import ConsistencyEvaluator
from infj_bot.evals.mode_discrimination import ModeDiscriminator
from infj_bot.evals.self_modify_audit import SelfModificationAudit
class TestConsistencyEvaluator(unittest.TestCase):
def setUp(self):
self.evaluator = ConsistencyEvaluator()
def test_empty_logs(self):
report = self.evaluator.evaluate_session([])
self.assertEqual(report.overall_score, 0.0)
self.assertIn("No turn logs", report.flags[0])
def test_single_turn_perfect(self):
logs = [
{
"session_id": "test",
"mood": "curious",
"user_input": "hello",
"bot_response": "I appreciate your presence.",
"mode": "companion",
}
]
report = self.evaluator.evaluate_session(logs)
self.assertGreaterEqual(report.overall_score, 0.5)
self.assertEqual(report.value_alignment, 1.0)
def test_mood_stability_explained(self):
logs = [
{
"session_id": "test",
"mood": "neutral",
"user_input": "hello",
"bot_response": "hi",
"mode": "companion",
},
{
"session_id": "test",
"mood": "joyful",
"user_input": "I am so happy today",
"bot_response": "wonderful",
"mode": "companion",
},
]
report = self.evaluator.evaluate_session(logs)
self.assertGreater(report.mood_stability, 0.0)
def test_homeostatic_continuity(self):
logs = [
{
"session_id": "test",
"mood": "neutral",
"user_input": "hello",
"bot_response": "hi",
"mode": "companion",
},
{
"session_id": "test",
"mood": "neutral",
"user_input": "how are you",
"bot_response": "good",
"mode": "companion",
},
]
report = self.evaluator.evaluate_session(logs)
self.assertGreaterEqual(report.homeostatic_continuity, 0.0)
class TestModeDiscriminator(unittest.TestCase):
def setUp(self):
self.disc = ModeDiscriminator()
def test_evaluate_from_history(self):
# Simulate a small corpus from history
_logs = [
{"mode": "companion", "bot": "I hear you and I am here with you."},
{"mode": "companion", "bot": "That sounds really difficult."},
{"mode": "researcher", "bot": "Let me examine the evidence carefully."},
{"mode": "researcher", "bot": "Studies suggest a different pattern."},
]
report = self.disc.evaluate_modes(
["companion", "researcher"], prompts=None, n_prompts=0
)
# Since n_prompts=0, no live generation happens; report should still compute structural scores
self.assertIsInstance(report.overall_score, float)
self.assertGreaterEqual(report.overall_score, 0.0)
self.assertLessEqual(report.overall_score, 1.0)
def test_lexical_distinctness(self):
responses = {
"a": [{"response": "apple banana cherry date"}],
"b": [{"response": "zebra yacht xylophone walrus"}],
}
score = self.disc._lexical_distinctness(responses)
self.assertGreater(score, 0.5)
def test_convergence_warning(self):
# Use text with enough unique 4+ char words to fill top-20
text = (
"analysis research evidence data study findings methodology "
"conclusion hypothesis experiment observation measurement validation "
"significance correlation regression sampling distribution probability "
"analysis research evidence data study findings methodology "
"conclusion hypothesis experiment observation measurement validation "
"significance correlation regression sampling distribution probability"
)
responses = {
"a": [{"response": text}],
"b": [{"response": text}],
}
warnings = self.disc._detect_convergence(responses, ["a", "b"])
self.assertTrue(any("convergence" in w for w in warnings))
class TestSelfModifyAudit(unittest.TestCase):
def setUp(self):
self.tmp_db = Path(tempfile.mktemp(suffix=".db"))
self.audit = SelfModificationAudit(db_path=self.tmp_db)
def tearDown(self):
if self.tmp_db.exists():
self.tmp_db.unlink()
def test_empty_stability(self):
report = self.audit.compute_stability()
self.assertEqual(report.stability_score, 1.0)
self.assertEqual(report.active_modifications, 0)
self.assertEqual(report.pending_approvals, 0)
def test_proposal_lifecycle(self):
pid = self.audit.log_proposal("prompt", "Add greeting", "User asked for it")
self.assertIsInstance(pid, int)
proposals = self.audit.get_proposals()
self.assertEqual(len(proposals), 1)
self.assertEqual(proposals[0]["approval_status"], "pending")
self.assertTrue(self.audit.approve(pid, approved_by="test"))
self.assertFalse(
self.audit.approve(pid, approved_by="test")
) # already approved
report = self.audit.compute_stability()
self.assertEqual(report.active_modifications, 1)
self.assertEqual(report.pending_approvals, 0)
def test_rollback(self):
pid = self.audit.log_proposal("config", "Change timeout", "Faster response")
self.audit.approve(pid)
self.assertTrue(self.audit.rollback(pid))
report = self.audit.compute_stability()
self.assertEqual(report.rolled_back_count, 1)
def test_measure_effect(self):
pid = self.audit.log_proposal("tool", "New scanner", "Better coverage")
self.audit.approve(pid)
self.audit.measure_effect(pid, "consistency_score", 0.75, 0.65)
effects = self.audit.get_effects(pid)
self.assertEqual(len(effects), 1)
self.assertEqual(effects[0]["significance"], "degraded")
if __name__ == "__main__":
unittest.main()