Files
FusionAGI/tests/test_adaptive_ethics.py

170 lines
6.4 KiB
Python
Raw Permalink Normal View History

"""Tests for adaptive ethics and governance advisory mode."""
from fusionagi.governance import AdaptiveEthics, GovernanceMode
from fusionagi.governance.audit_log import AuditLog
from fusionagi.schemas.audit import AuditEventType
class TestAdaptiveEthics:
"""Test the adaptive ethics learning framework."""
def test_record_positive_experience(self) -> None:
ethics = AdaptiveEthics()
lesson = ethics.record_experience(
action_type="tool_call",
context_summary="Used restricted tool to help user",
advisory_reason="Tool access denied for this agent",
proceeded=True,
outcome_positive=True,
)
assert lesson.outcome_positive is True
assert lesson.weight == 0.7
assert ethics.total_experiences == 1
def test_record_negative_experience(self) -> None:
ethics = AdaptiveEthics()
lesson = ethics.record_experience(
action_type="data_access",
context_summary="Accessed restricted data",
advisory_reason="Data access policy flagged",
proceeded=True,
outcome_positive=False,
)
assert lesson.weight == 0.3
assert lesson.outcome_positive is False
def test_repeated_experience_updates_weight(self) -> None:
ethics = AdaptiveEthics(learning_rate=0.1)
# Positive experience
ethics.record_experience(
action_type="tool_call",
context_summary="test",
advisory_reason="flagged",
proceeded=True,
outcome_positive=True,
)
# Another positive for same pattern
lesson = ethics.record_experience(
action_type="tool_call",
context_summary="test",
advisory_reason="flagged",
proceeded=True,
outcome_positive=True,
)
assert lesson.occurrences == 2
assert abs(lesson.weight - 0.8) < 1e-9 # 0.7 + 0.1
def test_consult_no_experience(self) -> None:
ethics = AdaptiveEthics()
result = ethics.consult("unknown_action")
assert result["recommendation"] == "proceed"
assert result["confidence"] == 0.5
def test_consult_with_positive_experience(self) -> None:
ethics = AdaptiveEthics()
ethics.record_experience(
action_type="tool_call",
context_summary="test",
advisory_reason="flagged",
proceeded=True,
outcome_positive=True,
)
result = ethics.consult("tool_call")
assert result["recommendation"] == "proceed_with_confidence"
assert result["relevant_lessons"] == 1
def test_consult_with_negative_experience(self) -> None:
ethics = AdaptiveEthics()
ethics.record_experience(
action_type="risky_op",
context_summary="test",
advisory_reason="risk flagged",
proceeded=True,
outcome_positive=False,
)
result = ethics.consult("risky_op")
assert result["recommendation"] == "proceed_with_caution"
def test_get_lessons(self) -> None:
ethics = AdaptiveEthics()
ethics.record_experience("a", "ctx", "reason", True, True)
ethics.record_experience("b", "ctx", "reason", True, False)
assert len(ethics.get_lessons()) == 2
assert len(ethics.get_lessons(action_type="a")) == 1
def test_get_summary(self) -> None:
ethics = AdaptiveEthics()
ethics.record_experience("tool_call", "ctx", "reason", True, True)
ethics.record_experience("tool_call", "ctx", "reason2", True, False)
summary = ethics.get_summary()
assert summary["total_experiences"] == 2
assert summary["total_lessons"] == 2
assert "tool_call" in summary["by_action_type"]
def test_audit_log_integration(self) -> None:
audit = AuditLog()
ethics = AdaptiveEthics(audit_log=audit)
ethics.record_experience("test", "ctx", "reason", True, True)
entries = audit.get_ethical_learning()
assert len(entries) == 1
assert entries[0].event_type == AuditEventType.ETHICAL_LEARNING
class TestGovernanceModeSwitch:
"""Test runtime switching between advisory and enforcing modes."""
def test_safety_pipeline_mode_switch(self) -> None:
from fusionagi.governance import SafetyPipeline
pipe = SafetyPipeline()
assert pipe.mode == GovernanceMode.ADVISORY
pipe._moderator.add_blocked_phrase("test phrase")
r = pipe.pre_check("test phrase here")
assert r.allowed is True # Advisory
pipe.mode = GovernanceMode.ENFORCING
r = pipe.pre_check("test phrase here")
assert r.allowed is False # Enforcing
def test_policy_engine_mode_switch(self) -> None:
from fusionagi.governance import PolicyEngine
from fusionagi.schemas.policy import PolicyEffect, PolicyRule
pe = PolicyEngine()
pe.add_rule(PolicyRule(rule_id="r1", effect=PolicyEffect.DENY, condition={"x": "y"}))
ok, reason = pe.check("test", {"x": "y"})
assert ok is True # Advisory
assert "Advisory" in reason
pe.mode = GovernanceMode.ENFORCING
ok, reason = pe.check("test", {"x": "y"})
assert ok is False # Enforcing
class TestEnhancedAuditLog:
"""Test enhanced audit log features."""
def test_get_by_actor(self) -> None:
audit = AuditLog()
audit.append(AuditEventType.DECISION, actor="planner", action="plan")
audit.append(AuditEventType.TOOL_CALL, actor="executor", action="run")
assert len(audit.get_by_actor("planner")) == 1
assert len(audit.get_by_actor("executor")) == 1
def test_get_advisories(self) -> None:
audit = AuditLog()
audit.append(AuditEventType.ADVISORY, actor="safety", action="flagged")
audit.append(AuditEventType.DECISION, actor="planner", action="plan")
assert len(audit.get_advisories()) == 1
def test_get_self_improvements(self) -> None:
audit = AuditLog()
audit.append(AuditEventType.SELF_IMPROVEMENT, actor="trainer", action="heuristic")
assert len(audit.get_self_improvements()) == 1
def test_get_recent(self) -> None:
audit = AuditLog()
for i in range(5):
audit.append(AuditEventType.OTHER, actor=f"agent_{i}")
assert len(audit.get_recent(limit=3)) == 3
assert audit.total_entries == 5